refactor(test): simplify auth-profile-cooldowns tests with helpers
- Add makeStore() helper to eliminate repeated store object literals - Add withTempDir() helper to eliminate try/finally boilerplate - Merge cooldownKey edge cases into main describe block - Remove redundant markAuthProfileCooldown test (covered by markAuthProfileFailure) - Net reduction: 245 lines removed (57% smaller), 19→17 tests
This commit is contained in:
parent
e3caf006bb
commit
e954d29291
@ -20,7 +20,6 @@ import {
|
|||||||
clearAuthProfileCooldown,
|
clearAuthProfileCooldown,
|
||||||
cooldownKey,
|
cooldownKey,
|
||||||
isProfileInCooldown,
|
isProfileInCooldown,
|
||||||
markAuthProfileCooldown,
|
|
||||||
markAuthProfileFailure,
|
markAuthProfileFailure,
|
||||||
markAuthProfileUsed,
|
markAuthProfileUsed,
|
||||||
saveAuthProfileStore,
|
saveAuthProfileStore,
|
||||||
@ -28,6 +27,24 @@ import {
|
|||||||
import type { AuthProfileStore } from "./auth-profiles.js";
|
import type { AuthProfileStore } from "./auth-profiles.js";
|
||||||
import { AUTH_STORE_VERSION } from "./auth-profiles/constants.js";
|
import { AUTH_STORE_VERSION } from "./auth-profiles/constants.js";
|
||||||
|
|
||||||
|
// Test helpers
|
||||||
|
const makeStore = (usageStats?: AuthProfileStore["usageStats"]): AuthProfileStore => ({
|
||||||
|
version: AUTH_STORE_VERSION,
|
||||||
|
profiles: {
|
||||||
|
"openai:default": { type: "api_key", provider: "openai", key: "test" },
|
||||||
|
},
|
||||||
|
...(usageStats && { usageStats }),
|
||||||
|
});
|
||||||
|
|
||||||
|
async function withTempDir<T>(fn: (tempDir: string) => Promise<T>): Promise<T> {
|
||||||
|
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-auth-"));
|
||||||
|
try {
|
||||||
|
return await fn(tempDir);
|
||||||
|
} finally {
|
||||||
|
await fs.rm(tempDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
describe("auth profile cooldowns", () => {
|
describe("auth profile cooldowns", () => {
|
||||||
it("applies exponential backoff with a 1h cap", () => {
|
it("applies exponential backoff with a 1h cap", () => {
|
||||||
expect(calculateAuthProfileCooldownMs(1)).toBe(60_000);
|
expect(calculateAuthProfileCooldownMs(1)).toBe(60_000);
|
||||||
@ -39,9 +56,11 @@ describe("auth profile cooldowns", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("cooldownKey", () => {
|
describe("cooldownKey", () => {
|
||||||
it("returns profileId when model is not provided", () => {
|
it("returns profileId when model is not provided or empty", () => {
|
||||||
expect(cooldownKey("openai:default")).toBe("openai:default");
|
expect(cooldownKey("openai:default")).toBe("openai:default");
|
||||||
expect(cooldownKey("openai:default", undefined)).toBe("openai:default");
|
expect(cooldownKey("openai:default", undefined)).toBe("openai:default");
|
||||||
|
expect(cooldownKey("openai:default", "")).toBe("openai:default");
|
||||||
|
expect(cooldownKey("openai:default", " ")).toBe("openai:default");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns composite key when model is provided", () => {
|
it("returns composite key when model is provided", () => {
|
||||||
@ -52,44 +71,20 @@ describe("cooldownKey", () => {
|
|||||||
|
|
||||||
describe("isProfileInCooldown with per-model support", () => {
|
describe("isProfileInCooldown with per-model support", () => {
|
||||||
it("returns false when no cooldown exists", () => {
|
it("returns false when no cooldown exists", () => {
|
||||||
const store: AuthProfileStore = {
|
const store = makeStore();
|
||||||
version: AUTH_STORE_VERSION,
|
|
||||||
profiles: {
|
|
||||||
"openai:default": { type: "api_key", provider: "openai", key: "test" },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
expect(isProfileInCooldown(store, "openai:default")).toBe(false);
|
expect(isProfileInCooldown(store, "openai:default")).toBe(false);
|
||||||
expect(isProfileInCooldown(store, "openai:default", "gpt-4")).toBe(false);
|
expect(isProfileInCooldown(store, "openai:default", "gpt-4")).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("checks profile-level cooldown when model not provided", () => {
|
it("checks profile-level cooldown when model not provided", () => {
|
||||||
const store: AuthProfileStore = {
|
const store = makeStore({ "openai:default": { cooldownUntil: Date.now() + 60_000 } });
|
||||||
version: AUTH_STORE_VERSION,
|
|
||||||
profiles: {
|
|
||||||
"openai:default": { type: "api_key", provider: "openai", key: "test" },
|
|
||||||
},
|
|
||||||
usageStats: {
|
|
||||||
"openai:default": { cooldownUntil: Date.now() + 60_000 },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
expect(isProfileInCooldown(store, "openai:default")).toBe(true);
|
expect(isProfileInCooldown(store, "openai:default")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("checks per-model cooldown when model is provided", () => {
|
it("checks per-model cooldown when model is provided", () => {
|
||||||
const store: AuthProfileStore = {
|
const store = makeStore({ "openai:default:gpt-4": { cooldownUntil: Date.now() + 60_000 } });
|
||||||
version: AUTH_STORE_VERSION,
|
|
||||||
profiles: {
|
|
||||||
"openai:default": { type: "api_key", provider: "openai", key: "test" },
|
|
||||||
},
|
|
||||||
usageStats: {
|
|
||||||
"openai:default:gpt-4": { cooldownUntil: Date.now() + 60_000 },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
// model-specific cooldown exists
|
|
||||||
expect(isProfileInCooldown(store, "openai:default", "gpt-4")).toBe(true);
|
expect(isProfileInCooldown(store, "openai:default", "gpt-4")).toBe(true);
|
||||||
// different model is not in cooldown
|
|
||||||
expect(isProfileInCooldown(store, "openai:default", "gpt-3.5")).toBe(false);
|
expect(isProfileInCooldown(store, "openai:default", "gpt-3.5")).toBe(false);
|
||||||
// profile-level is not in cooldown
|
|
||||||
expect(isProfileInCooldown(store, "openai:default")).toBe(false);
|
expect(isProfileInCooldown(store, "openai:default")).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -97,55 +92,31 @@ describe("isProfileInCooldown with per-model support", () => {
|
|||||||
const store: AuthProfileStore = {
|
const store: AuthProfileStore = {
|
||||||
version: AUTH_STORE_VERSION,
|
version: AUTH_STORE_VERSION,
|
||||||
profiles: {
|
profiles: {
|
||||||
"github-copilot:default": {
|
"github-copilot:default": { type: "api_key", provider: "github-copilot", key: "test" },
|
||||||
type: "api_key",
|
|
||||||
provider: "github-copilot",
|
|
||||||
key: "test",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
usageStats: {
|
|
||||||
// gpt-5.2 is in cooldown (rate limited)
|
|
||||||
"github-copilot:default:gpt-5.2": { cooldownUntil: Date.now() + 60_000 },
|
|
||||||
// gpt-5-mini has no cooldown (unlimited quota)
|
|
||||||
},
|
},
|
||||||
|
usageStats: { "github-copilot:default:gpt-5.2": { cooldownUntil: Date.now() + 60_000 } },
|
||||||
};
|
};
|
||||||
expect(isProfileInCooldown(store, "github-copilot:default", "gpt-5.2")).toBe(true);
|
expect(isProfileInCooldown(store, "github-copilot:default", "gpt-5.2")).toBe(true);
|
||||||
expect(isProfileInCooldown(store, "github-copilot:default", "gpt-5-mini")).toBe(false);
|
expect(isProfileInCooldown(store, "github-copilot:default", "gpt-5-mini")).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns false when cooldown has expired", () => {
|
it("returns false when cooldown has expired", () => {
|
||||||
const store: AuthProfileStore = {
|
const store = makeStore({ "openai:default:gpt-4": { cooldownUntil: Date.now() - 1000 } });
|
||||||
version: AUTH_STORE_VERSION,
|
|
||||||
profiles: {
|
|
||||||
"openai:default": { type: "api_key", provider: "openai", key: "test" },
|
|
||||||
},
|
|
||||||
usageStats: {
|
|
||||||
"openai:default:gpt-4": { cooldownUntil: Date.now() - 1000 }, // expired
|
|
||||||
},
|
|
||||||
};
|
|
||||||
expect(isProfileInCooldown(store, "openai:default", "gpt-4")).toBe(false);
|
expect(isProfileInCooldown(store, "openai:default", "gpt-4")).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("markAuthProfileUsed with per-model support", () => {
|
describe("markAuthProfileUsed with per-model support", () => {
|
||||||
it("clears per-model cooldown when model is provided", async () => {
|
it("clears per-model cooldown when model is provided", async () => {
|
||||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-auth-"));
|
await withTempDir(async (tempDir) => {
|
||||||
const cooldownTime = Date.now() + 60_000;
|
const cooldownTime = Date.now() + 60_000;
|
||||||
const store: AuthProfileStore = {
|
const store = makeStore({
|
||||||
version: AUTH_STORE_VERSION,
|
|
||||||
profiles: {
|
|
||||||
"openai:default": { type: "api_key", provider: "openai", key: "test" },
|
|
||||||
},
|
|
||||||
usageStats: {
|
|
||||||
"openai:default": { cooldownUntil: cooldownTime },
|
"openai:default": { cooldownUntil: cooldownTime },
|
||||||
"openai:default:gpt-4": { cooldownUntil: cooldownTime, errorCount: 3 },
|
"openai:default:gpt-4": { cooldownUntil: cooldownTime, errorCount: 3 },
|
||||||
"openai:default:gpt-3.5": { cooldownUntil: cooldownTime },
|
"openai:default:gpt-3.5": { cooldownUntil: cooldownTime },
|
||||||
},
|
});
|
||||||
};
|
saveAuthProfileStore(store, tempDir);
|
||||||
saveAuthProfileStore(store, tempDir);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Mark gpt-4 as used (successful)
|
|
||||||
await markAuthProfileUsed({
|
await markAuthProfileUsed({
|
||||||
store,
|
store,
|
||||||
profileId: "openai:default",
|
profileId: "openai:default",
|
||||||
@ -153,84 +124,41 @@ describe("markAuthProfileUsed with per-model support", () => {
|
|||||||
agentDir: tempDir,
|
agentDir: tempDir,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Profile-level cooldown should be cleared
|
|
||||||
expect(store.usageStats?.["openai:default"]?.cooldownUntil).toBeUndefined();
|
expect(store.usageStats?.["openai:default"]?.cooldownUntil).toBeUndefined();
|
||||||
// Per-model cooldown for gpt-4 should be cleared
|
|
||||||
expect(store.usageStats?.["openai:default:gpt-4"]?.cooldownUntil).toBeUndefined();
|
expect(store.usageStats?.["openai:default:gpt-4"]?.cooldownUntil).toBeUndefined();
|
||||||
expect(store.usageStats?.["openai:default:gpt-4"]?.errorCount).toBe(0);
|
expect(store.usageStats?.["openai:default:gpt-4"]?.errorCount).toBe(0);
|
||||||
// Per-model cooldown for gpt-3.5 should remain (different model)
|
|
||||||
expect(store.usageStats?.["openai:default:gpt-3.5"]?.cooldownUntil).toBe(cooldownTime);
|
expect(store.usageStats?.["openai:default:gpt-3.5"]?.cooldownUntil).toBe(cooldownTime);
|
||||||
} finally {
|
});
|
||||||
await fs.rm(tempDir, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("only clears profile-level cooldown when model is not provided", async () => {
|
it("only clears profile-level cooldown when model is not provided", async () => {
|
||||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-auth-"));
|
await withTempDir(async (tempDir) => {
|
||||||
const cooldownTime = Date.now() + 60_000;
|
const cooldownTime = Date.now() + 60_000;
|
||||||
const store: AuthProfileStore = {
|
const store = makeStore({
|
||||||
version: AUTH_STORE_VERSION,
|
|
||||||
profiles: {
|
|
||||||
"openai:default": { type: "api_key", provider: "openai", key: "test" },
|
|
||||||
},
|
|
||||||
usageStats: {
|
|
||||||
"openai:default": { cooldownUntil: cooldownTime },
|
"openai:default": { cooldownUntil: cooldownTime },
|
||||||
"openai:default:gpt-4": { cooldownUntil: cooldownTime },
|
"openai:default:gpt-4": { cooldownUntil: cooldownTime },
|
||||||
},
|
});
|
||||||
};
|
saveAuthProfileStore(store, tempDir);
|
||||||
saveAuthProfileStore(store, tempDir);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Mark profile as used without specifying model
|
|
||||||
await markAuthProfileUsed({ store, profileId: "openai:default", agentDir: tempDir });
|
await markAuthProfileUsed({ store, profileId: "openai:default", agentDir: tempDir });
|
||||||
|
|
||||||
// Profile-level cooldown should be cleared
|
|
||||||
expect(store.usageStats?.["openai:default"]?.cooldownUntil).toBeUndefined();
|
expect(store.usageStats?.["openai:default"]?.cooldownUntil).toBeUndefined();
|
||||||
// Per-model cooldown should remain (no model specified)
|
|
||||||
expect(store.usageStats?.["openai:default:gpt-4"]?.cooldownUntil).toBe(cooldownTime);
|
expect(store.usageStats?.["openai:default:gpt-4"]?.cooldownUntil).toBe(cooldownTime);
|
||||||
} finally {
|
});
|
||||||
await fs.rm(tempDir, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("cooldownKey edge cases", () => {
|
|
||||||
it("treats empty string model the same as undefined", () => {
|
|
||||||
// Empty string should be treated as "no model" to avoid trailing colon
|
|
||||||
expect(cooldownKey("openai:default", "")).toBe("openai:default");
|
|
||||||
expect(cooldownKey("openai:default", " ")).toBe("openai:default");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("isProfileInCooldown backward compatibility", () => {
|
describe("isProfileInCooldown backward compatibility", () => {
|
||||||
it("returns true for any model when profile-level cooldown exists", () => {
|
it("returns true for any model when profile-level cooldown exists", () => {
|
||||||
const store: AuthProfileStore = {
|
const store = makeStore({ "openai:default": { cooldownUntil: Date.now() + 60_000 } });
|
||||||
version: AUTH_STORE_VERSION,
|
|
||||||
profiles: {
|
|
||||||
"openai:default": { type: "api_key", provider: "openai", key: "test" },
|
|
||||||
},
|
|
||||||
usageStats: {
|
|
||||||
"openai:default": { cooldownUntil: Date.now() + 60_000 }, // profile-level only
|
|
||||||
},
|
|
||||||
};
|
|
||||||
// Any model should be blocked when profile-level cooldown exists
|
|
||||||
expect(isProfileInCooldown(store, "openai:default", "gpt-4")).toBe(true);
|
expect(isProfileInCooldown(store, "openai:default", "gpt-4")).toBe(true);
|
||||||
expect(isProfileInCooldown(store, "openai:default", "gpt-3.5")).toBe(true);
|
expect(isProfileInCooldown(store, "openai:default", "gpt-3.5")).toBe(true);
|
||||||
expect(isProfileInCooldown(store, "openai:default", "o1-preview")).toBe(true);
|
expect(isProfileInCooldown(store, "openai:default", "o1-preview")).toBe(true);
|
||||||
// Profile-level check also works
|
|
||||||
expect(isProfileInCooldown(store, "openai:default")).toBe(true);
|
expect(isProfileInCooldown(store, "openai:default")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("checks disabledUntil for per-model cooldowns (billing failures)", () => {
|
it("checks disabledUntil for per-model cooldowns (billing failures)", () => {
|
||||||
const store: AuthProfileStore = {
|
const store = makeStore({ "openai:default:gpt-4": { disabledUntil: Date.now() + 60_000 } });
|
||||||
version: AUTH_STORE_VERSION,
|
|
||||||
profiles: {
|
|
||||||
"openai:default": { type: "api_key", provider: "openai", key: "test" },
|
|
||||||
},
|
|
||||||
usageStats: {
|
|
||||||
"openai:default:gpt-4": { disabledUntil: Date.now() + 60_000 }, // billing failure
|
|
||||||
},
|
|
||||||
};
|
|
||||||
expect(isProfileInCooldown(store, "openai:default", "gpt-4")).toBe(true);
|
expect(isProfileInCooldown(store, "openai:default", "gpt-4")).toBe(true);
|
||||||
expect(isProfileInCooldown(store, "openai:default", "gpt-3.5")).toBe(false);
|
expect(isProfileInCooldown(store, "openai:default", "gpt-3.5")).toBe(false);
|
||||||
});
|
});
|
||||||
@ -238,16 +166,10 @@ describe("isProfileInCooldown backward compatibility", () => {
|
|||||||
|
|
||||||
describe("markAuthProfileFailure with per-model support", () => {
|
describe("markAuthProfileFailure with per-model support", () => {
|
||||||
it("tracks failure per model when model is provided", async () => {
|
it("tracks failure per model when model is provided", async () => {
|
||||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-auth-"));
|
await withTempDir(async (tempDir) => {
|
||||||
const store: AuthProfileStore = {
|
const store = makeStore();
|
||||||
version: AUTH_STORE_VERSION,
|
saveAuthProfileStore(store, tempDir);
|
||||||
profiles: {
|
|
||||||
"openai:default": { type: "api_key", provider: "openai", key: "test" },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
saveAuthProfileStore(store, tempDir);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await markAuthProfileFailure({
|
await markAuthProfileFailure({
|
||||||
store,
|
store,
|
||||||
profileId: "openai:default",
|
profileId: "openai:default",
|
||||||
@ -256,29 +178,18 @@ describe("markAuthProfileFailure with per-model support", () => {
|
|||||||
agentDir: tempDir,
|
agentDir: tempDir,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Per-model key should have cooldown
|
|
||||||
expect(store.usageStats?.["openai:default:gpt-4"]?.cooldownUntil).toBeGreaterThan(Date.now());
|
expect(store.usageStats?.["openai:default:gpt-4"]?.cooldownUntil).toBeGreaterThan(Date.now());
|
||||||
expect(store.usageStats?.["openai:default:gpt-4"]?.errorCount).toBe(1);
|
expect(store.usageStats?.["openai:default:gpt-4"]?.errorCount).toBe(1);
|
||||||
// Profile-level should NOT have cooldown (only model-specific)
|
|
||||||
expect(store.usageStats?.["openai:default"]?.cooldownUntil).toBeUndefined();
|
expect(store.usageStats?.["openai:default"]?.cooldownUntil).toBeUndefined();
|
||||||
// Other models should not be affected
|
|
||||||
expect(store.usageStats?.["openai:default:gpt-3.5"]).toBeUndefined();
|
expect(store.usageStats?.["openai:default:gpt-3.5"]).toBeUndefined();
|
||||||
} finally {
|
});
|
||||||
await fs.rm(tempDir, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("tracks failure at profile level when model is not provided", async () => {
|
it("tracks failure at profile level when model is not provided", async () => {
|
||||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-auth-"));
|
await withTempDir(async (tempDir) => {
|
||||||
const store: AuthProfileStore = {
|
const store = makeStore();
|
||||||
version: AUTH_STORE_VERSION,
|
saveAuthProfileStore(store, tempDir);
|
||||||
profiles: {
|
|
||||||
"openai:default": { type: "api_key", provider: "openai", key: "test" },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
saveAuthProfileStore(store, tempDir);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await markAuthProfileFailure({
|
await markAuthProfileFailure({
|
||||||
store,
|
store,
|
||||||
profileId: "openai:default",
|
profileId: "openai:default",
|
||||||
@ -286,25 +197,16 @@ describe("markAuthProfileFailure with per-model support", () => {
|
|||||||
agentDir: tempDir,
|
agentDir: tempDir,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Profile-level key should have cooldown
|
|
||||||
expect(store.usageStats?.["openai:default"]?.cooldownUntil).toBeGreaterThan(Date.now());
|
expect(store.usageStats?.["openai:default"]?.cooldownUntil).toBeGreaterThan(Date.now());
|
||||||
expect(store.usageStats?.["openai:default"]?.errorCount).toBe(1);
|
expect(store.usageStats?.["openai:default"]?.errorCount).toBe(1);
|
||||||
} finally {
|
});
|
||||||
await fs.rm(tempDir, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("tracks billing failures with disabledUntil per model", async () => {
|
it("tracks billing failures with disabledUntil per model", async () => {
|
||||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-auth-"));
|
await withTempDir(async (tempDir) => {
|
||||||
const store: AuthProfileStore = {
|
const store = makeStore();
|
||||||
version: AUTH_STORE_VERSION,
|
saveAuthProfileStore(store, tempDir);
|
||||||
profiles: {
|
|
||||||
"openai:default": { type: "api_key", provider: "openai", key: "test" },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
saveAuthProfileStore(store, tempDir);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await markAuthProfileFailure({
|
await markAuthProfileFailure({
|
||||||
store,
|
store,
|
||||||
profileId: "openai:default",
|
profileId: "openai:default",
|
||||||
@ -313,62 +215,23 @@ describe("markAuthProfileFailure with per-model support", () => {
|
|||||||
agentDir: tempDir,
|
agentDir: tempDir,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Billing failures use disabledUntil instead of cooldownUntil
|
|
||||||
expect(store.usageStats?.["openai:default:gpt-4"]?.disabledUntil).toBeGreaterThan(Date.now());
|
expect(store.usageStats?.["openai:default:gpt-4"]?.disabledUntil).toBeGreaterThan(Date.now());
|
||||||
expect(store.usageStats?.["openai:default:gpt-4"]?.disabledReason).toBe("billing");
|
expect(store.usageStats?.["openai:default:gpt-4"]?.disabledReason).toBe("billing");
|
||||||
} finally {
|
});
|
||||||
await fs.rm(tempDir, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("markAuthProfileCooldown with per-model support", () => {
|
|
||||||
it("marks cooldown per model when model is provided", async () => {
|
|
||||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-auth-"));
|
|
||||||
const store: AuthProfileStore = {
|
|
||||||
version: AUTH_STORE_VERSION,
|
|
||||||
profiles: {
|
|
||||||
"openai:default": { type: "api_key", provider: "openai", key: "test" },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
saveAuthProfileStore(store, tempDir);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await markAuthProfileCooldown({
|
|
||||||
store,
|
|
||||||
profileId: "openai:default",
|
|
||||||
model: "gpt-4",
|
|
||||||
agentDir: tempDir,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Per-model key should have cooldown
|
|
||||||
expect(store.usageStats?.["openai:default:gpt-4"]?.cooldownUntil).toBeGreaterThan(Date.now());
|
|
||||||
// Profile-level should NOT have cooldown
|
|
||||||
expect(store.usageStats?.["openai:default"]?.cooldownUntil).toBeUndefined();
|
|
||||||
} finally {
|
|
||||||
await fs.rm(tempDir, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("clearAuthProfileCooldown with per-model support", () => {
|
describe("clearAuthProfileCooldown with per-model support", () => {
|
||||||
it("clears per-model cooldown when model is provided", async () => {
|
it("clears per-model cooldown when model is provided", async () => {
|
||||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-auth-"));
|
await withTempDir(async (tempDir) => {
|
||||||
const cooldownTime = Date.now() + 60_000;
|
const cooldownTime = Date.now() + 60_000;
|
||||||
const store: AuthProfileStore = {
|
const store = makeStore({
|
||||||
version: AUTH_STORE_VERSION,
|
|
||||||
profiles: {
|
|
||||||
"openai:default": { type: "api_key", provider: "openai", key: "test" },
|
|
||||||
},
|
|
||||||
usageStats: {
|
|
||||||
"openai:default": { cooldownUntil: cooldownTime },
|
"openai:default": { cooldownUntil: cooldownTime },
|
||||||
"openai:default:gpt-4": { cooldownUntil: cooldownTime, errorCount: 3 },
|
"openai:default:gpt-4": { cooldownUntil: cooldownTime, errorCount: 3 },
|
||||||
"openai:default:gpt-3.5": { cooldownUntil: cooldownTime },
|
"openai:default:gpt-3.5": { cooldownUntil: cooldownTime },
|
||||||
},
|
});
|
||||||
};
|
saveAuthProfileStore(store, tempDir);
|
||||||
saveAuthProfileStore(store, tempDir);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await clearAuthProfileCooldown({
|
await clearAuthProfileCooldown({
|
||||||
store,
|
store,
|
||||||
profileId: "openai:default",
|
profileId: "openai:default",
|
||||||
@ -376,47 +239,27 @@ describe("clearAuthProfileCooldown with per-model support", () => {
|
|||||||
agentDir: tempDir,
|
agentDir: tempDir,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Per-model cooldown for gpt-4 should be cleared
|
|
||||||
expect(store.usageStats?.["openai:default:gpt-4"]?.cooldownUntil).toBeUndefined();
|
expect(store.usageStats?.["openai:default:gpt-4"]?.cooldownUntil).toBeUndefined();
|
||||||
expect(store.usageStats?.["openai:default:gpt-4"]?.errorCount).toBe(0);
|
expect(store.usageStats?.["openai:default:gpt-4"]?.errorCount).toBe(0);
|
||||||
// Profile-level cooldown should remain (different key)
|
|
||||||
expect(store.usageStats?.["openai:default"]?.cooldownUntil).toBe(cooldownTime);
|
expect(store.usageStats?.["openai:default"]?.cooldownUntil).toBe(cooldownTime);
|
||||||
// Other model cooldown should remain
|
|
||||||
expect(store.usageStats?.["openai:default:gpt-3.5"]?.cooldownUntil).toBe(cooldownTime);
|
expect(store.usageStats?.["openai:default:gpt-3.5"]?.cooldownUntil).toBe(cooldownTime);
|
||||||
} finally {
|
});
|
||||||
await fs.rm(tempDir, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("clears profile-level cooldown when model is not provided", async () => {
|
it("clears profile-level cooldown when model is not provided", async () => {
|
||||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-auth-"));
|
await withTempDir(async (tempDir) => {
|
||||||
const cooldownTime = Date.now() + 60_000;
|
const cooldownTime = Date.now() + 60_000;
|
||||||
const store: AuthProfileStore = {
|
const store = makeStore({
|
||||||
version: AUTH_STORE_VERSION,
|
|
||||||
profiles: {
|
|
||||||
"openai:default": { type: "api_key", provider: "openai", key: "test" },
|
|
||||||
},
|
|
||||||
usageStats: {
|
|
||||||
"openai:default": { cooldownUntil: cooldownTime, errorCount: 2 },
|
"openai:default": { cooldownUntil: cooldownTime, errorCount: 2 },
|
||||||
"openai:default:gpt-4": { cooldownUntil: cooldownTime },
|
"openai:default:gpt-4": { cooldownUntil: cooldownTime },
|
||||||
},
|
|
||||||
};
|
|
||||||
saveAuthProfileStore(store, tempDir);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await clearAuthProfileCooldown({
|
|
||||||
store,
|
|
||||||
profileId: "openai:default",
|
|
||||||
agentDir: tempDir,
|
|
||||||
});
|
});
|
||||||
|
saveAuthProfileStore(store, tempDir);
|
||||||
|
|
||||||
|
await clearAuthProfileCooldown({ store, profileId: "openai:default", agentDir: tempDir });
|
||||||
|
|
||||||
// Profile-level cooldown should be cleared
|
|
||||||
expect(store.usageStats?.["openai:default"]?.cooldownUntil).toBeUndefined();
|
expect(store.usageStats?.["openai:default"]?.cooldownUntil).toBeUndefined();
|
||||||
expect(store.usageStats?.["openai:default"]?.errorCount).toBe(0);
|
expect(store.usageStats?.["openai:default"]?.errorCount).toBe(0);
|
||||||
// Per-model cooldown should remain (different key)
|
|
||||||
expect(store.usageStats?.["openai:default:gpt-4"]?.cooldownUntil).toBe(cooldownTime);
|
expect(store.usageStats?.["openai:default:gpt-4"]?.cooldownUntil).toBe(cooldownTime);
|
||||||
} finally {
|
});
|
||||||
await fs.rm(tempDir, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user