/** * DC-160 regression tests: catalog detail endpoint must read app.docker.* * (was app.image/ports/env/volumes -> empty payload for every app) and * surface install-time config fields. Table-driven over real templates. */ const path = require('path'); const express = require('express'); const request = require('supertest'); const APP_TEMPLATES = require(path.resolve(__dirname, '..', 'src', 'docker', 'app-templates')); const buildCatalog = require(path.resolve(__dirname, '..', 'routes', 'catalog')); const T = APP_TEMPLATES.APP_TEMPLATES || APP_TEMPLATES; // Canonical id derivation MUST mirror routes/catalog.js: templates carry no // `id` field — ids come from the map key / lowercased hyphenated name. function canonicalId(t) { return (t.id || (t.name || '').toLowerCase().replace(/\s+/g, '-')).toLowerCase().replace(/\s+/g, '-'); } const all = Object.values(T); // Widget/static templates have no docker.image (they are dashboard widgets, // not container apps) — they return correctly-empty details. The deployable // set for this regression is the 73 templates WITH an image (matches the // shipdeck catalog manifest count). const cases = all .filter((t) => t && t.docker && t.docker.image) .map((t) => [canonicalId(t), t]); function buildApp() { const e = express(); e.use('/api/v1', buildCatalog({ APP_TEMPLATES: T })); return e; } describe('DC-160: catalog detail reads app.docker.*', () => { it.each(cases)( 'detail for %s returns non-empty image + real ports/env/volumes', (id, t) => { const d = t.docker || {}; return request(buildApp()) .get('/api/v1/catalog/' + id) .expect(200) .then((res) => { const body = res.body.data || res.body; expect(body.image).toBeTruthy(); expect(body.image).toBe(d.image); expect(body.ports).toEqual(d.ports || []); expect(body.env).toEqual(d.environment || {}); expect(body.volumes).toEqual(d.volumes || []); expect(body.category).toBe(t.category); }); } ); it('detail for plex surfaces config fields for the deploy form', () => { return request(buildApp()) .get('/api/v1/catalog/plex') .expect(200) .then((res) => { const body = res.body.data || res.body; expect(body.image).toBe('plexinc/pms-docker:latest'); expect(body.defaultPort).toBe(32400); expect(body.claimToken).toMatchObject({ envVar: 'PLEX_CLAIM' }); expect(body.mediaMount).toBeTruthy(); expect(typeof body.mediaMount.containerPath).toBe('string'); }); }); it('unknown app returns 404', () => { return request(buildApp()) .get('/api/v1/catalog/definitely-not-an-app') .expect(404); }); });