(cherry picked from commit367fd0d240) (cherry picked from commit068e1e0f1c)
112 lines
5.6 KiB
JavaScript
112 lines
5.6 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { spawnSync } from 'node:child_process';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { surveyMain } from '../src/index.mjs';
|
|
import { BASE, FIXTURES, MARKER, repo } from './helpers.mjs';
|
|
|
|
const ROOT = fileURLToPath(new URL('../../..', import.meta.url));
|
|
async function run(args) {
|
|
let stdout = '', stderr = '';
|
|
const code = await surveyMain(args, { stdout: { write: s => { stdout += s; } }, stderr: { write: s => { stderr += s; } } });
|
|
return { code, stdout, stderr };
|
|
}
|
|
// Strings from the fixture that are text, values, file names or link targets: none may be printed.
|
|
const NEVER = [MARKER, 'Grey eyes', 'The Ferryman', 'Mara Vell', 'Tomas', 'Harbor Town', 'What If', 'Inbox Note', 'Chapter 01',
|
|
'Sketch', 'Board', 'Not A Link', 'the harbor', 'Docks', 'character'];
|
|
|
|
test('the survey prints folder names, counts, key sets and link-form counts, and never text', async t => {
|
|
const r = repo(t, { ...BASE, 'Book One/Places/Odd.md': ['---', `"${MARKER} key": 1`, '---', ''].join('\n') });
|
|
const out = await run(['survey', r.repo]);
|
|
assert.equal(out.code, 0, out.stderr);
|
|
assert.equal(out.stdout, [
|
|
`commit ${r.commit}`,
|
|
'entries 10',
|
|
'folders (direct children: md canvas base other)',
|
|
' (root) 1 0 0 0',
|
|
' "Book One" 0 1 0 0',
|
|
' "Book One/Chapters" 1 0 0 0',
|
|
' "Book One/Characters" 2 0 0 0',
|
|
' "Book One/Places" 2 0 0 0',
|
|
' "Book One/Playground" 1 1 0 0',
|
|
' "Second World/Chapters" 1 0 0 0',
|
|
'markdown 8 files, 8 read, 3 without front matter, 0 with front matter not fully parsed',
|
|
'front matter key sets',
|
|
' 1 [(other key)]',
|
|
' 1 [appears_in, name, relationships, type]',
|
|
' 1 [appears_in, name, type]',
|
|
' 1 [name]',
|
|
' 1 [type]',
|
|
'body links plain 3, label 1, heading 1, embed 0, self 0, markdown 1; attachment 0',
|
|
'field appears_in wikilink 2, name 0',
|
|
'field relationships wikilink 1, name 1',
|
|
''].join('\n'));
|
|
for (const s of NEVER) assert.ok(!out.stdout.includes(s), s);
|
|
});
|
|
|
|
test('folder names print DEL, C1 and bidi controls, U+061C among them, as escapes', async t => {
|
|
// One name per escaped range, with each range's first and last code point.
|
|
const names = ['A\u007fB', 'C\u0080D\u009fE', 'F\u200eG\u200fH', 'I\u202aJ\u202eK', 'L\u2066M\u2069N', 'O\u061cP'];
|
|
const r = repo(t, Object.fromEntries(names.map(n => [`${n}/a.md`, `${MARKER}\n`])));
|
|
const out = await run(['survey', r.repo]);
|
|
assert.equal(out.code, 0, out.stderr);
|
|
assert.deepEqual(out.stdout.split('\n').slice(3, 9), [
|
|
' "A\\u007fB" 1 0 0 0', ' "C\\u0080D\\u009fE" 1 0 0 0', ' "F\\u200eG\\u200fH" 1 0 0 0',
|
|
' "I\\u202aJ\\u202eK" 1 0 0 0', ' "L\\u2066M\\u2069N" 1 0 0 0', ' "O\\u061cP" 1 0 0 0']);
|
|
assert.doesNotMatch(out.stdout, /[\u007f-\u009f\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/);
|
|
});
|
|
|
|
test('an embed of an attachment counts as an embed and as an attachment; a plain link to one does not', async t => {
|
|
const r = repo(t, { 'Book One/Chapters/A.md': ['![[cover.png]] ![[missing-note]] [[map.PDF]] [[Chapter 1.5]]', ''].join('\n') });
|
|
const out = await run(['survey', r.repo]);
|
|
assert.match(out.stdout, /^body links plain 2, label 0, heading 0, embed 2, self 0, markdown 0; attachment 1$/m);
|
|
});
|
|
|
|
test('with --mapping the survey adds scope, resolver step and problem counts', async t => {
|
|
const r = repo(t);
|
|
const out = await run(['survey', r.repo, '--mapping', join(FIXTURES, 'mapping.json')]);
|
|
assert.equal(out.code, 0, out.stderr);
|
|
const tail = out.stdout.trimEnd().split('\n').slice(-3);
|
|
assert.deepEqual(tail, [
|
|
'scopes canon 5, playground 1, supporting 0, unmapped 3; indexed 6',
|
|
'resolved root 2, vault-prefix 1, relative 2, shortest 3, unresolved 0',
|
|
'problems R1 0, R2 3, R3 0, R4 0, R5 0, R6 0, R7 0, R8 0']);
|
|
for (const s of NEVER) assert.ok(!out.stdout.includes(s), s);
|
|
});
|
|
|
|
test('C2-01d: with --mapping the survey counts supporting files', async t => {
|
|
const r = repo(t);
|
|
const entry = JSON.parse(readFileSync(join(FIXTURES, 'mapping.json'), 'utf8'));
|
|
entry.universes.harbor.supporting = ['Book One/Places/'];
|
|
const file = join(r.root, 'supporting.json');
|
|
writeFileSync(file, JSON.stringify(entry));
|
|
const out = await run(['survey', r.repo, '--mapping', file]);
|
|
assert.equal(out.code, 0, out.stderr);
|
|
assert.match(out.stdout, /^scopes canon 4, playground 1, supporting 1, unmapped 3; indexed 6$/m);
|
|
});
|
|
|
|
test('survey exit codes: usage 4, bad mapping 3, unreadable repo 2', async t => {
|
|
const r = repo(t);
|
|
for (const args of [[], ['scan', r.repo], ['survey'], ['survey', r.repo, '--mapping'], ['survey', r.repo, '--other'], ['survey', r.repo, r.repo]])
|
|
assert.equal((await run(args)).code, 4, args.join(' '));
|
|
const bad = join(r.root, 'bad.json');
|
|
writeFileSync(bad, JSON.stringify({ kind: 'library' }));
|
|
const refused = await run(['survey', r.repo, '--mapping', bad]);
|
|
assert.equal(refused.code, 3);
|
|
assert.match(refused.stderr, /library-mapping-invalid/);
|
|
writeFileSync(bad, 'not json');
|
|
assert.equal((await run(['survey', r.repo, '--mapping', bad])).code, 3);
|
|
assert.equal((await run(['survey', join(r.root, 'missing')])).code, 2);
|
|
assert.equal((await run(['survey', r.root])).code, 2, 'a directory that is not a repo');
|
|
});
|
|
|
|
test('scripts/mosaic library survey reaches the survey', t => {
|
|
const r = repo(t);
|
|
const out = spawnSync(join(ROOT, 'scripts/mosaic'), ['library', 'survey', r.repo], { encoding: 'utf8', timeout: 30000 });
|
|
assert.equal(out.status, 0, out.stderr);
|
|
assert.match(out.stdout, new RegExp(`^commit ${r.commit}\n`));
|
|
assert.ok(!out.stdout.includes(MARKER));
|
|
});
|