Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions apps/server/test/agents-routes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,59 @@ describe("POST /api/v1/agents (create)", () => {
expect(res.statusCode).toBe(400);
expect(res.json().error).toContain("disabled");
});

it("does not apply fullAccess for terminal agents", async () => {
const agent = await createAgent({
type: "terminal",
fullAccess: true,
});
expect(agent.type).toBe("terminal");
expect(agent.fullAccess).toBe(false);
});

it("defaults type to codex when omitted", async () => {
const agent = await createAgent({});
expect(agent.type).toBe("codex");
});

it("applies fullAccess arg for codex type", async () => {
const agent = await createAgent({
type: "codex",
fullAccess: true,
});
expect(agent.fullAccess).toBe(true);
});

it("trims whitespace-only initialPrompt to undefined", async () => {
const agent = await createAgent({
initialPrompt: " ",
});
expect(agent.initialPrompt).toBeFalsy();
});
});

// ---------------------------------------------------------------------------
// GET /api/v1/agents/:id/repo-icon
// ---------------------------------------------------------------------------
describe("GET /api/v1/agents/:id/repo-icon", () => {
it("returns 404 for non-existent agent", async () => {
const res = await authedInject(
"GET",
"/api/v1/agents/agt_nonexistent/repo-icon"
);
expect(res.statusCode).toBe(404);
expect(res.json().error).toContain("Agent not found");
});

it("returns 404 when agent has no repo icon", async () => {
const agent = await createAgent();
const res = await authedInject(
"GET",
`/api/v1/agents/${agent.id}/repo-icon`
);
expect(res.statusCode).toBe(404);
expect(res.json().error).toContain("No repo icon");
});
});

// ---------------------------------------------------------------------------
Expand Down
101 changes: 101 additions & 0 deletions apps/server/test/system-routes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,107 @@ describe("POST /api/v1/app/settings/ides", () => {
});
});

describe("GET /api/v1/app/settings/cross-repo-messaging", () => {
it("returns enabled status (defaults to false)", async () => {
const res = await ctx.app.inject({
method: "GET",
url: "/api/v1/app/settings/cross-repo-messaging",
headers: { cookie: sessionCookie },
});
expect(res.statusCode).toBe(200);
expect(res.json()).toEqual({ enabled: false });
});

it("reflects updated state after POST", async () => {
await ctx.app.inject({
method: "POST",
url: "/api/v1/app/settings/cross-repo-messaging",
headers: { cookie: sessionCookie },
payload: { enabled: true },
});
const res = await ctx.app.inject({
method: "GET",
url: "/api/v1/app/settings/cross-repo-messaging",
headers: { cookie: sessionCookie },
});
expect(res.statusCode).toBe(200);
expect(res.json()).toEqual({ enabled: true });
});
});

describe("POST /api/v1/app/settings/cross-repo-messaging", () => {
it("enables cross-repo messaging", async () => {
const res = await ctx.app.inject({
method: "POST",
url: "/api/v1/app/settings/cross-repo-messaging",
headers: { cookie: sessionCookie },
payload: { enabled: true },
});
expect(res.statusCode).toBe(200);
expect(res.json()).toEqual({ enabled: true });
});

it("disables cross-repo messaging", async () => {
await ctx.app.inject({
method: "POST",
url: "/api/v1/app/settings/cross-repo-messaging",
headers: { cookie: sessionCookie },
payload: { enabled: true },
});
const res = await ctx.app.inject({
method: "POST",
url: "/api/v1/app/settings/cross-repo-messaging",
headers: { cookie: sessionCookie },
payload: { enabled: false },
});
expect(res.statusCode).toBe(200);
expect(res.json()).toEqual({ enabled: false });
});

it("rejects non-boolean enabled", async () => {
const res = await ctx.app.inject({
method: "POST",
url: "/api/v1/app/settings/cross-repo-messaging",
headers: { cookie: sessionCookie },
payload: { enabled: "yes" },
});
expect(res.statusCode).toBe(400);
expect(res.json().error).toMatch(/enabled must be a boolean/);
});

it("rejects missing enabled field", async () => {
const res = await ctx.app.inject({
method: "POST",
url: "/api/v1/app/settings/cross-repo-messaging",
headers: { cookie: sessionCookie },
payload: {},
});
expect(res.statusCode).toBe(400);
expect(res.json().error).toMatch(/enabled must be a boolean/);
});

it("rejects null body", async () => {
const res = await ctx.app.inject({
method: "POST",
url: "/api/v1/app/settings/cross-repo-messaging",
headers: { cookie: sessionCookie },
payload: null,
});
expect(res.statusCode).toBe(400);
});

it("rejects numeric enabled", async () => {
const res = await ctx.app.inject({
method: "POST",
url: "/api/v1/app/settings/cross-repo-messaging",
headers: { cookie: sessionCookie },
payload: { enabled: 1 },
});
expect(res.statusCode).toBe(400);
expect(res.json().error).toMatch(/enabled must be a boolean/);
});
});

