import fs from 'node:fs' import os from 'node:os' import path from 'node:path' import { fileURLToPath } from 'node:url' import { describe, expect, it } from 'vitest' import { buildBundleValidationReport, buildFrontendEnvEntries, buildServerEnvEntries, loadBundleManifest, renderSameOriginBundle, writeSameOriginBundle, } from './render-same-origin-bundle-lib.mjs' const currentFile = fileURLToPath(import.meta.url) const websiteRoot = path.resolve(path.dirname(currentFile), '..') const launchBundleExamplePath = path.join(websiteRoot, 'deploy', 'hypertwist.same-origin.bundle.example.json') const previewBundleExamplePath = path.join(websiteRoot, 'deploy', 'hypertwist.same-origin.preview.bundle.example.json') function createValidManifest() { return { deploymentTier: 'launch', checkoutRoot: '/srv/hypertwist/current', publicOrigin: 'https://hypertwist.app', supportEmail: 'hello@hypertwist.app', publicDocsUrl: 'https://docs.hypertwist.app', releaseNotesUrl: 'https://notes.hypertwist.app', correspondingSourceUrl: 'https://hypertwist.app/open-source/source.zip', openSourceRepoUrl: 'https://git.scriptoriumai.io/scriptoriumadmin/hypertwist', operatorCheckoutUrl: 'https://buy.paddle.com/operator', studioCheckoutUrl: 'https://buy.paddle.com/studio', windowsDownloadUrl: 'https://downloads.hypertwist.app/windows.exe', releaseManifestVersion: '1.0.0', releaseManifestChannel: 'candidate', windowsRelease: { buildId: 'win64-1000', sha256: 'abc123', fileSizeBytes: 123456789, publishedAt: '2026-06-22T00:00:00.000Z', }, server: { paddleWebhookSecret: 'secret', paddleProductPlanMap: { prod_operator: 'operator', }, paddlePricePlanMap: { pri_operator: 'operator', }, }, } } function createPreviewManifest() { return { deploymentTier: 'preview', checkoutRoot: '/srv/hypertwist/current', publicOrigin: 'https://hypertwist.app', supportEmail: 'hello@hypertwist.app', publicDocsUrl: 'https://hypertwist.app/docs', releaseNotesUrl: 'https://hypertwist.app/changelog', correspondingSourceUrl: 'https://git.scriptoriumai.io/scriptoriumadmin/hypertwist', openSourceRepoUrl: 'https://git.scriptoriumai.io/scriptoriumadmin/hypertwist', operatorCheckoutUrl: '', studioCheckoutUrl: '', windowsDownloadUrl: '', releaseManifestVersion: '', releaseManifestChannel: 'preview', windowsRelease: { buildId: '', sha256: '', fileSizeBytes: '', publishedAt: '', }, server: { paddleWebhookSecret: '', paddleProductPlanMap: {}, paddlePricePlanMap: {}, }, } } describe('buildFrontendEnvEntries', () => { it('derives consistent frontend env from the bundle manifest', () => { const env = buildFrontendEnvEntries(createValidManifest()) expect(env.VITE_AUTH_API_BASE_URL).toBe('https://hypertwist.app') expect(env.VITE_WINDOWS_DOWNLOAD_URL).toBe('https://downloads.hypertwist.app/windows.exe') expect(env.VITE_PADDLE_CHECKOUT_URL_OPERATOR).toBe('https://buy.paddle.com/operator') }) it('uses the manifest public origin when deriving auth env', () => { const env = buildFrontendEnvEntries({ ...createValidManifest(), publicOrigin: 'https://preview.hypertwist.app', }) expect(env.VITE_SUPERTOKENS_API_DOMAIN).toBe('https://preview.hypertwist.app') expect(env.VITE_SUPERTOKENS_WEBSITE_DOMAIN).toBe('https://preview.hypertwist.app') expect(env.VITE_AUTH_API_BASE_URL).toBe('https://preview.hypertwist.app') }) it('enables ORCID in frontend env only when the server manifest carries a complete ORCID credential pair', () => { const env = buildFrontendEnvEntries({ ...createValidManifest(), server: { ...createValidManifest().server, orcidClientId: 'orcid-client-id', orcidClientSecret: 'orcid-client-secret', }, }) expect(env.VITE_ORCID_OAUTH_ENABLED).toBe('true') }) }) describe('buildServerEnvEntries', () => { it('derives consistent server env from the bundle manifest', () => { const env = buildServerEnvEntries(createValidManifest()) expect(env.PORT).toBe('3011') expect(env.API_DOMAIN).toBe('https://hypertwist.app') expect(env.WINDOWS_RELEASE_BUILD_ID).toBe('win64-1000') expect(env.PADDLE_PRODUCT_PLAN_MAP).toContain('prod_operator') }) it('derives the public origin from server names when no explicit publicOrigin is set', () => { const env = buildServerEnvEntries({ ...createValidManifest(), publicOrigin: '', serverNames: ['preview.hypertwist.app', 'hypertwist.app'], }) expect(env.API_DOMAIN).toBe('https://preview.hypertwist.app') expect(env.WEBSITE_DOMAIN).toBe('https://preview.hypertwist.app') }) it('carries ORCID server env through when the manifest declares the custom provider credentials', () => { const env = buildServerEnvEntries({ ...createValidManifest(), server: { ...createValidManifest().server, orcidClientId: 'orcid-client-id', orcidClientSecret: 'orcid-client-secret', orcidIsSandbox: true, }, }) expect(env.ORCID_CLIENT_ID).toBe('orcid-client-id') expect(env.ORCID_CLIENT_SECRET).toBe('orcid-client-secret') expect(env.ORCID_IS_SANDBOX).toBe('true') }) }) describe('buildBundleValidationReport', () => { it('fails when placeholder values are still present in the manifest', () => { const report = buildBundleValidationReport({ ...createValidManifest(), operatorCheckoutUrl: 'https://buy.paddle.com/replace-me-operator', }) expect(report.ok).toBe(false) expect(report.failures).toContain('VITE_PADDLE_CHECKOUT_URL_OPERATOR still contains a placeholder value.') }) it('accepts preview-tier bundle manifests with honest missing launch values', () => { const report = buildBundleValidationReport(createPreviewManifest()) expect(report.ok).toBe(true) expect(report.failures).toEqual([]) expect(report.warnings).toContain('VITE_PADDLE_CHECKOUT_URL_OPERATOR is not set; Operator pricing will stay on the support fallback until checkout is configured.') expect(report.warnings).toContain('VITE_WINDOWS_DOWNLOAD_URL or WINDOWS_DOWNLOAD_URL is not set; Windows download will remain in preview posture until the release lane is configured.') }) }) describe('renderSameOriginBundle', () => { it('renders a runtime-ready bundle when required values are present', () => { const rendered = renderSameOriginBundle(createValidManifest()) expect(rendered.validationReport.ok).toBe(true) expect(rendered.frontendEnvContent).toContain('VITE_PUBLIC_DEPLOYMENT_TIER=launch') expect(rendered.serverEnvContent).toContain('PORT=3011') expect(rendered.frontendEnvContent).toContain('VITE_WINDOWS_DOWNLOAD_URL=https://downloads.hypertwist.app/windows.exe') expect(rendered.serverEnvContent).toContain('RELEASE_MANIFEST_VERSION=1.0.0') expect(rendered.systemdContent).toContain('WorkingDirectory=/srv/hypertwist/current/website/server') expect(rendered.nginxContent).toContain('server_name hypertwist.app www.hypertwist.app;') expect(rendered.nginxContent).toContain('server 127.0.0.1:3011;') }) it('renders a preview-tier bundle without checkout, download, or webhook values', () => { const rendered = renderSameOriginBundle(createPreviewManifest()) expect(rendered.validationReport.ok).toBe(true) expect(rendered.frontendEnvContent).toContain('VITE_PUBLIC_DEPLOYMENT_TIER=preview') expect(rendered.frontendEnvContent).toContain('VITE_PADDLE_CHECKOUT_URL_OPERATOR=') expect(rendered.serverEnvContent).toContain('DEPLOYMENT_TIER=preview') expect(rendered.serverEnvContent).toContain('PADDLE_WEBHOOK_SECRET=') }) it('keeps the checked-in launch example bundle intentionally non-ready until placeholders are replaced', () => { const { manifest } = loadBundleManifest(launchBundleExamplePath) expect(() => renderSameOriginBundle(manifest)).toThrow(/not runtime-ready/i) }) it('renders the checked-in preview example bundle as an honest preview-tier deployment', () => { const { manifest } = loadBundleManifest(previewBundleExamplePath) const rendered = renderSameOriginBundle(manifest) expect(rendered.validationReport.ok).toBe(true) expect(rendered.validationReport.failures).toEqual([]) expect(rendered.frontendEnv.VITE_PUBLIC_DEPLOYMENT_TIER).toBe('preview') expect(rendered.serverEnv.DEPLOYMENT_TIER).toBe('preview') expect(rendered.frontendEnv.VITE_MPL_SOURCE_URL).toBe('https://git.scriptoriumai.io/scriptoriumadmin/hypertwist') expect(rendered.serverEnv.PADDLE_WEBHOOK_SECRET).toBe('') }) }) describe('writeSameOriginBundle', () => { it('writes all bundle outputs to the chosen directory', () => { const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'hypertwist-bundle-render-')) try { const written = writeSameOriginBundle({ manifest: createValidManifest(), outDir: tempDir, }) expect(fs.readFileSync(written.frontendEnvPath, 'utf8')).toContain('VITE_SUPPORT_EMAIL=hello@hypertwist.app') expect(fs.readFileSync(written.serverEnvPath, 'utf8')).toContain('PADDLE_WEBHOOK_SECRET=secret') expect(fs.readFileSync(written.systemdPath, 'utf8')).toContain('EnvironmentFile=/srv/hypertwist/current/website/server/.env') expect(fs.readFileSync(written.nginxPath, 'utf8')).toContain('proxy_pass http://hypertwist_website_auth_server;') expect(JSON.parse(fs.readFileSync(written.summaryPath, 'utf8')).validationReport.ok).toBe(true) } finally { fs.rmSync(tempDir, { recursive: true, force: true }) } }) })