27 lines
7.0 KiB
JavaScript
27 lines
7.0 KiB
JavaScript
// The real config loader and unit renderers with made-up HOME/config/data only.
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { dirname, join } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { spawnSync } from 'node:child_process';
|
|
import { installPaths } from '../src/settings.mjs';
|
|
const repo=fileURLToPath(new URL('../../../',import.meta.url));
|
|
function fixture(name='dev'){const root=mkdtempSync(join(tmpdir(),'p1-unit-')),configDir=join(root,'.config',`mosaic-${name}`),dataRoot=join(root,'data');mkdirSync(configDir,{recursive:true});mkdirSync(dataRoot);const file=join(configDir,'config.json');writeFileSync(file,JSON.stringify({configVersion:1,environment:'development',dataRoot,execution:{backend:'docker',provider:'fixture',model:'fixture'}}));if(name!=='dev')writeFileSync(join(dataRoot,'instance.json'),JSON.stringify({instanceVersion:1,name}));const env={PATH:process.env.PATH,HOME:root,MOSAIC_CONFIG:file};return {root,configDir,dataRoot,file,env};}
|
|
function run(f,script,args=[]){return spawnSync(script.endsWith('.mjs')?process.execPath:'bash',[join(repo,script),...args],{env:f.env,encoding:'utf8',timeout:10000});}
|
|
function proxy(f){const v={port:18432,binary:installPaths(f.dataRoot).binary};writeFileSync(join(f.configDir,'proxy.json'),JSON.stringify(v));return v;}
|
|
test('loader proxy absent is byte-compatible; present validates sidecar and preserves config.json',()=>{const f=fixture(),before=readFileSync(f.file);let r=run(f,'scripts/mosaic-config.mjs',['validate']);assert.equal(r.status,0,r.stderr);assert.equal(Object.hasOwn(JSON.parse(r.stdout),'proxy'),false);const value=proxy(f);r=run(f,'scripts/mosaic-config.mjs',['validate']);assert.equal(r.status,0,r.stderr);assert.deepEqual(JSON.parse(r.stdout).proxy,value);assert.deepEqual(readFileSync(f.file),before);});
|
|
test('proxy-config-invalid: loader refuses an unknown sidecar key and binary outside pinned install',()=>{for(const value of [{port:8317,binary:'/bad'},{port:18432,binary:'/bad'},{...{port:18432,binary:'/bad'},future:true}]){const f=fixture();writeFileSync(join(f.configDir,'proxy.json'),JSON.stringify(value));const r=run(f,'scripts/mosaic-config.mjs',['validate']);assert.equal(r.status,2);assert.match(r.stderr,/proxy-config-invalid/);}});
|
|
test('proxy unit and bus dependency render default and named instance only when configured',()=>{for(const name of ['dev','testbox']){const f=fixture(name),flags=name==='dev'?[]:['--instance',name];let r=run(f,'scripts/bus-service.sh',[...flags,'render']);assert.equal(r.status,0,r.stderr);assert.doesNotMatch(r.stdout,/Wants=mosaic-proxy/);proxy(f);r=run(f,'scripts/proxy-service.sh',[...flags,'render']);assert.equal(r.status,0,r.stderr);assert.match(r.stdout,/packages\/model-proxy\/src\/run.mjs/);assert.match(r.stdout,new RegExp(`Environment=MOSAIC_CONFIG=${f.file}`));assert.doesNotMatch(r.stdout,/@[A-Z_]+@/);r=run(f,'scripts/bus-service.sh',[...flags,'render']);assert.equal(r.status,0,r.stderr);const unit=`mosaic-proxy${name==='dev'?'':'-'+name}.service`;assert.ok(r.stdout.includes(`Wants=${unit}\nAfter=${unit}`),r.stdout);}});
|
|
test('proxy-not-configured: runner and unit render refuse absent sidecar',()=>{const f=fixture();for(const [script,args]of [['scripts/proxy-service.sh',['render']],['packages/model-proxy/src/run.mjs',[]]]){const r=run(f,script,args);assert.equal(r.status,3);assert.match(r.stderr,/proxy-not-configured/);}});
|
|
test('proxy-install-invalid: unit runner refuses tampered executable before spawning',()=>{const f=fixture(),p=installPaths(f.dataRoot);mkdirSync(dirname(p.binary),{recursive:true});writeFileSync(p.binary,'#!/bin/sh\ntouch '+join(f.root,'executed')+'\n',{mode:0o755});proxy(f);const r=run(f,'packages/model-proxy/src/run.mjs');assert.equal(r.status,3);assert.match(r.stderr,/proxy-install-invalid/);assert.equal(existsSync(join(f.root,'executed')),false);});
|
|
for(const [label,args,prep]of [['proxy-unit-usage',['bad'],()=>{}],['proxy-unit-exists',['install','--no-reload'],(f,dir)=>{mkdirSync(dir);writeFileSync(join(dir,'mosaic-proxy.service'),'old');}],['proxy-unit-dropin',['install','--no-reload'],(f,dir)=>{mkdirSync(join(dir,'service.d'),{recursive:true});}],['proxy-unit-path',['install','--no-reload','--dir','/invalid space'],()=>{}]])test(label,()=>{const f=fixture();proxy(f);const dir=join(f.root,'units');prep(f,dir);const r=run(f,'scripts/proxy-service.sh',[...args,...(label==='proxy-unit-path'?[]:['--dir',dir])]);assert.notEqual(r.status,0);assert.match(r.stderr,new RegExp(label));});
|
|
test('proxy-config-invalid: instance selection also refuses invalid present sidecar',()=>{const f=fixture();writeFileSync(join(f.configDir,'proxy.json'),'{');const r=run(f,'scripts/mosaic-config.mjs',['instance']);assert.equal(r.status,2);assert.match(r.stderr,/proxy-config-invalid/);});
|
|
test('explicit unit install writes only a scratch user unit and refuses a repeat',()=>{const f=fixture();proxy(f);const dir=join(f.root,'units');let r=run(f,'scripts/proxy-service.sh',['install','--dir',dir,'--no-reload']);assert.equal(r.status,0,r.stderr);const file=join(dir,'mosaic-proxy.service'),before=readFileSync(file);r=run(f,'scripts/proxy-service.sh',['install','--dir',dir,'--no-reload']);assert.equal(r.status,3);assert.match(r.stderr,/proxy-unit-exists/);assert.deepEqual(readFileSync(file),before);});
|
|
test('proxy-unit-copy: renderer refuses an unknown or unpinned stack copy',()=>{const f=fixture();proxy(f);const r=run(f,'scripts/proxy-service.sh',['render','--copy','a'.repeat(40),'--copies',join(f.root,'missing-copies')]);assert.equal(r.status,3);assert.match(r.stderr,/proxy-unit-copy/);});
|
|
test('historical no-proxy config loader works without new package dependency',()=>{const f=fixture();const copy=join(f.root,'historical/scripts');mkdirSync(copy,{recursive:true});writeFileSync(join(copy,'mosaic-config.mjs'),readFileSync(join(repo,'scripts/mosaic-config.mjs')));const r=spawnSync(process.execPath,[join(copy,'mosaic-config.mjs'),'validate'],{env:f.env,encoding:'utf8',timeout:10000});assert.equal(r.status,0,r.stderr);assert.equal(Object.hasOwn(JSON.parse(r.stdout),'proxy'),false);});
|
|
function customConfig(f,file){const bytes=readFileSync(f.file);mkdirSync(dirname(file),{recursive:true});writeFileSync(file,bytes);f.file=file;f.configDir=dirname(file);f.env.MOSAIC_CONFIG=file;proxy(f);}
|
|
test('configured default bus and proxy units bind the same explicit MOSAIC_CONFIG',()=>{const f=fixture();customConfig(f,join(f.root,'custom/config.json'));for(const script of ['scripts/bus-service.sh','scripts/proxy-service.sh']){const r=run(f,script,['render']);assert.equal(r.status,0,r.stderr);assert.ok(r.stdout.includes(`Environment=MOSAIC_CONFIG=${f.file}\n`),r.stdout);}});
|
|
test('proxy-config-invalid: configured bus refuses a config path systemd cannot carry',()=>{const f=fixture();customConfig(f,join(f.root,'custom config/config.json'));const r=run(f,'scripts/bus-service.sh',['render']);assert.equal(r.status,1);assert.match(r.stderr,/proxy-config-invalid/);});
|