|
11 | 11 |
|
12 | 12 | declare(strict_types=1); |
13 | 13 |
|
14 | | -namespace spec\Sylius\Resource\Symfony\Routing\Factory; |
| 14 | +namespace Sylius\Resource\Tests\Symfony\Routing\Factory; |
15 | 15 |
|
16 | | -use PhpSpec\ObjectBehavior; |
17 | | -use Prophecy\Argument; |
| 16 | +use PHPUnit\Framework\TestCase; |
18 | 17 | use Sylius\Component\Resource\Tests\Dummy\DummyResourceWithOperations; |
19 | 18 | use Sylius\Resource\Metadata\Create; |
20 | 19 | use Sylius\Resource\Metadata\Index; |
21 | 20 | use Sylius\Resource\Metadata\MetadataInterface; |
| 21 | +use Sylius\Resource\Metadata\Operation; |
| 22 | +use Sylius\Resource\Metadata\Operations; |
22 | 23 | use Sylius\Resource\Metadata\RegistryInterface; |
23 | 24 | use Sylius\Resource\Metadata\Resource\Factory\AttributesResourceMetadataCollectionFactory; |
| 25 | +use Sylius\Resource\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; |
| 26 | +use Sylius\Resource\Metadata\Resource\ResourceMetadataCollection; |
| 27 | +use Sylius\Resource\Metadata\ResourceMetadata; |
24 | 28 | use Sylius\Resource\Metadata\Show; |
25 | 29 | use Sylius\Resource\Metadata\Update; |
26 | 30 | use Sylius\Resource\Symfony\Routing\Factory\AttributesOperationRouteFactory; |
27 | 31 | use Sylius\Resource\Symfony\Routing\Factory\OperationRouteFactory; |
28 | 32 | use Sylius\Resource\Symfony\Routing\Factory\RouteName\OperationRouteNameFactory; |
29 | 33 | use Sylius\Resource\Symfony\Routing\Factory\RoutePath\OperationRoutePathFactoryInterface; |
30 | 34 | use Symfony\Component\Routing\RouteCollection; |
31 | | -use Webmozart\Assert\Assert; |
32 | 35 |
|
33 | | -final class AttributesOperationRouteFactorySpec extends ObjectBehavior |
| 36 | +final class AttributesOperationRouteFactoryTest extends TestCase |
34 | 37 | { |
35 | | - function let( |
36 | | - RegistryInterface $resourceRegistry, |
37 | | - OperationRoutePathFactoryInterface $routePathFactory, |
38 | | - ): void { |
39 | | - $this->beConstructedWith( |
40 | | - $resourceRegistry, |
41 | | - new OperationRouteFactory($routePathFactory->getWrappedObject()), |
| 38 | + private RegistryInterface $resourceRegistry; |
| 39 | + |
| 40 | + private OperationRoutePathFactoryInterface $routePathFactory; |
| 41 | + |
| 42 | + private AttributesOperationRouteFactory $attributesOperationRouteFactory; |
| 43 | + |
| 44 | + protected function setUp(): void |
| 45 | + { |
| 46 | + $this->resourceRegistry = $this->createMock(RegistryInterface::class); |
| 47 | + $this->routePathFactory = $this->createMock(OperationRoutePathFactoryInterface::class); |
| 48 | + |
| 49 | + $this->attributesOperationRouteFactory = new AttributesOperationRouteFactory( |
| 50 | + $this->resourceRegistry, |
| 51 | + new OperationRouteFactory($this->routePathFactory), |
42 | 52 | new AttributesResourceMetadataCollectionFactory( |
43 | | - $resourceRegistry->getWrappedObject(), |
| 53 | + $this->resourceRegistry, |
44 | 54 | new OperationRouteNameFactory(), |
45 | 55 | 'symfony', |
46 | 56 | ), |
47 | 57 | ); |
48 | 58 | } |
49 | 59 |
|
50 | | - function it_is_initializable(): void |
| 60 | + private function createDummyMetadataMock(): MetadataInterface |
51 | 61 | { |
52 | | - $this->shouldHaveType(AttributesOperationRouteFactory::class); |
| 62 | + $metadata = $this->createMock(MetadataInterface::class); |
| 63 | + $metadata->method('getServiceId')->with('repository')->willReturn('app.repository.dummy'); |
| 64 | + $metadata->method('hasClass')->with('form')->willReturn(true); |
| 65 | + $metadata->method('getClass')->willReturnMap([ |
| 66 | + ['form', 'App\Form'], |
| 67 | + ['model', 'App\Dummy'], |
| 68 | + ]); |
| 69 | + $metadata->method('getApplicationName')->willReturn('app'); |
| 70 | + $metadata->method('getName')->willReturn('dummy'); |
| 71 | + $metadata->method('getPluralName')->willReturn('dummies'); |
| 72 | + |
| 73 | + return $metadata; |
| 74 | + } |
| 75 | + |
| 76 | + public function testItCreatesRoutesWithOperations(): void |
| 77 | + { |
| 78 | + $routeCollection = new RouteCollection(); |
| 79 | + $metadata = $this->createDummyMetadataMock(); |
| 80 | + $this->resourceRegistry->method('get')->with('app.dummy')->willReturn($metadata); |
| 81 | + |
| 82 | + $this->routePathFactory |
| 83 | + ->expects($this->exactly(4)) |
| 84 | + ->method('createRoutePath') |
| 85 | + ->willReturnCallback(function ($operation, $path) { |
| 86 | + if ($operation instanceof Index) { |
| 87 | + return '/dummies'; |
| 88 | + } |
| 89 | + if ($operation instanceof Create) { |
| 90 | + return '/dummies/new'; |
| 91 | + } |
| 92 | + if ($operation instanceof Update) { |
| 93 | + return '/dummies/{id}/edit'; |
| 94 | + } |
| 95 | + if ($operation instanceof Show) { |
| 96 | + return '/dummies/{id}'; |
| 97 | + } |
| 98 | + |
| 99 | + return $path; |
| 100 | + }); |
| 101 | + |
| 102 | + $this->attributesOperationRouteFactory->createRouteForClass($routeCollection, DummyResourceWithOperations::class); |
| 103 | + |
| 104 | + $this->assertCount(4, $routeCollection); |
| 105 | + $this->assertNotNull($routeCollection->get('app_dummy_index'), 'Route "app_dummy_index" not found but it should.'); |
| 106 | + $this->assertNotNull($routeCollection->get('app_dummy_create'), 'Route "app_dummy_create" not found but it should.'); |
| 107 | + $this->assertNotNull($routeCollection->get('app_dummy_update'), 'Route "app_dummy_update" not found but it should.'); |
| 108 | + $this->assertNotNull($routeCollection->get('app_dummy_show'), 'Route "app_dummy_show" not found but it should.'); |
53 | 109 | } |
54 | 110 |
|
55 | | - function it_creates_routes_with_operations( |
56 | | - RegistryInterface $resourceRegistry, |
57 | | - MetadataInterface $metadata, |
58 | | - OperationRoutePathFactoryInterface $routePathFactory, |
59 | | - ): void { |
| 111 | + public function testItSkipsNonHttpOperations(): void |
| 112 | + { |
60 | 113 | $routeCollection = new RouteCollection(); |
61 | 114 |
|
62 | | - $metadata->getServiceId('repository')->willReturn('app.repository.dummy'); |
63 | | - $metadata->hasClass('form')->willReturn(true); |
64 | | - $metadata->getClass('form')->willReturn('App\Form'); |
65 | | - $metadata->getClass('model')->willReturn('App\Dummy'); |
66 | | - $metadata->getStateMachineComponent()->willReturn('symfony'); |
67 | | - $resourceRegistry->get('app.dummy')->willReturn($metadata); |
68 | | - |
69 | | - $metadata->getApplicationName()->willReturn('app'); |
70 | | - $metadata->getName()->willReturn('dummy'); |
71 | | - $metadata->getPluralName()->willReturn('dummies'); |
72 | | - |
73 | | - $routePathFactory->createRoutePath(Argument::type(Index::class), 'dummies')->willReturn('/dummies')->shouldBeCalled(); |
74 | | - $routePathFactory->createRoutePath(Argument::type(Create::class), 'dummies')->willReturn('/dummies/new')->shouldBeCalled(); |
75 | | - $routePathFactory->createRoutePath(Argument::type(Update::class), 'dummies')->willReturn('/dummies/{id}/edit')->shouldBeCalled(); |
76 | | - $routePathFactory->createRoutePath(Argument::type(Show::class), 'dummies')->willReturn('/dummies/{id}')->shouldBeCalled(); |
77 | | - |
78 | | - $this->createRouteForClass($routeCollection, DummyResourceWithOperations::class); |
79 | | - |
80 | | - Assert::count($routeCollection, 4); |
81 | | - Assert::notNull($routeCollection->get('app_dummy_index'), 'Route "app_dummy_index" not found but it should.'); |
82 | | - Assert::notNull($routeCollection->get('app_dummy_create'), 'Route "app_dummy_create" not found but it should.'); |
83 | | - Assert::notNull($routeCollection->get('app_dummy_update'), 'Route "app_dummy_update" not found but it should.'); |
84 | | - Assert::notNull($routeCollection->get('app_dummy_show'), 'Route "app_dummy_show" not found but it should.'); |
| 115 | + // Create a non-HTTP operation (directly extends Operation, not HttpOperation) |
| 116 | + $nonHttpOperation = new class() extends Operation { |
| 117 | + public function getShortName(): ?string |
| 118 | + { |
| 119 | + return 'custom'; |
| 120 | + } |
| 121 | + }; |
| 122 | + |
| 123 | + // Create an HTTP operation |
| 124 | + $httpOperation = (new Index(name: 'app_dummy_index'))->withRouteName('app_dummy_index'); |
| 125 | + |
| 126 | + // Create resource with mixed operations |
| 127 | + $resource = new ResourceMetadata( |
| 128 | + alias: 'app.dummy', |
| 129 | + name: 'dummy', |
| 130 | + operations: [ |
| 131 | + 'app_dummy_custom' => $nonHttpOperation, |
| 132 | + 'app_dummy_index' => $httpOperation, |
| 133 | + ], |
| 134 | + ); |
| 135 | + |
| 136 | + $resourceCollection = new ResourceMetadataCollection(); |
| 137 | + $resourceCollection[] = $resource; |
| 138 | + |
| 139 | + // Mock resource metadata factory to return our custom collection |
| 140 | + $resourceMetadataFactory = $this->createMock(ResourceMetadataCollectionFactoryInterface::class); |
| 141 | + $resourceMetadataFactory |
| 142 | + ->method('create') |
| 143 | + ->with(\stdClass::class) |
| 144 | + ->willReturn($resourceCollection); |
| 145 | + |
| 146 | + // Mock metadata for registry |
| 147 | + $metadata = $this->createDummyMetadataMock(); |
| 148 | + $this->resourceRegistry->method('get')->with('app.dummy')->willReturn($metadata); |
| 149 | + |
| 150 | + $this->routePathFactory |
| 151 | + ->expects($this->once()) |
| 152 | + ->method('createRoutePath') |
| 153 | + ->with($httpOperation, 'dummies') |
| 154 | + ->willReturn('/dummies'); |
| 155 | + |
| 156 | + // Create factory with mocked dependencies |
| 157 | + $factory = new AttributesOperationRouteFactory( |
| 158 | + $this->resourceRegistry, |
| 159 | + new OperationRouteFactory($this->routePathFactory), |
| 160 | + $resourceMetadataFactory, |
| 161 | + ); |
| 162 | + |
| 163 | + $factory->createRouteForClass($routeCollection, \stdClass::class); |
| 164 | + |
| 165 | + // Only the HTTP operation should create a route |
| 166 | + $this->assertCount(1, $routeCollection); |
| 167 | + $this->assertNotNull($routeCollection->get('app_dummy_index'), 'Route "app_dummy_index" not found but it should.'); |
| 168 | + $this->assertNull($routeCollection->get('app_dummy_custom'), 'Non-HTTP operation should not create a route.'); |
85 | 169 | } |
86 | 170 | } |
0 commit comments