Files
my-pi/pi-compaction/test/native-provider.test.ts
云服务部-叶林立 ff1e7400da feat(compaction): support SubAPI native remote compaction
- support sub-api/openai-responses through the public Responses endpoint
- preserve explicit Authorization and support header-only authentication
- retain Codex JWT requirements and native compaction failure fallback
- restore SubAPI checkpoints and preserve portable model-switching summaries
- add provider, authentication and checkpoint lifecycle regression tests
- document compatibility requirements and the Sub2API protocol reference
2026-09-07 16:25:48 +08:00

117 lines
5.7 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import { buildCodexHeaders, mergeFeatureHeader } from "../src/native-compaction.ts";
import { buildNativeCompactionTransport, isNativeCompactionModel } from "../src/native-provider.ts";
const subApi = {
provider: "sub-api",
api: "openai-responses",
id: "gpt-test",
baseUrl: "https://gateway.example/v1",
};
const params = { model: subApi, apiKey: "test-api-key", sessionId: "session-test" };
test("native compaction eligibility is limited to the supported provider/API pairs", () => {
assert.equal(isNativeCompactionModel(subApi), true);
assert.equal(isNativeCompactionModel({ provider: "openai-codex", api: "openai-codex-responses" }), true);
for (const model of [
null, {},
{ ...subApi, provider: "another-gateway" },
{ ...subApi, api: "openai-completions" },
{ ...subApi, api: "openai-codex-responses" },
{ provider: "openai-codex", api: "openai-responses" },
]) assert.equal(isNativeCompactionModel(model), false);
});
test("SubAPI uses its public Responses URL and API key without Codex account parsing", () => {
const { url, headers } = buildNativeCompactionTransport({
...params,
headers: { "x-gateway-route": "test", "x-codex-beta-features": "existing-feature" },
});
assert.equal(url, "https://gateway.example/v1/responses");
assert.equal(headers.get("authorization"), "Bearer test-api-key");
assert.equal(headers.has("chatgpt-account-id"), false);
assert.equal(headers.get("x-gateway-route"), "test");
assert.equal(headers.get("content-type"), "application/json");
assert.equal(headers.get("accept"), "text/event-stream");
assert.equal(headers.get("session-id"), "session-test");
assert.equal(headers.get("x-client-request-id"), "session-test");
assert.equal(headers.get("x-codex-beta-features"), mergeFeatureHeader("existing-feature"));
});
test("SubAPI URL normalization preserves prefixes and avoids duplicating responses", () => {
for (const [baseUrl, expected] of [
["https://gateway.example/v1/", "https://gateway.example/v1/responses"],
["https://gateway.example/v1/responses", "https://gateway.example/v1/responses"],
["https://gateway.example/v1/responses/", "https://gateway.example/v1/responses"],
["https://gateway.example/prefix/v1?route=test", "https://gateway.example/prefix/v1/responses?route=test"],
["http://localhost:8080", "http://localhost:8080/responses"],
]) {
assert.equal(buildNativeCompactionTransport({ ...params, model: { ...subApi, baseUrl } }).url, expected);
}
});
test("invalid SubAPI endpoints and unsupported providers fail without a Codex fallback", () => {
for (const baseUrl of [undefined, "", " ", "/v1", "invalid", "file:///tmp/test", "https://user:secret@gateway.example/v1", "https://gateway.example/v1#fragment"]) {
assert.throws(() => buildNativeCompactionTransport({ ...params, model: { ...subApi, baseUrl } }), /SubAPI/);
}
assert.throws(() => buildNativeCompactionTransport({
...params, model: { ...subApi, provider: "other" },
}), /not supported/);
});
test("official Codex transport retains its URL, JWT account ID and headers", () => {
const payload = Buffer.from(JSON.stringify({
"https://api.openai.com/auth": { chatgpt_account_id: "test-account" },
})).toString("base64url");
const codexParams = {
...params,
model: { provider: "openai-codex", api: "openai-codex-responses", id: "gpt-test" },
apiKey: `header.${payload}.signature`,
headers: { "x-custom": "preserved" },
};
const transport = buildNativeCompactionTransport(codexParams);
assert.equal(transport.url, "https://chatgpt.com/backend-api/codex/responses");
assert.deepEqual([...transport.headers], [...buildCodexHeaders(codexParams)]);
assert.equal(transport.headers.get("chatgpt-account-id"), "test-account");
assert.throws(() => buildNativeCompactionTransport({ ...codexParams, apiKey: "ordinary-key" }), /account ID/);
});
test("SubAPI preserves explicit Authorization ahead of its API key, case-insensitively", () => {
for (const name of ["Authorization", "authorization", "AUTHORIZATION"]) {
const { headers } = buildNativeCompactionTransport({
...params, headers: { [name]: "Bearer explicit-token" },
});
assert.equal(headers.get("authorization"), "Bearer explicit-token");
}
});
test("SubAPI accepts header-only authentication without synthesizing a bearer token", () => {
for (const name of ["Authorization", "CF-AIG-Authorization"]) {
const { headers } = buildNativeCompactionTransport({
...params, apiKey: undefined, headers: { [name]: "Bearer header-only-token" },
});
assert.equal(headers.get(name), "Bearer header-only-token");
if (name === "CF-AIG-Authorization") assert.equal(headers.has("authorization"), false);
}
});
test("SubAPI rejects absent or blank credentials but can fall back to a real API key", () => {
const candidates: Record<string, string>[] = [{}, { Authorization: " " }, { "cf-aig-authorization": " " }, { "x-api-key": "not-supported" }];
for (const headers of candidates) {
for (const apiKey of [undefined, "", " "]) {
assert.throws(() => buildNativeCompactionTransport({ ...params, apiKey, headers }), /authentication/i);
}
}
const result = buildNativeCompactionTransport({ ...params, headers: { Authorization: " " } });
assert.equal(result.headers.get("authorization"), "Bearer test-api-key");
});
test("official Codex still requires an API key even with explicit header credentials", () => {
assert.throws(() => buildNativeCompactionTransport({
...params, apiKey: undefined,
model: { provider: "openai-codex", api: "openai-codex-responses" },
headers: { Authorization: "Bearer header-only-token" },
}), /Codex.*authentication/i);
});