fix(ci): simplify test runner to plain bun test by fixing mock.module() leakage
- Add afterAll(() => { mock.restore() }) to 52 test files missing cleanup
- Rewrite create-tool-guard-hooks.test.ts to use spyOn instead of barrel mock
- Fix skill-mcp-manager OAuth tests with missing mockTokens/mockLogin definitions
- Fix start-work hook: show worktree active block on resume with existing worktree_path
- Extract createWorktreeActiveBlock to worktree-block.ts to avoid circular import
- Replace 80-line isolated test runner CI config with single `bun test` command
This commit is contained in:
@@ -44,86 +44,8 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
BUN_INSTALL_ALLOW_SCRIPTS: "@ast-grep/napi"
|
BUN_INSTALL_ALLOW_SCRIPTS: "@ast-grep/napi"
|
||||||
|
|
||||||
- name: Run mock-heavy tests (isolated)
|
- name: Run tests
|
||||||
run: |
|
run: bun test
|
||||||
# These files use mock.module() which pollutes module cache
|
|
||||||
# Run them in separate processes to prevent cross-file contamination
|
|
||||||
bun test src/plugin-handlers
|
|
||||||
bun test src/hooks/atlas
|
|
||||||
bun test src/hooks/compaction-context-injector
|
|
||||||
bun test src/features/tmux-subagent
|
|
||||||
bun test src/cli/doctor/formatter.test.ts
|
|
||||||
bun test src/cli/doctor/format-default.test.ts
|
|
||||||
bun test src/tools/call-omo-agent/sync-executor.test.ts
|
|
||||||
bun test src/tools/call-omo-agent/session-creator.test.ts
|
|
||||||
bun test src/tools/session-manager
|
|
||||||
bun test src/features/opencode-skill-loader/loader.test.ts
|
|
||||||
bun test src/hooks/anthropic-context-window-limit-recovery/recovery-hook.test.ts
|
|
||||||
bun test src/hooks/anthropic-context-window-limit-recovery/executor.test.ts
|
|
||||||
# src/shared mock-heavy files (mock.module pollutes connected-providers-cache and legacy-plugin-warning)
|
|
||||||
bun test src/shared/model-capabilities.test.ts
|
|
||||||
bun test src/shared/log-legacy-plugin-startup-warning.test.ts
|
|
||||||
bun test src/shared/model-error-classifier.test.ts
|
|
||||||
bun test src/shared/opencode-message-dir.test.ts
|
|
||||||
# session-recovery mock isolation (recover-tool-result-missing mocks ./storage)
|
|
||||||
bun test src/hooks/session-recovery/recover-tool-result-missing.test.ts
|
|
||||||
# legacy-plugin-toast mock isolation (hook.test.ts mocks ./auto-migrate)
|
|
||||||
bun test src/hooks/legacy-plugin-toast/hook.test.ts
|
|
||||||
# src/plugin - ALL isolated (mock.module pollution crosses between files)
|
|
||||||
for f in $(find src/plugin -name '*.test.ts' | sort); do bun test "$f"; done
|
|
||||||
# src/features/background-agent - ALL isolated (mock.module pollution)
|
|
||||||
for f in $(find src/features/background-agent -name '*.test.ts' | sort); do bun test "$f"; done
|
|
||||||
|
|
||||||
- name: Run remaining tests
|
|
||||||
run: |
|
|
||||||
# Enumerate subdirectories/files explicitly to EXCLUDE mock-heavy files
|
|
||||||
# that were already run in isolation above.
|
|
||||||
# Excluded from src/shared: model-capabilities, log-legacy-plugin-startup-warning, model-error-classifier, opencode-message-dir
|
|
||||||
# Excluded from src/cli: doctor/formatter.test.ts, doctor/format-default.test.ts
|
|
||||||
# Excluded from src/tools: call-omo-agent/sync-executor.test.ts, call-omo-agent/session-creator.test.ts, session-manager (all)
|
|
||||||
# Excluded from src/hooks/anthropic-context-window-limit-recovery: recovery-hook.test.ts, executor.test.ts
|
|
||||||
# Excluded: src/plugin/* (all run isolated above)
|
|
||||||
# Excluded: src/features/background-agent/* (all run isolated above)
|
|
||||||
# Build src/shared file list excluding mock-heavy files already run in isolation
|
|
||||||
SHARED_FILES=$(find src/shared -name '*.test.ts' \
|
|
||||||
! -name 'model-capabilities.test.ts' \
|
|
||||||
! -name 'log-legacy-plugin-startup-warning.test.ts' \
|
|
||||||
! -name 'model-error-classifier.test.ts' \
|
|
||||||
! -name 'opencode-message-dir.test.ts' \
|
|
||||||
| sort | tr '\n' ' ')
|
|
||||||
# plugin and background-agent fully isolated above — excluded from remaining
|
|
||||||
bun test bin script src/config src/mcp src/index.test.ts \
|
|
||||||
src/agents $SHARED_FILES \
|
|
||||||
src/cli/run src/cli/config-manager src/cli/mcp-oauth \
|
|
||||||
src/cli/index.test.ts src/cli/install.test.ts src/cli/model-fallback.test.ts \
|
|
||||||
src/cli/config-manager.test.ts \
|
|
||||||
src/cli/doctor/runner.test.ts src/cli/doctor/checks \
|
|
||||||
src/tools/ast-grep src/tools/background-task src/tools/delegate-task \
|
|
||||||
src/tools/glob src/tools/grep src/tools/interactive-bash \
|
|
||||||
src/tools/look-at src/tools/lsp \
|
|
||||||
src/tools/skill src/tools/skill-mcp src/tools/slashcommand src/tools/task \
|
|
||||||
src/tools/call-omo-agent/background-agent-executor.test.ts \
|
|
||||||
src/tools/call-omo-agent/background-executor.test.ts \
|
|
||||||
src/tools/call-omo-agent/subagent-session-creator.test.ts \
|
|
||||||
src/hooks/anthropic-context-window-limit-recovery/empty-content-recovery-sdk.test.ts src/hooks/anthropic-context-window-limit-recovery/parser.test.ts src/hooks/anthropic-context-window-limit-recovery/pruning-deduplication.test.ts src/hooks/anthropic-context-window-limit-recovery/recovery-deduplication.test.ts src/hooks/anthropic-context-window-limit-recovery/storage.test.ts \
|
|
||||||
src/hooks/session-recovery/detect-error-type.test.ts src/hooks/session-recovery/index.test.ts src/hooks/session-recovery/recover-empty-content-message-sdk.test.ts src/hooks/session-recovery/resume.test.ts src/hooks/session-recovery/storage \
|
|
||||||
src/hooks/legacy-plugin-toast/auto-migrate.test.ts \
|
|
||||||
src/hooks/claude-code-compatibility \
|
|
||||||
src/hooks/context-injection \
|
|
||||||
src/hooks/provider-toast \
|
|
||||||
src/hooks/session-notification \
|
|
||||||
src/hooks/sisyphus \
|
|
||||||
src/hooks/todo-continuation-enforcer \
|
|
||||||
src/features/builtin-commands \
|
|
||||||
src/features/builtin-skills \
|
|
||||||
src/features/claude-code-session-state \
|
|
||||||
src/features/hook-message-injector \
|
|
||||||
src/features/opencode-skill-loader/config-source-discovery.test.ts \
|
|
||||||
src/features/opencode-skill-loader/merger.test.ts \
|
|
||||||
src/features/opencode-skill-loader/skill-content.test.ts \
|
|
||||||
src/features/opencode-skill-loader/blocking.test.ts \
|
|
||||||
src/features/opencode-skill-loader/async-loader.test.ts \
|
|
||||||
src/features/skill-mcp-manager
|
|
||||||
|
|
||||||
typecheck:
|
typecheck:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/// <reference types="bun-types" />
|
/// <reference types="bun-types" />
|
||||||
|
|
||||||
import { beforeEach, describe, expect, it, mock } from "bun:test"
|
import { afterAll, beforeEach, describe, expect, it, mock } from "bun:test"
|
||||||
import { PLUGIN_NAME } from "../../../shared"
|
import { PLUGIN_NAME } from "../../../shared"
|
||||||
import type { PluginInfo } from "./system-plugin"
|
import type { PluginInfo } from "./system-plugin"
|
||||||
|
|
||||||
@@ -47,6 +47,10 @@ mock.module("./system-loaded-version", () => ({
|
|||||||
getSuggestedInstallTag: mockGetSuggestedInstallTag,
|
getSuggestedInstallTag: mockGetSuggestedInstallTag,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
describe("system check", () => {
|
describe("system check", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockFindOpenCodeBinary.mockReset()
|
mockFindOpenCodeBinary.mockReset()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach, mock } from "bun:test"
|
import { afterAll, describe, it, expect, beforeEach, afterEach, mock } from "bun:test"
|
||||||
|
|
||||||
const mockLogin = mock(() => Promise.resolve({ accessToken: "test-token", expiresAt: 1710000000 }))
|
const mockLogin = mock(() => Promise.resolve({ accessToken: "test-token", expiresAt: 1710000000 }))
|
||||||
|
|
||||||
@@ -11,6 +11,10 @@ mock.module("../../features/mcp-oauth/provider", () => ({
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
const { login } = await import("./login")
|
const { login } = await import("./login")
|
||||||
|
|
||||||
describe("login command", () => {
|
describe("login command", () => {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ mock.module("../../shared/port-utils", () => ({
|
|||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
mock.module("@opencode-ai/sdk", () => originalSdk)
|
mock.module("@opencode-ai/sdk", () => originalSdk)
|
||||||
mock.module("../../shared/port-utils", () => originalPortUtils)
|
mock.module("../../shared/port-utils", () => originalPortUtils)
|
||||||
|
mock.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
const { createServerConnection } = await import("./server-connection")
|
const { createServerConnection } = await import("./server-connection")
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ afterAll(() => {
|
|||||||
mock.module("@opencode-ai/sdk", () => originalSdk)
|
mock.module("@opencode-ai/sdk", () => originalSdk)
|
||||||
mock.module("../../shared/port-utils", () => originalPortUtils)
|
mock.module("../../shared/port-utils", () => originalPortUtils)
|
||||||
mock.module("./opencode-binary-resolver", () => originalBinaryResolver)
|
mock.module("./opencode-binary-resolver", () => originalBinaryResolver)
|
||||||
|
mock.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
const { createServerConnection } = await import("./server-connection")
|
const { createServerConnection } = await import("./server-connection")
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
declare const require: (name: string) => any
|
declare const require: (name: string) => any
|
||||||
const { describe, test, expect, beforeEach, afterEach, spyOn, mock } = require("bun:test")
|
const { describe, test, expect, beforeEach, afterEach, afterAll, spyOn, mock } = require("bun:test")
|
||||||
|
|
||||||
mock.module("../../shared/connected-providers-cache", () => ({
|
mock.module("../../shared/connected-providers-cache", () => ({
|
||||||
readConnectedProvidersCache: () => null,
|
readConnectedProvidersCache: () => null,
|
||||||
@@ -10,6 +10,8 @@ mock.module("../../shared/connected-providers-cache", () => ({
|
|||||||
updateConnectedProvidersCache: () => {},
|
updateConnectedProvidersCache: () => {},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
import { getSessionPromptParams, clearSessionPromptParams } from "../../shared/session-prompt-params-state"
|
import { getSessionPromptParams, clearSessionPromptParams } from "../../shared/session-prompt-params-state"
|
||||||
import { tmpdir } from "node:os"
|
import { tmpdir } from "node:os"
|
||||||
import type { PluginInput } from "@opencode-ai/plugin"
|
import type { PluginInput } from "@opencode-ai/plugin"
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { describe, test, expect, mock } from "bun:test"
|
import { describe, test, expect, mock, afterAll } from "bun:test"
|
||||||
import { isActiveSessionStatus, isTerminalSessionStatus } from "./session-status-classifier"
|
import { isActiveSessionStatus, isTerminalSessionStatus } from "./session-status-classifier"
|
||||||
|
|
||||||
const mockLog = mock()
|
const mockLog = mock()
|
||||||
mock.module("../../shared", () => ({ log: mockLog }))
|
mock.module("../../shared", () => ({ log: mockLog }))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
describe("isActiveSessionStatus", () => {
|
describe("isActiveSessionStatus", () => {
|
||||||
describe("#given a known active session status", () => {
|
describe("#given a known active session status", () => {
|
||||||
test('#when type is "busy" #then returns true', () => {
|
test('#when type is "busy" #then returns true', () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
|
import { afterEach, beforeEach, describe, expect, it, mock, afterAll } from "bun:test"
|
||||||
import type { ClaudeCodeMcpServer } from "../claude-code-mcp-loader/types"
|
import type { ClaudeCodeMcpServer } from "../claude-code-mcp-loader/types"
|
||||||
import type { SkillMcpClientInfo, SkillMcpManagerState } from "./types"
|
import type { SkillMcpClientInfo, SkillMcpManagerState } from "./types"
|
||||||
|
|
||||||
@@ -47,6 +47,8 @@ mock.module("@modelcontextprotocol/sdk/client/stdio.js", () => ({
|
|||||||
StdioClientTransport: MockStdioClientTransport,
|
StdioClientTransport: MockStdioClientTransport,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
const { disconnectAll, disconnectSession } = await import("./cleanup")
|
const { disconnectAll, disconnectSession } = await import("./cleanup")
|
||||||
const { getOrCreateClient } = await import("./connection")
|
const { getOrCreateClient } = await import("./connection")
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach, mock, spyOn } from "bun:test"
|
import { describe, it, expect, beforeEach, afterEach, afterAll, mock, spyOn } from "bun:test"
|
||||||
import { SkillMcpManager } from "./manager"
|
import { SkillMcpManager } from "./manager"
|
||||||
import type { SkillMcpClientInfo, SkillMcpServerContext } from "./types"
|
import type { SkillMcpClientInfo, SkillMcpServerContext } from "./types"
|
||||||
import type { ClaudeCodeMcpServer } from "../claude-code-mcp-loader/types"
|
import type { ClaudeCodeMcpServer } from "../claude-code-mcp-loader/types"
|
||||||
@@ -22,33 +22,19 @@ mock.module("@modelcontextprotocol/sdk/client/streamableHttp.js", () => ({
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const mockTokens = mock(() => null as { accessToken: string; refreshToken?: string; expiresAt?: number } | null)
|
// Mock OAuth provider for OAuth integration tests
|
||||||
const mockLogin = mock(() => Promise.resolve({ accessToken: "new-token" }))
|
const mockTokens = mock(() => null as { accessToken: string } | null)
|
||||||
|
const mockLogin = mock(() => Promise.resolve({ accessToken: "test-token" }) as Promise<{ accessToken: string } | null>)
|
||||||
|
|
||||||
mock.module("../mcp-oauth/provider", () => ({
|
mock.module("../mcp-oauth/provider", () => ({
|
||||||
McpOAuthProvider: class MockMcpOAuthProvider {
|
McpOAuthProvider: class MockMcpOAuthProvider {
|
||||||
constructor(public options: { serverUrl: string; clientId?: string; scopes?: string[] }) {}
|
tokens = mockTokens
|
||||||
tokens() {
|
login = mockLogin
|
||||||
return mockTokens()
|
constructor(_opts: unknown) {}
|
||||||
}
|
|
||||||
async login() {
|
|
||||||
return mockLogin()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
describe("SkillMcpManager", () => {
|
describe("SkillMcpManager", () => {
|
||||||
let manager: SkillMcpManager
|
let manager: SkillMcpManager
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, test, expect, mock, beforeEach, spyOn } from 'bun:test'
|
import { describe, test, expect, mock, beforeEach, spyOn, afterAll } from 'bun:test'
|
||||||
import type { TmuxConfig } from '../../config/schema'
|
import type { TmuxConfig } from '../../config/schema'
|
||||||
import type { WindowState, PaneAction } from './types'
|
import type { WindowState, PaneAction } from './types'
|
||||||
import type { ActionResult, ExecuteContext } from './action-executor'
|
import type { ActionResult, ExecuteContext } from './action-executor'
|
||||||
@@ -77,6 +77,8 @@ mock.module('./pane-state-querier', () => ({
|
|||||||
: null,
|
: null,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
mock.module('./action-executor', () => ({
|
mock.module('./action-executor', () => ({
|
||||||
executeActions: mockExecuteActions,
|
executeActions: mockExecuteActions,
|
||||||
executeAction: mockExecuteAction,
|
executeAction: mockExecuteAction,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { beforeEach, describe, expect, mock, test } from "bun:test"
|
import { beforeEach, describe, expect, mock, test, afterAll } from "bun:test"
|
||||||
import type { TmuxConfig } from "../../config/schema"
|
import type { TmuxConfig } from "../../config/schema"
|
||||||
import type { ActionResult, ExecuteContext, ExecuteActionsResult } from "./action-executor"
|
import type { ActionResult, ExecuteContext, ExecuteActionsResult } from "./action-executor"
|
||||||
import type { TmuxUtilDeps } from "./manager"
|
import type { TmuxUtilDeps } from "./manager"
|
||||||
@@ -46,6 +46,8 @@ mock.module("../../shared/tmux", () => ({
|
|||||||
SESSION_MISSING_GRACE_MS: 1_000,
|
SESSION_MISSING_GRACE_MS: 1_000,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
const mockTmuxDeps: TmuxUtilDeps = {
|
const mockTmuxDeps: TmuxUtilDeps = {
|
||||||
isInsideTmux: mockIsInsideTmux,
|
isInsideTmux: mockIsInsideTmux,
|
||||||
getCurrentPaneId: mockGetCurrentPaneId,
|
getCurrentPaneId: mockGetCurrentPaneId,
|
||||||
|
|||||||
+5
-1
@@ -1,4 +1,4 @@
|
|||||||
import { describe, it, expect, mock, beforeEach } from "bun:test"
|
import { afterAll, describe, it, expect, mock, beforeEach } from "bun:test"
|
||||||
import { fixEmptyMessagesWithSDK } from "./empty-content-recovery-sdk"
|
import { fixEmptyMessagesWithSDK } from "./empty-content-recovery-sdk"
|
||||||
|
|
||||||
const mockReplaceEmptyTextParts = mock(() => Promise.resolve(false))
|
const mockReplaceEmptyTextParts = mock(() => Promise.resolve(false))
|
||||||
@@ -11,6 +11,10 @@ mock.module("../session-recovery/storage/text-part-injector", () => ({
|
|||||||
injectTextPartAsync: mockInjectTextPart,
|
injectTextPartAsync: mockInjectTextPart,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
function createMockClient(messages: Array<{ info?: { id?: string }; parts?: Array<{ type?: string; text?: string }> }>) {
|
function createMockClient(messages: Array<{ info?: { id?: string }; parts?: Array<{ type?: string; text?: string }> }>) {
|
||||||
return {
|
return {
|
||||||
session: {
|
session: {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ mock.module("./deduplication-recovery", () => ({
|
|||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
mock.module("./deduplication-recovery", () => originalDeduplicationRecovery)
|
mock.module("./deduplication-recovery", () => originalDeduplicationRecovery)
|
||||||
|
mock.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
function createImmediateTimeouts(): () => void {
|
function createImmediateTimeouts(): () => void {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ mock.module("./storage", () => {
|
|||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
mock.module("./storage", () => storage)
|
mock.module("./storage", () => storage)
|
||||||
|
mock.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("truncateUntilTargetTokens", () => {
|
describe("truncateUntilTargetTokens", () => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
declare const require: (name: string) => any
|
declare const require: (name: string) => any
|
||||||
const { afterEach, beforeEach, describe, expect, mock, test } = require("bun:test")
|
const { afterEach, beforeEach, describe, expect, mock, test, afterAll } = require("bun:test")
|
||||||
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
|
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
|
||||||
import { tmpdir } from "node:os"
|
import { tmpdir } from "node:os"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
@@ -30,6 +30,8 @@ mock.module("../../shared/opencode-storage-detection", () => ({
|
|||||||
isSqliteBackend: () => false,
|
isSqliteBackend: () => false,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
const { createAtlasHook } = await import("./index")
|
const { createAtlasHook } = await import("./index")
|
||||||
|
|
||||||
describe("atlas hook compaction agent filtering", () => {
|
describe("atlas hook compaction agent filtering", () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test"
|
import { afterEach, beforeEach, describe, expect, mock, test, afterAll } from "bun:test"
|
||||||
import { randomUUID } from "node:crypto"
|
import { randomUUID } from "node:crypto"
|
||||||
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
|
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
|
||||||
import { tmpdir } from "node:os"
|
import { tmpdir } from "node:os"
|
||||||
@@ -29,6 +29,8 @@ mock.module("../../shared/opencode-storage-detection", () => ({
|
|||||||
isSqliteBackend: () => false,
|
isSqliteBackend: () => false,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
const { createAtlasHook } = await import("./index")
|
const { createAtlasHook } = await import("./index")
|
||||||
const { MESSAGE_STORAGE } = await import("../../features/hook-message-injector")
|
const { MESSAGE_STORAGE } = await import("../../features/hook-message-injector")
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test"
|
import { afterEach, beforeEach, describe, expect, mock, test, afterAll } from "bun:test"
|
||||||
import { randomUUID } from "node:crypto"
|
import { randomUUID } from "node:crypto"
|
||||||
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
|
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
|
||||||
import { tmpdir } from "node:os"
|
import { tmpdir } from "node:os"
|
||||||
@@ -29,6 +29,8 @@ mock.module("../../shared/opencode-storage-detection", () => ({
|
|||||||
isSqliteBackend: () => false,
|
isSqliteBackend: () => false,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
const { createAtlasHook } = await import("./index")
|
const { createAtlasHook } = await import("./index")
|
||||||
const { MESSAGE_STORAGE } = await import("../../features/hook-message-injector")
|
const { MESSAGE_STORAGE } = await import("../../features/hook-message-injector")
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, expect, test, beforeEach, afterEach, mock } from "bun:test"
|
import { describe, expect, test, beforeEach, afterEach, mock, afterAll } from "bun:test"
|
||||||
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
|
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
import { tmpdir } from "node:os"
|
import { tmpdir } from "node:os"
|
||||||
@@ -33,6 +33,8 @@ mock.module("../../shared/opencode-storage-detection", () => ({
|
|||||||
isSqliteBackend: () => false,
|
isSqliteBackend: () => false,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
const { createAtlasHook } = await import("./index")
|
const { createAtlasHook } = await import("./index")
|
||||||
const { createToolExecuteAfterHandler } = await import("./tool-execute-after")
|
const { createToolExecuteAfterHandler } = await import("./tool-execute-after")
|
||||||
const { createToolExecuteBeforeHandler } = await import("./tool-execute-before")
|
const { createToolExecuteBeforeHandler } = await import("./tool-execute-before")
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const { describe, expect, mock, test } = require("bun:test")
|
const { describe, expect, mock, test, afterAll } = require("bun:test")
|
||||||
|
|
||||||
mock.module("../../shared/opencode-message-dir", () => ({
|
mock.module("../../shared/opencode-message-dir", () => ({
|
||||||
getMessageDir: () => null,
|
getMessageDir: () => null,
|
||||||
@@ -12,6 +12,8 @@ mock.module("../../shared/normalize-sdk-response", () => ({
|
|||||||
normalizeSDKResponse: <TData>(response: { data?: TData }, fallback: TData): TData => response.data ?? fallback,
|
normalizeSDKResponse: <TData>(response: { data?: TData }, fallback: TData): TData => response.data ?? fallback,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
const { getLastAgentFromSession } = await import("./session-last-agent")
|
const { getLastAgentFromSession } = await import("./session-last-agent")
|
||||||
|
|
||||||
function createMockClient(messages: Array<{ info?: { agent?: string } }>) {
|
function createMockClient(messages: Array<{ info?: { agent?: string } }>) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/// <reference types="bun-types" />
|
/// <reference types="bun-types" />
|
||||||
|
|
||||||
import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
|
import { afterEach, beforeEach, describe, expect, it, mock, afterAll } from "bun:test"
|
||||||
import { existsSync, mkdirSync, rmSync } from "node:fs"
|
import { existsSync, mkdirSync, rmSync } from "node:fs"
|
||||||
import { tmpdir } from "node:os"
|
import { tmpdir } from "node:os"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
@@ -23,6 +23,8 @@ mock.module("../../shared/git-worktree", () => ({
|
|||||||
formatFileChanges: mock(() => "No file changes"),
|
formatFileChanges: mock(() => "No file changes"),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
const { createToolExecuteAfterHandler } = await import("./tool-execute-after")
|
const { createToolExecuteAfterHandler } = await import("./tool-execute-after")
|
||||||
|
|
||||||
describe("createToolExecuteAfterHandler background launch detection", () => {
|
describe("createToolExecuteAfterHandler background launch detection", () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { beforeEach, describe, expect, it, mock, spyOn } from "bun:test"
|
import { afterAll, beforeEach, describe, expect, it, mock, spyOn } from "bun:test"
|
||||||
import { AUTO_SLASH_COMMAND_TAG_OPEN } from "./constants"
|
import { AUTO_SLASH_COMMAND_TAG_OPEN } from "./constants"
|
||||||
import type {
|
import type {
|
||||||
AutoSlashCommandHookInput,
|
AutoSlashCommandHookInput,
|
||||||
@@ -19,6 +19,10 @@ mock.module("./executor", () => ({
|
|||||||
executeSlashCommand: executeSlashCommandMock,
|
executeSlashCommand: executeSlashCommandMock,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
const logMock = spyOn(shared, "log").mockImplementation(() => {})
|
const logMock = spyOn(shared, "log").mockImplementation(() => {})
|
||||||
|
|
||||||
const { createAutoSlashCommandHook } = await import("./hook")
|
const { createAutoSlashCommandHook } = await import("./hook")
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, expect, it, mock } from "bun:test"
|
import { afterAll, describe, expect, it, mock } from "bun:test"
|
||||||
import type { LoadedSkill } from "../../features/opencode-skill-loader"
|
import type { LoadedSkill } from "../../features/opencode-skill-loader"
|
||||||
|
|
||||||
mock.module("../../shared", () => ({
|
mock.module("../../shared", () => ({
|
||||||
@@ -27,6 +27,10 @@ mock.module("../../features/opencode-skill-loader", () => ({
|
|||||||
discoverAllSkills: async (): Promise<LoadedSkill[]> => [],
|
discoverAllSkills: async (): Promise<LoadedSkill[]> => [],
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
const { executeSlashCommand } = await import("./executor")
|
const { executeSlashCommand } = await import("./executor")
|
||||||
|
|
||||||
function createRestrictedSkill(): LoadedSkill {
|
function createRestrictedSkill(): LoadedSkill {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
|
import { afterAll, afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
|
||||||
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
|
|
||||||
@@ -16,6 +16,10 @@ mock.module("../../shared/logger", () => ({
|
|||||||
log: () => {},
|
log: () => {},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
function resetTestCache(): void {
|
function resetTestCache(): void {
|
||||||
if (existsSync(TEST_CACHE_DIR)) {
|
if (existsSync(TEST_CACHE_DIR)) {
|
||||||
rmSync(TEST_CACHE_DIR, { recursive: true, force: true })
|
rmSync(TEST_CACHE_DIR, { recursive: true, force: true })
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
|
import { afterAll, afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
|
||||||
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
import type { PluginEntryInfo } from "./plugin-entry"
|
import type { PluginEntryInfo } from "./plugin-entry"
|
||||||
@@ -22,6 +22,10 @@ mock.module("../../../shared/logger", () => ({
|
|||||||
log: () => {},
|
log: () => {},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
function resetTestCache(currentVersion = "3.10.0"): void {
|
function resetTestCache(currentVersion = "3.10.0"): void {
|
||||||
if (existsSync(TEST_CACHE_DIR)) {
|
if (existsSync(TEST_CACHE_DIR)) {
|
||||||
rmSync(TEST_CACHE_DIR, { recursive: true, force: true })
|
rmSync(TEST_CACHE_DIR, { recursive: true, force: true })
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
|
import { afterAll, afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
|
||||||
|
|
||||||
const mockShowConfigErrorsIfAny = mock(async () => {})
|
const mockShowConfigErrorsIfAny = mock(async () => {})
|
||||||
const mockShowModelCacheWarningIfNeeded = mock(async () => {})
|
const mockShowModelCacheWarningIfNeeded = mock(async () => {})
|
||||||
@@ -45,6 +45,10 @@ mock.module("../../shared/logger", () => ({
|
|||||||
log: () => {},
|
log: () => {},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
type HookFactory = typeof import("./hook").createAutoUpdateCheckerHook
|
type HookFactory = typeof import("./hook").createAutoUpdateCheckerHook
|
||||||
|
|
||||||
async function importFreshHookFactory(): Promise<HookFactory> {
|
async function importFreshHookFactory(): Promise<HookFactory> {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { PluginInput } from "@opencode-ai/plugin"
|
import type { PluginInput } from "@opencode-ai/plugin"
|
||||||
import { beforeEach, describe, expect, it, mock } from "bun:test"
|
import { afterAll, beforeEach, describe, expect, it, mock } from "bun:test"
|
||||||
|
|
||||||
type PluginEntry = {
|
type PluginEntry = {
|
||||||
entry: string
|
entry: string
|
||||||
@@ -51,6 +51,10 @@ mock.module("./update-toasts", () => ({
|
|||||||
}))
|
}))
|
||||||
mock.module("../../../shared/logger", () => ({ log: () => {} }))
|
mock.module("../../../shared/logger", () => ({ log: () => {} }))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
const modulePath = "./background-update-check?test"
|
const modulePath = "./background-update-check?test"
|
||||||
const { runBackgroundUpdateCheck } = await import(modulePath)
|
const { runBackgroundUpdateCheck } = await import(modulePath)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { PluginInput } from "@opencode-ai/plugin"
|
import type { PluginInput } from "@opencode-ai/plugin"
|
||||||
import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
|
import { afterAll, afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
|
||||||
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
|
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
|
|
||||||
@@ -101,6 +101,10 @@ mock.module("../../../shared/opencode-config-dir", () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
const modulePath = "./background-update-check?test"
|
const modulePath = "./background-update-check?test"
|
||||||
const { runBackgroundUpdateCheck } = await import(modulePath)
|
const { runBackgroundUpdateCheck } = await import(modulePath)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const { beforeEach, describe, expect, mock, test } = require("bun:test")
|
const { beforeEach, describe, expect, mock, test, afterAll } = require("bun:test")
|
||||||
|
|
||||||
const executeStopHooks = mock(async (context: { parentSessionId?: string }) => ({
|
const executeStopHooks = mock(async (context: { parentSessionId?: string }) => ({
|
||||||
block: false,
|
block: false,
|
||||||
@@ -19,6 +19,8 @@ mock.module("../stop", () => ({
|
|||||||
executeStopHooks,
|
executeStopHooks,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
const { createSessionEventHandler } = await import("./session-event-handler")
|
const { createSessionEventHandler } = await import("./session-event-handler")
|
||||||
|
|
||||||
describe("createSessionEventHandler retry behavior", () => {
|
describe("createSessionEventHandler retry behavior", () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { beforeEach, describe, expect, it, mock } from "bun:test"
|
import { beforeEach, describe, expect, it, mock, afterAll } from "bun:test"
|
||||||
|
|
||||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||||
return typeof value === "object" && value !== null && !Array.isArray(value)
|
return typeof value === "object" && value !== null && !Array.isArray(value)
|
||||||
@@ -26,6 +26,8 @@ mock.module("../transcript", () => ({
|
|||||||
getTranscriptPath: () => "/tmp/transcript.jsonl",
|
getTranscriptPath: () => "/tmp/transcript.jsonl",
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
const { createToolExecuteAfterHandler } = await import("./tool-execute-after-handler")
|
const { createToolExecuteAfterHandler } = await import("./tool-execute-after-handler")
|
||||||
|
|
||||||
describe("createToolExecuteAfterHandler", () => {
|
describe("createToolExecuteAfterHandler", () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, it, expect, mock, beforeEach } from "bun:test"
|
import { describe, it, expect, mock, beforeEach, afterAll } from "bun:test"
|
||||||
import type { ClaudeHooksConfig } from "./types"
|
import type { ClaudeHooksConfig } from "./types"
|
||||||
import type { StopContext } from "./stop"
|
import type { StopContext } from "./stop"
|
||||||
|
|
||||||
@@ -17,6 +17,8 @@ mock.module("../../shared/logger", () => ({
|
|||||||
getLogFilePath: () => "/tmp/test.log",
|
getLogFilePath: () => "/tmp/test.log",
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
const { executeStopHooks } = await import("./stop")
|
const { executeStopHooks } = await import("./stop")
|
||||||
|
|
||||||
function createStopContext(overrides?: Partial<StopContext>): StopContext {
|
function createStopContext(overrides?: Partial<StopContext>): StopContext {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, test, expect, mock } from "bun:test"
|
import { describe, test, expect, mock, afterAll } from "bun:test"
|
||||||
import { chmodSync, mkdtempSync, writeFileSync } from "node:fs"
|
import { chmodSync, mkdtempSync, writeFileSync } from "node:fs"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
import { tmpdir } from "node:os"
|
import { tmpdir } from "node:os"
|
||||||
@@ -24,6 +24,8 @@ function createScriptBinary(scriptContent: string): string {
|
|||||||
return binaryPath
|
return binaryPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
describe("comment-checker CLI", () => {
|
describe("comment-checker CLI", () => {
|
||||||
describe("lazy initialization", () => {
|
describe("lazy initialization", () => {
|
||||||
test("getCommentCheckerPathSync should be lazy and callable", async () => {
|
test("getCommentCheckerPathSync should be lazy and callable", async () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, it, expect, mock, beforeEach } from "bun:test"
|
import { describe, it, expect, mock, beforeEach, afterAll } from "bun:test"
|
||||||
|
|
||||||
const processApplyPatchEditsWithCli = mock(async () => {})
|
const processApplyPatchEditsWithCli = mock(async () => {})
|
||||||
|
|
||||||
@@ -10,6 +10,8 @@ mock.module("./cli-runner", () => ({
|
|||||||
processApplyPatchEditsWithCli,
|
processApplyPatchEditsWithCli,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
const { createCommentCheckerHooks } = await import("./hook")
|
const { createCommentCheckerHooks } = await import("./hook")
|
||||||
|
|
||||||
describe("comment-checker apply_patch integration", () => {
|
describe("comment-checker apply_patch integration", () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, expect, it, mock } from "bun:test"
|
import { afterAll, describe, expect, it, mock } from "bun:test"
|
||||||
|
|
||||||
mock.module("../../shared/system-directive", () => ({
|
mock.module("../../shared/system-directive", () => ({
|
||||||
createSystemDirective: (type: string) => `[DIRECTIVE:${type}]`,
|
createSystemDirective: (type: string) => `[DIRECTIVE:${type}]`,
|
||||||
@@ -14,6 +14,10 @@ mock.module("../../shared/system-directive", () => ({
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
import { createCompactionContextInjector } from "./index"
|
import { createCompactionContextInjector } from "./index"
|
||||||
import { TaskHistory } from "../../features/background-agent/task-history"
|
import { TaskHistory } from "../../features/background-agent/task-history"
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ afterAll(() => {
|
|||||||
update: async () => {},
|
update: async () => {},
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
mock.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
function createMockContext(todoResponses: Array<Todo>[]): PluginInput {
|
function createMockContext(todoResponses: Array<Todo>[]): PluginInput {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { mkdirSync, rmSync, writeFileSync } from "node:fs"
|
|||||||
import { tmpdir } from "node:os"
|
import { tmpdir } from "node:os"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
import type { PluginInput } from "@opencode-ai/plugin"
|
import type { PluginInput } from "@opencode-ai/plugin"
|
||||||
import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
|
import { afterAll, afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
|
||||||
|
|
||||||
const storageMaps = new Map<string, Set<string>>()
|
const storageMaps = new Map<string, Set<string>>()
|
||||||
|
|
||||||
@@ -22,6 +22,10 @@ mock.module("./storage", () => ({
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
const truncator = {
|
const truncator = {
|
||||||
truncate: async (_sessionID: string, content: string) => ({ result: content, truncated: false }),
|
truncate: async (_sessionID: string, content: string) => ({ result: content, truncated: false }),
|
||||||
getUsage: async (_sessionID: string) => null,
|
getUsage: async (_sessionID: string) => null,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
|
import { afterAll, afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
|
||||||
import { randomUUID } from "node:crypto"
|
import { randomUUID } from "node:crypto"
|
||||||
import { mkdirSync, rmSync, writeFileSync } from "node:fs"
|
import { mkdirSync, rmSync, writeFileSync } from "node:fs"
|
||||||
import { tmpdir } from "node:os"
|
import { tmpdir } from "node:os"
|
||||||
@@ -15,6 +15,10 @@ mock.module("./storage", () => ({
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
function createPluginContext(directory: string): PluginInput {
|
function createPluginContext(directory: string): PluginInput {
|
||||||
return { directory } as PluginInput
|
return { directory } as PluginInput
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
declare const require: (name: string) => any
|
declare const require: (name: string) => any
|
||||||
const { beforeEach, describe, expect, mock, test } = require("bun:test")
|
const { beforeEach, describe, expect, mock, test, afterAll } = require("bun:test")
|
||||||
|
|
||||||
const readConnectedProvidersCacheMock = mock(() => null)
|
const readConnectedProvidersCacheMock = mock(() => null)
|
||||||
const readProviderModelsCacheMock = mock(() => null)
|
const readProviderModelsCacheMock = mock(() => null)
|
||||||
@@ -53,6 +53,10 @@ mock.module("../../shared/model-error-classifier", () => ({
|
|||||||
selectFallbackProvider: selectFallbackProviderMock,
|
selectFallbackProvider: selectFallbackProviderMock,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
import {
|
import {
|
||||||
clearPendingModelFallback,
|
clearPendingModelFallback,
|
||||||
createModelFallbackHook,
|
createModelFallbackHook,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { beforeEach, describe, expect, mock, test } from "bun:test"
|
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test"
|
||||||
|
|
||||||
const wakeOpenClawMock = mock(async () => null)
|
const wakeOpenClawMock = mock(async () => null)
|
||||||
|
|
||||||
@@ -6,6 +6,10 @@ mock.module("../openclaw", () => ({
|
|||||||
wakeOpenClaw: wakeOpenClawMock,
|
wakeOpenClaw: wakeOpenClawMock,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
describe("createOpenClawHook", () => {
|
describe("createOpenClawHook", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wakeOpenClawMock.mockClear()
|
wakeOpenClawMock.mockClear()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, expect, it, mock } from "bun:test"
|
import { describe, expect, it, mock, afterAll } from "bun:test"
|
||||||
|
|
||||||
import { applyProviderConfig } from "../plugin-handlers/provider-config-handler"
|
import { applyProviderConfig } from "../plugin-handlers/provider-config-handler"
|
||||||
import { createModelCacheState } from "../plugin-state"
|
import { createModelCacheState } from "../plugin-state"
|
||||||
@@ -9,6 +9,8 @@ mock.module("../shared/logger", () => ({
|
|||||||
log: logMock,
|
log: logMock,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
const { createPreemptiveCompactionHook } = await import("./preemptive-compaction")
|
const { createPreemptiveCompactionHook } = await import("./preemptive-compaction")
|
||||||
|
|
||||||
function createMockCtx() {
|
function createMockCtx() {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/// <reference types="bun-types" />
|
/// <reference types="bun-types" />
|
||||||
|
|
||||||
import { beforeEach, describe, expect, it, mock } from "bun:test"
|
import { beforeEach, describe, expect, it, mock, afterAll } from "bun:test"
|
||||||
|
|
||||||
const logMock = mock(() => {})
|
const logMock = mock(() => {})
|
||||||
|
|
||||||
@@ -8,6 +8,8 @@ mock.module("../shared/logger", () => ({
|
|||||||
log: logMock,
|
log: logMock,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
const { createPreemptiveCompactionHook } = await import("./preemptive-compaction")
|
const { createPreemptiveCompactionHook } = await import("./preemptive-compaction")
|
||||||
|
|
||||||
type AssistantHistoryMessage = {
|
type AssistantHistoryMessage = {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/// <reference types="bun-types" />
|
/// <reference types="bun-types" />
|
||||||
|
|
||||||
import { describe, it, expect, mock, beforeEach, afterEach } from "bun:test"
|
import { afterAll, describe, it, expect, mock, beforeEach, afterEach } from "bun:test"
|
||||||
|
|
||||||
const ANTHROPIC_CONTEXT_ENV_KEY = "ANTHROPIC_1M_CONTEXT"
|
const ANTHROPIC_CONTEXT_ENV_KEY = "ANTHROPIC_1M_CONTEXT"
|
||||||
const VERTEX_CONTEXT_ENV_KEY = "VERTEX_ANTHROPIC_1M_CONTEXT"
|
const VERTEX_CONTEXT_ENV_KEY = "VERTEX_ANTHROPIC_1M_CONTEXT"
|
||||||
@@ -28,6 +28,10 @@ mock.module("../shared/logger", () => ({
|
|||||||
log: logMock,
|
log: logMock,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
const { createPreemptiveCompactionHook } = await import("./preemptive-compaction")
|
const { createPreemptiveCompactionHook } = await import("./preemptive-compaction")
|
||||||
|
|
||||||
function createMockCtx() {
|
function createMockCtx() {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, expect, test, beforeEach, afterEach, mock } from "bun:test"
|
import { afterAll, describe, expect, test, beforeEach, afterEach, mock } from "bun:test"
|
||||||
import { mkdirSync, rmSync, writeFileSync } from "node:fs"
|
import { mkdirSync, rmSync, writeFileSync } from "node:fs"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
import { tmpdir } from "node:os"
|
import { tmpdir } from "node:os"
|
||||||
@@ -11,6 +11,10 @@ mock.module("../../shared/opencode-storage-detection", () => ({
|
|||||||
resetSqliteBackendCache: () => {},
|
resetSqliteBackendCache: () => {},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
const { createPrometheusMdOnlyHook } = await import("./index")
|
const { createPrometheusMdOnlyHook } = await import("./index")
|
||||||
const { MESSAGE_STORAGE } = await import("../../features/hook-message-injector")
|
const { MESSAGE_STORAGE } = await import("../../features/hook-message-injector")
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test"
|
import { afterAll, afterEach, beforeEach, describe, expect, mock, test } from "bun:test"
|
||||||
import type { HookDeps, RuntimeFallbackPluginInput } from "./types"
|
import type { HookDeps, RuntimeFallbackPluginInput } from "./types"
|
||||||
|
|
||||||
let capturedDeps: HookDeps | undefined
|
let capturedDeps: HookDeps | undefined
|
||||||
@@ -36,6 +36,10 @@ mock.module("./chat-message-handler", () => ({
|
|||||||
createChatMessageHandler: mockCreateChatMessageHandler,
|
createChatMessageHandler: mockCreateChatMessageHandler,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
const { createRuntimeFallbackHook } = await import("./hook")
|
const { createRuntimeFallbackHook } = await import("./hook")
|
||||||
|
|
||||||
function createMockContext(): RuntimeFallbackPluginInput {
|
function createMockContext(): RuntimeFallbackPluginInput {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const { describe, it, expect, mock, beforeEach } = require("bun:test")
|
const { describe, it, expect, mock, beforeEach, afterAll } = require("bun:test")
|
||||||
|
|
||||||
import type { MessageData } from "./types"
|
import type { MessageData } from "./types"
|
||||||
|
|
||||||
@@ -17,6 +17,10 @@ mock.module("./storage", () => ({
|
|||||||
readParts: () => storedParts,
|
readParts: () => storedParts,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
const { recoverToolResultMissing } = await import("./recover-tool-result-missing")
|
const { recoverToolResultMissing } = await import("./recover-tool-result-missing")
|
||||||
|
|
||||||
function createMockClient(messages: MessageData[] = []) {
|
function createMockClient(messages: MessageData[] = []) {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
writeBoulderState,
|
writeBoulderState,
|
||||||
} from "../../features/boulder-state"
|
} from "../../features/boulder-state"
|
||||||
import { log } from "../../shared/logger"
|
import { log } from "../../shared/logger"
|
||||||
|
import { createWorktreeActiveBlock } from "./worktree-block"
|
||||||
import type { PluginInput } from "@opencode-ai/plugin"
|
import type { PluginInput } from "@opencode-ai/plugin"
|
||||||
import { HOOK_NAME } from "./start-work-hook"
|
import { HOOK_NAME } from "./start-work-hook"
|
||||||
|
|
||||||
@@ -158,7 +159,9 @@ Looking for new plans...`
|
|||||||
appendSessionId(directory, sessionId)
|
appendSessionId(directory, sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
const worktreeDisplay = effectiveWorktree ? worktreeBlock.replace(worktreePath ?? "", effectiveWorktree) : worktreeBlock
|
const worktreeDisplay = effectiveWorktree
|
||||||
|
? (worktreeBlock || createWorktreeActiveBlock(effectiveWorktree))
|
||||||
|
: worktreeBlock
|
||||||
|
|
||||||
return `
|
return `
|
||||||
## Active Work Session Found
|
## Active Work Session Found
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
import { detectWorktreePath } from "./worktree-detector"
|
import { detectWorktreePath } from "./worktree-detector"
|
||||||
import { parseUserRequest } from "./parse-user-request"
|
import { parseUserRequest } from "./parse-user-request"
|
||||||
import { buildStartWorkContextInfo } from "./context-info-builder"
|
import { buildStartWorkContextInfo } from "./context-info-builder"
|
||||||
|
import { createWorktreeActiveBlock } from "./worktree-block"
|
||||||
|
|
||||||
export const HOOK_NAME = "start-work" as const
|
export const HOOK_NAME = "start-work" as const
|
||||||
const START_WORK_TEMPLATE_MARKER = "You are starting a Sisyphus work session."
|
const START_WORK_TEMPLATE_MARKER = "You are starting a Sisyphus work session."
|
||||||
@@ -44,18 +45,6 @@ interface StartWorkHookOutput {
|
|||||||
parts: Array<{ type: string; text?: string }>
|
parts: Array<{ type: string; text?: string }>
|
||||||
}
|
}
|
||||||
|
|
||||||
function createWorktreeActiveBlock(worktreePath: string): string {
|
|
||||||
return `
|
|
||||||
## Worktree Active
|
|
||||||
|
|
||||||
**Worktree**: \`${worktreePath}\`
|
|
||||||
|
|
||||||
**CRITICAL - DO NOT FORGET**: You are working inside a git worktree. ALL operations MUST be performed exclusively within this worktree directory.
|
|
||||||
- Every file read, write, edit, and git operation MUST target paths under: \`${worktreePath}\`
|
|
||||||
- When delegating tasks to subagents, you MUST include the worktree path in your delegation prompt so they also operate exclusively within the worktree
|
|
||||||
- NEVER operate on the main repository directory - always use the worktree path above`
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveWorktreeContext(
|
function resolveWorktreeContext(
|
||||||
explicitWorktreePath: string | null,
|
explicitWorktreePath: string | null,
|
||||||
): { worktreePath: string | undefined; block: string } {
|
): { worktreePath: string | undefined; block: string } {
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export function createWorktreeActiveBlock(worktreePath: string): string {
|
||||||
|
return `
|
||||||
|
## Worktree Active
|
||||||
|
|
||||||
|
**Worktree**: \`${worktreePath}\`
|
||||||
|
|
||||||
|
**CRITICAL - DO NOT FORGET**: You are working inside a git worktree. ALL operations MUST be performed exclusively within this worktree directory.
|
||||||
|
- Every file read, write, edit, and git operation MUST target paths under: \`${worktreePath}\`
|
||||||
|
- When delegating tasks to subagents, you MUST include the worktree path in your delegation prompt so they also operate exclusively within the worktree
|
||||||
|
- NEVER operate on the main repository directory - always use the worktree path above`
|
||||||
|
}
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
declare const require: (name: string) => any
|
declare const require: (name: string) => any
|
||||||
const { afterEach, describe, expect, mock, test } = require("bun:test")
|
const { afterEach, afterAll, describe, expect, mock, test } = require("bun:test")
|
||||||
|
|
||||||
mock.module("../shared/connected-providers-cache", () => ({
|
mock.module("../shared/connected-providers-cache", () => ({
|
||||||
readConnectedProvidersCache: () => null,
|
readConnectedProvidersCache: () => null,
|
||||||
readProviderModelsCache: () => null,
|
readProviderModelsCache: () => null,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
import { createEventHandler } from "./event"
|
import { createEventHandler } from "./event"
|
||||||
import { createChatMessageHandler } from "./chat-message"
|
import { createChatMessageHandler } from "./chat-message"
|
||||||
import { _resetForTesting, setMainSession } from "../features/claude-code-session-state"
|
import { _resetForTesting, setMainSession } from "../features/claude-code-session-state"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
declare const require: (name: string) => any
|
declare const require: (name: string) => any
|
||||||
const { afterEach, describe, expect, mock, test } = require("bun:test")
|
const { afterEach, afterAll, describe, expect, mock, test } = require("bun:test")
|
||||||
|
|
||||||
const PROVIDER_ID = "cliproxyapi"
|
const PROVIDER_ID = "cliproxyapi"
|
||||||
|
|
||||||
@@ -10,6 +10,8 @@ mock.module("../shared/connected-providers-cache", () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
import { createEventHandler } from "./event"
|
import { createEventHandler } from "./event"
|
||||||
import { createChatMessageHandler } from "./chat-message"
|
import { createChatMessageHandler } from "./chat-message"
|
||||||
import { createModelFallbackHook } from "../hooks/model-fallback/hook"
|
import { createModelFallbackHook } from "../hooks/model-fallback/hook"
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { beforeEach, describe, expect, it, mock } from "bun:test"
|
import { beforeEach, describe, expect, it, spyOn } from "bun:test"
|
||||||
import type { OhMyOpenCodeConfig } from "../../config"
|
import type { OhMyOpenCodeConfig } from "../../config"
|
||||||
import type { ModelCacheState } from "../../plugin-state"
|
import type { ModelCacheState } from "../../plugin-state"
|
||||||
import type { PluginContext } from "../types"
|
import type { PluginContext } from "../types"
|
||||||
|
import * as hooks from "../../hooks"
|
||||||
|
|
||||||
const mockContext = {
|
const mockContext = {
|
||||||
directory: "/tmp",
|
directory: "/tmp",
|
||||||
@@ -9,58 +10,41 @@ const mockContext = {
|
|||||||
|
|
||||||
const mockModelCacheState = {
|
const mockModelCacheState = {
|
||||||
anthropicContext1MEnabled: false,
|
anthropicContext1MEnabled: false,
|
||||||
|
modelContextLimitsCache: new Map(),
|
||||||
} satisfies ModelCacheState
|
} satisfies ModelCacheState
|
||||||
|
|
||||||
let capturedRulesInjectorOptions: { skipClaudeUserRules?: boolean } | undefined
|
|
||||||
|
|
||||||
mock.module("../../hooks", () => ({
|
|
||||||
createCommentCheckerHooks: () => ({ name: "comment-checker" }),
|
|
||||||
createToolOutputTruncatorHook: () => ({ name: "tool-output-truncator" }),
|
|
||||||
createDirectoryAgentsInjectorHook: () => ({ name: "directory-agents-injector" }),
|
|
||||||
createDirectoryReadmeInjectorHook: () => ({ name: "directory-readme-injector" }),
|
|
||||||
createEmptyTaskResponseDetectorHook: () => ({ name: "empty-task-response-detector" }),
|
|
||||||
createRulesInjectorHook: (
|
|
||||||
_ctx: PluginContext,
|
|
||||||
_modelCacheState: ModelCacheState,
|
|
||||||
options?: { skipClaudeUserRules?: boolean },
|
|
||||||
) => {
|
|
||||||
capturedRulesInjectorOptions = options
|
|
||||||
return { name: "rules-injector" }
|
|
||||||
},
|
|
||||||
createTasksTodowriteDisablerHook: () => ({ name: "tasks-todowrite-disabler" }),
|
|
||||||
createWriteExistingFileGuardHook: () => ({ name: "write-existing-file-guard" }),
|
|
||||||
createBashFileReadGuardHook: () => ({ name: "bash-file-read-guard" }),
|
|
||||||
createHashlineReadEnhancerHook: () => ({ name: "hashline-read-enhancer" }),
|
|
||||||
createReadImageResizerHook: () => ({ name: "read-image-resizer" }),
|
|
||||||
createJsonErrorRecoveryHook: () => ({ name: "json-error-recovery" }),
|
|
||||||
createTodoDescriptionOverrideHook: () => ({ name: "todo-description-override" }),
|
|
||||||
createWebFetchRedirectGuardHook: () => ({ name: "webfetch-redirect-guard" }),
|
|
||||||
}))
|
|
||||||
|
|
||||||
describe("createToolGuardHooks", () => {
|
describe("createToolGuardHooks", () => {
|
||||||
|
let capturedOptions: { skipClaudeUserRules?: boolean } | undefined
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
capturedRulesInjectorOptions = undefined
|
capturedOptions = undefined
|
||||||
|
spyOn(hooks, "createRulesInjectorHook").mockImplementation(
|
||||||
|
(_ctx: unknown, _state: unknown, options?: { skipClaudeUserRules?: boolean }) => {
|
||||||
|
capturedOptions = options
|
||||||
|
return { name: "rules-injector" } as never
|
||||||
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("skips Claude user rules when claude_code.hooks is false", async () => {
|
it("skips Claude user rules when claude_code.hooks is false", () => {
|
||||||
// given
|
// given
|
||||||
const pluginConfig = {
|
const pluginConfig = {
|
||||||
claude_code: {
|
claude_code: {
|
||||||
hooks: false,
|
hooks: false,
|
||||||
},
|
},
|
||||||
} as OhMyOpenCodeConfig
|
} as OhMyOpenCodeConfig
|
||||||
const { createToolGuardHooks } = await import("./create-tool-guard-hooks")
|
const { createToolGuardHooks } = require("./create-tool-guard-hooks")
|
||||||
|
|
||||||
// when
|
// when
|
||||||
createToolGuardHooks({
|
createToolGuardHooks({
|
||||||
ctx: mockContext,
|
ctx: mockContext,
|
||||||
pluginConfig,
|
pluginConfig,
|
||||||
modelCacheState: mockModelCacheState,
|
modelCacheState: mockModelCacheState,
|
||||||
isHookEnabled: (hookName) => hookName === "rules-injector",
|
isHookEnabled: (hookName: string) => hookName === "rules-injector",
|
||||||
safeHookEnabled: true,
|
safeHookEnabled: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(capturedRulesInjectorOptions).toEqual({ skipClaudeUserRules: true })
|
expect(capturedOptions).toEqual({ skipClaudeUserRules: true })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/// <reference path="../../bun-test.d.ts" />
|
/// <reference path="../../bun-test.d.ts" />
|
||||||
|
|
||||||
import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
|
import { afterAll, afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
|
||||||
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
||||||
import { tmpdir } from "node:os"
|
import { tmpdir } from "node:os"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
@@ -9,6 +9,10 @@ async function importFreshMigrationModule(): Promise<typeof import("./migrate-le
|
|||||||
return import(`./migrate-legacy-plugin-entry?test=${Date.now()}-${Math.random()}`)
|
return import(`./migrate-legacy-plugin-entry?test=${Date.now()}-${Math.random()}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore()
|
||||||
|
})
|
||||||
|
|
||||||
describe("migrateLegacyPluginEntry", () => {
|
describe("migrateLegacyPluginEntry", () => {
|
||||||
let testDir = ""
|
let testDir = ""
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
declare const require: (name: string) => any
|
declare const require: (name: string) => any
|
||||||
const { describe, expect, test, beforeEach, mock } = require("bun:test")
|
const { describe, expect, test, beforeEach, mock, afterAll } = require("bun:test")
|
||||||
|
|
||||||
const readConnectedProvidersCacheMock = mock(() => null)
|
const readConnectedProvidersCacheMock = mock(() => null)
|
||||||
|
|
||||||
@@ -7,6 +7,8 @@ mock.module("./connected-providers-cache", () => ({
|
|||||||
readConnectedProvidersCache: readConnectedProvidersCacheMock,
|
readConnectedProvidersCache: readConnectedProvidersCacheMock,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
import { shouldRetryError, selectFallbackProvider } from "./model-error-classifier"
|
import { shouldRetryError, selectFallbackProvider } from "./model-error-classifier"
|
||||||
|
|
||||||
describe("model-error-classifier", () => {
|
describe("model-error-classifier", () => {
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ mock.module("./opencode-storage-detection", () => ({
|
|||||||
resetSqliteBackendCache: () => {},
|
resetSqliteBackendCache: () => {},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
const { getMessageDir } = await import("./opencode-message-dir")
|
const { getMessageDir } = await import("./opencode-message-dir")
|
||||||
|
|
||||||
describe("getMessageDir", () => {
|
describe("getMessageDir", () => {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { mkdtempSync, rmSync, writeFileSync } from "node:fs"
|
|||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
import { tmpdir } from "node:os"
|
import { tmpdir } from "node:os"
|
||||||
|
|
||||||
import { describe, it, expect, spyOn, mock, beforeEach, afterEach } from "bun:test"
|
import { describe, it, expect, spyOn, mock, beforeEach, afterEach, afterAll } from "bun:test"
|
||||||
|
|
||||||
mock.module("vscode-jsonrpc/node", () => ({
|
mock.module("vscode-jsonrpc/node", () => ({
|
||||||
createMessageConnection: () => {
|
createMessageConnection: () => {
|
||||||
@@ -12,6 +12,8 @@ mock.module("vscode-jsonrpc/node", () => ({
|
|||||||
StreamMessageWriter: function StreamMessageWriter() {},
|
StreamMessageWriter: function StreamMessageWriter() {},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
import { LSPClient, lspManager, validateCwd } from "./client"
|
import { LSPClient, lspManager, validateCwd } from "./client"
|
||||||
import type { ResolvedServer } from "./types"
|
import type { ResolvedServer } from "./types"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, test, expect, beforeEach, afterEach, mock } from "bun:test"
|
import { describe, test, expect, beforeEach, afterEach, afterAll, mock } from "bun:test"
|
||||||
import { mkdirSync, writeFileSync, rmSync, existsSync, readdirSync } from "node:fs"
|
import { mkdirSync, writeFileSync, rmSync, existsSync, readdirSync } from "node:fs"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
import { tmpdir } from "node:os"
|
import { tmpdir } from "node:os"
|
||||||
@@ -59,6 +59,9 @@ mock.module("../../shared/opencode-message-dir", () => ({
|
|||||||
return null
|
return null
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => { mock.restore() })
|
||||||
|
|
||||||
const { getAllSessions, getMessageDir, sessionExists, readSessionMessages, readSessionTodos, getSessionInfo } =
|
const { getAllSessions, getMessageDir, sessionExists, readSessionMessages, readSessionTodos, getSessionInfo } =
|
||||||
await import("./storage")
|
await import("./storage")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user