Files
XC_VM/tests/Unit/ClusterSchemaTest.php
T
Claude 23b465d9f8 feat(db): cluster API schema and settings (Phase 1)
Migrations 028–034 (the plan's 026–032; 026 and 027 are taken by the SSH
host key and the fanout switch), each with a down file, mirrored in
database.sql:

- 028: the 19 cluster settings with the plan's defaults. They stay inert
  until cluster_api_enabled and cluster:init.
- 029: cluster_nodes (mode, flow bits, gen, node keys, state, sequence
  high-waters, row MAC), cluster_node_epochs (the extension's sealed epoch
  records; deleting a row erases z) and cluster_meta.
- 030: cluster_commands, FIFO per node with UNIQUE(server_id, seq),
  UNIQUE(server_id, dedupe_key) and UNIQUE(cmd_id).
- 031: cluster_enrol_codes (sealed K_req/K_res under a row MAC, looked up
  by SHA-256 of the secret) and cluster_enrol_requests (UNIQUE(server_id):
  no bulk approve).
- 032: cluster_audit, cluster_nonces (replay cache) and
  cluster_reservations (MySQL-mode admission).
- 033: crontab.role (all/main/legacy). The four crons Phase 0 gated to
  MAIN become 'main', and a disabled 'cluster' row is added for
  cron:cluster (Phase 2).
- 034: cluster_changes (blocklist delta log) and cluster_stream_ver (R2
  versions).

Checked on MariaDB 10.11:
- a fresh database.sql and the previous database.sql plus these
  migrations produce identical columns, indexes, cron rows and defaults;
- the migrations are idempotent;
- the down files remove everything and up re-applies.

ClusterSchemaTest keeps the two copies in step in CI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Es8UhyPS8MNjuYTagqfGWi
2026-09-25 10:28:53 +00:00

62 lines
2.7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
use PHPUnit\Framework\TestCase;
/**
* The cluster schema exists twice: as migrations 028–034 for upgrades and in
* database.sql for fresh installs. Both were loaded into MariaDB 10.11 and
* compared column by column when written; this test keeps them from drifting
* where CI has no database.
*/
final class ClusterSchemaTest extends TestCase {
private const MIGRATIONS = ['028_add_cluster_settings', '029_create_cluster_nodes', '030_create_cluster_commands', '031_create_cluster_enrolment', '032_create_cluster_audit', '033_add_crontab_role', '034_create_cluster_changes'];
private function src(string $rPath): string {
return (string) file_get_contents(dirname(__DIR__, 2) . '/src/' . $rPath);
}
/** @return array<string, string> table => column block */
private function tables(string $rSql): array {
preg_match_all('/CREATE TABLE IF NOT EXISTS `([a-z_]+)` \((.*?)\n\) ENGINE/s', $rSql, $rM);
return array_combine($rM[1], array_map('trim', $rM[2]));
}
public function testEveryMigrationHasADownFile(): void {
foreach (self::MIGRATIONS as $rName) {
$this->assertFileExists(dirname(__DIR__, 2) . '/src/migrations/database/up/' . $rName . '.sql');
$this->assertFileExists(dirname(__DIR__, 2) . '/src/migrations/database/down/' . $rName . '.sql');
}
}
public function testClusterTablesMatchDatabaseSql(): void {
$rInstall = $this->tables($this->src('bin/install/database.sql'));
$rCount = 0;
foreach (self::MIGRATIONS as $rName) {
foreach ($this->tables($this->src('migrations/database/up/' . $rName . '.sql')) as $rTable => $rBody) {
$this->assertArrayHasKey($rTable, $rInstall, $rTable . ' missing from database.sql');
$this->assertSame($rBody, $rInstall[$rTable], $rTable);
$rCount++;
}
}
$this->assertSame(11, $rCount);
}
public function testSettingsColumnsMatchDatabaseSql(): void {
preg_match_all('/ADD COLUMN IF NOT EXISTS (`[a-z_]+` [^\n]*?),?\n/', $this->src('migrations/database/up/028_add_cluster_settings.sql') . "\n", $rM);
$this->assertCount(19, $rM[1]);
$rSettings = $this->tables($this->src('bin/install/database.sql'))['settings'];
foreach ($rM[1] as $rColumn) {
$this->assertStringContainsString(' ' . rtrim($rColumn, ';') . ',', $rSettings);
}
}
public function testCrontabRoles(): void {
$rSql = $this->src('bin/install/database.sql');
$this->assertStringContainsString("`role` enum('all','main','legacy')", $rSql);
foreach (['cleanup', 'tmdb', 'tmdb_popular', 'update'] as $rCron) {
$this->assertMatchesRegularExpression("/\\(\\d+, '" . $rCron . "', '[^']*', 1, 'main'\\)/", $rSql, $rCron);
}
$this->assertStringContainsString("(30, 'cluster', '* * * * *', 0, 'main')", $rSql, 'disabled until cron:cluster exists');
}
}