import { createHash } from 'node:crypto' import { mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs' import { DatabaseSync } from 'node:sqlite' import { tmpdir } from 'node:os' import path from 'node:path' import { afterEach, describe, expect, it } from 'vitest' import { createDefaultLocalOrcaProfile } from '../../src/shared/orca-profiles' import { seedDevProfile } from './dev-profile-seed.mjs' const roots = [] afterEach(() => { for (const root of roots.splice(0)) { rmSync(root, { recursive: true, force: true }) } }) function hashTree(dir) { const hash = createHash('sha256') for (const entry of readdirSync(dir, { recursive: true, withFileTypes: true })) { if (entry.isFile()) { const file = path.join(entry.parentPath, entry.name) hash.update(path.relative(dir, file)).update(readFileSync(file)) } } return hash.digest('hex') } function createSourceProfile() { const root = mkdtempSync(path.join(tmpdir(), 'orca-seed-source-')) roots.push(root) const profile = createDefaultLocalOrcaProfile(0) const profileDir = path.join(root, 'profiles', profile.id) mkdirSync(profileDir, { recursive: true }) writeFileSync( path.join(root, 'orca-profile-index.json'), JSON.stringify({ schemaVersion: 1, activeProfileId: profile.id, profiles: [profile] }) ) writeFileSync(path.join(profileDir, 'account-session.json.enc'), '{"ciphertext":"x"}') writeFileSync(path.join(root, 'SingletonLock'), 'locked') mkdirSync(path.join(root, 'Code Cache')) writeFileSync(path.join(root, 'Code Cache', 'blob'), 'cache') const db = new DatabaseSync(path.join(profileDir, 'profile-state.db')) db.exec( 'CREATE TABLE profile_state_documents (domain TEXT PRIMARY KEY, payload TEXT NOT NULL, domain_version INTEGER, revision INTEGER, updated_at INTEGER, content_hash TEXT)' ) const insert = db.prepare("INSERT INTO profile_state_documents VALUES (?, ?, 1, 1, 0, 'hash')") insert.run('repos', JSON.stringify([{ id: 'repo-1', path: '/src/app' }])) insert.run('settings', JSON.stringify({ theme: 'dark' })) insert.run('sshTargets', JSON.stringify([{ id: 'ssh-1' }])) insert.run('workspaceSession', JSON.stringify({ tabsByWorktree: { a: [] } })) insert.run('sshRemotePtyLeases', JSON.stringify([{ ptyId: 'p' }])) insert.run('automations', JSON.stringify([{ id: 'nightly' }])) db.close() return root } describe('seedDevProfile', () => { it('copies durable profile domains and the account file without changing the source', async () => { const source = createSourceProfile() const before = hashTree(source) const target = path.join(mkdtempSync(path.join(tmpdir(), 'orca-seed-target-')), 'profile') roots.push(path.dirname(target)) await seedDevProfile({ sourceDir: source, targetDir: target, report: () => {} }) expect(hashTree(source)).toBe(before) const seeded = JSON.parse( readFileSync(path.join(target, 'profiles', 'local-default', 'orca-data.json'), 'utf8') ) expect(seeded).toEqual({ repos: [{ id: 'repo-1', path: '/src/app' }], settings: { theme: 'dark' }, sshTargets: [{ id: 'ssh-1' }] }) expect( readFileSync( path.join(target, 'profiles', 'local-default', 'account-session.json.enc'), 'utf8' ) ).toBe('{"ciphertext":"x"}') expect(readdirSync(target).sort()).toEqual([ 'orca-dev-seed.json', 'orca-profile-index.json', 'profiles' ]) }) it('refuses to overwrite a profile that already has state', async () => { const source = createSourceProfile() const target = mkdtempSync(path.join(tmpdir(), 'orca-seed-target-')) roots.push(target) writeFileSync(path.join(target, 'orca-profile-index.json'), '{"existing":true}') await expect( seedDevProfile({ sourceDir: source, targetDir: target, report: () => {} }) ).rejects.toThrow('already has state') expect(readFileSync(path.join(target, 'orca-profile-index.json'), 'utf8')).toBe( '{"existing":true}' ) }) })