diff --git a/config/scripts/agent-typescript-lsp.test.mjs b/config/scripts/agent-typescript-lsp.test.mjs index 5cc59b2b1b4..872fccbce11 100644 --- a/config/scripts/agent-typescript-lsp.test.mjs +++ b/config/scripts/agent-typescript-lsp.test.mjs @@ -85,7 +85,11 @@ describe('agent TypeScript LSP diagnostics bridge', () => { it('re-pulls open documents when the server asks for a refresh', () => { const { bridge, client, server, flush, pulls } = harness({}) - bridge.fromClient(edit(1)) + bridge.fromClient({ + jsonrpc: '2.0', + method: 'textDocument/didOpen', + params: { textDocument: { uri, version: 1, languageId: 'typescript', text: 'v1' } } + }) flush() bridge.fromServer({ jsonrpc: '2.0', id: 'srv-1', method: 'workspace/diagnostic/refresh' }) flush() @@ -113,18 +117,20 @@ describe('agent TypeScript LSP diagnostics bridge', () => { encodeLspMessage({ id: 2 }) ]) read(frames.subarray(0, 7)) - read(frames.subarray(7, 30)) - read(frames.subarray(30)) + const split = frames.indexOf(Buffer.from('é')) + 1 + read(frames.subarray(7, split)) + read(frames.subarray(split)) expect(received).toEqual([{ id: 1, text: 'héllo' }, { id: 2 }]) }) - it('is the server every committed agent config launches', () => { + it('wires Claude and OpenCode to the repository language server', () => { const plugin = JSON.parse( readFileSync(join(repoRoot, 'config/agent-plugins/orca-typescript/.lsp.json'), 'utf8') ) const opencode = JSON.parse(readFileSync(join(repoRoot, 'opencode.json'), 'utf8')) const script = 'config/scripts/agent-typescript-lsp.mjs' expect(existsSync(join(repoRoot, script))).toBe(true) + expect(plugin['orca-typescript'].command).toBe('node') expect(plugin['orca-typescript'].args).toEqual([`\${CLAUDE_PROJECT_DIR}/${script}`]) expect(opencode.lsp['orca-typescript'].command).toEqual(['node', script]) }) diff --git a/config/scripts/check-changed-selection.test.mjs b/config/scripts/check-changed-selection.test.mjs index 909a492e49e..2306b6948e2 100644 --- a/config/scripts/check-changed-selection.test.mjs +++ b/config/scripts/check-changed-selection.test.mjs @@ -31,7 +31,12 @@ it('limits typecheck to the projects a tree reaches', () => { 'config/tsconfig.node.json', 'config/tsconfig.tc.web.json' ]) - expect(plan(['src/packages/process-host/src/run-process.ts']).typecheckProjects).toHaveLength(4) + expect(plan(['src/packages/process-host/src/run-process.ts']).typecheckProjects).toEqual([ + 'config/tsconfig.node.json', + 'config/tsconfig.tc.web.json', + 'config/tsconfig.tc.cli.json', + 'src/packages/process-host/tsconfig.test.json' + ]) }) it('keeps Vitest full-run triggers out of the related set and runs their colocated tests', () => { diff --git a/config/scripts/dev-instance-isolation.test.mjs b/config/scripts/dev-instance-isolation.test.mjs index fde4ae8821c..11688ad1e85 100644 --- a/config/scripts/dev-instance-isolation.test.mjs +++ b/config/scripts/dev-instance-isolation.test.mjs @@ -78,16 +78,19 @@ describe('dropInheritedDevInstanceEnv', () => { ORCA_USER_DATA_PATH: '/profiles/parent', ORCA_DEV_USER_DATA_PATH: '/profiles/parent' } - dropInheritedDevInstanceEnv(inherited, '/worktrees/child') - expect(inherited).toEqual({ ORCA_USER_DATA_PATH: '/profiles/parent' }) - - const explicit = { ...inherited, ORCA_DEV_USER_DATA_PATH: '/profiles/mine' } - dropInheritedDevInstanceEnv(explicit, '/worktrees/child') - expect(explicit.ORCA_DEV_USER_DATA_PATH).toBe('/profiles/mine') - const same = { ...inherited } dropInheritedDevInstanceEnv(same, '/worktrees/parent') expect(same).toEqual(inherited) + + const explicit = { ...inherited, ORCA_DEV_USER_DATA_PATH: '/profiles/mine' } + dropInheritedDevInstanceEnv(explicit, '/worktrees/child') + expect(explicit).toEqual({ + ORCA_USER_DATA_PATH: '/profiles/parent', + ORCA_DEV_USER_DATA_PATH: '/profiles/mine' + }) + + dropInheritedDevInstanceEnv(inherited, '/worktrees/child') + expect(inherited).toEqual({ ORCA_USER_DATA_PATH: '/profiles/parent' }) }) }) diff --git a/config/scripts/dev-profile-seed.test.mjs b/config/scripts/dev-profile-seed.test.mjs index 35e5812400b..3f1fbf3a41b 100644 --- a/config/scripts/dev-profile-seed.test.mjs +++ b/config/scripts/dev-profile-seed.test.mjs @@ -4,6 +4,7 @@ import { DatabaseSync } from 'node:sqlite' import { tmpdir } from 'node:os' import path from 'node:path' import { afterEach, describe, expect, it } from 'vitest' +import { createDefaultLocalOrcaProfile } from '../../src/shared/orca-profiles' import { seedDevProfile } from './dev-profile-seed.mjs' const roots = [] @@ -27,14 +28,15 @@ function hashTree(dir) { function createSourceProfile() { const root = mkdtempSync(path.join(tmpdir(), 'orca-seed-source-')) roots.push(root) - const profileDir = path.join(root, 'profiles', 'local-default') + const profile = createDefaultLocalOrcaProfile(0) + const profileDir = path.join(root, 'profiles', profile.id) mkdirSync(profileDir, { recursive: true }) writeFileSync( path.join(root, 'orca-profile-index.json'), JSON.stringify({ schemaVersion: 1, - activeProfileId: 'local-default', - profiles: [{ id: 'local-default' }] + activeProfileId: profile.id, + profiles: [profile] }) ) writeFileSync(path.join(profileDir, 'account-session.json.enc'), '{"ciphertext":"x"}') @@ -57,7 +59,7 @@ function createSourceProfile() { } describe('seedDevProfile', () => { - it('writes an importable slim profile and never changes the source', async () => { + it('copies durable profile domains and the account file without changing the source', async () => { const source = createSourceProfile() const before = hashTree(source) const target = path.join(mkdtempSync(path.join(tmpdir(), 'orca-seed-target-')), 'profile') diff --git a/config/scripts/resolve-7za-path.test.mjs b/config/scripts/resolve-7za-path.test.mjs index e7b1eb40bce..09838a2cfd2 100644 --- a/config/scripts/resolve-7za-path.test.mjs +++ b/config/scripts/resolve-7za-path.test.mjs @@ -1,7 +1,7 @@ import { spawnSync } from 'node:child_process' import { existsSync, mkdirSync, mkdtempSync, rmSync, statSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' -import { join, resolve } from 'node:path' +import { dirname, join, resolve } from 'node:path' import { describe, expect, it } from 'vitest' import { legacy7zaRelativePath, resolve7zaPath } from './resolve-7za-path.mjs' @@ -26,13 +26,23 @@ describe('7za path resolution for the Windows signing gates (#6487)', () => { ) }) - it('prefers a real legacy binary over the downloaded toolset', async () => { - // Only meaningful if a transitive dep reintroduces the package. - const legacy = join(projectRoot, ...legacy7zaRelativePath()) - if (!existsSync(legacy)) { - return + it('prefers an existing legacy file over the downloaded toolset', async () => { + const scratch = mkdtempSync(join(tmpdir(), 'orca 7za legacy ')) + const previous = process.env.ELECTRON_BUILDER_7ZIP_PATH + try { + delete process.env.ELECTRON_BUILDER_7ZIP_PATH + const legacy = join(scratch, ...legacy7zaRelativePath()) + mkdirSync(dirname(legacy), { recursive: true }) + writeFileSync(legacy, 'legacy binary fixture') + await expect(resolve7zaPath(scratch)).resolves.toBe(legacy) + } finally { + if (previous === undefined) { + delete process.env.ELECTRON_BUILDER_7ZIP_PATH + } else { + process.env.ELECTRON_BUILDER_7ZIP_PATH = previous + } + rmSync(scratch, { recursive: true, force: true }) } - await expect(resolve7zaPath(projectRoot)).resolves.toBe(legacy) }) it('resolves an executable 7za that can extract an archive', async () => { diff --git a/src/main/ai-vault/opencode-wsl-runtime-preparation.test.ts b/src/main/ai-vault/opencode-wsl-runtime-preparation.test.ts index ec05ca0fc76..780adfaf05c 100644 --- a/src/main/ai-vault/opencode-wsl-runtime-preparation.test.ts +++ b/src/main/ai-vault/opencode-wsl-runtime-preparation.test.ts @@ -65,7 +65,7 @@ async function prepared() { } describe('WSL SQLite runtime preparation', () => { - it('returns immediately, coalesces distro aliases, and uses an actual guest SQLite probe', async () => { + it('returns immediately, coalesces distro aliases, and requests the guest SQLite probe', async () => { let release: (value: ReturnType) => void = () => {} mocks.run.mockReturnValueOnce( new Promise((resolve) => { @@ -128,7 +128,7 @@ describe('WSL SQLite runtime preparation', () => { await prepared() }) - it('rejects a guest Node without the SyncDatabase surface and tolerates its stderr warning', async () => { + it('checks the generated SQLite probe against missing API and stderr warnings locally', async () => { await prepared() const probe = mocks.run.mock.calls.find(([spec]) => spec.program === 'node')?.[0] expect(probe?.args[0]).toBe('-e') @@ -161,7 +161,7 @@ describe('WSL SQLite runtime preparation', () => { } }) - it('falls back to the pinned Node in the runtimes/ store, verified in the guest, with literal argv', async () => { + it('requests pinned runtime verification and preserves literal argv', async () => { const expected = NODE_RUNTIME_ASSETS['linux-arm64-musl'].executableSha256 mocks.run.mockImplementation(async (spec) => { if (spec.script?.startsWith('data=')) { @@ -278,7 +278,7 @@ describe('WSL SQLite runtime preparation', () => { }) it.skipIf(process.platform === 'win32').each(['opencode', 'zcode'] as const)( - 'prepares a %s-only distro despite unrelated OpenCode overrides', + 'presence script finds %s-only data despite unrelated OpenCode overrides', async (agent) => { await prepared() const script = mocks.run.mock.calls[0]?.[0].script diff --git a/src/main/ai-vault/runtime-session-scanner.test.ts b/src/main/ai-vault/runtime-session-scanner.test.ts index 8f866c23c02..c7dfe917f26 100644 --- a/src/main/ai-vault/runtime-session-scanner.test.ts +++ b/src/main/ai-vault/runtime-session-scanner.test.ts @@ -72,13 +72,20 @@ describe('runtime AI Vault session scanner', () => { ) }) - it('surfaces project scope truncation from the runtime transport bound', async () => { + it('caps forwarded project paths and reports scope truncation', async () => { const scopePaths = Array.from({ length: 80 }, (_, index) => `/srv/repo-${index}`) const scanResult = await scanRuntimeAiVaultSessions('/user-data', 'env-1', { scopePaths }) + expect(mocks.callRuntimeEnvironment).toHaveBeenCalledWith( + '/user-data', + 'env-1', + 'aiVault.listSessions', + expect.objectContaining({ scopePaths: scopePaths.slice(0, 64) }), + undefined + ) expect(scanResult.issues).toContainEqual( expect.objectContaining({ kind: 'scope', diff --git a/src/main/ai-vault/session-delete-target.test.ts b/src/main/ai-vault/session-delete-target.test.ts index 0cd2ea0a99c..3b5c4db67c5 100644 --- a/src/main/ai-vault/session-delete-target.test.ts +++ b/src/main/ai-vault/session-delete-target.test.ts @@ -18,7 +18,7 @@ const ROVO_ROOT = join(HOME, '.rovodev', 'sessions') const GROK_ROOT = join(HOME, '.grok', 'sessions') describe('validateAiVaultSessionDeleteTarget', () => { - it('allows a canonical Cline manifest and removes its whole session directory', () => { + it('plans whole session-directory removal for a canonical Cline manifest', () => { const root = join('/tmp', 'cline-sessions') const sessionId = '1786466194549_xrzrl' const sessionDir = join(root, sessionId) @@ -312,7 +312,7 @@ describe('validateAiVaultSessionDeleteTarget', () => { }) describe('directory-shaped agents', () => { - it('plans claude companions before the transcript, so a failure keeps the row', () => { + it('plans claude companions before the transcript', () => { const filePath = join(CLAUDE_ROOT, '-proj', 'sess-1.jsonl') const result = validateAiVaultSessionDeleteTarget({ agent: 'claude', @@ -403,7 +403,7 @@ describe('directory-shaped agents', () => { }) }) - it("removes rovo's session directory, not just the metadata.json inside it", () => { + it("plans removal of rovo's whole session directory", () => { const result = validateAiVaultSessionDeleteTarget({ agent: 'rovo', filePath: join(ROVO_ROOT, 'sess-1', 'metadata.json'), @@ -419,7 +419,7 @@ describe('directory-shaped agents', () => { }) }) - it("removes grok's session directory under its encoded-cwd group", () => { + it("plans removal of grok's session directory under its encoded-cwd group", () => { const result = validateAiVaultSessionDeleteTarget({ agent: 'grok', filePath: join(GROK_ROOT, '-Users-me-proj', 'sess-1', 'summary.json'), diff --git a/src/main/ai-vault/session-delete.test.ts b/src/main/ai-vault/session-delete.test.ts index e664f01ba59..dd6c0c9f791 100644 --- a/src/main/ai-vault/session-delete.test.ts +++ b/src/main/ai-vault/session-delete.test.ts @@ -167,7 +167,7 @@ describe('deleteAiVaultSessionFile', () => { expect(tryDeleteWslUncPathMock).not.toHaveBeenCalled() }) - it('delegates a WSL UNC file to tryDeleteWslUncPath (non-recursive) and never calls trashItem', async () => { + it('uses a successful WSL-helper result before local file trash', async () => { const filePath = join(GEMINI_ROOT, 'project-a', 'session-1.json') tryDeleteWslUncPathMock.mockResolvedValue(true) @@ -182,7 +182,7 @@ describe('deleteAiVaultSessionFile', () => { expect(trashItemMock).not.toHaveBeenCalled() }) - it('deletes a WSL UNC directory-shaped session recursively, never via trashItem', async () => { + it('requests recursive helper removal for a directory-shaped session', async () => { // WSL UNC volumes cannot use Trash and may give unreliable 9P stats. tryDeleteWslUncPathMock.mockResolvedValue(true) diff --git a/src/main/ai-vault/session-first-user-prompt-opencode.test.ts b/src/main/ai-vault/session-first-user-prompt-opencode.test.ts index c0d81a531a6..c164f261516 100644 --- a/src/main/ai-vault/session-first-user-prompt-opencode.test.ts +++ b/src/main/ai-vault/session-first-user-prompt-opencode.test.ts @@ -12,7 +12,7 @@ beforeEach(() => { reader.v2.mockReset().mockResolvedValue({ firstUserPrompt: 'full second-schema prompt' }) }) -it('asks the host reader for the full prompt instead of opening SQLite in the calling process', async () => { +it('requests the full prompt through the host reader', async () => { expect( await readAiVaultFirstUserPrompt({ agent: 'opencode', @@ -74,7 +74,7 @@ it('does not substitute local history for a remote session', async () => { expect(reader.v1).not.toHaveBeenCalled() }) -it('degrades a failed guest read to an unavailable prompt', async () => { +it('degrades a failed host-reader request to an unavailable prompt', async () => { reader.v1.mockRejectedValue(new Error('guest stopped')) expect( await readAiVaultFirstUserPrompt({ diff --git a/src/main/ai-vault/session-parse-cache-persistence.test.ts b/src/main/ai-vault/session-parse-cache-persistence.test.ts index 37f92767655..98941ba8d71 100644 --- a/src/main/ai-vault/session-parse-cache-persistence.test.ts +++ b/src/main/ai-vault/session-parse-cache-persistence.test.ts @@ -351,6 +351,9 @@ describe('session parse cache persistence', () => { resume: null }) await vi.advanceTimersByTimeAsync(1_000) + if (second === 14) { + expect(write).toHaveBeenCalledOnce() + } } expect(write).toHaveBeenCalledOnce() await flushSessionParseCachePersist()