Files
pikasTech-unidesk/scripts/platform-infra-sub2api-codex-temp-unsched-contract-test.ts
T

87 lines
6.6 KiB
TypeScript

import { defaultCodexTempUnschedulablePolicy, renderSub2ApiTempUnschedulableCredentials } from "./src/platform-infra-sub2api-codex";
function assertCondition(condition: unknown, message: string, detail: unknown = {}): void {
if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`);
}
const policy = defaultCodexTempUnschedulablePolicy();
const credentials = renderSub2ApiTempUnschedulableCredentials(policy) as {
temp_unschedulable_enabled?: boolean;
temp_unschedulable_rules?: Array<{
error_code?: number;
keywords?: string[];
duration_minutes?: number;
description?: string;
}>;
pool_mode?: unknown;
};
const rules = credentials.temp_unschedulable_rules ?? [];
assertCondition(credentials.temp_unschedulable_enabled === true, "default policy must enable Sub2API temporary unschedulable mode", credentials);
assertCondition(Array.isArray(credentials.temp_unschedulable_rules), "Sub2API rules must be rendered as an array", credentials);
assertCondition(rules.length === policy.rules.length, "rendered rule count must match the UniDesk policy", { policy, credentials });
assertCondition(rules.every((rule, index) => rule.error_code === policy.rules[index]?.statusCode), "rules must map statusCode to Sub2API error_code", { policy, credentials });
assertCondition(rules.every((rule, index) => rule.duration_minutes === policy.rules[index]?.durationMinutes), "rules must map durationMinutes to Sub2API duration_minutes", { policy, credentials });
assertCondition(rules.every((rule, index) => JSON.stringify(rule.keywords ?? []) === JSON.stringify(policy.rules[index]?.keywords ?? [])), "rules must preserve policy keywords", { policy, credentials });
assertCondition(rules.every((rule, index) => rule.description === policy.rules[index]?.description), "rules must preserve policy descriptions", { policy, credentials });
assertCondition(!("pool_mode" in credentials), "pool_mode must not be enabled because it retries the same account instead of cooling it down", credentials);
assertCondition(!("api_key" in credentials) && !("base_url" in credentials), "temporary-unschedulable rendering must not include secrets or endpoints", credentials);
const accountState403Rule = rules.find((rule) => rule.error_code === 403);
const clientError400Rule = rules.find((rule) => rule.error_code === 400);
const quota429Rule = rules.find((rule) => rule.error_code === 429);
const successBody200Rule = rules.find((rule) => rule.error_code === 200);
const gateway502Rule = rules.find((rule) => rule.error_code === 502);
const serviceUnavailable503Rule = rules.find((rule) => rule.error_code === 503);
const gatewayTimeout504Rule = rules.find((rule) => rule.error_code === 504);
const largeContext413Rule = rules.find((rule) => rule.error_code === 413);
const cloudflare524Rule = rules.find((rule) => rule.error_code === 524);
const accountStatePhrases = ["weekly limit", "less than 10% of your weekly limit left", "run /status for a breakdown"];
const successBodyPhrase = "less than 10% of your weekly limit left";
assertCondition(successBody200Rule?.keywords?.length === 1 && successBody200Rule.keywords.includes(successBodyPhrase), "200 rendered rule must use the single stable success-body account-state phrase", successBody200Rule);
for (const keyword of ["invalid_encrypted_content", "encrypted content", "could not be verified", "bad_response_status_code", "暂不支持", "可用模型"]) {
assertCondition(clientError400Rule?.keywords?.includes(keyword), "400 rendered rule must catch upstream Responses compatibility and model-routing failures", { keyword, clientError400Rule });
}
for (const accountStatePhrase of accountStatePhrases) {
assertCondition(accountState403Rule?.keywords?.includes(accountStatePhrase), "403 rendered rule must preserve Codex account-state phrases", { accountStatePhrase, accountState403Rule });
assertCondition(quota429Rule?.keywords?.includes(accountStatePhrase), "429 rendered rule must preserve Codex account-state phrases", { accountStatePhrase, quota429Rule });
}
for (const keyword of ["model_not_found", "no available channel for model"]) {
assertCondition(serviceUnavailable503Rule?.keywords?.includes(keyword), "503 rendered rule must catch upstream model-routing failures", { keyword, serviceUnavailable503Rule });
}
for (const keyword of ["openai_error", "context length", "maximum context"]) {
assertCondition(largeContext413Rule?.keywords?.includes(keyword), "413 rendered rule must catch large-context upstream failures", { keyword, largeContext413Rule });
}
for (const keyword of ["unknown error", "upstream request failed", "context deadline exceeded", "context canceled"]) {
assertCondition(gateway502Rule?.keywords?.includes(keyword), "502 rendered rule must catch compact gateway timeout wrappers", { keyword, gateway502Rule });
}
for (const keyword of ["gateway timeout", "unknown error", "context deadline exceeded"]) {
assertCondition(gatewayTimeout504Rule?.keywords?.includes(keyword), "504 rendered rule must preserve gateway-timeout cooldown keyword", { keyword, gatewayTimeout504Rule });
}
for (const keyword of ["timeout", "a timeout occurred", "cloudflare", "upstream request failed", "unknown error", "context canceled", "recovered upstream error"]) {
assertCondition(cloudflare524Rule?.keywords?.includes(keyword), "524 rendered rule must catch Cloudflare timeout wrappers", { keyword, cloudflare524Rule });
}
const disabled = renderSub2ApiTempUnschedulableCredentials({ enabled: false, rules: policy.rules }) as {
temp_unschedulable_enabled?: boolean;
temp_unschedulable_rules?: unknown[];
};
assertCondition(disabled.temp_unschedulable_enabled === false, "disabled policy must explicitly disable Sub2API temporary unschedulable mode", disabled);
assertCondition(Array.isArray(disabled.temp_unschedulable_rules) && disabled.temp_unschedulable_rules.length === 0, "disabled policy must not leave stale rules active", disabled);
console.log(JSON.stringify({
ok: true,
checks: [
"temporary unschedulable policy renders to Sub2API credential field names",
"temporary unschedulable rendering follows the input policy without hard-coded policy gates",
"Codex account-state prompt uses one stable phrase, including the 200 success-body rule",
"upstream 400 Responses compatibility and model-routing failures render into the 400 cooldown rule",
"large-context upstream failures render into the 413 cooldown rule",
"upstream model-routing failures render into the 503 cooldown rule",
"gateway timeout wrappers render into the 504 cooldown rule",
"Cloudflare timeout wrappers render into the 524 cooldown rule",
"disabled policies clear runtime rules",
],
}));