Files
sim/scripts/setup/feature-setup.test.ts
T
Theodore Li 35fd4ef42f improvement(self-host): simplify capability setup configuration (#6230)
* feat(self-host): add capability-aware setup

* fix(self-host): preserve capability compatibility

* fix(copilot): honor preview availability server-side

* improvement(self-host): centralize capability resolution

* fix(self-host): preserve integration availability paths

* fix(testing): align capability-aware config mocks

* improvement(self-host): simplify capability setup configuration

* fix(setup): preserve unowned storage overrides

* fix(self-host): reconcile storage and allowlists

* fix(integrations): preserve connect deep links
2026-08-04 15:47:36 -04:00

116 lines
4.2 KiB
TypeScript

import { describe, expect, it } from 'bun:test'
import {
SANDBOX_CAPABILITY,
validateCapabilityFieldInput,
} from '../../apps/sim/lib/core/config/env-capabilities.ts'
import { SANDBOX_SETUP } from './capability-config.ts'
import { buildCapabilitySetupTransition } from './capability-setup.ts'
import type { ConfigurationSource } from './configuration-sources.ts'
import { reconcileLlmSetup, resolveFeatureSetupDestination } from './feature-setup.ts'
function source(
kind: ConfigurationSource['kind'],
managedByCurrentCheckout: boolean,
values: Map<string, string> | null = new Map()
): ConfigurationSource {
return {
kind,
label: `${kind} source`,
location: `${kind} location`,
values,
managedByCurrentCheckout,
}
}
describe('resolveFeatureSetupDestination', () => {
it('uses the effective values of the one setup-managed source', () => {
const effective = new Map([['RESEND_API_KEY', 'effective-key']])
const destination = resolveFeatureSetupDestination([
source('compose', false),
source('dev', true, effective),
])
expect(destination.target).toBe('sim')
expect(destination.containerized).toBe(false)
expect(destination.vars).toBe(effective)
})
it('maps a managed Compose source to the root env file', () => {
const destination = resolveFeatureSetupDestination([source('compose', true)])
expect(destination.target).toBe('root')
expect(destination.containerized).toBe(true)
})
it('refuses effective sources this checkout cannot safely update', () => {
expect(() => resolveFeatureSetupDestination([source('dev', false)])).toThrow(
/No effective configuration is safely writable/
)
expect(() => resolveFeatureSetupDestination([source('helm', false)])).toThrow(
/No effective configuration is safely writable/
)
})
it('refuses an unreadable managed source instead of claiming success', () => {
expect(() => resolveFeatureSetupDestination([source('compose', true, null)])).toThrow(
/effective environment could not be resolved/
)
})
})
describe('sandbox capability setup', () => {
it('requires an explicit non-floating snapshot tag', () => {
const validate = (value: string) =>
validateCapabilityFieldInput(SANDBOX_CAPABILITY, 'DAYTONA_SHELL_SNAPSHOT_ID', value)
expect(validate('mothership-shell:v1')).toBeUndefined()
expect(validate('mothership-shell')).toContain('name:tag')
expect(validate('mothership-shell:latest')).toContain('name:tag')
})
it('writes Daytona API and shell snapshot configuration and disables E2B', () => {
const result = buildCapabilitySetupTransition(
SANDBOX_SETUP,
'daytona',
{
DAYTONA_API_KEY: 'daytona-key',
DAYTONA_SHELL_SNAPSHOT_ID: 'mothership-shell:v1',
},
{}
)
expect(result.remove).toContain('E2B_API_KEY')
expect(result.values).toMatchObject({
DAYTONA_API_KEY: 'daytona-key',
DAYTONA_SHELL_SNAPSHOT_ID: 'mothership-shell:v1',
E2B_ENABLED: 'false',
NEXT_PUBLIC_E2B_ENABLED: 'false',
NEXT_PUBLIC_SANDBOX_ENABLED: 'true',
})
})
it('removes stale Daytona configuration for E2B and disabled modes', () => {
expect(
buildCapabilitySetupTransition(SANDBOX_SETUP, 'e2b', { E2B_API_KEY: 'e2b-key' }, {}).remove
).toEqual(expect.arrayContaining(['DAYTONA_API_KEY', 'DAYTONA_SHELL_SNAPSHOT_ID']))
expect(buildCapabilitySetupTransition(SANDBOX_SETUP, 'disabled', {}, {}).remove).toEqual(
expect.arrayContaining(['DAYTONA_API_KEY', 'DAYTONA_SHELL_SNAPSHOT_ID'])
)
})
})
describe('reconcileLlmSetup', () => {
it('removes trailing rotation keys omitted after empty-to-finish', () => {
expect(reconcileLlmSetup('openai', { OPENAI_API_KEY_1: 'replacement' })).toEqual({
values: { OPENAI_API_KEY_1: 'replacement' },
remove: ['OPENAI_API_KEY_2', 'OPENAI_API_KEY_3', 'OPENAI_API_KEY'],
})
})
it('removes a legacy fallback when rotation keys replace it', () => {
expect(reconcileLlmSetup('fireworks', { FIREWORKS_API_KEY_1: 'replacement' })).toEqual({
values: { FIREWORKS_API_KEY_1: 'replacement' },
remove: ['FIREWORKS_API_KEY_2', 'FIREWORKS_API_KEY_3', 'FIREWORKS_API_KEY'],
})
})
})