describe("POST /api/v1/energy-report", () => {
it("accepts any body and returns 204", async () => {
const res = await ctx.app.inject({
Expand Down
104 changes: 104 additions & 0 deletions apps/web/src/lib/store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
reconcileMediaSidebarStateStorage,
MEDIA_SIDEBAR_STATE_STORAGE_PREFIX,
defaultMediaSidebarState,
reconcileDiffViewStateStorage,
DIFF_VIEW_STATE_STORAGE_PREFIX,
} from "./store";

describe("reconcileAgentSidebarOrder", () => {
Expand Down Expand Up @@ -118,3 +120,105 @@ describe("reconcileMediaSidebarStateStorage", () => {
).toBeNull();
});
});

describe("reconcileDiffViewStateStorage", () => {
beforeEach(() => {
window.localStorage.clear();
});

afterEach(() => {
window.localStorage.clear();
});

const defaultDiffState = JSON.stringify({
collapsedFiles: [],
collapsedDirs: [],
scrollTop: 0,
});

const storeDiffForAgent = (agentId: string) => {
window.localStorage.setItem(
`${DIFF_VIEW_STATE_STORAGE_PREFIX}${agentId}`,
defaultDiffState
);
};

it("removes keys for agents not in the live set", () => {
storeDiffForAgent("agt_1");
storeDiffForAgent("agt_2");
storeDiffForAgent("agt_3");

reconcileDiffViewStateStorage(["agt_1", "agt_3"]);

expect(
window.localStorage.getItem(`${DIFF_VIEW_STATE_STORAGE_PREFIX}agt_1`)
).not.toBeNull();
expect(
window.localStorage.getItem(`${DIFF_VIEW_STATE_STORAGE_PREFIX}agt_2`)
).toBeNull();
expect(
window.localStorage.getItem(`${DIFF_VIEW_STATE_STORAGE_PREFIX}agt_3`)
).not.toBeNull();
});

it("does nothing when all stored agents are live", () => {
storeDiffForAgent("agt_1");
storeDiffForAgent("agt_2");

reconcileDiffViewStateStorage(["agt_1", "agt_2"]);

expect(window.localStorage.length).toBe(2);
});

it("removes all diff view keys when live set is empty", () => {
storeDiffForAgent("agt_1");
storeDiffForAgent("agt_2");

reconcileDiffViewStateStorage([]);

expect(window.localStorage.length).toBe(0);
});

it("does nothing when localStorage is empty", () => {
reconcileDiffViewStateStorage(["agt_1"]);

expect(window.localStorage.length).toBe(0);
});

it("does not affect non-diff-view keys", () => {
window.localStorage.setItem("dispatch:leftSidebarOpen", "true");
window.localStorage.setItem(
`${MEDIA_SIDEBAR_STATE_STORAGE_PREFIX}agt_live`,
"{}"
);
storeDiffForAgent("agt_dead");

reconcileDiffViewStateStorage([]);

expect(window.localStorage.getItem("dispatch:leftSidebarOpen")).toBe(
"true"
);
expect(
window.localStorage.getItem(
`${MEDIA_SIDEBAR_STATE_STORAGE_PREFIX}agt_live`
)
).toBe("{}");
expect(
window.localStorage.getItem(`${DIFF_VIEW_STATE_STORAGE_PREFIX}agt_dead`)
).toBeNull();
});

it("accepts a Set as agentIds", () => {
storeDiffForAgent("agt_1");
storeDiffForAgent("agt_2");

reconcileDiffViewStateStorage(new Set(["agt_1"]));

expect(
window.localStorage.getItem(`${DIFF_VIEW_STATE_STORAGE_PREFIX}agt_1`)
).not.toBeNull();
expect(
window.localStorage.getItem(`${DIFF_VIEW_STATE_STORAGE_PREFIX}agt_2`)
).toBeNull();
});
});