From f9df6923f6acc33f60c7e67cab2e7feb50783f57 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Sun, 25 Oct 2026 01:00:18 -0700 Subject: [PATCH] test: remove seven low-value cases and repair behavioral assertions (#33453) * test: remove low-value cases and repair behavioral assertions * test: prune repaired wall-clock finding from flaky-test baseline --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- config/flaky-test-pattern-baseline.txt | 1 - .../startup-agent-worktree-create.test.ts | 8 +-- src/main/agent-trust-presets.test.ts | 4 +- .../session-search-file-write.test.ts | 2 +- .../session-search-index-generation.test.ts | 4 +- .../session-search-message-rows.test.ts | 2 +- .../automation-create-destination.test.ts | 26 +++---- .../automation-detail-tab-navigation.test.ts | 8 +-- ...mation-extra-agent-args-capability.test.ts | 2 +- .../automation-host-cache-controller.test.ts | 2 +- .../automations/automation-host-cache.test.ts | 7 +- .../automation-host-catalog-source.test.ts | 20 ++++-- .../automation-host-diagnostics.test.ts | 12 ---- .../automation-host-health.test.ts | 2 +- .../automation-host-scale-gates.test.ts | 38 +---------- .../automation-list-empty-state.test.ts | 12 ---- .../automation-list-search-rows.test.ts | 13 ++-- .../automation-run-context.test.ts | 2 +- src/shared/command-token-scanner.test.ts | 2 +- src/shared/commit-message-agent-spec.test.ts | 67 ++++++------------- src/shared/commit-message-generation.test.ts | 2 +- src/shared/commit-message-omp.spec.ts | 2 +- .../conversation-name-generation.test.ts | 2 +- src/shared/darwin-terminal-names.test.ts | 7 +- ...te-ready-scanner-grok-trace-replay.test.ts | 27 ++------ src/shared/dsh-startup.spec.ts | 2 +- .../emoji-shortcode-catalog.lazy.test.ts | 22 +++--- src/shared/emulator-keyboard-frame.test.ts | 5 +- ...hemeral-setup-terminal-worktree-id.test.ts | 3 +- 29 files changed, 107 insertions(+), 199 deletions(-) diff --git a/config/flaky-test-pattern-baseline.txt b/config/flaky-test-pattern-baseline.txt index 4c29082f98d..40a9e8efff6 100644 --- a/config/flaky-test-pattern-baseline.txt +++ b/config/flaky-test-pattern-baseline.txt @@ -1051,7 +1051,6 @@ 3 test-no-wall-clock:src/renderer/src/components/activity/activity-auto-mark-read-loop.react185.test.tsx 2 test-no-wall-clock:src/renderer/src/components/artifacts/artifact-list-windowing.test.tsx 2 test-no-wall-clock:src/renderer/src/components/artifacts/ArtifactCollection.test.tsx -1 test-no-wall-clock:src/renderer/src/components/automations/automation-list-search-rows.test.ts 7 test-no-wall-clock:src/renderer/src/components/automations/AutomationListLocalRows.test.tsx 1 test-no-wall-clock:src/renderer/src/components/automations/use-automation-list-search.test.tsx 1 test-no-wall-clock:src/renderer/src/components/browser-pane/assemble-chrome/browser-workspace-pane.adoption-chrome.test.tsx diff --git a/src/main/agent-launch/startup-agent-worktree-create.test.ts b/src/main/agent-launch/startup-agent-worktree-create.test.ts index f20661db659..100d80c174d 100644 --- a/src/main/agent-launch/startup-agent-worktree-create.test.ts +++ b/src/main/agent-launch/startup-agent-worktree-create.test.ts @@ -82,7 +82,7 @@ const FULL: RuntimeManagedWorktreeCreateArgs = { } describe('createWorktreeWithStartupAgent', () => { - it('starts a terminal agent with the prompt folded by the create, whatever the chat default', async () => { + it('forwards the startup agent and prompt to the create with the chat default on', async () => { const { runtime, created, create } = harness() const prompt = 'x'.repeat(3000) const args = { @@ -100,7 +100,7 @@ describe('createWorktreeWithStartupAgent', () => { expect(runtime.getStructuredAgentSessionCreateSupport).not.toHaveBeenCalled() }) - it('hands a post-start agent its prompt through the create, which sends it once the agent is up', async () => { + it('forwards a post-start agent and prompt to the create', async () => { const { runtime, create } = harness() const args = { ...FULL, startupAgent: 'aider' as const, startupPrompt: 'fix the bug' } @@ -109,7 +109,7 @@ describe('createWorktreeWithStartupAgent', () => { expect(runtime.createManagedWorktree).toHaveBeenCalledWith(args) }) - it('treats a blank prompt as none, which the create launches bare either way', async () => { + it('omits a blank startup prompt from the create request', async () => { for (const startupPrompt of ['', ' \n']) { const { runtime, create } = harness() @@ -119,7 +119,7 @@ describe('createWorktreeWithStartupAgent', () => { } }) - it('starts a linked draft through `startupDraft`, unsent, with the agent the draft resolves', async () => { + it('forwards a linked draft through `startupDraft` with its resolved agent', async () => { const { runtime, create } = harness({ draftAgent: 'codex' }) const { createdWithAgent: _requested, ...request } = FULL const draft = 'https://github.com/o/r/issues/1' diff --git a/src/main/agent-trust-presets.test.ts b/src/main/agent-trust-presets.test.ts index 9ea50677c3a..8290577a221 100644 --- a/src/main/agent-trust-presets.test.ts +++ b/src/main/agent-trust-presets.test.ts @@ -84,7 +84,7 @@ describe('markCursorWorkspaceTrusted', () => { const trustFile = join(projectsDir, slugDirs[0], '.workspace-trusted') expect(existsSync(trustFile)).toBe(true) const payload = JSON.parse(readFileSync(trustFile, 'utf-8')) - expect(payload.workspacePath).toBeTruthy() + expect(payload.workspacePath).toBe(realpathSync.native(workspace)) expect(typeof payload.trustedAt).toBe('string') } finally { rmSync(workspace, { recursive: true, force: true }) @@ -152,7 +152,7 @@ describe('markCopilotFolderTrusted', () => { const parsed = JSON.parse(readFileSync(configPath, 'utf-8')) expect(Array.isArray(parsed.trustedFolders)).toBe(true) expect(parsed.trustedFolders.length).toBe(1) - expect(typeof parsed.trustedFolders[0]).toBe('string') + expect(parsed.trustedFolders[0]).toBe(realpathSync.native(workspace)) } finally { rmSync(workspace, { recursive: true, force: true }) } diff --git a/src/main/ai-vault-search/session-search-file-write.test.ts b/src/main/ai-vault-search/session-search-file-write.test.ts index a98991e92da..fa62e914447 100644 --- a/src/main/ai-vault-search/session-search-file-write.test.ts +++ b/src/main/ai-vault-search/session-search-file-write.test.ts @@ -72,7 +72,7 @@ function counts(db: SyncDatabase): Record { } } -it('writes a whole read in one transaction', () => { +it('publishes all rows from a completed whole read', () => { replayTranscriptRead({ messages: userMessages('needle text', 300) }) const after = counts(index.db) diff --git a/src/main/ai-vault-search/session-search-index-generation.test.ts b/src/main/ai-vault-search/session-search-index-generation.test.ts index 17d9b32e036..f5747ef4ba7 100644 --- a/src/main/ai-vault-search/session-search-index-generation.test.ts +++ b/src/main/ai-vault-search/session-search-index-generation.test.ts @@ -154,7 +154,7 @@ it('moves the generation when a purge reclaims rows nothing can reach', async () } }) -it("leaves the generation alone when a replace swaps a session's own rows", async () => { +it('leaves the generation alone when messages attached to live sessions are deleted', async () => { // Deleting messages attached to live sessions must not fire the orphan-reclamation trigger. const root = await tempRoot() const path = join(root, 'index.sqlite') @@ -213,7 +213,7 @@ it('keeps the generation across a reopen, because the bump rides its own commit' } }) -it('fences a reader against a writer it does not share a process with', async () => { +it('fences a reader against another database handle moving the index', async () => { // A process-local generation would miss another process’s writes and admit stale cursors. const root = await tempRoot() const path = join(root, 'index.sqlite') diff --git a/src/main/ai-vault-search/session-search-message-rows.test.ts b/src/main/ai-vault-search/session-search-message-rows.test.ts index 7556a41af01..38f3ca5057f 100644 --- a/src/main/ai-vault-search/session-search-message-rows.test.ts +++ b/src/main/ai-vault-search/session-search-message-rows.test.ts @@ -132,7 +132,7 @@ it('cuts at punctuation when the window holds no whitespace at all', async () => } }) -it('keeps a 9,000-character identifier whole rather than cutting at its underscores', () => { +it('hard-cuts a 9,000-character identifier without treating underscores as boundaries', () => { // Underscores are token characters, so this oversized identifier requires a hard cut. const text = 'ab_'.repeat(3000) expect(text.length).toBe(9000) diff --git a/src/renderer/src/components/automations/automation-create-destination.test.ts b/src/renderer/src/components/automations/automation-create-destination.test.ts index faf0905ee37..a3100238869 100644 --- a/src/renderer/src/components/automations/automation-create-destination.test.ts +++ b/src/renderer/src/components/automations/automation-create-destination.test.ts @@ -20,7 +20,7 @@ function entry(overrides: Partial = {}): AutomationH return { stableRef: { authority: { kind: 'desktop' }, selector: { kind: 'self' } }, owner: { authority: { kind: 'desktop' }, selector: { kind: 'self' } }, - stableKey: 'desktop:self', + stableKey: 'host:desktop:self', label: 'This computer', authorityLabel: 'Desktop', kind: 'self', @@ -39,7 +39,7 @@ function sshEntry(targetGeneration: number): AutomationHostCatalogEntry { authority: { kind: 'desktop' }, selector: { kind: 'ssh', targetId: 'box', targetGeneration } }, - stableKey: 'desktop:ssh:box', + stableKey: 'host:desktop:ssh:box', kind: 'ssh' }) } @@ -82,13 +82,13 @@ describe('create destination resolution', () => { it('prefers the explicit selection and only then the active workspace', () => { const entries = [entry(), sshEntry(3)] expect( - preselectAutomationCreateHost(entries, 'desktop:ssh:box', 'desktop:self')?.stableKey - ).toBe('desktop:ssh:box') - expect(preselectAutomationCreateHost(entries, null, 'desktop:self')?.stableKey).toBe( - 'desktop:self' + preselectAutomationCreateHost(entries, 'host:desktop:ssh:box', 'host:desktop:self')?.stableKey + ).toBe('host:desktop:ssh:box') + expect(preselectAutomationCreateHost(entries, null, 'host:desktop:self')?.stableKey).toBe( + 'host:desktop:self' ) // An unresolvable workspace host leaves the choice open rather than picking one. - expect(preselectAutomationCreateHost(entries, null, 'desktop:ssh:gone')).toBeNull() + expect(preselectAutomationCreateHost(entries, null, 'host:desktop:ssh:gone')).toBeNull() expect(preselectAutomationCreateHost(entries, null, null)).toBeNull() }) }) @@ -172,14 +172,14 @@ describe('offered create hosts', () => { it('does not blame the server for hosts another repair or none would fix', () => { // Unverified since disconnect: the repair is a reconnect, not an update. const unverified = entry({ - stableKey: 'desktop:ssh:cold', + stableKey: 'host:desktop:ssh:cold', kind: 'ssh', querySupport: 'legacy-unscoped', scopeGap: 'target-unverified' }) // Scoped but not yet hydrated: disabled, and no repair to name. const unhydrated = entry({ - stableKey: 'desktop:ssh:warm', + stableKey: 'host:desktop:ssh:warm', kind: 'ssh', owner: null, catalogState: 'unhydrated' @@ -199,19 +199,19 @@ describe('sole create host', () => { } it('states the only eligible host and never one of several', () => { - expect(soleAutomationCreateHost([entry()], hydrated)?.stableKey).toBe('desktop:self') + expect(soleAutomationCreateHost([entry()], hydrated)?.stableKey).toBe('host:desktop:self') expect(soleAutomationCreateHost([entry(), sshEntry(3)], hydrated)).toBeNull() // An orphan bucket, an unowned host, and a view-only host are not candidates at all. expect( soleAutomationCreateHost([entry(), entry({ stableKey: 'orphan', kind: 'orphan' })], hydrated) ?.stableKey - ).toBe('desktop:self') + ).toBe('host:desktop:self') expect( soleAutomationCreateHost( [entry(), entry({ stableKey: 'legacy', querySupport: 'legacy-unscoped' })], hydrated )?.stableKey - ).toBe('desktop:self') + ).toBe('host:desktop:self') }) it('states nothing until the catalog has settled', () => { @@ -244,7 +244,7 @@ describe('create project mismatch', () => { addedAt: 1, worktreeBaseRef: 'main', ...overrides - } as Repo + } } const desktopSelf = { authority: { kind: 'desktop' } as const, diff --git a/src/renderer/src/components/automations/automation-detail-tab-navigation.test.ts b/src/renderer/src/components/automations/automation-detail-tab-navigation.test.ts index 75c60f4d9cc..c2bca14b50f 100644 --- a/src/renderer/src/components/automations/automation-detail-tab-navigation.test.ts +++ b/src/renderer/src/components/automations/automation-detail-tab-navigation.test.ts @@ -68,9 +68,9 @@ describe('shouldHandleAutomationDetailTabArrowKey', () => { expect(shouldHandleAutomationDetailTabArrowKey(makeEvent({ target: editable }))).toBe(false) }) - it('ignores keys when target is inside a modal dialog, menu, or listbox', () => { + it.each(['dialog', 'menu', 'listbox'])('ignores keys inside a %s', (role) => { const dialog = document.createElement('div') - dialog.setAttribute('role', 'dialog') + dialog.setAttribute('role', role) const childButton = document.createElement('button') dialog.appendChild(childButton) document.body.appendChild(dialog) @@ -198,9 +198,9 @@ describe('shouldHandleAutomationDetailEscapeKey', () => { expect(shouldHandleAutomationDetailEscapeKey(makeEvent({ target: clearsValue }))).toBe(false) }) - it('ignores Escape when target is inside a modal dialog, menu, or listbox', () => { + it.each(['dialog', 'menu', 'listbox'])('ignores Escape inside a %s', (role) => { const dialog = document.createElement('div') - dialog.setAttribute('role', 'dialog') + dialog.setAttribute('role', role) const childButton = document.createElement('button') dialog.appendChild(childButton) document.body.appendChild(dialog) diff --git a/src/renderer/src/components/automations/automation-extra-agent-args-capability.test.ts b/src/renderer/src/components/automations/automation-extra-agent-args-capability.test.ts index fb2dcd5b260..a144ccd170d 100644 --- a/src/renderer/src/components/automations/automation-extra-agent-args-capability.test.ts +++ b/src/renderer/src/components/automations/automation-extra-agent-args-capability.test.ts @@ -49,7 +49,7 @@ beforeEach(async () => { }) describe('extra agent args capability gate', () => { - it('refuses a create or move to a host without the capability before writing', async () => { + it('refuses a create on a host without the capability before writing', async () => { const { createAutomationForDestination } = await client() getRuntimeEnvironmentStatus.mockResolvedValue(LEGACY_HOST) diff --git a/src/renderer/src/components/automations/automation-host-cache-controller.test.ts b/src/renderer/src/components/automations/automation-host-cache-controller.test.ts index 8fd748f8dc8..2e4aab1940f 100644 --- a/src/renderer/src/components/automations/automation-host-cache-controller.test.ts +++ b/src/renderer/src/components/automations/automation-host-cache-controller.test.ts @@ -137,7 +137,7 @@ afterEach(() => { }) describe('automation host query controller', () => { - it('advances catalog generations before fetching anything', async () => { + it('syncs catalog generations and fetches the catalog host', async () => { const listScoped = vi.fn(() => Promise.resolve(emptyList())) const { controller } = createController(listScoped) expect(getAuthorityCatalogGeneration(RUNTIME)).toBe(0) diff --git a/src/renderer/src/components/automations/automation-host-cache.test.ts b/src/renderer/src/components/automations/automation-host-cache.test.ts index f1d87df29f5..92ae63b7900 100644 --- a/src/renderer/src/components/automations/automation-host-cache.test.ts +++ b/src/renderer/src/components/automations/automation-host-cache.test.ts @@ -169,16 +169,15 @@ describe('automation host cache invalidation scope', () => { expect(cache.freshness(RUNTIME_SELF)).toBe('missing') }) - it('evicts a departed entry and caps the retired pool', () => { + it('evicts departed entries and rejects work captured before retirement', () => { const cache = createCache({ retiredLimit: 1 }) cache.commit(cache.beginRequest(DESKTOP_SELF), { rows: [row('a')] }) cache.commit(cache.beginRequest(RUNTIME_SELF), { rows: [row('b')] }) + const stale = cache.beginRequest(RUNTIME_SELF) cache.evict(hostStableKey(DESKTOP_SELF)) cache.evict(hostStableKey(RUNTIME_SELF)) expect(cache.keys()).toEqual([]) - // The revived entry keeps its advanced generation, so nothing captured before eviction commits. - const stale = cache.beginRequest(RUNTIME_SELF) - cache.invalidate(RUNTIME_SELF) + cache.beginRequest(RUNTIME_SELF) expect(cache.commit(stale, { rows: [] })).toBe(false) }) diff --git a/src/renderer/src/components/automations/automation-host-catalog-source.test.ts b/src/renderer/src/components/automations/automation-host-catalog-source.test.ts index 7104026fe6a..0d706d27a02 100644 --- a/src/renderer/src/components/automations/automation-host-catalog-source.test.ts +++ b/src/renderer/src/components/automations/automation-host-catalog-source.test.ts @@ -1,4 +1,6 @@ import { describe, expect, it } from 'vitest' +import type { RuntimeStatus } from '../../../../shared/runtime-types' +import { createCompatibleRuntimeStatusResponse } from '@/runtime/runtime-compatibility-test-fixture' import { AUTOMATION_LIST_HOST_SCOPE_RUNTIME_CAPABILITY } from '../../../../shared/protocol-version' import { buildAutomationHostCatalogSource, @@ -8,6 +10,14 @@ import { type AutomationCatalogSshSource } from './automation-host-catalog-source' +function status(overrides: Partial = {}): RuntimeStatus { + const response = createCompatibleRuntimeStatusResponse() + if (!response.ok) { + throw new Error(response.error.message) + } + return { ...response.result, ...overrides } +} + function runtime( overrides: Partial = {} ): AutomationCatalogRuntimeSource { @@ -32,7 +42,7 @@ describe('automation host catalog source', () => { it('treats a never-probed environment as loading, not as down', () => { expect(runtimeAuthorityHealth(runtime())).toBe('loading') expect(runtimeAuthorityHealth(runtime({ status: { status: null } }))).toBe('unavailable') - expect(runtimeAuthorityHealth(runtime({ status: { status: {} as never } }))).toBe('fresh') + expect(runtimeAuthorityHealth(runtime({ status: { status: status() } }))).toBe('fresh') }) it('assumes scoped support until the capability set is actually known', () => { @@ -40,14 +50,14 @@ describe('automation host catalog source', () => { // guessing legacy here would only discard owners on a modern host. expect(runtimeQuerySupport(runtime())).toBe('scoped') expect(runtimeQuerySupport(runtime({ status: { status: null } }))).toBe('scoped') - expect( - runtimeQuerySupport(runtime({ status: { status: { capabilities: [] } as never } })) - ).toBe('legacy-unscoped') + expect(runtimeQuerySupport(runtime({ status: { status: status({ capabilities: [] }) } }))).toBe( + 'legacy-unscoped' + ) expect( runtimeQuerySupport( runtime({ status: { - status: { capabilities: [AUTOMATION_LIST_HOST_SCOPE_RUNTIME_CAPABILITY] } as never + status: status({ capabilities: [AUTOMATION_LIST_HOST_SCOPE_RUNTIME_CAPABILITY] }) } }) ) diff --git a/src/renderer/src/components/automations/automation-host-diagnostics.test.ts b/src/renderer/src/components/automations/automation-host-diagnostics.test.ts index 71768183dbc..40d0470b9ba 100644 --- a/src/renderer/src/components/automations/automation-host-diagnostics.test.ts +++ b/src/renderer/src/components/automations/automation-host-diagnostics.test.ts @@ -605,16 +605,4 @@ describe('automation host diagnostics bookkeeping', () => { byStableKey: {} }) }) - - it('never copies an automation, a prompt, or a name into a counter', () => { - const diagnostics = createAutomationHostDiagnostics() - diagnostics.recordResponse({ - authorityKey: 'a', - stableKey: 'k1', - rows: 3, - durationMs: 12, - approxSerializedChars: 900 - }) - expect(JSON.stringify(diagnostics.snapshot())).not.toMatch(/prompt|automation-|secret/i) - }) }) diff --git a/src/renderer/src/components/automations/automation-host-health.test.ts b/src/renderer/src/components/automations/automation-host-health.test.ts index 571a36b544e..d94add14cd0 100644 --- a/src/renderer/src/components/automations/automation-host-health.test.ts +++ b/src/renderer/src/components/automations/automation-host-health.test.ts @@ -45,7 +45,7 @@ describe('execution health', () => { expect(resolveSshExecutionHealth('unhydrated', 'connected', 'disconnected')).toBe('unknown') }) - it('maps every SSH status onto a distinct execution state', () => { + it('maps reported SSH statuses and absent status to execution health', () => { expect(resolveSshExecutionHealth('authoritative', 'connected', undefined)).toBe('connected') expect(resolveSshExecutionHealth('authoritative', 'reconnecting', undefined)).toBe('connecting') expect(resolveSshExecutionHealth('authoritative', 'deploying-relay', undefined)).toBe( diff --git a/src/renderer/src/components/automations/automation-host-scale-gates.test.ts b/src/renderer/src/components/automations/automation-host-scale-gates.test.ts index cd444419504..b1247b7e877 100644 --- a/src/renderer/src/components/automations/automation-host-scale-gates.test.ts +++ b/src/renderer/src/components/automations/automation-host-scale-gates.test.ts @@ -55,8 +55,6 @@ const SCOPED_AUTHORITIES = AUTHORITY_COUNT / 2 const HOST_COUNT = AUTHORITY_COUNT * ENTRIES_PER_AUTHORITY const AUTOMATION_COUNT = HOST_COUNT * AUTOMATIONS_PER_HOST const SSH_TARGET_GENERATION = 3 -// Doc 417: a refresh at this size must not block the renderer for a frame budget's worth of work. -const LONG_TASK_BUDGET_MS = 50 function authorityAt(index: number): AutomationAuthorityRef { return { kind: 'runtime', environmentId: `env-${index}`, pairingRevision: index + 1 } @@ -230,31 +228,6 @@ async function roundTrip(environmentId: string, value: () => T): Promise { } } -/** - * Longest gap between successive macrotasks: a synchronous block anywhere in the - * refresh delays the next timer by its own duration, which is the only way to - * observe "no long task" rather than to assert it about one function. - */ -function watchLongTasks(): () => number { - let worst = 0 - let last = performance.now() - let running = true - const tick = (): void => { - if (!running) { - return - } - const at = performance.now() - worst = Math.max(worst, at - last) - last = at - setTimeout(tick, 0) - } - setTimeout(tick, 0) - return () => { - running = false - return worst - } -} - function harness(): { cache: AutomationHostCache refresh: (options?: { force?: boolean }) => Promise @@ -378,15 +351,6 @@ describe('one refresh of 50 hosts carrying 1,000 automations', () => { expect(snap.totals.rows).toBe(AUTOMATION_COUNT) expect(perHost).toBe(AUTOMATION_COUNT) }) - - it('does no synchronous work long enough to drop a frame', async () => { - const { refresh } = harness() - const stop = watchLongTasks() - - await refresh() - - expect(stop()).toBeLessThan(LONG_TASK_BUDGET_MS) - }) }) describe('relay cost the request pool cannot see', () => { @@ -428,7 +392,7 @@ describe('relay cost the request pool cannot see', () => { }) describe('the commit fence under real concurrency', () => { - it('commits nothing from an authority that re-paired while its answer was in flight', async () => { + it('commits nothing from an authority invalidated while its answer was in flight', async () => { const { cache, refresh } = harness() // One modern and one old server, so both the per-entry and the fan-out arm // are fenced out while the other 40 hosts keep landing around them. diff --git a/src/renderer/src/components/automations/automation-list-empty-state.test.ts b/src/renderer/src/components/automations/automation-list-empty-state.test.ts index 717f1053b64..bb640338145 100644 --- a/src/renderer/src/components/automations/automation-list-empty-state.test.ts +++ b/src/renderer/src/components/automations/automation-list-empty-state.test.ts @@ -209,16 +209,4 @@ describe('automation list empty state', () => { expect(state.kind).toBe('host-empty') expect(state.title).toBe('No automations on web-01') }) - - it('keeps each empty string exclusive to its own condition', () => { - const titles = [ - resolveAutomationListEmptyState(input()).title, - resolveAutomationListEmptyState(input({ resolution: ALL_HOSTS })).title, - resolveAutomationListEmptyState(input({ searchActive: true, hostRowCount: 2 })).title, - resolveAutomationListEmptyState( - input({ resolution: hostResolution({ entry: entry({ authorityHealth: 'stale-error' }) }) }) - ).title - ] - expect(new Set(titles).size).toBe(4) - }) }) diff --git a/src/renderer/src/components/automations/automation-list-search-rows.test.ts b/src/renderer/src/components/automations/automation-list-search-rows.test.ts index 27db2b6a558..e0fa7397846 100644 --- a/src/renderer/src/components/automations/automation-list-search-rows.test.ts +++ b/src/renderer/src/components/automations/automation-list-search-rows.test.ts @@ -79,7 +79,9 @@ describe('automation search row fields', () => { }) it('indexes external jobs by provider, target host, and workdir', () => { - const [entry] = buildExternalAutomationListEntries([makeScopedExternalManager()]) + const manager = makeScopedExternalManager() + manager.manager.jobs[0]!.workdir = '/srv/sweep-project' + const [entry] = buildExternalAutomationListEntries([manager]) if (!entry) { throw new Error('fixture produced no external entry') } @@ -88,6 +90,7 @@ describe('automation search row fields', () => { expect(fields.agent).toBe('OpenClaw') expect(fields.host).toBe('This computer') expect(fields.prompt).toBe('Sweep') + expect(fields.project).toBe('/srv/sweep-project') }) }) @@ -131,14 +134,14 @@ describe('automation search row index', () => { }) it('fingerprints identical content identically across a refresh tick', () => { - const build = (): ReturnType => + const build = (nextRunAt: number): ReturnType => buildAutomationSearchRowSources( - [makeAutomationListRow({ automation: makeAutomation({ nextRunAt: Date.now() }) })], + [makeAutomationListRow({ automation: makeAutomation({ nextRunAt }) })], { repoMap } ) // Why: nextRunAt churns on every tick and must not invalidate the index. - expect(buildAutomationListSearchRowFingerprint(build())).toBe( - buildAutomationListSearchRowFingerprint(build()) + expect(buildAutomationListSearchRowFingerprint(build(100))).toBe( + buildAutomationListSearchRowFingerprint(build(200)) ) }) diff --git a/src/renderer/src/components/automations/automation-run-context.test.ts b/src/renderer/src/components/automations/automation-run-context.test.ts index 4e9d7a76fd1..f1dd660f0b5 100644 --- a/src/renderer/src/components/automations/automation-run-context.test.ts +++ b/src/renderer/src/components/automations/automation-run-context.test.ts @@ -68,7 +68,7 @@ describe('buildAutomationRunContextForRepo', () => { buildAutomationRunContextForRepo({ repoId: 'repo-builder', repos: [repo('repo-builder')], - projectHostSetups: [setup({ setupState: 'setting-up' })] + projectHostSetups: [setup({ hostId: 'local', setupState: 'setting-up' })] }) ).toBeNull() diff --git a/src/shared/command-token-scanner.test.ts b/src/shared/command-token-scanner.test.ts index 2f3e7323f68..7b2e340832e 100644 --- a/src/shared/command-token-scanner.test.ts +++ b/src/shared/command-token-scanner.test.ts @@ -31,7 +31,7 @@ describe('command token scanner', () => { ) }) - it('extracts path basenames without allocating path segment arrays', () => { + it('extracts basenames from Windows and POSIX paths', () => { expect(getCommandTokenPathBasename('C:\\Program Files\\Orca\\codex.cmd')).toBe('codex.cmd') expect(getCommandTokenPathBasename('/usr/local/bin/omp')).toBe('omp') }) diff --git a/src/shared/commit-message-agent-spec.test.ts b/src/shared/commit-message-agent-spec.test.ts index d24387144cc..ce61f09d409 100644 --- a/src/shared/commit-message-agent-spec.test.ts +++ b/src/shared/commit-message-agent-spec.test.ts @@ -1,4 +1,3 @@ -import { claudeBackgroundArgs } from './claude-background-run-isolation' import { afterEach, describe, expect, it, vi } from 'vitest' import { COMMIT_MESSAGE_AGENT_SPECS, @@ -26,26 +25,6 @@ afterEach(() => { }) describe('COMMIT_MESSAGE_AGENT_SPECS', () => { - it('exposes the installed local agents as commit-message agents', () => { - const ids = listCommitMessageAgentIds().sort() - expect(ids).toEqual([ - 'amp', - 'antigravity', - 'claude', - 'codex', - 'copilot', - 'cursor', - 'dsh', - 'jcode', - 'kimi', - 'muse', - 'omp', - 'opencode', - 'opencode2', - 'pi' - ]) - }) - it('uses the expected defaults for core agents', () => { expect(COMMIT_MESSAGE_AGENT_SPECS.claude?.defaultModelId).toBe('sonnet') expect(COMMIT_MESSAGE_AGENT_SPECS.codex?.defaultModelId).toBe('gpt-5.6-terra') @@ -106,28 +85,6 @@ describe('COMMIT_MESSAGE_AGENT_SPECS', () => { } }) - it('lists Copilot hosted CLI models even when account policy filters the picker', () => { - expect(COMMIT_MESSAGE_AGENT_SPECS.copilot?.defaultModelId).toBe('gpt-5.4') - expect(COMMIT_MESSAGE_AGENT_SPECS.copilot?.models.map((m) => m.id)).toEqual([ - 'auto', - 'claude-haiku-4.5', - 'claude-sonnet-4.5', - 'claude-sonnet-4.6', - 'claude-opus-4.5', - 'claude-opus-4.6', - 'claude-opus-4.6-fast', - 'claude-opus-4.7', - 'gpt-4.1', - 'gpt-5-mini', - 'gpt-5.2', - 'gpt-5.2-codex', - 'gpt-5.3-codex', - 'gpt-5.4', - 'gpt-5.4-mini', - 'gpt-5.5' - ]) - }) - it('treats disabled default agents as unavailable for implicit Source Control AI choices', () => { expect(resolveCommitMessageAgentChoice(null, 'codex', ['codex'])).toBe('claude') expect(resolveCommitMessageAgentChoice(null, null, ['claude'])).toBeNull() @@ -174,7 +131,7 @@ describe('COMMIT_MESSAGE_AGENT_SPECS', () => { expect(listCommitMessageAgentIds()).not.toContain(CUSTOM_AGENT_ID) }) - it('orders Codex models by version descending to match the official picker', () => { + it('orders seeded Codex models by version descending', () => { const ids = COMMIT_MESSAGE_AGENT_SPECS.codex?.models.map((m) => m.id) expect(ids).toEqual([ 'gpt-5.6-terra', @@ -215,7 +172,9 @@ describe('buildArgs (Claude)', () => { 'haiku', '--permission-mode', 'plan', - ...claudeBackgroundArgs() + '--strict-mcp-config', + '--settings', + '{"disableAllHooks":true}' ]) }) @@ -235,7 +194,9 @@ describe('buildArgs (Claude)', () => { 'plan', '--effort', 'high', - ...claudeBackgroundArgs() + '--strict-mcp-config', + '--settings', + '{"disableAllHooks":true}' ]) }) @@ -296,7 +257,15 @@ describe('buildArgs (Jcode)', () => { const args = spec.buildArgs({ prompt: 'name this branch', model: 'claude-haiku-4-5' }) const runIndex = args.indexOf('run') expect(runIndex).toBeGreaterThan(0) - expect(args.slice(0, runIndex).every((arg) => arg.startsWith('--') || arg !== 'run')).toBe(true) + expect(args.slice(0, runIndex)).toEqual([ + '--no-update', + '--quiet', + '--no-selfdev', + '--tool-profile', + 'none', + '--model', + 'claude-haiku-4-5' + ]) expect(args.slice(runIndex)).toEqual(['run', '--json', 'name this branch']) }) @@ -385,7 +354,9 @@ describe('model discovery parsers', () => { '--output-format', 'stream-json', '--verbose', - ...claudeBackgroundArgs() + '--strict-mcp-config', + '--settings', + '{"disableAllHooks":true}' ]) const payload = JSON.parse(discovery?.stdinPayload ?? '') as { type?: string diff --git a/src/shared/commit-message-generation.test.ts b/src/shared/commit-message-generation.test.ts index 5ceb2e89f85..e330709e170 100644 --- a/src/shared/commit-message-generation.test.ts +++ b/src/shared/commit-message-generation.test.ts @@ -24,7 +24,7 @@ describe('buildCommitMessagePrompt', () => { expect(prompt).not.toContain('Additional user prompt:') }) - it('keeps a custom prompt in a separate bounded section', () => { + it('keeps a custom prompt in a separate section', () => { const prompt = buildCommitMessagePrompt( { branch: null, diff --git a/src/shared/commit-message-omp.spec.ts b/src/shared/commit-message-omp.spec.ts index 2ffa43d9c44..479a4566b1d 100644 --- a/src/shared/commit-message-omp.spec.ts +++ b/src/shared/commit-message-omp.spec.ts @@ -18,7 +18,7 @@ describe('OMP Source Control AI', () => { }) } ) - it('delivers large diffs on stdin and keeps generation isolated from tools and extensions', () => { + it('plans stdin delivery with tools and extensions disabled', () => { const spec = getCommitMessageAgentSpec('omp') expect(spec?.promptDelivery).toBe('stdin') if (!spec) { diff --git a/src/shared/conversation-name-generation.test.ts b/src/shared/conversation-name-generation.test.ts index 96ca34627c4..e5d6e1854e5 100644 --- a/src/shared/conversation-name-generation.test.ts +++ b/src/shared/conversation-name-generation.test.ts @@ -6,7 +6,7 @@ import { } from './conversation-name-generation' describe('conversation name generation', () => { - it('bounds the first prompt in both the built-in prompt and template variable', () => { + it('bounds the first prompt and includes it in the built-in prompt', () => { const firstPrompt = `Start ${'x'.repeat(5000)}` const clamped = clampConversationNameFirstPrompt(firstPrompt) expect(clamped).toHaveLength(4000) diff --git a/src/shared/darwin-terminal-names.test.ts b/src/shared/darwin-terminal-names.test.ts index 11ea273231c..d74d8444d5c 100644 --- a/src/shared/darwin-terminal-names.test.ts +++ b/src/shared/darwin-terminal-names.test.ts @@ -218,10 +218,13 @@ describe('nameDarwinTerminals', () => { directory.entries = [ { name: 'gone', rdev: 0x10000009n, error: new Error('ENOENT') }, { name: 'denied', rdev: 0x10000009n, error: new Error('EACCES') }, - { name: 'ttys009', rdev: 0x10000009n } + { name: 'cu.found', rdev: 0x10000009n } ] - expect(await nameDarwinTerminals(darwinRow('16/9'), directory)).toBe(darwinRow('ttys009')) + expect(await nameDarwinTerminals(darwinRow('16/9'), directory)).toBe(darwinRow('cu.found')) + expect(directory.readPaths).toEqual( + ['ttys009', 'gone', 'denied', 'cu.found'].map((name) => join('/dev', name)) + ) }) it('rejects directory open failures so the reader can fall back to ordinary tty ps', async () => { diff --git a/src/shared/draft-paste-ready-scanner-grok-trace-replay.test.ts b/src/shared/draft-paste-ready-scanner-grok-trace-replay.test.ts index a853b075b6c..b3ed73fc542 100644 --- a/src/shared/draft-paste-ready-scanner-grok-trace-replay.test.ts +++ b/src/shared/draft-paste-ready-scanner-grok-trace-replay.test.ts @@ -62,7 +62,7 @@ function replayReadyAtMs( } describe('grok startup trace replay (alternate-screen mode)', () => { - it('delivers on the composer frame instead of waiting out the hard timeout', () => { + it('reports readiness within the first second of the alternate-screen trace', () => { const readyAt = replayReadyAtMs('grok-composer-prompt', GROK_STARTUP_PTY_TRACE) expect(readyAt).not.toBeNull() expect(readyAt).toBeLessThan(1000) @@ -74,7 +74,7 @@ describe('grok startup trace replay (alternate-screen mode)', () => { expect(replayReadyAtMs('render-quiet-after-bracketed-paste', GROK_STARTUP_PTY_TRACE)).toBeNull() }) - it('fires on the same frame that paints the composer box', () => { + it('reports readiness on the frame that paints the composer box', () => { const composerFrame = GROK_STARTUP_PTY_TRACE.find((chunk) => chunk.data?.includes('❯')) expect(composerFrame).toBeDefined() expect(replayReadyAtMs('grok-composer-prompt', GROK_STARTUP_PTY_TRACE)).toBe(composerFrame?.t) @@ -82,7 +82,7 @@ describe('grok startup trace replay (alternate-screen mode)', () => { }) describe('grok startup trace replay (inline mode, no alternate screen)', () => { - it('still delivers through the quiet window when the marker never anchors', () => { + it('arms a quiet window before the inline trace reaches the modeled timeout', () => { // Why: inline grok emits no \x1b[?1049h, so the composer glyph never counts. // Anchoring the quiet window on the alt-screen switch too would leave this // launch with NO delivery path — orca-runtime drops the draft when readiness @@ -92,28 +92,9 @@ describe('grok startup trace replay (inline mode, no alternate screen)', () => { expect(readyAt).toBeLessThan(HARD_TIMEOUT_MS) }) - it('matches the default signal exactly, so inline launches keep their old timing', () => { + it('matches the default signal timing for the inline trace', () => { expect(replayReadyAtMs('grok-composer-prompt', GROK_INLINE_STARTUP_PTY_TRACE)).toBe( replayReadyAtMs('render-quiet-after-bracketed-paste', GROK_INLINE_STARTUP_PTY_TRACE) ) }) - - it('reports the hard timeout, not a late marker, once the waiters have settled', () => { - // Guards the model above: the real waiters resolve at 8s, so a marker landing - // after that must not be reported as a delivery time. - const lateMarker: GrokStartupTraceChunk[] = [ - { t: 0, data: '\x1b[?1049h' }, - { t: 8500, data: '\x1b[38;2;200;200;200m❯ ' } - ] - expect(replayReadyAtMs('grok-composer-prompt', lateMarker)).toBeNull() - }) - - it('records a startup with no alternate-screen switch', () => { - // Guards the fixture itself: if a future re-recording captures alt-screen - // output, the inline assertions above would silently stop testing inline. - expect(GROK_INLINE_STARTUP_PTY_TRACE.some((chunk) => chunk.data?.includes('\x1b[?1049h'))).toBe( - false - ) - expect(GROK_INLINE_STARTUP_PTY_TRACE.some((chunk) => chunk.data?.includes('❯'))).toBe(true) - }) }) diff --git a/src/shared/dsh-startup.spec.ts b/src/shared/dsh-startup.spec.ts index 0a99d2cc67b..604fcc54671 100644 --- a/src/shared/dsh-startup.spec.ts +++ b/src/shared/dsh-startup.spec.ts @@ -4,7 +4,7 @@ import { TUI_AGENT_CONFIG } from './tui-agent-config' describe('DSH first launch', () => { it.each(['darwin', 'linux', 'win32'] as const)( - 'selects the current workspace before pasting the task on %s', + 'plans the current workspace with task input deferred on %s', (platform) => { const plan = buildAgentStartupPlan({ agent: 'dsh', diff --git a/src/shared/emoji-shortcode-catalog.lazy.test.ts b/src/shared/emoji-shortcode-catalog.lazy.test.ts index f46e0e742aa..ad412a97421 100644 --- a/src/shared/emoji-shortcode-catalog.lazy.test.ts +++ b/src/shared/emoji-shortcode-catalog.lazy.test.ts @@ -3,8 +3,9 @@ import emojiShortcodes from 'emojibase-data/en/shortcodes/emojibase.json' async function importConfiguredCatalog() { const catalog = await import('./emoji-shortcode-catalog.js') - catalog.setEmojiShortcodeDatasetLoader(() => emojiShortcodes) - return catalog + const loadDataset = vi.fn(() => emojiShortcodes) + catalog.setEmojiShortcodeDatasetLoader(loadDataset) + return { catalog, loadDataset } } describe('emoji shortcode catalog laziness', () => { @@ -13,18 +14,21 @@ describe('emoji shortcode catalog laziness', () => { }) it('does not build the catalog when the shared module is imported', async () => { - const catalog = await importConfiguredCatalog() + const { catalog, loadDataset } = await importConfiguredCatalog() - expect(catalog.isEmojiShortcodeCatalogBuiltForTest()).toBe(false) + expect(loadDataset).not.toHaveBeenCalled() - expect(catalog.getStandardEmojiShortcodeEntries().length).toBeGreaterThan(1000) - expect(catalog.isEmojiShortcodeCatalogBuiltForTest()).toBe(true) + const entries = catalog.getStandardEmojiShortcodeEntries() + expect(entries).toContainEqual({ emoji: '🎉', shortcode: 'party' }) + expect(catalog.getStandardEmojiShortcodeEntries()).toBe(entries) + expect(loadDataset).toHaveBeenCalledTimes(1) }) - it('builds on first use and keeps the main process off the eager path', async () => { - const catalog = await importConfiguredCatalog() + it('loads once when replacing emoji and reuses the catalog', async () => { + const { catalog, loadDataset } = await importConfiguredCatalog() expect(catalog.replaceKnownEmojiWithShortcodes('ship \u{1F389}')).toBe('ship party ') - expect(catalog.isEmojiShortcodeCatalogBuiltForTest()).toBe(true) + expect(catalog.replaceKnownEmojiWithShortcodes('again 🎉')).toBe('again party ') + expect(loadDataset).toHaveBeenCalledTimes(1) }) }) diff --git a/src/shared/emulator-keyboard-frame.test.ts b/src/shared/emulator-keyboard-frame.test.ts index fbfb9d120e6..1224e648dfb 100644 --- a/src/shared/emulator-keyboard-frame.test.ts +++ b/src/shared/emulator-keyboard-frame.test.ts @@ -2,8 +2,7 @@ import { describe, expect, it } from 'vitest' import { buildServeSimKeyboardFramesForKey, buildServeSimKeyboardFramesForText, - encodeServeSimKeyboardFrame, - SERVE_SIM_KEYBOARD_MESSAGE_TAG + encodeServeSimKeyboardFrame } from './emulator-keyboard-frame' describe('serve-sim keyboard frames', () => { @@ -46,7 +45,7 @@ describe('serve-sim keyboard frames', () => { it('encodes serve-sim keyboard messages with the binary keyboard tag', () => { const frame = encodeServeSimKeyboardFrame({ type: 'down', usage: 4 }) - expect(frame[0]).toBe(SERVE_SIM_KEYBOARD_MESSAGE_TAG) + expect(frame[0]).toBe(0x06) expect(JSON.parse(new TextDecoder().decode(frame.subarray(1)))).toEqual({ type: 'down', usage: 4 diff --git a/src/shared/ephemeral-setup-terminal-worktree-id.test.ts b/src/shared/ephemeral-setup-terminal-worktree-id.test.ts index c93c869ac58..d8b89dd8179 100644 --- a/src/shared/ephemeral-setup-terminal-worktree-id.test.ts +++ b/src/shared/ephemeral-setup-terminal-worktree-id.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from 'vitest' import { - EPHEMERAL_SETUP_TERMINAL_WORKTREE_ID_PREFIX, brandEphemeralSetupTerminalWorktreeId, isEphemeralSetupTerminalWorktreeId } from './ephemeral-setup-terminal-worktree-id' @@ -8,7 +7,7 @@ import { describe('ephemeral setup terminal worktree id', () => { it('brands a panel id with the ephemeral prefix', () => { expect(brandEphemeralSetupTerminalWorktreeId('feature-wall-orchestration-skill-terminal')).toBe( - `${EPHEMERAL_SETUP_TERMINAL_WORKTREE_ID_PREFIX}feature-wall-orchestration-skill-terminal` + 'ephemeral-setup-terminal:feature-wall-orchestration-skill-terminal' ) })