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

258 lines
7.4 KiB
JavaScript

const assert = require('node:assert/strict');
const test = require('node:test');
const {
createModelPriceCatalogHead,
createModelPriceCatalogPublication,
createModelPriceCatalogPublishCommand,
} = require('../dist/pricing/modelPriceCatalog.js');
const {
InvalidModelPriceCatalogManagementValueError,
ModelPriceCatalogManagementAuthenticationError,
ModelPriceCatalogManagementAuthorizationError,
ModelPriceCatalogManagementSeparationOfDutyError,
createModelPriceCatalogAuthorization,
createModelPriceCatalogManagementService,
createModelPriceCatalogPolicyDecision,
normalizeModelPriceCatalogAuthorization,
normalizeModelPriceCatalogAuthorizationCommand,
} = require('../dist/pricing/modelPriceCatalogManagement.js');
const NOW = 2_000_000;
function principal(userId, assurance = 'multi_factor') {
return {
subject: { type: 'user', id: userId },
authenticationId: `auth-${userId}`,
authenticatedAtMs: NOW - 1_000,
expiresAtMs: NOW + 60_000,
assurance,
};
}
function policy(effect = 'allow') {
return createModelPriceCatalogPolicyDecision({
effect,
revision: 'platform-policy-7',
reasons: [effect === 'allow' ? 'catalog_operator' : 'catalog_denied'],
});
}
function fakeRepository(publishedByUserId = 'publisher') {
const calls = [];
return {
calls,
async findPublication(lookup) {
calls.push(['findPublication', lookup]);
return createModelPriceCatalogPublication(
createModelPriceCatalogPublishCommand({
provider: lookup.provider,
model: lookup.model,
priceRevision: lookup.priceRevision,
currency: 'USD',
inputMicrosPerMillionTokens: 100,
outputMicrosPerMillionTokens: 400,
mutationId: 'existing-publication',
publishedByUserId,
}),
NOW - 100,
);
},
async findCurrent() {
return null;
},
async findAuthorization() {
return null;
},
async publish() {
throw new Error('raw publish must not be used');
},
async transition() {
throw new Error('raw transition must not be used');
},
async resolve() {
return null;
},
async publishAuthorized(command, authorizationCommand) {
calls.push(['publishAuthorized', command, authorizationCommand]);
const publication = createModelPriceCatalogPublication(command, NOW);
const authorization = createModelPriceCatalogAuthorization(
authorizationCommand,
publication.publicationDigest,
NOW,
);
return { status: 'created', publication, authorization };
},
async transitionAuthorized(command, authorizationCommand) {
calls.push(['transitionAuthorized', command, authorizationCommand]);
const publication = await this.findPublication({
provider: command.provider,
model: command.model,
priceRevision: command.priceRevision,
});
const head = createModelPriceCatalogHead(
null,
command,
publication,
false,
NOW,
);
const authorization = createModelPriceCatalogAuthorization(
authorizationCommand,
head.headDigest,
NOW,
);
return { status: 'created', head, authorization };
},
};
}
function publishRequest(userId = 'publisher') {
return {
authorizationId: 'authorize-publish-1',
requestId: 'request-publish-1',
mutationId: 'publish-price-1',
provider: 'remote',
model: 'model-a',
principal: principal(userId),
priceRevision: 'price-1',
currency: 'USD',
inputMicrosPerMillionTokens: 150_000,
outputMicrosPerMillionTokens: 600_000,
};
}
function transitionRequest(userId = 'reviewer') {
return {
authorizationId: 'authorize-activate-1',
requestId: 'request-activate-1',
mutationId: 'activate-price-1',
provider: 'remote',
model: 'model-a',
principal: principal(userId),
expectedGeneration: 0,
expectedHeadDigest: null,
action: 'activate',
priceRevision: 'price-1',
};
}
test('authorization fact binds strong User, policy, catalog command and commit', () => {
const decision = policy();
const serviceRepository = fakeRepository();
const service = createModelPriceCatalogManagementService(serviceRepository, {
decisionMode: 'human_confirmation',
authorizer: {
async authorize() {
return decision;
},
},
now: () => NOW,
});
return service.publish(publishRequest()).then((result) => {
assert.equal(result.authorization.operation, 'publish');
assert.equal(result.authorization.principal.subject.id, 'publisher');
assert.equal(result.authorization.policy.revision, 'platform-policy-7');
assert.equal(
result.authorization.resultDigest,
result.publication.publicationDigest,
);
assert.deepEqual(
normalizeModelPriceCatalogAuthorization(result.authorization),
result.authorization,
);
assert.deepEqual(
normalizeModelPriceCatalogAuthorizationCommand(
serviceRepository.calls[0][2],
),
serviceRepository.calls[0][2],
);
assert.throws(
() =>
normalizeModelPriceCatalogAuthorization({
...result.authorization,
resultDigest: 'f'.repeat(64),
}),
InvalidModelPriceCatalogManagementValueError,
);
});
});
test('management rejects weak principals and deny decisions before mutation', async () => {
const repository = fakeRepository();
let authorizations = 0;
const service = createModelPriceCatalogManagementService(repository, {
decisionMode: 'human_confirmation',
authorizer: {
async authorize() {
authorizations += 1;
return policy('deny');
},
},
now: () => NOW,
});
await assert.rejects(
service.publish({
...publishRequest(),
principal: principal('publisher', 'single_factor'),
}),
ModelPriceCatalogManagementAuthenticationError,
);
assert.equal(authorizations, 0);
await assert.rejects(
service.publish(publishRequest()),
ModelPriceCatalogManagementAuthorizationError,
);
assert.equal(authorizations, 1);
assert.equal(
repository.calls.some(([name]) => name === 'publishAuthorized'),
false,
);
});
test('cluster activation requires a different strong publishing User', async () => {
const sameUserRepository = fakeRepository('publisher');
const sameUserService = createModelPriceCatalogManagementService(
sameUserRepository,
{
decisionMode: 'separation_of_duty',
authorizer: {
async authorize() {
return policy();
},
},
now: () => NOW,
},
);
await assert.rejects(
sameUserService.transition(transitionRequest('publisher')),
ModelPriceCatalogManagementSeparationOfDutyError,
);
assert.equal(
sameUserRepository.calls.some(([name]) => name === 'transitionAuthorized'),
false,
);
const reviewedRepository = fakeRepository('publisher');
const reviewedService = createModelPriceCatalogManagementService(
reviewedRepository,
{
decisionMode: 'separation_of_duty',
authorizer: {
async authorize() {
return policy();
},
},
now: () => NOW,
},
);
const result = await reviewedService.transition(
transitionRequest('reviewer'),
);
assert.equal(result.head.activePriceRevision, 'price-1');
assert.equal(result.authorization.principal.subject.id, 'reviewer');
assert.equal(result.authorization.decisionMode, 'separation_of_duty');
});