fix(audit) : visibilité protected pour ActorProfileResolver
AbstractAuditSubscriber déclarait $actorProfileResolver en private readonly via promoted property. MachineAuditSubscriber surcharge onFlush() et accède à $this->actorProfileResolver, mais private n'est pas hérité — PHP voyait null et levait "Call to a member function resolve() on null" sur chaque flush Doctrine touchant des link entities. Le passage à protected suit la convention déjà en place dans la classe (safeGet, normalizeValue, persistAuditLog, etc. sont protected). readonly préserve l'immutabilité de la dépendance DI. Ajoute aussi deux tests de régression pour le clone des contextFieldValues (symétrique au test composant existant) et nettoie deux lignes vides cosmétiques laissées par le refactor précédent. - testCloneMachineCopiesPieceContextFieldValues : vérifie que les CFV context d'un MachinePieceLink sont bien rattachées au nouveau lien après clone. - testCloneMachineLeavesSourceContextFieldValuesIntact : vérifie que la machine source garde ses CFV context après clone (invariant implicite).
This commit is contained in:
@@ -7,6 +7,9 @@ namespace App\Tests\Api\Entity;
|
||||
use App\Enum\ModelCategory;
|
||||
use App\Tests\AbstractApiTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class MachineContextCustomFieldTest extends AbstractApiTestCase
|
||||
{
|
||||
public function testStructureReturnsContextFieldsOnComponentLink(): void
|
||||
@@ -56,7 +59,7 @@ class MachineContextCustomFieldTest extends AbstractApiTestCase
|
||||
|
||||
$normalFields = array_filter(
|
||||
$componentLink['composant']['customFields'],
|
||||
fn (array $f) => $f['name'] === 'Serial',
|
||||
fn (array $f) => 'Serial' === $f['name'],
|
||||
);
|
||||
$this->assertCount(1, $normalFields);
|
||||
}
|
||||
@@ -65,8 +68,8 @@ class MachineContextCustomFieldTest extends AbstractApiTestCase
|
||||
{
|
||||
$client = $this->createGestionnaireClient();
|
||||
|
||||
$site = $this->createSite('Site B');
|
||||
$modelType = $this->createModelType('Bearing', 'BRG', ModelCategory::PIECE);
|
||||
$site = $this->createSite('Site B');
|
||||
$modelType = $this->createModelType('Bearing', 'BRG', ModelCategory::PIECE);
|
||||
$contextField = $this->createCustomField(
|
||||
name: 'Wear Level',
|
||||
type: 'select',
|
||||
@@ -101,8 +104,8 @@ class MachineContextCustomFieldTest extends AbstractApiTestCase
|
||||
{
|
||||
$client = $this->createGestionnaireClient();
|
||||
|
||||
$site = $this->createSite('Site C');
|
||||
$modelType = $this->createModelType('Pump', 'PMP', ModelCategory::COMPONENT);
|
||||
$site = $this->createSite('Site C');
|
||||
$modelType = $this->createModelType('Pump', 'PMP', ModelCategory::COMPONENT);
|
||||
$contextField = $this->createCustomField(
|
||||
name: 'Flow Rate',
|
||||
type: 'number',
|
||||
@@ -131,8 +134,8 @@ class MachineContextCustomFieldTest extends AbstractApiTestCase
|
||||
{
|
||||
$client = $this->createGestionnaireClient();
|
||||
|
||||
$site = $this->createSite('Site D');
|
||||
$modelType = $this->createModelType('Valve', 'VLV', ModelCategory::COMPONENT);
|
||||
$site = $this->createSite('Site D');
|
||||
$modelType = $this->createModelType('Valve', 'VLV', ModelCategory::COMPONENT);
|
||||
$contextField = $this->createCustomField(
|
||||
name: 'Pressure',
|
||||
type: 'number',
|
||||
@@ -171,8 +174,8 @@ class MachineContextCustomFieldTest extends AbstractApiTestCase
|
||||
{
|
||||
$client = $this->createGestionnaireClient();
|
||||
|
||||
$site = $this->createSite('Site E');
|
||||
$modelType = $this->createModelType('Sensor', 'SNS', ModelCategory::COMPONENT);
|
||||
$site = $this->createSite('Site E');
|
||||
$modelType = $this->createModelType('Sensor', 'SNS', ModelCategory::COMPONENT);
|
||||
$contextField = $this->createCustomField(
|
||||
name: 'Calibration Date',
|
||||
type: 'date',
|
||||
@@ -190,8 +193,8 @@ class MachineContextCustomFieldTest extends AbstractApiTestCase
|
||||
{
|
||||
$client = $this->createGestionnaireClient();
|
||||
|
||||
$site = $this->createSite('Site F');
|
||||
$modelType = $this->createModelType('Motor Clone', 'MOTC', ModelCategory::COMPONENT);
|
||||
$site = $this->createSite('Site F');
|
||||
$modelType = $this->createModelType('Motor Clone', 'MOTC', ModelCategory::COMPONENT);
|
||||
$contextField = $this->createCustomField(
|
||||
name: 'RPM Setting',
|
||||
type: 'number',
|
||||
@@ -225,4 +228,84 @@ class MachineContextCustomFieldTest extends AbstractApiTestCase
|
||||
$this->assertCount(1, $clonedLink['contextCustomFieldValues']);
|
||||
$this->assertSame('3000', $clonedLink['contextCustomFieldValues'][0]['value']);
|
||||
}
|
||||
|
||||
public function testCloneMachineCopiesPieceContextFieldValues(): void
|
||||
{
|
||||
$client = $this->createGestionnaireClient();
|
||||
|
||||
$site = $this->createSite('Site G');
|
||||
$modelType = $this->createModelType('Bearing Clone', 'BRGC', ModelCategory::PIECE);
|
||||
$contextField = $this->createCustomField(
|
||||
name: 'Wear Level',
|
||||
type: 'text',
|
||||
typePiece: $modelType,
|
||||
machineContextOnly: true,
|
||||
);
|
||||
|
||||
$source = $this->createMachine('Source Piece Machine', $site);
|
||||
$piece = $this->createPiece('Bearing C', 'BRGC-001', $modelType);
|
||||
$link = $this->createMachinePieceLink($source, $piece);
|
||||
|
||||
$this->createCustomFieldValue(
|
||||
customField: $contextField,
|
||||
value: 'Fair',
|
||||
piece: $piece,
|
||||
machinePieceLink: $link,
|
||||
);
|
||||
|
||||
$response = $client->request('POST', '/api/machines/'.$source->getId().'/clone', [
|
||||
'json' => [
|
||||
'name' => 'Cloned Piece Machine',
|
||||
'siteId' => $site->getId(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertResponseStatusCodeSame(201);
|
||||
$data = $response->toArray();
|
||||
|
||||
$clonedLink = $data['pieceLinks'][0] ?? null;
|
||||
$this->assertNotNull($clonedLink, 'Clone should expose at least one pieceLink');
|
||||
$this->assertCount(1, $clonedLink['contextCustomFieldValues']);
|
||||
$this->assertSame('Fair', $clonedLink['contextCustomFieldValues'][0]['value']);
|
||||
}
|
||||
|
||||
public function testCloneMachineLeavesSourceContextFieldValuesIntact(): void
|
||||
{
|
||||
$client = $this->createGestionnaireClient();
|
||||
|
||||
$site = $this->createSite('Site H');
|
||||
$modelType = $this->createModelType('Motor Source', 'MOTS', ModelCategory::COMPONENT);
|
||||
$contextField = $this->createCustomField(
|
||||
name: 'RPM',
|
||||
type: 'number',
|
||||
typeComposant: $modelType,
|
||||
machineContextOnly: true,
|
||||
);
|
||||
|
||||
$source = $this->createMachine('Original Machine', $site);
|
||||
$composant = $this->createComposant('Motor S', 'MOTS-001', $modelType);
|
||||
$link = $this->createMachineComponentLink($source, $composant);
|
||||
|
||||
$this->createCustomFieldValue(
|
||||
customField: $contextField,
|
||||
value: '1500',
|
||||
composant: $composant,
|
||||
machineComponentLink: $link,
|
||||
);
|
||||
|
||||
$client->request('POST', '/api/machines/'.$source->getId().'/clone', [
|
||||
'json' => [
|
||||
'name' => 'Clone Machine',
|
||||
'siteId' => $site->getId(),
|
||||
],
|
||||
]);
|
||||
$this->assertResponseStatusCodeSame(201);
|
||||
|
||||
// Source must still expose its original context field value
|
||||
$sourceData = $client->request('GET', '/api/machines/'.$source->getId().'/structure')->toArray();
|
||||
$sourceLink = $sourceData['componentLinks'][0] ?? null;
|
||||
$this->assertNotNull($sourceLink, 'Source machine should still expose its component link');
|
||||
$this->assertCount(1, $sourceLink['contextCustomFieldValues']);
|
||||
$this->assertSame('1500', $sourceLink['contextCustomFieldValues'][0]['value']);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user