Tests: add campaign-2 controller characterization coverage

This commit is contained in:
joshavant
2026-04-09 20:08:48 -05:00
committed by Josh Avant
parent d5284a0d40
commit 6a21c0fba9
3 changed files with 157 additions and 1 deletions

View File

@@ -173,6 +173,30 @@ describe("loadToolsCatalog", () => {
expect(state.toolsCatalogError).toContain("gateway unavailable");
expect(state.toolsCatalogLoading).toBe(false);
});
it("ignores catalog responses after selected agent changes mid-request", async () => {
const { state, request } = createState();
const resolvers: Array<(value: unknown) => void> = [];
request.mockImplementation(
() =>
new Promise((resolve) => {
resolvers.push(resolve);
}),
);
const pending = loadToolsCatalog(state, "main");
state.agentsSelectedId = "other-agent";
resolvers.shift()?.({
agentId: "main",
profiles: [{ id: "full", label: "Full" }],
groups: [],
});
await pending;
expect(state.toolsCatalogResult).toBeNull();
expect(state.toolsCatalogError).toBeNull();
expect(state.toolsCatalogLoading).toBe(false);
});
});
describe("loadToolsEffective", () => {
@@ -224,6 +248,31 @@ describe("loadToolsEffective", () => {
expect(state.toolsEffectiveLoading).toBe(false);
});
it("ignores effective-tool responses after selected agent changes mid-request", async () => {
const { state, request } = createState();
const resolvers: Array<(value: unknown) => void> = [];
request.mockImplementation(
() =>
new Promise((resolve) => {
resolvers.push(resolve);
}),
);
const pending = loadToolsEffective(state, { agentId: "main", sessionKey: "main" });
state.agentsSelectedId = "other-agent";
resolvers.shift()?.({
agentId: "main",
profile: "coding",
groups: [],
});
await pending;
expect(state.toolsEffectiveResult).toBeNull();
expect(state.toolsEffectiveResultKey).toBeNull();
expect(state.toolsEffectiveError).toBeNull();
expect(state.toolsEffectiveLoading).toBe(false);
});
it("uses the catalog provider when the active session reports a stale provider", async () => {
const { state, request } = createState();
const sessionsResult = state.sessionsResult!;

View File

@@ -1,6 +1,7 @@
import { describe, expect, it, vi } from "vitest";
import {
installSkill,
loadClawHubDetail,
saveSkillApiKey,
searchClawHub,
setClawHubSearchQuery,
@@ -113,6 +114,63 @@ describe("searchClawHub", () => {
expect(state.clawhubSearchError).toBeNull();
expect(state.clawhubSearchLoading).toBe(false);
});
it("ignores stale search responses after query changes", async () => {
const { state, request } = createState();
const resolvers: Array<(value: unknown) => void> = [];
request.mockImplementation(
() =>
new Promise((resolve) => {
resolvers.push(resolve);
}),
);
const pending = searchClawHub(state, "github");
setClawHubSearchQuery(state, "gitlab");
resolvers.shift()?.({
results: [{ score: 1, slug: "github", displayName: "GitHub" }],
});
await pending;
expect(state.clawhubSearchQuery).toBe("gitlab");
expect(state.clawhubSearchResults).toBeNull();
expect(state.clawhubSearchError).toBeNull();
expect(state.clawhubSearchLoading).toBe(false);
});
});
describe("loadClawHubDetail", () => {
it("ignores stale detail responses after slug changes", async () => {
const { state, request } = createState();
const resolvers: Array<(value: unknown) => void> = [];
request.mockImplementation(
() =>
new Promise((resolve) => {
resolvers.push(resolve);
}),
);
const firstPending = loadClawHubDetail(state, "github");
const secondPending = loadClawHubDetail(state, "gitlab");
resolvers.shift()?.({
skill: { slug: "github", displayName: "GitHub", createdAt: 1, updatedAt: 2 },
});
await firstPending;
expect(state.clawhubDetailSlug).toBe("gitlab");
expect(state.clawhubDetail).toBeNull();
expect(state.clawhubDetailError).toBeNull();
expect(state.clawhubDetailLoading).toBe(true);
resolvers.shift()?.({
skill: { slug: "gitlab", displayName: "GitLab", createdAt: 3, updatedAt: 4 },
});
await secondPending;
expect(state.clawhubDetailLoading).toBe(false);
expect(state.clawhubDetail?.skill?.slug).toBe("gitlab");
});
});
describe("skill mutations", () => {

View File

@@ -1,5 +1,11 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { __test, loadUsage, type UsageState } from "./usage.ts";
import {
__test,
loadSessionLogs,
loadSessionTimeSeries,
loadUsage,
type UsageState,
} from "./usage.ts";
type RequestFn = (method: string, params?: unknown) => Promise<unknown>;
@@ -162,6 +168,49 @@ describe("usage controller date interpretation params", () => {
});
});
describe("usage session detail loaders", () => {
beforeEach(() => {
__test.resetLegacyUsageDateParamsCache();
});
afterEach(() => {
vi.restoreAllMocks();
});
it("keeps optional loaders resilient when requests fail", async () => {
const request = vi.fn(async (method: string) => {
if (method === "sessions.usage.timeseries" || method === "sessions.usage.logs") {
throw new Error("optional endpoint unavailable");
}
return {};
});
const state = createState(request);
await loadSessionTimeSeries(state, "session-1");
await loadSessionLogs(state, "session-1");
expect(state.usageTimeSeries).toBeNull();
expect(state.usageSessionLogs).toBeNull();
expect(state.usageTimeSeriesLoading).toBe(false);
expect(state.usageSessionLogsLoading).toBe(false);
});
it("normalizes usage logs payloads when logs is not an array", async () => {
const request = vi.fn(async (method: string) => {
if (method === "sessions.usage.logs") {
return { logs: "unexpected-shape" };
}
return {};
});
const state = createState(request);
await loadSessionLogs(state, "session-1");
expect(state.usageSessionLogs).toBeNull();
expect(state.usageSessionLogsLoading).toBe(false);
});
});
function createStorageMock() {
const store = new Map<string, string>();
return {