Files
vendoo/tools/verify-platform-architecture-1.41.2.mjs
Masterluke77andGitHub 40071f718e Vendoo 1.43.0 – Production Update
Manifestbasierter Production-Updater 3.3, Modulnavigation, optionales Produktbild Studio, Listing-Studio-Aktionsleiste sowie plattformübergreifende Windows-/Linux-Release-Gates.
2026-07-10 13:16:21 +02:00

88 lines
6.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { mkdtempSync, readFileSync } from 'node:fs';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { tmpdir } from 'node:os';
import { closeVendooDatabase, removeTempDirectory } from './test-runtime-cleanup.mjs';
import { ModuleRegistry } from '../app/kernel/module-registry.mjs';
import { PolicyEngine } from '../app/kernel/policy-engine.mjs';
import { RouteRegistry } from '../app/kernel/route-registry.mjs';
const tempPrefix = process.env.VENDOO_TEST_TEMP_PREFIX || 'vendoo-platform-architecture-';
const temp = mkdtempSync(join(tmpdir(), tempPrefix));
process.env.NODE_ENV = 'test';
process.env.VENDOO_DATA_DIR = temp;
process.env.VENDOO_DB_PATH = join(temp, 'db', 'vendoo.db');
process.env.VENDOO_CONFIG_PATH = join(temp, 'config', 'vendoo.env');
process.env.VENDOO_MODULE_STATE_PATH = join(temp, 'config', 'modules-state.json');
process.env.VENDOO_MODULES_DIR = join(temp, 'modules');
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const read = rel => readFileSync(join(root, rel), 'utf8');
const failures = [];
const assert = (condition, message) => { if (!condition) failures.push(message); };
const expectThrow = async (fn, code, message) => { try { await fn(); failures.push(message); } catch (error) { if (error.code !== code) failures.push(`${message} (${error.code || error.message})`); } };
try {
const { loadBuiltInModules } = await import('../app/modules/catalog.mjs');
const pkg = JSON.parse(read('package.json'));
assert(pkg.version === '1.43.0', `package.json ist ${pkg.version}`);
const modules = loadBuiltInModules();
assert(modules.length === 18, `Erwartet 18 Module, erhalten ${modules.length}`);
const nativeIds = modules.filter(item => item.manifest.status === 'native').map(item => item.manifest.id).sort();
for (const id of ['vendoo.auth','vendoo.users','vendoo.sessions','vendoo.settings','vendoo.security','vendoo.themes','vendoo.listings','vendoo.inventory','vendoo.media','vendoo.ai','vendoo.flux-studio','vendoo.quality','vendoo.publishing','vendoo.module-manager','vendoo.deployments']) {
assert(nativeIds.includes(id), `Natives Modul fehlt: ${id}`);
}
assert(nativeIds.length === 16, `Erwartet 16 native Module, erhalten ${nativeIds.length}`);
assert(modules.filter(item => item.manifest.status === 'legacy-bridge').length === 2, 'Es müssen 2 Legacy-Bridges verbleiben');
const productImages = modules.find(item => item.manifest.id === 'vendoo.product-image-tools')?.manifest;
assert(productImages?.status === 'native' && productImages?.defaultEnabled === false, 'Produktbild Studio muss nativ und initial deaktiviert sein');
const registry = new ModuleRegistry();
for (const module of modules) registry.register(module);
let realGraph = null;
try { realGraph = registry.validateGraph(); } catch (error) { failures.push(`Realer Modulgraph ungültig: ${error.code || error.message} ${error.message}`); }
assert(registry.snapshot().length === 18, 'Modulregistrierung unvollständig');
assert(realGraph?.enabledModules.length === 18, 'Nicht alle 18 registrierten Module sind im realen Graph auflösbar');
assert(realGraph?.capabilities.includes('settings.configuration'), 'settings.configuration fehlt im realen Capability-Graph');
const cycle = new ModuleRegistry();
cycle.register({ manifest: { schemaVersion:1,id:'vendoo.a',name:'A',version:'1.0.0',type:'feature',status:'native',requires:['vendoo.b'],permissions:[],provides:['a.ready'],consumes:[],owns:['a.state'] } });
cycle.register({ manifest: { schemaVersion:1,id:'vendoo.b',name:'B',version:'1.0.0',type:'feature',status:'native',requires:['vendoo.a'],permissions:[],provides:['b.ready'],consumes:[],owns:['b.state'] } });
await expectThrow(() => cycle.startAll({}), 'MODULE_DEPENDENCY_CYCLE', 'Zyklus wurde nicht blockiert');
const policies = new PolicyEngine();
policies.register('test.read', ({ user }) => Boolean(user));
const routeModules = new ModuleRegistry();
routeModules.register({ manifest: { schemaVersion:1,id:'vendoo.test',name:'Test',version:'1.0.0',type:'feature',status:'native',requires:[],permissions:[],provides:[],consumes:[],owns:[] } }, { enabled: false });
const routes = new RouteRegistry({ policyEngine: policies, moduleRegistry: routeModules });
const fake = { get(_path, handler) { this.handler = handler; } };
routes.register(fake, { id:'test.route.read', method:'GET', path:'/x', owner:'vendoo.test', policy:'test.read', csrf:false, handler(_req,res){res.json({ok:true});} });
let status = 0; let payload = null;
await fake.handler({ user:{id:1}, requestId:'test' }, { headersSent:false, status(code){status=code;return this;}, json(value){payload=value;} }, () => {});
assert(status === 503 && payload?.code === 'MODULE_DISABLED', `Deaktiviertes Modul ergab ${status}`);
const moduleContract = read('app/kernel/module-contract.mjs');
assert(moduleContract.includes('defaultEnabled: manifest.defaultEnabled !== false'), 'Manifestvertrag für defaultEnabled fehlt');
const moduleSchema = JSON.parse(read('app/contracts/module.schema.json'));
assert(moduleSchema.properties.defaultEnabled?.type === 'boolean', 'JSON-Schema für defaultEnabled fehlt');
const kernel = read('app/kernel/platform-kernel.mjs');
for (const marker of ["policies.register('modules.manage'", "policies.register('updates.manage'", "features.define('deployments.coolify'", "features.define('modules.manager'", "features.define('publishing.native-module'", 'moduleStateStore', 'modulePackageStore']) assert(kernel.includes(marker), `Kernelvertrag fehlt: ${marker}`);
const catalog = read('app/modules/catalog.mjs');
for (const marker of ['createAiModule','createFluxStudioModule','createQualityModule','createPublishingModule','createModuleManagerModule','createDeploymentsModule','createProductImageToolsModule']) assert(catalog.includes(marker), `Modulkatalog fehlt: ${marker}`);
assert(catalog.includes("const legacyModuleNames = ['system', 'operations']"), 'Legacy-Liste ist nicht auf System und Operations begrenzt');
const boundaries = JSON.parse(read('app/architecture-boundaries.json'));
assert(boundaries.architecture === 'modular-monolith', 'Architekturtyp wurde verändert');
} finally {
await closeVendooDatabase({ failures, label: 'Architektur-Testdatenbank' });
await removeTempDirectory(temp, { failures, label: 'Architektur-Testordner' });
}
if (failures.length) {
console.error(`Platform-Architecture-Gate 1.43.0 fehlgeschlagen (${failures.length}):`);
failures.forEach(item => console.error(`- ${item}`));
process.exit(1);
}
console.log('Platform-Architecture-Gate 1.43.0 erfolgreich: 18 Module, 16 native Module, 2 Legacy-Bridges, davon 1 optionales Modul, dynamische Zustände und Modul-Routensperre geprüft.');