From d2f23fe2ab170dcacb1209c99382ea8374fb3754 Mon Sep 17 00:00:00 2001 From: Alex Standiford Date: Sun, 20 Sep 2026 10:24:19 -0400 Subject: [PATCH 1/5] Restore junction schema extension compatibility --- lib/Abstracts/JunctionTable.php | 6 +- lib/Services/TableSchemaService.php | 18 ++-- ...est.php => JunctionSchemaContractTest.php} | 14 +-- ...unctionSchemaOverrideCompatibilityTest.php | 100 ++++++++++++++++++ 4 files changed, 120 insertions(+), 18 deletions(-) rename tests/Integration/{UncachedJunctionSchemaContractTest.php => JunctionSchemaContractTest.php} (92%) create mode 100644 tests/Integration/JunctionSchemaOverrideCompatibilityTest.php diff --git a/lib/Abstracts/JunctionTable.php b/lib/Abstracts/JunctionTable.php index 882821f..843da89 100644 --- a/lib/Abstracts/JunctionTable.php +++ b/lib/Abstracts/JunctionTable.php @@ -104,7 +104,7 @@ public function getUnprefixedName(): string */ public function getLeftColumnName(): string { - return $this->tableSchemaService->getJunctionColumnNameFromTableUncached($this->leftTable); + return $this->tableSchemaService->getJunctionColumnNameFromTable($this->leftTable); } /** @@ -114,7 +114,7 @@ public function getLeftColumnName(): string */ public function getRightColumnName(): string { - return $this->tableSchemaService->getJunctionColumnNameFromTableUncached($this->rightTable); + return $this->tableSchemaService->getJunctionColumnNameFromTable($this->rightTable); } /** @@ -146,7 +146,7 @@ protected function buildForeignKeyFor(string $columnName, TableInterface $primar { try { // Find the corresponding primary column by the column name - $primaryColumn = $this->tableSchemaService->getPrimaryColumnNameForTableUncached($primaryTable); + $primaryColumn = $this->tableSchemaService->getPrimaryColumnNameForTable($primaryTable); }catch(ColumnNotFoundException $e){ $this->logger->logException($e, '', [], LoggerLevel::Emergency); throw $e; diff --git a/lib/Services/TableSchemaService.php b/lib/Services/TableSchemaService.php index a3c9f26..b96c361 100644 --- a/lib/Services/TableSchemaService.php +++ b/lib/Services/TableSchemaService.php @@ -34,11 +34,11 @@ public function getPrimaryColumnsForTable(TableInterface $table): array } /** - * Read the supplied descriptor without consulting or changing shared cache. - * Returns the same primary-column metadata that the cached lookup computes - * after a cache miss. + * Read the supplied descriptor without this service consulting or changing + * its shared cache. Returns the same primary-column metadata that the cached + * lookup computes after a cache miss. * Descriptor failures propagate unchanged. This does not inspect storage. - * Custom descriptors must provide stable, side-effect-free metadata. + * Descriptor callbacks may invoke cached methods or perform other side effects. * * @return Column[] */ @@ -48,8 +48,9 @@ public function getPrimaryColumnsForTableUncached(TableInterface $table): array } /** - * Return the descriptor's sole primary Column without shared cache access. - * Preserves the cached helper's cardinality rule and original Column object. + * Return the descriptor's sole primary Column without this service accessing + * its shared cache. Preserves the cached helper's cardinality rule and original + * Column object. Descriptor callbacks may cross their own cache boundaries. * * @throws ColumnNotFoundException When there is not exactly one primary column. */ @@ -68,8 +69,9 @@ public function getPrimaryColumnNameForTableUncached(TableInterface $table): Col } /** - * Compose the existing junction-column name without shared cache access. - * Descriptor failures propagate unchanged. + * Compose the existing junction-column name without this service accessing + * its shared cache. Descriptor failures propagate unchanged. Descriptor + * callbacks may cross their own cache boundaries. * * @throws ColumnNotFoundException When there is not exactly one primary column. */ diff --git a/tests/Integration/UncachedJunctionSchemaContractTest.php b/tests/Integration/JunctionSchemaContractTest.php similarity index 92% rename from tests/Integration/UncachedJunctionSchemaContractTest.php rename to tests/Integration/JunctionSchemaContractTest.php index e06f86f..0b270e9 100644 --- a/tests/Integration/UncachedJunctionSchemaContractTest.php +++ b/tests/Integration/JunctionSchemaContractTest.php @@ -19,8 +19,8 @@ use PHPUnit\Framework\MockObject\MockObject; use RuntimeException; -/** Wires JunctionTable to TableSchemaService and asserts that no shared-cache method runs. */ -final class UncachedJunctionSchemaContractTest extends TestCase +/** Wires JunctionTable through the established TableSchemaService extension points. */ +final class JunctionSchemaContractTest extends TestCase { private TableSchemaService $schema; @@ -28,13 +28,13 @@ protected function setUp(): void { parent::setUp(); $cache = $this->createMock(CacheableService::class); - foreach (['getWithCache', 'get', 'set', 'delete', 'exists'] as $method) { - $cache->expects(self::never())->method($method); - } + $cache->method('getWithCache')->willReturnCallback( + static fn(string $operation, array $context, callable $callback) => $callback() + ); $this->schema = new TableSchemaService($cache); } - public function testBuiltInJunctionMetadataAvoidsTheSharedCacheBoundary(): void + public function testBuiltInJunctionMetadataPreservesNamesAndIndices(): void { $left = $this->table('program', 'externalKey'); $right = $this->table('distributor', 'id'); @@ -58,7 +58,7 @@ public function testBuiltInJunctionMetadataAvoidsTheSharedCacheBoundary(): void )); } - public function testNestedJunctionMetadataRetainsItsCompoundKeyRefusalWithoutSharedCacheAccess(): void + public function testNestedJunctionMetadataRetainsItsCompoundKeyRefusal(): void { $inner = $this->junction($this->table('program', 'id'), $this->table('distributor', 'id')); $outer = $this->junction($inner, $this->table('account', 'id')); diff --git a/tests/Integration/JunctionSchemaOverrideCompatibilityTest.php b/tests/Integration/JunctionSchemaOverrideCompatibilityTest.php new file mode 100644 index 0000000..4335dcb --- /dev/null +++ b/tests/Integration/JunctionSchemaOverrideCompatibilityTest.php @@ -0,0 +1,100 @@ +table('left'); + $right = $this->table('right'); + $schema = $this->schema(); + $schema->expects(self::once())->method('getJunctionColumnNameFromTable') + ->with(self::identicalTo($side === 'left' ? $left : $right)) + ->willReturn('custom' . ucfirst($side) . 'Id'); + $junction = $this->junction($schema, $left, $right); + + self::assertSame('custom' . ucfirst($side) . 'Id', $side === 'left' + ? $junction->getLeftColumnName() : $junction->getRightColumnName()); + } + + public function testForeignKeysUseTheExistingPrimaryColumnOverride(): void + { + $left = $this->table('left'); + $right = $this->table('right'); + $schema = $this->schema(); + /** @var list $seen */ + $seen = []; + $schema->method('getJunctionColumnNameFromTable')->willReturnCallback( + static fn(TableInterface $table): string => $table === $left ? 'leftLink' : 'rightLink' + ); + $schema->expects(self::exactly(2))->method('getPrimaryColumnNameForTable')->willReturnCallback( + static function (TableInterface $table) use ($left, &$seen): Column { + $seen[] = $table; + return new Column($table === $left ? 'externalLeft' : 'externalRight', 'BIGINT'); + } + ); + + $indices = $this->junction($schema, $left, $right)->getIndices(); + + self::assertCount(2, $seen); + self::assertSame($left, $seen[0]); + self::assertSame($right, $seen[1]); + self::assertSame([ + ['FOREIGN KEY', ['leftLink'], ['REFERENCES lefts(externalLeft)']], + ['FOREIGN KEY', ['rightLink'], ['REFERENCES rights(externalRight)']], + ], array_map(static fn(Index $index): array => [ + $index->getType(), $index->getColumns(), $index->getAttributes(), + ], array_slice($indices, 1))); + } + + /** @return array */ + public static function sides(): array + { + return ['left lookup' => ['left'], 'right lookup' => ['right']]; + } + + /** @return TableSchemaService&MockObject */ + private function schema(): TableSchemaService + { + return $this->getMockBuilder(TableSchemaService::class)->disableOriginalConstructor() + ->onlyMethods(['getJunctionColumnNameFromTable', 'getPrimaryColumnNameForTable'])->getMock(); + } + + private function table(string $name): Table + { + $table = $this->createMock(Table::class); + $table->method('getName')->willReturn($name . 's'); + $table->method('getSingularUnprefixedName')->willReturn($name); + $table->method('getColumns')->willReturn([new Column('id', 'BIGINT', null, 'PRIMARY KEY')]); + $table->method('getIndices')->willReturn([]); + return $table; + } + + private function junction(TableSchemaService $schema, Table $left, Table $right): JunctionTable + { + return new class( + $this->createMock(HasLocalDatabasePrefix::class), $this->createMock(HasGlobalDatabasePrefix::class), + $this->createMock(HasCharsetProvider::class), $this->createMock(HasCollateProvider::class), + $schema, $left, $right, $this->createMock(LoggerStrategy::class) + ) extends JunctionTable { + public function getTableVersion(): string { return '1'; } + public function getSingularUnprefixedName(): string { return 'link'; } + }; + } +} From 04ebc6262d2657ed2d388e23fc3660d7b0a7d6fa Mon Sep 17 00:00:00 2001 From: Alex Standiford Date: Sun, 20 Sep 2026 10:26:20 -0400 Subject: [PATCH 2/5] Format junction compatibility contracts --- .../JunctionSchemaContractTest.php | 21 ++++++++++----- ...unctionSchemaOverrideCompatibilityTest.php | 27 +++++++++++++------ 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/tests/Integration/JunctionSchemaContractTest.php b/tests/Integration/JunctionSchemaContractTest.php index 0b270e9..e8007e5 100644 --- a/tests/Integration/JunctionSchemaContractTest.php +++ b/tests/Integration/JunctionSchemaContractTest.php @@ -29,7 +29,7 @@ protected function setUp(): void parent::setUp(); $cache = $this->createMock(CacheableService::class); $cache->method('getWithCache')->willReturnCallback( - static fn(string $operation, array $context, callable $callback) => $callback() + static fn (string $operation, array $context, callable $callback) => $callback() ); $this->schema = new TableSchemaService($cache); } @@ -47,14 +47,15 @@ public function testBuiltInJunctionMetadataPreservesNamesAndIndices(): void self::assertSame(['programExternalKey', 'distributorId'], $junction->getFieldsForIdentity()); self::assertSame([ ['programExternalKey', 'BIGINT'], ['distributorId', 'BIGINT'], - ], array_map(static fn(Column $column): array => [$column->getName(), $column->getType()], $junction->getColumns())); + ], array_map(static fn (Column $column): array => [$column->getName(), $column->getType()], $junction->getColumns())); self::assertSame([ ['PRIMARY KEY', ['distributorId', 'programExternalKey'], []], ['FOREIGN KEY', ['programExternalKey'], ['REFERENCES global_local_programs(externalKey)']], ['FOREIGN KEY', ['distributorId'], ['REFERENCES global_local_distributors(id)']], - ], array_map(static fn(Index $index): array => [$index->getType(), $index->getColumns(), $index->getAttributes()], $junction->getIndices())); + ], array_map(static fn (Index $index): array => [$index->getType(), $index->getColumns(), $index->getAttributes()], $junction->getIndices())); self::assertSame(['programExternalKey', 'distributorId'], array_map( - static fn(Column $column): string => $column->getName(), $this->schema->getPrimaryColumnsForTableUncached($junction) + static fn (Column $column): string => $column->getName(), + $this->schema->getPrimaryColumnsForTableUncached($junction) )); } @@ -111,9 +112,15 @@ private function table(string $singular, string $identity, bool $withColumns = t private function junction(Table $left, Table $right): JunctionTable { $arguments = [...$this->tableArguments(), $left, $right, $this->createMock(LoggerStrategy::class)]; - return new class(...$arguments) extends JunctionTable { - public function getTableVersion(): string { return '1'; } - public function getSingularUnprefixedName(): string { return 'link'; } + return new class (...$arguments) extends JunctionTable { + public function getTableVersion(): string + { + return '1'; + } + public function getSingularUnprefixedName(): string + { + return 'link'; + } }; } diff --git a/tests/Integration/JunctionSchemaOverrideCompatibilityTest.php b/tests/Integration/JunctionSchemaOverrideCompatibilityTest.php index 4335dcb..613fd13 100644 --- a/tests/Integration/JunctionSchemaOverrideCompatibilityTest.php +++ b/tests/Integration/JunctionSchemaOverrideCompatibilityTest.php @@ -41,7 +41,7 @@ public function testForeignKeysUseTheExistingPrimaryColumnOverride(): void /** @var list $seen */ $seen = []; $schema->method('getJunctionColumnNameFromTable')->willReturnCallback( - static fn(TableInterface $table): string => $table === $left ? 'leftLink' : 'rightLink' + static fn (TableInterface $table): string => $table === $left ? 'leftLink' : 'rightLink' ); $schema->expects(self::exactly(2))->method('getPrimaryColumnNameForTable')->willReturnCallback( static function (TableInterface $table) use ($left, &$seen): Column { @@ -58,7 +58,7 @@ static function (TableInterface $table) use ($left, &$seen): Column { self::assertSame([ ['FOREIGN KEY', ['leftLink'], ['REFERENCES lefts(externalLeft)']], ['FOREIGN KEY', ['rightLink'], ['REFERENCES rights(externalRight)']], - ], array_map(static fn(Index $index): array => [ + ], array_map(static fn (Index $index): array => [ $index->getType(), $index->getColumns(), $index->getAttributes(), ], array_slice($indices, 1))); } @@ -88,13 +88,24 @@ private function table(string $name): Table private function junction(TableSchemaService $schema, Table $left, Table $right): JunctionTable { - return new class( - $this->createMock(HasLocalDatabasePrefix::class), $this->createMock(HasGlobalDatabasePrefix::class), - $this->createMock(HasCharsetProvider::class), $this->createMock(HasCollateProvider::class), - $schema, $left, $right, $this->createMock(LoggerStrategy::class) + return new class ( + $this->createMock(HasLocalDatabasePrefix::class), + $this->createMock(HasGlobalDatabasePrefix::class), + $this->createMock(HasCharsetProvider::class), + $this->createMock(HasCollateProvider::class), + $schema, + $left, + $right, + $this->createMock(LoggerStrategy::class) ) extends JunctionTable { - public function getTableVersion(): string { return '1'; } - public function getSingularUnprefixedName(): string { return 'link'; } + public function getTableVersion(): string + { + return '1'; + } + public function getSingularUnprefixedName(): string + { + return 'link'; + } }; } } From 9427b2da75f408cf09aabfd0a2390118db647ca9 Mon Sep 17 00:00:00 2001 From: Alex Standiford Date: Sun, 20 Sep 2026 10:33:00 -0400 Subject: [PATCH 3/5] Classify junction dispatch proofs as unit contracts --- .../Contracts}/JunctionSchemaContractTest.php | 2 +- .../Contracts}/JunctionSchemaOverrideCompatibilityTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename tests/{Integration => Unit/Contracts}/JunctionSchemaContractTest.php (99%) rename tests/{Integration => Unit/Contracts}/JunctionSchemaOverrideCompatibilityTest.php (98%) diff --git a/tests/Integration/JunctionSchemaContractTest.php b/tests/Unit/Contracts/JunctionSchemaContractTest.php similarity index 99% rename from tests/Integration/JunctionSchemaContractTest.php rename to tests/Unit/Contracts/JunctionSchemaContractTest.php index e8007e5..0134369 100644 --- a/tests/Integration/JunctionSchemaContractTest.php +++ b/tests/Unit/Contracts/JunctionSchemaContractTest.php @@ -1,6 +1,6 @@ Date: Sun, 20 Sep 2026 10:36:32 -0400 Subject: [PATCH 4/5] Remove the empty integration suite entry --- phpunit.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 2653df5..f749950 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -4,9 +4,6 @@ ./tests/Unit - - ./tests/Integration - From e478283d0d5562122d9d034cff2b11da419f66fb Mon Sep 17 00:00:00 2001 From: Alex Standiford Date: Sun, 20 Sep 2026 11:01:02 -0400 Subject: [PATCH 5/5] Exercise junction overrides through real consumer components --- composer.json | 1 + composer.lock | 42 ++- phpunit.xml | 3 + .../JunctionSchemaConsumerFixture.php | 354 ++++++++++++++++++ .../JunctionSchemaConsumerContractTest.php | 38 ++ .../WithDatastoreHandlerMethodsTest.php | 15 - 6 files changed, 437 insertions(+), 16 deletions(-) create mode 100644 tests/Integration/Fixtures/JunctionSchemaConsumerFixture.php create mode 100644 tests/Integration/JunctionSchemaConsumerContractTest.php diff --git a/composer.json b/composer.json index 3021e65..67a7093 100644 --- a/composer.json +++ b/composer.json @@ -32,6 +32,7 @@ "phpnomad/logger": "^1.0" }, "require-dev": { + "phpnomad/event": "^1.0", "phpnomad/tests": "^0.1.0 || ^0.3.0" }, "config": { diff --git a/composer.lock b/composer.lock index b273094..6f66f1c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d7706b5c7ec555b54b39004f3f284d90", + "content-hash": "2fb4b17e17d8c0337f2e3e27e44f5549", "packages": [ { "name": "phpnomad/cache", @@ -1200,6 +1200,46 @@ }, "time": "2022-02-21T01:04:05+00:00" }, + { + "name": "phpnomad/event", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phpnomad/event.git", + "reference": "b418d8fc90af18b7cb37e118795f9dd92ffeb538" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpnomad/event/zipball/b418d8fc90af18b7cb37e118795f9dd92ffeb538", + "reference": "b418d8fc90af18b7cb37e118795f9dd92ffeb538", + "shasum": "" + }, + "require-dev": { + "phpnomad/tests": "^0.1.0 || ^0.3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPNomad\\Events\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Standiford", + "email": "alex@standiford.us" + } + ], + "homepage": "https://github.com/phpnomad/core", + "support": { + "issues": "https://github.com/phpnomad/event/issues", + "source": "https://github.com/phpnomad/event/tree/1.0.1" + }, + "time": "2026-06-12T10:56:48+00:00" + }, { "name": "phpnomad/tests", "version": "0.3.0", diff --git a/phpunit.xml b/phpunit.xml index f749950..2653df5 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -4,6 +4,9 @@ ./tests/Unit + + ./tests/Integration + diff --git a/tests/Integration/Fixtures/JunctionSchemaConsumerFixture.php b/tests/Integration/Fixtures/JunctionSchemaConsumerFixture.php new file mode 100644 index 0000000..294d6dc --- /dev/null +++ b/tests/Integration/Fixtures/JunctionSchemaConsumerFixture.php @@ -0,0 +1,354 @@ +cache = new InMemoryCacheStrategy(); + $cacheable = new CacheableService( + new InMemoryEventStrategy(), + $this->cache, + new AlwaysCachePolicy() + ); + $this->schema = new ConsumerTableSchemaService($cacheable); + $prefixes = new FixturePrefixProvider(); + $encoding = new FixtureEncodingProvider(); + $programs = new ConsumerTable($prefixes, $prefixes, $encoding, $encoding, $this->schema, 'programs', 'program'); + $distributors = new ConsumerTable( + $prefixes, + $prefixes, + $encoding, + $encoding, + $this->schema, + 'distributors', + 'distributor' + ); + $this->junction = new ConsumerJunctionTable( + $prefixes, + $prefixes, + $encoding, + $encoding, + $this->schema, + $programs, + $distributors, + new IgnoringLoggerStrategy() + ); + } + + public function junction(): JunctionTable + { + return $this->junction; + } + + public function schema(): TableSchemaService + { + return $this->schema; + } + + public function cacheEntryCount(): int + { + return $this->cache->entryCount(); + } + } + + final class InMemoryCacheStrategy implements CacheStrategy + { + /** @var array */ + private array $entries = []; + + /** @return mixed */ + public function get(string $key) + { + if (!$this->exists($key)) { + throw new CachedItemNotFoundException($key); + } + + return $this->entries[$key]; + } + + /** @param mixed $value */ + public function set(string $key, $value, ?int $ttl): void + { + $this->entries[$key] = $value; + } + + public function delete(string $key): void + { + unset($this->entries[$key]); + } + + public function exists(string $key): bool + { + return array_key_exists($key, $this->entries); + } + + public function clear(): void + { + $this->entries = []; + } + + public function entryCount(): int + { + return count($this->entries); + } + } + + final class AlwaysCachePolicy implements CachePolicy + { + /** @param array $context */ + public function shouldCache(string $operation, array $context = []): bool + { + return true; + } + + /** @param array $context */ + public function getTtl(array $context = []): ?int + { + return null; + } + + /** @param array $context */ + public function shouldInvalidate(string $operation, array $context = []): bool + { + return false; + } + + /** @param array $context */ + public function getCacheKey(array $context): string + { + return sha1(serialize($context)); + } + } + + final class InMemoryEventStrategy implements EventStrategy + { + /** @var array> */ + private array $listeners = []; + + public function broadcast(Event $event): void + { + $listeners = $this->listeners[$event::getId()] ?? []; + usort( + $listeners, + static fn (array $left, array $right): int => $left['priority'] <=> $right['priority'] + ); + + foreach ($listeners as $listener) { + ($listener['action'])($event); + } + } + + public function attach(string $event, callable $action, ?int $priority = null): void + { + $this->listeners[$event][] = [ + 'action' => $action, + 'priority' => $priority ?? 10, + ]; + } + + public function detach(string $event, callable $action, ?int $priority = null): void + { + $expectedPriority = $priority ?? 10; + $this->listeners[$event] = array_values(array_filter( + $this->listeners[$event] ?? [], + static fn (array $listener): bool => $listener['action'] !== $action + || $listener['priority'] !== $expectedPriority + )); + } + } + + final class FixturePrefixProvider implements HasLocalDatabasePrefix, HasGlobalDatabasePrefix + { + public function getLocalDatabasePrefix(): string + { + return 'local'; + } + + public function getGlobalDatabasePrefix(): string + { + return 'global'; + } + } + + final class FixtureEncodingProvider implements HasCharsetProvider, HasCollateProvider + { + public function getCharset(): ?string + { + return null; + } + + public function getCollation(): ?string + { + return null; + } + } + + final class ConsumerTableSchemaService extends TableSchemaService + { + public function getJunctionColumnNameFromTable(TableInterface $table): string + { + return 'custom' . ucfirst(parent::getJunctionColumnNameFromTable($table)); + } + + public function getPrimaryColumnNameForTable(TableInterface $table): Column + { + foreach ($table->getColumns() as $column) { + if ($column->getName() === 'externalKey') { + return $column; + } + } + + throw new ColumnNotFoundException('The consumer identity column was not found.'); + } + } + + final class ConsumerTable extends Table + { + private string $unprefixedName; + + private string $singularName; + + public function __construct( + HasLocalDatabasePrefix $localPrefixProvider, + HasGlobalDatabasePrefix $globalPrefixProvider, + HasCharsetProvider $charsetProvider, + HasCollateProvider $collateProvider, + TableSchemaService $tableSchemaService, + string $unprefixedName, + string $singularName + ) { + parent::__construct( + $localPrefixProvider, + $globalPrefixProvider, + $charsetProvider, + $collateProvider, + $tableSchemaService + ); + $this->unprefixedName = $unprefixedName; + $this->singularName = $singularName; + } + + public function getAlias(): string + { + return $this->singularName; + } + + public function getTableVersion(): string + { + return '1'; + } + + /** @return list */ + public function getColumns(): array + { + return [ + new Column('id', 'BIGINT', null, 'PRIMARY KEY'), + new Column('externalKey', 'BIGINT'), + ]; + } + + /** @return list */ + public function getIndices(): array + { + return [new Index(['externalKey'], null, 'UNIQUE')]; + } + + public function getUnprefixedName(): string + { + return $this->unprefixedName; + } + + public function getSingularUnprefixedName(): string + { + return $this->singularName; + } + } + + final class ConsumerJunctionTable extends JunctionTable + { + public function getTableVersion(): string + { + return '1'; + } + + public function getSingularUnprefixedName(): string + { + return 'link'; + } + } + + final class IgnoringLoggerStrategy implements LoggerStrategy + { + public function emergency(string $message, array $context = []): void + { + } + + public function alert(string $message, array $context = []): void + { + } + + public function critical(string $message, array $context = []): void + { + } + + public function error(string $message, array $context = []): void + { + } + + public function warning(string $message, array $context = []): void + { + } + + public function notice(string $message, array $context = []): void + { + } + + public function info(string $message, array $context = []): void + { + } + + public function debug(string $message, array $context = []): void + { + } + + /** + * @param array $context + * @return null + */ + public function logException( + Exception $e, + string $message = '', + array $context = [], + string $level = null + ) { + return null; + } + } +} diff --git a/tests/Integration/JunctionSchemaConsumerContractTest.php b/tests/Integration/JunctionSchemaConsumerContractTest.php new file mode 100644 index 0000000..c6665fa --- /dev/null +++ b/tests/Integration/JunctionSchemaConsumerContractTest.php @@ -0,0 +1,38 @@ +junction(); + $schema = $consumer->schema(); + + self::assertSame('global_local_programsDistributors', $junction->getName()); + self::assertSame(['customProgramExternalKey', 'customDistributorExternalKey'], $junction->getFieldsForIdentity()); + self::assertSame([ + ['customProgramExternalKey', 'BIGINT'], ['customDistributorExternalKey', 'BIGINT'], + ], array_map(static fn (Column $column): array => [$column->getName(), $column->getType()], $junction->getColumns())); + self::assertSame([ + ['PRIMARY KEY', ['customDistributorExternalKey', 'customProgramExternalKey'], []], + ['FOREIGN KEY', ['customProgramExternalKey'], ['REFERENCES global_local_programs(externalKey)']], + ['FOREIGN KEY', ['customDistributorExternalKey'], ['REFERENCES global_local_distributors(externalKey)']], + ], array_map(static fn (Index $index): array => [ + $index->getType(), $index->getColumns(), $index->getAttributes(), + ], $junction->getIndices())); + + $primary = $schema->getPrimaryColumnsForTable($junction); + self::assertSame(['customProgramExternalKey', 'customDistributorExternalKey'], array_map( + static fn (Column $column): string => $column->getName(), $primary + )); + self::assertSame($primary, $schema->getPrimaryColumnsForTable($junction)); + self::assertSame(1, $consumer->cacheEntryCount()); + } +} diff --git a/tests/Unit/Traits/WithDatastoreHandlerMethodsTest.php b/tests/Unit/Traits/WithDatastoreHandlerMethodsTest.php index 8e3ce0c..82a7b08 100644 --- a/tests/Unit/Traits/WithDatastoreHandlerMethodsTest.php +++ b/tests/Unit/Traits/WithDatastoreHandlerMethodsTest.php @@ -1,20 +1,5 @@