Files
roadmap/.github/scripts/storage-weekly.test.mjs
E M ae8d788d6c Workflow now creates PR instead of Claude
Moving the PR creation to the workflow and using a classic PAT with repo scope should hopefully allow the PR to be created. Previously, using a fine-grained PAT required pull-requests: write permissions on logos-co/roadmap which needed to be approved.
2026-08-13 14:09:49 +10:00

250 lines
11 KiB
JavaScript

/**
* Unit tests for the deterministic parts of storage-weekly.mjs.
* Run with: node --test .github/scripts/storage-weekly.test.mjs
* Golden cases come verbatim from the original routine's examples.
*/
import { test } from 'node:test';
import assert from 'node:assert/strict';
import {
weekFacts,
isoDate,
noteIsFilled,
normalizeLabels,
noteBody,
stripHighlights,
stripEmptySections,
buildUpdateFile,
buildSkeleton,
findNoteByTitle,
compareUrl,
buildPrPayload,
} from './storage-weekly.mjs';
// --- Date math -------------------------------------------------------------
test('golden: today 2026-07-07 → reported week 27', () => {
const f = weekFacts(new Date('2026-07-07T10:00:00Z'));
assert.equal(f.reported.week, 27);
assert.equal(f.reported.fileName, '2026-07-06.md');
assert.equal(isoDate(f.reported.monday), '2026-06-29');
assert.equal(f.reported.heading, '## Week 27 2026 (Mon Jun 29 - Fri Jul 3, 2026)');
assert.equal(f.reported.noteTitle, '2026-07-06 Logos storage weekly');
assert.equal(f.reported.branch, 'weeklies/storage-weekly-week-27');
});
test('golden: today 2026-07-21 (last real fire) → reported week 29', () => {
const f = weekFacts(new Date('2026-07-21T22:01:00Z'));
assert.equal(f.reported.week, 29);
assert.equal(f.reported.noteTitle, '2026-07-20 Logos storage weekly');
assert.equal(f.reported.heading, '## Week 29 2026 (Mon Jul 13 - Fri Jul 17, 2026)');
// upcoming = current week 30 → note titled with Monday+7
assert.equal(f.upcoming.noteTitle, '2026-07-27 Logos storage weekly');
assert.equal(isoDate(f.upcoming.monday), '2026-07-20');
});
test('golden: week 26 file (2026-06-29.md, the real repo file)', () => {
const f = weekFacts(new Date('2026-06-30T22:00:00Z'));
assert.equal(f.reported.week, 26);
assert.equal(f.reported.fileName, '2026-06-29.md');
assert.equal(isoDate(f.reported.monday), '2026-06-22');
assert.equal(f.reported.heading, '## Week 26 2026 (Mon Jun 22 - Fri Jun 26, 2026)');
});
test('reported week is day-of-week independent within a week', () => {
const tue = weekFacts(new Date('2026-07-21T00:00:00Z'));
const sun = weekFacts(new Date('2026-07-26T23:59:59Z'));
assert.equal(tue.reported.week, sun.reported.week);
assert.equal(tue.reported.fileName, sun.reported.fileName);
});
test('ISO week-year boundary: early January belongs to the right week-year', () => {
// 2027-01-05 is in ISO week 1 of 2027; reported week is 53 of 2026.
const f = weekFacts(new Date('2027-01-05T12:00:00Z'));
assert.equal(f.reported.week, 53);
assert.equal(f.reported.isoYear, 2026);
});
// --- Note matching ---------------------------------------------------------
const NOTES = [
{ id: 'a', title: '2026-07-20 Logos storage weekly' },
{ id: 'b', title: '2026-07-27 Logos storage weekly' },
{ id: 'c', title: 'REAL 2026-07-13 Logos storage weekly' },
{ id: 'd', title: 'unrelated note' },
];
test('findNoteByTitle: exact match wins, never picks the newest', () => {
const r = findNoteByTitle(NOTES, '2026-07-20 Logos storage weekly', '2026-07-20');
assert.equal(r.note.id, 'a');
assert.equal(r.fuzzy, false);
});
test('findNoteByTitle: tolerant fallback matches stray prefixes and flags it', () => {
const r = findNoteByTitle(NOTES, '2026-07-13 Logos storage weekly', '2026-07-13');
assert.equal(r.note.id, 'c');
assert.equal(r.fuzzy, true);
});
test('findNoteByTitle: zero matches → error flagged none (safe to create)', () => {
const r = findNoteByTitle(NOTES, '2026-08-03 Logos storage weekly', '2026-08-03');
assert.ok(r.error);
assert.equal(r.none, true);
assert.ok(!r.ambiguous);
});
test('findNoteByTitle: multiple fuzzy matches → flagged ambiguous (must not create)', () => {
const notes = [...NOTES, { id: 'e', title: 'DRAFT 2026-07-13 Logos storage weekly' }];
const r = findNoteByTitle(notes, '2026-07-13 Logos storage weekly', '2026-07-13');
assert.ok(r.error);
assert.equal(r.ambiguous, true);
});
// --- Empty-note guard ------------------------------------------------------
const SKELETON = `---
title: "2026-07-27 Logos storage weekly"
---
## Week 30 2026 (Mon Jul 20 - Fri Jul 24, 2026)
### [Status Integration](https://example.com)
- **achieved:**
- **next**
`;
test('noteIsFilled: blank skeleton (even with colonless label) is not filled', () => {
assert.equal(noteIsFilled(SKELETON), false);
});
test('noteIsFilled: one sub-bullet under any label counts as filled', () => {
assert.equal(noteIsFilled(SKELETON + ' - did a thing\n'), true);
});
// --- Transforms ------------------------------------------------------------
test('normalizeLabels: fixes colonless and outside-colon variants, keeps content', () => {
const input = '- **next**\n- **achieved:**\n- **Achieved**: stuff\n - sub';
assert.equal(
normalizeLabels(input),
'- **next:**\n- **achieved:**\n- **achieved:** stuff\n - sub'
);
});
test('noteBody drops frontmatter and the week heading, stripHighlights removes the section', () => {
const note = `---\ntitle: "x"\n---\n\n## REAL Week 29 2026 (stray edit)\n\n### Highlights\n\n* old bullet\n\n### [Section](url)\n\n- **achieved:**\n - work\n`;
const body = stripHighlights(noteBody(note));
assert.ok(!body.includes('Week 29'));
assert.ok(!body.includes('old bullet'));
assert.ok(body.startsWith('### [Section](url)'));
});
// --- Empty sections --------------------------------------------------------
// Verbatim from the routine's examples: the first reports nothing, the second does.
const EMPTY_SECTION = `### [Integration with Logos Core](https://github.com/logos-storage/logos-storage-nim/milestone/10)
- **achieved:**
- **next:**
`;
const FILLED_SECTION = `### [Anonymous DHT Queries](https://github.com/logos-storage/logos-storage-pm/issues/3)
- **achieved:**
- **next:**
- Evaluate replacing discv5 DHT with libp2p Kademlia DHT
`;
test('stripEmptySections: drops the label-only section, keeps the one with a sub-bullet', () => {
const out = stripEmptySections(`${EMPTY_SECTION}\n${FILLED_SECTION}`);
assert.ok(!out.includes('Integration with Logos Core'));
assert.ok(out.includes('Evaluate replacing discv5 DHT'));
assert.ok(out.startsWith('### [Anonymous DHT Queries]'));
});
test('stripEmptySections: an empty last section has no following heading to stop at', () => {
const out = stripEmptySections(`${FILLED_SECTION}\n${EMPTY_SECTION}`);
assert.ok(!out.includes('Integration with Logos Core'));
assert.ok(out.includes('Evaluate replacing discv5 DHT'));
});
test('stripEmptySections: no empty sections → byte-identical passthrough', () => {
const input = `${FILLED_SECTION}\n\n\n${FILLED_SECTION}`;
assert.equal(stripEmptySections(input), input);
});
test('stripEmptySections: one-sided and heading-only sections also count as empty', () => {
const out = stripEmptySections(
`### [Only a label](u1)\n\n- **next**\n\n### [Nothing at all](u2)\n\n${FILLED_SECTION}`
);
assert.ok(!out.includes('Only a label'));
assert.ok(!out.includes('Nothing at all'));
assert.ok(out.includes('Anonymous DHT Queries'));
});
test('stripEmptySections: content that is not a sub-bullet keeps the section', () => {
const prose = '### [Prose section](u)\n\n- **achieved:**\n- **next:**\n\nBlocked on review.\n';
assert.equal(stripEmptySections(prose), prose);
const sub = '### [Sub-heading section](u)\n\n- **achieved:**\n\n#### Detail\n';
assert.equal(stripEmptySections(sub), sub);
});
test('stripEmptySections: tolerates CRLF line endings from the HackMD API', () => {
const out = stripEmptySections(`${EMPTY_SECTION}\n${FILLED_SECTION}`.replace(/\n/g, '\r\n'));
assert.ok(!out.includes('Integration with Logos Core'));
});
test('buildUpdateFile: drops empty sections from the generated file', () => {
const facts = weekFacts(new Date('2026-07-21T22:00:00Z'));
const out = buildUpdateFile(facts, `${EMPTY_SECTION}\n${FILLED_SECTION}`, 'https://hackmd.io/xyz');
assert.ok(!out.includes('Integration with Logos Core'));
assert.ok(out.includes('Evaluate replacing discv5 DHT'));
assert.ok(out.includes('### Highlights'), 'the generated Highlights section survives');
});
test('buildUpdateFile: frontmatter, regenerated heading, pending-highlights marker', () => {
const facts = weekFacts(new Date('2026-07-21T22:00:00Z'));
const out = buildUpdateFile(facts, '### [S](u)\n\n- **next**\n - item\n', 'https://hackmd.io/xyz');
assert.ok(out.startsWith('---\ntitle: "2026-07-20 Logos storage weekly"\n'));
assert.ok(out.includes('date: 2026-07-13\n'));
assert.ok(out.includes('## Week 29 2026 (Mon Jul 13 - Fri Jul 17, 2026)\n'));
assert.ok(out.includes('### Highlights\n\n<!-- HIGHLIGHTS-PENDING source: https://hackmd.io/xyz -->'));
assert.ok(out.includes('- **next:**\n - item')); // label normalised
});
test('buildSkeleton: sections preserved in order, labels bare with colons, dates advanced', () => {
const facts = weekFacts(new Date('2026-07-21T22:00:00Z'));
const src = `## Week 29\n\n### [B section](u1)\n\n- **achieved:**\n - old work\n- **next**\n - old plan\n\n### [A section](u2)\n\n- **achieved:**\n - more\n`;
const skel = buildSkeleton(facts, src);
assert.ok(skel.includes('title: "2026-07-27 Logos storage weekly"'));
assert.ok(skel.includes('date: 2026-07-20'));
assert.ok(skel.includes('## Week 30 2026 (Mon Jul 20 - Fri Jul 24, 2026)'));
assert.ok(skel.indexOf('### [B section](u1)') < skel.indexOf('### [A section](u2)'));
assert.ok(skel.includes('### [B section](u1)\n\n- **achieved:**\n- **next:**'));
assert.ok(!skel.includes('old work'), 'must not carry work items forward');
assert.ok(!skel.includes('### Highlights'));
});
// --- Compare URL -----------------------------------------------------------
test('compareUrl: encodes title/body, includes /cc @gmega and source note', () => {
const facts = weekFacts(new Date('2026-07-21T22:00:00Z'));
const url = compareUrl(facts, 'https://hackmd.io/xyz');
assert.ok(url.startsWith('https://github.com/logos-co/roadmap/compare/v5...logos-storage:roadmap:weeklies%2Fstorage-weekly-week-29?expand=1'));
assert.ok(url.includes(`title=${encodeURIComponent('docs(storage): 2026-07-20 weekly')}`));
assert.ok(decodeURIComponent(url).includes('/cc @gmega'));
assert.ok(decodeURIComponent(url).includes('https://hackmd.io/xyz'));
assert.ok(decodeURIComponent(url).includes('Highlights section was generated'));
});
// --- PR payload ------------------------------------------------------------
test('buildPrPayload: cross-repo fork PR head/base, reuses title and body', () => {
const facts = weekFacts(new Date('2026-07-21T22:00:00Z'));
const p = buildPrPayload(facts, 'https://hackmd.io/xyz');
assert.equal(p.title, 'docs(storage): 2026-07-20 weekly');
assert.equal(p.base, 'v5');
assert.equal(p.head, 'logos-storage:weeklies/storage-weekly-week-29');
assert.ok(p.body.includes('https://hackmd.io/xyz'));
assert.ok(p.body.includes('/cc @gmega'));
});