sibling explicit-grouping implementation: commit all work, removal of stray scratch files; update docs and status; keep P1 fix and other senior details
This commit is contained in:
1 parent
9dd8c07e66
commit
99f19e1052
16 files changed
+1244
-44
No files matched your search
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MeshVault\Tests\Integration;
|
||||
|
||||
use DomainException;
|
||||
use InvalidArgumentException;
|
||||
use MeshVault\Indexer\ExplicitImportGroupingAdapter;
|
||||
use MeshVault\Indexer\ScannedFile;
|
||||
use MeshVault\Repository\PdoCatalogRepository;
|
||||
use MeshVault\Repository\PdoImportJobRepository;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class ExplicitGroupingTest extends TestCase
|
||||
{
|
||||
private string $database;
|
||||
private \PDO $pdo;
|
||||
private PdoCatalogRepository $catalog;
|
||||
private PdoImportJobRepository $jobs;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->database = tempnam(sys_get_temp_dir(), 'grouping-');
|
||||
$this->pdo = RepositoryDatabase::migrate($this->database);
|
||||
$this->catalog = new PdoCatalogRepository($this->pdo);
|
||||
$this->jobs = new PdoImportJobRepository($this->pdo);
|
||||
self::assertTrue(class_exists(ExplicitImportGroupingAdapter::class), 'Explicit adapter must exist');
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
RepositoryDatabase::cleanup($this->database);
|
||||
}
|
||||
|
||||
public function testServicePersistsPerFileAndWildcardDecisionsAtomically(): void
|
||||
{
|
||||
$source = RepositoryDatabase::directory('explicit-source');
|
||||
$library = RepositoryDatabase::directory('explicit-library');
|
||||
try {
|
||||
file_put_contents($source . '/a.stl', 'solid a');
|
||||
file_put_contents($source . '/b.stl', 'solid b');
|
||||
$adapter = new ExplicitImportGroupingAdapter($this->catalog, $this->jobs);
|
||||
$service = new \MeshVault\Indexer\ImportService(
|
||||
new \MeshVault\Indexer\FileEnumerator(
|
||||
new \MeshVault\Indexer\Filesystem\LocalFilesystem(),
|
||||
new \MeshVault\Indexer\Filesystem\PathSafety($source)
|
||||
),
|
||||
$this->jobs,
|
||||
$adapter,
|
||||
new \MeshVault\Indexer\SpaceChecker(),
|
||||
$library
|
||||
);
|
||||
$context = ['*' => ['confirmed' => true, 'folder_path' => '/parts', 'model_name' => 'Part']];
|
||||
$job = $service->import($source, ['stl'], $context);
|
||||
$map = $adapter->recoverDecisions($job);
|
||||
self::assertCount(2, $map);
|
||||
self::assertSame($map, $adapter->recoverDecisions($service->import($source, ['stl'], $context)));
|
||||
$before = (int) $this->pdo->query('SELECT count(*) FROM import_jobs')->fetchColumn();
|
||||
try {
|
||||
$service->import($source, ['stl'], ['a.stl' => $context['*']]);
|
||||
self::fail('Incomplete grouping accepted');
|
||||
} catch (\MeshVault\Indexer\ImportException) {
|
||||
self::assertSame($before, (int) $this->pdo->query('SELECT count(*) FROM import_jobs')->fetchColumn());
|
||||
}
|
||||
} finally {
|
||||
RepositoryDatabase::remove($source);
|
||||
RepositoryDatabase::remove($library);
|
||||
}
|
||||
}
|
||||
|
||||
private function resolve(array $context): int
|
||||
{
|
||||
return (new ExplicitImportGroupingAdapter($this->catalog, $this->jobs))->modelFor(
|
||||
'/source',
|
||||
new ScannedFile('/source/a.stl', 'a.stl', 1, 'model/stl'),
|
||||
$context
|
||||
);
|
||||
}
|
||||
|
||||
public function testConfirmationAndCompleteContextAreRequired(): void
|
||||
{
|
||||
foreach (
|
||||
[[], ['confirmed' => true], ['confirmed' => 1, 'model_id' => 1],
|
||||
['confirmed' => true, 'folder_path' => '/x'], ['confirmed' => true, 'model_id' => '1']] as $context
|
||||
) {
|
||||
try {
|
||||
$this->resolve($context);
|
||||
self::fail('Malformed or unconfirmed context accepted');
|
||||
} catch (InvalidArgumentException) {
|
||||
self::assertSame(0, (int) $this->pdo->query('SELECT count(*) FROM models')->fetchColumn());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testStableIdentityNewIdentityAndAmbiguity(): void
|
||||
{
|
||||
$context = ['confirmed' => true, 'folder_path' => '/parts', 'model_name' => 'Part'];
|
||||
$id = $this->resolve($context);
|
||||
self::assertSame($id, $this->resolve($context));
|
||||
self::assertSame($id, $this->resolve(['model_id' => $id] + $context));
|
||||
self::assertNotSame($id, $this->resolve(['create_new' => true] + $context));
|
||||
$this->expectException(DomainException::class);
|
||||
$this->resolve($context);
|
||||
}
|
||||
|
||||
public function testIdentityMismatchAndMissingModelAreRejected(): void
|
||||
{
|
||||
$id = $this->catalog->createModel('/parts', 'Part');
|
||||
foreach (
|
||||
[['model_id' => $id, 'model_name' => 'Other'],
|
||||
['model_id' => $id, 'folder_path' => '/other'], ['model_id' => 999999]] as $context
|
||||
) {
|
||||
try {
|
||||
$this->resolve(['confirmed' => true] + $context);
|
||||
self::fail('Conflicting identity accepted');
|
||||
} catch (DomainException) {
|
||||
self::assertTrue(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testDurableImmutableIdempotentDecisionsAndCallerRollback(): void
|
||||
{
|
||||
$id = $this->resolve(['confirmed' => true, 'folder_path' => '/parts', 'model_name' => 'Part']);
|
||||
$job = $this->jobs->create('/source', [['source_path' => '/source/a.stl', 'size' => 1, 'model_id' => $id]]);
|
||||
$adapter = new ExplicitImportGroupingAdapter($this->catalog, $this->jobs);
|
||||
$decision = ['source_path' => '/source/a.stl', 'model_id' => $id, 'context' => ['confirmed' => true]];
|
||||
$adapter->persistDecisions($job, [$decision]);
|
||||
$adapter->persistDecisions($job, [$decision]);
|
||||
$fresh = new ExplicitImportGroupingAdapter(
|
||||
$this->catalog,
|
||||
new PdoImportJobRepository(RepositoryDatabase::connect($this->database))
|
||||
);
|
||||
self::assertSame(['/source/a.stl' => $id], $fresh->recoverDecisions($job));
|
||||
try {
|
||||
$changed = array_replace($decision, ['context' => ['confirmed' => true, 'extra' => 1]]);
|
||||
$adapter->persistDecisions($job, [$changed]);
|
||||
self::fail('Decision changed');
|
||||
} catch (DomainException) {
|
||||
self::assertSame(['/source/a.stl' => $id], $adapter->recoverDecisions($job));
|
||||
}
|
||||
$this->pdo->beginTransaction();
|
||||
$other = $this->jobs->create('/other', []);
|
||||
$adapter->persistDecisions($other, [$decision]);
|
||||
self::assertTrue($this->pdo->inTransaction());
|
||||
$this->pdo->rollBack();
|
||||
self::assertSame([], $adapter->recoverDecisions($other));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user