Files
newznab-tmux-NNTmux/tests/Integration/ReleasesNormalizeGuidsMariaDbTest.php
T
2026-08-14 13:50:48 +02:00

165 lines
5.5 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Integration;
use Dotenv\Dotenv;
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
/**
* Covers the MySQL-only branches of `releases:normalize-guids`: the `NOT REGEXP`
* validity check and the duplicate-guid sample join. The SQLite feature test
* exercises the portable branches instead.
*/
final class ReleasesNormalizeGuidsMariaDbTest extends TestCase
{
private string $tablePrefix;
public function createApplication()
{
Dotenv::createMutable(dirname(__DIR__, 2))->safeLoad();
$this->tablePrefix = 'guid_norm_'.getmypid().'_'.bin2hex(random_bytes(4)).'_';
$this->setEnvironmentValue('APP_ENV', 'testing');
$this->setEnvironmentValue('DB_URL', null);
$this->setEnvironmentValue('DB_CONNECTION', 'mariadb');
$app = require __DIR__.'/../../bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
$app->make('config')->set('database.connections.mariadb.prefix', $this->tablePrefix);
$app->make('db')->purge('mariadb');
return $app;
}
protected function setUp(): void
{
parent::setUp();
if (DB::getDriverName() !== 'mariadb') {
$this->markTestSkipped('MariaDB integration test.');
}
DB::statement('DROP TABLE IF EXISTS `'.$this->table('releases').'`');
DB::statement(
'CREATE TABLE `'.$this->table('releases').'` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`guid` VARCHAR(40) NOT NULL,
`leftguid` CHAR(1) NOT NULL DEFAULT "",
`name` VARCHAR(255) NOT NULL DEFAULT "",
PRIMARY KEY (`id`),
KEY `ix_releases_guid` (`guid`)
) ENGINE=InnoDB'
);
}
protected function tearDown(): void
{
if (isset($this->tablePrefix) && preg_match('/^guid_norm_\d+_[a-f0-9]{8}_$/', $this->tablePrefix) === 1) {
DB::statement('DROP TABLE IF EXISTS `'.$this->table('releases').'`');
}
DB::disconnect();
parent::tearDown();
}
#[Test]
public function clean_data_passes_and_resyncs_leftguid(): void
{
$this->insert(1, '01234567-89ab-cdef-0123-456789abcdef', 'z');
$status = Artisan::call('releases:normalize-guids');
$this->assertSame(0, $status);
$this->assertStringContainsString('Release guids are consistent.', Artisan::output());
$this->assertSame('0', DB::table('releases')->where('id', 1)->value('leftguid'));
}
#[Test]
public function legacy_md5_guids_are_detected_by_the_regexp_check(): void
{
$this->insert(1, '01234567-89ab-cdef-0123-456789abcdef', '0');
$this->insert(2, 'd41d8cd98f00b204e9800998ecf8427e', 'd', 'Legacy nZEDb release');
$this->insert(3, 'not-a-uuid-at-all-------------------x', 'n');
$status = Artisan::call('releases:normalize-guids');
$output = Artisan::output();
$this->assertSame(1, $status);
$this->assertStringContainsString('2 releases have a guid that blocks', $output);
$this->assertStringContainsString('Legacy nZEDb release', $output);
$this->assertSame('d41d8cd98f00b204e9800998ecf8427e', DB::table('releases')->where('id', 2)->value('guid'));
}
#[Test]
public function case_insensitive_duplicates_are_counted_and_sampled(): void
{
$guid = 'abcdef01-2345-6789-abcd-ef0123456789';
$this->insert(1, $guid, 'a');
$this->insert(2, strtoupper($guid), 'A', 'Duplicate of one');
$this->insert(3, strtoupper($guid), 'A', 'Second duplicate');
$status = Artisan::call('releases:normalize-guids');
$output = Artisan::output();
$this->assertSame(1, $status);
$this->assertStringContainsString('2 releases have a guid that blocks', $output);
$this->assertStringContainsString('Duplicate of one', $output);
$this->assertStringContainsString('Second duplicate', $output);
$this->assertSame(3, DB::table('releases')->count());
}
#[Test]
public function scans_never_use_offset_paging(): void
{
for ($id = 1; $id <= 250; $id++) {
$this->insert($id, sprintf('%08x-89ab-cdef-0123-456789abcdef', $id), 'x');
}
$queries = [];
DB::listen(static function ($query) use (&$queries): void {
$queries[] = $query->sql;
});
Artisan::call('releases:normalize-guids', ['--chunk' => 100]);
$this->assertNotEmpty($queries);
foreach ($queries as $sql) {
$this->assertStringNotContainsStringIgnoringCase(
'offset',
$sql,
'releases:normalize-guids must page by primary key, never with LIMIT/OFFSET.',
);
}
}
private function insert(int $id, string $guid, string $leftguid, string $name = 'Release'): void
{
DB::table('releases')->insert([
'id' => $id,
'guid' => $guid,
'leftguid' => $leftguid,
'name' => $name,
]);
}
private function table(string $name): string
{
return $this->tablePrefix.$name;
}
private function setEnvironmentValue(string $key, ?string $value): void
{
if ($value === null) {
putenv($key);
unset($_ENV[$key], $_SERVER[$key]);
return;
}
putenv("{$key}={$value}");
$_ENV[$key] = $_SERVER[$key] = $value;
}
}