import { describe, expect, it } from 'vitest' import { buildDefaultLiveDeployBundleDir, buildRemoteLiveDeployScript, buildStageBundleRemoteCommand, buildStageCheckoutRemoteCommand, buildSshArgs, extractLiveDeploySummary, resolveLiveDeployOptions, sanitizeLiveDeployManifest, } from './run-vps-same-origin-live-deploy-lib.mjs' function createManifest() { return { deploymentTier: 'preview', checkoutRoot: '/srv/hypertwist/current', publicOrigin: 'https://hypertwist.app', serviceUser: 'hypertwist', serviceGroup: 'hypertwist', supportEmail: 'hello@hypertwist.app', publicDocsUrl: 'https://hypertwist.app/resources', releaseNotesUrl: 'https://hypertwist.app/changelog', correspondingSourceUrl: 'https://git.scriptoriumai.io/scriptoriumadmin/hypertwist', openSourceRepoUrl: 'https://git.scriptoriumai.io/scriptoriumadmin/hypertwist', operatorCheckoutUrl: '', windowsDownloadUrl: '', releaseManifestVersion: '', windowsRelease: { buildId: '', sha256: '', publishedAt: '', }, server: { port: '3011', billingStatePath: '/var/lib/hypertwist/auth/hypertwist-billing-state.json', paddleWebhookSecret: 'super-secret-token', }, } } describe('resolveLiveDeployOptions', () => { it('normalizes the required CLI inputs', () => { const options = resolveLiveDeployOptions({ manifestPath: 'deploy/hypertwist.same-origin.bundle.json', identityFile: '/tmp/key', vpsHost: '212.227.13.220', vpsUser: 'root', archiveSource: 'worktree', bundleDir: '', healthUrl: '', waitSeconds: '45', keepBundleDir: true, skipLiveValidation: false, json: true, dryRun: false, manifest: createManifest(), gitRef: 'abc1234', }) expect(options.vpsUser).toBe('root') expect(options.archiveSource).toBe('worktree') expect(options.bundleDir).toBe('/tmp/hypertwist-live-deploy-abc1234') expect(options.healthUrl).toBe('https://hypertwist.app') expect(options.waitSeconds).toBe(45) expect(options.keepBundleDir).toBe(true) }) }) describe('sanitizeLiveDeployManifest', () => { it('masks secret-like values before dry-run output', () => { const sanitized = sanitizeLiveDeployManifest(createManifest()) expect(sanitized.server.paddleWebhookSecret).toBe('su***en') }) }) describe('buildRemoteLiveDeployScript', () => { it('includes the expected root-owned deployment steps and summary marker', () => { const script = buildRemoteLiveDeployScript({ checkoutRoot: '/srv/hypertwist/current', bundleDir: '/tmp/hypertwist-live-deploy-abc1234', keepBundleDir: false, manifest: createManifest(), }) expect(script).toContain('useradd --system') expect(script).toContain('npm ci && npm --prefix server ci && npm run build') expect(script).toContain('systemctl restart "$SERVICE_NAME"') expect(script).toContain('nginx -t') expect(script).toContain('__HYPERTWIST_LIVE_DEPLOY_SUMMARY__') }) }) describe('extractLiveDeploySummary', () => { it('parses the emitted summary marker from remote output', () => { const summary = extractLiveDeploySummary([ '[live_checkout_root] /srv/hypertwist/current', '__HYPERTWIST_LIVE_DEPLOY_SUMMARY__{"ok":true,"serviceStatus":"active","nginxStatus":"active"}', ].join('\n')) expect(summary).toEqual({ ok: true, serviceStatus: 'active', nginxStatus: 'active', }) }) }) describe('ssh helpers', () => { it('builds the archive and ssh command shapes', () => { expect(buildDefaultLiveDeployBundleDir('abc1234')).toBe('/tmp/hypertwist-live-deploy-abc1234') expect(buildStageCheckoutRemoteCommand('/srv/hypertwist/current')).toContain('tar -xf - -C "/srv/hypertwist/current"') expect(buildStageBundleRemoteCommand('/tmp/hypertwist-live-deploy-abc1234')).toContain('tar -xf - -C "/tmp/hypertwist-live-deploy-abc1234"') expect(buildSshArgs({ identityFile: '/tmp/key', vpsUser: 'root', vpsHost: '212.227.13.220', remoteCommand: 'echo hello', })).toEqual([ '-i', '/tmp/key', '-o', 'StrictHostKeyChecking=no', 'root@212.227.13.220', 'echo hello', ]) }) })