Files
duckbrain-toolbox/tests/group3_state_test.php
T

152 lines
5.5 KiB
PHP

<?php
use Toolbox\Destination;
use Toolbox\Drift;
use Toolbox\Lock;
use Toolbox\Ownership;
use Toolbox\ProjectManifest;
Harness::suite('group3');
Harness::it('manifest de deseo guarda y recupera restricciones', function () {
$dir = tmpdir();
$m = ProjectManifest::load($dir);
$m->setConstraint('http:aria2', '^1.2');
$m->coreConstraint = '>=1.1';
$m->save($dir);
$loaded = ProjectManifest::load($dir);
eq('^1.2', $loaded->constraint('http:aria2'));
eq('>=1.1', $loaded->coreConstraint);
rrmdir($dir);
});
Harness::it('lock guarda core y componentes con schema esperado', function () {
$dir = tmpdir();
$file = $dir . '/src/Libs/Aria2.php';
write_file($file, "<?php\n");
$lock = Lock::empty();
$lock->setCore(['version' => '1.5.0', 'commit' => 'abc', 'files' => ['src/Libs/Router.php' => 'sha256:x']]);
$lock->setComponent('http:aria2', [
'package' => 'http',
'version' => '1.2.0',
'toolbox_commit' => 'D',
'mode' => 'copy',
'files' => ['src/Libs/Aria2.php' => Drift::hash($file)],
]);
$lock->save($dir);
$loaded = Lock::load($dir);
eq('1.5.0', $loaded->core()['version']);
eq('http', $loaded->component('http:aria2')['package']);
eq('copy', $loaded->component('http:aria2')['mode']);
rrmdir($dir);
});
Harness::it('ownership detecta colisión de rutas', function () {
$lock = Lock::empty();
$lock->setCore(['files' => ['src/Libs/X.php' => 'sha256:a']]);
$lock->setComponent('a:b', ['package' => 'a', 'version' => '1', 'toolbox_commit' => null, 'mode' => 'copy', 'files' => ['src/Libs/X.php' => 'sha256:b']]);
$ownership = Ownership::fromLock($lock);
ok($ownership->collisions() !== [], 'debía detectar colisión');
throws(fn () => $ownership->assertNoCollisions());
});
Harness::it('ownership separa core y componentes sin colisión', function () {
$lock = Lock::empty();
$lock->setCore(['files' => ['src/Libs/Router.php' => 'sha256:a']]);
$lock->setComponent('a:b', ['package' => 'a', 'version' => '1', 'toolbox_commit' => null, 'mode' => 'copy', 'files' => ['src/Libs/B.php' => 'sha256:b']]);
$ownership = Ownership::fromLock($lock);
eq('core', $ownership->ownerOf('src/Libs/Router.php'));
eq('a:b', $ownership->ownerOf('src/Libs/B.php'));
$ownership->assertNoCollisions();
});
Harness::it('drift distingue match, drifted y missing', function () {
$dir = tmpdir();
$file = $dir . '/f.php';
write_file($file, 'original');
$hash = Drift::hash($file);
eq(Drift::MATCH, Drift::status($file, $hash));
write_file($file, 'editado');
eq(Drift::DRIFTED, Drift::status($file, $hash));
eq(Drift::MISSING, Drift::status($dir . '/nope.php', $hash));
rrmdir($dir);
});
Harness::it('destino aplica la regla src y fuera de src', function () {
$rootDir = '/proj';
$rootCore = '/proj/src';
eq('src/Libs/Crypto.php', Destination::relative('src/Libs/Crypto.php', $rootDir, $rootCore));
eq('duckbrain', Destination::relative('duckbrain', $rootDir, $rootCore));
eq('/proj/src/Libs/Crypto.php', Destination::resolve('src/Libs/Crypto.php', $rootDir, $rootCore));
eq('/proj/duckbrain', Destination::resolve('duckbrain', $rootDir, $rootCore));
eq('custom/Libs/Crypto.php', Destination::relative('src/Libs/Crypto.php', $rootDir, '/proj/custom'));
});
Harness::it('destino explícito sobreescribe la regla', function () {
$dest = Destination::relative('src/Libs/Crypto.php', '/proj', '/proj/src', ['src/Libs/Crypto.php' => 'elsewhere/Crypto.php']);
eq('elsewhere/Crypto.php', $dest);
});
Harness::it('el lock persiste la descripción y tolera locks sin el campo', function () {
$dir = tmpdir();
$lock = Lock::empty();
$lock->setComponent('db:sql', [
'package' => 'db',
'version' => '1.0.0',
'mode' => 'copy',
'description' => 'SQL',
'files' => [],
]);
$lock->save($dir);
eq('SQL', Lock::load($dir)->component('db:sql')['description'], 'roundtrip de description');
$raw = json_decode((string) file_get_contents($dir . '/.duckbrain/lock.json'), true);
unset($raw['components']['db:sql']['description']);
file_put_contents($dir . '/.duckbrain/lock.json', json_encode($raw));
eq(null, Lock::load($dir)->component('db:sql')['description'], 'lock antiguo sin campo se lee tolerante');
rrmdir($dir);
});
Harness::it('add captura la descripción efectiva del catálogo en el lock', function () {
$root = tmpdir('proj');
$tool = tmpdir('tool');
write_file($tool . '/packages/mix/duckbrain.json', json_encode([
'name' => 'mix',
'version' => '1.0.0',
'description' => 'Descripción del paquete',
'components' => [
'own' => ['files' => ['src/Libs/Own.php'], 'description' => 'Descripción propia'],
'inh' => ['files' => ['src/Libs/Inh.php']],
],
'default' => ['own', 'inh'],
]));
write_file($tool . '/packages/mix/src/Libs/Own.php', "<?php\n");
write_file($tool . '/packages/mix/src/Libs/Inh.php', "<?php\n");
(new Toolbox\Installer($root, $tool, new Toolbox\Console()))->add(['mix'], ['yes' => true]);
$lock = Lock::load($root);
eq('Descripción propia', $lock->component('mix:own')['description'], 'propia gana en el lock');
eq('Descripción del paquete', $lock->component('mix:inh')['description'], 'sin propia, hereda del paquete');
rrmdir($root);
rrmdir($tool);
});