'151886f9274e9ee9d1de5c9cd2b654214064272ac96579c43e86b5d40de43c97', 'FkReference.php' => '8d056106e3800903ef647127e527c8dec4d82bec5020504906576bf6d5ec0ba8', 'TaxRateFkResolver.php' => 'e86d77f660dfb1e25ca61569f5f3c4e4d1f9d6937201346e369d1341c98dea28', ]; $checks = 0; $check = static function (bool $ok, string $label) use (&$checks): void { if (!$ok) { throw new RuntimeException('Failed assertion: '.$label); } ++$checks; }; $check(InstalledVersions::getPrettyVersion('doctrine/dbal') === '4.4.5', 'Pinned original DBAL'); foreach ($hashes as $name => $hash) { $check(hash_file('sha256', $sourceDirectory.'/'.$name) === $hash, 'Unmodified released '.$name); require_once $sourceDirectory.'/'.$name; } $check(realpath((new ReflectionClass(TaxRateFkResolver::class))->getFileName()) === realpath($sourceDirectory.'/TaxRateFkResolver.php'), 'Actual released resolver loaded'); $check(TaxRateFkResolver::getName() === 'tax.tax_rate', 'Actual resolver name'); $ids = ['A' => str_repeat('a', 32), 'B' => str_repeat('b', 32), 'C' => str_repeat('c', 32)]; $cases = [ ['unique_rate', [['A', 20]], [[20, false]], ['A']], ['duplicate_rate', [['A', 20], ['B', 20]], [[20, false]], [null]], ['missing_rate', [], [[20, false]], [null]], ['same_numeric_rate', [['A', 20], ['B', 20.0]], [[20, false]], [null]], ['other_rate_duplicate', [['A', 20], ['B', 10], ['C', 10]], [[20, false]], ['A']], ['mixed_unique_and_ambiguous', [['A', 20], ['B', 10], ['C', 10]], [[20, false], [10, false]], ['A', null]], ['null_on_missing_true', [['A', 20], ['B', 20]], [[20, true]], [null]], ['reverse_insertion', [['B', 20], ['A', 20]], [[20, false]], [null]], ['unique_b_is_distinct', [['B', 20]], [[20, false]], ['B']], ['numeric_string', [['A', 20]], [['20.0', false]], ['A']], ['zero_fixture_rate', [['C', 0]], [[0, false]], ['C']], ['empty_reference_list', [['A', 20]], [], []], ['two_fresh_references', [['A', 20]], [[20, false], [20, false]], ['A', 'A']], ['different_rate_only', [['B', 10]], [[20, false]], [null]], ['new_duplicate_between_attempts', [['A', 20]], [[20, false]], ['A']], ]; $observations = []; $nativeCalls = 0; foreach ($cases as [$name, $rows, $inputs, $expected]) { $connection = DriverManager::getConnection(['driver' => 'pdo_sqlite', 'memory' => true]); $connection->executeStatement('CREATE TABLE tax (id BLOB NOT NULL, tax_rate NUMERIC NOT NULL)'); foreach ($rows as [$alias, $rate]) { $connection->executeStatement('INSERT INTO tax(id,tax_rate) VALUES (X\''.$ids[$alias].'\', ?)', [$rate]); } $makeReferences = static function () use ($inputs): array { $references = []; foreach ($inputs as $index => [$value, $nullable]) { $references[] = new FkReference('/import-products/payload/'.$index.'/taxId', 'tax', 'taxId', $value, $nullable); } return $references; }; $references = $makeReferences(); $resolver = new TaxRateFkResolver($connection); $result = $resolver->resolve($references); ++$nativeCalls; $resolved = array_map(static fn (FkReference $reference): ?string => $reference->resolved, $result); $wanted = array_map(static fn (?string $alias): ?string => $alias === null ? null : $ids[$alias], $expected); $check($resolved === $wanted, $name.' actual native resolution'); $check(count($result) === count($inputs), $name.' reference count preserved'); foreach ($result as $index => $reference) { $check($reference === $references[$index], $name.' native mutates the supplied reference'); } $observation = ['case' => $name, 'fixtureRows' => $rows, 'referenceInputs' => $inputs, 'resolved' => $resolved]; if ($name === 'new_duplicate_between_attempts') { $connection->executeStatement('INSERT INTO tax(id,tax_rate) VALUES (X\''.$ids['B'].'\', 20)'); // New objects are essential: the direct resolver does not clear an old resolved property. $freshReferences = $makeReferences(); $second = $resolver->resolve($freshReferences); ++$nativeCalls; $secondResolved = array_map(static fn (FkReference $reference): ?string => $reference->resolved, $second); $check($secondResolved === [null], 'New duplicate blocks the fresh second attempt'); $check($references[0]->resolved === $ids['A'], 'Old first-attempt observation remains historical'); $observation['secondAttemptWithFreshReference'] = $secondResolved; $observation['firstAttemptReferenceStillResolved'] = $references[0]->resolved; } $check((int) $connection->fetchOne('SELECT COUNT(*) FROM tax') === count($rows) + ($name === 'new_duplicate_between_attempts' ? 1 : 0), $name.' no resolver row mutation'); $observations[] = $observation; $connection->close(); } $check($nativeCalls === 16, 'Sixteen actual native resolve calls'); $check(count($observations) === 15, 'Fifteen observed cases'); echo json_encode([ 'shopwareRelease' => '6.7.15.1', 'dbalVersion' => InstalledVersions::getPrettyVersion('doctrine/dbal'), 'phpVersion' => PHP_VERSION, 'driver' => 'pdo_sqlite', 'sqliteVersion' => SQLite3::version()['versionString'], 'checksPassed' => $checks, 'cases' => count($observations), 'nativeResolverCalls' => $nativeCalls, 'observations' => $observations, 'shopwareApiCalls' => 0, 'realProductsChanged' => 0, 'shopwareServerValidated' => false, 'mysqlCompatibilityCertified' => false, 'taxLegalAdvice' => false, ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR).PHP_EOL;