Files
openchamber/packages/web/server/lib/opencode/settings-runtime.test.js
T
Bohdan Triapitsyn 85400459e9 perf: overhaul session loading, caching, and runtime isolation (#2360)
Improve OpenChamber responsiveness under large session workloads while fixing
cache, synchronization, and persistence correctness across runtimes, projects,
directories, and worktrees.

- prioritize selected and visible sessions during bootstrap and defer
  non-critical enrichment work
- reduce redundant message loading, event processing, store publication, and
  hidden sidebar work
- prevent stale session and message requests from overwriting newer
  authoritative state
- preserve existing data when authoritative fetches fail instead of treating
  failures as successful empty responses
- scope session materialization, messages, drafts, queues, todos, pins,
  permissions, folders, tabs, Git state, and pull request data by runtime and
  directory identity
- harden runtime switching, reconnect, cleanup, mutation reconciliation, and
  persisted-state ordering
- preserve live subagent Task linkage when metadata arrives after an older
  message request or while streaming parts are suspended
- coalesce overlapping tail refreshes without losing newer refresh demand
- improve cold-session loading by moving deferrable work out of the critical
  bootstrap path
- isolate URL authentication, mobile credentials, native secrets, and other
  runtime-owned state across endpoint changes
- bound long-lived caches and remove avoidable allocations from event and
  rendering hot paths
- limit virtualization to archive collections where it improves rendering
  without disrupting active sidebar layout
- stabilize session folders, pin ordering, expanded state, and persisted
  sidebar behavior
- open skill files through the same secure editor and outside-workspace grant
  flow used by file navigation, including worktree sessions
- expand regression coverage for stale completions, runtime collisions,
  reconnect behavior, persistence races, authoritative empty results, and
  subagent refresh ordering
- document the updated synchronization, cache ownership, performance, and
  runtime-isolation invariants
2026-07-21 20:52:20 +03:00

137 lines
5.6 KiB
JavaScript

import { describe, expect, it } from 'vitest';
import crypto from 'crypto';
import fsPromises from 'fs/promises';
import os from 'os';
import path from 'path';
import { createProjectIdFromPath } from '../projects/project-id.js';
import { createSettingsRuntime } from './settings-runtime.js';
const createRuntime = async () => {
const tempRoot = await fsPromises.mkdtemp(path.join(os.tmpdir(), 'oc-settings-runtime-'));
const settingsFilePath = path.join(tempRoot, 'settings.json');
const runtime = createSettingsRuntime({
fsPromises,
path,
crypto,
SETTINGS_FILE_PATH: settingsFilePath,
sanitizeProjects: (projects) => Array.isArray(projects) ? projects : [],
sanitizeSettingsUpdate: (settings) => settings,
mergePersistedSettings: (_current, changes) => changes,
normalizeSettingsPaths: (settings) => ({ settings, changed: false }),
normalizeStringArray: (values) => Array.isArray(values) ? values.filter((value) => typeof value === 'string') : [],
formatSettingsResponse: (settings) => settings,
resolveDirectoryCandidate: (value) => value,
normalizeManagedRemoteTunnelHostname: (value) => value,
normalizeManagedRemoteTunnelPresets: (value) => value,
normalizeManagedRemoteTunnelPresetTokens: (value) => value,
syncManagedRemoteTunnelConfigWithPresets: async () => {},
upsertManagedRemoteTunnelToken: async () => {},
});
return {
runtime,
settingsFilePath,
tempRoot,
cleanup: async () => {
await fsPromises.rm(tempRoot, { recursive: true, force: true });
},
};
};
describe('settings runtime', () => {
it.skipIf(process.platform === 'win32')('writes settings with restrictive directory and file permissions', async () => {
const { runtime, settingsFilePath, tempRoot, cleanup } = await createRuntime();
try {
await runtime.writeSettingsToDisk({ desktopUiPassword: 'secret' });
expect((await fsPromises.stat(tempRoot)).mode & 0o777).toBe(0o700);
expect((await fsPromises.stat(settingsFilePath)).mode & 0o777).toBe(0o600);
} finally {
await cleanup();
}
});
it('only remaps project plan paths within the migrated storage directory', async () => {
const { runtime, settingsFilePath, tempRoot, cleanup } = await createRuntime();
try {
const projectPath = path.join(tempRoot, 'project');
const oldProjectId = 'legacy-project-id';
const newProjectId = createProjectIdFromPath(projectPath);
const projectsRoot = path.join(path.dirname(settingsFilePath), 'projects');
const oldStorageDir = path.join(projectsRoot, oldProjectId);
const newStorageDir = path.join(projectsRoot, newProjectId);
const siblingStorageDir = `${oldStorageDir}-sibling`;
await fsPromises.mkdir(projectPath, { recursive: true });
await fsPromises.mkdir(projectsRoot, { recursive: true });
await fsPromises.writeFile(
settingsFilePath,
JSON.stringify({
projects: [{ id: oldProjectId, path: projectPath, addedAt: 1, lastOpenedAt: 1 }],
activeProjectId: oldProjectId,
}, null, 2),
'utf8',
);
await fsPromises.writeFile(
path.join(projectsRoot, `${oldProjectId}.json`),
JSON.stringify({
projectPlanFiles: [
{ id: 'inside', path: path.join(oldStorageDir, 'plans', 'inside.md') },
{ id: 'sibling', path: path.join(siblingStorageDir, 'plans', 'outside.md') },
],
}, null, 2),
'utf8',
);
await runtime.readSettingsFromDiskMigrated();
const migratedConfig = JSON.parse(await fsPromises.readFile(path.join(projectsRoot, `${newProjectId}.json`), 'utf8'));
expect(migratedConfig.projectPlanFiles).toEqual([
{ id: 'inside', path: path.join(newStorageDir, 'plans', 'inside.md') },
{ id: 'sibling', path: path.join(siblingStorageDir, 'plans', 'outside.md') },
]);
} finally {
await cleanup();
}
});
it.skipIf(process.platform !== 'win32')('falls back when Windows blocks atomic settings replacement', async () => {
const tempRoot = await fsPromises.mkdtemp(path.join(os.tmpdir(), 'oc-settings-runtime-'));
const settingsFilePath = path.join(tempRoot, 'settings.json');
const wrappedFs = {
...fsPromises,
rename: async () => {
const error = new Error('operation not permitted');
error.code = 'EPERM';
throw error;
},
};
const runtime = createSettingsRuntime({
fsPromises: wrappedFs,
path,
crypto,
SETTINGS_FILE_PATH: settingsFilePath,
sanitizeProjects: (projects) => Array.isArray(projects) ? projects : [],
sanitizeSettingsUpdate: (settings) => settings,
mergePersistedSettings: (_current, changes) => changes,
normalizeSettingsPaths: (settings) => ({ settings, changed: false }),
normalizeStringArray: (values) => Array.isArray(values) ? values.filter((value) => typeof value === 'string') : [],
formatSettingsResponse: (settings) => settings,
resolveDirectoryCandidate: (value) => value,
normalizeManagedRemoteTunnelHostname: (value) => value,
normalizeManagedRemoteTunnelPresets: (value) => value,
normalizeManagedRemoteTunnelPresetTokens: (value) => value,
syncManagedRemoteTunnelConfigWithPresets: async () => {},
upsertManagedRemoteTunnelToken: async () => {},
});
try {
await runtime.writeSettingsToDisk({ theme: 'dark' });
await expect(fsPromises.readFile(settingsFilePath, 'utf8')).resolves.toBe(JSON.stringify({ theme: 'dark' }, null, 2));
} finally {
await fsPromises.rm(tempRoot, { recursive: true, force: true });
}
});
});