Files
openchamber/packages/ui/src/components/onboarding/desktopRecoveryConfig.test.ts
T
Bohdan Triapitsyn 2031e3b4a8 Decouple bundled UI from runtime API and add remote instance tooling (#1228)
Add a packaged-client runtime boundary so the shared UI can talk to local,
desktop, remote, and VS Code runtimes through the right transport instead of
assuming one same-origin web server.

Centralize OpenChamber-owned API access behind RuntimeAPIs, runtimeFetch, and
runtime URL helpers, while keeping official OpenCode traffic on the SDK path.
Support runtime switching, remote host selection, desktop client credentials,
and headless connection links for pairing packaged clients with remote
OpenChamber servers.

Harden the new auth model by moving long-lived client tokens out of browser
URLs, introducing short-lived scoped URL tokens for browser-owned transports,
restricting URL-token access to explicit readable/realtime routes, and making
client-token management session-scoped or self-scoped as appropriate.

Update browser-owned assets and preview proxy flows to work with the split
runtime model, including authenticated project icons, preview token propagation,
CSP-safe preview bridge injection, and preview proxy auth that survives
short-lived URL-token expiry.

Tighten Electron security boundaries for packaged clients by gating privileged
preload state to trusted origins and requiring explicit confirmation before
connect deep-links import or switch remote runtimes.

Also refresh agent guidance and project skills so future runtime/API, auth,
preview, UI, CLI, settings, locale, and drag-to-reorder work follows the new
architecture.
2026-06-02 00:43:05 +03:00

258 lines
10 KiB
TypeScript

import { describe, expect, test } from 'bun:test';
import { getDesktopRecoveryConfig } from './desktopRecoveryConfig';
describe('getDesktopRecoveryConfig', () => {
// ---------------------------------------------------------------------------
// 1. local-unavailable: both actions visible + retry labeled "Retry Local"
// ---------------------------------------------------------------------------
test('local-unavailable exposes both actions and Retry Local', () => {
const config = getDesktopRecoveryConfig('local-unavailable');
expect(config.title).toBe('Local OpenCode Unavailable');
expect(config.iconKey).toBe('local');
expect(config.showRetry).toBe(true);
expect(config.retryLabel).toBe('Retry Local');
expect(config.showUseLocal).toBe(true);
expect(config.showUseRemote).toBe(true);
// local-unavailable uses setup-oriented label since local needs installing
expect(config.useLocalLabel).toBe('Set Up Local');
expect(config.useRemoteLabel).toBe('Use Remote');
});
// ---------------------------------------------------------------------------
// 2. remote-unreachable: both actions + retry
// ---------------------------------------------------------------------------
test('remote-unreachable exposes both actions + retry', () => {
const config = getDesktopRecoveryConfig(
'remote-unreachable',
'My Server',
'https://example.com:4096',
);
expect(config.title).toBe('Remote Server Unreachable');
expect(config.iconKey).toBe('remote');
expect(config.showRetry).toBe(true);
expect(config.retryLabel).toBe('Retry Connection');
expect(config.showUseLocal).toBe(true);
expect(config.showUseRemote).toBe(true);
// remote variants keep standard "Use Local"
expect(config.useLocalLabel).toBe('Use Local');
expect(config.useRemoteLabel).toBe('Use Remote');
});
// ---------------------------------------------------------------------------
// 3. remote-wrong-service: both actions, NO retry
// ---------------------------------------------------------------------------
test('remote-wrong-service exposes both actions and no retry', () => {
const config = getDesktopRecoveryConfig(
'remote-wrong-service',
'Bad Host',
'https://wrong.example.com',
);
expect(config.title).toBe('Incompatible Server');
expect(config.iconKey).toBe('remote');
expect(config.showRetry).toBe(false);
expect(config.retryLabel).toBe(undefined);
expect(config.showUseLocal).toBe(true);
expect(config.showUseRemote).toBe(true);
expect(config.useLocalLabel).toBe('Use Local');
expect(config.useRemoteLabel).toBe('Use Remote');
});
test('remote-incompatible exposes retry and both actions', () => {
const config = getDesktopRecoveryConfig('remote-incompatible', 'Old Server', 'https://old.example');
expect(config.showRetry).toBe(true);
expect(config.showUseLocal).toBe(true);
expect(config.showUseRemote).toBe(true);
expect(config.titleKey).toBe('onboarding.desktopRecovery.remoteIncompatible.title');
});
// ---------------------------------------------------------------------------
// 4. missing-default-host: chooser-with-context (both actions, no retry)
// ---------------------------------------------------------------------------
test('missing-default-host behaves like chooser-with-context', () => {
const config = getDesktopRecoveryConfig('missing-default-host');
expect(config.title).toBe('No Default Connection');
expect(config.iconKey).toBe('local');
expect(config.showRetry).toBe(false);
expect(config.retryLabel).toBe(undefined);
expect(config.showUseLocal).toBe(true);
expect(config.showUseRemote).toBe(true);
expect(config.useLocalLabel).toBe('Use Local');
expect(config.useRemoteLabel).toBe('Use Remote');
});
// ---------------------------------------------------------------------------
// 5. descriptions redact sensitive query params for remote variants
// ---------------------------------------------------------------------------
test('remote-unreachable description redacts sensitive query params in URL', () => {
const sensitiveUrl =
'https://example.com:4096?token=super-secret&auth=abc123';
const config = getDesktopRecoveryConfig(
'remote-unreachable',
undefined,
sensitiveUrl,
);
// Secrets must never appear in the description
expect(config.description).not.toContain('super-secret');
expect(config.description).not.toContain('abc123');
// Redaction marker is present
expect(config.description).toContain('REDACTED');
expect(config.description).toContain('example.com');
});
test('remote-wrong-service description redacts sensitive query params in URL', () => {
const sensitiveUrl =
'https://wrong.example.com?api_key=sk-12345&secret=mysecret';
const config = getDesktopRecoveryConfig(
'remote-wrong-service',
undefined,
sensitiveUrl,
);
// Secrets must never appear in the description
expect(config.description).not.toContain('sk-12345');
expect(config.description).not.toContain('mysecret');
// Redaction marker is present
expect(config.description).toContain('REDACTED');
expect(config.description).toContain('wrong.example.com');
});
test('local-unavailable description does not reference host URL', () => {
const config = getDesktopRecoveryConfig(
'local-unavailable',
'Some Host',
'https://example.com?token=secret',
);
// local-unavailable ignores hostUrl in its description
expect(config.description).not.toContain('example.com');
expect(config.description).not.toContain('secret');
});
test('missing-default-host description does not reference host URL', () => {
const config = getDesktopRecoveryConfig(
'missing-default-host',
'Some Host',
'https://example.com?token=secret',
);
expect(config.description).not.toContain('example.com');
expect(config.description).not.toContain('secret');
});
// ---------------------------------------------------------------------------
// 6. URL-like hostLabel is also redacted (sensitive data leak prevention)
// ---------------------------------------------------------------------------
test('remote-unreachable redacts URL-like hostLabel containing sensitive query params', () => {
const urlAsLabel =
'https://example.com:4096?token=super-secret&auth=abc123';
const config = getDesktopRecoveryConfig(
'remote-unreachable',
urlAsLabel,
'https://fallback.example.com',
);
// Secrets in hostLabel must never appear in the description
expect(config.description).not.toContain('super-secret');
expect(config.description).not.toContain('abc123');
// Redaction marker is present
expect(config.description).toContain('REDACTED');
expect(config.description).toContain('example.com');
});
test('remote-wrong-service redacts URL-like hostLabel containing sensitive query params', () => {
const urlAsLabel =
'https://wrong.example.com?api_key=sk-12345&secret=mysecret';
const config = getDesktopRecoveryConfig(
'remote-wrong-service',
urlAsLabel,
'https://fallback.example.com',
);
// Secrets in hostLabel must never appear in the description
expect(config.description).not.toContain('sk-12345');
expect(config.description).not.toContain('mysecret');
// Redaction marker is present
expect(config.description).toContain('REDACTED');
expect(config.description).toContain('wrong.example.com');
});
test('remote-unreachable redacts embedded credentials in URL-like hostLabel', () => {
const urlWithCreds = 'https://admin:s3cret@example.com:4096';
const config = getDesktopRecoveryConfig(
'remote-unreachable',
urlWithCreds,
'https://fallback.example.com',
);
// Username/password must never appear in the description
expect(config.description).not.toContain('admin');
expect(config.description).not.toContain('s3cret');
// Hostname should still be visible
expect(config.description).toContain('example.com');
});
test('remote-wrong-service redacts embedded credentials in URL-like hostLabel', () => {
const urlWithCreds = 'https://user:pass123@wrong.example.com';
const config = getDesktopRecoveryConfig(
'remote-wrong-service',
urlWithCreds,
'https://fallback.example.com',
);
expect(config.description).not.toContain('user');
expect(config.description).not.toContain('pass123');
expect(config.description).toContain('wrong.example.com');
});
test('non-URL hostLabel is used as-is without redaction', () => {
const config = getDesktopRecoveryConfig(
'remote-unreachable',
'My Server',
'https://example.com?token=secret',
);
// Plain label should appear verbatim
expect(config.description).toContain('My Server');
// hostUrl secrets should not leak (already tested above, but sanity check)
expect(config.description).not.toContain('secret');
});
// ---------------------------------------------------------------------------
// Fallback descriptions when no host info is provided
// ---------------------------------------------------------------------------
test('remote-unreachable falls back to generic text when no host info', () => {
const config = getDesktopRecoveryConfig('remote-unreachable');
expect(config.description).toContain('the remote server');
expect(config.description).not.toContain('undefined');
});
test('remote-wrong-service falls back to generic text when no host info', () => {
const config = getDesktopRecoveryConfig('remote-wrong-service');
expect(config.description).toContain('unknown');
});
// ---------------------------------------------------------------------------
// Whitespace-only hostLabel is treated as absent
// ---------------------------------------------------------------------------
test('whitespace-only hostLabel falls back to hostUrl', () => {
const config = getDesktopRecoveryConfig(
'remote-unreachable',
' ',
'https://fallback.example.com?token=secret',
);
// hostLabel is whitespace-only → should use redacted hostUrl instead
expect(config.description).not.toContain('secret');
expect(config.description).toContain('fallback.example.com');
expect(config.description).toContain('REDACTED');
});
});