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
10 changes: 5 additions & 5 deletions packages/cloud/src/__tests__/CloudShareService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ vi.mock("vscode", () => ({
},
}))

vi.mock("../Config", () => ({
vi.mock("../config", () => ({
getRooCodeApiUrl: () => "https://app.roocode.com",
}))

vi.mock("../utils", () => ({
getUserAgent: () => "Roo-Code 1.0.0",
getUserAgent: () => "Zoo-Code 1.0.0",
}))

describe("CloudShareService", () => {
Expand Down Expand Up @@ -93,7 +93,7 @@ describe("CloudShareService", () => {
headers: {
"Content-Type": "application/json",
Authorization: "Bearer session-token",
"User-Agent": "Roo-Code 1.0.0",
"User-Agent": "Zoo-Code 1.0.0",
},
body: JSON.stringify({
taskId: "task-123",
Expand Down Expand Up @@ -127,7 +127,7 @@ describe("CloudShareService", () => {
headers: {
"Content-Type": "application/json",
Authorization: "Bearer session-token",
"User-Agent": "Roo-Code 1.0.0",
"User-Agent": "Zoo-Code 1.0.0",
},
body: JSON.stringify({ taskId: "task-123", visibility: "public" }),
signal: expect.any(AbortSignal),
Expand All @@ -154,7 +154,7 @@ describe("CloudShareService", () => {
headers: {
"Content-Type": "application/json",
Authorization: "Bearer session-token",
"User-Agent": "Roo-Code 1.0.0",
"User-Agent": "Zoo-Code 1.0.0",
},
body: JSON.stringify({
taskId: "task-123",
Expand Down
2 changes: 1 addition & 1 deletion packages/cloud/src/__tests__/WebAuthService.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ describe("WebAuthService", () => {
vi.mocked(getRooCodeApiUrl).mockReturnValue("https://api.test.com")

// Setup utils mock
vi.mocked(getUserAgent).mockReturnValue("Roo-Code 1.0.0")
vi.mocked(getUserAgent).mockReturnValue("Zoo-Code 1.0.0")

// Setup crypto mock
vi.mocked(crypto.randomBytes).mockReturnValue(Buffer.from("test-random-bytes") as never)
Expand Down
2 changes: 1 addition & 1 deletion packages/cloud/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ExtensionContext } from "vscode"

export function getUserAgent(context?: ExtensionContext): string {
return `Roo-Code ${context?.extension?.packageJSON?.version || "unknown"}`
return `Zoo-Code ${context?.extension?.packageJSON?.version || "unknown"}`
}
8 changes: 8 additions & 0 deletions src/api/providers/__tests__/bedrock.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ describe("AwsBedrockHandler", () => {
expect(modelInfo.info.contextWindow).toBeDefined()
})

it("should identify itself as Zoo Code in the AWS client app id", () => {
expect(mockBedrockRuntimeClient).toHaveBeenCalledWith(
expect.objectContaining({
userAgentAppId: expect.stringMatching(/^ZooCode#/),
}),
)
})

it("should use custom ARN when provided", () => {
// This test is incompatible with the refactored implementation
// The implementation now extracts the model ID from the ARN instead of using the ARN directly
Expand Down
8 changes: 4 additions & 4 deletions src/api/providers/__tests__/constants.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,24 @@ describe("DEFAULT_HEADERS", () => {
})

it("should have correct HTTP-Referer value", () => {
expect(DEFAULT_HEADERS["HTTP-Referer"]).toBe("https://github.com/RooVetGit/Roo-Cline")
expect(DEFAULT_HEADERS["HTTP-Referer"]).toBe("https://github.com/Zoo-Code-Org/Zoo-Code")
})

it("should have correct X-Title value", () => {
expect(DEFAULT_HEADERS["X-Title"]).toBe("Roo Code")
expect(DEFAULT_HEADERS["X-Title"]).toBe("Zoo Code")
})

it("should have correct User-Agent format", () => {
const userAgent = DEFAULT_HEADERS["User-Agent"]
expect(userAgent).toBe(`RooCode/${Package.version}`)
expect(userAgent).toBe(`ZooCode/${Package.version}`)

// Verify it follows the tool_name/version pattern
expect(userAgent).toMatch(/^[a-zA-Z-]+\/\d+\.\d+\.\d+$/)
})

it("should have User-Agent with correct tool name", () => {
const userAgent = DEFAULT_HEADERS["User-Agent"]
expect(userAgent.startsWith("RooCode/")).toBe(true)
expect(userAgent.startsWith("ZooCode/")).toBe(true)
})

it("should have User-Agent with semantic version format", () => {
Expand Down
122 changes: 122 additions & 0 deletions src/api/providers/__tests__/openai-codex-native-tool-calls.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { OpenAiCodexHandler } from "../openai-codex"
import type { ApiHandlerOptions } from "../../../shared/api"
import { NativeToolCallParser } from "../../../core/assistant-message/NativeToolCallParser"
import { openAiCodexOAuthManager } from "../../../integrations/openai-codex/oauth"
import { Package } from "../../../shared/package"

describe("OpenAiCodexHandler native tool calls", () => {
let handler: OpenAiCodexHandler
Expand Down Expand Up @@ -405,4 +406,125 @@ describe("OpenAiCodexHandler native tool calls", () => {
const textChunks = chunks.filter((c) => c.type === "text")
expect(textChunks.map((c) => c.text).join("")).toBe("hello world")
})

it("identifies SDK requests as Zoo Code", async () => {
vi.spyOn(openAiCodexOAuthManager, "getAccessToken").mockResolvedValue("test-token")
vi.spyOn(openAiCodexOAuthManager, "getAccountId").mockResolvedValue("acct_test")

const mockCreate = vi.fn().mockResolvedValue({
async *[Symbol.asyncIterator]() {
yield { type: "response.output_text.delta", delta: "ok" }
yield {
type: "response.completed",
response: {
id: "resp_sdk_headers",
status: "completed",
output: [],
usage: { input_tokens: 1, output_tokens: 1 },
},
}
},
})
;(handler as any).client = { responses: { create: mockCreate } }

const stream = handler.createMessage("system", [{ role: "user", content: "headers" } as any], {
taskId: "task-123",
tools: [],
})
for await (const _chunk of stream) {
// drain stream
}

expect(mockCreate).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
headers: expect.objectContaining({
originator: "zoo-code",
session_id: "task-123",
"ChatGPT-Account-Id": "acct_test",
"User-Agent": expect.stringContaining(`zoo-code/${Package.version}`),
}),
}),
)
})

it("identifies fetch fallback requests as Zoo Code", async () => {
vi.spyOn(openAiCodexOAuthManager, "getAccessToken").mockResolvedValue("test-token")
vi.spyOn(openAiCodexOAuthManager, "getAccountId").mockResolvedValue("acct_test")

const mockFetch = vi.fn().mockResolvedValue({
ok: true,
body: new ReadableStream({
start(controller) {
controller.enqueue(
new TextEncoder().encode('data: {"type":"response.output_text.delta","delta":"fallback"}\n\n'),
)
controller.enqueue(
new TextEncoder().encode(
'data: {"type":"response.completed","response":{"id":"resp_fetch_headers","status":"completed","output":[],"usage":{"input_tokens":1,"output_tokens":1}}}\n\n',
),
)
controller.close()
},
}),
})
global.fetch = mockFetch as any
;(handler as any).client = {
responses: {
create: vi.fn().mockRejectedValue(new Error("SDK unavailable")),
},
}

const stream = handler.createMessage("system", [{ role: "user", content: "fallback" } as any], {
taskId: "task-456",
tools: [],
})
for await (const _chunk of stream) {
// drain stream
}

expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining("/responses"),
expect.objectContaining({
headers: expect.objectContaining({
originator: "zoo-code",
session_id: "task-456",
"ChatGPT-Account-Id": "acct_test",
"User-Agent": expect.stringContaining(`zoo-code/${Package.version}`),
}),
}),
)
})

it("identifies completePrompt requests as Zoo Code", async () => {
vi.spyOn(openAiCodexOAuthManager, "getAccessToken").mockResolvedValue("test-token")
vi.spyOn(openAiCodexOAuthManager, "getAccountId").mockResolvedValue("acct_test")

const mockFetch = vi.fn().mockResolvedValue({
ok: true,
json: vi.fn().mockResolvedValue({
output: [
{
type: "message",
content: [{ type: "output_text", text: "done" }],
},
],
}),
})
global.fetch = mockFetch as any

await expect(handler.completePrompt("Test prompt")).resolves.toBe("done")

expect(mockFetch).toHaveBeenCalledWith(
expect.stringContaining("/responses"),
expect.objectContaining({
headers: expect.objectContaining({
originator: "zoo-code",
"ChatGPT-Account-Id": "acct_test",
"User-Agent": expect.stringContaining(`zoo-code/${Package.version}`),
session_id: expect.any(String),
}),
}),
)
})
})
18 changes: 18 additions & 0 deletions src/api/providers/__tests__/openai-native.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ApiProviderError } from "@roo-code/types"

import { OpenAiNativeHandler } from "../openai-native"
import { ApiHandlerOptions } from "../../../shared/api"
import { Package } from "../../../shared/package"

// Mock OpenAI client - now everything uses Responses API
const mockResponsesCreate = vitest.fn()
Expand Down Expand Up @@ -99,6 +100,23 @@ describe("OpenAiNativeHandler", () => {
expect.objectContaining({ baseURL: "https://custom-openai.example.com/v1" }),
)
})

it("should identify itself as Zoo Code in request headers", () => {
;(OpenAI as unknown as ReturnType<typeof vitest.fn>).mockClear()
new OpenAiNativeHandler({
apiModelId: "gpt-4.1",
openAiNativeApiKey: "test-key",
})

expect(OpenAI).toHaveBeenCalledWith(
expect.objectContaining({
defaultHeaders: expect.objectContaining({
originator: "zoo-code",
"User-Agent": expect.stringContaining(`zoo-code/${Package.version}`),
}),
}),
)
})
})

describe("createMessage", () => {
Expand Down
6 changes: 3 additions & 3 deletions src/api/providers/__tests__/openai.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ describe("OpenAiHandler", () => {
baseURL: expect.any(String),
apiKey: expect.any(String),
defaultHeaders: {
"HTTP-Referer": "https://github.com/RooVetGit/Roo-Cline",
"X-Title": "Roo Code",
"User-Agent": `RooCode/${Package.version}`,
"HTTP-Referer": "https://github.com/Zoo-Code-Org/Zoo-Code",
"X-Title": "Zoo Code",
"User-Agent": `ZooCode/${Package.version}`,
},
timeout: expect.any(Number),
})
Expand Down
6 changes: 3 additions & 3 deletions src/api/providers/__tests__/openrouter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ describe("OpenRouterHandler", () => {
baseURL: "https://openrouter.ai/api/v1",
apiKey: mockOptions.openRouterApiKey,
defaultHeaders: {
"HTTP-Referer": "https://github.com/RooVetGit/Roo-Cline",
"X-Title": "Roo Code",
"User-Agent": `RooCode/${Package.version}`,
"HTTP-Referer": "https://github.com/Zoo-Code-Org/Zoo-Code",
"X-Title": "Zoo Code",
"User-Agent": `ZooCode/${Package.version}`,
},
})
})
Expand Down
12 changes: 6 additions & 6 deletions src/api/providers/__tests__/requesty.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ describe("RequestyHandler", () => {
baseURL: "https://router.requesty.ai/v1",
apiKey: mockOptions.requestyApiKey,
defaultHeaders: {
"HTTP-Referer": "https://github.com/RooVetGit/Roo-Cline",
"X-Title": "Roo Code",
"User-Agent": `RooCode/${Package.version}`,
"HTTP-Referer": "https://github.com/Zoo-Code-Org/Zoo-Code",
"X-Title": "Zoo Code",
"User-Agent": `ZooCode/${Package.version}`,
},
})
})
Expand All @@ -73,9 +73,9 @@ describe("RequestyHandler", () => {
baseURL: "https://custom.requesty.ai/v1",
apiKey: mockOptions.requestyApiKey,
defaultHeaders: {
"HTTP-Referer": "https://github.com/RooVetGit/Roo-Cline",
"X-Title": "Roo Code",
"User-Agent": `RooCode/${Package.version}`,
"HTTP-Referer": "https://github.com/Zoo-Code-Org/Zoo-Code",
"X-Title": "Zoo Code",
"User-Agent": `ZooCode/${Package.version}`,
},
})
})
Expand Down
Loading
Loading