diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index 21f82faaae7..00b3edcbd75 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -2379,7 +2379,7 @@ "providers": ["local", "ssh", "remote-runtime"], "coveredPlatforms": ["macos"], "coveredProviders": [], - "coverageNotes": "Audit2697 browser source review (metadata followup2702): Reduce src/renderer/src/lib/browser/palette/browser-palette-page-entries.test.ts by four authored/expanded cases without replacement. The remaining three cases preserve only their existing same-ID execution-host separation, absent stamped-owner refusal and ambiguous legacy-bucket refusal scope. Intentionally lose duplicate backing-tab-ID suppression, multiple unified-browser claims of one workspace, foreign-worktree unified-tab rejection, and the 1000-workspace output/internal getter-read budget alarm. These are real removed alarms; the retained host cases are not equivalent replacements. Current test-value policy rejects internal getter/read budgets; no replacement performance or measured-cost claim is made. Keep existing nonempty selectors and the retained same-ID browser assertionRef. No fresh gate execution or reduced-source validation is supplied; dated receipts, results, commands and historical coverage accounts remain within their original source scope. No live browser activation, physical provider, mixed-version execution, mobile, native UI, platform hardware, soak or timing result is added. Prior coverage account (preserved verbatim): Audit2266 reduces cmd-j-host-qualified-candidate-ownership.test.ts to the scoped open-tab hidden same-ID legacy ambiguity refusal. Separate browser/simulator paired-runtime alias and current-row projections, generic workspace host projection and hostless explicit-owner refusal examples are removed without equivalent replacement. No fresh gate execution, browser activation, physical host, simulator or remote delivery evidence is claimed. Historical dated receipts and budgets remain raw-byte exact. Prior coverage account: Audit1899 replays sealed Audit1895: workspace-session-schema.test.ts keeps only cross-workspace truncated-tab salvage and legacy browser-history cap normalization. Four current commands and testFiles retain that reduced source; no current assertionRef or active receipt selects it. SSH file-owner, isolated browser partition, launch/title/quick-command fields, recency, structured-chat/view-mode and remote browser identity hydration alarms are intentionally lost. Existing retired receipt strings and unrelated active receipts remain verbatim. No equivalent replacement, new Cmd-J gate execution, native simulator or remote/browser restore proof is claimed. Historical coverage account before Audit1899: Audit1494 removes use-open-tab-search.test.ts. Supplied host/worktree/title/ranking projections, simple subscription updates and result-array identity alarms are retired. No actual activation or navigation occurred in that suite. No equivalent replacement coverage or fresh Cmd-J gate validation is claimed. Historical receipts remain scoped to their original selectors. Audit1433 narrows worktree-checks-review-index.test.ts to same-ID local/SSH object-key review isolation. Local provider/cache lookup, GitLab preference, suppression/legacy fallback, explicit-PR authority, physical-versus-runtime review isolation, and branchless-workspace alarms are intentionally removed. No equivalent replacement coverage is claimed. Historical receipts and commands remain unchanged and do not certify the reduced source. Historical coverage account: Audit1396 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Audit1308 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Deterministic candidate-builder contracts inject local and paired-runtime worktrees with the same bare id into the real aggregated tab buckets, including folder workspaces and a paired runtime transporting an SSH-owned worktree. A mirror contract preserves distinct same-id runtime tabs. A paired Electron journey verifies both visible browser rows and both visible simulator rows, then requires each selection to render its owning active tab and update backing host-qualified state. The test-value audit narrowed simulator-palette-search.test.ts to the recency tie, same-id host isolation, ambiguous legacy host rejection and oversized-input getter tripwire. Supplied timestamp, empty-query context ordering, host projection, alias/range, metadata matching, active-tab score, over-token/whitespace and malformed display-name fallback examples were removed. Their separate alarms are lost; retained candidate and activation contracts do not establish equivalent coverage. Historical run commands and results remain historical. The audit also removed the standalone workspace-tab-palette-search suite: supplied row, title, metadata, alias, range, ranking and ownership examples no longer provide their separate alarms. Other retained contracts are not replacement coverage. The mirror-identity fixture now retains same-id sibling-runtime isolation and the repeated-frame subscriber tripwire only. Supplied field, owner, alias and mapping examples are removed; their separate alarms are lost. The retained sibling-runtime case is not equivalent coverage for them. The test-value audit removed tabs-open-close-lifecycle host-stamping examples and ordinary tab/group operations from tabs-pane-layout-operations. Local-null, explicit-runtime and SSH-without-active-host stamp assertions are lost; retained mobile/MRU and subscriber/atomic split cases do not replace them. Candidate and live Cmd-J evidence retains its own scope. Historical commands, outcomes and narratives remain historical. Audit1308: src/renderer/src/lib/browser-palette-search.test.ts removed. Browser-palette token and pasted-query size-guard work alarms disappear. No equivalent replacement coverage is claimed. Audit1386: src/renderer/src/lib/open-mobile-emulator-tab.test.ts reduced. Duplicate attach count and null-tab creation retry/guard reset standalone alarms removed; no simulator device used. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1396: src/renderer/src/lib/simulator-palette-search.test.ts reduced. Lose simulator-specific lastFocusedAt tie-order alarm; identity/routing and pre-scan size fence remain. No native simulator or real remote launch claim. No equivalent replacement coverage is claimed. Audit1402: src/renderer/src/runtime/web-session-tabs-sync-mirror-identity.test.ts reduced. Loses repeated no-op frame store-notification budget; independent cross-runtime resource preservation remains. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", + "coverageNotes": "Audit2729 reduces browser-page-palette-activation.test.ts: separate SSH/paired-runtime alias forwarding, distinct multigroup views accepted, and remote-folder activation forwarding alarms are removed. Collision refusal, preactivation address-bar focus capture, owning-group activation state and missing-tab refusal remain byte-identical. No equivalent replacement, fresh gate execution or actual host/browser delivery evidence is claimed. Prior coverage account: Audit2697 browser source review (metadata followup2702): Reduce src/renderer/src/lib/browser/palette/browser-palette-page-entries.test.ts by four authored/expanded cases without replacement. The remaining three cases preserve only their existing same-ID execution-host separation, absent stamped-owner refusal and ambiguous legacy-bucket refusal scope. Intentionally lose duplicate backing-tab-ID suppression, multiple unified-browser claims of one workspace, foreign-worktree unified-tab rejection, and the 1000-workspace output/internal getter-read budget alarm. These are real removed alarms; the retained host cases are not equivalent replacements. Current test-value policy rejects internal getter/read budgets; no replacement performance or measured-cost claim is made. Keep existing nonempty selectors and the retained same-ID browser assertionRef. No fresh gate execution or reduced-source validation is supplied; dated receipts, results, commands and historical coverage accounts remain within their original source scope. No live browser activation, physical provider, mixed-version execution, mobile, native UI, platform hardware, soak or timing result is added. Prior coverage account (preserved verbatim): Audit2266 reduces cmd-j-host-qualified-candidate-ownership.test.ts to the scoped open-tab hidden same-ID legacy ambiguity refusal. Separate browser/simulator paired-runtime alias and current-row projections, generic workspace host projection and hostless explicit-owner refusal examples are removed without equivalent replacement. No fresh gate execution, browser activation, physical host, simulator or remote delivery evidence is claimed. Historical dated receipts and budgets remain raw-byte exact. Prior coverage account: Audit1899 replays sealed Audit1895: workspace-session-schema.test.ts keeps only cross-workspace truncated-tab salvage and legacy browser-history cap normalization. Four current commands and testFiles retain that reduced source; no current assertionRef or active receipt selects it. SSH file-owner, isolated browser partition, launch/title/quick-command fields, recency, structured-chat/view-mode and remote browser identity hydration alarms are intentionally lost. Existing retired receipt strings and unrelated active receipts remain verbatim. No equivalent replacement, new Cmd-J gate execution, native simulator or remote/browser restore proof is claimed. Historical coverage account before Audit1899: Audit1494 removes use-open-tab-search.test.ts. Supplied host/worktree/title/ranking projections, simple subscription updates and result-array identity alarms are retired. No actual activation or navigation occurred in that suite. No equivalent replacement coverage or fresh Cmd-J gate validation is claimed. Historical receipts remain scoped to their original selectors. Audit1433 narrows worktree-checks-review-index.test.ts to same-ID local/SSH object-key review isolation. Local provider/cache lookup, GitLab preference, suppression/legacy fallback, explicit-PR authority, physical-versus-runtime review isolation, and branchless-workspace alarms are intentionally removed. No equivalent replacement coverage is claimed. Historical receipts and commands remain unchanged and do not certify the reduced source. Historical coverage account: Audit1396 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Audit1308 changes referenced sources. Retained unchanged-source receipts apply only to their unchanged commands; the reduced selectors have no new execution result. Historical coverage account (not a current claim for changed sources): Deterministic candidate-builder contracts inject local and paired-runtime worktrees with the same bare id into the real aggregated tab buckets, including folder workspaces and a paired runtime transporting an SSH-owned worktree. A mirror contract preserves distinct same-id runtime tabs. A paired Electron journey verifies both visible browser rows and both visible simulator rows, then requires each selection to render its owning active tab and update backing host-qualified state. The test-value audit narrowed simulator-palette-search.test.ts to the recency tie, same-id host isolation, ambiguous legacy host rejection and oversized-input getter tripwire. Supplied timestamp, empty-query context ordering, host projection, alias/range, metadata matching, active-tab score, over-token/whitespace and malformed display-name fallback examples were removed. Their separate alarms are lost; retained candidate and activation contracts do not establish equivalent coverage. Historical run commands and results remain historical. The audit also removed the standalone workspace-tab-palette-search suite: supplied row, title, metadata, alias, range, ranking and ownership examples no longer provide their separate alarms. Other retained contracts are not replacement coverage. The mirror-identity fixture now retains same-id sibling-runtime isolation and the repeated-frame subscriber tripwire only. Supplied field, owner, alias and mapping examples are removed; their separate alarms are lost. The retained sibling-runtime case is not equivalent coverage for them. The test-value audit removed tabs-open-close-lifecycle host-stamping examples and ordinary tab/group operations from tabs-pane-layout-operations. Local-null, explicit-runtime and SSH-without-active-host stamp assertions are lost; retained mobile/MRU and subscriber/atomic split cases do not replace them. Candidate and live Cmd-J evidence retains its own scope. Historical commands, outcomes and narratives remain historical. Audit1308: src/renderer/src/lib/browser-palette-search.test.ts removed. Browser-palette token and pasted-query size-guard work alarms disappear. No equivalent replacement coverage is claimed. Audit1386: src/renderer/src/lib/open-mobile-emulator-tab.test.ts reduced. Duplicate attach count and null-tab creation retry/guard reset standalone alarms removed; no simulator device used. No equivalent replacement coverage is claimed. Existing exact unaffected receipts and their platform/provider/budget claims remain scoped to unchanged selectors. Changed optional selectors have no new execution result. Audit1396: src/renderer/src/lib/simulator-palette-search.test.ts reduced. Lose simulator-specific lastFocusedAt tie-order alarm; identity/routing and pre-scan size fence remain. No native simulator or real remote launch claim. No equivalent replacement coverage is claimed. Audit1402: src/renderer/src/runtime/web-session-tabs-sync-mirror-identity.test.ts reduced. Loses repeated no-op frame store-notification budget; independent cross-runtime resource preservation remains. No equivalent replacement coverage is claimed. Existing exact unaffected receipts remain unchanged and apply only to their unchanged selectors. Changed optional selectors have no new execution result.", "motivatingLinks": [ "https://linear.app/stably/issue/STA-4965", "https://github.com/stablyai/orca/pull/15371" @@ -2571,6 +2571,7 @@ "Add a live paired-runtime-over-SSH journey when the E2E harness can provision that combined topology." ], "knownGaps": [ + "Audit2729 retires browser palette standalone paired-runtime SSH alias forwarding, multigroup-view acceptance and remote-folder forwarding alarms without equivalent replacement. The retained partial suite has no fresh execution result; existing exact receipts and budgets stay scoped to their unchanged selectors.", "Audit2697/2702 intentional palette coverage losses: 'omits a browser row whose backing tab id is duplicated': Remove malformed duplicate unified-tab-ID fixture and its empty result. Real duplicate-ID suppression alarm is lost; this narrow private inventory has no palette selection or activation flow. 'omits a workspace claimed by more than one unified browser tab': Remove two unified browser claims of one workspace. Real first-indexed-foreign-versus-later-owned collision rejection alarm is lost; same-host and ambiguous legacy cases do not replace it. 'omits a workspace whose single unified browser tab is not owned by this worktree': Remove one unique workspace backed by foreign-worktree unified tab fixture. Real foreign-tab/worktree rejection alarm is lost; stamped host tests are not equivalent to foreign worktree mismatch. 'indexes workspace tabs when building a large browser palette': Remove internal getter read budget over1000workspaces: current main policy explicitly rejects getter/Proxy/read counts and says performance changes get correctness tests only. Loses direct quadratic-indexing cost alarm and positive1000workspace output check; no equivalent replacement claimed. The same source review also deletes browser-link-profile-isolation.test.ts (two authored/five expanded) and removes two HTTP-routing declarations (three expanded). Exact tracked-tree filename/case search finds no active gate selector/assertionRef for those navigation sources; their local profile isolation/reuse, deferred minted-profile privacy/lifetime, foreground view/group projection and contextless must-create-page alarms still disappear without replacement. This palette gate is not credited with coverage of those unrelated navigation cases. Historical shard timings are retained unchanged. Audit2697 browser source review (metadata followup2702): Reduce src/renderer/src/lib/browser/palette/browser-palette-page-entries.test.ts by four authored/expanded cases without replacement. The remaining three cases preserve only their existing same-ID execution-host separation, absent stamped-owner refusal and ambiguous legacy-bucket refusal scope. Intentionally lose duplicate backing-tab-ID suppression, multiple unified-browser claims of one workspace, foreign-worktree unified-tab rejection, and the 1000-workspace output/internal getter-read budget alarm. These are real removed alarms; the retained host cases are not equivalent replacements. Current test-value policy rejects internal getter/read budgets; no replacement performance or measured-cost claim is made. Keep existing nonempty selectors and the retained same-ID browser assertionRef. No fresh gate execution or reduced-source validation is supplied; dated receipts, results, commands and historical coverage accounts remain within their original source scope. No live browser activation, physical provider, mixed-version execution, mobile, native UI, platform hardware, soak or timing result is added.", "Live Linux, Windows, and paired-runtime-over-SSH journeys are not collected; the SSH/runtime dual-owner topology is deterministic renderer coverage.", "The paired Electron simulator path seeds renderer state because remote web clients cannot launch a native emulator.", diff --git a/src/main/claude/claude-informational-row.test.ts b/src/main/claude/claude-informational-row.test.ts deleted file mode 100644 index 8bd8ff68d62..00000000000 --- a/src/main/claude/claude-informational-row.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { mkdtemp, rm } from 'node:fs/promises' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { AgentSessionJournalIdentity } from '../../shared/agent-session/journal/agent-session-journal-identity' -import { openAgentSessionJournal } from '../native-chat/agent-session-journal/journal-store-factory' -import { createDeferredStructuredAgentSessionEventSink } from '../native-chat/agent-session-wire/journal-ingress/structured-agent-session-event-sink' -import { createClaudeJournalTranslator } from './structured/journal/claude-structured-journal-translation' -import { - closeTestJournalHostDatabases, - openTestJournalHostDatabase -} from '../native-chat/agent-session-journal/store-lifecycle/journal-host-database-test-support' -import { testEventSinkLogging } from '../native-chat/agent-session-wire/host/testing/structured-agent-session-logger-test-support' -import { claudeProviderHandle } from '../../shared/agent-session/lifecycle/agent-session-provider-handle-encoding' - -const IDENTITY: AgentSessionJournalIdentity = { - sessionId: 'session-1', - workspaceId: 'workspace-1', - hostId: 'host-1', - agent: 'claude', - providerHandle: claudeProviderHandle('provider-1', 'leaf-1') -} - -/** Informational frame shape from CLI 2.1.280. */ -function informational( - level: 'info' | 'notice' | 'suggestion' | 'warning', - content: string -): Record { - return { - type: 'system', - subtype: 'informational', - content, - level, - uuid: `5d3c1f0e-7a2b-4c6d-9e8f-00000000000${level.length}`, - session_id: 'provider-1' - } -} - -let root = '' - -beforeEach(async () => { - root = await mkdtemp(join(tmpdir(), 'orca-claude-informational-')) -}) - -afterEach(async () => { - await closeTestJournalHostDatabases() - await rm(root, { recursive: true, force: true }) -}) - -async function itemsFor(frames: Record[]) { - const journal = await openAgentSessionJournal({ - identity: IDENTITY, - database: openTestJournalHostDatabase(root), - now: () => 1_700_000_000_000, - mintEpoch: () => 'epoch-1' - }) - const deferred = createDeferredStructuredAgentSessionEventSink(testEventSinkLogging()) - deferred.bind({ journal, fence: 1, publish: vi.fn() }) - const translator = createClaudeJournalTranslator({ sink: deferred.sink, fallbackIdPrefix: '1' }) - for (const frame of frames) { - translator.handle({ type: 'message', sessionId: 'orca-session', message: frame }) - await deferred.drained() - } - return journal.snapshot().items.map((item) => item.body) -} - -describe('a Claude informational frame', () => { - it.each(['info', 'notice', 'suggestion'] as const)('writes no row at level %s', async (level) => { - expect(await itemsFor([informational(level, `A ${level} note`)])).toEqual([]) - }) - - it('writes its own words as one warning row at level warning', async () => { - const content = - 'UserPromptSubmit operation blocked by hook: secrets are not allowed\n\nOriginal prompt: deploy it' - const items = await itemsFor([informational('warning', content)]) - - expect(items).toEqual([{ kind: 'status', tone: 'warning', text: content }]) - }) -}) diff --git a/src/main/claude/claude-model-catalog-probe.test.ts b/src/main/claude/claude-model-catalog-probe.test.ts index 8cb9dac7e30..e807112bf26 100644 --- a/src/main/claude/claude-model-catalog-probe.test.ts +++ b/src/main/claude/claude-model-catalog-probe.test.ts @@ -55,7 +55,6 @@ describe('claude model catalog probe', () => { it.each([ [[], [], ['/orca-probe', '/orca-probe'], 'user,project,local'], [['node_modules/claude/cli.js'], [], ['/chat-a', '/chat-b'], 'user'], - [['--env-file=work env', '--', 'claude'], [], ['/chat-a', '/chat-b'], 'user'], [[], ['--settings', 'settings.json'], ['/chat-a', '/chat-b'], 'user'] ])( 'lists prefix %j with arguments %j from %j, reading %s settings', diff --git a/src/main/claude/structured/claude-structured-execution-recording.test.ts b/src/main/claude/structured/claude-structured-execution-recording.test.ts index bc2b1f50b73..b20f110b9e6 100644 --- a/src/main/claude/structured/claude-structured-execution-recording.test.ts +++ b/src/main/claude/structured/claude-structured-execution-recording.test.ts @@ -14,7 +14,7 @@ import { HOST_TEST_SESSION as SESSION } from '../../native-chat/agent-session-wi const claudeTurnEnds = (connection: Parameters[0]) => claudeFrame(connection, { type: 'system', subtype: 'session_state_changed', state: 'idle' }) -it.each(['low', 'high', undefined])( +it.each(['high', undefined])( 'admits accepted high independently of resolved effort (effective %s)', async (effective) => { const models = [ diff --git a/src/main/claude/structured/launch/claude-structured-applied-options.test.ts b/src/main/claude/structured/launch/claude-structured-applied-options.test.ts index ac112bb968c..07b4c3e57a3 100644 --- a/src/main/claude/structured/launch/claude-structured-applied-options.test.ts +++ b/src/main/claude/structured/launch/claude-structured-applied-options.test.ts @@ -46,16 +46,6 @@ const SETTINGS = { effective: { model: 'haiku' }, sources: [{ source: 'userSettings', settings: { model: 'haiku' } }], applied: { model: 'claude-haiku-4-5-20251001', effort: null, advisor: null, ultracode: false } - }, - envSonnet: { - effective: {}, - sources: [], - applied: { model: 'claude-sonnet-5', effort: 'high', advisor: null, ultracode: false } - }, - settingsHaikuEnvSonnet: { - effective: { model: 'haiku' }, - sources: [{ source: 'userSettings', settings: { model: 'haiku' } }], - applied: { model: 'claude-sonnet-5', effort: 'high', advisor: null, ultracode: false } } } @@ -69,29 +59,7 @@ function startedWithoutATurn(settings: unknown) { }) } -async function readCurrent(settings: unknown) { - const adapter = await acquired(startedWithoutATurn(settings)) - return (await adapter.readOptions({ sessionId: 'session-1', fence: 7 })).current -} - describe('Claude model before the first turn', () => { - it.each([ - ['nothing overrides the default', SETTINGS.noOverride, 'claude-opus-5-5[1m]', 'medium'], - ['settings name a model', SETTINGS.settingsHaiku, 'claude-haiku-4-5-20251001', undefined], - ['the env names a model', SETTINGS.envSonnet, 'claude-sonnet-5', 'high'], - ['the env overrides the settings', SETTINGS.settingsHaikuEnvSonnet, 'claude-sonnet-5', 'high'] - ])( - 'names what Claude will apply when %s, unconfirmed', - async (_case, settings, model, effort) => { - const current = await readCurrent(settings) - - expect(current.model).toBe(model) - expect(current.effort).toBe(effort) - expect(current.confirmed ?? []).not.toContain('model') - expect(current.confirmed ?? []).not.toContain('effort') - } - ) - it('uses native applied effort despite settings High, before and after a reply', async () => { // Claude Code 2.1.318: no flag applies Medium; --effort low applies Low. const claude = startedWithoutATurn({ @@ -156,13 +124,6 @@ describe('Claude model before the first turn', () => { await adapter.closeAll() }) - it('marks an unknown applied model as defaulted for the host to resolve', async () => { - const current = await readCurrent({ effective: {}, sources: [] }) - - expect(current.defaulted).toEqual(['model']) - expect(current.effort).toBeUndefined() - }) - it('lets the first turn confirm or correct it', async () => { const claude = startedWithoutATurn(SETTINGS.settingsHaiku) const adapter = await acquired(claude) diff --git a/src/main/claude/structured/launch/claude-structured-child-env.test.ts b/src/main/claude/structured/launch/claude-structured-child-env.test.ts index aff78f757a7..18c73fc32e0 100644 --- a/src/main/claude/structured/launch/claude-structured-child-env.test.ts +++ b/src/main/claude/structured/launch/claude-structured-child-env.test.ts @@ -3,7 +3,6 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, expect, it, vi } from 'vitest' import { - claudeChildEnv, resolveClaudeChildEnvSources, withOrcaClaudeEntrypoint } from './claude-structured-child-env' @@ -32,21 +31,6 @@ it('resolves a custom Claude command against the login shell PATH', async () => } }) -it('names Orca as the Claude client unless the launch environment sets one', () => { - const sources = { - command: 'claude', - prefixArgs: [], - overlay: undefined, - inheritedEnv: { CLAUDE_CODE_ENTRYPOINT: 'cli' } - } - const options = { runtimeOnPath: false } - expect(claudeChildEnv(sources, undefined, options).CLAUDE_CODE_ENTRYPOINT).toBe('orca') - const overlay = { CLAUDE_CODE_ENTRYPOINT: 'custom' } - expect(claudeChildEnv({ ...sources, overlay }, undefined, options).CLAUDE_CODE_ENTRYPOINT).toBe( - 'custom' - ) -}) - it('keeps a differently cased Windows launch value over the Orca default', () => { const inherited = { Claude_Code_Entrypoint: 'cli', PATH: 'C:\\bin' } expect( diff --git a/src/main/codex/structured/catalog/codex-model-catalog-probe.test.ts b/src/main/codex/structured/catalog/codex-model-catalog-probe.test.ts index d3fb98dccf4..47779af453c 100644 --- a/src/main/codex/structured/catalog/codex-model-catalog-probe.test.ts +++ b/src/main/codex/structured/catalog/codex-model-catalog-probe.test.ts @@ -15,12 +15,7 @@ const MODEL_ROW = { } describe('codex model catalog probe', () => { - it.each([ - [[]], - [['node_modules/@openai/codex/bin/codex.js']], - [['--env-file=work env', '--', 'codex']], - [['-c', 'model_instructions_file="./prompt.md"']] - ])( + it.each([[[]]])( "lists a command line with arguments %j from Orca's folder for every chat", async (prefixArgs) => { // The chat's repo never picks the list, so no listing runs in a chat's folder. diff --git a/src/main/rate-limits/providers/codex/codex-fetcher-auth-errors.test.ts b/src/main/rate-limits/providers/codex/codex-fetcher-auth-errors.test.ts deleted file mode 100644 index 3561c7344a8..00000000000 --- a/src/main/rate-limits/providers/codex/codex-fetcher-auth-errors.test.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { EventEmitter } from 'node:events' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' - -const { childSpawnMock, resolveCodexCommandMock, ptySpawnMock } = vi.hoisted(() => ({ - childSpawnMock: vi.fn(), - resolveCodexCommandMock: vi.fn(), - ptySpawnMock: vi.fn() -})) - -// Keep child-process assertions in this suite; worker transport is covered separately. -vi.mock( - '../../../worker-thread-child-process/worker-thread-child-process', - () => - import('../../../worker-thread-child-process/worker-thread-child-process-in-process.test-fixtures') -) -vi.mock('node:child_process', () => ({ - spawn: childSpawnMock -})) - -// Mock recovery so its process-table import does not require real execFile. -vi.mock('../../../codex/history/index/codex-state-db-backfill-recovery', () => ({ - startCodexStateDbBackfillRecoveryInBackground: vi.fn() -})) - -vi.mock('../../../codex-cli/command', () => ({ - resolveCodexCommand: resolveCodexCommandMock -})) - -vi.mock('node-pty', () => ({ - spawn: ptySpawnMock -})) - -vi.mock('./codex-auth-presence', () => ({ - probeCodexAuthPresence: vi.fn(() => 'present') -})) - -import { fetchCodexRateLimits } from './codex-fetcher' - -function makeRpcChild() { - const child = new EventEmitter() as EventEmitter & { - stdout: EventEmitter - stderr: EventEmitter - stdin: EventEmitter & { write: ReturnType; end: ReturnType } - kill: ReturnType - exitCode: number | null - } - child.stdout = new EventEmitter() - child.stderr = new EventEmitter() - // Graceful shutdown must wait for EOF/signal to produce exit, as real app-server does. - const exitNow = (): void => { - child.exitCode = 0 - child.emit('exit', 0, null) - } - child.stdin = Object.assign(new EventEmitter(), { write: vi.fn(), end: vi.fn(exitNow) }) - child.exitCode = null - child.kill = vi.fn(() => { - exitNow() - return true - }) - return child -} - -describe('fetchCodexRateLimits auth errors', () => { - beforeEach(() => { - vi.useFakeTimers() - vi.clearAllMocks() - resolveCodexCommandMock.mockReturnValue('codex') - vi.stubGlobal('fetch', vi.fn()) - }) - - afterEach(() => { - vi.unstubAllGlobals() - }) - - it('returns Codex RPC auth refresh errors without masking them behind a fallback', async () => { - const rpcChild = makeRpcChild() - const authError = - 'Your access token could not be refreshed because your refresh token was already used. Please log out and sign in again.' - - childSpawnMock.mockReturnValue(rpcChild) - rpcChild.stdin.write.mockImplementation((line: string) => { - const msg = JSON.parse(line) as { id?: number; method?: string } - if (msg.method === 'initialize') { - setTimeout(() => { - rpcChild.stdout.emit( - 'data', - Buffer.from(`${JSON.stringify({ jsonrpc: '2.0', id: msg.id, result: {} })}\n`) - ) - }, 0) - } - if (msg.method === 'account/rateLimits/read') { - setTimeout(() => { - rpcChild.stdout.emit( - 'data', - Buffer.from( - `${JSON.stringify({ - jsonrpc: '2.0', - id: msg.id, - error: { code: -32000, message: authError } - })}\n` - ) - ) - }, 0) - } - }) - - const resultPromise = fetchCodexRateLimits() - await vi.advanceTimersByTimeAsync(1) - await vi.advanceTimersByTimeAsync(1) - - await expect(resultPromise).resolves.toMatchObject({ - provider: 'codex', - session: null, - weekly: null, - status: 'error', - error: authError - }) - expect(fetch).not.toHaveBeenCalled() - expect(ptySpawnMock).not.toHaveBeenCalled() - }) - - it('returns the app-server chatgpt-auth-required error without falling back', async () => { - const rpcChild = makeRpcChild() - const authError = 'chatgpt authentication required to read rate limits' - - childSpawnMock.mockReturnValue(rpcChild) - rpcChild.stdin.write.mockImplementation((line: string) => { - const msg = JSON.parse(line) as { id?: number; method?: string } - if (msg.method === 'initialize') { - setTimeout(() => { - rpcChild.stdout.emit( - 'data', - Buffer.from(`${JSON.stringify({ jsonrpc: '2.0', id: msg.id, result: {} })}\n`) - ) - }, 0) - } - if (msg.method === 'account/rateLimits/read') { - setTimeout(() => { - rpcChild.stdout.emit( - 'data', - Buffer.from( - `${JSON.stringify({ - jsonrpc: '2.0', - id: msg.id, - error: { code: -32600, message: authError } - })}\n` - ) - ) - }, 0) - } - }) - - const resultPromise = fetchCodexRateLimits() - await vi.advanceTimersByTimeAsync(1) - await vi.advanceTimersByTimeAsync(1) - - await expect(resultPromise).resolves.toMatchObject({ - provider: 'codex', - session: null, - weekly: null, - status: 'error', - error: authError - }) - expect(fetch).not.toHaveBeenCalled() - expect(ptySpawnMock).not.toHaveBeenCalled() - }) -}) diff --git a/src/main/rate-limits/providers/codex/codex-fetcher-process-contract.test.ts b/src/main/rate-limits/providers/codex/codex-fetcher-process-contract.test.ts deleted file mode 100644 index 58dedbdcc78..00000000000 --- a/src/main/rate-limits/providers/codex/codex-fetcher-process-contract.test.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { mkdtempSync, rmSync, writeFileSync } from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type * as RunProcess from '@orca/process-host' - -const { ptySpawnMock, resolveCodexCommandMock, stubScript } = vi.hoisted(() => ({ - stubScript: { path: '' }, - ptySpawnMock: vi.fn(), - resolveCodexCommandMock: vi.fn() -})) - -// Run the Node fixture through the real process chokepoint; worker transport has separate coverage. -vi.mock( - '../../../worker-thread-child-process/worker-thread-child-process', - () => - import('../../../worker-thread-child-process/worker-thread-child-process-in-process.test-fixtures') -) -vi.mock('@orca/process-host', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - spawnProcess: (spec: Parameters[0]) => - actual.spawnProcess({ - ...spec, - program: process.execPath, - args: [stubScript.path, ...(spec.args ?? [])] - }) - } -}) - -vi.mock('../../../codex-cli/command', () => ({ - resolveCodexCommand: resolveCodexCommandMock -})) - -vi.mock('node-pty', () => ({ - spawn: ptySpawnMock -})) - -vi.mock('./codex-auth-presence', () => ({ - probeCodexAuthPresence: vi.fn(() => 'present') -})) - -vi.mock('../../../codex/history/index/codex-state-db', () => ({ - isCodexStateDbBackfillPending: vi.fn(() => false) -})) - -vi.mock('../../../codex/history/index/codex-state-db-backfill-recovery', () => ({ - startCodexStateDbBackfillRecoveryInBackground: vi.fn(() => Promise.resolve(null)) -})) - -import { fetchCodexRateLimits } from './codex-fetcher' - -const STUB_CODEX_SOURCE = ` -const expectedArgs = [ - '-c', - 'approval_policy=never', - '-c', - 'features.plugins=false', - '-s', - 'read-only', - '-a', - 'never', - 'app-server' -] -if (JSON.stringify(process.argv.slice(2)) !== JSON.stringify(expectedArgs)) { - process.stderr.write( - "error: invalid value 'untrusted' for '--ask-for-approval '\\n" + - ' [possible values: on-request, never]\\n' - ) - process.exit(2) -} -if (process.env.CODEX_HOME !== process.env.ORCA_EXPECTED_CODEX_HOME) { - process.stderr.write('managed CODEX_HOME was not preserved\\n') - process.exit(3) -} -if (process.env.ORCA_EXPECTED_PROXY_ENV) { - for (const [key, value] of Object.entries(JSON.parse(process.env.ORCA_EXPECTED_PROXY_ENV))) { - if (process.env[key] !== value) { - process.stderr.write('probe proxy environment mismatch: ' + key + '\\n') - process.exit(4) - } - } -} -let buffer = '' -function send(message) { - process.stdout.write(JSON.stringify(message) + '\\n') -} -process.stdin.setEncoding('utf8') -process.stdin.on('data', (chunk) => { - buffer += chunk - let newlineIndex - while ((newlineIndex = buffer.indexOf('\\n')) !== -1) { - const line = buffer.slice(0, newlineIndex).trim() - buffer = buffer.slice(newlineIndex + 1) - if (!line) continue - const message = JSON.parse(line) - if (message.method === 'initialize') { - send({ id: message.id, result: { userAgent: 'codex-contract/0.149.0' } }) - continue - } - if (message.method === 'account/rateLimits/read') { - send({ - id: message.id, - result: { - rateLimits: { - primary: { usedPercent: 17, windowDurationMins: 300 }, - secondary: { usedPercent: 29, windowDurationMins: 10080 } - } - } - }) - } - } -}) -process.stdin.on('end', () => process.exit(0)) -` - -let tempRoot: string -let stubPath: string -let previousExpectedHome: string | undefined - -describe('Codex rate-limit process contract', () => { - beforeEach(() => { - tempRoot = mkdtempSync(join(tmpdir(), 'orca-codex-rate-limit-contract-')) - stubPath = join(tempRoot, 'codex-contract.cjs') - writeFileSync(stubPath, STUB_CODEX_SOURCE) - previousExpectedHome = process.env.ORCA_EXPECTED_CODEX_HOME - process.env.ORCA_EXPECTED_CODEX_HOME = join(tempRoot, 'managed-codex-home') - resolveCodexCommandMock.mockReturnValue('codex') - stubScript.path = stubPath - }) - - afterEach(() => { - vi.unstubAllEnvs() - if (previousExpectedHome === undefined) { - delete process.env.ORCA_EXPECTED_CODEX_HOME - } else { - process.env.ORCA_EXPECTED_CODEX_HOME = previousExpectedHome - } - rmSync(tempRoot, { recursive: true, force: true }) - vi.clearAllMocks() - }) - - it('starts a read-only non-interactive app-server with the managed home', async () => { - await expect( - fetchCodexRateLimits({ - codexHomePath: process.env.ORCA_EXPECTED_CODEX_HOME - }) - ).resolves.toMatchObject({ - provider: 'codex', - session: { usedPercent: 17, windowMinutes: 300 }, - weekly: { usedPercent: 29, windowMinutes: 10080 }, - status: 'ok', - error: null - }) - expect(ptySpawnMock).not.toHaveBeenCalled() - }) - - it.each(['localhost;*.fixture.invalid', ''])( - 'passes configured proxy and bypass %j to the actual probe', - async (bypassRules) => { - const proxy = 'http://proxy.fixture.invalid:8080' - const expected = { - HTTP_PROXY: proxy, - HTTPS_PROXY: proxy, - ALL_PROXY: proxy, - http_proxy: proxy, - https_proxy: proxy, - all_proxy: proxy, - NO_PROXY: bypassRules.replaceAll(';', ','), - no_proxy: bypassRules.replaceAll(';', ',') - } - vi.stubEnv('NO_PROXY', 'inherited.invalid') - vi.stubEnv('no_proxy', 'inherited.invalid') - vi.stubEnv('ORCA_EXPECTED_PROXY_ENV', JSON.stringify(expected)) - - await expect( - fetchCodexRateLimits({ - codexHomePath: process.env.ORCA_EXPECTED_CODEX_HOME, - networkProxySettings: { httpProxyUrl: proxy, httpProxyBypassRules: bypassRules } - }) - ).resolves.toMatchObject({ status: 'ok', session: { usedPercent: 17 } }) - expect(process.env.NO_PROXY).toBe('inherited.invalid') - } - ) - - it.each([undefined, { httpProxyUrl: '' }, { httpProxyUrl: 'file:///invalid-proxy' }])( - 'preserves inherited proxy settings when app settings are unset or invalid: %j', - async (networkProxySettings) => { - const inherited = { - HTTPS_PROXY: 'http://inherited.fixture.invalid:8080', - NO_PROXY: 'fixture.invalid' - } - for (const [key, value] of Object.entries(inherited)) { - vi.stubEnv(key, value) - } - vi.stubEnv('ORCA_EXPECTED_PROXY_ENV', JSON.stringify(inherited)) - await expect( - fetchCodexRateLimits({ - codexHomePath: process.env.ORCA_EXPECTED_CODEX_HOME, - networkProxySettings - }) - ).resolves.toMatchObject({ status: 'ok', session: { usedPercent: 17 } }) - } - ) -}) diff --git a/src/main/rate-limits/providers/codex/codex-fetcher-rpc-exit-diagnostics.test.ts b/src/main/rate-limits/providers/codex/codex-fetcher-rpc-exit-diagnostics.test.ts index 1cadfd71314..9cc25820685 100644 --- a/src/main/rate-limits/providers/codex/codex-fetcher-rpc-exit-diagnostics.test.ts +++ b/src/main/rate-limits/providers/codex/codex-fetcher-rpc-exit-diagnostics.test.ts @@ -86,29 +86,6 @@ describe('Codex RPC exit diagnostics', () => { vi.clearAllMocks() }) - it('names the exit code and what the child reported', async () => { - const result = await runStub( - "error: invalid value 'untrusted' for '--ask-for-approval '\n" + - " [possible values: on-request, never]\n\nFor more information, try '--help'.\n", - 2 - ) - - expect(result.error).toContain('Codex RPC process exited (exit code 2)') - expect(result.error).toContain("invalid value 'untrusted'") - expect(result.error).toContain("try '--help'") - }) - - it('classifies a dead refresh token so the re-auth warning fires', async () => { - const result = await runStub( - '2026-08-20T18:04:11.221Z INFO codex_core::auth: refreshing ChatGPT tokens\n' + - 'ERROR: The ChatGPT access token could not be refreshed; please sign in again.\n', - 1 - ) - - expect(result.error).toBe('Your ChatGPT session could not be refreshed. Please sign in again.') - expect(isCodexAuthError(result.error)).toBe(true) - }) - it('keeps the re-auth phrase that path redaction would swallow', async () => { const result = await runStub( `ERROR: /Users/haris/.codex/auth.json is stale — please sign in again.\n`, diff --git a/src/main/skills/discovery/claude-plugin-skill-sources-wsl.test.ts b/src/main/skills/discovery/claude-plugin-skill-sources-wsl.test.ts index cf3538343a1..74a81dc0efa 100644 --- a/src/main/skills/discovery/claude-plugin-skill-sources-wsl.test.ts +++ b/src/main/skills/discovery/claude-plugin-skill-sources-wsl.test.ts @@ -6,16 +6,6 @@ function record(...fields: string[]): string { } describe('WSL Claude plugin metadata', () => { - it('decodes present files and retains missing files as null', () => { - const settings = JSON.stringify({ enabledPlugins: { 'plugin@market': true } }) - const output = [ - record('F', '0', '1', Buffer.from(settings).toString('base64')), - record('F', '1', '0', '') - ].join('') - - expect(parseWslClaudePluginMetadataOutput(output, 2)).toEqual([settings, null]) - }) - it('rejects malformed metadata responses', () => { expect(() => parseWslClaudePluginMetadataOutput(record('F', '4', '1', ''), 1)).toThrow( 'invalid response' diff --git a/src/main/skills/package-archive/skill-package-creation.test.ts b/src/main/skills/package-archive/skill-package-creation.test.ts index 5e912d3cb17..bac5070ed31 100644 --- a/src/main/skills/package-archive/skill-package-creation.test.ts +++ b/src/main/skills/package-archive/skill-package-creation.test.ts @@ -1,4 +1,4 @@ -import { chmod, lstat, mkdir, mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises' +import { chmod, mkdir, mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { gunzipSync, gzipSync } from 'node:zlib' @@ -136,33 +136,6 @@ describe('skill package creation and extraction', () => { ).rejects.toThrow('skill-package-tar-truncated') }) - it('cancels during streamed extraction and removes partial bytes', async () => { - const root = await temporaryDirectory() - const created = await createSkillPackageArchive({ - sourceDirectory: await createSkill(root), - archivePath: join(root, 'package.tar.gz'), - packageId: 'package_1', - versionId: 'version_1' - }) - let checks = 0 - const signal = { - get aborted() { - checks += 1 - return checks >= 3 - } - } as AbortSignal - const destinationDirectory = join(root, 'cancelled-extraction') - - await expect( - extractSkillPackageArchive({ - archivePath: created.archivePath, - destinationDirectory, - signal - }) - ).rejects.toThrow('skill-install-cancelled') - await expect(lstat(destinationDirectory)).rejects.toMatchObject({ code: 'ENOENT' }) - }) - it('rejects content checksum and SKILL identity mismatches', async () => { const root = await temporaryDirectory() const created = await createSkillPackageArchive({ diff --git a/src/main/skills/sharing/skill-share-preparation.test.ts b/src/main/skills/sharing/skill-share-preparation.test.ts index 6bd5f82ae54..552409c769c 100644 --- a/src/main/skills/sharing/skill-share-preparation.test.ts +++ b/src/main/skills/sharing/skill-share-preparation.test.ts @@ -69,23 +69,4 @@ describe('SkillSharePreparationService', () => { expect(prepared.skills?.[0]?.executablePaths).toEqual(['run.sh']) }) - - it('enforces the preparation cap while archives are still being created', async () => { - const { root, source } = await createSource() - const service = new SkillSharePreparationService(join(root, 'preparations'), { - findOwnedPackageId: nameIsFree, - publishVersion: vi.fn(), - createShare: vi.fn() - }) - - const results = await Promise.allSettled( - Array.from({ length: 9 }, () => service.prepare({ sourceDirectory: source })) - ) - - expect(results.filter((result) => result.status === 'fulfilled')).toHaveLength(8) - expect(results.filter((result) => result.status === 'rejected')).toHaveLength(1) - expect(results.find((result) => result.status === 'rejected')).toMatchObject({ - reason: new Error('skill-share-preparation-limit') - }) - }) }) diff --git a/src/renderer/src/components/editor/git-graph/git-graph-rendering.test.tsx b/src/renderer/src/components/editor/git-graph/git-graph-rendering.test.tsx index d04bcf20276..f4b13750029 100644 --- a/src/renderer/src/components/editor/git-graph/git-graph-rendering.test.tsx +++ b/src/renderer/src/components/editor/git-graph/git-graph-rendering.test.tsx @@ -102,28 +102,6 @@ it('keeps the branch checkout strip open after selecting a badge context-menu ac expect(container.querySelector('form')?.textContent).toContain('Check out branch topic?') }) -it('renders converging branch lanes and a detached root without duplicate child keys', async () => { - const errors = vi.spyOn(console, 'error').mockImplementation(() => {}) - getHistory.mockReset().mockResolvedValue({ - ...result, - items: [ - { id: 'a', subject: 'a', message: 'a', parentIds: ['root'] }, - { id: 'b', subject: 'b', message: 'b', parentIds: ['root'] }, - { id: 'c', subject: 'c', message: 'c', parentIds: ['root'] }, - { id: 'detached', subject: 'detached', message: 'detached', parentIds: [] }, - { id: 'root', subject: 'root', message: 'root', parentIds: [] } - ] - }) - await act(async () => - root.render( - - - - ) - ) - expect(errors.mock.calls.filter(([message]) => String(message).includes('same key'))).toEqual([]) -}) - it('recovers silently after each later token expiry and ignores refreshes for other worktrees', async () => { let history: ReturnType function History() { diff --git a/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRun.appearance.test.tsx b/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRun.appearance.test.tsx index 7228e7622d9..8d491592066 100644 --- a/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRun.appearance.test.tsx +++ b/src/renderer/src/components/native-chat/tools/runs/NativeChatToolRun.appearance.test.tsx @@ -18,9 +18,6 @@ import { useNativeChatDisclosures } from '../../messages/disclosure/native-chat-disclosure-store' -import { i18n } from '@/i18n/i18n' -import { TranslationScopeProvider } from '@/i18n/translation-scope-provider' - { const QUESTION = 'What would you like me to do next in this repo?' const ASK_INPUT = { questions: [{ question: QUESTION }] } @@ -288,66 +285,3 @@ import { TranslationScopeProvider } from '@/i18n/translation-scope-provider' }) }) } - -{ - function runHeader(container: HTMLElement): HTMLElement { - const header = container.querySelector('button') - if (!header) { - throw new Error('run header did not render') - } - return header - } - - describe('tool run sentences', () => { - afterEach(async () => { - cleanup() - await i18n.changeLanguage('en') - }) - - it('relabels an already-mounted run and its rows when the UI language changes', async () => { - const blocks: NativeChatBlock[] = [ - { type: 'tool-call', name: 'Bash', input: { command: 'false' }, state: 'failed' }, - { type: 'tool-result', output: 'exit 1', isError: true }, - { - type: 'tool-call', - name: 'Agent', - input: { description: 'explore settings' }, - state: 'completed' - } - ] - const { container } = render( - , - { wrapper: TranslationScopeProvider } - ) - const header = runHeader(container) - expect(header).toHaveTextContent('Ran 1 command and ran 1 agent · 1 failed') - expect(screen.getByText('Subagent')).toBeInTheDocument() - expect(screen.getByText('Background workflow')).toBeInTheDocument() - expect(screen.getByText('Output: /tmp/result.txt')).toBeInTheDocument() - - await act(async () => { - await i18n.changeLanguage('es') - }) - - expect(header).toHaveTextContent('Se ejecutó 1 comando y se ejecutó 1 agente · 1 fallidas') - expect(header).toHaveAccessibleName(/Llamadas a herramientas fallidas: 1/) - expect(screen.getByText('Subagente')).toBeInTheDocument() - expect(screen.getByText('Flujo de trabajo en segundo plano')).toBeInTheDocument() - expect(screen.getByText('Salida: /tmp/result.txt')).toBeInTheDocument() - expect(screen.getByText('bloqueado · con error')).toBeInTheDocument() - }) - }) -} diff --git a/src/renderer/src/components/settings/developer-permissions/developer-permissions-search.test.ts b/src/renderer/src/components/settings/developer-permissions/developer-permissions-search.test.ts deleted file mode 100644 index ffb180ce1dc..00000000000 --- a/src/renderer/src/components/settings/developer-permissions/developer-permissions-search.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { afterAll, describe, expect, it } from 'vitest' - -import { i18n } from '@/i18n/i18n' -import { getDeveloperPermissionsPaneSearchEntries } from './developer-permissions-search' -import { matchesSettingsSearch } from '../search/settings-search' - -// Use macOS 26’s actual Local Network translations (SecurityPrivacyExtension LOCAL_NETWORK). -const MACOS_LOCAL_NETWORK_LABEL = { - en: 'local network', - es: 'red local', - ja: 'ローカルネットワーク', - ko: '로컬 네트워크', - zh: '本地网络' -} as const - -function getLanEntry() { - const entry = getDeveloperPermissionsPaneSearchEntries().find((candidate) => - candidate.keywords?.includes('usb') - ) - if (!entry) { - throw new Error('LAN, USB, and Bluetooth search entry is missing') - } - return entry -} - -async function searchInLocale(locale: string, query: string): Promise { - await i18n.changeLanguage(locale) - return matchesSettingsSearch(query, getLanEntry()) -} - -describe('developer permissions LAN search', () => { - afterAll(async () => { - await i18n.changeLanguage('en') - }) - - it.each(Object.entries(MACOS_LOCAL_NETWORK_LABEL))( - 'finds the LAN row from the macOS Local Network wording in %s', - async (locale, label) => { - expect(await searchInLocale(locale, label)).toBe(true) - } - ) - - it('keeps the English LAN aliases findable in every locale', async () => { - for (const locale of Object.keys(MACOS_LOCAL_NETWORK_LABEL)) { - for (const query of ['lan', 'local network', 'local-network']) { - expect(await searchInLocale(locale, query)).toBe(true) - } - } - }) - - it('keeps the localized LAN wording findable', async () => { - expect(await searchInLocale('zh', '局域网')).toBe(true) - expect(await searchInLocale('ja', 'LAN')).toBe(true) - expect(await searchInLocale('ko', 'LAN')).toBe(true) - expect(await searchInLocale('es', 'LAN')).toBe(true) - }) -}) diff --git a/src/renderer/src/components/settings/shortcuts/keybinding-title-copy.test.ts b/src/renderer/src/components/settings/shortcuts/keybinding-title-copy.test.ts deleted file mode 100644 index ae7fe38db06..00000000000 --- a/src/renderer/src/components/settings/shortcuts/keybinding-title-copy.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { afterEach, describe, expect, it } from 'vitest' -import { - agentTabActionId, - getKeybindingDefinition -} from '../../../../../shared/keybindings/definitions' -import type { - KeybindingActionId, - KeybindingDefinition -} from '../../../../../shared/keybindings/types' -import { i18n } from '@/i18n/i18n' -import { localizeKeybindingDefinition } from './keybinding-title-copy' - -function definition(actionId: KeybindingActionId): KeybindingDefinition { - const found = getKeybindingDefinition(actionId) - if (!found) { - throw new Error(`missing ${actionId}`) - } - return found -} - -describe('localizeKeybindingDefinition', () => { - afterEach(async () => { - await i18n.changeLanguage('en') - }) - - it('localizes built-in titles, keeps English searchable, and leaves plugin titles alone', async () => { - await i18n.changeLanguage('ko') - const quickOpen = definition('worktree.quickOpen') - - const localized = localizeKeybindingDefinition(quickOpen) - expect(localized.title).not.toBe(quickOpen.title) - expect(localized.group).not.toBe(quickOpen.group) - expect(localized.searchKeywords).toEqual( - expect.arrayContaining([quickOpen.title, quickOpen.group]) - ) - const agentTab = definition(agentTabActionId('claude')) - const agentTitle = localizeKeybindingDefinition(agentTab).title - expect(agentTitle).not.toBe(agentTab.title) - expect(agentTitle).toContain('Claude') - - const plugin: KeybindingDefinition = { - ...quickOpen, - id: 'plugin:orca-samples.tasks/open', - title: 'Open Tasks — Tasks', - group: 'Plugins' - } - expect(localizeKeybindingDefinition(plugin)).toBe(plugin) - }) -}) diff --git a/src/renderer/src/components/settings/terminal/TerminalInteractionSection.test.tsx b/src/renderer/src/components/settings/terminal/TerminalInteractionSection.test.tsx deleted file mode 100644 index 710397a6731..00000000000 --- a/src/renderer/src/components/settings/terminal/TerminalInteractionSection.test.tsx +++ /dev/null @@ -1,49 +0,0 @@ -// @vitest-environment happy-dom - -import { cleanup, render, screen } from '@testing-library/react' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { createGlobalSettingsFixture } from '../../../../../shared/global-settings-test-fixture' -import { useAppStore } from '../../../store' -import { TerminalInteractionSection } from './TerminalInteractionSection' - -const i18nMock = vi.hoisted(() => { - const translations: Record = {} - return { language: 'en', translations } -}) - -vi.mock('@/i18n/i18n', () => ({ - translate: (key: string, defaultValue: string) => i18nMock.translations[key] ?? defaultValue, - i18n: i18nMock -})) - -vi.mock('@/i18n/localized-catalog', () => ({ - createLocalizedCatalog: (factory: () => unknown) => factory -})) - -describe('TerminalInteractionSection scroll on input', () => { - beforeEach(() => { - useAppStore.setState({ settingsSearchQuery: '' }) - }) - - afterEach(() => { - cleanup() - i18nMock.language = 'en' - i18nMock.translations = {} - }) - - it('keeps the row visible for a localized search keyword', () => { - i18nMock.language = 'de' - i18nMock.translations = { 'components.settings.terminal.search.snap': 'einrasten' } - useAppStore.setState({ settingsSearchQuery: 'einrasten' }) - - render( - - ) - - expect(screen.getByRole('switch', { name: 'Scroll to Bottom on Input' })).toBeTruthy() - }) -}) diff --git a/src/renderer/src/lib/browser/palette/browser-page-palette-activation.test.ts b/src/renderer/src/lib/browser/palette/browser-page-palette-activation.test.ts index 8cacc1dcb90..3ccabf528a7 100644 --- a/src/renderer/src/lib/browser/palette/browser-page-palette-activation.test.ts +++ b/src/renderer/src/lib/browser/palette/browser-page-palette-activation.test.ts @@ -2,12 +2,10 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import type { BrowserPage, BrowserWorkspace } from '../../../../../shared/browser-workspace-types' -import type { FolderWorkspace } from '../../../../../shared/folder-workspaces/folder-workspace-types' import type { Tab, TabGroup } from '../../../../../shared/tab-types' import type { Worktree } from '../../../../../shared/worktree/types' import '../../../../../shared/constants' import { ORCA_BROWSER_BLANK_URL } from '../../../../../shared/browser/browser-blank-page' -import { folderWorkspaceKey } from '../../../../../shared/workspace-scope' import { useAppStore } from '@/store/index' import type { AppState } from '@/store/types' @@ -44,25 +42,6 @@ function makeWorktree(overrides: Partial = {}): Worktree { } } -function makeFolderWorkspace(overrides: Partial = {}): FolderWorkspace { - return { - id: 'folder-1', - projectGroupId: 'project-1', - name: 'Remote folder', - folderPath: '/workspace/folder-1', - linkedTask: null, - comment: '', - isArchived: false, - isUnread: false, - isPinned: false, - sortOrder: 0, - lastActivityAt: 0, - createdAt: 0, - updatedAt: 0, - ...overrides - } -} - function makeWorkspace(overrides: Partial = {}): BrowserWorkspace { return { id: 'ws-1', @@ -150,26 +129,6 @@ describe('activateBrowserPagePaletteResult', () => { seedStore() }) - it('activates an SSH worktree through its paired-runtime owner alias', () => { - seedStore({ - worktreesByRepo: { - 'repo-1': [ - makeWorktree({ - hostId: 'ssh:private-target', - runtimeOwnerEnvironmentId: 'paired-host' - }) - ] - } - }) - - expect( - activateBrowserPagePaletteResult({ ...target, executionHostId: 'runtime:paired-host' }).status - ).toBe('activated') - expect(mocks.activateAndRevealWorkspace).toHaveBeenCalledWith('wt-1', { - executionHostId: 'runtime:paired-host' - }) - }) - it('rejects colliding child ids before mutating either host', () => { seedStore({ worktreesByRepo: { @@ -196,41 +155,6 @@ describe('activateBrowserPagePaletteResult', () => { expect(mocks.activateAndRevealWorkspace).not.toHaveBeenCalled() }) - it('keeps browser workspaces with distinct unified tabs in multiple groups activatable', () => { - seedStore({ - unifiedTabsByWorktree: { - 'wt-1': [makeBrowserTab(), makeBrowserTab({ id: 'second-view', groupId: 'group-2' })] - }, - groupsByWorktree: { - 'wt-1': [ - makeGroup(), - makeGroup({ id: 'group-2', activeTabId: 'second-view', tabOrder: ['second-view'] }) - ] - } - }) - expect(activateBrowserPagePaletteResult(target).status).toBe('activated') - }) - - it('activates pages in remote folder workspaces', () => { - const worktreeId = folderWorkspaceKey('folder-1') - seedStore({ - worktreesByRepo: {}, - folderWorkspaces: [makeFolderWorkspace({ executionHostId: 'ssh:host-1' })], - browserTabsByWorktree: { [worktreeId]: [makeWorkspace({ worktreeId })] }, - browserPagesByWorkspace: { 'ws-1': [makePage({ worktreeId })] }, - unifiedTabsByWorktree: { [worktreeId]: [makeBrowserTab({ worktreeId })] }, - groupsByWorktree: { [worktreeId]: [makeGroup({ worktreeId })] }, - activeGroupIdByWorktree: { [worktreeId]: 'group-1' } - }) - - expect( - activateBrowserPagePaletteResult({ pageId: 'page-1', workspaceId: 'ws-1', worktreeId }) - ).toMatchObject({ status: 'activated' }) - expect(mocks.activateAndRevealWorkspace).toHaveBeenCalledWith(worktreeId, { - executionHostId: 'ssh:host-1' - }) - }) - it('derives the focus target from the page url captured before worktree activation', () => { seedStore({ browserPagesByWorkspace: { diff --git a/src/renderer/src/lib/source-control/ai/source-control-agent-launch-picker.test.ts b/src/renderer/src/lib/source-control/ai/source-control-agent-launch-picker.test.ts index d19a09bc566..38def191301 100644 --- a/src/renderer/src/lib/source-control/ai/source-control-agent-launch-picker.test.ts +++ b/src/renderer/src/lib/source-control/ai/source-control-agent-launch-picker.test.ts @@ -608,32 +608,3 @@ it('replaces the full unaccepted extent, accepts once, and preserves promoted ch expect(reopened.delegated).toEqual(expect.arrayContaining(['model', 'effort'])) expect(agentPickerEffectiveChoices(entry, reopened).model).toBeUndefined() }) - -it('orders raw and control picks once and gives one-shot and sparse consumers the same choices', () => { - const selection = canonicalSourceControlActionSelection({ agentId: 'claude', agentArgs: '' }) - for (const edits of [ - [ - { kind: 'arguments', text: '--model sonnet --effort high --debug' }, - { kind: 'choice', patch: { model: 'opus' } } - ], - [ - { kind: 'choice', patch: { model: 'opus' } }, - { kind: 'arguments', text: '--model sonnet --effort high --debug' } - ] - ] satisfies AgentPickerEdit[][]) { - const draft = acceptAgentPickerArguments( - entry, - edits.reduce( - (value, edit) => applyAgentPickerEdit(entry, value, edit), - sourceControlPickerDraft(selection, entry, 'terminal') - ) - ) - const saved = project(applySourceControlPickerDraft(selection, entry, draft)) - const chat = agentPickerLaunchInputs({ ...draft, surface: 'chat' }, entry) - expect(saved.sessionOptions?.model).toBe(chat.sessionOptions?.model) - expect(saved.sessionOptions?.effort).toBe(chat.sessionOptions?.effort) - expect(saved.agentArgs).toBe('--debug') - expect(agentPickerLaunchInputs(draft, entry).agentArgs).toContain('--debug') - expect(draft.pendingArguments).toBeUndefined() - } -})