- DC-160: /catalog/:appId read app.image/ports/env/volumes which live under app.docker.* — every detail response was empty. Now reads docker.* and surfaces defaultPort/subdomain/healthCheck/mediaMount/claimToken/difficulty/ setupInstructions for the deploy form. List/search use the templates own category field (old hand-rolled CATEGORY_MAP mis-categorized apps). - Shield (routes/shield.js + 5-line app.js mount) committed to source after weeks as prod-only drift; regression test asserts the mount exists. - New composition-root e2e suite: boots REAL createApp() on an ephemeral port, probes the historically-broken routes through the REAL mount table (wizard wiring, shield, catalog detail, deploys bridge) — the class of bug all 3 P7 dogfood findings belonged to. 5 tests. - Full suite 3499/3499 (155 suites); table-driven DC-160 test covers all 73 image templates.
76 lines
2.7 KiB
JavaScript
76 lines
2.7 KiB
JavaScript
/**
|
|
* 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);
|
|
});
|
|
});
|