createSite(); $this->createMachine('Machine A', $site); $this->createMachine('Machine B', $site); $client = $this->createViewerClient(); $client->request('GET', '/api/machines'); $this->assertResponseIsSuccessful(); $this->assertJsonContainsHydraCollection(); $this->assertJsonContains(['totalItems' => 2]); } public function testGetItem(): void { $m = $this->createMachine('Machine A', reference: 'REF-001'); $client = $this->createViewerClient(); $client->request('GET', self::iri('machines', $m->getId())); $this->assertResponseIsSuccessful(); $this->assertJsonContains([ 'name' => 'Machine A', 'reference' => 'REF-001', ]); } public function testPost(): void { $site = $this->createSite('Usine'); $client = $this->createGestionnaireClient(); $client->request('POST', '/api/machines', [ 'headers' => ['Content-Type' => 'application/ld+json'], 'json' => [ 'name' => 'Nouvelle Machine', 'reference' => 'REF-NEW', 'site' => self::iri('sites', $site->getId()), ], ]); $this->assertResponseStatusCodeSame(201); $this->assertJsonContains(['name' => 'Nouvelle Machine']); } public function testPut(): void { $m = $this->createMachine('Machine A'); $client = $this->createGestionnaireClient(); $client->request('PUT', self::iri('machines', $m->getId()), [ 'headers' => ['Content-Type' => 'application/ld+json'], 'json' => [ 'name' => 'Machine A Renommée', 'site' => self::iri('sites', $m->getSite()->getId()), ], ]); $this->assertResponseIsSuccessful(); $this->assertJsonContains(['name' => 'Machine A Renommée']); } public function testPatch(): void { $m = $this->createMachine('Machine A'); $client = $this->createGestionnaireClient(); $client->request('PATCH', self::iri('machines', $m->getId()), [ 'headers' => ['Content-Type' => 'application/merge-patch+json'], 'json' => ['prix' => '1500.00'], ]); $this->assertResponseIsSuccessful(); $this->assertJsonContains(['prix' => '1500.00']); } public function testDelete(): void { $m = $this->createMachine('ToDelete'); $client = $this->createGestionnaireClient(); $client->request('DELETE', self::iri('machines', $m->getId())); $this->assertResponseStatusCodeSame(204); } public function testUnauthenticatedAccess(): void { $client = $this->createUnauthenticatedClient(); $client->request('GET', '/api/machines'); $this->assertResponseStatusCodeSame(401); } public function testViewerCannotWrite(): void { $site = $this->createSite(); $client = $this->createViewerClient(); $client->request('POST', '/api/machines', [ 'headers' => ['Content-Type' => 'application/ld+json'], 'json' => [ 'name' => 'Blocked', 'site' => self::iri('sites', $site->getId()), ], ]); $this->assertResponseStatusCodeSame(403); } public function testPostWithoutSiteFails(): void { $client = $this->createGestionnaireClient(); $client->request('POST', '/api/machines', [ 'headers' => ['Content-Type' => 'application/ld+json'], 'json' => ['name' => 'No site'], ]); $this->assertResponseStatusCodeSame(422); } public function testGetStructureEndpoint(): void { $machine = $this->createMachine('Machine structure'); $composant = $this->createComposant('Composant A'); $piece = $this->createPiece('Pièce A', 'REF-PA'); $product = $this->createProduct('Produit A', 'REF-PRA'); $compLink = $this->createMachineComponentLink($machine, $composant); $this->createMachinePieceLink($machine, $piece, $compLink, 3); $this->createMachineProductLink($machine, $product); $client = $this->createViewerClient(); $client->request('GET', '/api/machines/'.$machine->getId().'/structure'); $this->assertResponseIsSuccessful(); $data = $client->getResponse()->toArray(); $this->assertArrayHasKey('machine', $data); $this->assertSame($machine->getId(), $data['machine']['id']); $this->assertArrayHasKey('componentLinks', $data); $this->assertCount(1, $data['componentLinks']); $this->assertSame($composant->getId(), $data['componentLinks'][0]['composantId']); $this->assertArrayHasKey('pieceLinks', $data); $this->assertCount(1, $data['pieceLinks']); $this->assertSame($piece->getId(), $data['pieceLinks'][0]['pieceId']); $this->assertArrayHasKey('productLinks', $data); $this->assertCount(1, $data['productLinks']); $this->assertSame($product->getId(), $data['productLinks'][0]['productId']); } public function testGetStructureReturnsComposantSlotsData(): void { $pieceType = $this->createModelType('Joint', 'JOINT-SLOT', ModelCategory::PIECE); $productType = $this->createModelType('Huile', 'HUILE-SLOT', ModelCategory::PRODUCT); $compType = $this->createModelType('Pompe', 'POMPE-SLOT', ModelCategory::COMPONENT); $composant = $this->createComposant('Composant avec slots', $compType); $piece = $this->createPiece('Joint sélectionné', 'REF-JS', $pieceType); $product = $this->createProduct('Huile sélectionnée', 'REF-HS', $productType); // Create slots on the composant $this->createComposantPieceSlot($composant, $pieceType, $piece, 2, 0); $this->createComposantProductSlot($composant, $productType, $product, 'LUB', 0); $this->createComposantSubcomponentSlot($composant, 'Sous-pompe', 'SP', $compType, null, 0); $machine = $this->createMachine('Machine slots'); $this->createMachineComponentLink($machine, $composant); $client = $this->createViewerClient(); $client->request('GET', '/api/machines/'.$machine->getId().'/structure'); $this->assertResponseIsSuccessful(); $data = $client->getResponse()->toArray(); $compData = $data['componentLinks'][0]['composant']; $this->assertArrayHasKey('structure', $compData); $structure = $compData['structure']; // Piece slots $this->assertCount(1, $structure['pieces']); $this->assertSame($pieceType->getId(), $structure['pieces'][0]['typePieceId']); $this->assertSame(2, $structure['pieces'][0]['quantity']); $this->assertSame($piece->getId(), $structure['pieces'][0]['selectedPieceId']); $this->assertArrayHasKey('resolvedPiece', $structure['pieces'][0]); $this->assertSame($piece->getId(), $structure['pieces'][0]['resolvedPiece']['id']); // Product slots $this->assertCount(1, $structure['products']); $this->assertSame($productType->getId(), $structure['products'][0]['typeProductId']); $this->assertSame('LUB', $structure['products'][0]['familyCode']); $this->assertSame($product->getId(), $structure['products'][0]['selectedProductId']); // Subcomponent slots $this->assertCount(1, $structure['subcomponents']); $this->assertSame('Sous-pompe', $structure['subcomponents'][0]['alias']); $this->assertSame('SP', $structure['subcomponents'][0]['familyCode']); $this->assertSame($compType->getId(), $structure['subcomponents'][0]['typeComposantId']); } public function testGetStructureUnauthenticated(): void { $machine = $this->createMachine('Machine auth'); $client = $this->createUnauthenticatedClient(); $client->request('GET', '/api/machines/'.$machine->getId().'/structure'); $this->assertResponseStatusCodeSame(401); } public function testGetStructureNotFound(): void { $client = $this->createViewerClient(); $client->request('GET', '/api/machines/nonexistent-id/structure'); $this->assertResponseStatusCodeSame(404); } public function testPatchStructureEndpoint(): void { $machine = $this->createMachine('Machine PATCH'); $composant = $this->createComposant('Composant PATCH'); $piece = $this->createPiece('Pièce PATCH', 'REF-PATCH'); $client = $this->createGestionnaireClient(); $client->request('PATCH', '/api/machines/'.$machine->getId().'/structure', [ 'headers' => ['Content-Type' => 'application/json'], 'json' => [ 'componentLinks' => [ ['composantId' => $composant->getId()], ], 'pieceLinks' => [ ['pieceId' => $piece->getId(), 'parentComponentLinkId' => null, 'quantity' => 5], ], 'productLinks' => [], ], ]); $this->assertResponseIsSuccessful(); $data = $client->getResponse()->toArray(); $this->assertCount(1, $data['componentLinks']); $this->assertSame($composant->getId(), $data['componentLinks'][0]['composantId']); $this->assertCount(1, $data['pieceLinks']); $this->assertSame($piece->getId(), $data['pieceLinks'][0]['pieceId']); $this->assertSame(5, $data['pieceLinks'][0]['quantity']); } public function testPatchStructureViewerForbidden(): void { $machine = $this->createMachine('Machine PATCH forbidden'); $client = $this->createViewerClient(); $client->request('PATCH', '/api/machines/'.$machine->getId().'/structure', [ 'headers' => ['Content-Type' => 'application/json'], 'json' => ['componentLinks' => [], 'pieceLinks' => [], 'productLinks' => []], ]); $this->assertResponseStatusCodeSame(403); } public function testPieceQuantityFromComposantSlot(): void { $pieceType = $this->createModelType('Joint', 'JOINT-QTY', ModelCategory::PIECE); $composant = $this->createComposant('Composant qty'); $piece = $this->createPiece('Pièce qty', 'REF-QTY', $pieceType); // Create a piece slot with quantity 4 on the composant $this->createComposantPieceSlot($composant, $pieceType, $piece, 4, 0); $machine = $this->createMachine('Machine qty'); $compLink = $this->createMachineComponentLink($machine, $composant); $this->createMachinePieceLink($machine, $piece, $compLink, 1); $client = $this->createViewerClient(); $client->request('GET', '/api/machines/'.$machine->getId().'/structure'); $this->assertResponseIsSuccessful(); $data = $client->getResponse()->toArray(); // The quantity should come from the composant slot (4), not the link (1) $pieceLinkData = $data['pieceLinks'][0]; $this->assertSame(4, $pieceLinkData['quantity']); } }