Files
simstudioai--sim/scripts/setup/capability-status.test.ts
WeHub Mirror 6bf8bebf51
CI / Test and Build (push) Failing after 1s
CI / Migrate Dev DB (push) Has been skipped
CI / Migrate DB (push) Has been skipped
CodeQL / Analyze actions (push) Has been cancelled
CodeQL / Analyze javascript-typescript (push) Has been cancelled
CI / Detect Version (push) Has been cancelled
CI / Detect Desktop Changes (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/cron.Dockerfile, ubuntu-latest, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build AMD64 (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/cron.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/db.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/pii.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/realtime.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-8vcpu-ubuntu-2404-arm, ./docker/app.Dockerfile, linux-arm64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
Helm Chart / Lint, test, and validate chart (push) Has been cancelled
Helm Chart / Chart version bumped (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Build Dev ECR (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core) (push) Has been cancelled
CI / Promote Images (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Check Desktop Signing Secrets (push) Has been cancelled
CI / Desktop Release (push) Has been cancelled
CI / Create Desktop Prerelease (push) Has been cancelled
CI / Desktop Prerelease Build (push) Has been cancelled
CI / Publish Desktop Prerelease (push) Has been cancelled
CI / Prune Desktop Prereleases (push) Has been cancelled
Helm Chart / Install on kind and run helm test (push) Has been cancelled
WeHub snapshot of cb28d14c6f2c081de7a0d8729a8c816c9adef67a
2026-08-10 11:17:50 +08:00

274 lines
9.6 KiB
TypeScript

import { describe, expect, it } from 'bun:test'
import { OAUTH_CLIENT_CAPABILITIES } from '../../apps/sim/lib/core/config/env-capabilities.ts'
import { buildEnvCapabilityStatus } from './capability-status.ts'
const DAYTONA_FUNCTION_SNAPSHOT_ID = '00000000-0000-4000-8000-000000000002'
describe('env capability status', () => {
it('reports built-in defaults without treating them as configured services', () => {
const status = buildEnvCapabilityStatus({})
expect(status.features.email).toMatchObject({ state: 'missing', providerIds: [] })
expect(status.features.storage).toMatchObject({ state: 'default', providerId: 'local' })
expect(status.features.sandbox).toMatchObject({ state: 'default', providerId: 'disabled' })
expect(status.features.jobs).toMatchObject({ state: 'default', providerId: 'database' })
expect(status.features.cache).toMatchObject({ state: 'default', providerId: 'database' })
expect(status.features.knowledge).toMatchObject({ state: 'default', providerId: 'local' })
expect(status.features.llm).toMatchObject({
state: 'missing',
configuredPoolCount: 0,
configuredKeyCount: 0,
effectiveKeyCount: 0,
})
expect(status.oauthClients.absentCount).toBe(Object.keys(OAUTH_CLIENT_CAPABILITIES).length)
})
it('reports an explicitly selected but disabled E2B provider as the default', () => {
const status = buildEnvCapabilityStatus({
SANDBOX_PROVIDER: 'e2b',
E2B_ENABLED: 'false',
NEXT_PUBLIC_E2B_ENABLED: 'false',
NEXT_PUBLIC_SANDBOXES_ENABLED: 'false',
})
expect(status.features.sandbox).toEqual({
id: 'sandbox',
label: 'Function sandboxes',
setupCommand: 'bun run setup sandbox',
state: 'default',
providerId: 'disabled',
})
})
it('preserves declared email fallback order', () => {
const status = buildEnvCapabilityStatus({
SMTP_HOST: 'localhost',
SMTP_PORT: '1025',
RESEND_API_KEY: 'secret-resend-key',
GMAIL_CREDENTIALS_JSON: JSON.stringify({
client_email: 'mailer@example.com',
private_key: 'secret-private-key',
}),
GMAIL_SENDER: 'mailer@example.com',
})
expect(status.features.email).toMatchObject({
state: 'configured',
providerIds: ['resend', 'smtp', 'gmail'],
})
expect(JSON.stringify(status)).not.toContain('secret-resend-key')
expect(JSON.stringify(status)).not.toContain('secret-private-key')
})
it('reports selected Daytona and cloud storage providers', () => {
const status = buildEnvCapabilityStatus({
SANDBOX_PROVIDER: 'daytona',
DAYTONA_API_KEY: 'daytona-secret',
DAYTONA_FUNCTION_SNAPSHOT_ID,
NEXT_PUBLIC_SANDBOXES_ENABLED: 'true',
STORAGE_PROVIDER: 's3',
AWS_REGION: 'us-east-1',
S3_BUCKET_NAME: 'files',
})
expect(status.features.sandbox).toMatchObject({
state: 'configured',
providerId: 'daytona',
})
expect(status.features.storage).toMatchObject({
state: 'configured',
providerId: 's3',
})
})
it('reports Daytona as missing when its Function snapshot is absent', () => {
const status = buildEnvCapabilityStatus({
SANDBOX_PROVIDER: 'daytona',
DAYTONA_API_KEY: 'daytona-secret',
NEXT_PUBLIC_SANDBOXES_ENABLED: 'true',
})
expect(status.features.sandbox).toMatchObject({
state: 'missing',
providerId: 'daytona',
issue: { state: 'missing' },
})
expect(status.features.sandbox.issue?.message).toContain('DAYTONA_FUNCTION_SNAPSHOT_ID')
})
it('reports a mutable Daytona Function snapshot name as invalid', () => {
const status = buildEnvCapabilityStatus({
SANDBOX_PROVIDER: 'daytona',
DAYTONA_API_KEY: 'daytona-secret',
DAYTONA_FUNCTION_SNAPSHOT_ID: 'mothership-shell:latest',
NEXT_PUBLIC_SANDBOXES_ENABLED: 'true',
})
expect(status.features.sandbox).toMatchObject({
state: 'invalid',
providerId: 'daytona',
issue: { state: 'invalid' },
})
expect(status.features.sandbox.issue?.message).toContain('immutable Daytona snapshot ID')
})
it('reports remote sandbox server/browser drift as partial', () => {
const status = buildEnvCapabilityStatus({
SANDBOX_PROVIDER: 'daytona',
DAYTONA_API_KEY: 'daytona-secret',
DAYTONA_FUNCTION_SNAPSHOT_ID,
})
expect(status.features.sandbox).toMatchObject({
state: 'partial',
providerId: 'daytona',
issue: { state: 'partial' },
})
expect(status.features.sandbox.issue?.message).toContain('NEXT_PUBLIC_SANDBOXES_ENABLED')
})
it('captures partial and invalid entries without aborting the snapshot', () => {
const status = buildEnvCapabilityStatus({
SMTP_HOST: 'localhost',
TRIGGER_DEV_ENABLED: 'true',
TRIGGER_PROJECT_ID: 'project',
REDIS_URL: 'not-a-url',
OCR_AZURE_ENDPOINT: 'https://ocr.example.com',
})
expect(status.features.email).toMatchObject({ state: 'partial' })
expect(
status.features.email.providers.find((provider) => provider.id === 'smtp')
).toMatchObject({
state: 'partial',
missingFields: ['SMTP_PORT'],
})
expect(status.features.jobs).toMatchObject({ state: 'partial', providerId: 'trigger-dev' })
expect(status.features.cache).toMatchObject({ state: 'invalid', providerId: 'redis' })
expect(status.features.knowledge).toMatchObject({
state: 'partial',
providerId: 'azure-mistral',
})
expect(status.features.storage).toMatchObject({ state: 'default', providerId: 'local' })
})
it('does not expose invalid selector values', () => {
const sensitiveValue = 'sensitive-selector-value'
const snapshots = [
buildEnvCapabilityStatus({ STORAGE_PROVIDER: sensitiveValue }),
buildEnvCapabilityStatus({ SANDBOX_PROVIDER: sensitiveValue }),
buildEnvCapabilityStatus({ OCR_PROVIDER: sensitiveValue }),
]
for (const snapshot of snapshots) {
expect(JSON.stringify(snapshot)).not.toContain(sensitiveValue)
}
})
it('reports every OAuth client group as ready, partial, or absent', () => {
const status = buildEnvCapabilityStatus({
GOOGLE_CLIENT_ID: 'google-id',
GOOGLE_CLIENT_SECRET: 'google-secret',
MICROSOFT_CLIENT_ID: 'microsoft-id',
})
expect(status.oauthClients.clients.google).toMatchObject({
state: 'ready',
configuredFieldCount: 2,
requiredFieldCount: 2,
})
expect(status.oauthClients.clients.microsoft).toMatchObject({
state: 'partial',
configuredFieldCount: 1,
missingFields: ['MICROSOFT_CLIENT_SECRET'],
})
expect(status.oauthClients.clients.slack).toMatchObject({
state: 'absent',
configuredFieldCount: 0,
})
expect(status.oauthClients.readyCount).toBe(1)
expect(status.oauthClients.partialCount).toBe(1)
expect(status.oauthClients.absentCount).toBe(Object.keys(OAUTH_CLIENT_CAPABILITIES).length - 2)
})
it('counts configured and effective LLM pool keys without exposing them', () => {
const status = buildEnvCapabilityStatus({
OPENAI_API_KEY_1: 'openai-one',
OPENAI_API_KEY_3: 'openai-three',
FIREWORKS_API_KEY: 'fireworks-fallback',
FIREWORKS_API_KEY_1: 'fireworks-one',
})
expect(status.features.llm).toMatchObject({
state: 'configured',
configuredPoolCount: 2,
configuredKeyCount: 4,
effectiveKeyCount: 3,
})
expect(status.features.llm.pools.openai).toMatchObject({
state: 'configured',
configuredKeyCount: 2,
effectiveKeyCount: 2,
fallbackKeyConfigured: false,
})
expect(status.features.llm.pools.fireworks).toMatchObject({
state: 'configured',
configuredKeyCount: 2,
effectiveKeyCount: 1,
fallbackKeyConfigured: true,
})
expect(JSON.stringify(status)).not.toContain('openai-one')
expect(JSON.stringify(status)).not.toContain('fireworks-fallback')
})
it('counts singular runtime LLM keys as effective pool fallbacks', () => {
const status = buildEnvCapabilityStatus({
OPENAI_API_KEY: 'openai-fallback',
GEMINI_API_KEY: 'gemini-fallback',
COHERE_API_KEY: 'cohere-fallback',
})
expect(status.features.llm).toMatchObject({
state: 'configured',
configuredPoolCount: 3,
configuredKeyCount: 3,
effectiveKeyCount: 3,
})
expect(status.features.llm.pools.openai).toMatchObject({
state: 'configured',
fallbackKeyConfigured: true,
})
expect(status.features.llm.pools.gemini).toMatchObject({
state: 'configured',
fallbackKeyConfigured: true,
})
expect(status.features.llm.pools.cohere).toMatchObject({
state: 'configured',
fallbackKeyConfigured: true,
})
expect(JSON.stringify(status)).not.toContain('openai-fallback')
expect(JSON.stringify(status)).not.toContain('gemini-fallback')
expect(JSON.stringify(status)).not.toContain('cohere-fallback')
})
it('reports non-Redis cache URLs and non-HTTP Azure OCR endpoints as invalid', () => {
const status = buildEnvCapabilityStatus({
REDIS_URL: 'https://cache.example.com',
OCR_PROVIDER: 'azure-mistral',
OCR_AZURE_API_KEY: 'azure-key',
OCR_AZURE_ENDPOINT: 'ftp://ocr.example.com',
OCR_AZURE_MODEL_NAME: 'mistral-ocr',
})
expect(status.features.cache).toMatchObject({ state: 'invalid', providerId: 'redis' })
expect(status.features.cache.issue?.message).toContain('redis:// or rediss://')
expect(status.features.knowledge).toMatchObject({
state: 'invalid',
providerId: 'azure-mistral',
})
expect(status.features.knowledge.issue?.message).toContain(
'OCR_AZURE_ENDPOINT must be a valid HTTP(S) URL'
)
})
})