Models: main.archify, registries, refs, links and the model page; 0.7.0
A folder with main.archify at its top is a model, a set of diagrams that belong together. Opening main.archify shows the model page: the model's name and description, its own overview diagram when the file has one, the diagrams grouped by folder (click to open), and the registry with where each element is used. The page is built by traversing the folder tree and refreshes when files change. *.archify-registry files define shared elements once; diagrams draw them with `ref: <id>` plus position and may override label, sublabel and tag. References are resolved before Archify runs, so Archify is unchanged. Hover, go-to-definition and completion on ref; unknown names, forbidden overrides, duplicate ids and broken registries in Problems; registry edits re-render open diagrams. `diagram:` links on elements and components open the linked diagram on double-click. New command Archify | Open Model, a Model template for New Diagram…, editor schemas for the new keys and for registries, docs/model.md and examples/online-shop. Single diagrams behave exactly as before.
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
schema_version: 1
|
||||
diagram_type: architecture
|
||||
meta:
|
||||
title: System context
|
||||
output: context.html
|
||||
components:
|
||||
- ref: customer
|
||||
pos: [40, 60]
|
||||
size: [130, 60]
|
||||
- ref: support
|
||||
pos: [40, 260]
|
||||
size: [130, 60]
|
||||
- ref: shop
|
||||
pos: [280, 160]
|
||||
size: [150, 60]
|
||||
- ref: psp
|
||||
pos: [520, 60]
|
||||
size: [150, 60]
|
||||
- ref: email
|
||||
pos: [520, 260]
|
||||
size: [150, 60]
|
||||
connections:
|
||||
- { from: customer, to: shop, label: buys, variant: emphasis }
|
||||
- { from: support, to: shop, label: manages orders }
|
||||
- { from: shop, to: psp, label: charges, variant: security }
|
||||
- { from: shop, to: email, label: sends mail }
|
||||
@@ -0,0 +1,39 @@
|
||||
schema_version: 1
|
||||
diagram_type: architecture
|
||||
meta:
|
||||
title: Containers
|
||||
output: containers.html
|
||||
components:
|
||||
- ref: customer
|
||||
pos: [40, 160]
|
||||
size: [130, 60]
|
||||
- ref: web
|
||||
pos: [250, 160]
|
||||
size: [130, 60]
|
||||
- ref: api
|
||||
pos: [460, 160]
|
||||
size: [130, 60]
|
||||
- ref: cache
|
||||
pos: [670, 60]
|
||||
size: [130, 60]
|
||||
- ref: db
|
||||
pos: [670, 260]
|
||||
size: [130, 60]
|
||||
- ref: queue
|
||||
pos: [460, 360]
|
||||
size: [130, 60]
|
||||
- ref: worker
|
||||
pos: [670, 460]
|
||||
size: [130, 60]
|
||||
boundaries:
|
||||
- kind: region
|
||||
label: Online Shop
|
||||
wraps: [web, api, cache, db, queue, worker]
|
||||
connections:
|
||||
- { from: customer, to: web, label: HTTPS, variant: emphasis }
|
||||
- { from: web, to: api, label: JSON }
|
||||
- { from: api, to: cache, label: read }
|
||||
- { from: api, to: db, label: SQL }
|
||||
- { from: api, to: queue, label: enqueue, labelAt: [560, 290] }
|
||||
- { from: queue, to: worker, label: poll }
|
||||
- { from: worker, to: db, label: SQL, labelAt: [735, 420] }
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
schema_version: 1
|
||||
diagram_type: architecture
|
||||
meta:
|
||||
title: API components
|
||||
output: api.html
|
||||
components:
|
||||
- ref: web
|
||||
pos: [40, 160]
|
||||
size: [130, 60]
|
||||
- id: auth
|
||||
type: security
|
||||
label: Auth
|
||||
sublabel: sessions, OIDC
|
||||
pos: [250, 60]
|
||||
size: [130, 60]
|
||||
- id: orders
|
||||
type: backend
|
||||
label: Orders
|
||||
pos: [250, 160]
|
||||
size: [130, 60]
|
||||
- id: catalogue
|
||||
type: backend
|
||||
label: Catalogue
|
||||
pos: [250, 260]
|
||||
size: [130, 60]
|
||||
- ref: db
|
||||
pos: [460, 160]
|
||||
size: [130, 60]
|
||||
- ref: queue
|
||||
pos: [460, 310]
|
||||
size: [130, 60]
|
||||
boundaries:
|
||||
- kind: region
|
||||
label: API
|
||||
wraps: [auth, orders, catalogue]
|
||||
connections:
|
||||
- { from: web, to: auth, label: sign in, variant: security }
|
||||
- { from: web, to: orders, label: place order }
|
||||
- { from: web, to: catalogue, label: browse }
|
||||
- { from: orders, to: db, label: SQL }
|
||||
- { from: catalogue, to: db, label: SQL }
|
||||
- { from: orders, to: queue, label: enqueue }
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
schema_version: 1
|
||||
model:
|
||||
id: online-shop
|
||||
name: Online Shop
|
||||
description: >
|
||||
The web shop, its API and workers, and the stores behind them.
|
||||
Context, containers and components, C4 style.
|
||||
diagram_type: architecture
|
||||
meta:
|
||||
title: Online Shop — overview
|
||||
output: main.html
|
||||
components:
|
||||
- ref: customer
|
||||
pos: [40, 160]
|
||||
size: [130, 60]
|
||||
- ref: shop
|
||||
pos: [250, 160]
|
||||
size: [150, 60]
|
||||
- ref: psp
|
||||
pos: [480, 160]
|
||||
size: [150, 60]
|
||||
connections:
|
||||
- { from: customer, to: shop, label: buys, variant: emphasis }
|
||||
- { from: shop, to: psp, label: charges }
|
||||
@@ -0,0 +1,37 @@
|
||||
schema_version: 1
|
||||
elements:
|
||||
web:
|
||||
type: frontend
|
||||
label: Web App
|
||||
sublabel: React
|
||||
brand: react
|
||||
description: The shop as customers see it; talks only to the API.
|
||||
api:
|
||||
type: backend
|
||||
label: API
|
||||
sublabel: Node.js
|
||||
brand: node-js
|
||||
description: Owns authentication, orders and the catalogue.
|
||||
diagram: 0300-components/api.archify
|
||||
worker:
|
||||
type: backend
|
||||
label: Worker
|
||||
sublabel: async jobs
|
||||
description: Picks jobs from the queue; emails, invoices, exports.
|
||||
queue:
|
||||
type: messagebus
|
||||
label: Queue
|
||||
sublabel: SQS
|
||||
description: Job queue between the API and the workers.
|
||||
db:
|
||||
type: database
|
||||
label: Database
|
||||
sublabel: PostgreSQL
|
||||
brand: postgresql
|
||||
description: Orders, customers and catalogue; the source of truth.
|
||||
cache:
|
||||
type: database
|
||||
label: Cache
|
||||
sublabel: Redis
|
||||
brand: redis
|
||||
description: Sessions and catalogue cache; nothing here is the source of truth.
|
||||
@@ -0,0 +1,12 @@
|
||||
schema_version: 1
|
||||
elements:
|
||||
customer:
|
||||
type: external
|
||||
label: Customer
|
||||
sublabel: web and mobile
|
||||
description: Buys products; signs in with email or a social account.
|
||||
support:
|
||||
type: external
|
||||
label: Support agent
|
||||
sublabel: back office
|
||||
description: Handles refunds and order problems in the admin console.
|
||||
@@ -0,0 +1,19 @@
|
||||
schema_version: 1
|
||||
elements:
|
||||
shop:
|
||||
type: cloud
|
||||
label: Online Shop
|
||||
sublabel: the system
|
||||
description: Everything we build and run; web app, API, workers and stores.
|
||||
diagram: 0200-containers/online-shop.archify
|
||||
psp:
|
||||
type: external
|
||||
label: Payment provider
|
||||
sublabel: Stripe
|
||||
brand: stripe
|
||||
description: Charges cards and pays out; we never see card numbers.
|
||||
email:
|
||||
type: external
|
||||
label: Email service
|
||||
sublabel: transactional mail
|
||||
description: Sends order confirmations and password resets.
|
||||
@@ -248,4 +248,96 @@ export async function run(): Promise<void> {
|
||||
'unverifiable evidence is a warning in fallback mode',
|
||||
);
|
||||
});
|
||||
|
||||
// --- models: a folder with main.archify, registries and diagrams that use `ref`
|
||||
|
||||
await check('a diagram inside a model resolves registry refs and validates clean', async () => {
|
||||
const uri = workspaceFile('model/0200-containers/online-shop.archify');
|
||||
const doc = await vscode.workspace.openTextDocument(uri);
|
||||
await vscode.window.showTextDocument(doc);
|
||||
// Validation runs through the Archify CLI; give it time, then expect no errors at all.
|
||||
await new Promise((resolve) => setTimeout(resolve, 6000));
|
||||
const errors = vscode.languages.getDiagnostics(doc.uri).filter((d) => d.source === 'archify' && d.severity === vscode.DiagnosticSeverity.Error);
|
||||
assert.deepEqual(errors.map((d) => d.message), []);
|
||||
});
|
||||
|
||||
await check('an unknown ref is reported at its line', async () => {
|
||||
const target = workspaceFile('model/0300-components/ghost.archify');
|
||||
await vscode.workspace.fs.writeFile(
|
||||
target,
|
||||
new TextEncoder().encode('schema_version: 1\ndiagram_type: architecture\nmeta: { title: Ghost, output: ghost.html }\ncomponents:\n - ref: ghost\n pos: [40, 40]\n size: [130, 60]\n'),
|
||||
);
|
||||
const doc = await vscode.workspace.openTextDocument(target);
|
||||
await vscode.window.showTextDocument(doc);
|
||||
const diagnostic = await waitFor('unknown ref diagnostic', () =>
|
||||
vscode.languages.getDiagnostics(doc.uri).find((d) => d.source === 'archify' && /Unknown element "ghost"/.test(d.message)),
|
||||
);
|
||||
assert.equal(diagnostic.range.start.line, 4, 'reported on the ref line');
|
||||
});
|
||||
|
||||
await check('hover and completion on ref come from the registry', async () => {
|
||||
const uri = workspaceFile('model/0200-containers/online-shop.archify');
|
||||
const doc = await vscode.workspace.openTextDocument(uri);
|
||||
const line = doc.getText().split('\n').findIndex((l) => /ref: api$/.test(l));
|
||||
assert.ok(line >= 0, 'the containers diagram refers to api');
|
||||
const hovers = await vscode.commands.executeCommand<vscode.Hover[]>('vscode.executeHoverProvider', uri, new vscode.Position(line, 10));
|
||||
const text = hovers.flatMap((h) => h.contents).map((c) => (typeof c === 'string' ? c : c.value)).join('\n');
|
||||
assert.match(text, /Owns authentication/);
|
||||
const defs = await vscode.commands.executeCommand<vscode.Location[]>('vscode.executeDefinitionProvider', uri, new vscode.Position(line, 10));
|
||||
assert.match(defs[0].uri.path, /registry\/containers\.archify-registry$/);
|
||||
const completions = await vscode.commands.executeCommand<vscode.CompletionList>('vscode.executeCompletionItemProvider', uri, new vscode.Position(line, 9));
|
||||
const labels = completions.items.map((i) => (typeof i.label === 'string' ? i.label : i.label.label));
|
||||
assert.ok(labels.includes('api') && labels.includes('queue'), `completion lists registry elements: ${labels.join(', ')}`);
|
||||
});
|
||||
|
||||
await check('a registry file reports duplicate ids and parse problems', async () => {
|
||||
const target = workspaceFile('model/registry/extra.archify-registry');
|
||||
await vscode.workspace.fs.writeFile(target, new TextEncoder().encode('schema_version: 1\nelements:\n api:\n type: backend\n label: API again\n'));
|
||||
const doc = await vscode.workspace.openTextDocument(target);
|
||||
await vscode.window.showTextDocument(doc);
|
||||
const diagnostic = await waitFor('duplicate id diagnostic', () =>
|
||||
vscode.languages.getDiagnostics(doc.uri).find((d) => d.source === 'archify' && /defined twice/.test(d.message)),
|
||||
);
|
||||
assert.equal(diagnostic.range.start.line, 2);
|
||||
await vscode.workspace.fs.delete(target);
|
||||
});
|
||||
|
||||
await check('main.archify opens as the model page and Open Model finds it from a diagram', async () => {
|
||||
const main = workspaceFile('model/main.archify');
|
||||
await vscode.commands.executeCommand('vscode.open', main);
|
||||
await waitFor('model page tab', () =>
|
||||
vscode.window.tabGroups.all.flatMap((g) => g.tabs).some((t) => t.input instanceof vscode.TabInputCustom && t.input.viewType === 'archify.diagram' && t.input.uri.toString() === main.toString()),
|
||||
);
|
||||
const diagram = workspaceFile('model/0100-context/online-shop.archify');
|
||||
await vscode.window.showTextDocument(await vscode.workspace.openTextDocument(diagram));
|
||||
await vscode.commands.executeCommand('archify.openModel', diagram);
|
||||
const tab = await waitFor('model page active', () =>
|
||||
vscode.window.tabGroups.all.flatMap((g) => g.tabs).find((t) => t.isActive && t.input instanceof vscode.TabInputCustom && t.input.uri.toString() === main.toString()),
|
||||
);
|
||||
assert.ok(tab);
|
||||
});
|
||||
|
||||
await check('Archify | New Diagram… → Model creates main.archify from the folder name', async () => {
|
||||
const folder = workspaceFile('new-shop');
|
||||
await vscode.workspace.fs.createDirectory(folder);
|
||||
const { window } = vscode;
|
||||
const originalPick = window.showQuickPick;
|
||||
(window as { showQuickPick: unknown }).showQuickPick = async (items: Thenable<{ label: string }[]> | { label: string }[]) => (await items).find((i) => /^Model/.test(i.label));
|
||||
try {
|
||||
await vscode.commands.executeCommand('archify.newDiagram', folder);
|
||||
} finally {
|
||||
window.showQuickPick = originalPick;
|
||||
}
|
||||
const main = vscode.Uri.joinPath(folder, 'main.archify');
|
||||
let content = '';
|
||||
for (let i = 0; i < 50 && !content; i++) {
|
||||
try {
|
||||
content = new TextDecoder().decode(await vscode.workspace.fs.readFile(main));
|
||||
} catch {
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
}
|
||||
}
|
||||
assert.match(content, /id: new-shop/);
|
||||
assert.match(content, /name: New shop/);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { test } from 'node:test';
|
||||
import {
|
||||
groupOf,
|
||||
hasDiagramBody,
|
||||
isMainFile,
|
||||
isRegistryFile,
|
||||
mergeRegistries,
|
||||
parseRegistry,
|
||||
readModelMeta,
|
||||
resolveRefs,
|
||||
sortedGroups,
|
||||
summarizeDiagram,
|
||||
} from '../src/model/format';
|
||||
import { parse } from 'yaml';
|
||||
|
||||
const model = path.resolve(__dirname, '..', '..', 'test', 'fixtures', 'model');
|
||||
const read = (rel: string) => fs.readFileSync(path.join(model, rel), 'utf8');
|
||||
|
||||
function registryOfModel() {
|
||||
const files = ['registry/people.archify-registry', 'registry/systems.archify-registry', 'registry/containers.archify-registry'];
|
||||
return mergeRegistries(files.map((file) => ({ file, ...parseRegistry(read(file), file) })));
|
||||
}
|
||||
|
||||
test('file kinds are told apart by name', () => {
|
||||
assert.ok(isMainFile('/m/online-shop/main.archify'));
|
||||
assert.ok(!isMainFile('/m/online-shop/0100-context/main-flow.archify'));
|
||||
assert.ok(isRegistryFile('registry/people.archify-registry'));
|
||||
assert.ok(!isRegistryFile('people.archify'));
|
||||
});
|
||||
|
||||
test('main.archify: model block and optional diagram body', () => {
|
||||
const doc = parse(read('main.archify'));
|
||||
assert.deepEqual(readModelMeta(doc), {
|
||||
id: 'online-shop',
|
||||
name: 'Online Shop',
|
||||
description: 'The web shop, its API and workers, and the stores behind them. Context, containers and components, C4 style.\n',
|
||||
});
|
||||
assert.ok(hasDiagramBody(doc));
|
||||
assert.equal(readModelMeta(parse(read('0100-context/online-shop.archify'))), undefined);
|
||||
assert.ok(!hasDiagramBody({ schema_version: 1, model: { id: 'x' } }));
|
||||
});
|
||||
|
||||
test('registry: elements with line numbers, duplicates reported once', () => {
|
||||
const people = parseRegistry(read('registry/people.archify-registry'), 'people');
|
||||
assert.deepEqual(people.problems, []);
|
||||
assert.equal(people.elements.length, 2);
|
||||
const customer = people.elements[0];
|
||||
assert.equal(customer.id, 'customer');
|
||||
assert.equal(customer.type, 'external');
|
||||
assert.equal(customer.line, 2);
|
||||
const merged = mergeRegistries([
|
||||
{ file: 'a', ...parseRegistry('elements:\n api: { type: backend, label: API }\n', 'a') },
|
||||
{ file: 'b', ...parseRegistry('elements:\n api: { type: backend, label: API again }\n db: { type: database, label: DB }\n', 'b') },
|
||||
]);
|
||||
assert.equal(merged.elements.size, 2);
|
||||
assert.equal(merged.problems.length, 1);
|
||||
assert.match(merged.problems[0].message, /"api" is defined twice/);
|
||||
assert.equal(merged.problems[0].file, 'b');
|
||||
});
|
||||
|
||||
test('registry: broken files are reported, not thrown', () => {
|
||||
const bad = parseRegistry('elements:\n api: {\n', 'bad');
|
||||
assert.equal(bad.elements.length, 0);
|
||||
assert.equal(bad.problems[0].severity, 'error');
|
||||
const noMap = parseRegistry('- just\n- a list\n', 'list');
|
||||
assert.match(noMap.problems[0].message, /mapping/);
|
||||
});
|
||||
|
||||
test('resolve: refs become full components, links and usage are collected', () => {
|
||||
const registry = registryOfModel();
|
||||
const doc = parse(read('0200-containers/online-shop.archify'));
|
||||
const r = resolveRefs(doc, { registry, fileName: 'containers.archify' });
|
||||
assert.deepEqual(r.problems, []);
|
||||
const api = (r.diagram.components as Record<string, unknown>[]).find((c) => c.id === 'api')!;
|
||||
assert.deepEqual(api, { id: 'api', type: 'backend', label: 'API', sublabel: 'Node.js', brand: 'node-js', pos: [460, 160], size: [130, 60] });
|
||||
assert.equal(r.links.get('api'), '0300-components/api.archify');
|
||||
assert.ok(!r.links.has('db'));
|
||||
assert.deepEqual([...r.used].sort(), ['api', 'cache', 'customer', 'db', 'queue', 'web', 'worker']);
|
||||
assert.equal(r.refs.get(2)?.id, 'api');
|
||||
assert.ok(!('model' in r.diagram));
|
||||
});
|
||||
|
||||
test('resolve: overrides, second id for the same element, inline elements and links', () => {
|
||||
const registry = registryOfModel();
|
||||
const doc = {
|
||||
components: [
|
||||
{ ref: 'db', label: 'Orders DB', pos: [0, 0] },
|
||||
{ ref: 'db', id: 'db-replica', sublabel: 'replica', pos: [0, 0] },
|
||||
{ id: 'cron', type: 'backend', label: 'Nightly job', diagram: 'jobs.archify', pos: [0, 0] },
|
||||
{ ref: 'api', diagram: 'other.archify', pos: [0, 0] },
|
||||
],
|
||||
};
|
||||
const r = resolveRefs(doc, { registry, fileName: 'x.archify' });
|
||||
const components = r.diagram.components as Record<string, unknown>[];
|
||||
assert.equal(components[0].label, 'Orders DB');
|
||||
assert.equal(components[0].brand, 'postgresql');
|
||||
assert.equal(components[1].id, 'db-replica');
|
||||
assert.equal(components[1].sublabel, 'replica');
|
||||
assert.deepEqual(components[2], { id: 'cron', type: 'backend', label: 'Nightly job', pos: [0, 0] });
|
||||
assert.equal(r.links.get('cron'), 'jobs.archify');
|
||||
assert.equal(r.links.get('api'), 'other.archify', 'the component link wins over the registry link');
|
||||
assert.deepEqual(r.problems, []);
|
||||
});
|
||||
|
||||
test('resolve: unknown refs, forbidden overrides, outside a model', () => {
|
||||
const registry = registryOfModel();
|
||||
const r = resolveRefs({ components: [{ ref: 'ghost', pos: [0, 0] }, { ref: 'api', type: 'frontend', brand: 'react', pos: [0, 0] }] }, { registry, fileName: 'x.archify' });
|
||||
assert.equal(r.problems.length, 3);
|
||||
assert.equal(r.problems[0].pointer, '/components/0/ref');
|
||||
assert.match(r.problems[0].message, /Unknown element "ghost"/);
|
||||
assert.deepEqual(r.problems.slice(1).map((p) => p.pointer), ['/components/1/type', '/components/1/brand']);
|
||||
const ghost = (r.diagram.components as Record<string, unknown>[])[0];
|
||||
assert.deepEqual(ghost, { type: 'external', label: 'ghost', pos: [0, 0], id: 'ghost' });
|
||||
const outside = resolveRefs({ components: [{ ref: 'api', pos: [0, 0] }] }, { fileName: '/w/loose.archify' });
|
||||
assert.match(outside.problems[0].message, /not inside a model/);
|
||||
});
|
||||
|
||||
test('summaries and groups: root first, then folders', () => {
|
||||
const summaries = [
|
||||
summarizeDiagram(read('0300-components/api.archify'), '0300-components/api.archify'),
|
||||
summarizeDiagram(read('0100-context/online-shop.archify'), '0100-context/online-shop.archify'),
|
||||
summarizeDiagram('schema_version: 1\ndiagram_type: workflow\nmeta: { title: Checkout }\n', 'checkout.archify'),
|
||||
summarizeDiagram('oops: [', 'broken.archify'),
|
||||
];
|
||||
assert.equal(summaries[0].title, 'API components');
|
||||
assert.equal(summaries[0].kind, 'architecture');
|
||||
assert.deepEqual(summaries[0].used, ['web', 'db', 'queue']);
|
||||
assert.ok(summaries[3].parseError);
|
||||
assert.equal(groupOf('a/b/c.archify'), 'a/b');
|
||||
assert.equal(groupOf('c.archify'), '');
|
||||
const groups = sortedGroups(summaries);
|
||||
assert.deepEqual(groups.map((g) => g.group), ['', '0100-context', '0300-components']);
|
||||
assert.deepEqual(groups[0].diagrams.map((d) => d.relativePath), ['broken.archify', 'checkout.archify']);
|
||||
});
|
||||
@@ -27,7 +27,7 @@ function load(name: string) {
|
||||
return source;
|
||||
}
|
||||
|
||||
for (const name of fs.readdirSync(fixtures).filter((file) => !file.startsWith('mco-'))) {
|
||||
for (const name of fs.readdirSync(fixtures).filter((file) => !file.startsWith('mco-') && fs.statSync(path.join(fixtures, file)).isFile())) {
|
||||
test(`renders ${name}`, async () => {
|
||||
const result = await renderDiagram(runtime(), load(name), 'strict');
|
||||
assert.equal(result.error, undefined);
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ test('bundled schemas carry no absolute $id', () => {
|
||||
|
||||
test('the .archify schema accepts every fixture', () => {
|
||||
const validate = validator();
|
||||
for (const file of fs.readdirSync(fixtures)) {
|
||||
for (const file of fs.readdirSync(fixtures).filter((entry) => fs.statSync(path.join(fixtures, entry)).isFile())) {
|
||||
const ok = validate(parseYaml(fs.readFileSync(path.join(fixtures, file), 'utf8')));
|
||||
assert.ok(ok, `${file}: ${JSON.stringify(validate.errors?.slice(0, 3))}`);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import path from 'node:path';
|
||||
import { test } from 'node:test';
|
||||
import { parse } from 'yaml';
|
||||
import { validate } from '../src/archify/cli';
|
||||
import { resolveRefs } from '../src/model/format';
|
||||
import { diagramFromTemplate, diagramName, invalidNameReason, TEMPLATES, titleFromName } from '../src/language/templates';
|
||||
import { readDiagramSource } from '../src/sources/detect';
|
||||
|
||||
@@ -29,7 +30,9 @@ for (const item of TEMPLATES) {
|
||||
assert.ok(source);
|
||||
assert.equal(source.type, item.type);
|
||||
assert.equal(source.format, 'yaml');
|
||||
const result = await validate(runtime, source.type, source.json);
|
||||
// As the extension does before Archify runs: drop the model block, expand refs.
|
||||
const resolved = resolveRefs(source.diagram, { fileName: `${item.defaultName}.archify` });
|
||||
const result = await validate(runtime, source.type, JSON.stringify(resolved.diagram));
|
||||
assert.deepEqual(
|
||||
result.diagnostics.filter((d) => d.severity === 'error').map((d) => `${d.code}: ${d.message}`),
|
||||
[],
|
||||
|
||||
Reference in New Issue
Block a user