Files
qinglong/packages/ql3-cluster-admin/test/pluginPackageExecutorProcess.test.cjs
T

181 lines
6.2 KiB
JavaScript

const assert = require('node:assert/strict');
const { test } = require('node:test');
const {
ClusterPluginPackageExecutorProcessConfigError,
loadClusterPluginPackageExecutorProcessConfig,
runClusterPluginPackageExecutorProcess,
} = require('@qinglong/cluster-admin/plugin-package-executor-process');
function environment(overrides = {}) {
return {
QL3_PLUGIN_PACKAGE_EXECUTOR_ENABLED: 'true',
QL3_PLUGIN_PACKAGE_EXECUTOR_OWNER: 'cluster_package_executor_1',
QL3_PLUGIN_PACKAGE_EXECUTOR_APPROVAL_BATCH_SIZE: '4',
QL3_PLUGIN_PACKAGE_EXECUTOR_DISPATCH_BATCH_SIZE: '4',
QL3_PLUGIN_PACKAGE_EXECUTOR_MAX_BATCHES: '2',
QL3_PLUGIN_PACKAGE_EXECUTOR_LEASE_DURATION_MS: '600000',
QL3_PLUGIN_PACKAGE_EXECUTOR_REVOCATION_PAGE_SIZE: '8',
QL3_PLUGIN_PACKAGE_EXECUTOR_REVOCATION_MAX_PAGES: '4',
QL3_PLUGIN_PACKAGE_EXECUTOR_SECRET_ROOT:
'/var/run/secrets/qinglong3/plugin-package-values',
QL3_POSTGRES_PACKAGE_EXECUTOR_URL:
'postgresql://ql3_package_executor:secret@postgres/qinglong',
QL3_POSTGRES_TLS_MODE: 'disable',
QL3_POSTGRES_ALLOW_INSECURE: 'true',
...overrides,
};
}
test('disabled executor opens no PostgreSQL authority', async () => {
let opened = 0;
const result = await runClusterPluginPackageExecutorProcess({
environment: { QL3_PLUGIN_PACKAGE_EXECUTOR_ENABLED: 'false' },
async openDatabase() {
opened += 1;
throw new Error('must not open');
},
});
assert.deepEqual(result, { status: 'disabled' });
assert.equal(opened, 0);
});
test('loads bounded low-footprint Package-executor configuration', () => {
const config = loadClusterPluginPackageExecutorProcessConfig(environment());
assert.equal(config.enabled, true);
assert.equal(config.owner, 'cluster_package_executor_1');
assert.equal(config.approvalBatchSize, 4);
assert.equal(config.dispatchBatchSize, 4);
assert.equal(config.maxBatches, 2);
assert.equal(config.revocationPageSize, 8);
assert.equal(config.revocationMaxPages, 4);
assert.equal(config.dispatchId, null);
assert.equal(
config.secretProjectionRoot,
'/var/run/secrets/qinglong3/plugin-package-values',
);
assert.equal(config.database.pool.maxConnections, 2);
assert.equal(config.database.connection.tls.mode, 'disable');
});
test('loads one bounded action-scoped dispatch without widening batch limits', () => {
const config = loadClusterPluginPackageExecutorProcessConfig(
environment({
QL3_PLUGIN_PACKAGE_EXECUTOR_DISPATCH_ID: 'dispatch.secret-binding.42',
QL3_PLUGIN_PACKAGE_EXECUTOR_APPROVAL_BATCH_SIZE: undefined,
QL3_PLUGIN_PACKAGE_EXECUTOR_DISPATCH_BATCH_SIZE: undefined,
QL3_PLUGIN_PACKAGE_EXECUTOR_MAX_BATCHES: undefined,
}),
);
assert.equal(config.enabled, true);
assert.equal(config.dispatchId, 'dispatch.secret-binding.42');
assert.equal(config.approvalBatchSize, 8);
assert.equal(config.dispatchBatchSize, 8);
assert.equal(config.maxBatches, 4);
});
test('action-scoped mode skips every Approval consumer and shared queue scan', async () => {
const calls = [];
const pool = {};
const readiness = {
ready: true,
writablePrimary: true,
serverVersionNum: 180004,
serverMajor: 18,
currentUser: 'ql3_package_executor',
contractName: 'control-core',
contractVersion: 62,
migrationIds: ['pg-0063-plugin-package-secret-binding-transition-receipts'],
};
const rejectConsumer = async () => {
throw new Error('action-scoped executor must not consume approvals');
};
const result = await runClusterPluginPackageExecutorProcess({
environment: environment({
QL3_PLUGIN_PACKAGE_EXECUTOR_DISPATCH_ID: 'dispatch.secret-binding.42',
}),
async openDatabase() {
calls.push('open');
return {
pool,
async close() {
calls.push('close');
},
};
},
async assertReady(candidate) {
assert.equal(candidate, pool);
calls.push('ready');
return readiness;
},
consumeApprovals: rejectConsumer,
consumeTrustTransitionApprovals: rejectConsumer,
consumeSecretBindingApprovals: rejectConsumer,
consumeSecretBindingTransitionApprovals: rejectConsumer,
createDispatcher(options) {
assert.equal(options.pool, pool);
assert.equal(typeof options.secretExistenceInspector.assertExists, 'function');
return {
async dispatchBatch() {
throw new Error('action-scoped executor must not scan the queue');
},
async dispatchById({ dispatchId }) {
calls.push(`dispatch:${dispatchId}`);
return {
scanned: 1,
claimed: 1,
started: 1,
succeeded: 1,
failed: 0,
blocked: 0,
retrying: 0,
deferred: 0,
recoveryRequired: 0,
alreadyTerminal: 0,
unavailable: 0,
truncated: false,
};
},
};
},
});
assert.deepEqual(calls, [
'open',
'ready',
'dispatch:dispatch.secret-binding.42',
'close',
]);
assert.equal(result.status, 'completed');
assert.equal(result.batches.length, 1);
assert.equal(result.batches[0].approvals.scanned, 0);
assert.equal(result.batches[0].dispatch.succeeded, 1);
});
test('rejects implicit insecure PostgreSQL and unbounded work', () => {
for (const invalid of [
environment({ QL3_POSTGRES_ALLOW_INSECURE: undefined }),
environment({ QL3_PLUGIN_PACKAGE_EXECUTOR_MAX_BATCHES: '65' }),
environment({ QL3_PLUGIN_PACKAGE_EXECUTOR_REVOCATION_PAGE_SIZE: '129' }),
environment({ QL3_PLUGIN_PACKAGE_EXECUTOR_OWNER: 'not safe' }),
environment({ QL3_PLUGIN_PACKAGE_EXECUTOR_SECRET_ROOT: 'relative/path' }),
environment({
QL3_PLUGIN_PACKAGE_EXECUTOR_DISPATCH_ID: 'dispatch id with spaces',
}),
]) {
assert.throws(
() => loadClusterPluginPackageExecutorProcessConfig(invalid),
ClusterPluginPackageExecutorProcessConfigError,
);
}
});
test('keeps executor authority off the cluster-admin root', () => {
const root = require('@qinglong/cluster-admin');
const manifest = require('../package.json');
assert.equal(root.runClusterPluginPackageExecutorProcess, undefined);
assert.equal(
manifest.bin['ql3-plugin-package-execute'],
'dist/plugin-package/executor/pluginPackageExecutorCli.js',
);
});