mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 00:38:14 +08:00
178 lines
5.8 KiB
JavaScript
178 lines
5.8 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const { test } = require('node:test');
|
|
|
|
const {
|
|
EXPECTED_LIMITATIONS,
|
|
auditPostgresHaEvidence,
|
|
} = require('../../scripts/ql3-postgres-ha-evidence-audit.cjs');
|
|
|
|
function legacyEnvApplicationFacts() {
|
|
return {
|
|
receiptDigest: '1'.repeat(64),
|
|
taskCount: 1,
|
|
triggerCount: 1,
|
|
taskRevision: 3,
|
|
taskContentDigest: '2'.repeat(64),
|
|
executionContentDigest: '3'.repeat(64),
|
|
triggerRevision: 2,
|
|
triggerContentDigest: '4'.repeat(64),
|
|
triggerTaskRevision: 3,
|
|
triggerTaskContentDigest: '5'.repeat(64),
|
|
scheduleRevision: 2,
|
|
scheduleStateVersion: 1,
|
|
scheduleClaimVersion: 1,
|
|
planRows: 1,
|
|
receiptRows: 1,
|
|
taskItemRows: 1,
|
|
triggerItemRows: 1,
|
|
};
|
|
}
|
|
|
|
function fixture(architecture = 'arm64') {
|
|
const gates = { passed: true };
|
|
for (let index = 0; index < 100; index += 1) gates[`gate${index}`] = true;
|
|
return {
|
|
schemaVersion: 1,
|
|
fixture: 'qinglong/postgresql-ha-contract@v1',
|
|
postgres: {
|
|
image: 'postgres:18',
|
|
imageId: `sha256:${'a'.repeat(64)}`,
|
|
repoDigests: [`postgres@sha256:${'b'.repeat(64)}`],
|
|
architecture,
|
|
version: '18.4',
|
|
versionNumber: 180004,
|
|
},
|
|
replication: {
|
|
mode: 'physical-streaming',
|
|
synchronousCommit: 'remote_apply',
|
|
synchronousStandbyBeforePartition: {
|
|
state: 'streaming',
|
|
syncState: 'sync',
|
|
},
|
|
initialPrimaryTimeline: 1,
|
|
promotedPrimaryTimeline: 2,
|
|
oldPrimaryFenced: true,
|
|
promotedWritable: true,
|
|
},
|
|
controlReplicas: {
|
|
beforePromotion: [{}, {}],
|
|
afterPromotion: [{}, {}],
|
|
oldAvailabilityAfterFailure: [
|
|
{ availability: 'unavailable' },
|
|
{ availability: 'unavailable' },
|
|
],
|
|
oldActivationsRecoveredInPlace: false,
|
|
freshActivationsReady: 2,
|
|
},
|
|
durability: { unexpectedDomainSideEffects: 0 },
|
|
transactionWindows: {
|
|
ambiguousCommit: {
|
|
transparentReplayAllowed: false,
|
|
durableRowsAfterPromotion: 1,
|
|
},
|
|
writeBeforeCommit: { durableRowsAfterPromotion: 0 },
|
|
},
|
|
networkPartition: {
|
|
promotionRejectedWhileOldPrimaryWritable: true,
|
|
commitClientObservedFailure: true,
|
|
acknowledgedWriteLost: false,
|
|
replicatedToPromotionCandidate: 0,
|
|
promotedPrimaryRows: 0,
|
|
unacknowledgedLocalCommitDiscarded: true,
|
|
},
|
|
oldPrimaryRejoin: {
|
|
method: 'pg_rewind --write-recovery-conf',
|
|
rewindExitStatus: 0,
|
|
inRecovery: true,
|
|
streaming: true,
|
|
synchronousState: 'sync',
|
|
rejoinedAsWritablePrimary: false,
|
|
},
|
|
clusterLegacyEnvMigrationApplication: {
|
|
primaryBeforePromotion: legacyEnvApplicationFacts(),
|
|
standbyBeforePromotion: legacyEnvApplicationFacts(),
|
|
promotedAfterReplay: legacyEnvApplicationFacts(),
|
|
replicatedBeforePromotion: true,
|
|
exactReplayAfterPromotion: true,
|
|
replayStatus: 'existing',
|
|
mutationStreamsOpenedAfterPromotion: 0,
|
|
durableRowsAddedByReplay: 0,
|
|
contentFree: true,
|
|
},
|
|
timeline: [
|
|
'primary_ready',
|
|
'standby_streaming',
|
|
'synchronous_remote_apply_ready',
|
|
'cluster_legacy_env_application_replicated',
|
|
'replication_partition_and_promotion_guard_verified',
|
|
'old_primary_fenced_and_admission_withdrawn',
|
|
'cluster_legacy_env_application_replayed_after_promotion',
|
|
'standby_promoted_old_primary_rejoined_endpoint_switched',
|
|
'two_fresh_control_replicas_ready',
|
|
].map((state, atMs) => ({ state, atMs })),
|
|
gates,
|
|
limitations: [...EXPECTED_LIMITATIONS],
|
|
};
|
|
}
|
|
|
|
test('accepts complete PostgreSQL HA evidence', () => {
|
|
assert.deepEqual(auditPostgresHaEvidence(fixture()), {
|
|
compatible: true,
|
|
findings: [],
|
|
});
|
|
assert.equal(auditPostgresHaEvidence(fixture('amd64')).compatible, true);
|
|
});
|
|
|
|
test('rejects false gates, promotion drift and hidden private material', () => {
|
|
const report = fixture();
|
|
report.gates.oldPrimaryFencedBeforePromotion = false;
|
|
report.replication.promotedPrimaryTimeline = 1;
|
|
report.clusterLegacyEnvMigrationApplication.promotedAfterReplay.receiptRows = 2;
|
|
report.privateValue = 'postgresql://private-credential';
|
|
const result = auditPostgresHaEvidence(report);
|
|
assert.equal(result.compatible, false);
|
|
assert.ok(result.findings.includes('GATE_FAILED'));
|
|
assert.ok(result.findings.includes('PROMOTION_TIMELINE_INVALID'));
|
|
assert.ok(
|
|
result.findings.includes('CLUSTER_LEGACY_ENV_APPLICATION_FACTS_DRIFTED'),
|
|
);
|
|
assert.ok(
|
|
result.findings.includes('CLUSTER_LEGACY_ENV_APPLICATION_FACTS_NOT_EXACT'),
|
|
);
|
|
assert.ok(result.findings.includes('PRIVATE_MATERIAL_PRESENT'));
|
|
});
|
|
|
|
test('rejects reordered timeline and limitations drift', () => {
|
|
const report = fixture();
|
|
report.timeline.reverse();
|
|
report.limitations.pop();
|
|
const result = auditPostgresHaEvidence(report);
|
|
assert.equal(result.compatible, false);
|
|
assert.ok(result.findings.includes('LIMITATIONS_DRIFTED'));
|
|
assert.ok(result.findings.some((finding) => finding.startsWith('TIMELINE_')));
|
|
});
|
|
|
|
test('rejects missing, widened or private Legacy Env replay evidence', () => {
|
|
const missing = fixture();
|
|
delete missing.clusterLegacyEnvMigrationApplication;
|
|
assert.ok(
|
|
auditPostgresHaEvidence(missing).findings.includes(
|
|
'CLUSTER_LEGACY_ENV_APPLICATION_EVIDENCE_MISSING',
|
|
),
|
|
);
|
|
|
|
const widened = fixture();
|
|
widened.clusterLegacyEnvMigrationApplication.privateRef =
|
|
'qlsecret:v1:private';
|
|
widened.clusterLegacyEnvMigrationApplication.primaryBeforePromotion.secretRef =
|
|
'ha-legacy-env-bundle-private';
|
|
const result = auditPostgresHaEvidence(widened);
|
|
assert.ok(
|
|
result.findings.includes('CLUSTER_LEGACY_ENV_APPLICATION_EVIDENCE_WIDENED'),
|
|
);
|
|
assert.ok(
|
|
result.findings.includes('CLUSTER_LEGACY_ENV_APPLICATION_FACTS_WIDENED'),
|
|
);
|
|
assert.ok(result.findings.includes('PRIVATE_MATERIAL_PRESENT'));
|
|
});
|