Skip to content
Draft
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
39 changes: 39 additions & 0 deletions tests/registry/gitops.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,29 @@ describe("gitops_agent", () => {
expect(call.method).toBe("DELETE");
expect(call.path).toBe("/gitops/api/v1/agents/agent1779094157087");
});

it("delete: rejects when agent_id is missing (paramsSchema, not resource_id)", async () => {
const client = makeClient(vi.fn());

await expect(
registry.dispatch(client, "gitops_agent", "delete", {}),
).rejects.toThrow(/Missing required param\(s\) for gitops_agent\.delete: agent_id/);
});

it("delete: account resource_scope omits org/project query params", async () => {
const mockRequest = vi.fn().mockResolvedValue({});
const client = makeClient(mockRequest);

await registry.dispatch(client, "gitops_agent", "delete", {
agent_id: "myagent",
resource_scope: "account",
});

const call = mockRequest.mock.calls[0][0];
expect(call.path).toBe("/gitops/api/v1/agents/myagent");
expect(call.params.orgIdentifier).toBeUndefined();
expect(call.params.projectIdentifier).toBeUndefined();
});
});

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -271,6 +294,22 @@ describe("gitops_application", () => {
).rejects.toThrow(/Deletion mode is required/);
});

it("delete: missing cascade error comes from bodyBuilder, not paramsSchema", async () => {
const client = makeClient(vi.fn());

try {
await registry.dispatch(client, "gitops_application", "delete", {
agent_id: "account.myagent",
app_name: "demo-app",
});
expect.fail("expected dispatch to throw");
} catch (err) {
const message = (err as Error).message;
expect(message).toMatch(/Deletion mode is required/);
expect(message).not.toMatch(/Missing required param\(s\)/);
}
});

it("delete: throws when cascade=true but propagation_policy is missing", async () => {
const client = makeClient(vi.fn());

Expand Down
36 changes: 36 additions & 0 deletions tests/registry/registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1895,6 +1895,30 @@ describe("Registry", () => {
expect(call.body.costCategoryDTOs).toBeUndefined();
});

it("cost_recommendation_count get does not include costCategoryDTOs when only cost_category is provided without cost_buckets", async () => {
const mockRequest = vi.fn().mockResolvedValue({ data: 5 });
const client = makeClient(mockRequest);

await registry.dispatch(client, "cost_recommendation_count", "get", {
cost_category: "AI Platform team",
});

const call = mockRequest.mock.calls[0][0];
expect(call.body.costCategoryDTOs).toBeUndefined();
});

it("cost_recommendation_count get surfaces _error when API returns non-number data", async () => {
const mockRequest = vi.fn().mockResolvedValue({ data: "not-a-number" });
const client = makeClient(mockRequest);

const result = await registry.dispatch(client, "cost_recommendation_count", "get", {});

expect(result).toEqual({
count: 0,
_error: "Unexpected response shape — data is not a number",
});
});

it("cost_recommendation_count get sends default body when no filters provided", async () => {
const mockRequest = vi.fn().mockResolvedValue({ data: 42 });
const client = makeClient(mockRequest);
Expand Down Expand Up @@ -1973,6 +1997,18 @@ describe("Registry", () => {
]);
});

it("cost_recommendation_stats get does not include costCategoryDTOs when only cost_category is provided without cost_buckets", async () => {
const mockRequest = vi.fn().mockResolvedValue({ data: {} });
const client = makeClient(mockRequest);

await registry.dispatch(client, "cost_recommendation_stats", "get", {
cost_category: "AI Platform team",
});

const call = mockRequest.mock.calls[0][0];
expect(call.body.costCategoryDTOs).toBeUndefined();
});

it("cost_recommendation_stats get passes recommendation_states", async () => {
const mockRequest = vi.fn().mockResolvedValue({ data: {} });
const client = makeClient(mockRequest);
Expand Down
22 changes: 22 additions & 0 deletions tests/tools/tool-handlers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1469,6 +1469,28 @@ describe("harness_delete", () => {
expect(parseResult(result)).toMatchObject({ error: expect.stringContaining("Conflicting identifiers") });
expect(mockRequest).not.toHaveBeenCalled();
});

it("maps resource_id to agent_id for gitops_agent delete", async () => {
const gitopsRegistry = new Registry(makeConfig({ HARNESS_TOOLSETS: "gitops" }));
const gitopsServer = makeMcpServer("accept");
const { registerDeleteTool } = await import("../../src/tools/harness-delete.js");
registerDeleteTool(gitopsServer, gitopsRegistry, client, makeConfig());

const result = await gitopsServer.call("harness_delete", {
resource_type: "gitops_agent",
resource_id: "myagent",
resource_scope: "account",
confirm: true,
});

expect(result.isError).toBeUndefined();
expect(mockRequest).toHaveBeenCalledTimes(1);
const call = mockRequest.mock.calls[0]![0] as { method: string; path: string; params: Record<string, unknown> };
expect(call.method).toBe("DELETE");
expect(call.path).toBe("/gitops/api/v1/agents/myagent");
expect(call.params.orgIdentifier).toBeUndefined();
expect(call.params.projectIdentifier).toBeUndefined();
});
});

describe("harness_execute", () => {
Expand Down
Loading