Skip to content
Closed
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
22 changes: 22 additions & 0 deletions packages/opencode/test/altimate/tools/finops-formatting.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,34 @@ describe("formatBytes: normal cases", () => {
})
})

describe("formatBytes: higher units (TB, PB)", () => {
test("TB boundary", () => {
expect(formatBytes(1024 ** 4)).toBe("1.00 TB")
})

test("PB boundary", () => {
expect(formatBytes(1024 ** 5)).toBe("1.00 PB")
})

test("values beyond PB stay at PB (no EB unit)", () => {
expect(formatBytes(1024 ** 6)).toBe("1024.00 PB")
})

test("multi-PB value", () => {
expect(formatBytes(2 * 1024 ** 5)).toBe("2.00 PB")
})
})

describe("formatBytes: edge cases", () => {
test("negative bytes displays with sign", () => {
expect(formatBytes(-100)).toBe("-100 B")
expect(formatBytes(-1536)).toBe("-1.50 KB")
})

test("negative KB", () => {
expect(formatBytes(-1024)).toBe("-1.00 KB")
})

test("fractional bytes clamps to B unit", () => {
expect(formatBytes(0.5)).toBe("1 B")
})
Expand Down
42 changes: 42 additions & 0 deletions packages/opencode/test/command/hints.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { describe, test, expect } from "bun:test"
import { Command } from "../../src/command/index"

describe("Command.hints: template placeholder extraction", () => {
test("extracts numbered placeholders in order", () => {
expect(Command.hints("Do $1 then $2")).toEqual(["$1", "$2"])
})

test("deduplicates repeated placeholders", () => {
expect(Command.hints("Use $1 and $1 again")).toEqual(["$1"])
})

test("sorts numbered placeholders numerically by string sort", () => {
// $10 sorts after $1 and $2 in string order
expect(Command.hints("$2 then $1 then $10")).toEqual(["$1", "$10", "$2"])
Comment on lines +13 to +15
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix contradictory sorting description in this test.

Line 13/Line 14 describe sorting ambiguously/incorrectly, while Line 15 correctly asserts lexicographic behavior ("$10" before "$2"). Please align the wording with the assertion.

Suggested wording fix
-  test("sorts numbered placeholders numerically by string sort", () => {
-    // $10 sorts after $1 and $2 in string order
+  test("sorts numbered placeholders lexicographically (string sort)", () => {
+    // In string sort: $1, $10, $2
     expect(Command.hints("$2 then $1 then $10")).toEqual(["$1", "$10", "$2"])
   })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test("sorts numbered placeholders numerically by string sort", () => {
// $10 sorts after $1 and $2 in string order
expect(Command.hints("$2 then $1 then $10")).toEqual(["$1", "$10", "$2"])
test("sorts numbered placeholders lexicographically (string sort)", () => {
// In string sort: $1, $10, $2
expect(Command.hints("$2 then $1 then $10")).toEqual(["$1", "$10", "$2"])
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/command/hints.test.ts` around lines 13 - 15, The test
description/comment is contradictory: update the test name/comment that
currently reads "sorts numbered placeholders numerically by string sort" to
explicitly state lexicographic/string sorting (e.g., "sorts numbered
placeholders lexicographically (string order)") so it matches the assertion on
Command.hints("$2 then $1 then $10") expecting ["$1","$10","$2"]; ensure the
comment about "$10" sorting before "$2" is worded to reflect string order rather
than "numerically".

})

test("extracts $ARGUMENTS", () => {
expect(Command.hints("Run with $ARGUMENTS")).toEqual(["$ARGUMENTS"])
})

test("extracts both numbered and $ARGUMENTS", () => {
expect(Command.hints("$1 and $ARGUMENTS")).toEqual(["$1", "$ARGUMENTS"])
})

test("returns empty array for template with no placeholders", () => {
expect(Command.hints("No placeholders here")).toEqual([])
})

test("returns empty array for empty string", () => {
expect(Command.hints("")).toEqual([])
})

test("does not extract $SOMETHING_ELSE as a hint", () => {
// Only $N and $ARGUMENTS should be extracted
expect(Command.hints("$FOO $BAR")).toEqual([])
})

test("handles template with only $ARGUMENTS", () => {
expect(Command.hints("$ARGUMENTS")).toEqual(["$ARGUMENTS"])
})
})
124 changes: 124 additions & 0 deletions packages/opencode/test/tool/batch.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { describe, test, expect } from "bun:test"
import { BatchTool } from "../../src/tool/batch"

// BatchTool is a Tool.Info object; call .init() to get schema + helpers.
async function getToolInfo() {
return BatchTool.init()
}

describe("BatchTool: schema validation", () => {
test("rejects empty tool_calls array", async () => {
const tool = await getToolInfo()
const result = tool.parameters.safeParse({ tool_calls: [] })
expect(result.success).toBe(false)
})

test("accepts single tool call", async () => {
const tool = await getToolInfo()
const result = tool.parameters.safeParse({
tool_calls: [{ tool: "read", parameters: { file_path: "/tmp/x" } }],
})
expect(result.success).toBe(true)
})

test("accepts multiple tool calls", async () => {
const tool = await getToolInfo()
const result = tool.parameters.safeParse({
tool_calls: [
{ tool: "read", parameters: { file_path: "/tmp/a" } },
{ tool: "grep", parameters: { pattern: "foo" } },
],
})
expect(result.success).toBe(true)
})

test("rejects tool call without tool name", async () => {
const tool = await getToolInfo()
const result = tool.parameters.safeParse({
tool_calls: [{ parameters: { file_path: "/tmp/x" } }],
})
expect(result.success).toBe(false)
})

test("rejects tool call without parameters object", async () => {
const tool = await getToolInfo()
const result = tool.parameters.safeParse({
tool_calls: [{ tool: "read" }],
})
expect(result.success).toBe(false)
})

test("accepts tool call with empty parameters", async () => {
const tool = await getToolInfo()
const result = tool.parameters.safeParse({
tool_calls: [{ tool: "read", parameters: {} }],
})
expect(result.success).toBe(true)
})
})

describe("BatchTool: formatValidationError", () => {
test("formatValidationError is defined", async () => {
const tool = await getToolInfo()
expect(tool.formatValidationError).toBeDefined()
})

test("produces readable error message for empty array", async () => {
const tool = await getToolInfo()
expect(tool.formatValidationError).toBeDefined()
const result = tool.parameters.safeParse({ tool_calls: [] })
expect(result.success).toBe(false)
if (!result.success) {
const msg = tool.formatValidationError!(result.error)
expect(msg).toContain("Invalid parameters for tool 'batch'")
expect(msg).toContain("Expected payload format")
}
})

test("includes field path in type error", async () => {
const tool = await getToolInfo()
expect(tool.formatValidationError).toBeDefined()
const result = tool.parameters.safeParse({
tool_calls: [{ tool: 123, parameters: {} }],
})
expect(result.success).toBe(false)
if (!result.success) {
const msg = tool.formatValidationError!(result.error)
expect(msg).toContain("tool_calls")
}
})
})

describe("BatchTool: DISALLOWED set enforcement", () => {
// The DISALLOWED set prevents recursive batch-in-batch calls.
// This is a critical safety mechanism — if the LLM can batch the batch tool,
// it creates infinite recursion.
// We verify the source code's DISALLOWED set by checking the module exports.
test("batch tool id is 'batch'", () => {
expect(BatchTool.id).toBe("batch")
})

// The 25-call cap and DISALLOWED enforcement happen inside execute(),
// which requires a full Session context. We verify the schema allows
// up to 25+ items at parse time (the cap is enforced at runtime).
test("schema accepts 25 tool calls (runtime cap is in execute)", async () => {
const tool = await getToolInfo()
const calls = Array.from({ length: 25 }, (_, i) => ({
tool: `tool_${i}`,
parameters: {},
}))
const result = tool.parameters.safeParse({ tool_calls: calls })
expect(result.success).toBe(true)
})

test("schema accepts 26+ tool calls (runtime slices to 25)", async () => {
const tool = await getToolInfo()
const calls = Array.from({ length: 30 }, (_, i) => ({
tool: `tool_${i}`,
parameters: {},
}))
const result = tool.parameters.safeParse({ tool_calls: calls })
// Schema allows it — the 25-cap is enforced in execute()
expect(result.success).toBe(true)
})
})
Loading