test: strengthen watcher and mobile async assertions (#33641)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-10-25 14:49:49 -07:00
committed by GitHub
co-authored by OrcaWin
parent 883b768bc7
commit 3fbf89bb4e
15 changed files with 88 additions and 85 deletions
-1
View File
@@ -429,7 +429,6 @@
1 test-no-real-time-wait:src/renderer/src/components/dashboard-popout/useDashboardSnapshot.test.tsx
1 test-no-real-time-wait:src/renderer/src/components/editor/combined-diff/load-sections/combined-diff-stale-analysis-work-budget.test.tsx
1 test-no-real-time-wait:src/renderer/src/components/editor/DiagramViewerModal.test.tsx
2 test-no-real-time-wait:src/renderer/src/components/mobile/MobilePage.test.tsx
1 test-no-real-time-wait:src/renderer/src/components/native-chat/native-chat-composer-draft-lifecycle.test.tsx
6 test-no-real-time-wait:src/renderer/src/components/native-chat/native-chat-composer-draft-store.test.ts
1 test-no-real-time-wait:src/renderer/src/components/native-chat/NativeChatModelCombobox.test.tsx
@@ -116,6 +116,15 @@ describe('WatcherProcessCrashFuse', () => {
expect(fuse.isOpen(crashAt)).toBe(false)
fuse.recordCrash(crashAt)
expect(fuse.isOpen(crashAt + 2 * CRASH_FUSE_COOLDOWN_INITIAL_MS - 1)).toBe(true)
const nextTrialAt = crashAt + 2 * CRASH_FUSE_COOLDOWN_INITIAL_MS
expect(fuse.isOpen(nextTrialAt)).toBe(false)
fuse.recordDeliveryHealth('healthy', nextTrialAt)
fuse.recordDeliveryHealth('unknown', nextTrialAt + CRASH_FUSE_TRIAL_STABLE_MS - 1)
const stableAt = nextTrialAt + CRASH_FUSE_TRIAL_STABLE_MS
expect(fuse.isOpen(stableAt)).toBe(false)
fuse.recordCrash(stableAt + 1)
expect(fuse.isOpen(stableAt + 1)).toBe(false)
})
it('closes immediately when explicitly reset', () => {
@@ -37,7 +37,7 @@ it('joins disposal and does not confuse disconnect/error with physical exit', as
const result = first.then(disposed)
c.events.emit('disconnect')
c.events.emit('error', new Error('spawn or IPC failure'))
await Promise.resolve()
await new Promise<void>((resolve) => setImmediate(resolve))
expect(disposed).not.toHaveBeenCalled()
expect(logical).toHaveBeenCalledOnce()
c.close()
@@ -55,7 +55,7 @@ it('includes children already signaled by synchronous or retired-owner disposal'
const disposed = vi.fn()
const result = owner.disposeAndWait(() => {}).then(disposed)
replacement.close()
await Promise.resolve()
await new Promise<void>((resolve) => setImmediate(resolve))
expect(disposed).not.toHaveBeenCalled()
old.close()
await result
@@ -71,7 +71,7 @@ it('retains a child after termination deadline failure and supports explicit ret
expect(await result).toMatchObject({ message: 'watcher_owned_children_shutdown_incomplete' })
const completed = vi.fn()
const retry = owner.disposeAndWait(() => {}).then(completed)
await Promise.resolve()
await vi.advanceTimersByTimeAsync(0)
expect(completed).not.toHaveBeenCalled()
c.close()
await retry
@@ -96,7 +96,7 @@ it('waits for other children even when logical disposal and one kill fail', asyn
finished()
return error
})
await Promise.resolve()
await new Promise<void>((resolve) => setImmediate(resolve))
expect(finished).not.toHaveBeenCalled()
pending.close()
expect(await result).toMatchObject({ errors: [logicalFailure, expect.any(Error)] })
@@ -32,7 +32,7 @@ describe('disposeWatcherProcessAndWait', () => {
settled()
throw error
})
await Promise.resolve()
await new Promise<void>((resolve) => setImmediate(resolve))
expect(settled).not.toHaveBeenCalled()
finishPool()
await expect(disposal).rejects.toMatchObject({ message: 'watcher_process_shutdown_incomplete' })
@@ -86,21 +86,6 @@ describe('subscribeViaWatcherProcess', () => {
expect(callback).toHaveBeenCalledWith(null, events)
})
it('retires the failed native subscription before reporting its error', async () => {
const callback = vi.fn()
const promise = subscribeViaWatcherProcess('/repo', callback, {})
const child = currentChild()
const id = ackSubscribe(child)
await promise
child.emit('message', { op: 'watch-error', id, message: 'boom' })
expect(callback).not.toHaveBeenCalled()
expect(child.kill).toHaveBeenCalledTimes(1)
child.emit('exit', 0, null)
await vi.waitFor(() => expect(callback).toHaveBeenCalledTimes(1))
expect(callback).toHaveBeenCalledWith(expect.objectContaining({ message: 'boom' }), [])
})
it('rejects and physically cancels a watcher error before subscription readiness', async () => {
const callback = vi.fn()
const promise = subscribeViaWatcherProcess('/repo', callback, {})
@@ -115,8 +100,10 @@ describe('subscribeViaWatcherProcess', () => {
settled = true
})
expect(child.sent.at(-1)).toEqual({ op: 'cancel-subscribe', id })
await new Promise<void>((resolve) => setImmediate(resolve))
expect(settled).toBe(false)
child.emit('message', { op: 'cancel-requires-restart', id })
await new Promise<void>((resolve) => setImmediate(resolve))
expect(settled).toBe(false)
expect(callback).not.toHaveBeenCalled()
expect(child.kill).toHaveBeenCalledTimes(1)
@@ -365,6 +352,7 @@ describe('subscribeViaWatcherProcess', () => {
const isSettled = trackPromiseSettlement(pending)
expect(child.kill).not.toHaveBeenCalled()
expect(child.sent.at(-1)).toEqual({ op: 'cancel-subscribe', id: queuedId })
await new Promise<void>((resolve) => setImmediate(resolve))
expect(isSettled()).toBe(false)
child.emit('message', { op: 'unsubscribed', id: queuedId })
await expect(pending).rejects.toThrow('aborted')
@@ -443,6 +431,7 @@ describe('subscribeViaWatcherProcess', () => {
expect(first.sent.at(-1)).toEqual({ op: 'cancel-subscribe', id: pendingId })
first.emit('message', { op: 'cancel-requires-restart', id: pendingId })
expect(first.kill).toHaveBeenCalledTimes(1)
await new Promise<void>((resolve) => setImmediate(resolve))
expect(isSettled()).toBe(false)
expect(forkMock).toHaveBeenCalledTimes(1)
first.emit('exit', 0, null)
@@ -695,6 +684,7 @@ describe('subscribeViaWatcherProcess', () => {
const unsubscribe = subscription.unsubscribe()
const isSettled = trackPromiseSettlement(unsubscribe)
expect(first.kill).toHaveBeenCalledTimes(1)
await new Promise<void>((resolve) => setImmediate(resolve))
expect(isSettled()).toBe(false)
expect(rmSyncMock).toHaveBeenCalledWith('/tmp/orca-watcher-canary-supervisor-test', {
recursive: true,
@@ -757,12 +747,17 @@ describe('subscribeViaWatcherProcess', () => {
const second = await secondPromise
const firstUnsub = first.unsubscribe()
const firstSettled = trackPromiseSettlement(firstUnsub)
expect(child.kill).not.toHaveBeenCalled()
expect(child.sent.at(-1)).toMatchObject({ op: 'unsubscribe' })
// Both callers may gate destructive cleanup, so both must await physical exit.
const secondUnsub = second.unsubscribe()
const secondSettled = trackPromiseSettlement(secondUnsub)
expect(child.kill).toHaveBeenCalledTimes(1)
await new Promise<void>((resolve) => setImmediate(resolve))
expect(firstSettled()).toBe(false)
expect(secondSettled()).toBe(false)
child.emit('exit', 0, null)
await expect(firstUnsub).resolves.toBeUndefined()
await expect(secondUnsub).resolves.toBeUndefined()
@@ -801,7 +796,7 @@ describe('subscribeViaWatcherProcess', () => {
expect(unsubscribe).toHaveBeenCalled()
})
it('rejects a pre-aborted in-process subscribe before loading Parcel', async () => {
it('rejects a pre-aborted in-process subscribe before invoking Parcel subscribe', async () => {
vi.stubEnv('VITEST', 'true')
const controller = new AbortController()
controller.abort()
@@ -77,6 +77,8 @@ describe('shallow watcher subscription', () => {
}
)
emit(root, 'unrelated')
emit(join(root, 'logs'), 'unrelated')
emit(root, 'config')
emit(join(root, 'logs'), 'HEAD')
await promise
@@ -84,6 +86,7 @@ describe('shallow watcher subscription', () => {
expect(events).toContain(join(root, 'config'))
expect(events).toContain(join(root, 'logs', 'HEAD'))
expect(events).not.toContain(join(root, 'unrelated'))
expect(events).not.toContain(join(root, 'logs', 'unrelated'))
await subscription.unsubscribe()
} finally {
await rm(root, { recursive: true, force: true })
@@ -130,9 +130,11 @@ describe('WatcherProcessSupervisor capacity wait', () => {
)
await Promise.resolve()
expect(forkMock).toHaveBeenCalledTimes(MAX_PHYSICAL_WATCHER_CHILDREN + 1)
const secondBlockedSettled = trackPromiseSettlement(secondBlocked)
firstController.abort()
await expect(firstBlocked).rejects.toMatchObject({ code: 'subscribe_aborted' })
expect(trackPromiseSettlement(secondBlocked)()).toBe(false)
await vi.advanceTimersByTimeAsync(0)
expect(secondBlockedSettled()).toBe(false)
secondController.abort()
await expect(secondBlocked).rejects.toMatchObject({ code: 'subscribe_aborted' })
@@ -163,8 +163,8 @@ describe('watcher native unsubscribe timeout', () => {
await siblingPromise
const result = firstSubscription.unsubscribe().catch((error: unknown) => error)
await vi.advanceTimersByTimeAsync(WATCHER_PROCESS_UNSUBSCRIBE_TIMEOUT_MS)
const isSettled = trackPromiseSettlement(result)
await vi.advanceTimersByTimeAsync(WATCHER_PROCESS_UNSUBSCRIBE_TIMEOUT_MS)
expect(isSettled()).toBe(false)
await vi.advanceTimersByTimeAsync(WATCHER_PROCESS_EXIT_DEADLINE_MS)
@@ -1,9 +1,5 @@
import { afterEach, describe, expect, it } from 'vitest'
import {
MACOS_FSEVENTS_EXCLUSION_PATH_LIMIT,
WATCHER_IGNORE_DIRS,
buildParcelWatcherIgnoreOptions
} from './filesystem-watcher-ignore'
import { WATCHER_IGNORE_DIRS, buildParcelWatcherIgnoreOptions } from './filesystem-watcher-ignore'
const realPlatform = process.platform
@@ -22,8 +18,8 @@ describe('buildParcelWatcherIgnoreOptions', () => {
const option = options.ignore ?? []
// FSEvents rejects all plain exclusions above eight paths, silently restoring node_modules/.git churn.
const plainPaths = option.filter((entry) => !entry.includes('*'))
expect(plainPaths.length).toBeLessThanOrEqual(MACOS_FSEVENTS_EXCLUSION_PATH_LIMIT)
expect(option).toEqual(WATCHER_IGNORE_DIRS.slice(0, MACOS_FSEVENTS_EXCLUSION_PATH_LIMIT))
expect(plainPaths.length).toBeLessThanOrEqual(8)
expect(option).toEqual(WATCHER_IGNORE_DIRS.slice(0, 8))
const fallbackRegex = new RegExp(options.ignoreGlobs?.[0] ?? '(?!)')
for (const dir of WATCHER_IGNORE_DIRS) {
expect(fallbackRegex.test(dir)).toBe(true)
@@ -17,7 +17,7 @@ afterEach(() => {
})
describe('showMobileEmulatorHiddenToast', () => {
it('auto-dismisses after 30s while staying dismissible', () => {
it('requests a dismissible toast with a 30-second duration', () => {
showMobileEmulatorHiddenToast({
openSettingsPage: vi.fn() as unknown as AppState['openSettingsPage'],
openSettingsTarget: vi.fn() as unknown as AppState['openSettingsTarget']
@@ -206,7 +206,7 @@ describe('HeroFlow height', () => {
expect(screen.getByText('Sign in to create a Relay pairing code')).toBeInTheDocument()
})
it('disables Relay recovery immediately and delays visible retry feedback', async () => {
it('disables Relay recovery while busy and eventually shows retry feedback', async () => {
renderFlow(1, {
pairLoading: true,
relayMintFailure: {
@@ -220,7 +220,7 @@ describe('HeroFlow height', () => {
await waitFor(() => expect(screen.getByText(/Creating a new pairing code/)).toBeVisible())
})
it('offers Retry Relay when the desktop provider is missing, since a retry installs it', () => {
it('offers Retry Relay when the desktop provider is missing', () => {
renderFlow(1, {
relayMintFailure: {
code: 'relay_provider_unavailable',
@@ -257,12 +257,12 @@ describe('HeroFlow height', () => {
expect(screen.queryByRole('button', { name: 'Retry Relay' })).toBeNull()
})
it('hides the mint-failure notice when a Relay QR is shown', () => {
it('shows a Relay QR without a failure notice when no failure is supplied', () => {
renderFlow(1, { pairQrDataUrl: 'data:image/png;base64,qr' })
expect(screen.queryByTestId('relay-mint-failure-notice')).not.toBeInTheDocument()
})
it('renders a pairing QR at its natural integer-scaled bitmap size', () => {
it('uses the supplied QR bitmap size and adds frame padding', () => {
renderFlow(1, { pairQrDataUrl: 'data:image/png;base64,qr', pairQrSize: 218 })
const image = screen.getByRole('img', { name: 'Pairing QR' })
@@ -368,7 +368,7 @@ describe('HeroFlow height', () => {
expect(refresh).toHaveFocus()
})
it('lets the user name this computer in the pairing step, ahead of the code', () => {
it('places the machine-name field slot ahead of the pairing code', () => {
const props: React.ComponentProps<typeof MobileHeroPairingStep> = {
pairQrDataUrl: 'data:image/png;base64,AAAA',
pairingUrl: 'orca://pair?code=abc',
@@ -2,7 +2,7 @@
import '@testing-library/jest-dom/vitest'
import { cleanup, render, screen, waitFor } from '@testing-library/react'
import { act, cleanup, render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { MobilePairingConnectionMode } from '../../../../shared/mobile-pairing-connection-mode'
@@ -283,7 +283,7 @@ describe('MobilePage pairing connection mode', () => {
await user.click(screen.getByRole('button', { name: 'Orca Relay' }))
await waitFor(() => expect(screen.getByTestId('mode')).toHaveTextContent('automatic'))
await waitFor(() => expect(screen.getByTestId('pairing-qr')).toHaveTextContent('none'))
await new Promise((resolve) => setTimeout(resolve, 20))
await act(async () => {})
expect(getPairingQR).not.toHaveBeenCalled()
expect(screen.getByTestId('can-generate')).toHaveTextContent('false')
})
@@ -297,7 +297,7 @@ describe('MobilePage pairing connection mode', () => {
getPairingQR.mockClear()
await user.click(screen.getByRole('button', { name: 'Change address' }))
await new Promise((resolve) => setTimeout(resolve, 20))
await act(async () => {})
expect(getPairingQR).not.toHaveBeenCalled()
expect(screen.getByTestId('pairing-qr')).toHaveTextContent('none')
})
@@ -350,7 +350,7 @@ describe('MobilePage pairing connection mode', () => {
expect(screen.getByTestId('pairing-url')).toHaveTextContent('none')
})
it('keeps the copy fallback when the real encoder cannot render the offer', async () => {
it('keeps the copy fallback when the host reports QR encoding failure', async () => {
getPairingQR.mockResolvedValue({
available: true,
qrDataUrl: null,
@@ -367,7 +367,7 @@ describe('MobilePage pairing connection mode', () => {
expect(screen.getByTestId('pairing-url')).toHaveTextContent('copy-fallback')
})
it('surfaces Relay failure and retries with a rotated credential', async () => {
it('surfaces Relay failure and requests credential rotation on retry', async () => {
getPairingQR.mockResolvedValueOnce({
available: false,
reason: 'relay_mint_failed',
@@ -439,12 +439,14 @@ describe('MobilePage pairing connection mode', () => {
expect(getPairingQR).toHaveBeenLastCalledWith({ connectionMode: 'local-only' })
)
await waitFor(() => expect(screen.getByTestId('pairing-qr')).toHaveTextContent('base64,local'))
resolveRetry?.({
available: true,
qrDataUrl: 'data:image/png;base64,stale-relay',
pairingUrl: 'orca://pair#stale-relay',
endpoint: 'ws://relay',
connectionMode: 'automatic'
await act(async () => {
resolveRetry?.({
available: true,
qrDataUrl: 'data:image/png;base64,stale-relay',
pairingUrl: 'orca://pair#stale-relay',
endpoint: 'ws://relay',
connectionMode: 'automatic'
})
})
await waitFor(() => expect(screen.getByTestId('pairing-qr')).toHaveTextContent('base64,local'))
expect(screen.getByTestId('mode')).toHaveTextContent('local-only')
@@ -587,7 +589,11 @@ describe('MobilePage pairing connection mode', () => {
await user.click(screen.getByRole('button', { name: 'Continue' }))
await waitFor(() => expect(getPairingQR).toHaveBeenCalledTimes(2))
resolveAbandonedMint?.({ available: true, qrDataUrl: 'abandoned' })
await act(async () => {
resolveAbandonedMint?.({ available: true, qrDataUrl: 'abandoned' })
})
expect(screen.getByTestId('pairing-qr')).toHaveTextContent('none')
expect(screen.getByTestId('pair-loading')).toHaveTextContent('true')
resolveCurrentMint?.({ available: true, qrDataUrl: 'current' })
await waitFor(() => expect(screen.getByTestId('pairing-qr')).toHaveTextContent('current'))
@@ -668,7 +674,9 @@ describe('MobilePage pairing connection mode', () => {
await user.click(screen.getByRole('button', { name: 'Continue' }))
await waitFor(() => expect(listNetworkInterfaces).toHaveBeenCalledTimes(2))
resolveFirstLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] })
await act(async () => {
resolveFirstLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] })
})
await waitFor(() => expect(screen.getByTestId('pair-loading')).toHaveTextContent('true'))
expect(getPairingQR).not.toHaveBeenCalled()
expect(screen.getByTestId('selected-address')).toHaveTextContent('none')
@@ -711,7 +719,9 @@ describe('MobilePage pairing connection mode', () => {
await waitFor(() => expect(listNetworkInterfaces).toHaveBeenCalledTimes(2))
// The old lookup cannot release minting before its replacement chooses the address.
resolveEntryLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] })
await act(async () => {
resolveEntryLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] })
})
await waitFor(() => expect(screen.getByTestId('pair-loading')).toHaveTextContent('true'))
expect(getPairingQR).not.toHaveBeenCalled()
expect(screen.getByTestId('selected-address')).toHaveTextContent('none')
@@ -758,10 +768,10 @@ describe('MobilePage pairing connection mode', () => {
await waitFor(() => expect(screen.getByTestId('pair-loading')).toHaveTextContent('false'))
// A late abandoned visit must not roll back the settled marker and strand Step 2.
resolveAbandonedLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] })
await waitFor(() =>
expect(screen.getByTestId('refreshing-addresses')).toHaveTextContent('false')
)
await act(async () => {
resolveAbandonedLookup?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.5' }] })
})
expect(screen.getByTestId('refreshing-addresses')).toHaveTextContent('false')
expect(screen.getByTestId('pair-loading')).toHaveTextContent('false')
expect(screen.getByTestId('selected-address')).toHaveTextContent('10.0.0.9')
expect(getPairingQR).toHaveBeenCalledTimes(1)
@@ -797,10 +807,10 @@ describe('MobilePage pairing connection mode', () => {
rotate: true
})
resolveStaleRefresh?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.1' }] })
await waitFor(() =>
expect(screen.getByTestId('refreshing-addresses')).toHaveTextContent('false')
)
await act(async () => {
resolveStaleRefresh?.({ interfaces: [{ name: 'Wi-Fi', address: '10.0.0.1' }] })
})
expect(screen.getByTestId('refreshing-addresses')).toHaveTextContent('false')
expect(screen.getByTestId('selected-address')).toHaveTextContent('10.0.0.7')
expect(getPairingQR).toHaveBeenCalledTimes(2)
@@ -879,11 +889,11 @@ describe('MobilePage pairing connection mode', () => {
)
expect(listNetworkInterfaces).toHaveBeenCalledOnce()
resolveRefresh?.({ interfaces: [{ name: 'Ethernet', address: '10.0.0.2' }] })
await act(async () => {
resolveRefresh?.({ interfaces: [{ name: 'Ethernet', address: '10.0.0.2' }] })
})
await waitFor(() =>
expect(screen.getByTestId('refreshing-addresses')).toHaveTextContent('false')
)
expect(screen.getByTestId('refreshing-addresses')).toHaveTextContent('false')
expect(screen.getByTestId('selected-address')).toHaveTextContent('wss://custom.example/large')
expect(getPairingQR).not.toHaveBeenCalledWith({
address: '10.0.0.2',
@@ -94,7 +94,9 @@ describe('WindowsFirewallNotice', () => {
const user = userEvent.setup()
render(<WindowsFirewallNotice pairingReady address="192.168.0.108" />)
await user.click(await screen.findByRole('button', { name: /allow phone connections/i }))
const repairButton = await screen.findByRole('button', { name: /allow phone connections/i })
expect(repairWindowsFirewall).not.toHaveBeenCalled()
await user.click(repairButton)
expect(repairWindowsFirewall).toHaveBeenCalledTimes(1)
await waitFor(() => expect(getWindowsFirewallStatus).toHaveBeenCalledTimes(2))
await waitFor(() =>
@@ -205,9 +207,11 @@ describe('WindowsFirewallNotice', () => {
getWindowsFirewallStatus
})
render(<WindowsFirewallNotice pairingReady address="10.0.0.4" />)
await act(async () => {
render(<WindowsFirewallNotice pairingReady address="10.0.0.4" />)
})
await waitFor(() => expect(getWindowsFirewallStatus).toHaveBeenCalledTimes(1))
expect(getWindowsFirewallStatus).toHaveBeenCalledTimes(1)
expect(screen.queryByRole('button', { name: /allow phone connections/i })).toBeNull()
})
})
@@ -4,17 +4,6 @@ import { getMobilePaneSearchEntries, shouldOpenMobilePairingAddress } from './mo
import { matchesSettingsSearch } from './settings-search'
describe('getMobilePaneSearchEntries', () => {
// Catalog order breaks search-score ties; preserve Network Interface ranking after catalog splits.
it('keeps Network Interface in its original catalog position', () => {
expect(getMobilePaneSearchEntries().map((entry) => entry.title)).toEqual([
'Mobile Pairing',
'Connected Devices',
'Network Interface',
'When you leave the mobile app',
'Machine name'
])
})
it('keeps the shared machine-name entry searchable from the Mobile pane', () => {
const entries = getMobilePaneSearchEntries()
@@ -1,6 +1,5 @@
import { describe, expect, it } from 'vitest'
import {
MAX_MOBILE_PAIRING_CUSTOM_ADDRESSES,
addMobilePairingCustomAddress,
normalizeMobilePairingCustomAddress,
normalizeMobilePairingCustomAddresses,
@@ -32,13 +31,10 @@ describe('mobile pairing custom address collection', () => {
).toEqual(['first.example:6768', 'second.example:6768'])
})
it('bounds persisted entries', () => {
const addresses = Array.from(
{ length: MAX_MOBILE_PAIRING_CUSTOM_ADDRESSES + 5 },
(_, index) => `host-${index}.example:6768`
)
expect(normalizeMobilePairingCustomAddresses(addresses)).toHaveLength(
MAX_MOBILE_PAIRING_CUSTOM_ADDRESSES
it('keeps the first 50 valid persisted addresses', () => {
const addresses = Array.from({ length: 55 }, (_, index) => `host-${index}.example:6768`)
expect(normalizeMobilePairingCustomAddresses(addresses)).toEqual(
Array.from({ length: 50 }, (_, index) => `host-${index}.example:6768`)
)
})