mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const { test } = require('node:test');
|
||||
|
||||
const {
|
||||
privateReportPath,
|
||||
writePrivateReport,
|
||||
} = require('../../scripts/ql3-postgres-ha-contract.cjs');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '../..');
|
||||
const SOURCE = fs.readFileSync(
|
||||
path.join(ROOT, 'scripts/ql3-postgres-ha-contract.cjs'),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
test('mounts PostgreSQL 18 named volumes at the image-declared root', () => {
|
||||
assert.match(
|
||||
SOURCE,
|
||||
/const POSTGRES_VOLUME_ROOT = '\/var\/lib\/postgresql';/,
|
||||
);
|
||||
assert.match(
|
||||
SOURCE,
|
||||
/const POSTGRES_DATA = '\/var\/lib\/postgresql\/18\/docker';/,
|
||||
);
|
||||
assert.equal(
|
||||
SOURCE.match(
|
||||
/\$\{names\.(?:primary|standby)Volume\}:\$\{POSTGRES_VOLUME_ROOT\}/g,
|
||||
)?.length,
|
||||
6,
|
||||
);
|
||||
assert.doesNotMatch(
|
||||
SOURCE,
|
||||
/\$\{names\.(?:primary|standby)Volume\}:\$\{POSTGRES_DATA\}/,
|
||||
);
|
||||
assert.equal(SOURCE.match(/`PGDATA=\$\{POSTGRES_DATA\}`/g)?.length, 2);
|
||||
assert.match(
|
||||
SOURCE,
|
||||
/mkdir -p \$\{POSTGRES_DATA\} && chown -R postgres:postgres \$\{POSTGRES_DATA\}/,
|
||||
);
|
||||
});
|
||||
|
||||
test('removes only its isolated HA containers, named volumes and networks', () => {
|
||||
const removeContainers =
|
||||
"docker(['rm', '-f', '-v', names.primary, names.standby]";
|
||||
const removeVolumes =
|
||||
"docker(['volume', 'rm', names.primaryVolume, names.standbyVolume]";
|
||||
assert.match(SOURCE, /ql3-ha-primary-/);
|
||||
assert.match(SOURCE, /ql3-ha-standby-/);
|
||||
assert.ok(SOURCE.indexOf(removeContainers) > 0);
|
||||
assert.ok(SOURCE.indexOf(removeVolumes) > SOURCE.indexOf(removeContainers));
|
||||
assert.doesNotMatch(SOURCE, /(?:system|builder|volume) prune/);
|
||||
assert.doesNotMatch(SOURCE, /ql3-cnpg-evidence-control-plane/);
|
||||
});
|
||||
|
||||
test('publishes only a compact stdout envelope after durable report creation', () => {
|
||||
assert.match(SOURCE, /const reportFile = privateReportPath\(argv\);/);
|
||||
assert.match(SOURCE, /writePrivateReport\(reportFile, report\);/);
|
||||
assert.ok(
|
||||
SOURCE.indexOf('writePrivateReport(reportFile, report);') <
|
||||
SOURCE.indexOf('reportSha256:'),
|
||||
);
|
||||
assert.doesNotMatch(
|
||||
SOURCE,
|
||||
/process\.stdout\.write\(`\$\{JSON\.stringify\(report, null, 2\)\}/,
|
||||
);
|
||||
});
|
||||
|
||||
test('publishes a private no-replace PostgreSQL HA report atomically', (t) => {
|
||||
const directory = fs.mkdtempSync(
|
||||
path.join(fs.realpathSync(os.tmpdir()), 'ql3-postgres-ha-report-test-'),
|
||||
);
|
||||
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
|
||||
const reportPath = path.join(directory, 'report.json');
|
||||
assert.equal(privateReportPath([`--report=${reportPath}`], {}), reportPath);
|
||||
assert.equal(
|
||||
privateReportPath(['--', `--report=${reportPath}`], {}),
|
||||
reportPath,
|
||||
);
|
||||
|
||||
const report = {
|
||||
schemaVersion: 1,
|
||||
fixture: 'qinglong/postgresql-ha-contract@v1',
|
||||
gates: { passed: true },
|
||||
};
|
||||
writePrivateReport(reportPath, report);
|
||||
assert.equal(fs.lstatSync(reportPath).mode & 0o777, 0o600);
|
||||
assert.deepEqual(JSON.parse(fs.readFileSync(reportPath, 'utf8')), report);
|
||||
assert.deepEqual(
|
||||
fs.readdirSync(directory).filter((name) => name.endsWith('.tmp')),
|
||||
[],
|
||||
);
|
||||
assert.throws(
|
||||
() => writePrivateReport(reportPath, report),
|
||||
/EEXIST|file already exists/,
|
||||
);
|
||||
assert.deepEqual(
|
||||
fs.readdirSync(directory).filter((name) => name.endsWith('.tmp')),
|
||||
[],
|
||||
);
|
||||
});
|
||||
|
||||
test('selects a canonical private temporary report path by default', () => {
|
||||
const selected = privateReportPath([], {});
|
||||
assert.equal(path.isAbsolute(selected), true);
|
||||
assert.equal(fs.realpathSync(path.dirname(selected)), path.dirname(selected));
|
||||
assert.equal(fs.existsSync(selected), false);
|
||||
});
|
||||
|
||||
test('rejects ambiguous, relative, existing and symlink-parent report paths', (t) => {
|
||||
const directory = fs.mkdtempSync(
|
||||
path.join(fs.realpathSync(os.tmpdir()), 'ql3-postgres-ha-path-test-'),
|
||||
);
|
||||
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
|
||||
const reportPath = path.join(directory, 'report.json');
|
||||
assert.throws(
|
||||
() => privateReportPath(['--report=relative.json'], {}),
|
||||
/normalized absolute file/,
|
||||
);
|
||||
assert.throws(
|
||||
() =>
|
||||
privateReportPath([`--report=${reportPath}`], {
|
||||
QL3_HA_REPORT: path.join(directory, 'other.json'),
|
||||
}),
|
||||
/only once/,
|
||||
);
|
||||
fs.writeFileSync(reportPath, '{}\n', { mode: 0o600 });
|
||||
assert.throws(
|
||||
() => privateReportPath([`--report=${reportPath}`], {}),
|
||||
/refusing to overwrite/,
|
||||
);
|
||||
const realParent = path.join(directory, 'real');
|
||||
const linkedParent = path.join(directory, 'linked');
|
||||
fs.mkdirSync(realParent, { mode: 0o700 });
|
||||
fs.symlinkSync(realParent, linkedParent);
|
||||
assert.throws(
|
||||
() =>
|
||||
privateReportPath(
|
||||
[`--report=${path.join(linkedParent, 'report.json')}`],
|
||||
{},
|
||||
),
|
||||
/canonical real directory/,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user