mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 17:37:40 +00:00
test: remove 54 low-value test declarations (#37841)
* test: remove 55 low-value checks across 15 whole suites * test: retain persisted usage mapping boundary coverage
This commit is contained in:
@@ -727,7 +727,6 @@
|
||||
"src/main/acp/acp-structured-child-work.test.ts": 4749,
|
||||
"src/main/acp/acp-structured-command-publication.test.ts": 5167,
|
||||
"src/main/acp/acp-structured-compaction.test.ts": 1981,
|
||||
"src/main/acp/acp-structured-context-clear.test.ts": 698,
|
||||
"src/main/acp/acp-structured-electron-free.test.ts": 299,
|
||||
"src/main/acp/acp-structured-grok-subagent-recordings.test.ts": 1564,
|
||||
"src/main/acp/acp-structured-host-configured-default.test.ts": 2487,
|
||||
@@ -1037,7 +1036,6 @@
|
||||
"src/main/ai-vault/scanners/antigravity/antigravity-ide-history.test.ts": 525,
|
||||
"src/main/ai-vault/scanners/antigravity/antigravity-index-file-admission.test.ts": 170,
|
||||
"src/main/ai-vault/scanners/antigravity/antigravity-opening-prompt-workspace.test.ts": 204,
|
||||
"src/main/ai-vault/scanners/antigravity/antigravity-recorded-history.test.ts": 395,
|
||||
"src/main/ai-vault/cached-session-list-wsl-probe.test.ts": 238,
|
||||
"src/main/ai-vault/cached-session-list.test.ts": 565,
|
||||
"src/main/ai-vault/scanners/codex/codex-session-collection.test.ts": 200,
|
||||
@@ -1065,7 +1063,6 @@
|
||||
"src/main/ai-vault/session-list-results.test.ts": 163,
|
||||
"src/main/ai-vault/session-newest-files.test.ts": 115,
|
||||
"src/main/ai-vault/parse-cache/session-parse-cache-antigravity-upgrade.test.ts": 348,
|
||||
"src/main/ai-vault/parse-cache/session-parse-cache-devin-upgrade.test.ts": 317,
|
||||
"src/main/ai-vault/parse-cache/session-parse-cache-large-scan.test.ts": 4606,
|
||||
"src/main/ai-vault/parse-cache/session-parse-cache-metadata-checkpoint.test.ts": 392,
|
||||
"src/main/ai-vault/parse-cache/session-parse-cache-persistence.test.ts": 2723,
|
||||
@@ -1157,7 +1154,6 @@
|
||||
"src/main/ai-vault/session-scanner-scope.test.ts": 550,
|
||||
"src/main/ai-vault/scan-service/session-scanner-service-cancellation.test.ts": 391,
|
||||
"src/main/ai-vault/scan-service/session-scanner-service-client.test.ts": 327,
|
||||
"src/main/ai-vault/scan-service/session-scanner-service-entry-path.test.ts": 46,
|
||||
"src/main/ai-vault/scan-service/session-scanner-service-entry-subagents.test.ts": 870,
|
||||
"src/main/ai-vault/scan-service/session-scanner-service-entry.test.ts": 909,
|
||||
"src/main/ai-vault/scan-service/session-scanner-service-env.test.ts": 53,
|
||||
@@ -1784,7 +1780,6 @@
|
||||
"src/main/cli/cli-installer-unreadable-command.test.ts": 183,
|
||||
"src/main/cli/cli-installer-windows-path.test.ts": 195,
|
||||
"src/main/cli/cli-installer.test.ts": 294,
|
||||
"src/main/cli/cli-privileged-processes.test.ts": 66,
|
||||
"src/main/cli/keyed-promise-queue.test.ts": 41,
|
||||
"src/main/cli/legacy-appimage-cli-wrapper.test.ts": 38,
|
||||
"src/main/cli/linux-bare-orca-dispatcher-script.test.ts": 272,
|
||||
@@ -2992,7 +2987,6 @@
|
||||
"src/main/ipc/filesystem/filesystem-runtime-upload-staging.test.ts": 106,
|
||||
"src/main/ipc/filesystem/filesystem-search-file-paths.test.ts": 443,
|
||||
"src/main/ipc/filesystem/filesystem-search-rg-timeout.test.ts": 1918,
|
||||
"src/main/ipc/filesystem/filesystem-source-control-ai-targets.test.ts": 59,
|
||||
"src/main/ipc/filesystem/filesystem-symlink-directory-entries.test.ts": 56,
|
||||
"src/main/ipc/filesystem/filesystem-write-bytes.test.ts": 147,
|
||||
"src/main/ipc/filesystem/filesystem-write-identical.test.ts": 138,
|
||||
@@ -3917,7 +3911,6 @@
|
||||
"src/main/native-chat/agent-session-wire/structured-agent-session-restart-stream-duration.test.ts": 2933,
|
||||
"src/main/native-chat/agent-session-wire/structured-agent-session-restart-teardown.test.ts": 2163,
|
||||
"src/main/native-chat/agent-session-wire/structured-agent-session-restart-undrivable.test.ts": 1752,
|
||||
"src/main/native-chat/agent-session-wire/structured-agent-session-restart-witnesses.test.ts": 511,
|
||||
"src/main/native-chat/agent-session-wire/structured-agent-session-resting-options.test.ts": 1696,
|
||||
"src/main/native-chat/agent-session-wire/structured-agent-session-resumed-child-owner.test.ts": 682,
|
||||
"src/main/native-chat/agent-session-wire/structured-agent-session-rewind-at-rest.test.ts": 2072,
|
||||
@@ -4275,7 +4268,6 @@
|
||||
"src/main/persistence/migrating-orcad-catalog/persistence-local-only-worktree-meta.test.ts": 1948,
|
||||
"src/main/persistence/preferences/persistence-onboarding-language-cohort.test.ts": 1760,
|
||||
"src/main/persistence/migrating-orcad-catalog/persistence-orcad-migration-wsl-generation-reuse.test.ts": 1658,
|
||||
"src/main/persistence/applying-settings/notification-settings-normalization.test.ts": 65,
|
||||
"src/main/persistence/applying-settings/persistence-automation-extra-agent-args.test.ts": 2268,
|
||||
"src/main/persistence/applying-settings/persistence-automation-mode-precondition.test.ts": 1595,
|
||||
"src/main/persistence/applying-settings/persistence-automations.test.ts": 2573,
|
||||
@@ -4358,7 +4350,6 @@
|
||||
"src/main/persistence/migrating-orcad-catalog/orcad-source-duplicate-session-identities.test.ts": 336,
|
||||
"src/main/persistence/migrating-orcad-catalog/orcad-source-export.test.ts": 1898,
|
||||
"src/main/persistence/migrating-orcad-catalog/orcad-source-host-qualified-scope.test.ts": 806,
|
||||
"src/main/persistence/migrating-orcad-catalog/orcad-source-left-behind-session-rows.test.ts": 324,
|
||||
"src/main/persistence/migrating-orcad-catalog/orcad-source-legacy-key-scope.test.ts": 796,
|
||||
"src/main/persistence/migrating-orcad-catalog/orcad-source-ownership.test.ts": 380,
|
||||
"src/main/persistence/migrating-orcad-catalog/orcad-source-partial-partition.test.ts": 808,
|
||||
@@ -4720,7 +4711,6 @@
|
||||
"src/main/plugins/plugin-cli-approval.test.ts": 189,
|
||||
"src/main/plugins/plugin-client-list.test.ts": 372,
|
||||
"src/main/plugins/plugin-command-registry.test.ts": 347,
|
||||
"src/main/plugins/plugin-command-replacement-owners.test.ts": 42,
|
||||
"src/main/plugins/plugin-content-hash-dev-folder.test.ts": 176,
|
||||
"src/main/plugins/plugin-content-safety.test.ts": 3933,
|
||||
"src/main/plugins/plugin-declared-settings.test.ts": 218,
|
||||
@@ -4762,7 +4752,6 @@
|
||||
"src/main/plugins/workers/plugin-host-methods.test.ts": 277,
|
||||
"src/main/plugins/workers/plugin-host-panel-methods.test.ts": 304,
|
||||
"src/main/plugins/workers/plugin-host-process.test.ts": 327,
|
||||
"src/main/plugins/workers/plugin-host-runtime-hooks.test.ts": 230,
|
||||
"src/main/plugins/workers/plugin-host-runtime.test.ts": 274,
|
||||
"src/main/plugins/workers/plugin-host-status-bar-methods.test.ts": 270,
|
||||
"src/main/plugins/workers/plugin-host-workspace-methods.test.ts": 252,
|
||||
@@ -4884,7 +4873,6 @@
|
||||
"src/main/pty/process-retirement/pty-descendant-exit-verification.test.ts": 155,
|
||||
"src/main/pty/process-retirement/pty-descendant-termination-libproc.test.ts": 135,
|
||||
"src/main/pty/process-retirement/pty-descendant-termination.test.ts": 200,
|
||||
"src/main/pty/shell-startup/appimage-terminal-env.test.ts": 41,
|
||||
"src/main/pty/shell-startup/chromium-session-bus-env.test.ts": 171,
|
||||
"src/main/pty/shell-startup/codex-no-daemon-binary-contract.test.ts": 41,
|
||||
"src/main/pty/shell-startup/codex-no-daemon-launch-command.test.ts": 108,
|
||||
@@ -4906,7 +4894,6 @@
|
||||
"src/main/pty/process-retirement/pty-master-foreground-observation.test.ts": 120,
|
||||
"src/main/pty/shell-startup/shell-startup-env.test.ts": 116,
|
||||
"src/main/pty/environment/windows-environment-path.test.ts": 116,
|
||||
"src/main/pty/environment/windows-path-registry-reader.test.ts": 44,
|
||||
"src/main/pty/shell-startup/wsl-orca-env.test.ts": 104,
|
||||
"src/main/pty/shell-startup/pwsh.test.ts": 69,
|
||||
"src/main/qoder/hook-service.test.ts": 327,
|
||||
@@ -5983,7 +5970,6 @@
|
||||
"src/main/runtime/rpc/ws-transport-transient-packet-loss.test.ts": 86,
|
||||
"src/main/runtime/rpc/ws-transport.test.ts": 819,
|
||||
"src/main/runtime/run-terminal-shell-alone.node-pty.test.ts": 1867,
|
||||
"src/main/runtime/runtime-account-controller.test.ts": 141,
|
||||
"src/main/runtime/runtime-agent-orchestration-projection.test.ts": 6246,
|
||||
"src/main/runtime/runtime-agent-row-main-agent-projection.test.ts": 91,
|
||||
"src/main/runtime/runtime-agent-terminal-startup.test.ts": 480,
|
||||
@@ -7080,7 +7066,6 @@
|
||||
"src/main/wsl/execution/wsl-guest-pane-process-script-cost.test.ts": 213,
|
||||
"src/main/wsl/execution/wsl-guest-pane-processes.test.ts": 48,
|
||||
"src/main/wsl/execution/wsl-guest-process-identity.test.ts": 54,
|
||||
"src/main/wsl/execution/wsl-guest-process-table.test.ts": 146,
|
||||
"src/main/wsl/host-lifecycle/wsl-host-automation-wake.test.ts": 288,
|
||||
"src/main/wsl/host-lifecycle/wsl-host-connector.test.ts": 826,
|
||||
"src/main/wsl/host-lifecycle/wsl-host-gate-startup.test.ts": 540,
|
||||
|
||||
@@ -19,7 +19,6 @@ const NODE_RUNTIME_INCLUDE = [
|
||||
'src/main/ai-vault/session-scanner-unlimited-dedup.test.ts',
|
||||
// The parse cache persists through node:sqlite.
|
||||
'src/main/ai-vault/parse-cache/session-parse-cache-antigravity-upgrade.test.ts',
|
||||
'src/main/ai-vault/parse-cache/session-parse-cache-devin-upgrade.test.ts',
|
||||
'src/main/ai-vault/parse-cache/session-parse-cache-large-scan.test.ts',
|
||||
'src/main/ai-vault/parse-cache/session-parse-cache-metadata-checkpoint.test.ts',
|
||||
'src/main/ai-vault/parse-cache/session-parse-cache-persistence.test.ts',
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import { createAcpStructuredLaunchResolver } from '../runtime/structured-agent-launch-composition.test-support'
|
||||
import { expect, it, vi } from 'vitest'
|
||||
import { agentSessionRecordFixture } from '../../shared/agent-session/lifecycle/agent-session-record.test-fixture'
|
||||
import { ACP_LAUNCH_SPECS } from './acp-launch-specs'
|
||||
import { openCodeAcpAccountBinding } from '../opencode/opencode-structured-account-home'
|
||||
|
||||
it.each(ACP_LAUNCH_SPECS)(
|
||||
'$agent starts a new session without probing pre-clear history',
|
||||
async (spec) => {
|
||||
const base = agentSessionRecordFixture()
|
||||
const record = {
|
||||
...base,
|
||||
provider: spec.agent,
|
||||
accountHome:
|
||||
'accountLocatorKind' in spec.account.pin
|
||||
? { kind: 'opencode' as const, locator: { kind: 'unmanaged' as const } }
|
||||
: { variable: spec.account.pin.accountHomeVariable, path: '/fixture/account' },
|
||||
providerContextBoundary: { operationId: 'clear', afterFence: 2, clearedAt: 100 },
|
||||
providerHandleChain: []
|
||||
}
|
||||
const account =
|
||||
spec.agent === 'opencode'
|
||||
? openCodeAcpAccountBinding(() => ({
|
||||
list: () => {
|
||||
throw new Error('launch must not change account selection')
|
||||
},
|
||||
restoreOriginalEnvironment: () => {},
|
||||
environmentForAccount: () => {
|
||||
throw new Error('unmanaged fixture must stay unmanaged')
|
||||
}
|
||||
}))
|
||||
: spec.account
|
||||
const readJournal = vi.fn(() => null)
|
||||
const resolve = createAcpStructuredLaunchResolver(
|
||||
{ ...spec, account },
|
||||
{
|
||||
store: { getRecord: () => record, pinLaunchDirectory: vi.fn() },
|
||||
readJournal,
|
||||
resolveWorkspacePath: async () => '/repo/worktree',
|
||||
resolveEnvironment: async () => ({ PATH: '/usr/bin', HOME: '/home/test' }),
|
||||
resolveCommand: () => `/fixture/${spec.command}`,
|
||||
probeVersion: async () => ({ version: null }),
|
||||
inheritedEnv: {}
|
||||
}
|
||||
)
|
||||
const launch = await resolve({
|
||||
identity: {
|
||||
sessionId: record.sessionId,
|
||||
workspaceId: record.location.workspaceId,
|
||||
hostId: record.location.executionHostId,
|
||||
agent: spec.agent,
|
||||
providerHandle: null
|
||||
}
|
||||
})
|
||||
expect(launch.resume).toBeNull()
|
||||
expect(readJournal).not.toHaveBeenCalled()
|
||||
expect(launch.args).toEqual(spec.args({ fullAccess: false, pluginDir: null }))
|
||||
}
|
||||
)
|
||||
@@ -1,71 +0,0 @@
|
||||
import { mkdtemp, rm, stat, writeFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { afterEach, expect, it } from 'vitest'
|
||||
import {
|
||||
initSessionParseCachePersistence,
|
||||
resetSessionParseCachePersistenceForTests
|
||||
} from './session-parse-cache-persistence'
|
||||
import { createSessionParseStats, parseAgentSessionFileCached } from './session-scanner-parse-cache'
|
||||
import {
|
||||
resetSessionParseCacheForTests,
|
||||
getSessionParseCacheEntry
|
||||
} from './session-parse-cache-store'
|
||||
|
||||
import type { SessionFileCandidate } from '../session-scanner-types'
|
||||
|
||||
let root: string | undefined
|
||||
|
||||
afterEach(async () => {
|
||||
resetSessionParseCacheForTests()
|
||||
resetSessionParseCachePersistenceForTests()
|
||||
if (root) {
|
||||
await rm(root, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('reparses unchanged ATIF transcripts cached before Devin source fields were supported', async () => {
|
||||
root = await mkdtemp(join(tmpdir(), 'orca-devin-upgrade-'))
|
||||
const path = join(root, 'devin-session.json')
|
||||
await writeFile(
|
||||
path,
|
||||
JSON.stringify({
|
||||
session_id: 'devin-session',
|
||||
steps: [{ source: 'user', message: 'Find the missing sessions' }]
|
||||
})
|
||||
)
|
||||
const fileStat = await stat(path)
|
||||
const candidate: SessionFileCandidate = {
|
||||
agent: 'devin',
|
||||
codexHome: null,
|
||||
file: {
|
||||
path,
|
||||
mtimeMs: fileStat.mtimeMs,
|
||||
modifiedAt: fileStat.mtime.toISOString(),
|
||||
sizeBytes: fileStat.size,
|
||||
sidecar: 'none'
|
||||
}
|
||||
}
|
||||
await parseAgentSessionFileCached(candidate, process.platform)
|
||||
const { resume: _resume, foldSession: _fold, ...entry } = getSessionParseCacheEntry(path)!
|
||||
const staleSession = entry.session && {
|
||||
...entry.session,
|
||||
title: 'Devin session devin-session',
|
||||
messageCount: 0,
|
||||
previewMessages: [],
|
||||
firstUserPrompt: null,
|
||||
lastUserPrompt: null
|
||||
}
|
||||
const entries = [[path, { ...entry, session: staleSession }]]
|
||||
const cacheFile = join(root, 'cache.json')
|
||||
await writeFile(cacheFile, JSON.stringify({ schemaVersion: 2, appVersion: 'old', entries }))
|
||||
resetSessionParseCacheForTests()
|
||||
initSessionParseCachePersistence({ filePath: cacheFile, appVersion: 'new' })
|
||||
|
||||
const stats = createSessionParseStats()
|
||||
const session = await parseAgentSessionFileCached(candidate, process.platform, stats)
|
||||
expect(stats.fullParses).toBe(1)
|
||||
expect(stats.reused).toBe(0)
|
||||
expect(session?.messageCount).toBe(1)
|
||||
expect(session?.title).toBe('Find the missing sessions')
|
||||
})
|
||||
@@ -1,83 +0,0 @@
|
||||
import { join } from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
resolveAiVaultServiceEntryPath,
|
||||
resolveAiVaultServiceEntryPathWithoutApp
|
||||
} from './session-scanner-service-entry-path'
|
||||
|
||||
describe('resolveAiVaultServiceEntryPath', () => {
|
||||
it('uses the adjacent electron-vite output when present', () => {
|
||||
const outMain = join(process.cwd(), 'out', 'main')
|
||||
const adjacent = join(outMain, 'session-scanner-service-entry.js')
|
||||
|
||||
expect(resolveAiVaultServiceEntryPath(outMain, false, (path) => path === adjacent)).toBe(
|
||||
adjacent
|
||||
)
|
||||
})
|
||||
|
||||
it('uses the nested output from a project root', () => {
|
||||
expect(resolveAiVaultServiceEntryPath(process.cwd(), false, () => false)).toBe(
|
||||
join(process.cwd(), 'out', 'main', 'session-scanner-service-entry.js')
|
||||
)
|
||||
})
|
||||
|
||||
it('uses app.asar.unpacked for packaged Electron', () => {
|
||||
const appPath = join('C:', 'Orca', 'resources', 'app.asar')
|
||||
|
||||
expect(resolveAiVaultServiceEntryPath(appPath, true)).toBe(
|
||||
join(
|
||||
'C:',
|
||||
'Orca',
|
||||
'resources',
|
||||
'app.asar.unpacked',
|
||||
'out',
|
||||
'main',
|
||||
'session-scanner-service-entry.js'
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
it('uses the entry beside the bundle on a packaged host without an asar', () => {
|
||||
const orcadRoot = join('/opt', 'orcad', '1.0.0')
|
||||
const adjacent = join(orcadRoot, 'session-scanner-service-entry.js')
|
||||
|
||||
expect(resolveAiVaultServiceEntryPath(orcadRoot, true, (path) => path === adjacent)).toBe(
|
||||
adjacent
|
||||
)
|
||||
})
|
||||
|
||||
it('never forks an adjacent decoy from inside app.asar', () => {
|
||||
const appPath = join('/opt', 'Orca', 'resources', 'app.asar')
|
||||
|
||||
expect(resolveAiVaultServiceEntryPath(appPath, true, () => true)).toBe(
|
||||
join(
|
||||
'/opt',
|
||||
'Orca',
|
||||
'resources',
|
||||
'app.asar.unpacked',
|
||||
'out',
|
||||
'main',
|
||||
'session-scanner-service-entry.js'
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
it('uses resourcesPath from packaged Electron-as-Node runtimes', () => {
|
||||
const resourcesPath = join('Applications', 'Orca.app', 'Contents', 'Resources')
|
||||
const entry = join(
|
||||
resourcesPath,
|
||||
'app.asar.unpacked',
|
||||
'out',
|
||||
'main',
|
||||
'session-scanner-service-entry.js'
|
||||
)
|
||||
|
||||
expect(
|
||||
resolveAiVaultServiceEntryPathWithoutApp(
|
||||
'/unrelated/cwd',
|
||||
resourcesPath,
|
||||
(path) => path === entry
|
||||
)
|
||||
).toBe(entry)
|
||||
})
|
||||
})
|
||||
@@ -1,60 +0,0 @@
|
||||
import { homedir, tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { mkdtemp, rm, stat, writeFile } from 'node:fs/promises'
|
||||
import { expect, it } from 'vitest'
|
||||
import { scanAiVaultSessions } from '../../session-scanner'
|
||||
import { isolatedScanRoots } from '../../session-scanner-test-fixtures'
|
||||
import { antigravitySessionOrigin } from '../../../../shared/antigravity-session-origin'
|
||||
|
||||
it.skipIf(process.env.ORCA_REAL_ANTIGRAVITY_HISTORY !== '1')(
|
||||
'scans bounded existing official artifacts without publishing their content',
|
||||
async () => {
|
||||
const sandbox = await mkdtemp(join(tmpdir(), 'orca-agy-recorded-'))
|
||||
try {
|
||||
const home = homedir()
|
||||
const rootPresence = await Promise.all(
|
||||
['antigravity-cli', 'antigravity-ide', 'antigravity'].map(async (origin) => {
|
||||
const present = await stat(join(home, '.gemini', origin, 'brain')).then(
|
||||
(value) => value.isDirectory(),
|
||||
() => false
|
||||
)
|
||||
return { origin, present }
|
||||
})
|
||||
)
|
||||
const result = await scanAiVaultSessions({
|
||||
...isolatedScanRoots(sandbox),
|
||||
antigravityBrainDir: join(home, '.gemini', 'antigravity-cli', 'brain'),
|
||||
antigravityAppHome: home,
|
||||
includeAntigravityIdeSessions: true,
|
||||
limit: 5,
|
||||
limitPerAgent: 10
|
||||
})
|
||||
expect(result.issues.length).toBe(0)
|
||||
expect(result.sessions.length).toBeGreaterThan(0)
|
||||
expect(result.sessions.length).toBeLessThanOrEqual(5)
|
||||
expect(result.sessions.some((session) => session.messageCount > 0)).toBe(true)
|
||||
const proof = {
|
||||
provenance:
|
||||
'Existing official recorded artifacts through production scanner; no live IDE/model claim',
|
||||
rootPresence,
|
||||
sessionCount: result.sessions.length,
|
||||
issueCount: result.issues.length,
|
||||
origins: result.sessions.map((session) => ({
|
||||
origin: antigravitySessionOrigin(session.filePath),
|
||||
fullTranscript: session.filePath.endsWith('transcript_full.jsonl'),
|
||||
messageCount: session.messageCount,
|
||||
hasWorkspace: session.cwd !== null,
|
||||
cliResume: session.resumeCommand.includes('--conversation')
|
||||
}))
|
||||
}
|
||||
if (process.env.ORCA_ANTIGRAVITY_HISTORY_PROOF_PATH) {
|
||||
await writeFile(
|
||||
process.env.ORCA_ANTIGRAVITY_HISTORY_PROOF_PATH,
|
||||
`${JSON.stringify(proof, null, 2)}\n`
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
await rm(sandbox, { recursive: true, force: true })
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -1,62 +0,0 @@
|
||||
// A low-spec Windows machine timed out the first user-PATH write at 5 s; a retry then succeeded.
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { ProcessResult } from '@orca/process-host/process-spec'
|
||||
|
||||
const { runProcessMock } = vi.hoisted(() => ({ runProcessMock: vi.fn() }))
|
||||
|
||||
vi.mock('@orca/process-host', () => ({ runProcess: runProcessMock }))
|
||||
|
||||
import { writeWindowsUserPath } from './cli-privileged-processes'
|
||||
import { WINDOWS_PATH_WRITE_TIMEOUT_MS } from './cli-install-constants'
|
||||
|
||||
function result(overrides: Partial<ProcessResult> = {}): ProcessResult {
|
||||
return { code: 0, signal: null, stdout: '', stderr: '', timedOut: false, ...overrides }
|
||||
}
|
||||
|
||||
const TIMED_OUT = result({ code: null, timedOut: true })
|
||||
|
||||
beforeEach(() => {
|
||||
runProcessMock.mockReset()
|
||||
})
|
||||
|
||||
describe('writeWindowsUserPath', () => {
|
||||
it('allows a slow PowerShell well past the old 5 s deadline', async () => {
|
||||
runProcessMock.mockResolvedValue(result())
|
||||
|
||||
await writeWindowsUserPath('C:\\Users\\me\\bin')
|
||||
|
||||
expect(WINDOWS_PATH_WRITE_TIMEOUT_MS).toBeGreaterThanOrEqual(15_000)
|
||||
expect(runProcessMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ program: 'powershell', timeoutMs: WINDOWS_PATH_WRITE_TIMEOUT_MS })
|
||||
)
|
||||
})
|
||||
|
||||
it('retries once after a timeout and succeeds', async () => {
|
||||
runProcessMock.mockResolvedValueOnce(TIMED_OUT).mockResolvedValueOnce(result())
|
||||
|
||||
await expect(writeWindowsUserPath('C:\\Users\\me\\bin')).resolves.toBeUndefined()
|
||||
|
||||
expect(runProcessMock).toHaveBeenCalledTimes(2)
|
||||
expect(runProcessMock.mock.calls[1]).toEqual(runProcessMock.mock.calls[0])
|
||||
})
|
||||
|
||||
it('gives up after a second timeout', async () => {
|
||||
runProcessMock.mockResolvedValue(TIMED_OUT)
|
||||
|
||||
await expect(writeWindowsUserPath('C:\\Users\\me\\bin')).rejects.toThrow(
|
||||
`Windows PATH command timed out after ${WINDOWS_PATH_WRITE_TIMEOUT_MS}ms.`
|
||||
)
|
||||
expect(runProcessMock).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('does not retry a PowerShell failure', async () => {
|
||||
runProcessMock.mockResolvedValue(
|
||||
result({ code: 1, stderr: 'Access to the registry is denied.' })
|
||||
)
|
||||
|
||||
await expect(writeWindowsUserPath('C:\\Users\\me\\bin')).rejects.toThrow(
|
||||
'Access to the registry is denied.'
|
||||
)
|
||||
expect(runProcessMock).toHaveBeenCalledOnce()
|
||||
})
|
||||
})
|
||||
@@ -1,56 +0,0 @@
|
||||
import { resolve } from 'node:path'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { Store } from '../../persistence/loading-store/store'
|
||||
import type { Repo } from '../../../shared/repo-types'
|
||||
|
||||
const mocks = vi.hoisted(() => ({ graph: vi.fn(), gitOptions: vi.fn() }))
|
||||
vi.mock('../../workspaces/catalog/repo-worktrees', () => ({ listRepoWorktreeGraph: mocks.graph }))
|
||||
vi.mock('../../workspaces/execution-host/project-runtime-git-options', () => ({
|
||||
getLocalProjectWorktreeGitOptions: mocks.gitOptions
|
||||
}))
|
||||
vi.mock('../worktrees/removal/registered-worktree-roots-cache', () => ({
|
||||
resolveRegisteredWorktreePath: async (path: string) => path
|
||||
}))
|
||||
vi.mock('./filesystem-auth', () => ({ resolveAuthorizedPath: vi.fn() }))
|
||||
import { getRepoForSourceControlAi } from './filesystem-source-control-ai-targets'
|
||||
|
||||
const repo: Repo = {
|
||||
id: 'wsl-drive-repo',
|
||||
path: resolve('/wsl-drive/repo'),
|
||||
displayName: 'repo',
|
||||
badgeColor: '#000',
|
||||
addedAt: 0
|
||||
}
|
||||
const worktree = resolve('/wsl-drive/workspaces/feature-extra')
|
||||
|
||||
function fixture(): Store {
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the ownership check reads only these store methods.
|
||||
return { getRepo: () => repo, getAllWorktreeMeta: () => ({}) } as unknown as Store
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks()
|
||||
mocks.gitOptions.mockReturnValue({ wslDistro: 'Ubuntu-24.04' })
|
||||
// Only WSL Git names the checkout the way the renderer does; host Git reads its `/mnt/c` metadata.
|
||||
mocks.graph.mockImplementation(async (_repo: Repo, options?: { wslDistro?: string }) => [
|
||||
{ path: options?.wslDistro ? worktree : resolve('/mnt/c/wsl-drive/workspaces/feature-extra') }
|
||||
])
|
||||
})
|
||||
|
||||
describe('source control AI repo ownership for a detected worktree', () => {
|
||||
it('lists through the project runtime, so a WSL-runtime drive worktree keeps its repo', async () => {
|
||||
await expect(
|
||||
getRepoForSourceControlAi(fixture(), { repoId: repo.id, worktreePath: worktree })
|
||||
).resolves.toBe(repo)
|
||||
expect(mocks.graph).toHaveBeenCalledWith(repo, { wslDistro: 'Ubuntu-24.04' })
|
||||
})
|
||||
|
||||
it('does not claim a worktree the repo listing does not name', async () => {
|
||||
await expect(
|
||||
getRepoForSourceControlAi(fixture(), {
|
||||
repoId: repo.id,
|
||||
worktreePath: resolve('/wsl-drive/elsewhere')
|
||||
})
|
||||
).resolves.toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -45,10 +45,4 @@ describe('provider timeline identity spelling', () => {
|
||||
agentJournalItemKey(request('gen-1', 1))
|
||||
)
|
||||
})
|
||||
|
||||
it('keeps turn rows on the record prefix every lane writes', () => {
|
||||
const turn = { namespace: 'ns', key: provider('t1') }
|
||||
expect(scheme.turn(turn)).toMatchObject({ recordId: 'turn-lifecycle:p:ns:t1' })
|
||||
expect(scheme.turnId(turn)).toBe('p:ns:t1')
|
||||
})
|
||||
})
|
||||
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import type { AgentSessionRecord } from '../../../shared/agent-session/lifecycle/agent-session-record'
|
||||
import type { AgentSessionResumeMarker } from '../../../shared/agent-session/lifecycle/agent-session-resume-marker'
|
||||
import { createStructuredAgentSessionRestartWitnesses } from './structured-agent-session-restart-witnesses'
|
||||
import { marker } from './structured-agent-session-restart-resume-test-harness'
|
||||
|
||||
// Mock every stopped session as working to isolate witness-clear scope.
|
||||
vi.mock('./structured-agent-session-working-at-teardown', () => ({
|
||||
structuredAgentSessionWorkingAtStop: (input: { sessionId: string; now: number }) =>
|
||||
marker({ sessionId: input.sessionId, recordedAt: input.now })
|
||||
}))
|
||||
|
||||
const AGENTS: Record<string, string> = { 'codex-chat': 'codex', 'grok-chat': 'grok' }
|
||||
|
||||
function witnessed() {
|
||||
const recorded: AgentSessionResumeMarker[][] = []
|
||||
const witnesses = createStructuredAgentSessionRestartWitnesses({
|
||||
sessions: new Map(),
|
||||
getRecord: (sessionId) =>
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: clear reads only the record's provider.
|
||||
AGENTS[sessionId] ? ({ provider: AGENTS[sessionId] } as AgentSessionRecord) : null,
|
||||
childWorkOpen: () => false,
|
||||
childWork: () => undefined,
|
||||
capsule: { record: async (markers) => void recorded.push([...markers]) },
|
||||
teardownId: 'teardown',
|
||||
now: () => 1,
|
||||
enqueue: (operation) => operation()
|
||||
})
|
||||
witnesses.begin('quit')
|
||||
for (const sessionId of ['codex-chat', 'grok-chat', 'unreadable-chat']) {
|
||||
witnesses.beforeStop(sessionId)
|
||||
witnesses.stopped(sessionId)
|
||||
}
|
||||
const written = async () => {
|
||||
await witnesses.record()
|
||||
return (recorded.at(-1) ?? []).map((each) => each.sessionId)
|
||||
}
|
||||
return { witnesses, written }
|
||||
}
|
||||
|
||||
describe('restart witnesses', () => {
|
||||
it('drops every unwritten witness on an unscoped clear', async () => {
|
||||
const { witnesses, written } = witnessed()
|
||||
witnesses.clear()
|
||||
expect(await written()).toEqual([])
|
||||
})
|
||||
|
||||
it('drops only the witnesses of agents the audience sees on a scoped clear', async () => {
|
||||
const { witnesses, written } = witnessed()
|
||||
witnesses.clear((agent) => agent !== 'grok')
|
||||
expect(await written()).toEqual(['grok-chat', 'unreadable-chat'])
|
||||
})
|
||||
})
|
||||
@@ -2,34 +2,6 @@ import { describe, expect, it } from 'vitest'
|
||||
import { structuredAgentConfiguredArgs } from './structured-agent-configured-args'
|
||||
|
||||
describe('structured chat configured Arguments', () => {
|
||||
it.each(['claude', 'codex', 'opencode', 'omp', 'grok'] as const)(
|
||||
'reads the existing %s setting and preserves quoted values',
|
||||
(agent) => {
|
||||
expect(
|
||||
structuredAgentConfiguredArgs(
|
||||
agent,
|
||||
{
|
||||
agentDefaultArgs: { [agent]: '--model "model with spaces"' }
|
||||
},
|
||||
'darwin'
|
||||
)
|
||||
).toEqual(['--model', 'model with spaces'])
|
||||
expect(structuredAgentConfiguredArgs(agent, { agentDefaultArgs: { [agent]: '' } })).toEqual(
|
||||
[]
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
it('retains original configured raw until the actual native typed replacement is known', () => {
|
||||
expect(
|
||||
structuredAgentConfiguredArgs('claude', {
|
||||
agentPermissionDefault: 'own',
|
||||
agentLaunchChoices: { claude: { model: 'fresh-only', options: { effort: 'high' } } },
|
||||
agentDefaultArgs: { claude: '--model literal --effort low --add-dir docs' }
|
||||
})
|
||||
).toEqual(['--model', 'literal', '--effort', 'low', '--add-dir', 'docs'])
|
||||
})
|
||||
|
||||
it('preserves Windows paths under the configured shell', () => {
|
||||
expect(
|
||||
structuredAgentConfiguredArgs(
|
||||
@@ -51,14 +23,3 @@ describe('structured chat configured Arguments', () => {
|
||||
).toThrow('Arguments are invalid')
|
||||
})
|
||||
})
|
||||
|
||||
describe('future structured provider argument lookup', () => {
|
||||
it('keeps configured arguments and empty fallback for an unregistered provider', () => {
|
||||
const agentDefaultArgs = { claude: '', 'future-provider': '--label "future value"' }
|
||||
expect(structuredAgentConfiguredArgs('future-provider', { agentDefaultArgs })).toEqual([
|
||||
'--label',
|
||||
'future value'
|
||||
])
|
||||
expect(structuredAgentConfiguredArgs('future-provider', {})).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
normalizeNotificationSettings,
|
||||
persistedNotificationSettingsRepaired
|
||||
} from './onboarding-normalization'
|
||||
import { getDefaultNotificationSettings } from '../../../shared/notification-settings-defaults'
|
||||
|
||||
describe('muted notification machines', () => {
|
||||
it('keeps valid machine ids once and drops anything else', () => {
|
||||
const normalized = normalizeNotificationSettings({
|
||||
mutedNotificationSourceIds: [
|
||||
'runtime:m4air',
|
||||
'ssh:openclaw',
|
||||
'runtime:m4air',
|
||||
'nope',
|
||||
42,
|
||||
'local'
|
||||
]
|
||||
})
|
||||
expect(normalized.mutedNotificationSourceIds).toEqual([
|
||||
'runtime:m4air',
|
||||
'ssh:openclaw',
|
||||
'local'
|
||||
])
|
||||
})
|
||||
|
||||
it('defaults a missing or malformed list to no muted machines', () => {
|
||||
expect(normalizeNotificationSettings({}).mutedNotificationSourceIds).toEqual([])
|
||||
expect(
|
||||
normalizeNotificationSettings({ mutedNotificationSourceIds: 'runtime:m4air' })
|
||||
.mutedNotificationSourceIds
|
||||
).toEqual([])
|
||||
})
|
||||
|
||||
it('does not count an unchanged list as a repair', () => {
|
||||
const persisted = {
|
||||
...getDefaultNotificationSettings(),
|
||||
mutedNotificationSourceIds: ['runtime:m4air']
|
||||
}
|
||||
const normalized = normalizeNotificationSettings(persisted)
|
||||
expect(persistedNotificationSettingsRepaired(persisted, normalized)).toBe(false)
|
||||
})
|
||||
|
||||
it('counts a dropped entry as a repair', () => {
|
||||
const persisted = { ...getDefaultNotificationSettings(), mutedNotificationSourceIds: ['nope'] }
|
||||
const normalized = normalizeNotificationSettings(persisted)
|
||||
expect(persistedNotificationSettingsRepaired(persisted, normalized)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('agent needs-input notifications', () => {
|
||||
it('inherits the old shared toggle on upgrade, then stands on its own', () => {
|
||||
const { agentNeedsInput: _absentBeforeUpgrade, ...upgraded } = {
|
||||
...getDefaultNotificationSettings(),
|
||||
agentTaskComplete: false
|
||||
}
|
||||
expect(normalizeNotificationSettings(upgraded).agentNeedsInput).toBe(false)
|
||||
expect(normalizeNotificationSettings({}).agentNeedsInput).toBe(true)
|
||||
expect(
|
||||
normalizeNotificationSettings({ agentTaskComplete: false, agentNeedsInput: true })
|
||||
.agentNeedsInput
|
||||
).toBe(true)
|
||||
})
|
||||
})
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { getDefaultWorkspaceSession } from '../../../shared/constants'
|
||||
import { toSshExecutionHostId } from '../../../shared/execution-host'
|
||||
import type { SleepingAgentSessionRecord } from '../../../shared/agent-session/lifecycle/agent-session-resume'
|
||||
import { withoutLeftBehindSessionRows } from './orcad-source-left-behind-session-rows'
|
||||
import type { OrcadMigrationSourceScope } from './orcad-source-scope'
|
||||
|
||||
const WORKTREE = 'repo-0::/home/dev/src/project-0'
|
||||
const SCOPE: OrcadMigrationSourceScope = {
|
||||
kind: 'ssh-relay',
|
||||
targetId: 'ssh-box',
|
||||
targetGeneration: 1,
|
||||
hostId: toSshExecutionHostId('ssh-box'),
|
||||
repoIds: new Set(['repo-0']),
|
||||
folderWorkspaceKeys: new Set(),
|
||||
sharedRepoIds: new Set(),
|
||||
partitionHostId: toSshExecutionHostId('ssh-box')
|
||||
}
|
||||
|
||||
function resume(paneKey: string, tabId: string): SleepingAgentSessionRecord {
|
||||
return {
|
||||
paneKey,
|
||||
tabId,
|
||||
worktreeId: WORKTREE,
|
||||
agent: 'codex',
|
||||
providerSession: { key: 'session_id', id: `session-${tabId}` },
|
||||
prompt: 'Saved prompt',
|
||||
state: 'done',
|
||||
capturedAt: 1,
|
||||
updatedAt: 1
|
||||
}
|
||||
}
|
||||
|
||||
describe('withoutLeftBehindSessionRows', () => {
|
||||
it('drops pane-keyed records of a skipped foreign tab, legacy keys included', () => {
|
||||
const session = getDefaultWorkspaceSession()
|
||||
session.unifiedTabs = {
|
||||
[WORKTREE]: ['own', 'foreign'].map((id, sortOrder) => ({
|
||||
id,
|
||||
entityId: id,
|
||||
groupId: 'group',
|
||||
worktreeId: WORKTREE,
|
||||
contentType: 'terminal' as const,
|
||||
label: id,
|
||||
customLabel: null,
|
||||
color: null,
|
||||
sortOrder,
|
||||
createdAt: 1,
|
||||
...(id === 'foreign' ? { executionHostId: 'runtime:env-other' } : {})
|
||||
}))
|
||||
}
|
||||
session.tabGroups = {
|
||||
[WORKTREE]: [
|
||||
{ id: 'group', worktreeId: WORKTREE, activeTabId: 'own', tabOrder: ['own', 'foreign'] }
|
||||
]
|
||||
}
|
||||
session.sleepingAgentSessionsByPaneKey = {
|
||||
'own:0': resume('own:0', 'own'),
|
||||
'foreign:0': resume('foreign:0', 'foreign')
|
||||
}
|
||||
session.terminalPtyIncarnationsByPaneKey = { 'own:0': 'inc-own', 'foreign:0': 'inc-foreign' }
|
||||
const exported = withoutLeftBehindSessionRows(session, SCOPE, true)
|
||||
expect(Object.keys(exported.sleepingAgentSessionsByPaneKey ?? {})).toEqual(['own:0'])
|
||||
expect(Object.keys(exported.terminalPtyIncarnationsByPaneKey ?? {})).toEqual(['own:0'])
|
||||
expect(exported.tabGroups?.[WORKTREE]?.[0]?.tabOrder).toEqual(['own'])
|
||||
})
|
||||
})
|
||||
@@ -1,76 +0,0 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import type { PluginHostListEntry } from '../../shared/plugins/plugin-list-entry'
|
||||
import type { PluginLockfile } from '../../shared/plugins/installation/plugin-install-lockfile'
|
||||
import { markCommandReplacementOwners } from './plugin-command-replacement-owners'
|
||||
|
||||
function entry(
|
||||
pluginKey: string,
|
||||
status: PluginHostListEntry['status'] = 'idle'
|
||||
): PluginHostListEntry {
|
||||
return {
|
||||
pluginKey,
|
||||
consentFingerprint: null,
|
||||
name: pluginKey,
|
||||
version: '1.0.0',
|
||||
publisher: 'acme',
|
||||
status,
|
||||
needsReconsent: false,
|
||||
isDev: false,
|
||||
official: false,
|
||||
bundled: false,
|
||||
capabilities: [],
|
||||
panels: [],
|
||||
commands: [
|
||||
{
|
||||
id: 'tasks',
|
||||
title: 'Tasks',
|
||||
context: 'global',
|
||||
handler: { type: 'worker' },
|
||||
keybindings: [],
|
||||
replaces: 'view.tasks'
|
||||
}
|
||||
],
|
||||
hasWorker: true,
|
||||
restarts: 0
|
||||
}
|
||||
}
|
||||
|
||||
function lock(times: Record<string, number>): Pick<PluginLockfile, 'plugins'> {
|
||||
const plugins = Object.fromEntries(
|
||||
Object.entries(times).map(([key, installedAt]) => [key, { installedAt }])
|
||||
)
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the owners read only installedAt.
|
||||
return { plugins } as unknown as Pick<PluginLockfile, 'plugins'>
|
||||
}
|
||||
|
||||
describe('who replaces a built-in action', () => {
|
||||
it('lets the plugin installed first replace it, marks and logs the other once', () => {
|
||||
const log = vi.fn()
|
||||
const entries = [entry('acme.later'), entry('acme.first'), entry('acme.dev')]
|
||||
const marked = markCommandReplacementOwners(
|
||||
entries,
|
||||
lock({ 'acme.first': 1, 'acme.later': 2 }),
|
||||
log
|
||||
)
|
||||
expect(marked.map((each) => each.commands[0]?.replacedBy)).toEqual([
|
||||
'acme.first',
|
||||
undefined,
|
||||
'acme.first'
|
||||
])
|
||||
expect(log).toHaveBeenCalledWith(
|
||||
'acme.later',
|
||||
'command tasks does not replace view.tasks: acme.first was installed first and replaces it'
|
||||
)
|
||||
markCommandReplacementOwners(entries, lock({ 'acme.first': 1, 'acme.later': 2 }), log)
|
||||
expect(log).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('ignores plugins that are off', () => {
|
||||
const marked = markCommandReplacementOwners(
|
||||
[entry('acme.first', 'disabled'), entry('acme.later')],
|
||||
lock({ 'acme.first': 1, 'acme.later': 2 }),
|
||||
vi.fn()
|
||||
)
|
||||
expect(marked[1]?.commands[0]?.replacedBy).toBeUndefined()
|
||||
})
|
||||
})
|
||||
@@ -1,74 +0,0 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { createPluginWorkerRuntime, type PluginWorkerOrcaApi } from './plugin-host-runtime'
|
||||
|
||||
async function activated(activate: (orca: PluginWorkerOrcaApi) => void) {
|
||||
const send = vi.fn()
|
||||
const runtime = createPluginWorkerRuntime({
|
||||
send,
|
||||
exit: vi.fn(),
|
||||
importModule: async () => ({ default: activate })
|
||||
})
|
||||
await runtime.handleMessage({
|
||||
orca: 1,
|
||||
kind: 'evt',
|
||||
event: 'init',
|
||||
payload: {
|
||||
pluginId: 'samples.demo',
|
||||
pluginRoot: '/plugin',
|
||||
mainEntry: 'worker.js',
|
||||
grantedCapabilities: []
|
||||
}
|
||||
})
|
||||
const request = async (id: number, params: unknown) => {
|
||||
await runtime.handleMessage({ orca: 1, kind: 'req', id, method: 'hook.run', params })
|
||||
return send.mock.calls.map(([message]) => message).find((message) => message.id === id)
|
||||
}
|
||||
return { request }
|
||||
}
|
||||
|
||||
const event = { terminalId: 't', text: 'rm -rf /', enter: true, inputKind: 'driving' }
|
||||
|
||||
describe('worker hook.run', () => {
|
||||
it('answers with the decision of the handlers registered for the hook', async () => {
|
||||
const { request } = await activated((orca) => {
|
||||
orca.hooks.on('terminal.send', () => ({ deny: 'blocked' }))
|
||||
})
|
||||
expect(await request(1, { event: 'terminal.send', payload: event })).toMatchObject({
|
||||
kind: 'res',
|
||||
result: { decision: 'result', result: { deny: 'blocked' } }
|
||||
})
|
||||
})
|
||||
|
||||
it('passes when no handler is registered or after dispose', async () => {
|
||||
const { request } = await activated((orca) => {
|
||||
orca.hooks.on('terminal.send', () => ({ deny: 'blocked' })).dispose()
|
||||
})
|
||||
expect(await request(2, { event: 'terminal.send', payload: event })).toMatchObject({
|
||||
result: { decision: 'continue', payload: event }
|
||||
})
|
||||
})
|
||||
|
||||
it('reports a throwing handler as a failure the host treats as passing', async () => {
|
||||
const { request } = await activated((orca) => {
|
||||
orca.hooks.on('terminal.send', () => {
|
||||
throw new Error('boom')
|
||||
})
|
||||
})
|
||||
expect(await request(3, { event: 'terminal.send', payload: event })).toMatchObject({
|
||||
error: { code: 'action_failed' }
|
||||
})
|
||||
})
|
||||
|
||||
it('refuses an unknown hook at registration', async () => {
|
||||
let failure: unknown
|
||||
await activated((orca) => {
|
||||
try {
|
||||
// @ts-expect-error not a hook
|
||||
orca.hooks.on('terminal.write', () => undefined)
|
||||
} catch (error) {
|
||||
failure = error
|
||||
}
|
||||
})
|
||||
expect(String(failure)).toMatch(/unknown hook terminal\.write/)
|
||||
})
|
||||
})
|
||||
@@ -1,79 +0,0 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
__setWindowsPathRegistryLoaderForTests,
|
||||
readWindowsPathRegistry
|
||||
} from './windows-path-registry-reader'
|
||||
|
||||
describe('readWindowsPathRegistry', () => {
|
||||
afterEach(() => __setWindowsPathRegistryLoaderForTests())
|
||||
|
||||
it('reads machine and user PATH values without creating a process', () => {
|
||||
const getRegistryKey = vi
|
||||
.fn()
|
||||
.mockReturnValueOnce({ PATH: { type: 2, value: '%SystemRoot%\\System32' } })
|
||||
.mockReturnValueOnce({ Path: { type: 1, value: 'C:\\Users\\me\\bin' } })
|
||||
__setWindowsPathRegistryLoaderForTests(() => ({
|
||||
HK: { LM: 1, CU: 2 },
|
||||
getRegistryKey
|
||||
}))
|
||||
|
||||
expect(readWindowsPathRegistry()).toEqual([
|
||||
{ failed: false, value: '%SystemRoot%\\System32' },
|
||||
{ failed: false, value: 'C:\\Users\\me\\bin' }
|
||||
])
|
||||
expect(getRegistryKey).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
1,
|
||||
'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment'
|
||||
)
|
||||
expect(getRegistryKey).toHaveBeenNthCalledWith(2, 2, 'Environment')
|
||||
})
|
||||
|
||||
it('distinguishes an empty PATH from a failed registry read', () => {
|
||||
const getRegistryKey = vi
|
||||
.fn()
|
||||
.mockReturnValueOnce({ Path: { type: 1, value: '' } })
|
||||
.mockReturnValueOnce({ Path: { type: 4, value: 1 } })
|
||||
__setWindowsPathRegistryLoaderForTests(() => ({
|
||||
HK: { LM: 1, CU: 2 },
|
||||
getRegistryKey
|
||||
}))
|
||||
|
||||
expect(readWindowsPathRegistry()).toEqual([
|
||||
{ failed: false, value: '' },
|
||||
{ failed: true, value: null }
|
||||
])
|
||||
})
|
||||
|
||||
it('treats a missing PATH value in a readable hive as empty', () => {
|
||||
__setWindowsPathRegistryLoaderForTests(() => ({
|
||||
HK: { LM: 1, CU: 2 },
|
||||
getRegistryKey: vi.fn(() => ({}))
|
||||
}))
|
||||
|
||||
expect(readWindowsPathRegistry()).toEqual([
|
||||
{ failed: false, value: '' },
|
||||
{ failed: false, value: '' }
|
||||
])
|
||||
})
|
||||
|
||||
it('treats a present but unreadable PATH entry as a failed read', () => {
|
||||
__setWindowsPathRegistryLoaderForTests(() => ({
|
||||
HK: { LM: 1, CU: 2 },
|
||||
getRegistryKey: vi.fn(() => ({ Path: undefined }))
|
||||
}))
|
||||
|
||||
expect(readWindowsPathRegistry()).toEqual([
|
||||
{ failed: true, value: null },
|
||||
{ failed: true, value: null }
|
||||
])
|
||||
})
|
||||
|
||||
it('reports an unavailable native module apart from a failed read', () => {
|
||||
__setWindowsPathRegistryLoaderForTests(() => {
|
||||
throw new Error('native module unavailable')
|
||||
})
|
||||
|
||||
expect(readWindowsPathRegistry()).toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -1,69 +0,0 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { delimiter } from 'node:path'
|
||||
import { removeAppImageRuntimeEnv } from './appimage-terminal-env'
|
||||
|
||||
describe('removeAppImageRuntimeEnv', () => {
|
||||
const originalPlatform = Object.getOwnPropertyDescriptor(process, 'platform')
|
||||
|
||||
afterEach(() => {
|
||||
if (originalPlatform) {
|
||||
Object.defineProperty(process, 'platform', originalPlatform)
|
||||
}
|
||||
})
|
||||
|
||||
it('removes AppImage runtime identity and mount paths from Linux terminal env', () => {
|
||||
Object.defineProperty(process, 'platform', { configurable: true, value: 'linux' })
|
||||
|
||||
const env = {
|
||||
APPIMAGE: '/data/apps/orca.appimage',
|
||||
APPDIR: '/tmp/.mount_orca123',
|
||||
ARGV0: '/data/apps/orca.appimage',
|
||||
OWD: '/home/user',
|
||||
APPIMAGE_LIBRARY_PATH: '/tmp/.mount_orca123/usr/lib',
|
||||
PATH: ['/tmp/.mount_orca123', '/tmp/.mount_orca123/usr/sbin', '/usr/bin'].join(delimiter),
|
||||
LD_LIBRARY_PATH: ['/tmp/.mount_orca123/usr/lib', '/opt/audio/lib'].join(delimiter),
|
||||
HOME: '/home/user'
|
||||
}
|
||||
|
||||
removeAppImageRuntimeEnv(env)
|
||||
|
||||
expect(env).toEqual({
|
||||
PATH: '/usr/bin',
|
||||
LD_LIBRARY_PATH: '/opt/audio/lib',
|
||||
HOME: '/home/user'
|
||||
})
|
||||
})
|
||||
|
||||
it('removes AppImage ARGV0 even without a mounted APPDIR', () => {
|
||||
Object.defineProperty(process, 'platform', { configurable: true, value: 'linux' })
|
||||
const env = {
|
||||
ARGV0: '/data/apps/orca.appimage',
|
||||
PATH: ['/usr/local/bin', '/usr/bin'].join(delimiter)
|
||||
}
|
||||
|
||||
removeAppImageRuntimeEnv(env)
|
||||
|
||||
expect(env).toEqual({
|
||||
PATH: ['/usr/local/bin', '/usr/bin'].join(delimiter)
|
||||
})
|
||||
})
|
||||
|
||||
it('leaves AppImage-looking env untouched outside Linux', () => {
|
||||
Object.defineProperty(process, 'platform', { configurable: true, value: 'darwin' })
|
||||
const env = {
|
||||
APPIMAGE: '/data/apps/orca.appimage',
|
||||
APPDIR: '/tmp/.mount_orca123',
|
||||
ARGV0: '/data/apps/orca.appimage',
|
||||
PATH: ['/tmp/.mount_orca123/usr/bin', '/usr/bin'].join(delimiter)
|
||||
}
|
||||
|
||||
removeAppImageRuntimeEnv(env)
|
||||
|
||||
expect(env).toEqual({
|
||||
APPIMAGE: '/data/apps/orca.appimage',
|
||||
APPDIR: '/tmp/.mount_orca123',
|
||||
ARGV0: '/data/apps/orca.appimage',
|
||||
PATH: ['/tmp/.mount_orca123/usr/bin', '/usr/bin'].join(delimiter)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { mkdtempSync, readFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join, sep } from 'node:path'
|
||||
import { join } from 'node:path'
|
||||
import {
|
||||
resolveShellWrapperRoot,
|
||||
type ShellWrapperFileBuilder
|
||||
@@ -21,26 +21,6 @@ function makeBase(): string {
|
||||
}
|
||||
|
||||
describe('resolveShellWrapperRoot', () => {
|
||||
it('gives builds with different wrapper contents different trees', () => {
|
||||
const base = makeBase()
|
||||
expect(resolveShellWrapperRoot(base, builderFor('ORCA_SHELL_READY_MARKER'))).not.toEqual(
|
||||
resolveShellWrapperRoot(base, builderFor('ORCA_SHELL_FEATURES'))
|
||||
)
|
||||
})
|
||||
|
||||
it('is stable for identical contents', () => {
|
||||
const base = makeBase()
|
||||
expect(resolveShellWrapperRoot(base, builderFor('a'))).toEqual(
|
||||
resolveShellWrapperRoot(base, builderFor('a'))
|
||||
)
|
||||
})
|
||||
|
||||
it('keeps the zsh dir ending in /shell-ready/zsh', () => {
|
||||
const root = resolveShellWrapperRoot(makeBase(), builderFor('a'))
|
||||
// Shell guards match a POSIX suffix on every host.
|
||||
expect(join(root, 'zsh').split(sep).join('/').endsWith('/shell-ready/zsh')).toBe(true)
|
||||
})
|
||||
|
||||
it('lets two contracts coexist instead of clobbering each other', () => {
|
||||
const base = makeBase()
|
||||
const oldBuild = builderFor('old')
|
||||
@@ -54,11 +34,4 @@ describe('resolveShellWrapperRoot', () => {
|
||||
expect(readFileSync(join(oldRoot, 'bash', 'rcfile'), 'utf8')).toBe('rcfile old')
|
||||
expect(readFileSync(join(newRoot, 'bash', 'rcfile'), 'utf8')).toBe('rcfile new')
|
||||
})
|
||||
|
||||
it('never resolves onto the legacy unversioned tree', () => {
|
||||
const userData = makeBase()
|
||||
const root = resolveShellWrapperRoot(join(userData, 'shell-wrappers'), builderFor('a'))
|
||||
expect(root.startsWith(join(userData, 'shell-wrappers') + sep)).toBe(true)
|
||||
expect(root).not.toEqual(join(userData, 'shell-ready'))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import type { ClaudeRateLimitAccountsState } from '../../shared/managed-account-types'
|
||||
import { RuntimeAccountController, type RuntimeAccountServices } from './runtime-account-controller'
|
||||
|
||||
// The saved lane key may differ in case from the account's distro, as older selections do.
|
||||
function claudeState(wslAccountId: string | null): ClaudeRateLimitAccountsState {
|
||||
return {
|
||||
activeAccountId: null,
|
||||
activeAccountIdsByRuntime: { host: null, wsl: { ubuntu: wslAccountId } },
|
||||
accounts: [
|
||||
{
|
||||
id: 'wsl-b',
|
||||
email: 'b@example.com',
|
||||
managedAuthRuntime: 'wsl',
|
||||
wslDistro: 'Ubuntu',
|
||||
authMethod: 'subscription-oauth',
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
lastAuthenticatedAt: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
function controllerWithSwitch(): {
|
||||
accounts: RuntimeAccountController
|
||||
onClaudeSelected: ReturnType<typeof vi.fn>
|
||||
} {
|
||||
const onClaudeSelected = vi.fn()
|
||||
const accounts = new RuntimeAccountController(() => ({
|
||||
claudeAccountSelected: onClaudeSelected
|
||||
}))
|
||||
const claudeAccounts = {
|
||||
listAccounts: () => claudeState(null),
|
||||
selectAccount: async (accountId: string | null) => claudeState(accountId)
|
||||
}
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: selectClaude reads only these two methods.
|
||||
accounts.setServices({ claudeAccounts } as unknown as RuntimeAccountServices)
|
||||
return { accounts, onClaudeSelected }
|
||||
}
|
||||
|
||||
describe('RuntimeAccountController.selectClaude', () => {
|
||||
it("tells the window about a phone switch, on the selected account's lane", async () => {
|
||||
const { accounts, onClaudeSelected } = controllerWithSwitch()
|
||||
await accounts.selectClaude('wsl-b')
|
||||
expect(onClaudeSelected).toHaveBeenCalledWith({
|
||||
target: { runtime: 'wsl', wslDistro: 'Ubuntu' },
|
||||
before: { state: claudeState(null), accountId: null },
|
||||
after: { state: claudeState('wsl-b'), accountId: 'wsl-b' }
|
||||
})
|
||||
})
|
||||
|
||||
it('leaves a web client switch to the web client, which moves its own sessions', async () => {
|
||||
const { accounts, onClaudeSelected } = controllerWithSwitch()
|
||||
await accounts.selectClaude('wsl-b', { movesRunningSessions: true })
|
||||
expect(onClaudeSelected).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -41,19 +41,4 @@ describe('electron-updater MacUpdater staging contract', () => {
|
||||
nativeUpdater.emit('update-downloaded')
|
||||
expect(updater.handleUpdateDownloaded).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('quits the app without relaunching once staged when autoRunAppAfterInstall is off', () => {
|
||||
const app = { quit: vi.fn() }
|
||||
const nativeUpdater = { quitAndInstall: vi.fn() }
|
||||
|
||||
MacUpdater.prototype['handleUpdateDownloaded'].call({
|
||||
autoRunAppAfterInstall: false,
|
||||
app,
|
||||
nativeUpdater,
|
||||
closeServerIfExists: vi.fn()
|
||||
})
|
||||
|
||||
expect(app.quit).toHaveBeenCalledOnce()
|
||||
expect(nativeUpdater.quitAndInstall).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,7 +2,6 @@ import { afterEach, describe, expect, it } from 'vitest'
|
||||
import {
|
||||
beginWslMoveUndoRun,
|
||||
claimWslMoveRun,
|
||||
isAnyWslMoveRunning,
|
||||
wslMoveClaimRefusal,
|
||||
releaseWslMoveRun,
|
||||
type RunningWslMove
|
||||
@@ -28,26 +27,6 @@ function beginUndo(): () => void {
|
||||
}
|
||||
|
||||
describe('wslMoveInstallHold', () => {
|
||||
it('lets an install through when nothing moves or comes back', () => {
|
||||
expect(wslMoveInstallHold()).toBeNull()
|
||||
})
|
||||
|
||||
it('holds for an undo, with its own message, without counting it as a move', () => {
|
||||
beginUndo()
|
||||
expect(wslMoveInstallHold()).toEqual({
|
||||
lifecycleEvent: 'quit_and_install_held_for_wsl_move_undo',
|
||||
message: 'A project is coming back from its WSL host. Install the update once that finishes.'
|
||||
})
|
||||
// The quit prompt and Cancel read this; an undo must not look like a move to them.
|
||||
expect(isAnyWslMoveRunning()).toBe(false)
|
||||
})
|
||||
|
||||
it('names the move when a move and an undo both run', () => {
|
||||
move = claimWslMoveRun('Debian')
|
||||
beginUndo()
|
||||
expect(wslMoveInstallHold()?.lifecycleEvent).toBe('quit_and_install_held_for_wsl_move')
|
||||
})
|
||||
|
||||
it('holds until every undo has ended, and an end counts once', () => {
|
||||
const first = beginUndo()
|
||||
const second = beginUndo()
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type * as BackgroundDistroGate from '../discovery/wsl-background-distro-gate'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
runWslProcess: vi.fn(),
|
||||
readWslDistroRunningState: vi.fn()
|
||||
}))
|
||||
vi.mock('./wsl-runner', () => ({ runWslProcess: mocks.runWslProcess }))
|
||||
vi.mock('../discovery/wsl-background-distro-gate', async (importOriginal) => ({
|
||||
...(await importOriginal<typeof BackgroundDistroGate>()),
|
||||
readWslDistroRunningState: mocks.readWslDistroRunningState
|
||||
}))
|
||||
|
||||
import { readWslGuestProcessTable } from './wsl-guest-process-table'
|
||||
import { runAsWslUserAct } from '../filesystem/wsl-unc-access-gate'
|
||||
|
||||
describe('readWslGuestProcessTable never boots a stopped distro (E9)', () => {
|
||||
const originalPlatform = process.platform
|
||||
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(process, 'platform', { configurable: true, value: 'win32' })
|
||||
mocks.runWslProcess.mockReset().mockResolvedValue({
|
||||
environmentResolved: true,
|
||||
code: 0,
|
||||
stdout: '',
|
||||
stderr: '',
|
||||
timedOut: false
|
||||
})
|
||||
mocks.readWslDistroRunningState.mockReset().mockResolvedValue('stopped')
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
Object.defineProperty(process, 'platform', { configurable: true, value: originalPlatform })
|
||||
})
|
||||
|
||||
it('reports an empty table for a stopped distro without listing it', async () => {
|
||||
await expect(readWslGuestProcessTable('Ubuntu')).resolves.toEqual([])
|
||||
expect(mocks.runWslProcess).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('reports an unreadable table when WSL cannot say whether the distro runs', async () => {
|
||||
mocks.readWslDistroRunningState.mockResolvedValue('unverifiable')
|
||||
await expect(readWslGuestProcessTable('Ubuntu')).resolves.toBeNull()
|
||||
expect(mocks.runWslProcess).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('lists a running distro, and a user move may list a stopped one', async () => {
|
||||
mocks.readWslDistroRunningState.mockResolvedValue('running')
|
||||
await readWslGuestProcessTable('Ubuntu')
|
||||
expect(mocks.runWslProcess).toHaveBeenCalledTimes(1)
|
||||
|
||||
mocks.readWslDistroRunningState.mockResolvedValue('stopped')
|
||||
await runAsWslUserAct('Debian', () => readWslGuestProcessTable('Debian'))
|
||||
expect(mocks.runWslProcess).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user