185 lines
7.3 KiB
JavaScript
185 lines
7.3 KiB
JavaScript
import fs from 'node:fs'
|
|
import os from 'node:os'
|
|
import path from 'node:path'
|
|
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
import {
|
|
buildBundleValidationReport,
|
|
buildFrontendEnvEntries,
|
|
buildServerEnvEntries,
|
|
renderSameOriginBundle,
|
|
writeSameOriginBundle,
|
|
} from './render-same-origin-bundle-lib.mjs'
|
|
|
|
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')
|
|
})
|
|
})
|
|
|
|
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')
|
|
})
|
|
})
|
|
|
|
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=')
|
|
})
|
|
})
|
|
|
|
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 })
|
|
}
|
|
})
|
|
})
|