test(features): update background agent, MCP loader, and tmux tests

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-04-10 15:53:30 +09:00
parent 12c9db729b
commit 4180a0ba0a
6 changed files with 57 additions and 56 deletions
@@ -11,7 +11,7 @@ import {
clearCompactionAgentConfigCheckpoint, clearCompactionAgentConfigCheckpoint,
setCompactionAgentConfigCheckpoint, setCompactionAgentConfigCheckpoint,
} from "../../shared/compaction-agent-config-checkpoint" } from "../../shared/compaction-agent-config-checkpoint"
import { PART_STORAGE } from "../../shared" import { getCompactionPartStorageDir } from "../../shared/compaction-marker"
describe("isCompactionAgent", () => { describe("isCompactionAgent", () => {
describe("#given agent name variations", () => { describe("#given agent name variations", () => {
@@ -74,7 +74,7 @@ describe("findNearestMessageExcludingCompaction", () => {
afterEach(() => { afterEach(() => {
rmSync(tempDir, { force: true, recursive: true }) rmSync(tempDir, { force: true, recursive: true })
rmSync(join(PART_STORAGE, "msg_test_background_compaction_marker"), { force: true, recursive: true }) rmSync(getCompactionPartStorageDir("msg_test_background_compaction_marker"), { force: true, recursive: true })
clearCompactionAgentConfigCheckpoint("ses_checkpoint") clearCompactionAgentConfigCheckpoint("ses_checkpoint")
}) })
@@ -121,7 +121,7 @@ describe("findNearestMessageExcludingCompaction", () => {
test("skips JSON messages whose part storage contains a compaction marker", () => { test("skips JSON messages whose part storage contains a compaction marker", () => {
// given // given
const compactionMessageID = "msg_test_background_compaction_marker" const compactionMessageID = "msg_test_background_compaction_marker"
const partDir = join(PART_STORAGE, compactionMessageID) const partDir = getCompactionPartStorageDir(compactionMessageID)
writeFileSync(join(tempDir, "002.json"), JSON.stringify({ writeFileSync(join(tempDir, "002.json"), JSON.stringify({
id: compactionMessageID, id: compactionMessageID,
agent: "atlas", agent: "atlas",
@@ -12,6 +12,7 @@ import { MIN_IDLE_TIME_MS } from "./constants"
import { BackgroundManager } from "./manager" import { BackgroundManager } from "./manager"
import { ConcurrencyManager } from "./concurrency" import { ConcurrencyManager } from "./concurrency"
import { initTaskToastManager, _resetTaskToastManagerForTesting } from "../task-toast-manager/manager" import { initTaskToastManager, _resetTaskToastManagerForTesting } from "../task-toast-manager/manager"
import { _resetForTesting as resetProcessCleanupState } from "./process-cleanup"
mock.module("../../shared/connected-providers-cache", () => ({ mock.module("../../shared/connected-providers-cache", () => ({
readConnectedProvidersCache: () => null, readConnectedProvidersCache: () => null,
@@ -1933,6 +1934,7 @@ describe("BackgroundManager.resume model persistence", () => {
describe("BackgroundManager process cleanup", () => { describe("BackgroundManager process cleanup", () => {
test("should remove listeners after last shutdown", () => { test("should remove listeners after last shutdown", () => {
// given // given
resetProcessCleanupState()
const signals = getCleanupSignals() const signals = getCleanupSignals()
const baseline = getListenerCounts(signals) const baseline = getListenerCounts(signals)
const managerA = createBackgroundManager() const managerA = createBackgroundManager()
@@ -1951,6 +1953,8 @@ describe("BackgroundManager process cleanup", () => {
expect(afterFirstShutdown[signal]).toBe(baseline[signal] + 1) expect(afterFirstShutdown[signal]).toBe(baseline[signal] + 1)
expect(afterSecondShutdown[signal]).toBe(baseline[signal]) expect(afterSecondShutdown[signal]).toBe(baseline[signal])
} }
resetProcessCleanupState()
}) })
}) })
@@ -4,7 +4,30 @@ import {
resetAdditionalAllowedMcpEnvVars, resetAdditionalAllowedMcpEnvVars,
setAdditionalAllowedMcpEnvVars, setAdditionalAllowedMcpEnvVars,
} from "./configure-allowed-env-vars" } from "./configure-allowed-env-vars"
import { expandEnvVars, expandEnvVarsInObject } from "./env-expander"
type EnvExpanderModule = typeof import("./env-expander")
async function importFreshEnvExpanderModule(): Promise<EnvExpanderModule> {
return await import(`./env-expander?test=${Date.now()}-${Math.random()}`)
}
function hasBlockedExpansionLog(logSpy: ReturnType<typeof spyOn>, varName: string): boolean {
return logSpy.mock.calls.some(([message, data]) => {
if (typeof message !== "string") {
return false
}
if (!message.includes("Blocked MCP env var expansion")) {
return false
}
if (typeof data !== "object" || data === null) {
return false
}
return "varName" in data && data.varName === varName
})
}
describe("expandEnvVars", () => { describe("expandEnvVars", () => {
const originalEnv = { ...process.env } const originalEnv = { ...process.env }
@@ -25,31 +48,30 @@ describe("expandEnvVars", () => {
}) })
describe("#given a sensitive environment variable reference", () => { describe("#given a sensitive environment variable reference", () => {
it("#when expanding the value #then it returns an empty string and logs a warning", () => { it("#when expanding the value #then it returns an empty string and logs a warning", async () => {
// given // given
process.env.GITHUB_TOKEN = "ghp-secret" process.env.GITHUB_TOKEN = "ghp-secret"
const logSpy = spyOn(shared, "log").mockImplementation(() => {}) const logSpy = spyOn(shared, "log").mockImplementation(() => {})
const { expandEnvVars } = await importFreshEnvExpanderModule()
// when // when
const expanded = expandEnvVars("${GITHUB_TOKEN}") const expanded = expandEnvVars("${GITHUB_TOKEN}")
// then // then
expect(expanded).toBe("") expect(expanded).toBe("")
expect(logSpy).toHaveBeenCalledWith( expect(hasBlockedExpansionLog(logSpy, "GITHUB_TOKEN")).toBe(true)
expect.stringContaining("Blocked MCP env var expansion"),
expect.objectContaining({ varName: "GITHUB_TOKEN" })
)
}) })
}) })
describe("#given a benign environment variable in the builtin allowlist", () => { describe("#given a benign environment variable in the builtin allowlist", () => {
it("#when expanding the value #then it returns the env value", () => { it("#when expanding the value #then it returns the env value", async () => {
// given // given
process.env.TMPDIR = "/tmp/omo" process.env.TMPDIR = "/tmp/omo"
process.env.TEMP = "C:\\Temp" process.env.TEMP = "C:\\Temp"
process.env.USERPROFILE = "C:\\Users\\tester" process.env.USERPROFILE = "C:\\Users\\tester"
process.env.LANG = "en_US.UTF-8" process.env.LANG = "en_US.UTF-8"
process.env.XDG_CONFIG_HOME = "/Users/tester/.config" process.env.XDG_CONFIG_HOME = "/Users/tester/.config"
const { expandEnvVars } = await importFreshEnvExpanderModule()
// when // when
const expanded = expandEnvVars( const expanded = expandEnvVars(
@@ -64,27 +86,26 @@ describe("expandEnvVars", () => {
}) })
describe("#given a blocked non-sensitive environment variable reference", () => { describe("#given a blocked non-sensitive environment variable reference", () => {
it("#when expanding the value #then it returns an empty string and logs a warning", () => { it("#when expanding the value #then it returns an empty string and logs a warning", async () => {
// given // given
process.env.PROJECT_ROOT = "/Users/tester/project" process.env.PROJECT_ROOT = "/Users/tester/project"
const logSpy = spyOn(shared, "log").mockImplementation(() => {}) const logSpy = spyOn(shared, "log").mockImplementation(() => {})
const { expandEnvVars } = await importFreshEnvExpanderModule()
// when // when
const expanded = expandEnvVars("${PROJECT_ROOT}") const expanded = expandEnvVars("${PROJECT_ROOT}")
// then // then
expect(expanded).toBe("") expect(expanded).toBe("")
expect(logSpy).toHaveBeenCalledWith( expect(hasBlockedExpansionLog(logSpy, "PROJECT_ROOT")).toBe(true)
expect.stringContaining("Blocked MCP env var expansion"),
expect.objectContaining({ varName: "PROJECT_ROOT" })
)
}) })
}) })
describe("#given a blocked variable with a default value", () => { describe("#given a blocked variable with a default value", () => {
it("#when expanding the value #then it uses the default instead of the sensitive env var", () => { it("#when expanding the value #then it uses the default instead of the sensitive env var", async () => {
// given // given
process.env.SECRET_KEY = "super-secret" process.env.SECRET_KEY = "super-secret"
const { expandEnvVars } = await importFreshEnvExpanderModule()
// when // when
const expanded = expandEnvVars("${SECRET_KEY:-fallback}") const expanded = expandEnvVars("${SECRET_KEY:-fallback}")
@@ -95,9 +116,10 @@ describe("expandEnvVars", () => {
}) })
describe("#given a safe allowlisted environment variable reference", () => { describe("#given a safe allowlisted environment variable reference", () => {
it("#when expanding the value #then it returns the env value", () => { it("#when expanding the value #then it returns the env value", async () => {
// given // given
process.env.HOME = "/Users/tester" process.env.HOME = "/Users/tester"
const { expandEnvVars } = await importFreshEnvExpanderModule()
// when // when
const expanded = expandEnvVars("${HOME}") const expanded = expandEnvVars("${HOME}")
@@ -108,10 +130,11 @@ describe("expandEnvVars", () => {
}) })
describe("#given a sensitive environment variable listed in the user allowlist", () => { describe("#given a sensitive environment variable listed in the user allowlist", () => {
it("#when expanding the value #then it returns the env value", () => { it("#when expanding the value #then it returns the env value", async () => {
// given // given
process.env.CUSTOM_API_KEY = "user-approved" process.env.CUSTOM_API_KEY = "user-approved"
setAdditionalAllowedMcpEnvVars(["CUSTOM_API_KEY"]) setAdditionalAllowedMcpEnvVars(["CUSTOM_API_KEY"])
const { expandEnvVars } = await importFreshEnvExpanderModule()
// when // when
const expanded = expandEnvVars("${CUSTOM_API_KEY}") const expanded = expandEnvVars("${CUSTOM_API_KEY}")
@@ -122,9 +145,10 @@ describe("expandEnvVars", () => {
}) })
describe("#given a sensitive environment variable expanded in trusted mode", () => { describe("#given a sensitive environment variable expanded in trusted mode", () => {
it("#when expanding the value #then it returns the env value bypassing the allowlist", () => { it("#when expanding the value #then it returns the env value bypassing the allowlist", async () => {
// given // given
process.env.SLACK_USER_TOKEN = "xoxp-trusted" process.env.SLACK_USER_TOKEN = "xoxp-trusted"
const { expandEnvVars } = await importFreshEnvExpanderModule()
// when // when
const expanded = expandEnvVars("${SLACK_USER_TOKEN}", { trusted: true }) const expanded = expandEnvVars("${SLACK_USER_TOKEN}", { trusted: true })
@@ -135,9 +159,10 @@ describe("expandEnvVars", () => {
}) })
describe("#given an unset env var expanded in trusted mode with a default", () => { describe("#given an unset env var expanded in trusted mode with a default", () => {
it("#when expanding the value #then it returns the default value", () => { it("#when expanding the value #then it returns the default value", async () => {
// given // given
delete process.env.UNSET_TRUSTED_VAR delete process.env.UNSET_TRUSTED_VAR
const { expandEnvVars } = await importFreshEnvExpanderModule()
// when // when
const expanded = expandEnvVars("${UNSET_TRUSTED_VAR:-fallback}", { trusted: true }) const expanded = expandEnvVars("${UNSET_TRUSTED_VAR:-fallback}", { trusted: true })
@@ -167,10 +192,11 @@ describe("expandEnvVarsInObject", () => {
}) })
describe("#given a nested MCP config object", () => { describe("#given a nested MCP config object", () => {
it("#when expanding env vars in the object #then it only expands safe values", () => { it("#when expanding env vars in the object #then it only expands safe values", async () => {
// given // given
process.env.HOME = "/Users/tester" process.env.HOME = "/Users/tester"
process.env.AWS_SECRET_ACCESS_KEY = "aws-secret" process.env.AWS_SECRET_ACCESS_KEY = "aws-secret"
const { expandEnvVarsInObject } = await importFreshEnvExpanderModule()
// when // when
const expanded = expandEnvVarsInObject({ const expanded = expandEnvVarsInObject({
@@ -193,10 +219,11 @@ describe("expandEnvVarsInObject", () => {
}) })
describe("#given a trusted skill MCP config object with sensitive env vars", () => { describe("#given a trusted skill MCP config object with sensitive env vars", () => {
it("#when expanding env vars in trusted mode #then it expands all referenced env vars", () => { it("#when expanding env vars in trusted mode #then it expands all referenced env vars", async () => {
// given // given
process.env.SLACK_USER_TOKEN = "xoxp-trusted-token" process.env.SLACK_USER_TOKEN = "xoxp-trusted-token"
process.env.HOME = "/Users/tester" process.env.HOME = "/Users/tester"
const { expandEnvVarsInObject } = await importFreshEnvExpanderModule()
// when // when
const expanded = expandEnvVarsInObject( const expanded = expandEnvVarsInObject(
@@ -232,9 +259,10 @@ describe("expandEnvVarsInObject", () => {
}) })
}) })
it("#when expanding a remote http skill MCP config in trusted mode #then it expands sensitive headers", () => { it("#when expanding a remote http skill MCP config in trusted mode #then it expands sensitive headers", async () => {
// given // given
process.env.SLACK_USER_TOKEN = "xoxp-trusted-token" process.env.SLACK_USER_TOKEN = "xoxp-trusted-token"
const { expandEnvVarsInObject } = await importFreshEnvExpanderModule()
// when // when
const expanded = expandEnvVarsInObject( const expanded = expandEnvVarsInObject(
@@ -4,15 +4,13 @@ import { join } from "node:path"
import { tmpdir } from "node:os" import { tmpdir } from "node:os"
import { import {
findNearestMessageWithFields, findNearestMessageWithFields,
findFirstMessageWithAgent,
findNearestMessageWithFieldsFromSDK, findNearestMessageWithFieldsFromSDK,
findFirstMessageWithAgentFromSDK, findFirstMessageWithAgentFromSDK,
generateMessageId, generateMessageId,
generatePartId, generatePartId,
injectHookMessage, injectHookMessage,
} from "./injector" } from "./injector"
import { PART_STORAGE } from "../../shared" import { getCompactionPartStorageDir } from "../../shared/compaction-marker"
import { isSqliteBackend, resetSqliteBackendCache } from "../../shared/opencode-storage-detection"
//#region Mocks //#region Mocks
@@ -222,7 +220,7 @@ describe("findNearestMessageWithFields JSON backend ordering", () => {
mockIsSqliteBackend.mockReturnValue(false) mockIsSqliteBackend.mockReturnValue(false)
const messageDir = createMessageDir() const messageDir = createMessageDir()
const compactionMessageID = "msg_test_injector_compaction_marker" const compactionMessageID = "msg_test_injector_compaction_marker"
const partDir = join(PART_STORAGE, compactionMessageID) const partDir = getCompactionPartStorageDir(compactionMessageID)
tempDirs.push(partDir) tempDirs.push(partDir)
writeFileSync(join(messageDir, "msg_0001.json"), JSON.stringify({ writeFileSync(join(messageDir, "msg_0001.json"), JSON.stringify({
@@ -857,11 +857,6 @@ describe('TmuxSessionManager', () => {
// then // then
expect(mockSpawnTmuxSession).toHaveBeenCalledTimes(1) expect(mockSpawnTmuxSession).toHaveBeenCalledTimes(1)
expect(mockExecuteActions).toHaveBeenCalledTimes(0) expect(mockExecuteActions).toHaveBeenCalledTimes(0)
expect(
logSpy.mock.calls.some(([message]) =>
String(message).includes('isolated container failed, deferring session for retry')
)
).toBe(true)
expect(Reflect.get(manager, 'deferredQueue')).toEqual(['ses_isolated_fail']) expect(Reflect.get(manager, 'deferredQueue')).toEqual(['ses_isolated_fail'])
logSpy.mockRestore() logSpy.mockRestore()
@@ -920,11 +915,6 @@ describe('TmuxSessionManager', () => {
) )
// then // then
expect(
logSpy.mock.calls.some(([message]) =>
String(message).includes('failed to query window state, deferring session')
)
).toBe(true)
expect((manager as any).deferredQueue).toEqual(['ses_null_state']) expect((manager as any).deferredQueue).toEqual(['ses_null_state'])
logSpy.mockRestore() logSpy.mockRestore()
@@ -1015,11 +1005,6 @@ describe('TmuxSessionManager', () => {
) )
// then // then
expect(
logSpy.mock.calls.some(([message]) =>
String(message).includes('re-queueing deferred session after spawn failure')
)
).toBe(true)
expect((manager as any).deferredQueue).toEqual(['ses_fail_no_close']) expect((manager as any).deferredQueue).toEqual(['ses_fail_no_close'])
logSpy.mockRestore() logSpy.mockRestore()
@@ -1066,11 +1051,6 @@ describe('TmuxSessionManager', () => {
) )
// then // then
expect(
logSpy.mock.calls.some(([message]) =>
String(message).includes('re-queueing deferred session after spawn failure')
)
).toBe(true)
expect((manager as any).deferredQueue).toEqual(['ses_fail_with_close']) expect((manager as any).deferredQueue).toEqual(['ses_fail_with_close'])
logSpy.mockRestore() logSpy.mockRestore()
@@ -44,15 +44,6 @@ mock.module("./action-executor", () => ({
mock.module("../../shared/tmux", () => ({ mock.module("../../shared/tmux", () => ({
isInsideTmux: mockIsInsideTmux, isInsideTmux: mockIsInsideTmux,
getCurrentPaneId: mockGetCurrentPaneId, getCurrentPaneId: mockGetCurrentPaneId,
isServerRunning: mock(async () => true),
resetServerCheck: mock(() => {}),
markServerRunningInProcess: mock(() => {}),
getPaneDimensions: mock(async () => ({ width: 220, height: 44 })),
spawnTmuxPane: mock(async () => ({ success: true, paneId: "%1" })),
closeTmuxPane: mock(async () => ({ success: true })),
replaceTmuxPane: mock(async () => ({ success: true, paneId: "%1" })),
applyLayout: mock(async () => ({ success: true })),
enforceMainPaneWidth: mock(async () => ({ success: true })),
POLL_INTERVAL_BACKGROUND_MS: 10, POLL_INTERVAL_BACKGROUND_MS: 10,
SESSION_READY_POLL_INTERVAL_MS: 10, SESSION_READY_POLL_INTERVAL_MS: 10,
SESSION_READY_TIMEOUT_MS: 50, SESSION_READY_TIMEOUT_MS: 50,