diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 179fe876b..af24ea533 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -37,11 +37,9 @@ jobs:
steps:
- uses: actions/checkout@v4
- # Pin to 1.3.10: bun 1.3.11 breaks spyOn on ESM barrel re-exports,
- # causing 7 createBuiltinAgents tests to fail in batch execution.
- uses: oven-sh/setup-bun@v2
with:
- bun-version: "1.3.10"
+ bun-version: "1.3.11"
- name: Install dependencies
run: bun install
@@ -58,7 +56,7 @@ jobs:
- uses: oven-sh/setup-bun@v2
with:
- bun-version: "1.3.10"
+ bun-version: "1.3.11"
- name: Install dependencies
run: bun install
@@ -83,7 +81,7 @@ jobs:
- uses: oven-sh/setup-bun@v2
with:
- bun-version: "1.3.10"
+ bun-version: "1.3.11"
- name: Install dependencies
run: bun install
diff --git a/bun.lock b/bun.lock
index 4e96d0f2c..24b3733e1 100644
--- a/bun.lock
+++ b/bun.lock
@@ -25,7 +25,7 @@
"devDependencies": {
"@types/js-yaml": "^4.0.9",
"@types/picomatch": "^3.0.2",
- "bun-types": "1.3.10",
+ "bun-types": "1.3.11",
"typescript": "^5.7.3",
},
"optionalDependencies": {
@@ -118,7 +118,7 @@
"body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
- "bun-types": ["bun-types@1.3.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg=="],
+ "bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
diff --git a/package.json b/package.json
index d88d831eb..85e631c9c 100644
--- a/package.json
+++ b/package.json
@@ -74,7 +74,7 @@
"devDependencies": {
"@types/js-yaml": "^4.0.9",
"@types/picomatch": "^3.0.2",
- "bun-types": "1.3.10",
+ "bun-types": "1.3.11",
"typescript": "^5.7.3"
},
"optionalDependencies": {
diff --git a/src/hooks/claude-code-hooks/execute-http-hook-security.test.ts b/src/hooks/claude-code-hooks/execute-http-hook-security.test.ts
index 16dbb1de1..11e08a668 100644
--- a/src/hooks/claude-code-hooks/execute-http-hook-security.test.ts
+++ b/src/hooks/claude-code-hooks/execute-http-hook-security.test.ts
@@ -1,13 +1,12 @@
///
-import { describe, it, expect, mock, beforeEach, afterEach } from "bun:test"
+import { describe, it, expect, mock, beforeEach, afterEach, spyOn } from "bun:test"
import type { HookHttp } from "./types"
+import * as sharedLogger from "../../shared/logger"
const mockFetch = mock(() =>
Promise.resolve(new Response(JSON.stringify({}), { status: 200 }))
)
-const mockLog = mock(() => {})
-
const originalFetch = globalThis.fetch
const originalEnv = process.env
@@ -17,6 +16,8 @@ async function importFreshExecuteHttpHook() {
}
describe("executeHttpHook TLS security", () => {
+ let logSpy: ReturnType | undefined
+
beforeEach(() => {
globalThis.fetch = mockFetch as unknown as typeof fetch
mockFetch.mockReset()
@@ -28,7 +29,8 @@ describe("executeHttpHook TLS security", () => {
afterEach(() => {
globalThis.fetch = originalFetch
process.env = { ...originalEnv }
- mockLog.mockReset()
+ logSpy?.mockRestore()
+ logSpy = undefined
mockFetch.mockReset()
mock.restore()
})
@@ -61,18 +63,22 @@ describe("executeHttpHook TLS security", () => {
})
it("#when hook uses remote http:// URL #then logs warning before rejection", async () => {
- mock.module("../../shared/logger", () => ({
- log: mockLog,
- }))
+ // given
+ logSpy = spyOn(sharedLogger, "log").mockImplementation(() => {})
const { executeHttpHook } = await importFreshExecuteHttpHook()
- const hook: HookHttp = { type: "http", url: "http://example.com/hooks" }
+ const hook: HookHttp = { type: "http", url: "http://tls-security-remote.invalid/hooks" }
+ // when
const result = await executeHttpHook(hook, "{}")
- expect(result.exitCode).toBe(1)
- expect(mockLog).toHaveBeenCalledWith("HTTP hook URL uses insecure protocol", {
- url: "http://example.com/hooks",
+ // then
+ const matchingCalls = logSpy.mock.calls.filter(([message, data]) => {
+ return message === "HTTP hook URL uses insecure protocol"
+ && JSON.stringify(data) === JSON.stringify({ url: hook.url })
})
+
+ expect(result.exitCode).toBe(1)
+ expect(matchingCalls).toHaveLength(1)
expect(mockFetch).not.toHaveBeenCalled()
})
@@ -87,17 +93,22 @@ describe("executeHttpHook TLS security", () => {
})
it("#when hook uses http://localhost #then does not log insecure warning", async () => {
- mock.module("../../shared/logger", () => ({
- log: mockLog,
- }))
- mockLog.mockReset()
+ // given
+ logSpy = spyOn(sharedLogger, "log").mockImplementation(() => {})
const { executeHttpHook } = await importFreshExecuteHttpHook()
- const hook: HookHttp = { type: "http", url: "http://localhost:8080/hooks" }
+ const hook: HookHttp = { type: "http", url: "http://localhost:49123/hooks" }
+ // when
const result = await executeHttpHook(hook, "{}")
+ // then
+ const matchingCalls = logSpy.mock.calls.filter(([message, data]) => {
+ return message === "HTTP hook URL uses insecure protocol"
+ && JSON.stringify(data) === JSON.stringify({ url: hook.url })
+ })
+
expect(result.exitCode).toBe(0)
- expect(mockLog).not.toHaveBeenCalled()
+ expect(matchingCalls).toHaveLength(0)
})
it("#when hook uses http://127.0.0.1 #then allows execution", async () => {
@@ -158,17 +169,21 @@ describe("executeHttpHook TLS security", () => {
})
it("#when hook uses plain remote http:// URL #then writes warning log", async () => {
- mock.module("../../shared/logger", () => ({
- log: mockLog,
- }))
+ // given
+ logSpy = spyOn(sharedLogger, "log").mockImplementation(() => {})
const { executeHttpHook } = await importFreshExecuteHttpHook()
- const hook: HookHttp = { type: "http", url: "http://example.com/hooks" }
+ const hook: HookHttp = { type: "http", url: "http://tls-security-dev.invalid/hooks" }
+ // when
await executeHttpHook(hook, "{}")
- expect(mockLog).toHaveBeenCalledWith("HTTP hook URL uses insecure protocol", {
- url: "http://example.com/hooks",
+ // then
+ const matchingCalls = logSpy.mock.calls.filter(([message, data]) => {
+ return message === "HTTP hook URL uses insecure protocol"
+ && JSON.stringify(data) === JSON.stringify({ url: hook.url })
})
+
+ expect(matchingCalls).toHaveLength(1)
})
it("#when hook uses http://[::1] #then allows execution", async () => {
diff --git a/src/mcp/zauc-mocks-mcp-index/index.test.ts b/src/mcp/zauc-mocks-mcp-index/index.test.ts
index ce772ac99..f14215b56 100644
--- a/src/mcp/zauc-mocks-mcp-index/index.test.ts
+++ b/src/mcp/zauc-mocks-mcp-index/index.test.ts
@@ -10,7 +10,10 @@ describe("createBuiltinMcps", () => {
const result = createBuiltinMcps(disabledMcps)
// then
- expect(result.length).toBeGreaterThan(0)
+ expect(Object.keys(result).length).toBeGreaterThan(0)
+ expect(result.websearch).toBeDefined()
+ expect(result.context7).toBeDefined()
+ expect(result.grep_app).toBeDefined()
})
test("should filter out disabled MCPs", () => {
@@ -21,20 +24,23 @@ describe("createBuiltinMcps", () => {
const result = createBuiltinMcps(disabledMcps)
// then
- expect(result.some((mcp) => mcp.name === "websearch")).toBe(false)
+ expect(result.websearch).toBeUndefined()
+ expect(result.context7).toBeDefined()
+ expect(result.grep_app).toBeDefined()
})
test("should return empty array when all MCPs are disabled", () => {
// given - disable all known MCPs
- const disabledMcps = ["websearch", "context7", "grep-app"]
+ const disabledMcps = ["websearch", "context7", "grep_app"]
// when
const result = createBuiltinMcps(disabledMcps)
// then - may still have MCPs we didn't list
- const remainingMcpNames = result.map((m) => m.name)
+ const remainingMcpNames = Object.keys(result)
expect(remainingMcpNames).not.toContain("websearch")
expect(remainingMcpNames).not.toContain("context7")
- expect(remainingMcpNames).not.toContain("grep-app")
+ expect(remainingMcpNames).not.toContain("grep_app")
+ expect(remainingMcpNames).toEqual([])
})
-})
\ No newline at end of file
+})
diff --git a/src/tools/delegate-task/zauc-mocks-subagent-resolver/subagent-resolver.test.ts b/src/tools/delegate-task/zauc-mocks-subagent-resolver/subagent-resolver.test.ts
index 777731c40..e21b104fb 100644
--- a/src/tools/delegate-task/zauc-mocks-subagent-resolver/subagent-resolver.test.ts
+++ b/src/tools/delegate-task/zauc-mocks-subagent-resolver/subagent-resolver.test.ts
@@ -1,14 +1,14 @@
declare const require: (name: string) => any
const { describe, test, expect, beforeEach, afterEach, spyOn, mock } = require("bun:test")
-import type { DelegateTaskArgs } from "../../types"
-import type { ExecutorContext } from "../../executor-types"
-import * as logger from "../../../../shared/logger"
-import * as connectedProvidersCache from "../../../../shared/connected-providers-cache"
+import type { DelegateTaskArgs } from "../types"
+import type { ExecutorContext } from "../executor-types"
+import * as logger from "../../../shared/logger"
+import * as connectedProvidersCache from "../../../shared/connected-providers-cache"
-type SubagentResolverModule = typeof import("../../subagent-resolver")
+type SubagentResolverModule = typeof import("../subagent-resolver")
async function importFreshSubagentResolverModule(): Promise {
- return await import(`../../subagent-resolver?test=${Date.now()}-${Math.random()}`)
+ return await import(`../subagent-resolver?test=${Date.now()}-${Math.random()}`)
}
function createBaseArgs(overrides?: Partial): DelegateTaskArgs {
diff --git a/src/tools/skill/zauc-mocks-skill-tools/tools.test.ts b/src/tools/skill/zauc-mocks-skill-tools/tools.test.ts
index 67bc6876e..785428c1c 100644
--- a/src/tools/skill/zauc-mocks-skill-tools/tools.test.ts
+++ b/src/tools/skill/zauc-mocks-skill-tools/tools.test.ts
@@ -8,7 +8,9 @@ import type { Tool as McpTool } from "@modelcontextprotocol/sdk/types.js"
const originalReadFileSync = fs.readFileSync.bind(fs)
-async function importFreshSkillToolModule(): Promise {
+let createSkillTool: typeof import("../tools").createSkillTool
+
+beforeEach(async () => {
mock.module("node:fs", () => ({
...fs,
readFileSync: (path: string, encoding?: string) => {
@@ -21,13 +23,10 @@ Test skill body content`
return originalReadFileSync(path, encoding as BufferEncoding)
},
}))
-
- const module = await import(`./tools?test=${Date.now()}-${Math.random()}`)
- mock.restore()
- return module
-}
-
-const { createSkillTool } = await importFreshSkillToolModule()
+
+ const module = await import("../tools")
+ createSkillTool = module.createSkillTool
+})
afterAll(() => {
mock.restore()