. */ namespace App\Tests\Services\ImportExportSystem; use App\Entity\Parts\Part; use App\Services\ImportExportSystem\BOMValidationService; use Doctrine\ORM\EntityManagerInterface; use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Contracts\Translation\TranslatorInterface; /** * @see \App\Services\ImportExportSystem\BOMValidationService */ class BOMValidationServiceTest extends WebTestCase { private BOMValidationService $validationService; private EntityManagerInterface $entityManager; private TranslatorInterface $translator; protected function setUp(): void { self::bootKernel(); $this->entityManager = self::getContainer()->get(EntityManagerInterface::class); $this->translator = self::getContainer()->get(TranslatorInterface::class); $this->validationService = new BOMValidationService($this->entityManager, $this->translator); } public function testValidateBOMEntryWithValidData(): void { $entry = [ 'Designator' => 'R1,C2,R3', 'Quantity' => '3', 'MPN' => 'RES-10K', 'Package' => '0603', 'Value' => '10k', ]; $result = $this->validationService->validateBOMEntry($entry, 1); $this->assertTrue($result['is_valid']); $this->assertEmpty($result['errors']); $this->assertEquals(1, $result['line_number']); } public function testValidateBOMEntryWithMissingRequiredFields(): void { $entry = [ 'MPN' => 'RES-10K', 'Package' => '0603', ]; $result = $this->validationService->validateBOMEntry($entry, 1); $this->assertFalse($result['is_valid']); $this->assertCount(2, $result['errors']); $this->assertStringContainsString('Designator', (string) $result['errors'][0]); $this->assertStringContainsString('Quantity', (string) $result['errors'][1]); } public function testValidateBOMEntryWithQuantityMismatch(): void { $entry = [ 'Designator' => 'R1,C2,R3,C4', 'Quantity' => '3', 'MPN' => 'RES-10K', ]; $result = $this->validationService->validateBOMEntry($entry, 1); $this->assertFalse($result['is_valid']); $this->assertCount(1, $result['errors']); $this->assertStringContainsString('Mismatch between quantity and component references', (string) $result['errors'][0]); } public function testValidateBOMEntryWithInvalidQuantity(): void { $entry = [ 'Designator' => 'R1', 'Quantity' => 'abc', 'MPN' => 'RES-10K', ]; $result = $this->validationService->validateBOMEntry($entry, 1); $this->assertFalse($result['is_valid']); $this->assertGreaterThanOrEqual(1, count($result['errors'])); $this->assertStringContainsString('not a valid number', implode(' ', array_map('strval', $result['errors']))); } public function testValidateBOMEntryWithZeroQuantity(): void { $entry = [ 'Designator' => 'R1', 'Quantity' => '0', 'MPN' => 'RES-10K', ]; $result = $this->validationService->validateBOMEntry($entry, 1); $this->assertFalse($result['is_valid']); $this->assertGreaterThanOrEqual(1, count($result['errors'])); $this->assertStringContainsString('must be greater than 0', implode(' ', array_map('strval', $result['errors']))); } public function testValidateBOMEntryWithDuplicateDesignators(): void { $entry = [ 'Designator' => 'R1,R1,C2', 'Quantity' => '3', 'MPN' => 'RES-10K', ]; $result = $this->validationService->validateBOMEntry($entry, 1); $this->assertFalse($result['is_valid']); $this->assertCount(1, $result['errors']); $this->assertStringContainsString('Duplicate component references', (string) $result['errors'][0]); } public function testValidateBOMEntryWithInvalidDesignatorFormat(): void { $entry = [ 'Designator' => 'R1,invalid,C2', 'Quantity' => '3', 'MPN' => 'RES-10K', ]; $result = $this->validationService->validateBOMEntry($entry, 1); $this->assertTrue($result['is_valid']); // Warnings don't make it invalid $this->assertCount(1, $result['warnings']); $this->assertStringContainsString('unusual format', (string) $result['warnings'][0]); } public function testValidateBOMEntryWithEmptyDesignator(): void { $entry = [ 'Designator' => '', 'Quantity' => '1', 'MPN' => 'RES-10K', ]; $result = $this->validationService->validateBOMEntry($entry, 1); $this->assertFalse($result['is_valid']); $this->assertGreaterThanOrEqual(1, count($result['errors'])); $this->assertStringContainsString('Required field "Designator" is missing or empty', implode(' ', array_map('strval', $result['errors']))); } public function testValidateBOMEntryWithInvalidPartDBID(): void { $entry = [ 'Designator' => 'R1', 'Quantity' => '1', 'MPN' => 'RES-10K', 'Part-DB ID' => 'abc', ]; $result = $this->validationService->validateBOMEntry($entry, 1); $this->assertFalse($result['is_valid']); $this->assertGreaterThanOrEqual(1, count($result['errors'])); $this->assertStringContainsString('not a valid number', implode(' ', array_map('strval', $result['errors']))); } public function testValidateBOMEntryWithNonExistentPartDBID(): void { $entry = [ 'Designator' => 'R1', 'Quantity' => '1', 'MPN' => 'RES-10K', 'Part-DB ID' => '999999', // Use very high ID that doesn't exist ]; $result = $this->validationService->validateBOMEntry($entry, 1); $this->assertTrue($result['is_valid']); // Warnings don't make it invalid $this->assertCount(1, $result['warnings']); $this->assertStringContainsString('not found in database', (string) $result['warnings'][0]); } public function testValidateBOMEntryWithNoComponentName(): void { $entry = [ 'Designator' => 'R1', 'Quantity' => '1', 'Package' => '0603', ]; $result = $this->validationService->validateBOMEntry($entry, 1); $this->assertTrue($result['is_valid']); // Warnings don't make it invalid $this->assertCount(1, $result['warnings']); $this->assertStringContainsString('No component name/designation', (string) $result['warnings'][0]); } public function testValidateBOMEntryWithLongPackageName(): void { $entry = [ 'Designator' => 'R1', 'Quantity' => '1', 'MPN' => 'RES-10K', 'Package' => str_repeat('A', 150), // Very long package name ]; $result = $this->validationService->validateBOMEntry($entry, 1); $this->assertTrue($result['is_valid']); // Warnings don't make it invalid $this->assertCount(1, $result['warnings']); $this->assertStringContainsString('unusually long', (string) $result['warnings'][0]); } public function testValidateBOMEntryWithLibraryPrefix(): void { $entry = [ 'Designator' => 'R1', 'Quantity' => '1', 'MPN' => 'RES-10K', 'Package' => 'Resistor_SMD:R_0603_1608Metric', ]; $result = $this->validationService->validateBOMEntry($entry, 1); $this->assertTrue($result['is_valid']); $this->assertCount(1, $result['info']); $this->assertStringContainsString('library prefix', $result['info'][0]); } public function testValidateBOMEntriesWithMultipleEntries(): void { $entries = [ [ 'Designator' => 'R1', 'Quantity' => '1', 'MPN' => 'RES-10K', ], [ 'Designator' => 'C1,C2', 'Quantity' => '2', 'MPN' => 'CAP-100nF', ], ]; $result = $this->validationService->validateBOMEntries($entries); $this->assertTrue($result['is_valid']); $this->assertEquals(2, $result['total_entries']); $this->assertEquals(2, $result['valid_entries']); $this->assertEquals(0, $result['invalid_entries']); $this->assertCount(2, $result['line_results']); } public function testValidateBOMEntriesWithMixedResults(): void { $entries = [ [ 'Designator' => 'R1', 'Quantity' => '1', 'MPN' => 'RES-10K', ], [ 'Designator' => 'C1,C2', 'Quantity' => '1', // Mismatch 'MPN' => 'CAP-100nF', ], ]; $result = $this->validationService->validateBOMEntries($entries); $this->assertFalse($result['is_valid']); $this->assertEquals(2, $result['total_entries']); $this->assertEquals(1, $result['valid_entries']); $this->assertEquals(1, $result['invalid_entries']); $this->assertCount(1, $result['errors']); } public function testGetValidationStats(): void { $validation_result = [ 'total_entries' => 10, 'valid_entries' => 8, 'invalid_entries' => 2, 'errors' => ['Error 1', 'Error 2'], 'warnings' => ['Warning 1'], 'info' => ['Info 1', 'Info 2'], ]; $stats = $this->validationService->getValidationStats($validation_result); $this->assertEquals(10, $stats['total_entries']); $this->assertEquals(8, $stats['valid_entries']); $this->assertEquals(2, $stats['invalid_entries']); $this->assertEquals(2, $stats['error_count']); $this->assertEquals(1, $stats['warning_count']); $this->assertEquals(2, $stats['info_count']); $this->assertEquals(80.0, $stats['success_rate']); } public function testGetErrorMessage(): void { $validation_result = [ 'is_valid' => false, 'errors' => ['Error 1', 'Error 2'], 'warnings' => ['Warning 1'], ]; $message = $this->validationService->getErrorMessage($validation_result); $this->assertStringContainsString('Errors:', $message); $this->assertStringContainsString('• Error 1', $message); $this->assertStringContainsString('• Error 2', $message); $this->assertStringContainsString('Warnings:', $message); $this->assertStringContainsString('• Warning 1', $message); } public function testGetErrorMessageWithValidResult(): void { $validation_result = [ 'is_valid' => true, 'errors' => [], 'warnings' => [], ]; $message = $this->validationService->getErrorMessage($validation_result); $this->assertEquals('', $message); } }