test: remove low-value mobile and host policy suites (#41436)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-11-11 23:25:07 -08:00
committed by GitHub
co-authored by OrcaWin
parent 443a0ee846
commit 4dcf689fb2
22 changed files with 22 additions and 857 deletions
File diff suppressed because one or more lines are too long
@@ -2175,7 +2175,6 @@
"src/main/ipc/wsl/wsl-host-automation-wake-launch.test.ts": 939,
"src/main/ipc/wsl/wsl-host-handlers-user-actions.test.ts": 772,
"src/main/ipc/wsl/wsl-host-visible-work-windows.test.ts": 39,
"src/main/ipc/wsl/wsl-move-driver-handlers.test.ts": 432,
"src/main/ipc/wsl/wsl-move-handlers-setting-off.test.ts": 724,
"src/main/ipc/wsl/wsl-move-host-connect.test.ts": 984,
"src/main/ipc/wsl/wsl-move-partial-undo.test.ts": 939,
@@ -1,24 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import { readPushNotificationsPreference } from '../../storage/preferences'
import { shouldPresentNotificationOptIn } from './notification-opt-in-gate'
vi.mock('../../storage/preferences', () => ({
readPushNotificationsPreference: vi.fn()
}))
describe('notification opt-in gate', () => {
it('asks for push-service consent when no choice is saved', async () => {
vi.mocked(readPushNotificationsPreference).mockResolvedValue({ value: null, loaded: true })
await expect(shouldPresentNotificationOptIn()).resolves.toBe(true)
})
it.each([true, false])('does not ask again after choosing %s', async (value) => {
vi.mocked(readPushNotificationsPreference).mockResolvedValue({ value, loaded: true })
await expect(shouldPresentNotificationOptIn()).resolves.toBe(false)
})
it('does not prompt when the saved choice cannot be read', async () => {
vi.mocked(readPushNotificationsPreference).mockResolvedValue({ value: null, loaded: false })
await expect(shouldPresentNotificationOptIn()).resolves.toBe(false)
})
})
@@ -1,58 +0,0 @@
import { expect, it, vi } from 'vitest'
const state = vi.hoisted(
(): { taskName: string; task: null | ((input: unknown) => Promise<void>) } => ({
taskName: '',
task: null
})
)
vi.mock('expo-task-manager', () => ({
defineTask: (name: string, task: typeof state.task) => {
state.taskName = name
state.task = task
},
isAvailableAsync: async () => true
}))
vi.mock('expo-notifications', () => ({
registerTaskAsync: vi.fn(),
getPresentedNotificationsAsync: vi.fn(async () => []),
dismissNotificationAsync: vi.fn()
}))
vi.mock('./push-tray-dismissal', async (importOriginal) => {
const actual = await importOriginal<typeof import('./push-tray-dismissal')>()
return {
...actual,
dismissPresentedPushNotification: vi.fn(actual.dismissPresentedPushNotification)
}
})
import * as Notifications from 'expo-notifications'
import { dismissPresentedPushNotification } from './push-tray-dismissal'
import './push-background-dismissal'
it('does not turn ordinary alerts into dismissals', async () => {
vi.mocked(dismissPresentedPushNotification).mockClear()
await state.task!({
data: { data: { orca: { hostFingerprint: 'host-a', notificationId: 'same-id' } } }
})
expect(dismissPresentedPushNotification).not.toHaveBeenCalled()
})
it('an ID-only background dismissal preserves versioned tray alerts', async () => {
const base = { hostFingerprint: 'host-a', notificationId: 'same-id' }
vi.mocked(Notifications.getPresentedNotificationsAsync).mockResolvedValue([
{ request: { identifier: 'legacy', content: { data: base } } },
{
request: {
identifier: 'versioned',
content: {
data: {
...base,
notificationEpoch: 'epoch',
notificationSeq: 3
}
}
}
}
] as never)
await state.task!({ data: { data: { ...base, kind: 'dismiss' } } })
expect(Notifications.dismissNotificationAsync).toHaveBeenCalledExactlyOnceWith('legacy')
})
@@ -54,24 +54,3 @@ it('merges batched and concurrent fences without lowering duplicates or acceptin
expect(await wasPushDismissed({ ...payload, notificationId: 'other' })).toBe(true)
expect(await wasPushDismissed({ ...payload, notificationId: 'invalid' })).toBe(false)
})
it.each([1, 3])('retains live dismissals beyond 512 entries across %i hosts', async (hosts) => {
for (let index = 0; index < 520; index++) {
await rememberPushDismissal({
...payload,
hostFingerprint: `host-${index % hosts}`,
notificationId: `note-${index}`
})
}
vi.resetModules()
const restarted = await import('./push-dismissal-watermarks')
for (const index of [0, 1, 519]) {
const alert = {
...payload,
hostFingerprint: `host-${index % hosts}`,
notificationId: `note-${index}`
}
expect(await restarted.wasPushDismissed(alert)).toBe(true)
expect(await restarted.wasPushDismissed({ ...alert, notificationSeq: 3 })).toBe(false)
}
})
@@ -1,125 +0,0 @@
import { beforeEach, expect, it, vi } from 'vitest'
import type {
FirebaseRemoteMessageNotification,
Notification,
NotificationTrigger
} from 'expo-notifications'
import { startAndroidForegroundPushPresentation } from './android-foreground-push'
const mocks = vi.hoisted(() => ({
platform: { OS: 'android' },
receive: (_notification: Notification) => {},
remove: vi.fn(),
eligible: vi.fn().mockResolvedValue(true),
schedule: vi.fn().mockResolvedValue('message-1')
}))
vi.mock('./push-receive', () => ({ canPresentForegroundPush: mocks.eligible }))
vi.mock('react-native', () => ({ Platform: mocks.platform }))
vi.mock('expo-notifications', () => ({
addNotificationReceivedListener: (listener: typeof mocks.receive) => {
mocks.receive = listener
return { remove: mocks.remove }
},
scheduleNotificationAsync: mocks.schedule
}))
// Build complete Expo message types once so fixtures need no unchecked assertions.
const REMOTE_NOTIFICATION: FirebaseRemoteMessageNotification = {
body: null,
bodyLocalizationArgs: null,
bodyLocalizationKey: null,
channelId: null,
clickAction: null,
color: null,
eventTime: null,
icon: null,
imageUrl: null,
lightSettings: null,
link: null,
localOnly: false,
notificationCount: null,
notificationPriority: null,
sound: null,
sticky: false,
tag: null,
ticker: null,
title: null,
titleLocalizationArgs: null,
titleLocalizationKey: null,
usesDefaultLightSettings: false,
usesDefaultSound: false,
usesDefaultVibrateSettings: false,
vibrateTimings: null,
visibility: null
}
function pushTrigger(remote: FirebaseRemoteMessageNotification | null): NotificationTrigger {
return {
type: 'push',
remoteMessage: {
collapseKey: null,
data: {},
from: null,
messageId: 'message-1',
messageType: null,
notification: remote,
originalPriority: 1,
priority: 1,
sentTime: 0,
to: null,
ttl: 0
}
}
}
const ORCA_PUSH_DATA: Record<string, unknown> = {
hostFingerprint: 'host',
notificationId: 'event',
notificationEpoch: 'epoch',
notificationSeq: '3',
paneKey: 'pane',
channelId: 'orca-desktop'
}
function notification(
trigger: NotificationTrigger = pushTrigger(null),
data: Record<string, unknown> = ORCA_PUSH_DATA
): Notification {
return {
date: 0,
request: {
identifier: 'message-1',
trigger,
content: {
title: 'Test notification',
subtitle: null,
body: '',
categoryIdentifier: null,
threadIdentifier: null,
sound: 'default',
data
}
}
}
}
beforeEach(() => {
vi.clearAllMocks()
mocks.eligible.mockReset().mockResolvedValue(true)
mocks.platform.OS = 'android'
})
it('does not reschedule its own local notification or normal provider notifications', () => {
startAndroidForegroundPushPresentation()
mocks.receive(notification(null))
mocks.receive(notification({ channelId: 'orca-desktop' }))
mocks.receive(notification(pushTrigger({ ...REMOTE_NOTIFICATION, title: 'Test' })))
expect(mocks.schedule).not.toHaveBeenCalled()
})
it('leaves silent dismissals and unrelated messages alone', () => {
startAndroidForegroundPushPresentation()
mocks.receive(notification(undefined, { ...ORCA_PUSH_DATA, kind: 'dismiss' }))
mocks.receive(notification(undefined, {}))
expect(mocks.schedule).not.toHaveBeenCalled()
})
@@ -1,36 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import * as Notifications from 'expo-notifications'
import { Platform } from 'react-native'
import { ensureDesktopNotificationChannel } from './desktop-notification-channel'
vi.mock('expo-notifications', () => ({
AndroidImportance: { HIGH: 'high' },
setNotificationChannelAsync: vi.fn()
}))
vi.mock('react-native', () => ({
AppState: { currentState: 'background' },
Platform: { OS: 'android' }
}))
beforeEach(() => {
vi.clearAllMocks()
Object.assign(Platform, { OS: 'android' })
vi.mocked(Notifications.setNotificationChannelAsync).mockResolvedValue(null as never)
})
describe('ensureDesktopNotificationChannel', () => {
it('does nothing on iOS, which has no notification channels', () => {
Object.assign(Platform, { OS: 'ios' })
ensureDesktopNotificationChannel()
expect(Notifications.setNotificationChannelAsync).not.toHaveBeenCalled()
})
it('reports channel failure so registration can retry', async () => {
vi.mocked(Notifications.setNotificationChannelAsync).mockRejectedValue(new Error('no channels'))
await expect(ensureDesktopNotificationChannel()).rejects.toThrow('no channels')
})
})
@@ -1,62 +0,0 @@
import { describe, expect, it } from 'vitest'
import { getNotificationNavigationTarget } from './notification-routing'
describe('notification routing', () => {
// Identities stay raw: the target is dispatched as navigator params, not a URL.
it('routes notification taps to the worktree terminal screen', () => {
expect(
getNotificationNavigationTarget({
hostId: 'host-1',
worktreeId: 'repo::/Users/me/orca/workspaces/feature'
})
).toEqual({
hostId: 'host-1',
sessionTarget: {
name: '[hostId]/session/[worktreeId]',
params: { hostId: 'host-1', worktreeId: 'repo::/Users/me/orca/workspaces/feature' }
}
})
})
it('falls back to the host screen when the payload has no worktree id', () => {
expect(getNotificationNavigationTarget({ hostId: 'host-1' })).toEqual({
hostId: 'host-1',
sessionTarget: null
})
})
it('ignores payloads that cannot identify the paired host', () => {
expect(getNotificationNavigationTarget({ worktreeId: 'repo::/tmp/worktree' })).toBeNull()
})
it('ignores payloads for hosts that are no longer paired', () => {
expect(
getNotificationNavigationTarget(
{ hostId: 'removed-host', worktreeId: 'repo::/tmp/worktree' },
{ knownHostIds: new Set(['host-1']) }
)
).toBeNull()
})
})
it('preserves the originating pane in the workspace route', () => {
const paneKey = 'tab-b:11111111-1111-4111-8111-111111111111'
expect(
getNotificationNavigationTarget({ hostId: 'host', worktreeId: 'folder:/work', paneKey })
?.sessionTarget?.params
).toEqual({ hostId: 'host', worktreeId: 'folder:/work', paneKey })
})
it('scopes a tap to a server workspace, and keeps anything else on the desktop', () => {
const paramsFor = (executionHost: unknown) =>
getNotificationNavigationTarget({ hostId: 'host', worktreeId: 'repo::/srv', executionHost })
?.sessionTarget?.params
expect(paramsFor('runtime:env-1')).toEqual({
hostId: 'host',
worktreeId: 'repo::/srv',
executionHost: 'runtime:env-1'
})
for (const notAServer of [undefined, '', 'local', 'ssh:box', 'runtime:', 'env-1', 42]) {
expect(paramsFor(notAServer)).toEqual({ hostId: 'host', worktreeId: 'repo::/srv' })
}
})
@@ -1,8 +0,0 @@
import { expect, it } from 'vitest'
import { readNativeNotificationData } from './native-notification-data'
it('keeps Android push and local notification data', () => {
const data = { hostId: 'host', notificationId: 'done' }
expect(readNativeNotificationData({ content: { data }, trigger: { type: 'push' } })).toBe(data)
expect(readNativeNotificationData({ content: { data }, trigger: null })).toBe(data)
})
@@ -1,19 +1,9 @@
import { describe, expect, it } from 'vitest'
import { deriveHostFingerprint, resolveHostIdForFingerprint } from './push-host-fingerprint'
import { resolveHostIdForFingerprint } from './push-host-fingerprint'
const publicKey = Uint8Array.from({ length: 32 }, (_, index) => index)
const publicKeyB64 = Buffer.from(publicKey).toString('base64')
describe('deriveHostFingerprint', () => {
it.each([
['a key of the wrong length', Buffer.from(new Uint8Array(16)).toString('base64')],
['text that is not base64 at all', '!!!not base64!!!'],
['an empty key', '']
])('returns null for %s', (_label, value) => {
expect(deriveHostFingerprint(value)).toBeNull()
})
})
describe('resolveHostIdForFingerprint', () => {
const other = Uint8Array.from({ length: 32 }, (_, index) => index + 1)
const hosts = [
@@ -25,12 +15,4 @@ describe('resolveHostIdForFingerprint', () => {
it('maps a push fingerprint back to the paired host id', () => {
expect(resolveHostIdForFingerprint('Yw3NKWbEM2aRElRI', hosts)).toBe('host-1')
})
it('returns null for a fingerprint no paired host derives', () => {
expect(resolveHostIdForFingerprint('0123456789abcdef', hosts)).toBeNull()
})
it('rejects a fingerprint of the wrong length', () => {
expect(resolveHostIdForFingerprint('Yw3NKWbEM2aRElRI'.slice(0, 8), hosts)).toBeNull()
})
})
@@ -1,84 +0,0 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import * as Notifications from 'expo-notifications'
import { addPushTokenListener, getDevicePushToken } from './push-token'
vi.mock('expo-notifications', () => ({
getDevicePushTokenAsync: vi.fn(),
addPushTokenListener: vi.fn()
}))
const dev = globalThis as { __DEV__?: boolean }
beforeEach(() => {
vi.clearAllMocks()
})
afterEach(() => {
delete dev.__DEV__
})
describe('getDevicePushToken', () => {
it.each([
[true, 'sandbox'],
[false, 'production']
])('reports apnsEnvironment for a __DEV__=%s iOS build as %s', async (isDev, environment) => {
dev.__DEV__ = isDev
vi.mocked(Notifications.getDevicePushTokenAsync).mockResolvedValue({
type: 'ios',
data: 'a'.repeat(64)
} as never)
await expect(getDevicePushToken()).resolves.toEqual({
platform: 'ios',
token: 'a'.repeat(64),
apnsEnvironment: environment
})
})
it('omits apnsEnvironment for Android, where FCM has no environment split', async () => {
vi.mocked(Notifications.getDevicePushTokenAsync).mockResolvedValue({
type: 'android',
data: 'fcm-registration-token'
} as never)
await expect(getDevicePushToken()).resolves.toEqual({
platform: 'android',
token: 'fcm-registration-token'
})
})
it.each([
['a web push subscription', { type: 'web', data: { endpoint: 'https://example.test' } }],
['an empty token', { type: 'ios', data: '' }]
])('returns null for %s', async (_label, raw) => {
vi.mocked(Notifications.getDevicePushTokenAsync).mockResolvedValue(raw as never)
await expect(getDevicePushToken()).resolves.toBeNull()
})
it('returns null when the shell cannot mint a token at all', async () => {
vi.mocked(Notifications.getDevicePushTokenAsync).mockRejectedValue(new Error('no entitlement'))
await expect(getDevicePushToken()).resolves.toBeNull()
})
})
describe('addPushTokenListener', () => {
it('forwards a rolled native token and removes the subscription on teardown', () => {
const remove = vi.fn()
const native: { emit?: Parameters<typeof Notifications.addPushTokenListener>[0] } = {}
vi.mocked(Notifications.addPushTokenListener).mockImplementation((listener) => {
native.emit = listener
return { remove }
})
const seen: unknown[] = []
const stop = addPushTokenListener((token) => seen.push(token))
native.emit?.({ type: 'android', data: 'rolled' })
native.emit?.({ type: 'web', data: {} })
stop()
expect(seen).toEqual([{ platform: 'android', token: 'rolled' }])
expect(remove).toHaveBeenCalledTimes(1)
})
})
@@ -69,17 +69,6 @@ function createWriteQueue(): WriteQueueHarness {
}
}
function drain(queue: WriteQueueHarness): void {
queue.pump()
let guard = 0
while (queue.pendingWrites.length > 0) {
queue.flushWrite()
if (++guard > 10_000) {
throw new Error('write queue did not drain')
}
}
}
describe('terminal WebView write queue', () => {
// Count queue-reachable string units, not heap bytes; xterm may still retain submitted chunks.
@@ -127,14 +116,4 @@ describe('terminal WebView write queue', () => {
expect(queue.writes).toEqual(['first'])
})
it('drains reentrant enqueues in order', () => {
const queue = createWriteQueue()
queue.enqueue('first')
queue.enqueueBoundary(() => queue.enqueue('reentrant'))
queue.enqueue('second')
drain(queue)
expect(queue.writes).toEqual(['first', 'secondreentrant'])
})
})
@@ -47,14 +47,4 @@ describe('the terminal waits for its bundled glyphs', () => {
expect(clearTextureAtlas).not.toHaveBeenCalled()
expect(fonts.postToHost).toHaveBeenCalledOnce()
})
it('withholds readiness until the font can paint the first terminal snapshot', async () => {
const fonts = pendingFonts()
startMessageBridge(fonts.scope)
expect(fonts.postToHost).not.toHaveBeenCalled()
fonts.finish()
await fonts.loaded
expect(fonts.postToHost).toHaveBeenCalledWith({ type: 'web-ready', cellBox: null })
stopMessageBridge(fonts.scope)
})
})
@@ -9,7 +9,6 @@ const terminals: Terminal[] = []
afterEach(() => {
terminals.splice(0).forEach((term) => term.dispose())
vi.unstubAllGlobals()
vi.useRealTimers()
})
@@ -30,29 +29,6 @@ async function fixture(text: string) {
return { term, scope, select, selected }
}
describe('word selection uses the rendered xterm cell range', () => {
it('keeps cell mapping when a legacy WebView has no grapheme segmenter', async () => {
const f = await fixture('합성 🙂 cafe\u0301')
vi.stubGlobal('Intl', Object.assign(Object.create(Intl), { Segmenter: undefined }))
seedWordSelection(f.scope, 3, 0)
expect(f.select).toHaveBeenLastCalledWith(0, 0, 4)
expect(f.selected()).toBe('합성')
seedWordSelection(f.scope, 6, 0)
expect(f.select).toHaveBeenLastCalledWith(5, 0, 2)
expect(f.selected()).toBe('🙂')
})
it('keeps an absolute scrollback row and tolerates an evicted line', async () => {
const f = await fixture('한글\r\nsecond\r\nthird\r\nfourth')
expect(f.term.buffer.active.viewportY).toBeGreaterThan(0)
seedWordSelection(f.scope, 2, 0)
expect(f.select).toHaveBeenLastCalledWith(0, 0, 4)
expect(f.selected()).toBe('한글')
seedWordSelection(f.scope, 2, 99)
expect(f.select).toHaveBeenLastCalledWith(2, 99, 1)
})
})
describe('Unicode selection boundaries across every rendered column', () => {
it.each([
['한글', 4],
@@ -73,16 +49,4 @@ describe('Unicode selection boundaries across every rendered column', () => {
seedWordSelection(f.scope, 3 + width + 1, 0)
expect(f.selected()).toBe('next')
})
it('keeps the last wide word on the row and the next wrapped row independent', async () => {
const f = await fixture(' '.repeat(56) + '한글next')
for (const col of [56, 57, 58, 59]) {
seedWordSelection(f.scope, col, 0)
expect(f.select).toHaveBeenLastCalledWith(56, 0, 4)
expect(f.selected()).toBe('한글')
}
seedWordSelection(f.scope, 0, 1)
expect(f.select).toHaveBeenLastCalledWith(0, 1, 4)
expect(f.selected()).toBe('next')
})
})
+1 -11
View File
@@ -5,13 +5,11 @@ import { join } from 'node:path'
import { afterAll, describe, expect, it } from 'vitest'
import type { AgentJournalMessageItem } from '../../../shared/agent-session/journal/agent-session-journal-types'
import type { NativeChatBlock } from '../../../shared/native-chat/native-chat-types'
import { AcpPromptContentError, acpDispatchPrompt, acpPromptBlocks } from './acp-prompt-content'
import { AcpPromptContentError, acpPromptBlocks } from './acp-prompt-content'
const PNG = Buffer.from('89504e470d0a1a0a', 'hex')
const dir = mkdtempSync(join(tmpdir(), 'acp-prompt-content-'))
afterAll(() => rmSync(dir, { recursive: true, force: true }))
const pngPath = join(dir, 'shot.png')
writeFileSync(pngPath, PNG)
function message(...blocks: NativeChatBlock[]): AgentJournalMessageItem {
return { kind: 'message', role: 'user', blocks }
@@ -26,14 +24,6 @@ async function refusal(body: AgentJournalMessageItem, images = true) {
}
describe('ACP prompt content', () => {
it('refuses a withheld command typed with arguments', async () => {
const result = await acpDispatchPrompt(message({ type: 'text', text: '/allow-all on' }), {
spec: { agent: 'copilot', dialect: { withheldCommands: ['allow-all'] } },
connection: { initialize: async () => ({ protocolVersion: 1 }) }
})
expect(Array.isArray(result)).toBe(false)
})
it('sends a host image up to 20 MiB inline and keeps a larger one as a path', async () => {
const inlinePath = join(dir, 'photo.png')
writeFileSync(inlinePath, Buffer.alloc(20 * 1024 * 1024))
@@ -87,22 +87,6 @@ describe('WSL host IPC: the user Add and Remove (D3a)', () => {
service.add.mockClear()
})
it('refuses an Add in serve-only and says so in its capability, keeping saved hosts usable', async () => {
vi.stubEnv('ORCA_INTERNAL_WSL_HOSTS', '0')
register()
expect(await handlers.get('wslHosts:add')?.({}, { distro: 'Ubuntu' })).toMatchObject({
ok: false,
kind: 'disabled'
})
expect(service.add).not.toHaveBeenCalled()
expect(await handlers.get('wslHosts:getCapability')?.({})).toMatchObject({
acceptsNewWork: false
})
await handlers.get('wslHosts:remove')?.({}, { environmentId: 'wsl:Ubuntu' })
expect(service.remove).toHaveBeenCalledWith('wsl:Ubuntu')
})
// A block published while Orca runs must stop new hosts now, not at the next launch.
it('a live policy block refuses an Add at once, tells the window, and keeps the saved host served', async () => {
vi.stubEnv('ORCA_INTERNAL_WSL_HOSTS', '1')
@@ -1,91 +0,0 @@
import type * as WslMoveDriverPreviewModule from '../../persistence/moving-wsl-projects/execution/wsl-move-driver-preview'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { WSL_HOSTS_INTERNAL_GATE_ENV } from '../../../shared/wsl-host-internal-gate'
type Handler = (event: unknown, request?: unknown) => unknown
const handlers = vi.hoisted(() => new Map<string, Handler>())
vi.mock('electron', () => ({
ipcMain: {
removeHandler: (channel: string) => handlers.delete(channel),
handle: (channel: string, handler: Handler) => handlers.set(channel, handler)
}
}))
const driver = vi.hoisted(() => ({
previewWslMove: vi.fn(async () => ({ outcome: 'ready' })),
moveProjectsToWslHost: vi.fn(async () => ({ outcome: 'moved' })),
cancelWslMove: vi.fn(() => true)
}))
vi.mock('../../persistence/moving-wsl-projects/wsl-move-driver', () => ({
moveProjectsToWslHost: driver.moveProjectsToWslHost
}))
vi.mock(
'../../persistence/moving-wsl-projects/execution/wsl-move-driver-preview',
async (importOriginal) => ({
...(await importOriginal<typeof WslMoveDriverPreviewModule>()),
previewWslMove: driver.previewWslMove
})
)
const running = vi.hoisted(() => ({ any: false }))
vi.mock('../../persistence/moving-wsl-projects/orchestration/wsl-move-running-moves', () => ({
isAnyWslMoveRunning: () => running.any,
cancelWslMove: driver.cancelWslMove
}))
const track = vi.hoisted(() => vi.fn())
vi.mock('../../telemetry/client', () => ({ track }))
import { registerWslMoveHandlers } from './wsl-move-handlers'
const MAIN_WINDOW_ID = 7
const progress = vi.fn()
const deps = vi.fn(() => ({ now: () => new Date(0), progress }))
const invoke = (channel: string, request?: unknown, senderId = MAIN_WINDOW_ID) =>
handlers.get(channel)?.({ sender: { id: senderId } }, request)
beforeEach(() => {
vi.stubEnv(WSL_HOSTS_INTERNAL_GATE_ENV, '1')
deps.mockClear()
track.mockClear()
for (const fn of Object.values(driver)) {
fn.mockClear()
}
registerWslMoveHandlers(
() => '/unused',
(senderId) => senderId === MAIN_WINDOW_ID,
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: The driver is mocked; the handler only passes deps through.
deps as never
)
})
afterEach(() => vi.unstubAllEnvs())
describe('WSL move driver IPC', () => {
it('is unreachable with the internal WSL hosts gate closed: nothing is built or run', async () => {
vi.stubEnv(WSL_HOSTS_INTERNAL_GATE_ENV, '0')
const refused = { outcome: 'refused', code: 'wsl_move_unavailable' }
expect(await invoke('wslMove:preview', { distro: 'Ubuntu' })).toEqual(refused)
expect(await invoke('wslMove:start', { distro: 'Ubuntu' })).toEqual(refused)
expect(invoke('wslMove:cancel', 'Ubuntu')).toBe(false)
expect(deps).not.toHaveBeenCalled()
expect(driver.previewWslMove).not.toHaveBeenCalled()
expect(driver.moveProjectsToWslHost).not.toHaveBeenCalled()
expect(driver.cancelWslMove).not.toHaveBeenCalled()
})
it('refuses another window, and a malformed request', async () => {
expect(await invoke('wslMove:start', { distro: 'Ubuntu' }, 99)).toMatchObject({
code: 'wsl_move_unavailable',
detail: 'not-main-window'
})
for (const bad of [
undefined,
'Ubuntu',
{ distro: '' },
{ distro: '..\\x' },
{ distro: 'U', repoIds: [1] }
]) {
expect(await invoke('wslMove:start', bad)).toMatchObject({ detail: 'invalid-request' })
}
expect(driver.moveProjectsToWslHost).not.toHaveBeenCalled()
})
})
@@ -297,36 +297,6 @@ describe('orchestration federated worker output', () => {
return dispatchId
}
it('routes show and read by Dispatch without repeating the worker server', async () => {
const dispatchId = await startRemoteWorker()
const shown = await homeCall('rpc_remote_show', 'orchestration.workerShow', {
dispatch: dispatchId
})
const read = await homeCall('rpc_remote_read', 'orchestration.workerRead', {
dispatch: dispatchId,
limit: 20
})
expect(shown).toMatchObject({
ok: true,
result: {
server: { environmentId: 'environment_windows', name: 'windows' },
observation: { status: 'live', exactWorker: true },
terminal: { handle: 'term_windows_worker' }
}
})
expect(read).toMatchObject({
ok: true,
result: {
source: 'terminal',
fallbackReason: 'session_not_reported',
server: { environmentId: 'environment_windows', name: 'windows' },
terminal: { tail: ['remote output'] }
}
})
})
it('reads the exact transcript on the worker server without leaking its path home', async () => {
const dispatchId = await startRemoteWorker()
const directory = await mkdtemp(join(tmpdir(), 'orca-federated-worker-output-'))
@@ -455,84 +425,4 @@ describe('orchestration federated worker output', () => {
homeOmitsAutoRelease = omitted
}
})
it('does not report a captured remote archive or close when persistence fails', async () => {
const dispatchId = await startRemoteWorker()
workerDb.db.exec('DROP TABLE worker_terminal_archives')
const released = await homeDispatcher.dispatch({
id: 'rpc_remote_release_archive_failure',
authToken: 'coordinator-token',
orchestrationContractVersion: ORCHESTRATION_CONTRACT_VERSION,
orchestrationRequestId: 'remote_release_archive_failure',
method: 'orchestration.workerRelease',
params: { dispatch: dispatchId }
})
expect(released).toMatchObject({
ok: true,
result: { state: 'retained', processAction: 'none', archive: null }
})
expect(workerRuntime.closeTerminal).not.toHaveBeenCalled()
})
it('keeps reads, fleet snapshots, and release on legacy fallbacks for an old peer', async () => {
workerAdvertisesNewCapabilities = false
// Ask shipped hosts directly for structured output; only method_not_found permits terminal fallback.
workerSupportsStructuredRead = false
const dispatchId = await startRemoteWorker()
remoteCalls = []
const read = await homeCall('rpc_old_peer_read', 'orchestration.workerRead', {
dispatch: dispatchId
})
const fleet = await homeCall('rpc_old_peer_fleet', 'orchestration.workerList', {
includeRemote: true
})
const release = await homeDispatcher.dispatch({
id: 'rpc_old_peer_release',
authToken: 'coordinator-token',
orchestrationContractVersion: ORCHESTRATION_CONTRACT_VERSION,
orchestrationRequestId: 'old_peer_release',
method: 'orchestration.workerRelease',
params: { dispatch: dispatchId }
})
expect(read).toMatchObject({
ok: true,
result: { fallbackReason: 'remote_capability_unavailable' }
})
expect(fleet).toMatchObject({
ok: true,
result: { partialHostErrors: [{ code: 'capability_unsupported' }] }
})
expect(release).toMatchObject({
ok: true,
result: { state: 'retained', reason: 'federation_unsupported' }
})
expect(remoteCalls).toContain('orchestration.federationReadOutput')
expect(remoteCalls).toContain('orchestration.federationFleetSnapshot')
expect(remoteCalls).not.toContain('orchestration.federationRelease')
})
it('retains a mixed-version worker when its host cannot guarantee a durable archive', async () => {
workerAdvertisesDurableRelease = false
const dispatchId = await startRemoteWorker()
remoteCalls = []
const release = await homeDispatcher.dispatch({
id: 'rpc_nondurable_peer_release',
authToken: 'coordinator-token',
orchestrationContractVersion: ORCHESTRATION_CONTRACT_VERSION,
orchestrationRequestId: 'nondurable_peer_release',
method: 'orchestration.workerRelease',
params: { dispatch: dispatchId }
})
expect(release).toMatchObject({
ok: true,
result: { state: 'retained', reason: 'federation_unsupported', archive: null }
})
expect(remoteCalls).not.toContain('orchestration.federationRelease')
expect(workerRuntime.closeTerminal).not.toHaveBeenCalled()
})
})
-35
View File
@@ -1,35 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
vi.mock('electron', () => ({
ipcRenderer: { invoke: vi.fn(), on: vi.fn(), removeListener: vi.fn() }
}))
import {
formatWslHostsPolicyBlockedArgument,
formatWslHostsServeOnlyArgument,
WSL_HOSTS_INTERNAL_GATE_ENV
} from '../../shared/wsl-host-internal-gate'
import { getWslHostsApi } from './wsl-hosts-api'
describe('getWslHostsApi', () => {
it('is present on Windows with no gate variable set', () => {
expect(getWslHostsApi({}, 'win32', [])).toBeDefined()
})
it.each<NodeJS.Platform>(['darwin', 'linux'])('is absent on %s', (platform) => {
expect(getWslHostsApi({}, platform, [])).toBeUndefined()
})
it('is absent on Windows when the launch turns the gate off', () => {
expect(getWslHostsApi({ [WSL_HOSTS_INTERNAL_GATE_ENV]: '0' }, 'win32', [])).toBeUndefined()
})
it('is absent on Windows when the remote policy blocked the gate', () => {
expect(getWslHostsApi({}, 'win32', [formatWslHostsPolicyBlockedArgument()])).toBeUndefined()
})
it('stays present behind a closed gate when main stamped serve-only', () => {
const env = { [WSL_HOSTS_INTERNAL_GATE_ENV]: '0' }
expect(getWslHostsApi(env, 'win32', [formatWslHostsServeOnlyArgument()])).toBeDefined()
})
})
@@ -2,7 +2,6 @@ import { describe, expect, it } from 'vitest'
import {
parsePluginGrammarArtifact,
parsePluginLanguageConfiguration,
parsePluginSyntaxPayload,
validatePluginGrammarIncludes
} from './plugin-syntax-artifact'
@@ -11,12 +10,6 @@ function grammar(scopeName: string, body: Record<string, unknown> = {}): string
}
describe('plugin syntax artifacts', () => {
it('refuses a grammar whose scope differs from its declaration', () => {
expect(() => parsePluginGrammarArtifact(grammar('source.other'), 'source.demo')).toThrow(
'does not match'
)
})
it('refuses oversized and deeply nested grammars', () => {
const huge = grammar('source.demo', { comment: 'x'.repeat(1024 * 1024) })
expect(() => parsePluginGrammarArtifact(huge, 'source.demo')).toThrow('exceeds')
@@ -82,24 +75,4 @@ describe('plugin syntax artifacts', () => {
surroundingPairs: []
})
})
it('bounds language configuration and validates payloads at renderer ingress', () => {
expect(
parsePluginLanguageConfiguration(
JSON.stringify({ comments: { lineComment: '//' }, brackets: [['{', '}']] })
)
).toEqual({ comments: { lineComment: '//' }, brackets: [['{', '}']] })
expect(() =>
parsePluginLanguageConfiguration(JSON.stringify({ brackets: [['', '}']] }), 'strict')
).toThrow()
const payload = {
id: 'plugin:samples.demo/demo',
revision: 'abc',
scopeName: 'source.demo',
grammars: [{ scopeName: 'source.demo', patterns: [], repository: {} }],
configuration: {}
}
expect(parsePluginSyntaxPayload(payload)).toEqual(payload)
expect(parsePluginSyntaxPayload({ ...payload, grammars: [] })).toBeNull()
})
})
-23
View File
@@ -1,23 +0,0 @@
import { describe, expect, it } from 'vitest'
import { isWslHostsInternalGateOpen, WSL_HOSTS_INTERNAL_GATE_ENV } from './wsl-host-internal-gate'
describe('WSL hosts internal gate', () => {
it('is open when the variable is unset', () => {
expect(isWslHostsInternalGateOpen({})).toBe(true)
})
it.each(['0', 'false', 'off', 'no', 'FALSE', ' Off ', 'No\n', '"0"', "'off'", ' "False" '])(
'is closed for %j',
(value) => {
expect(isWslHostsInternalGateOpen({ [WSL_HOSTS_INTERNAL_GATE_ENV]: value })).toBe(false)
}
)
// Empty reads like unset; anything else that isn't an off word keeps today's internal "on".
it.each(['1', 'true', 'on', 'yes', '', ' ', 'disabled', '""', '"0'])(
'is open for %j',
(value) => {
expect(isWslHostsInternalGateOpen({ [WSL_HOSTS_INTERNAL_GATE_ENV]: value })).toBe(true)
}
)
})
+1 -29
View File
@@ -1,8 +1,7 @@
/**
* The WSL move dialog against a fake move transport: no wsl.exe or distro in CI. Main's
* `wslMove:*` handlers are swapped for a scripted host, so the real dialog runs open preview →
* blockers → skip → start → progress → refusal → start again → moved → reload. A second test
* proves that with the internal gate closed none of it shows and main refuses.
* blockers → skip → start → progress → refusal → start again → moved → reload.
*/
import { execFileSync } from 'node:child_process'
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
@@ -409,30 +408,3 @@ test('moves projects through preview, blockers, progress and result', async ({},
rmSync(path.dirname(webPath), { recursive: true, force: true })
}
})
// oxlint-disable-next-line no-empty-pattern -- Playwright passes fixtures before testInfo.
test('with the internal gate closed, no move entry shows and main refuses', async ({}, testInfo) => {
// The gated-off launch still boots a full app before main is asked to refuse.
test.setTimeout(180_000)
const desktop = await launch(testInfo, false)
const { page } = desktop
try {
expect(
await page.evaluate((distro) => window.api.wslMove.preview({ distro }), DISTRO)
).toMatchObject({ outcome: 'refused', code: 'wsl_move_unavailable' })
expect(await page.evaluate(() => window.api.wslMove.listUnfinished())).toEqual([])
// Serve-only (#34683): the saved host still lists, but nothing new is added or moved.
await openServersPane(page)
const hosts = page.locator('[data-settings-section="wsl-hosts"]')
await expect(hosts.getByRole('button', { name: 'Add WSL distro' })).toHaveCount(0)
await hosts.getByRole('button', { name: `More actions for ${DISTRO} (WSL)` }).click()
await expect(page.getByRole('menuitem', { name: 'Remove…' })).toBeVisible()
await expect(
page.getByRole('menuitem', { name: 'Move projects from this distro…' })
).toHaveCount(0)
await page.keyboard.press('Escape')
await expect(page.getByTestId('wsl-move-dialog')).toHaveCount(0)
} finally {
await desktop.dispose()
}
})