import { afterEach, beforeEach, describe, expect, it, vi } from "vitest" ;
import type { ProviderExternalAuthProfile } from "../../plugins/types.js" ;
import {
__testing,
overlayExternalOAuthProfiles,
shouldPersistExternalOAuthProfile,
} from "./external-auth.js" ;
import type { AuthProfileStore, OAuthCredential } from "./types.js" ;
const resolveExternalAuthProfilesWithPluginsMock = vi.fn<
(params: unknown) => ProviderExternalAuthProfile[]
>(() => []);
const readCodexCliCredentialsCachedMock = vi.hoisted(() =>
vi.fn<() => OAuthCredential | null >(() => null ),
);
vi.mock("../cli-credentials.js" , () => ({
readCodexCliCredentialsCached: readCodexCliCredentialsCachedMock,
readMiniMaxCliCredentialsCached: () => null ,
}));
function createStore(profiles: AuthProfileStore["profiles" ] = {}): AuthProfileStore {
return { version: 1 , profiles };
}
function createCredential(overrides: Partial<OAuthCredential> = {}): OAuthCredential {
return {
type: "oauth" ,
provider: "openai-codex" ,
access: "access-token" ,
refresh: "refresh-token" ,
expires: 123 ,
...overrides,
};
}
function createUsableOAuthExpiry(): number {
// Keep fixtures comfortably outside the shared near-expiry refresh margin.
return Date.now() + 30 * 60 * 1000 ;
}
describe("auth external oauth helpers" , () => {
beforeEach(() => {
resolveExternalAuthProfilesWithPluginsMock.mockReset();
resolveExternalAuthProfilesWithPluginsMock.mockReturnValue([]);
readCodexCliCredentialsCachedMock.mockReset();
readCodexCliCredentialsCachedMock.mockReturnValue(null );
__testing.setResolveExternalAuthProfilesForTest(resolveExternalAuthProfilesWithPluginsMock);
});
afterEach(() => {
__testing.resetResolveExternalAuthProfilesForTest();
});
it("overlays provider-managed runtime oauth profiles onto the store" , () => {
resolveExternalAuthProfilesWithPluginsMock.mockReturnValueOnce([
{
profileId: "openai-codex:default" ,
credential: createCredential(),
},
]);
const store = overlayExternalOAuthProfiles(createStore());
expect(store.profiles["openai-codex:default" ]).toMatchObject({
type: "oauth" ,
provider: "openai-codex" ,
access: "access-token" ,
});
});
it("omits exact runtime-only overlays from persisted store writes" , () => {
const credential = createCredential();
resolveExternalAuthProfilesWithPluginsMock.mockReturnValueOnce([
{
profileId: "openai-codex:default" ,
credential,
},
]);
const shouldPersist = shouldPersistExternalOAuthProfile({
store: createStore({ "openai-codex:default" : credential }),
profileId: "openai-codex:default" ,
credential,
});
expect(shouldPersist).toBe(false );
});
it("keeps persisted copies when the external overlay is marked persisted" , () => {
const credential = createCredential();
resolveExternalAuthProfilesWithPluginsMock.mockReturnValueOnce([
{
profileId: "openai-codex:default" ,
credential,
persistence: "persisted" ,
},
]);
const shouldPersist = shouldPersistExternalOAuthProfile({
store: createStore({ "openai-codex:default" : credential }),
profileId: "openai-codex:default" ,
credential,
});
expect(shouldPersist).toBe(true );
});
it("keeps stale local copies when runtime overlay no longer matches" , () => {
const credential = createCredential();
resolveExternalAuthProfilesWithPluginsMock.mockReturnValueOnce([
{
profileId: "openai-codex:default" ,
credential: createCredential({ access: "fresh-access-token" }),
},
]);
const shouldPersist = shouldPersistExternalOAuthProfile({
store: createStore({ "openai-codex:default" : credential }),
profileId: "openai-codex:default" ,
credential,
});
expect(shouldPersist).toBe(true );
});
it("does not use Codex CLI OAuth as a runtime overlay source" , () => {
readCodexCliCredentialsCachedMock.mockReturnValue(
createCredential({
access: "fresh-cli-access-token" ,
refresh: "fresh-cli-refresh-token" ,
expires: createUsableOAuthExpiry(),
accountId: "acct-cli" ,
}),
);
const overlaid = overlayExternalOAuthProfiles(
createStore({
"openai-codex:default" : createCredential({
access: "stale-store-access-token" ,
refresh: "stale-store-refresh-token" ,
expires: Date.now() - 60 _000 ,
accountId: "acct-cli" ,
}),
}),
);
expect(overlaid.profiles["openai-codex:default" ]).toMatchObject({
access: "stale-store-access-token" ,
refresh: "stale-store-refresh-token" ,
accountId: "acct-cli" ,
});
});
it("keeps healthy local oauth even when external cli has a fresher token" , () => {
readCodexCliCredentialsCachedMock.mockReturnValue(
createCredential({
access: "fresh-cli-access-token" ,
refresh: "fresh-cli-refresh-token" ,
expires: Date.now() + 5 * 24 * 60 * 60 _000 ,
}),
);
const overlaid = overlayExternalOAuthProfiles(
createStore({
"openai-codex:default" : createCredential({
access: "healthy-local-access-token" ,
refresh: "healthy-local-refresh-token" ,
expires: createUsableOAuthExpiry(),
}),
}),
);
expect(overlaid.profiles["openai-codex:default" ]).toMatchObject({
access: "healthy-local-access-token" ,
refresh: "healthy-local-refresh-token" ,
});
});
it("keeps explicit local non-oauth auth over external cli oauth overlays" , () => {
readCodexCliCredentialsCachedMock.mockReturnValue(
createCredential({
access: "fresh-cli-access-token" ,
refresh: "fresh-cli-refresh-token" ,
expires: Date.now() + 5 * 24 * 60 * 60 _000 ,
}),
);
const overlaid = overlayExternalOAuthProfiles(
createStore({
"openai-codex:default" : {
type: "api_key" ,
provider: "openai-codex" ,
key: "sk-local" ,
},
}),
);
expect(overlaid.profiles["openai-codex:default" ]).toMatchObject({
type: "api_key" ,
provider: "openai-codex" ,
key: "sk-local" ,
});
});
it("keeps expired local oauth when external cli belongs to a different account" , () => {
readCodexCliCredentialsCachedMock.mockReturnValue(
createCredential({
access: "fresh-cli-access-token" ,
refresh: "fresh-cli-refresh-token" ,
expires: createUsableOAuthExpiry(),
accountId: "acct-external" ,
}),
);
const overlaid = overlayExternalOAuthProfiles(
createStore({
"openai-codex:default" : createCredential({
access: "expired-local-access-token" ,
refresh: "expired-local-refresh-token" ,
expires: Date.now() - 60 _000 ,
accountId: "acct-local" ,
}),
}),
);
expect(overlaid.profiles["openai-codex:default" ]).toMatchObject({
access: "expired-local-access-token" ,
refresh: "expired-local-refresh-token" ,
accountId: "acct-local" ,
});
});
});
Messung V0.5 in Prozent C=100 H=100 G=100
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-05)
¤
*© Formatika GbR, Deutschland