createMachine('Machine V'); $gClient = $this->createGestionnaireClient(); $gClient->request('PATCH', self::iri('machines', $machine->getId()), [ 'headers' => ['Content-Type' => 'application/merge-patch+json'], 'json' => ['name' => 'Machine V Updated'], ]); $this->assertResponseIsSuccessful(); $vClient = $this->createViewerClient(); $vClient->request('GET', sprintf('/api/machines/%s/versions', $machine->getId())); $this->assertResponseIsSuccessful(); $data = $vClient->getResponse()->toArray(); $this->assertArrayHasKey('items', $data); $this->assertArrayHasKey('total', $data); $this->assertGreaterThanOrEqual(1, $data['total']); $firstItem = $data['items'][0]; $this->assertArrayHasKey('version', $firstItem); $this->assertArrayHasKey('action', $firstItem); $this->assertArrayHasKey('createdAt', $firstItem); } public function testComposantVersionsList(): void { $composant = $this->createComposant('Composant V'); $client = $this->createViewerClient(); $client->request('GET', sprintf('/api/composants/%s/versions', $composant->getId())); $this->assertResponseIsSuccessful(); $data = $client->getResponse()->toArray(); $this->assertArrayHasKey('items', $data); } public function testPieceVersionsList(): void { $piece = $this->createPiece('Piece V'); $client = $this->createViewerClient(); $client->request('GET', sprintf('/api/pieces/%s/versions', $piece->getId())); $this->assertResponseIsSuccessful(); } public function testProductVersionsList(): void { $product = $this->createProduct('Product V'); $client = $this->createViewerClient(); $client->request('GET', sprintf('/api/products/%s/versions', $product->getId())); $this->assertResponseIsSuccessful(); } public function testVersionsNotFound(): void { $client = $this->createViewerClient(); $client->request('GET', '/api/machines/nonexistent-id/versions'); $this->assertResponseStatusCodeSame(404); } public function testVersionsUnauthenticated(): void { $machine = $this->createMachine('Machine V'); $client = $this->createUnauthenticatedClient(); $client->request('GET', sprintf('/api/machines/%s/versions', $machine->getId())); $this->assertResponseStatusCodeSame(401); } // ── Preview ───────────────────────────────────────────────────── public function testPreviewRequiresGestionnaire(): void { $machine = $this->createMachine('Machine P'); $client = $this->createViewerClient(); $client->request('GET', sprintf('/api/machines/%s/versions/1/preview', $machine->getId())); $this->assertResponseStatusCodeSame(403); } public function testPreviewReturnsRestoreInfo(): void { $composant = $this->createComposant('Composant P'); // Update to create version 2 $gClient = $this->createGestionnaireClient(); $gClient->request('PATCH', self::iri('composants', $composant->getId()), [ 'headers' => ['Content-Type' => 'application/merge-patch+json'], 'json' => ['name' => 'Composant P Updated'], ]); $this->assertResponseIsSuccessful(); // Preview restore to version 1 $gClient2 = $this->createGestionnaireClient(); $gClient2->request('GET', sprintf('/api/composants/%s/versions/1/preview', $composant->getId())); $this->assertResponseIsSuccessful(); $data = $gClient2->getResponse()->toArray(); $this->assertArrayHasKey('version', $data); $this->assertArrayHasKey('restoreMode', $data); $this->assertArrayHasKey('diff', $data); $this->assertArrayHasKey('warnings', $data); $this->assertEquals(1, $data['version']); $this->assertEquals('full', $data['restoreMode']); } // ── Restore ───────────────────────────────────────────────────── public function testRestoreRequiresGestionnaire(): void { $machine = $this->createMachine('Machine R'); $client = $this->createViewerClient(); $client->request('POST', sprintf('/api/machines/%s/versions/1/restore', $machine->getId())); $this->assertResponseStatusCodeSame(403); } public function testRestoreCreatesNewVersion(): void { $composant = $this->createComposant('Original Name'); // Update $gClient = $this->createGestionnaireClient(); $gClient->request('PATCH', self::iri('composants', $composant->getId()), [ 'headers' => ['Content-Type' => 'application/merge-patch+json'], 'json' => ['name' => 'Updated Name'], ]); $this->assertResponseIsSuccessful(); // Restore to version 1 $gClient2 = $this->createGestionnaireClient(); $gClient2->request('POST', sprintf('/api/composants/%s/versions/1/restore', $composant->getId())); $this->assertResponseIsSuccessful(); $data = $gClient2->getResponse()->toArray(); $this->assertTrue($data['success']); $this->assertEquals(1, $data['restoredFromVersion']); $this->assertGreaterThan(2, $data['newVersion']); // Verify the name was restored $vClient = $this->createViewerClient(); $vClient->request('GET', self::iri('composants', $composant->getId())); $this->assertResponseIsSuccessful(); $entityData = $vClient->getResponse()->toArray(); $this->assertEquals('Original Name', $entityData['name']); } public function testRestoreVersionNotFound(): void { $composant = $this->createComposant('Composant NF'); $client = $this->createGestionnaireClient(); $client->request('POST', sprintf('/api/composants/%s/versions/999/restore', $composant->getId())); $this->assertResponseStatusCodeSame(404); } }