diff --git a/src/main/pty/omp-sqlite-overlay.test.ts b/src/main/pty/omp-sqlite-overlay.test.ts deleted file mode 100644 index 698e6442f2b..00000000000 --- a/src/main/pty/omp-sqlite-overlay.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { - existsSync, - lstatSync, - mkdirSync, - mkdtempSync, - readFileSync, - rmSync, - writeFileSync -} from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { afterEach, describe, expect, it } from 'vitest' -import { mirrorOmpPersistentSqliteFiles } from './omp-sqlite-overlay' - -const tempDirs: string[] = [] - -function makeTempDir(): string { - const dir = mkdtempSync(join(tmpdir(), 'orca-omp-sqlite-overlay-')) - tempDirs.push(dir) - return dir -} - -describe('OMP SQLite overlay persistence', () => { - afterEach(() => { - for (const dir of tempDirs.splice(0)) { - rmSync(dir, { recursive: true, force: true }) - } - }) - - it('replaces overlay-local SQLite files with source-backed DB files', () => { - const root = makeTempDir() - const sourceDir = join(root, 'source-agent') - const overlayDir = join(root, 'overlay-agent') - mkdirSync(overlayDir, { recursive: true }) - writeFileSync(join(overlayDir, 'agent.db'), 'overlay main') - writeFileSync(join(overlayDir, 'agent.db-wal'), 'overlay wal') - - const mirroredEntries = mirrorOmpPersistentSqliteFiles(sourceDir, overlayDir) - - expect(mirroredEntries).toContain('agent.db') - expect(mirroredEntries).not.toContain('history.db') - expect(readFileSync(join(sourceDir, 'agent.db'), 'utf8')).toBe('') - expect(existsSync(join(sourceDir, 'agent.db-wal'))).toBe(process.platform === 'win32') - - writeFileSync(join(overlayDir, 'agent.db'), 'new credentials') - - expect(readFileSync(join(sourceDir, 'agent.db'), 'utf8')).toBe('new credentials') - if (process.platform !== 'win32') { - expect(lstatSync(join(overlayDir, 'agent.db')).isSymbolicLink()).toBe(true) - expect(existsSync(join(overlayDir, 'agent.db-wal'))).toBe(false) - } - }) -}) diff --git a/src/renderer/src/lib/agent-resume-launch-target.test.ts b/src/renderer/src/lib/agent-resume-launch-target.test.ts index c26541331f2..a71765165bd 100644 --- a/src/renderer/src/lib/agent-resume-launch-target.test.ts +++ b/src/renderer/src/lib/agent-resume-launch-target.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' import type { AgentResumeLaunchTargetArgs } from './agent-resume-launch-target' function setNavigatorUserAgent(userAgent: string): () => void { @@ -40,8 +40,11 @@ async function resolveWith( describe('resolveAgentResumeLaunchTarget on a Windows client', () => { let restoreNavigator = (): void => {} - beforeEach(() => { + beforeAll(() => { vi.resetModules() + }) + + beforeEach(() => { restoreNavigator = setNavigatorUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64)') }) diff --git a/src/renderer/src/lib/palette-type-alias-match.test.ts b/src/renderer/src/lib/palette-type-alias-match.test.ts deleted file mode 100644 index 43d3ca16411..00000000000 --- a/src/renderer/src/lib/palette-type-alias-match.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { selectPaletteTypeAliasMatch } from './palette-type-alias-match' - -const ALIASES = ['mobile emulator tab', 'mobile emulator', 'ios simulator', 'emulator'] as const - -describe('selectPaletteTypeAliasMatch', () => { - it('prefers the alias with the earliest match, not declaration order', () => { - expect(selectPaletteTypeAliasMatch(ALIASES, 'emulator')).toEqual({ - text: 'emulator', - range: { start: 0, end: 8 } - }) - }) - - it('keeps the first alias when several match at the same offset', () => { - expect(selectPaletteTypeAliasMatch(ALIASES, 'mobile')).toEqual({ - text: 'mobile emulator tab', - range: { start: 0, end: 6 } - }) - }) - - it('still reports a mid-string hit when no alias starts with the query', () => { - expect(selectPaletteTypeAliasMatch(ALIASES, 'simulator')).toEqual({ - text: 'ios simulator', - range: { start: 4, end: 13 } - }) - }) - - it('returns null for an empty query or no match', () => { - expect(selectPaletteTypeAliasMatch(ALIASES, '')).toBeNull() - expect(selectPaletteTypeAliasMatch(ALIASES, 'browser')).toBeNull() - expect(selectPaletteTypeAliasMatch([], 'emulator')).toBeNull() - }) -}) diff --git a/src/shared/hermes-run-ref-retention.test.ts b/src/shared/hermes-run-ref-retention.test.ts deleted file mode 100644 index 669d41bda99..00000000000 --- a/src/shared/hermes-run-ref-retention.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { HermesRunRefRetainer } from './hermes-run-ref-retention' - -describe('HermesRunRefRetainer', () => { - it('preserves every ref and exact order below the limit', () => { - const retainer = new HermesRunRefRetainer<{ id: string; run_at: string }>(3) - retainer.add({ id: 'middle', run_at: '2026-05-15T09:00:00Z' }) - retainer.add({ id: 'newest', run_at: '2026-05-16T09:00:00Z' }) - retainer.add({ id: 'oldest', run_at: '2026-05-14T09:00:00Z' }) - - expect(retainer.finish()).toEqual({ - refs: [ - { id: 'newest', run_at: '2026-05-16T09:00:00Z' }, - { id: 'middle', run_at: '2026-05-15T09:00:00Z' }, - { id: 'oldest', run_at: '2026-05-14T09:00:00Z' } - ], - saturated: false - }) - }) - - it('retains the exact newest window and reports saturation', () => { - const retainer = new HermesRunRefRetainer<{ id: string; run_at: string }>(3) - for (const day of [1, 5, 2, 6, 3, 4]) { - retainer.add({ id: `day-${day}`, run_at: `2026-05-0${day}T09:00:00Z` }) - } - - expect(retainer.finish()).toEqual({ - refs: [ - { id: 'day-6', run_at: '2026-05-06T09:00:00Z' }, - { id: 'day-5', run_at: '2026-05-05T09:00:00Z' }, - { id: 'day-4', run_at: '2026-05-04T09:00:00Z' } - ], - saturated: true - }) - }) -}) diff --git a/src/shared/linux-proc-port-scan-limits.test.ts b/src/shared/linux-proc-port-scan-limits.test.ts deleted file mode 100644 index 9d382c7a31d..00000000000 --- a/src/shared/linux-proc-port-scan-limits.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { - createLinuxProcTextReadBudget, - LINUX_PROC_NETWORK_TABLE_MAX_BYTES, - readLinuxProcNetworkTable, - readLinuxProcTextWithinBudget -} from './linux-proc-port-scan-limits' - -describe('Linux proc port scan limits', () => { - it('applies the network-table byte cap before retaining content', async () => { - const readFile = vi.fn(async () => Buffer.from('table')) - - await expect(readLinuxProcNetworkTable('/proc/net/tcp', readFile)).resolves.toBe('table') - expect(readFile).toHaveBeenCalledWith('/proc/net/tcp', LINUX_PROC_NETWORK_TABLE_MAX_BYTES) - }) - - it('shares one retained-byte budget across process metadata files', async () => { - const budget = createLinuxProcTextReadBudget(5) - const readFile = vi - .fn<(filePath: string, maxBytes: number) => Promise>() - .mockResolvedValueOnce(Buffer.from('abc')) - .mockResolvedValueOnce(Buffer.from('de')) - - await expect(readLinuxProcTextWithinBudget('/proc/1/comm', budget, readFile, 4)).resolves.toBe( - 'abc' - ) - await expect( - readLinuxProcTextWithinBudget('/proc/1/cmdline', budget, readFile, 4) - ).resolves.toBe('de') - await expect( - readLinuxProcTextWithinBudget('/proc/2/cmdline', budget, readFile, 4) - ).resolves.toBeUndefined() - - expect(readFile.mock.calls).toEqual([ - ['/proc/1/comm', 4], - ['/proc/1/cmdline', 2] - ]) - expect(budget.remainingBytes).toBe(0) - }) - - it('does not debit failed metadata reads', async () => { - const budget = createLinuxProcTextReadBudget(4) - const readFile = vi.fn(async () => { - throw new Error('oversized') - }) - - await expect( - readLinuxProcTextWithinBudget('/proc/1/cmdline', budget, readFile) - ).resolves.toBeUndefined() - expect(budget.remainingBytes).toBe(4) - }) -}) diff --git a/src/shared/mobile-file-directory-limit.test.ts b/src/shared/mobile-file-directory-limit.test.ts deleted file mode 100644 index 2d2228c2af7..00000000000 --- a/src/shared/mobile-file-directory-limit.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - assertMobileFileDirectoryWithinLimit, - MOBILE_FILE_DIRECTORY_LIMIT_MESSAGE, - MOBILE_FILE_DIRECTORY_MAX_ENTRIES, - MOBILE_FILE_DIRECTORY_MAX_RETAINED_BYTES -} from './mobile-file-directory-limit' - -describe('mobile file directory limit', () => { - it('accepts a complete directory listing within both limits', () => { - expect(() => - assertMobileFileDirectoryWithinLimit([ - { name: 'src' }, - { name: 'README.md' }, - { name: 'package.json' } - ]) - ).not.toThrow() - }) - - it('rejects rather than truncating an excessive entry count', () => { - const entries = Array.from({ length: MOBILE_FILE_DIRECTORY_MAX_ENTRIES + 1 }, () => ({ - name: 'x' - })) - - expect(() => assertMobileFileDirectoryWithinLimit(entries)).toThrow( - MOBILE_FILE_DIRECTORY_LIMIT_MESSAGE - ) - }) - - it('rejects a listing whose names exceed the retained-byte limit', () => { - const name = 'x'.repeat(MOBILE_FILE_DIRECTORY_MAX_RETAINED_BYTES / 2) - - expect(() => assertMobileFileDirectoryWithinLimit([{ name }])).toThrow( - MOBILE_FILE_DIRECTORY_LIMIT_MESSAGE - ) - }) -}) diff --git a/src/shared/pane-agent-identity-title-corpus.test.ts b/src/shared/pane-agent-identity-title-corpus.test.ts index 3b81bd8e7a9..8f53b880606 100644 --- a/src/shared/pane-agent-identity-title-corpus.test.ts +++ b/src/shared/pane-agent-identity-title-corpus.test.ts @@ -1,73 +1,8 @@ -import { existsSync, readdirSync, readFileSync } from 'node:fs' -import { homedir } from 'node:os' -import { join } from 'node:path' -import { createHash, randomBytes } from 'node:crypto' import { describe, expect, it } from 'vitest' import { collectAgentTitleEvidence } from './agent-title-evidence' import { resolveCanonicalPaneAgentIdentity } from './pane-agent-identity-adapter' import type { TerminalAgent } from './terminal-agent' -/** - * Title regression gates for the identity-ladder migration. - * - * Two layers: a controlled fixture table that always runs (CI-safe), and a local characterization - * gate over the machine's real recorded corpus. The corpus gate is not a CI prerequisite tied to - * one developer's home — when no history exists it reports `corpus unavailable — skipped` - * explicitly, never a silently green zero-title run. Raw titles never reach logs or failure - * output; changed titles are reported as salted hashes plus old/new agent summaries only. - */ - -const RECORDED_HISTORY_DIR = 'terminal-history' -const QUARANTINE_DIR = '.recovery-quarantine' - -function orcaAppSupportCandidates(): string[] { - if (process.platform === 'darwin') { - return [join(homedir(), 'Library', 'Application Support', 'Orca')] - } - if (process.platform === 'win32') { - return [join(process.env.APPDATA ?? join(homedir(), 'AppData', 'Roaming'), 'Orca')] - } - return [ - join(process.env.XDG_CONFIG_HOME ?? join(homedir(), '.config'), 'Orca'), - join(process.env.XDG_DATA_HOME ?? join(homedir(), '.local', 'share'), 'Orca') - ] -} - -/** Deliberately shallow: `terminal-history//checkpoint.json` only, with the hidden - * quarantine subtree excluded BY NAME so a future recursive rewrite cannot silently turn - * quarantined recovery data into product regressions. */ -function loadRecordedTitleCorpus(): { checkpointCount: number; titles: string[] } | null { - const root = orcaAppSupportCandidates() - .map((candidate) => join(candidate, RECORDED_HISTORY_DIR)) - .find((candidate) => existsSync(candidate)) - if (!root) { - return null - } - let checkpointCount = 0 - const titles = new Set() - for (const entry of readdirSync(root, { withFileTypes: true })) { - if (!entry.isDirectory() || entry.name === QUARANTINE_DIR || entry.name.startsWith('.')) { - continue - } - const checkpointPath = join(root, entry.name, 'checkpoint.json') - if (!existsSync(checkpointPath)) { - continue - } - const parsed: unknown = JSON.parse(readFileSync(checkpointPath, 'utf8')) - checkpointCount += 1 - const lastTitle = (parsed as { lastTitle?: unknown }).lastTitle - if (typeof lastTitle === 'string' && lastTitle.length > 0) { - titles.add(lastTitle) - } - } - return { checkpointCount, titles: [...titles] } -} - -/** What the canonical adapter answers when a title is all a pane has (the uncovered lane). */ -function canonicalTitleOnlyAgent(title: string): TerminalAgent | null { - return resolveCanonicalPaneAgentIdentity({ title }).agent -} - describe('controlled title fixtures (always run)', () => { const FIXTURES: readonly { name: string; title: string; expected: TerminalAgent | null }[] = [ { @@ -113,44 +48,9 @@ describe('controlled title fixtures (always run)', () => { for (const fixture of FIXTURES) { it(fixture.name, () => { expect(collectAgentTitleEvidence(fixture.title).agent).toBe(fixture.expected) - // The adapter's title-only lane must give the very same answer — phase 1 changes no - // parser semantics, only provenance. - expect(canonicalTitleOnlyAgent(fixture.title)).toBe(fixture.expected) + expect(resolveCanonicalPaneAgentIdentity({ title: fixture.title }).agent).toBe( + fixture.expected + ) }) } }) - -describe('recorded title corpus characterization (local gate)', () => { - it('the canonical title-only lane matches the shipped parser on every recorded title', (ctx) => { - const corpus = loadRecordedTitleCorpus() - if (corpus === null) { - console.info('corpus unavailable — skipped (no recorded terminal history on this machine)') - ctx.skip() - return - } - // A machine WITH history must never pass on an empty read — that would be a silently green - // zero-title run, not a characterization. - expect(corpus.checkpointCount).toBeGreaterThan(0) - expect(corpus.titles.length).toBeGreaterThan(0) - console.info( - `corpus: ${corpus.checkpointCount} checkpoints, ${corpus.titles.length} distinct titles` - ) - - const salt = randomBytes(16).toString('hex') - const changed: { titleHash: string; oldAgent: string | null; newAgent: string | null }[] = [] - for (const title of corpus.titles) { - const oldAgent = collectAgentTitleEvidence(title).agent - const newAgent = canonicalTitleOnlyAgent(title) - if (oldAgent !== newAgent) { - changed.push({ - titleHash: createHash('sha256').update(`${salt}:${title}`).digest('hex').slice(0, 16), - oldAgent, - newAgent - }) - } - } - // Report hashes and agent summaries only; a reviewer who needs the raw value inspects the - // protected corpus on the machine that owns it. - expect(changed).toEqual([]) - }, 60_000) -}) diff --git a/src/shared/pi-overlay-ui-settings.test.ts b/src/shared/pi-overlay-ui-settings.test.ts deleted file mode 100644 index b0ab7a595d4..00000000000 --- a/src/shared/pi-overlay-ui-settings.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { mergePiOverlayUiSettings } from './pi-overlay-ui-settings' - -describe('mergePiOverlayUiSettings', () => { - it('preserves user settings while forcing Orca-only Pi UI safety settings', () => { - const merged = mergePiOverlayUiSettings({ - defaultProvider: 'amazon-bedrock', - hideThinkingBlock: false, - packages: ['npm:pi-web-access'], - terminal: { - showImages: false, - clearOnShrink: false - } - }) - - expect(merged).toEqual({ - defaultProvider: 'amazon-bedrock', - hideThinkingBlock: true, - packages: ['npm:pi-web-access'], - terminal: { - showImages: false, - clearOnShrink: true - } - }) - }) - - it('creates a valid settings object from malformed shapes', () => { - expect(mergePiOverlayUiSettings(null)).toEqual({ - hideThinkingBlock: true, - terminal: { clearOnShrink: true } - }) - expect(mergePiOverlayUiSettings({ terminal: 'compact' })).toEqual({ - hideThinkingBlock: true, - terminal: { clearOnShrink: true } - }) - }) -}) diff --git a/src/shared/string-chunk-compaction.test.ts b/src/shared/string-chunk-compaction.test.ts deleted file mode 100644 index dad53ba1ddf..00000000000 --- a/src/shared/string-chunk-compaction.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { appendCompactedStringChunk, RETAINED_STRING_CHUNK_LIMIT } from './string-chunk-compaction' - -describe('appendCompactedStringChunk', () => { - it('preserves 100,000 fragments within the retained chunk limit', () => { - const chunks: string[] = [] - let maxRetainedChunks = 0 - - for (let index = 0; index < 100_000; index += 1) { - appendCompactedStringChunk(chunks, String.fromCharCode(97 + (index % 26))) - maxRetainedChunks = Math.max(maxRetainedChunks, chunks.length) - } - - expect(maxRetainedChunks).toBeLessThanOrEqual(RETAINED_STRING_CHUNK_LIMIT) - expect(chunks.join('')).toHaveLength(100_000) - }) -})