mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 01:00:24 +08:00
146 lines
4.7 KiB
JavaScript
146 lines
4.7 KiB
JavaScript
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/,
|
|
);
|
|
});
|