Files
qinglong/packages/ql3-ai/test/modelProviderCredentialCatalog.test.cjs
T

104 lines
3.4 KiB
JavaScript

const assert = require('node:assert/strict');
const test = require('node:test');
const {
MODEL_PROVIDER_CREDENTIAL_TRANSITION_COMMAND_SCHEMA,
InvalidModelProviderCredentialTransitionError,
ModelProviderCredentialTransitionConflictError,
createModelProviderCredentialTransition,
createModelProviderCredentialTransitionCommand,
modelProviderCredentialBindingForTransition,
normalizeModelProviderCredentialTransition,
} = require('../dist/model-provider-credential/modelProviderCredentialCatalog.js');
const {
MODEL_PROVIDER_CREDENTIAL_BINDING_SCHEMA,
} = require('../dist/model-provider-credential/providerCredential.js');
const { createSecretRef } = require('@qinglong/runtime-core/secret-reference');
function binding(overrides = {}) {
return {
schema: MODEL_PROVIDER_CREDENTIAL_BINDING_SCHEMA,
projectId: 'project-a',
provider: 'openai-compatible',
revision: 'credential-v1',
secretRef: createSecretRef({
projectId: 'project-a',
name: 'openai-token',
}),
scheme: 'bearer',
...overrides,
};
}
function command(overrides = {}) {
return createModelProviderCredentialTransitionCommand({
schema: MODEL_PROVIDER_CREDENTIAL_TRANSITION_COMMAND_SCHEMA,
mutationId: 'credential-bind-1',
projectId: 'project-a',
provider: 'openai-compatible',
expectedGeneration: 0,
action: 'bind',
binding: binding(),
changedBy: { type: 'user', id: 'owner-a' },
...overrides,
});
}
test('credential catalog creates an immutable bind/revoke hash chain', () => {
const bind = command();
const first = createModelProviderCredentialTransition(bind, null, 10);
assert.equal(first.generation, 1);
assert.equal(first.action, 'bind');
assert.equal(first.previousTransitionDigest, null);
assert.deepEqual(
modelProviderCredentialBindingForTransition(first, bind.binding),
bind.binding,
);
assert.deepEqual(normalizeModelProviderCredentialTransition(first), first);
const revoke = command({
mutationId: 'credential-revoke-2',
expectedGeneration: 1,
action: 'revoke',
binding: null,
});
const second = createModelProviderCredentialTransition(revoke, first, 20);
assert.equal(second.generation, 2);
assert.equal(second.action, 'revoke');
assert.equal(second.previousTransitionDigest, first.transitionDigest);
assert.equal(modelProviderCredentialBindingForTransition(second, null), null);
});
test('credential catalog rejects cross-Project binding and stale CAS', () => {
assert.throws(
() => command({ binding: binding({ projectId: 'project-b' }) }),
InvalidModelProviderCredentialTransitionError,
);
const firstCommand = command();
const first = createModelProviderCredentialTransition(firstCommand, null, 1);
assert.throws(
() => createModelProviderCredentialTransition(command(), first, 2),
ModelProviderCredentialTransitionConflictError,
);
});
test('credential transition tampering is detected before storage use', () => {
const first = createModelProviderCredentialTransition(command(), null, 1);
assert.throws(
() =>
normalizeModelProviderCredentialTransition({
...first,
activeBindingRevision: 'credential-v2',
}),
InvalidModelProviderCredentialTransitionError,
);
assert.throws(
() =>
modelProviderCredentialBindingForTransition(
first,
binding({ revision: 'credential-v2' }),
),
/unavailable/,
);
});