Files
duckbrain-toolbox/tests/group10_release_test.php
T

165 lines
6.0 KiB
PHP

<?php
use Toolbox\SelfUpdater;
use Toolbox\ToolboxException;
Harness::suite('group10');
$GLOBALS['g10_repo'] = dirname(__DIR__);
Harness::it('self-update verifica sha256 y reemplaza atómicamente', function () {
$release = tmpdir('release');
$work = tmpdir('work');
$content = "#!/usr/bin/env php\n<?php // v2\n";
write_file($release . '/duckbrain.phar', $content);
write_file($release . '/duckbrain.phar.sha256', hash('sha256', $content));
$target = $work . '/duckbrain.phar';
write_file($target, "old");
(new SelfUpdater(new Toolbox\Console()))->update($release, $target, $work);
eq($content, file_get_contents($target), 'binario reemplazado por el nuevo');
ok(is_executable($target), 'queda ejecutable');
rrmdir($release);
rrmdir($work);
});
Harness::it('self-update aborta ante sha256 inválido sin tocar el binario', function () {
$release = tmpdir('release');
$work = tmpdir('work');
write_file($release . '/duckbrain.phar', "contenido nuevo");
write_file($release . '/duckbrain.phar.sha256', 'deadbeef');
$target = $work . '/duckbrain.phar';
write_file($target, 'viejo');
throws(function () use ($release, $target, $work) {
(new SelfUpdater(new Toolbox\Console()))->update($release, $target, $work);
});
eq('viejo', file_get_contents($target), 'el binario actual queda intacto');
rrmdir($release);
rrmdir($work);
});
Harness::it('self-update en modo API reemplaza cuando el tag es más nuevo', function () {
$rel = tmpdir('rel');
$work = tmpdir('work');
$content = "#!/usr/bin/env php\n<?php // v9\n";
write_file($rel . '/duckbrain.phar', $content);
write_file($rel . '/duckbrain.phar.sha256', hash('sha256', $content));
$json = json_encode([
'tag_name' => 'v9.9.9',
'assets' => [
['name' => 'duckbrain.phar', 'browser_download_url' => 'file://' . $rel . '/duckbrain.phar'],
['name' => 'duckbrain.phar.sha256', 'browser_download_url' => 'file://' . $rel . '/duckbrain.phar.sha256'],
],
]);
write_file($rel . '/releases.json', $json);
$target = $work . '/duckbrain.phar';
write_file($target, 'viejo');
(new SelfUpdater(new Toolbox\Console()))->update($rel . '/releases.json', $target, $work);
eq($content, file_get_contents($target), 'binario reemplazado desde la release');
eq('v9.9.9', (new SelfUpdater(new Toolbox\Console()))->available($rel . '/releases.json'), 'available lee el tag');
rrmdir($rel);
rrmdir($work);
});
Harness::it('self-update en modo API no toca el binario si la versión ya es actual', function () {
$rel = tmpdir('rel');
$work = tmpdir('work');
write_file($rel . '/duckbrain.phar', "nuevo");
write_file($rel . '/duckbrain.phar.sha256', hash('sha256', 'nuevo'));
$json = json_encode([
'tag_name' => 'v' . Toolbox\Config::VERSION,
'assets' => [
['name' => 'duckbrain.phar', 'browser_download_url' => 'file://' . $rel . '/duckbrain.phar'],
['name' => 'duckbrain.phar.sha256', 'browser_download_url' => 'file://' . $rel . '/duckbrain.phar.sha256'],
],
]);
write_file($rel . '/releases.json', $json);
$target = $work . '/duckbrain.phar';
write_file($target, 'actual');
(new SelfUpdater(new Toolbox\Console()))->update($rel . '/releases.json', $target, $work);
eq('actual', file_get_contents($target), 'sin cambios cuando el tag no es más nuevo');
(new SelfUpdater(new Toolbox\Console()))->update($rel . '/releases.json', $target, $work, true);
eq('nuevo', file_get_contents($target), 'con --force reemplaza igualmente');
rrmdir($rel);
rrmdir($work);
});
Harness::it('self-update elige el mayor tag semver de la lista e ignora latest y drafts', function () {
$rel = tmpdir('rel');
$work = tmpdir('work');
$winner = "#!/usr/bin/env php\n<?php // v10\n";
$loser = "older";
write_file($rel . '/p10', $winner);
write_file($rel . '/p10.sha256', hash('sha256', $winner));
write_file($rel . '/p1', $loser);
write_file($rel . '/p1.sha256', hash('sha256', $loser));
$pair = function (string $file): array {
return [
['name' => 'duckbrain.phar', 'browser_download_url' => 'file://' . $file],
['name' => 'duckbrain.phar.sha256', 'browser_download_url' => 'file://' . $file . '.sha256'],
];
};
$list = [
['tag_name' => 'latest', 'draft' => false, 'assets' => $pair($rel . '/p1')],
['tag_name' => 'v99.0.0', 'draft' => true, 'assets' => $pair($rel . '/p1')],
['tag_name' => 'v1.2.3', 'draft' => false, 'assets' => $pair($rel . '/p1')],
['tag_name' => 'v10.0.0', 'draft' => false, 'assets' => $pair($rel . '/p10')],
];
write_file($rel . '/releases.json', json_encode($list));
$target = $work . '/duckbrain.phar';
write_file($target, 'semver');
$updater = new SelfUpdater(new Toolbox\Console());
eq('v10.0.0', $updater->available($rel . '/releases.json'), 'el mayor semver gana sobre latest, drafts y v1.2.3');
$updater->update($rel . '/releases.json', $target, $work);
eq($winner, file_get_contents($target), 'se instala el phar del mayor semver');
rrmdir($rel);
rrmdir($work);
});
Harness::it('build-phar genera un phar ejecutable con su sha256', function () {
$repo = $GLOBALS['g10_repo'];
$dist = tmpdir('dist');
exec('DUCKBRAIN_DIST=' . escapeshellarg($dist) . ' bash ' . escapeshellarg($repo . '/scripts/build-phar') . ' 2>&1', $out, $code);
eq(0, $code, 'build-phar termina bien: ' . implode("\n", $out));
$phar = $dist . '/duckbrain.phar';
ok(is_file($phar), 'phar generado');
ok(is_file($phar . '.sha256'), 'sha256 publicado');
eq(hash_file('sha256', $phar), trim((string) file_get_contents($phar . '.sha256')));
exec('php ' . escapeshellarg($phar) . ' --version 2>&1', $ver, $vcode);
eq(0, $vcode, 'el phar arranca');
ok(str_contains(implode("\n", $ver), 'duckbrain-toolbox'), 'el phar imprime su versión');
rrmdir($dist);
});