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,264 @@
|
||||
require('ts-node/register/transpile-only');
|
||||
|
||||
const assert = require('node:assert/strict');
|
||||
const { test } = require('node:test');
|
||||
const {
|
||||
postgresqlMainMigrationStream,
|
||||
} = require('../../back/migrations/postgresql');
|
||||
const {
|
||||
postgresqlControlSchemaContract,
|
||||
} = require('../../back/migrations/postgresql/schemaContract');
|
||||
const {
|
||||
PostgresSchemaReadinessError,
|
||||
assertPostgresSchemaReady,
|
||||
} = require('../../back/migrations/postgresql/schemaReadiness');
|
||||
|
||||
function validHistory() {
|
||||
return postgresqlMainMigrationStream.migrations.map((migration, index) => ({
|
||||
streamId: 'postgresql-main',
|
||||
dialect: 'postgresql',
|
||||
migrationId: migration.id,
|
||||
checksum: migration.checksum,
|
||||
appliedAtMs: index + 1,
|
||||
}));
|
||||
}
|
||||
|
||||
function validPrivileges() {
|
||||
const expected = {
|
||||
schema_migrations: [true, false, false, false],
|
||||
schema_capabilities: [true, false, false, false],
|
||||
runs: [true, true, true, false],
|
||||
run_attempts: [true, true, true, false],
|
||||
run_events: [true, true, false, false],
|
||||
run_retry_policies: [true, true, true, false],
|
||||
};
|
||||
return Object.entries(expected).map(
|
||||
([
|
||||
tableName,
|
||||
[selectAllowed, insertAllowed, updateAllowed, deleteAllowed],
|
||||
]) => ({
|
||||
tableName,
|
||||
selectAllowed,
|
||||
insertAllowed,
|
||||
updateAllowed,
|
||||
deleteAllowed,
|
||||
isOwner: false,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function queryable(overrides = {}) {
|
||||
const contract = postgresqlControlSchemaContract;
|
||||
return {
|
||||
async query(text) {
|
||||
if (text.includes("current_setting('server_version_num')")) {
|
||||
return {
|
||||
rows: [
|
||||
{
|
||||
serverVersionNum: overrides.serverVersionNum ?? '160014',
|
||||
currentUser: 'ql3_runtime',
|
||||
inRecovery: overrides.inRecovery ?? false,
|
||||
transactionReadOnly: overrides.transactionReadOnly ?? 'off',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
if (text.includes('FROM "ql3"."schema_migrations"')) {
|
||||
return { rows: overrides.history ?? validHistory() };
|
||||
}
|
||||
if (text.includes('FROM "ql3"."schema_capabilities"')) {
|
||||
return {
|
||||
rows: [
|
||||
overrides.capability ?? {
|
||||
contractName: 'control-core',
|
||||
contractVersion: 2,
|
||||
migrationId: 'pg-0003-run-retry-policy',
|
||||
capabilities: { run_core: 1, run_retry_policy: 1 },
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
if (text.includes('FROM information_schema.columns')) {
|
||||
const rows = contract.tables.flatMap((table) =>
|
||||
table.columns.map((columnName) => ({
|
||||
tableName: table.name,
|
||||
columnName,
|
||||
})),
|
||||
);
|
||||
if (overrides.extraTable) {
|
||||
rows.push({ tableName: overrides.extraTable, columnName: 'id' });
|
||||
}
|
||||
return { rows };
|
||||
}
|
||||
if (text.includes('FROM pg_indexes')) {
|
||||
return {
|
||||
rows: [
|
||||
...contract.indexes.map((indexName) => ({ indexName })),
|
||||
...(overrides.extraIndex
|
||||
? [{ indexName: overrides.extraIndex }]
|
||||
: []),
|
||||
],
|
||||
};
|
||||
}
|
||||
if (text.includes('FROM pg_constraint')) {
|
||||
return {
|
||||
rows: [
|
||||
...contract.checks.map((constraintName) => ({
|
||||
constraintName,
|
||||
constraintType: 'check',
|
||||
})),
|
||||
...contract.foreignKeys.map((constraintName) => ({
|
||||
constraintName,
|
||||
constraintType: 'foreign_key',
|
||||
})),
|
||||
...(overrides.extraConstraint
|
||||
? [
|
||||
{
|
||||
constraintName: overrides.extraConstraint,
|
||||
constraintType: 'check',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
};
|
||||
}
|
||||
if (text.includes('FROM pg_catalog.pg_roles')) {
|
||||
return {
|
||||
rows: [
|
||||
{
|
||||
canLogin: true,
|
||||
superuser: overrides.superuser ?? false,
|
||||
createDatabase: false,
|
||||
createRole: false,
|
||||
replication: false,
|
||||
bypassRowLevelSecurity: false,
|
||||
databaseConnect: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
if (text.includes('has_schema_privilege')) {
|
||||
return {
|
||||
rows: [
|
||||
{
|
||||
schemaUsage: true,
|
||||
schemaCreate: overrides.schemaCreate ?? false,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
if (text.includes('has_table_privilege')) {
|
||||
return { rows: overrides.privileges ?? validPrivileges() };
|
||||
}
|
||||
throw new Error(`unexpected query: ${text}`);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test('accepts the exact PostgreSQL control schema and least-privilege runtime role', async () => {
|
||||
const report = await assertPostgresSchemaReady(queryable());
|
||||
assert.deepEqual(report, {
|
||||
ready: true,
|
||||
writablePrimary: true,
|
||||
serverVersionNum: 160014,
|
||||
serverMajor: 16,
|
||||
currentUser: 'ql3_runtime',
|
||||
contractName: 'control-core',
|
||||
contractVersion: 2,
|
||||
migrationIds: [
|
||||
'pg-0001-schema-capability',
|
||||
'pg-0002-run-core',
|
||||
'pg-0003-run-retry-policy',
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('rejects a standby or read-only endpoint before schema inspection', async () => {
|
||||
await assert.rejects(
|
||||
assertPostgresSchemaReady(queryable({ inRecovery: true })),
|
||||
(error) =>
|
||||
error instanceof PostgresSchemaReadinessError &&
|
||||
error.code === 'server_not_writable_primary' &&
|
||||
error.facts.includes('in-recovery:true'),
|
||||
);
|
||||
await assert.rejects(
|
||||
assertPostgresSchemaReady(queryable({ transactionReadOnly: 'on' })),
|
||||
(error) =>
|
||||
error instanceof PostgresSchemaReadinessError &&
|
||||
error.code === 'server_not_writable_primary' &&
|
||||
error.facts.includes('transaction-read-only:on'),
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects unsupported server versions and capability drift', async () => {
|
||||
await assert.rejects(
|
||||
assertPostgresSchemaReady(queryable({ serverVersionNum: '150018' })),
|
||||
(error) =>
|
||||
error instanceof PostgresSchemaReadinessError &&
|
||||
error.code === 'server_version_unsupported',
|
||||
);
|
||||
await assert.rejects(
|
||||
assertPostgresSchemaReady(
|
||||
queryable({
|
||||
capability: {
|
||||
contractName: 'control-core',
|
||||
contractVersion: 3,
|
||||
migrationId: 'pg-0003-run-retry-policy',
|
||||
capabilities: { run_core: 1, run_retry_policy: 1 },
|
||||
},
|
||||
}),
|
||||
),
|
||||
(error) =>
|
||||
error instanceof PostgresSchemaReadinessError &&
|
||||
error.code === 'capability_invalid',
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects unknown ql3 objects and an over-privileged runtime role', async () => {
|
||||
await assert.rejects(
|
||||
assertPostgresSchemaReady(
|
||||
queryable({
|
||||
extraTable: 'plugin_state',
|
||||
extraIndex: 'plugin_state_pkey',
|
||||
extraConstraint: 'plugin_state_payload_check',
|
||||
}),
|
||||
),
|
||||
(error) =>
|
||||
error instanceof PostgresSchemaReadinessError &&
|
||||
error.code === 'schema_contract_invalid' &&
|
||||
error.facts.includes('unknown-table:plugin_state') &&
|
||||
error.facts.includes('unknown-check:plugin_state_payload_check'),
|
||||
);
|
||||
await assert.rejects(
|
||||
assertPostgresSchemaReady(queryable({ schemaCreate: true })),
|
||||
(error) =>
|
||||
error instanceof PostgresSchemaReadinessError &&
|
||||
error.code === 'runtime_role_invalid',
|
||||
);
|
||||
});
|
||||
|
||||
test('preserves database availability errors for the outer readiness layer', async () => {
|
||||
const unavailable = new Error('database unavailable');
|
||||
let calls = 0;
|
||||
await assert.rejects(
|
||||
assertPostgresSchemaReady({
|
||||
async query() {
|
||||
calls += 1;
|
||||
if (calls === 1) {
|
||||
return {
|
||||
rows: [
|
||||
{
|
||||
serverVersionNum: '160014',
|
||||
currentUser: 'ql3_runtime',
|
||||
inRecovery: false,
|
||||
transactionReadOnly: 'off',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
throw unavailable;
|
||||
},
|
||||
}),
|
||||
(error) => error === unavailable,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user