Add tests to cover new additions

This commit is contained in:
barisgit 2025-08-03 18:58:31 +02:00 committed by Jan Böhmer
parent d0f2422e0d
commit 7c1ab6460d
3 changed files with 847 additions and 0 deletions

View file

@ -22,9 +22,12 @@ declare(strict_types=1);
*/
namespace App\Tests\Services\ImportExportSystem;
use App\Entity\Parts\Part;
use App\Entity\Parts\Supplier;
use App\Entity\ProjectSystem\Project;
use App\Entity\ProjectSystem\ProjectBOMEntry;
use App\Services\ImportExportSystem\BOMImporter;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\File\File;
@ -36,11 +39,17 @@ class BOMImporterTest extends WebTestCase
*/
protected $service;
/**
* @var EntityManagerInterface
*/
protected $entityManager;
protected function setUp(): void
{
//Get a service instance.
self::bootKernel();
$this->service = self::getContainer()->get(BOMImporter::class);
$this->entityManager = self::getContainer()->get(EntityManagerInterface::class);
}
public function testImportFileIntoProject(): void
@ -119,4 +128,489 @@ class BOMImporterTest extends WebTestCase
$this->service->stringToBOMEntries($input, ['type' => 'kicad_pcbnew']);
}
public function testDetectFields(): void
{
$input = <<<CSV
"Reference","Value","Footprint","Quantity","MPN","Manufacturer","LCSC SPN","Mouser SPN"
CSV;
$fields = $this->service->detectFields($input);
$this->assertIsArray($fields);
$this->assertCount(8, $fields);
$this->assertContains('Reference', $fields);
$this->assertContains('Value', $fields);
$this->assertContains('Footprint', $fields);
$this->assertContains('Quantity', $fields);
$this->assertContains('MPN', $fields);
$this->assertContains('Manufacturer', $fields);
$this->assertContains('LCSC SPN', $fields);
$this->assertContains('Mouser SPN', $fields);
}
public function testDetectFieldsWithQuotes(): void
{
$input = <<<CSV
"Reference","Value","Footprint","Quantity","MPN","Manufacturer","LCSC SPN","Mouser SPN"
CSV;
$fields = $this->service->detectFields($input);
$this->assertIsArray($fields);
$this->assertCount(8, $fields);
$this->assertEquals('Reference', $fields[0]);
$this->assertEquals('Value', $fields[1]);
}
public function testDetectFieldsWithSemicolon(): void
{
$input = <<<CSV
"Reference";"Value";"Footprint";"Quantity";"MPN";"Manufacturer";"LCSC SPN";"Mouser SPN"
CSV;
$fields = $this->service->detectFields($input, ';');
$this->assertIsArray($fields);
$this->assertCount(8, $fields);
$this->assertEquals('Reference', $fields[0]);
$this->assertEquals('Value', $fields[1]);
}
public function testGetAvailableFieldTargets(): void
{
$targets = $this->service->getAvailableFieldTargets();
$this->assertIsArray($targets);
$this->assertArrayHasKey('Designator', $targets);
$this->assertArrayHasKey('Quantity', $targets);
$this->assertArrayHasKey('Value', $targets);
$this->assertArrayHasKey('Package', $targets);
$this->assertArrayHasKey('MPN', $targets);
$this->assertArrayHasKey('Manufacturer', $targets);
$this->assertArrayHasKey('Part-DB ID', $targets);
$this->assertArrayHasKey('Comment', $targets);
// Check structure of a target
$this->assertArrayHasKey('label', $targets['Designator']);
$this->assertArrayHasKey('description', $targets['Designator']);
$this->assertArrayHasKey('required', $targets['Designator']);
$this->assertArrayHasKey('multiple', $targets['Designator']);
$this->assertTrue($targets['Designator']['required']);
$this->assertTrue($targets['Quantity']['required']);
$this->assertFalse($targets['Value']['required']);
}
public function testGetAvailableFieldTargetsWithSuppliers(): void
{
// Create test suppliers
$supplier1 = new Supplier();
$supplier1->setName('LCSC');
$supplier2 = new Supplier();
$supplier2->setName('Mouser');
$this->entityManager->persist($supplier1);
$this->entityManager->persist($supplier2);
$this->entityManager->flush();
$targets = $this->service->getAvailableFieldTargets();
$this->assertArrayHasKey('LCSC SPN', $targets);
$this->assertArrayHasKey('Mouser SPN', $targets);
$this->assertEquals('LCSC SPN', $targets['LCSC SPN']['label']);
$this->assertEquals('Mouser SPN', $targets['Mouser SPN']['label']);
$this->assertFalse($targets['LCSC SPN']['required']);
$this->assertTrue($targets['LCSC SPN']['multiple']);
// Clean up
$this->entityManager->remove($supplier1);
$this->entityManager->remove($supplier2);
$this->entityManager->flush();
}
public function testGetSuggestedFieldMapping(): void
{
$detected_fields = [
'Reference',
'Value',
'Footprint',
'Quantity',
'MPN',
'Manufacturer',
'LCSC',
'Mouser',
'Part-DB ID',
'Comment'
];
$suggestions = $this->service->getSuggestedFieldMapping($detected_fields);
$this->assertIsArray($suggestions);
$this->assertEquals('Designator', $suggestions['Reference']);
$this->assertEquals('Value', $suggestions['Value']);
$this->assertEquals('Package', $suggestions['Footprint']);
$this->assertEquals('Quantity', $suggestions['Quantity']);
$this->assertEquals('MPN', $suggestions['MPN']);
$this->assertEquals('Manufacturer', $suggestions['Manufacturer']);
$this->assertEquals('Part-DB ID', $suggestions['Part-DB ID']);
$this->assertEquals('Comment', $suggestions['Comment']);
}
public function testGetSuggestedFieldMappingWithSuppliers(): void
{
// Create test suppliers
$supplier1 = new Supplier();
$supplier1->setName('LCSC');
$supplier2 = new Supplier();
$supplier2->setName('Mouser');
$this->entityManager->persist($supplier1);
$this->entityManager->persist($supplier2);
$this->entityManager->flush();
$detected_fields = [
'Reference',
'LCSC',
'Mouser',
'lcsc_part',
'mouser_spn'
];
$suggestions = $this->service->getSuggestedFieldMapping($detected_fields);
$this->assertIsArray($suggestions);
$this->assertEquals('Designator', $suggestions['Reference']);
// Note: The exact mapping depends on the pattern matching logic
// We just check that supplier fields are mapped to something
$this->assertArrayHasKey('LCSC', $suggestions);
$this->assertArrayHasKey('Mouser', $suggestions);
$this->assertArrayHasKey('lcsc_part', $suggestions);
$this->assertArrayHasKey('mouser_spn', $suggestions);
// Clean up
$this->entityManager->remove($supplier1);
$this->entityManager->remove($supplier2);
$this->entityManager->flush();
}
public function testValidateFieldMappingValid(): void
{
$field_mapping = [
'Reference' => 'Designator',
'Quantity' => 'Quantity',
'Value' => 'Value'
];
$detected_fields = ['Reference', 'Quantity', 'Value', 'MPN'];
$result = $this->service->validateFieldMapping($field_mapping, $detected_fields);
$this->assertIsArray($result);
$this->assertArrayHasKey('errors', $result);
$this->assertArrayHasKey('warnings', $result);
$this->assertArrayHasKey('is_valid', $result);
$this->assertTrue($result['is_valid']);
$this->assertEmpty($result['errors']);
$this->assertNotEmpty($result['warnings']); // Should warn about unmapped MPN
}
public function testValidateFieldMappingMissingRequired(): void
{
$field_mapping = [
'Value' => 'Value',
'MPN' => 'MPN'
];
$detected_fields = ['Value', 'MPN'];
$result = $this->service->validateFieldMapping($field_mapping, $detected_fields);
$this->assertFalse($result['is_valid']);
$this->assertNotEmpty($result['errors']);
$this->assertContains("Required field 'Designator' is not mapped from any CSV column.", $result['errors']);
$this->assertContains("Required field 'Quantity' is not mapped from any CSV column.", $result['errors']);
}
public function testValidateFieldMappingInvalidTarget(): void
{
$field_mapping = [
'Reference' => 'Designator',
'Quantity' => 'Quantity',
'Value' => 'InvalidTarget'
];
$detected_fields = ['Reference', 'Quantity', 'Value'];
$result = $this->service->validateFieldMapping($field_mapping, $detected_fields);
$this->assertFalse($result['is_valid']);
$this->assertNotEmpty($result['errors']);
$this->assertContains("Invalid target field 'InvalidTarget' for CSV field 'Value'.", $result['errors']);
}
public function testStringToBOMEntriesKiCADSchematic(): void
{
$input = <<<CSV
"Reference","Value","Footprint","Quantity","MPN","Manufacturer","LCSC SPN","Mouser SPN"
"R1,R2","10k","R_0805_2012Metric",2,"CRCW080510K0FKEA","Vishay","C123456","123-M10K"
"C1","100nF","C_0805_2012Metric",1,"CL21A104KOCLRNC","Samsung","C789012","80-CL21A104KOCLRNC"
CSV;
$field_mapping = [
'Reference' => 'Designator',
'Value' => 'Value',
'Footprint' => 'Package',
'Quantity' => 'Quantity',
'MPN' => 'MPN',
'Manufacturer' => 'Manufacturer',
'LCSC SPN' => 'LCSC SPN',
'Mouser SPN' => 'Mouser SPN'
];
$bom_entries = $this->service->stringToBOMEntries($input, [
'type' => 'kicad_schematic',
'field_mapping' => $field_mapping,
'delimiter' => ','
]);
$this->assertContainsOnlyInstancesOf(ProjectBOMEntry::class, $bom_entries);
$this->assertCount(2, $bom_entries);
// Check first entry
$this->assertEquals('R1,R2', $bom_entries[0]->getMountnames());
$this->assertEquals(2.0, $bom_entries[0]->getQuantity());
$this->assertEquals('CRCW080510K0FKEA (R_0805_2012Metric)', $bom_entries[0]->getName());
$this->assertStringContainsString('Value: 10k', $bom_entries[0]->getComment());
$this->assertStringContainsString('MPN: CRCW080510K0FKEA', $bom_entries[0]->getComment());
$this->assertStringContainsString('Manf: Vishay', $bom_entries[0]->getComment());
// Check second entry
$this->assertEquals('C1', $bom_entries[1]->getMountnames());
$this->assertEquals(1.0, $bom_entries[1]->getQuantity());
}
public function testStringToBOMEntriesKiCADSchematicWithPriority(): void
{
$input = <<<CSV
"Reference","Value","MPN1","MPN2","Quantity"
"R1,R2","10k","CRCW080510K0FKEA","","2"
"C1","100nF","","CL21A104KOCLRNC","1"
CSV;
$field_mapping = [
'Reference' => 'Designator',
'Value' => 'Value',
'MPN1' => 'MPN',
'MPN2' => 'MPN',
'Quantity' => 'Quantity'
];
$field_priorities = [
'MPN1' => 1,
'MPN2' => 2
];
$bom_entries = $this->service->stringToBOMEntries($input, [
'type' => 'kicad_schematic',
'field_mapping' => $field_mapping,
'field_priorities' => $field_priorities,
'delimiter' => ','
]);
$this->assertContainsOnlyInstancesOf(ProjectBOMEntry::class, $bom_entries);
$this->assertCount(2, $bom_entries);
// First entry should use MPN1 (higher priority)
$this->assertEquals('CRCW080510K0FKEA', $bom_entries[0]->getName());
// Second entry should use MPN2 (MPN1 is empty)
$this->assertEquals('CL21A104KOCLRNC', $bom_entries[1]->getName());
}
public function testStringToBOMEntriesKiCADSchematicWithPartDBID(): void
{
// Create a test part with required fields
$part = new Part();
$part->setName('Test Part');
$part->setCategory($this->getDefaultCategory($this->entityManager));
$this->entityManager->persist($part);
$this->entityManager->flush();
$input = <<<CSV
"Reference","Value","Part-DB ID","Quantity"
"R1,R2","10k","{$part->getID()}","2"
CSV;
$field_mapping = [
'Reference' => 'Designator',
'Value' => 'Value',
'Part-DB ID' => 'Part-DB ID',
'Quantity' => 'Quantity'
];
$bom_entries = $this->service->stringToBOMEntries($input, [
'type' => 'kicad_schematic',
'field_mapping' => $field_mapping,
'delimiter' => ','
]);
$this->assertContainsOnlyInstancesOf(ProjectBOMEntry::class, $bom_entries);
$this->assertCount(1, $bom_entries);
$this->assertEquals('Test Part', $bom_entries[0]->getName());
$this->assertSame($part, $bom_entries[0]->getPart());
$this->assertStringContainsString("Part-DB ID: {$part->getID()}", $bom_entries[0]->getComment());
// Clean up
$this->entityManager->remove($part);
$this->entityManager->flush();
}
public function testStringToBOMEntriesKiCADSchematicWithInvalidPartDBID(): void
{
$input = <<<CSV
"Reference","Value","Part-DB ID","Quantity"
"R1,R2","10k","99999","2"
CSV;
$field_mapping = [
'Reference' => 'Designator',
'Value' => 'Value',
'Part-DB ID' => 'Part-DB ID',
'Quantity' => 'Quantity'
];
$bom_entries = $this->service->stringToBOMEntries($input, [
'type' => 'kicad_schematic',
'field_mapping' => $field_mapping,
'delimiter' => ','
]);
$this->assertContainsOnlyInstancesOf(ProjectBOMEntry::class, $bom_entries);
$this->assertCount(1, $bom_entries);
$this->assertEquals('10k', $bom_entries[0]->getName()); // Should use Value as name
$this->assertNull($bom_entries[0]->getPart()); // Should not link to part
$this->assertStringContainsString("Part-DB ID: 99999 (NOT FOUND)", $bom_entries[0]->getComment());
}
public function testStringToBOMEntriesKiCADSchematicMergeDuplicates(): void
{
$input = <<<CSV
"Reference","Value","MPN","Quantity"
"R1","10k","CRCW080510K0FKEA","1"
"R2","10k","CRCW080510K0FKEA","1"
CSV;
$field_mapping = [
'Reference' => 'Designator',
'Value' => 'Value',
'MPN' => 'MPN',
'Quantity' => 'Quantity'
];
$bom_entries = $this->service->stringToBOMEntries($input, [
'type' => 'kicad_schematic',
'field_mapping' => $field_mapping,
'delimiter' => ','
]);
$this->assertContainsOnlyInstancesOf(ProjectBOMEntry::class, $bom_entries);
$this->assertCount(1, $bom_entries); // Should merge into one entry
$this->assertEquals('R1,R2', $bom_entries[0]->getMountnames());
$this->assertEquals(2.0, $bom_entries[0]->getQuantity());
$this->assertEquals('CRCW080510K0FKEA', $bom_entries[0]->getName());
}
public function testStringToBOMEntriesKiCADSchematicMissingRequired(): void
{
$input = <<<CSV
"Value","MPN"
"10k","CRCW080510K0FKEA"
CSV;
$field_mapping = [
'Value' => 'Value',
'MPN' => 'MPN'
];
$this->expectException(\UnexpectedValueException::class);
$this->expectExceptionMessage('Required field "Designator" is missing or empty');
$this->service->stringToBOMEntries($input, [
'type' => 'kicad_schematic',
'field_mapping' => $field_mapping,
'delimiter' => ','
]);
}
public function testStringToBOMEntriesKiCADSchematicQuantityMismatch(): void
{
$input = <<<CSV
"Reference","Value","Quantity"
"R1,R2,R3","10k","2"
CSV;
$field_mapping = [
'Reference' => 'Designator',
'Value' => 'Value',
'Quantity' => 'Quantity'
];
$this->expectException(\UnexpectedValueException::class);
$this->expectExceptionMessage('Mismatch between quantity and component references');
$this->service->stringToBOMEntries($input, [
'type' => 'kicad_schematic',
'field_mapping' => $field_mapping,
'delimiter' => ','
]);
}
public function testStringToBOMEntriesKiCADSchematicWithBOM(): void
{
// Test with BOM (Byte Order Mark)
$input = "\xEF\xBB\xBF" . <<<CSV
"Reference","Value","Quantity"
"R1,R2","10k","2"
CSV;
$field_mapping = [
'Reference' => 'Designator',
'Value' => 'Value',
'Quantity' => 'Quantity'
];
$bom_entries = $this->service->stringToBOMEntries($input, [
'type' => 'kicad_schematic',
'field_mapping' => $field_mapping,
'delimiter' => ','
]);
$this->assertContainsOnlyInstancesOf(ProjectBOMEntry::class, $bom_entries);
$this->assertCount(1, $bom_entries);
$this->assertEquals('R1,R2', $bom_entries[0]->getMountnames());
}
private function getDefaultCategory(EntityManagerInterface $entityManager)
{
// Get the first available category or create a default one
$categoryRepo = $entityManager->getRepository(\App\Entity\Parts\Category::class);
$categories = $categoryRepo->findAll();
if (empty($categories)) {
// Create a default category if none exists
$category = new \App\Entity\Parts\Category();
$category->setName('Default Category');
$entityManager->persist($category);
$entityManager->flush();
return $category;
}
return $categories[0];
}
}