forked from CopilotKit/CopilotKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaudit.unit.test.ts
More file actions
1189 lines (1136 loc) · 46.7 KB
/
Copy pathaudit.unit.test.ts
File metadata and controls
1189 lines (1136 loc) · 46.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Split from audit.test.ts — see audit.shared.ts header for the full
// rationale (vitest birpc 60s cliff, fork-per-file).
//
// This file hosts the pure-unit describes that don't spawn the CLI and
// don't exercise the `auditPackage`/`buildReport` integration surface:
// manifest parsing, count helpers, findExamplesSource, parseArgs, small
// module constants (BORN_IN_SHOWCASE, SLUG_TO_EXAMPLES), and assorted
// utility classes (UnreadableDirError, canonicalizeForIsMain,
// listShowcasePackageSlugs).
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import fs from "fs";
import os from "os";
import path from "path";
import {
auditPackage,
buildReport,
listShowcasePackageSlugs,
readManifest,
countFiles,
findExamplesSource,
resolveExamplesSource,
isProgrammerBug,
parseArgs,
UnreadableDirError,
canonicalizeForIsMain,
BORN_IN_SHOWCASE,
SLUG_TO_EXAMPLES,
type Anomaly,
} from "../audit.js";
import {
makeTmpTree,
makeConfig,
writePackage,
makeExampleDir,
} from "./audit.shared.js";
describe("readManifest", () => {
let root: string;
beforeEach(() => {
root = makeTmpTree();
});
afterEach(() => {
fs.rmSync(root, { recursive: true, force: true });
});
it("returns { kind: 'missing' } when manifest.yaml does not exist", () => {
writePackage(root, "mypkg", {});
const cfg = makeConfig(root);
const r = readManifest("mypkg", cfg);
expect(r.kind).toBe("missing");
});
it("returns { kind: 'malformed', error } when manifest.yaml is malformed YAML", () => {
writePackage(root, "mypkg", {
manifest: "slug: mypkg\n bad:: indent: [unterminated\n",
});
const cfg = makeConfig(root);
const r = readManifest("mypkg", cfg);
expect(r.kind).toBe("malformed");
if (r.kind === "malformed") {
expect(typeof r.error).toBe("string");
expect(r.error.length).toBeGreaterThan(0);
}
});
it("returns { kind: 'ok', manifest } for a valid manifest", () => {
writePackage(root, "mypkg", {
manifest: "slug: mypkg\ndeployed: true\ndemos:\n - id: foo\n",
});
const cfg = makeConfig(root);
const r = readManifest("mypkg", cfg);
expect(r.kind).toBe("ok");
if (r.kind === "ok") {
expect(r.manifest.slug).toBe("mypkg");
expect(r.manifest.deployed).toBe(true);
expect(r.manifest.demos?.length).toBe(1);
}
});
it("returns { kind: 'malformed' } for empty manifest.yaml (yaml.parse → null)", () => {
// yaml.parse("") returns null, and previously the parsed-as-Manifest
// cast let null propagate into downstream .demos / .deployed access,
// crashing with TypeError. Empty/non-object YAML must be rejected here
// before auditPackage touches it.
writePackage(root, "mypkg", { manifest: "" });
const cfg = makeConfig(root);
const r = readManifest("mypkg", cfg);
expect(r.kind).toBe("malformed");
});
it("returns { kind: 'malformed' } when manifest.yaml parses to a non-object (e.g. bare scalar)", () => {
// yaml.parse("42") returns the number 42 — also not a valid Manifest.
writePackage(root, "mypkg", { manifest: "42\n" });
const cfg = makeConfig(root);
const r = readManifest("mypkg", cfg);
expect(r.kind).toBe("malformed");
});
it("passes the dir slug to parseManifest so a slug-mismatch flags as malformed", () => {
// readManifest must pass the directory slug to parseManifest so
// a manifest whose declared `slug:` disagrees
// with the directory holding it is surfaced as a shape error
// (not silently accepted). Package dir name is "dir-slug" but
// manifest declares "manifest-slug" — shape validation catches
// the drift only if dirSlug flows through.
writePackage(root, "dir-slug", {
manifest: "slug: manifest-slug\ndeployed: true\ndemos:\n - id: a\n",
});
const cfg = makeConfig(root);
const r = readManifest("dir-slug", cfg);
expect(r.kind).toBe("malformed");
if (r.kind === "malformed") {
expect(r.subkind).toBe("shape");
expect(r.error).toMatch(/slug mismatch/);
expect(r.error).toContain("manifest-slug");
expect(r.error).toContain("dir-slug");
}
});
it("accepts a manifest whose slug matches the directory name", () => {
// Counterpart of the slug-mismatch test: when the manifest's
// declared slug matches the directory slug, parseManifest's guard
// passes and the kind is 'ok'.
writePackage(root, "same-slug", {
manifest: "slug: same-slug\ndeployed: true\ndemos:\n - id: a\n",
});
const cfg = makeConfig(root);
const r = readManifest("same-slug", cfg);
expect(r.kind).toBe("ok");
});
it("returns { kind: 'unreadable', error } (distinct from 'malformed') on EACCES", () => {
// Contract: audit.ts does not collapse unreadable into malformed
// with a string prefix. Downstream switches on all 4 ParsedManifest
// variants and classifies unreadable manifests under the
// `unreadable` bucket (alongside spec/qa-dir I/O failures), not
// under `malformedManifest` (which is content-shape-only).
writePackage(root, "mypkg", {
manifest: "slug: mypkg\ndeployed: true\ndemos:\n - id: foo\n",
});
const target = path.join(root, "integrations", "mypkg", "manifest.yaml");
const orig = fs.readFileSync;
const spy = vi.spyOn(fs, "readFileSync").mockImplementation(((
p: fs.PathOrFileDescriptor,
options?: unknown,
) => {
if (typeof p === "string" && p === target) {
const e: NodeJS.ErrnoException = new Error("EACCES: permission denied");
e.code = "EACCES";
throw e;
}
return (
orig as unknown as (p: fs.PathOrFileDescriptor, o?: unknown) => unknown
)(p, options);
}) as typeof fs.readFileSync);
try {
const cfg = makeConfig(root);
const r = readManifest("mypkg", cfg);
expect(r.kind).toBe("unreadable");
if (r.kind === "unreadable") {
expect(r.error).toContain("EACCES");
}
// And buildReport routes it under the `unreadable` bucket, NOT
// `malformedManifest`.
const report = buildReport(["mypkg"], cfg);
expect(report.anomalies.unreadable).toContain("mypkg");
expect(report.anomalies.malformedManifest).not.toContain("mypkg");
} finally {
spy.mockRestore();
}
});
});
describe("countFiles", () => {
let root: string;
beforeEach(() => {
root = makeTmpTree();
});
afterEach(() => {
fs.rmSync(root, { recursive: true, force: true });
});
it("returns { state: 'missing' } when dir does not exist", () => {
const r = countFiles(path.join(root, "does-not-exist"), (n) =>
n.endsWith(".spec.ts"),
);
expect(r.state).toBe("missing");
});
it("returns { state: 'ok', count: N } for readable dir", () => {
const d = path.join(root, "some");
fs.mkdirSync(d);
fs.writeFileSync(path.join(d, "a.spec.ts"), "");
fs.writeFileSync(path.join(d, "b.spec.ts"), "");
fs.writeFileSync(path.join(d, "c.md"), "");
const r = countFiles(d, (n) => n.endsWith(".spec.ts"));
expect(r.state).toBe("ok");
if (r.state === "ok") {
expect(r.count).toBe(2);
}
});
it("surfaces { state: 'unreadable' } instead of silent 0 on readdirSync failure", () => {
const d = path.join(root, "unreadable");
fs.mkdirSync(d);
// Mock readdirSync to throw EACCES for this specific path.
const orig = fs.readdirSync;
const spy = vi.spyOn(fs, "readdirSync").mockImplementation(((
p: fs.PathLike,
options?: unknown,
) => {
if (typeof p === "string" && p === d) {
const e: NodeJS.ErrnoException = new Error("EACCES: permission denied");
e.code = "EACCES";
throw e;
}
return (orig as unknown as (p: fs.PathLike, o?: unknown) => unknown)(
p,
options,
);
}) as typeof fs.readdirSync);
try {
const r = countFiles(d, (n) => n.endsWith(".spec.ts"));
expect(r.state).toBe("unreadable");
if (r.state === "unreadable") {
expect(r.error).toContain("EACCES");
}
} finally {
spy.mockRestore();
}
});
it("surfaces { state: 'unreadable' } (not 'missing') when statSync throws EACCES", () => {
// Regression guard: the old implementation gated on `fs.existsSync`,
// which returns false for EACCES/EPERM/EIO/ELOOP/ENOTDIR just like it
// does for ENOENT. That silently classified unreadable dirs as
// `missing` (legitimate zero) and triggered phantom count-mismatch
// anomalies. The fix replaces existsSync with a statSync + errno
// branch so non-ENOENT stat failures surface as `unreadable`.
const d = path.join(root, "eacces-stat");
fs.mkdirSync(d);
const orig = fs.statSync;
const spy = vi.spyOn(fs, "statSync").mockImplementation(((
p: fs.PathLike,
options?: unknown,
) => {
if (typeof p === "string" && p === d) {
const e: NodeJS.ErrnoException = new Error(
"EACCES: permission denied (statSync)",
);
e.code = "EACCES";
throw e;
}
return (orig as unknown as (p: fs.PathLike, o?: unknown) => unknown)(
p,
options,
);
}) as typeof fs.statSync);
try {
const r = countFiles(d, (n) => n.endsWith(".spec.ts"));
// MUST NOT collapse to `missing` — operator needs the unreadable
// signal so downstream auditPackage emits `unreadable-dir` instead
// of a phantom count-mismatch on a zero count.
expect(r.state).toBe("unreadable");
if (r.state === "unreadable") {
expect(r.error).toContain("EACCES");
}
} finally {
spy.mockRestore();
}
});
});
describe("auditPackage — EACCES on spec dir stat → unreadable-dir anomaly (not count-mismatch)", () => {
let root: string;
beforeEach(() => {
root = makeTmpTree();
});
afterEach(() => {
fs.rmSync(root, { recursive: true, force: true });
});
it("EACCES on statSync of tests/e2e → unreadable-dir anomaly, no phantom count-mismatch", () => {
// End-to-end: when the e2e dir's stat fails with EACCES, the package
// must surface an `unreadable-dir` anomaly — NOT a `count-mismatch`
// claiming 0 specs against declared demos. Previously the existsSync
// gate in countFiles collapsed EACCES to `missing` (count=0), which
// then compared unequal against demos and fired a phantom mismatch.
writePackage(root, "eacces-e2e", {
manifest: `slug: eacces-e2e\ndeployed: true\ndemos:\n - id: a\n`,
specs: ["a.spec.ts"],
qaFiles: ["a.md"],
});
const e2eDir = path.join(
root,
"integrations",
"eacces-e2e",
"tests",
"e2e",
);
const orig = fs.statSync;
const spy = vi.spyOn(fs, "statSync").mockImplementation(((
p: fs.PathLike,
options?: unknown,
) => {
if (typeof p === "string" && p === e2eDir) {
const e: NodeJS.ErrnoException = new Error("EACCES: injected");
e.code = "EACCES";
throw e;
}
return (orig as unknown as (p: fs.PathLike, o?: unknown) => unknown)(
p,
options,
);
}) as typeof fs.statSync);
try {
const cfg = makeConfig(root);
const a = auditPackage("eacces-e2e", cfg);
// unreadable-dir anomaly MUST surface with an error containing EACCES.
const unreadable = a.anomalies.find(
(x): x is Extract<Anomaly, { kind: "unreadable-dir" }> =>
x.kind === "unreadable-dir",
);
expect(
unreadable,
`expected unreadable-dir anomaly, got: ${JSON.stringify(a.anomalies)}`,
).toBeDefined();
if (unreadable) {
expect(unreadable.error).toContain("EACCES");
}
// MUST NOT fire a phantom spec count-mismatch (unreadable !== zero).
const specMismatch = a.anomalies.find(
(x): x is Extract<Anomaly, { kind: "count-mismatch" }> =>
x.kind === "count-mismatch" && x.dimension === "spec",
);
expect(specMismatch).toBeUndefined();
// spec CountState must be "unreadable" (not "missing").
expect(a.spec.state).toBe("unreadable");
} finally {
spy.mockRestore();
}
});
});
describe("findExamplesSource", () => {
let root: string;
beforeEach(() => {
root = makeTmpTree();
});
afterEach(() => {
fs.rmSync(root, { recursive: true, force: true });
});
it("returns null source when no candidate directory exists", () => {
const cfg = makeConfig(root);
const r = findExamplesSource("does-not-exist", cfg);
expect(r.source).toBeNull();
expect(r.unreadableForSlug).toBe(false);
});
it("returns relative path when a candidate dir exists", () => {
makeExampleDir(root, "crewai-crews");
const cfg = makeConfig(root);
const r = findExamplesSource("crewai-crews", cfg);
expect(r.source).toBe(
path.join("examples", "integrations", "crewai-crews"),
);
expect(r.unreadableForSlug).toBe(false);
});
it("does not crash if statSync throws — treats error as not-found", () => {
makeExampleDir(root, "crewai-crews");
const target = path.join(root, "examples", "integrations", "crewai-crews");
const orig = fs.statSync;
const spy = vi.spyOn(fs, "statSync").mockImplementation(((
p: fs.PathLike,
options?: unknown,
) => {
if (typeof p === "string" && p === target) {
const e: NodeJS.ErrnoException = new Error("EIO: race condition");
e.code = "EIO";
throw e;
}
return (orig as unknown as (p: fs.PathLike, o?: unknown) => unknown)(
p,
options,
);
}) as typeof fs.statSync);
try {
const cfg = makeConfig(root);
const r = findExamplesSource("crewai-crews", cfg);
expect(r.source).toBeNull();
} finally {
spy.mockRestore();
}
});
it("records stale-mapping warnings on the supplied sink array (no stderr monkey-patch)", () => {
// Regression guard for the old global-state approach: findExamplesSource
// used to monkey-patch process.stderr.write and restore it in a
// finally block. Concurrent calls would collide, and any caller that
// captured stderr (e.g. vitest's stderr spy) saw nothing because
// the monkey-patch sandwich restored the original. The new contract:
// pass an explicit string[] sink; warnings are appended to it, and
// the function never touches process.stderr.
const mappedSlug = "mastra";
expect(SLUG_TO_EXAMPLES[mappedSlug]).toBeDefined();
const cfg = makeConfig(root);
const sink: string[] = [];
const r = findExamplesSource(mappedSlug, cfg, sink);
expect(r.source).toBeNull();
expect(r.unreadableForSlug).toBe(false);
// Sink received the stale-mapping warning verbatim.
expect(sink.length).toBeGreaterThan(0);
expect(sink.some((w) => w.includes(mappedSlug))).toBe(true);
expect(sink.some((w) => /warn/i.test(w))).toBe(true);
});
it("does not push to the sink when the slug is unmapped (fallback path)", () => {
// Unmapped slug falls back to [slug] — that's the "no mapping
// needed" path, not a dead entry, so no warning.
const cfg = makeConfig(root);
const sink: string[] = [];
const r = findExamplesSource("totally-unmapped-slug", cfg, sink);
expect(r.source).toBeNull();
expect(sink).toEqual([]);
});
it("statSync failures land on the sink (not stderr)", () => {
makeExampleDir(root, "crewai-crews");
const target = path.join(root, "examples", "integrations", "crewai-crews");
const orig = fs.statSync;
const spy = vi.spyOn(fs, "statSync").mockImplementation(((
p: fs.PathLike,
options?: unknown,
) => {
if (typeof p === "string" && p === target) {
const e: NodeJS.ErrnoException = new Error("EIO: injected");
e.code = "EIO";
throw e;
}
return (orig as unknown as (p: fs.PathLike, o?: unknown) => unknown)(
p,
options,
);
}) as typeof fs.statSync);
try {
const cfg = makeConfig(root);
const sink: string[] = [];
const r = findExamplesSource("crewai-crews", cfg, sink);
expect(r.source).toBeNull();
expect(sink.some((w) => w.includes("statSync"))).toBe(true);
expect(sink.some((w) => w.includes("EIO"))).toBe(true);
} finally {
spy.mockRestore();
}
});
it("returns the first declared candidate when only the first exists", () => {
const slug = "langgraph-typescript";
const mapped = SLUG_TO_EXAMPLES[slug];
expect(mapped).toBeDefined();
expect(mapped!.length).toBeGreaterThan(0);
const first = mapped![0];
makeExampleDir(root, first);
const cfg = makeConfig(root);
const r = findExamplesSource(slug, cfg);
expect(r.source).toBe(path.join("examples", "integrations", first));
expect(r.unreadableForSlug).toBe(false);
});
it("falls back to a later candidate when only that later candidate exists", () => {
// Exercise the multi-candidate fallback path via resolveExamplesSource,
// which accepts an explicit `mapped` tuple. All live SLUG_TO_EXAMPLES
// entries are single-candidate today, so using findExamplesSource
// would short-circuit the fallback loop and leave this codepath
// uncovered. Synthetic multi-candidate input makes the assertion
// meaningful regardless of the production map's shape.
const slug = "synthetic-multi";
const mapped = ["missing-first", "real-later"] as const;
makeExampleDir(root, "real-later");
const cfg = makeConfig(root);
const r = resolveExamplesSource(slug, mapped, cfg);
expect(r.source).toBe(path.join("examples", "integrations", "real-later"));
expect(r.unreadableForSlug).toBe(false);
});
it("warns and returns null when an unmapped slug's candidate path exists but is a regular file", () => {
// An unmapped slug whose candidate path resolves to a regular file
// (stray file in examples/integrations, or a name collision) must
// warn rather than return null silently — operators need a signal
// that a seemingly-present path was skipped. The warning wording
// ("exists but is not a directory") is distinct from the
// mapped-entry "no matching directory" warning so the two
// misconfiguration modes stay disambiguable.
const slug = "unmapped-file-slug";
// Write a regular file at examples/integrations/<slug> (no mkdir).
const full = path.join(root, "examples", "integrations", slug);
fs.writeFileSync(full, "not a directory\n");
const cfg = makeConfig(root);
const sink: string[] = [];
// mapped = undefined exercises the unmapped-slug branch.
const r = resolveExamplesSource(slug, undefined, cfg, sink);
expect(r.source).toBeNull();
expect(r.unreadableForSlug).toBe(false);
expect(
sink.some(
(w) => w.includes("exists but is not a directory") && w.includes(full),
),
`expected file-not-dir warning for ${full} in sink: ${JSON.stringify(
sink,
)}`,
).toBe(true);
// Must NOT fire the mapped-entry "no matching directory" warning —
// the fallback path is explicitly not a mapping.
expect(sink.some((w) => w.includes("SLUG_TO_EXAMPLES entry"))).toBe(false);
});
it("EACCES on existsSync does NOT silently skip mapped candidate (routes to unreadableForSlug)", () => {
// Regression guard: the old implementation used `fs.existsSync` to
// pre-filter candidates. existsSync returns false for EACCES just
// like it does for ENOENT, so a candidate whose parent dir is
// unreadable was silently skipped — not counted in `existedCount`,
// not counted in `unreadableCount`. When ALL mapped candidates were
// EACCES'd, the resolver returned `unreadableForSlug: false` and the
// package silently fell through to `missing-examples`.
//
// Fix: replace existsSync with a statSync inside try/catch. ENOENT →
// continue (absent); EACCES/other errno → increment unreadableCount
// and push diagnostic. Result: unreadableForSlug is truthy when every
// mapped candidate is EACCES'd.
const slug = "synthetic-eacces";
const mapped = ["only-cand"] as const;
// Do NOT create the candidate path on disk. Mock existsSync so the
// old code path would see false (as if ENOENT), while statSync is
// configured to throw EACCES for the candidate path — which is the
// signal the fixed code must use.
const full = path.join(root, "examples", "integrations", "only-cand");
const origExists = fs.existsSync;
const existsSpy = vi.spyOn(fs, "existsSync").mockImplementation(((
p: fs.PathLike,
) => {
if (typeof p === "string" && p === full) {
// Simulate existsSync hiding EACCES as false — the very bug we
// are guarding against.
return false;
}
return (origExists as (p: fs.PathLike) => boolean)(p);
}) as typeof fs.existsSync);
const origStat = fs.statSync;
const statSpy = vi.spyOn(fs, "statSync").mockImplementation(((
p: fs.PathLike,
options?: unknown,
) => {
if (typeof p === "string" && p === full) {
const e: NodeJS.ErrnoException = new Error("EACCES: parent unreadable");
e.code = "EACCES";
throw e;
}
return (origStat as unknown as (p: fs.PathLike, o?: unknown) => unknown)(
p,
options,
);
}) as typeof fs.statSync);
try {
const cfg = makeConfig(root);
const sink: string[] = [];
const r = resolveExamplesSource(slug, mapped, cfg, sink);
// MUST classify as unreadableForSlug — the operator needs the
// infrastructure-failure signal instead of the benign
// missing-examples classification.
expect(r.source).toBeNull();
expect(r.unreadableForSlug).toBe(true);
// A diagnostic must land on the sink so the EACCES is not silent.
expect(sink.some((w) => w.includes("EACCES"))).toBe(true);
} finally {
existsSpy.mockRestore();
statSpy.mockRestore();
}
});
it("returns the first candidate when both first and later candidates exist (first wins)", () => {
// Declared order decides — the first present candidate wins
// regardless of filesystem enumeration order. Uses synthetic
// multi-candidate mapping for the same reason as the fallback test
// above: the live map is single-candidate everywhere.
const slug = "synthetic-multi";
const mapped = ["first-dir", "later-dir"] as const;
makeExampleDir(root, "first-dir");
makeExampleDir(root, "later-dir");
const cfg = makeConfig(root);
const r = resolveExamplesSource(slug, mapped, cfg);
expect(r.source).toBe(path.join("examples", "integrations", "first-dir"));
expect(r.unreadableForSlug).toBe(false);
});
});
describe("parseArgs", () => {
it("treats --slug as requiring a non-flag argument (not --json)", () => {
const r = parseArgs(["--slug", "--json"]);
expect(r.slug).toBeNull();
expect(r.json).toBe(true);
expect(r.errors.some((e) => e.includes("--slug"))).toBe(true);
});
it("treats --slug followed by a flag as missing value", () => {
const r = parseArgs(["--slug"]);
expect(r.slug).toBeNull();
expect(r.errors.some((e) => e.includes("--slug"))).toBe(true);
});
it("parses --slug <value> correctly", () => {
const r = parseArgs(["--slug", "mastra"]);
expect(r.slug).toBe("mastra");
expect(r.errors).toEqual([]);
});
it("parses --json and --slug together", () => {
const r = parseArgs(["--json", "--slug", "mastra"]);
expect(r.slug).toBe("mastra");
expect(r.json).toBe(true);
expect(r.errors).toEqual([]);
});
it("rejects duplicate --json rather than silently accepting last-wins", () => {
const r = parseArgs(["--json", "--json"]);
expect(r.json).toBe(true);
expect(r.errors.some((e) => /--json/.test(e))).toBe(true);
});
it("rejects duplicate --slug rather than silently taking last-wins", () => {
const r = parseArgs(["--slug", "a", "--slug", "b"]);
expect(r.errors.some((e) => /--slug/.test(e))).toBe(true);
// Error message should mention both candidate values so the user can
// tell which occurrences collided.
expect(r.errors.some((e) => e.includes("a") && e.includes("b"))).toBe(true);
});
});
describe("BORN_IN_SHOWCASE set", () => {
it("contains the 5 known born-in-showcase slugs", () => {
expect(BORN_IN_SHOWCASE.has("ag2")).toBe(true);
expect(BORN_IN_SHOWCASE.has("claude-sdk-python")).toBe(true);
expect(BORN_IN_SHOWCASE.has("claude-sdk-typescript")).toBe(true);
expect(BORN_IN_SHOWCASE.has("langroid")).toBe(true);
expect(BORN_IN_SHOWCASE.has("spring-ai")).toBe(true);
});
it("every BORN_IN_SHOWCASE slug has no counterpart directory under examples/integrations/ (fixture-synthesized invariant)", () => {
// Fixture-based version of the real-repo invariant: synthesize a
// tmpdir that mimics examples/integrations/ containing only the
// slugs that SHOULD be there (i.e. nothing from BORN_IN_SHOWCASE).
// This always runs in CI — no `if (!fs.existsSync) return;` bail.
//
// The invariant asserted: given a clean examples/integrations/
// tree that contains no BORN_IN_SHOWCASE slugs, findExamplesSource
// must return null for every BORN_IN_SHOWCASE member. If someone
// later adds a BORN_IN_SHOWCASE slug to SLUG_TO_EXAMPLES by mistake
// this test will fail via the second assertion.
const tmp = makeTmpTree();
try {
// Seed a handful of non-born slugs so the examples dir isn't empty.
makeExampleDir(tmp, "mastra");
makeExampleDir(tmp, "agno");
const cfg = makeConfig(tmp);
for (const slug of BORN_IN_SHOWCASE) {
// No dir created for this slug — findExamplesSource must return
// a null source and SLUG_TO_EXAMPLES must not carry a mapping
// for it.
const r = findExamplesSource(slug, cfg);
expect(
r.source,
`BORN_IN_SHOWCASE slug "${slug}" resolved to a non-null examples source — the maps are inconsistent`,
).toBeNull();
expect(
SLUG_TO_EXAMPLES[slug],
`BORN_IN_SHOWCASE slug "${slug}" appears in SLUG_TO_EXAMPLES — remove one or the other`,
).toBeUndefined();
}
} finally {
fs.rmSync(tmp, { recursive: true, force: true });
}
});
});
describe("SLUG_TO_EXAMPLES — dead entries removed", () => {
// These showcase slugs do not exist under showcase/integrations/. Keeping
// them in the map produced phantom "no examples source" anomalies for
// nothing (and made the table/doc noise confusing). Each is a
// regression guard: if you add any of these back, you must create the
// corresponding showcase/integrations/<slug>/ dir too.
it.each(["crewai-flows", "agent-spec-langgraph", "mcp-apps"])(
"does not include dead entry %s",
(slug) => {
expect(SLUG_TO_EXAMPLES[slug]).toBeUndefined();
},
);
it("every mapped target resolves when its examples/integrations/ dir is present (fixture-synthesized)", () => {
// Fixture-based dead-entry guard: synthesize an examples/integrations/
// tmpdir containing every target named in SLUG_TO_EXAMPLES, then
// assert findExamplesSource returns a non-null path for each slug.
// This exercises the positive resolution path deterministically and
// always runs in CI — no `if (!fs.existsSync) return;` bail.
//
// Dead-entry protection: if anyone adds a SLUG_TO_EXAMPLES entry
// pointing at a non-existent target and the real repo lacks that
// dir, audit would emit a phantom "no examples source" anomaly.
// The slug-map.test.ts real-repo invariant (separate file) still
// enforces the production tree; this test locks in the runtime
// resolution contract.
const tmp = makeTmpTree();
try {
for (const targets of Object.values(SLUG_TO_EXAMPLES)) {
for (const target of targets) {
makeExampleDir(tmp, target);
}
}
const cfg = makeConfig(tmp);
for (const [slug, targets] of Object.entries(SLUG_TO_EXAMPLES)) {
const r = findExamplesSource(slug, cfg);
expect(
r.source,
`SLUG_TO_EXAMPLES[${slug}] → [${targets.join(
", ",
)}] failed to resolve against a fixture containing every target`,
).not.toBeNull();
}
} finally {
fs.rmSync(tmp, { recursive: true, force: true });
}
});
});
describe("findExamplesSource — sink-based warnings (no global stderr)", () => {
let root: string;
beforeEach(() => {
root = makeTmpTree();
});
afterEach(() => {
fs.rmSync(root, { recursive: true, force: true });
});
it("populates the caller-supplied sink when a mapped slug has no matching dir", () => {
// Use an explicit slug known to be in SLUG_TO_EXAMPLES rather than
// first-key indexing, which would silently keep passing if the map
// changed order.
const mappedSlug = "mastra";
expect(SLUG_TO_EXAMPLES[mappedSlug]).toBeDefined();
const cfg = makeConfig(root);
const sink: string[] = [];
const r = findExamplesSource(mappedSlug, cfg, sink);
expect(r.source).toBeNull();
const joined = sink.join("\n");
expect(joined).toMatch(/warn/i);
expect(joined).toContain(mappedSlug);
});
it("does NOT populate the sink for an unmapped slug missing a dir (falls back to slug==dirname)", () => {
// A slug not in SLUG_TO_EXAMPLES falls back to [slug]. That's the
// "no mapping" case — not a dead entry — so no warning.
const cfg = makeConfig(root);
const sink: string[] = [];
const r = findExamplesSource("totally-unmapped-slug", cfg, sink);
expect(r.source).toBeNull();
expect(sink).toEqual([]);
});
it("does NOT write to process.stderr at all (hard guarantee — no global state)", () => {
// Regression guard for the stderr-monkey-patch contract removal:
// findExamplesSource is a pure function with respect to
// stdout/stderr. Callers pass an explicit sink or accept that
// warnings are discarded.
const stderrWrites: string[] = [];
const spy = vi.spyOn(process.stderr, "write").mockImplementation(((
chunk: string | Uint8Array,
) => {
stderrWrites.push(
typeof chunk === "string" ? chunk : Buffer.from(chunk).toString(),
);
return true;
}) as typeof process.stderr.write);
try {
const mappedSlug = "mastra";
const cfg = makeConfig(root);
findExamplesSource(mappedSlug, cfg, []);
expect(stderrWrites).toEqual([]);
} finally {
spy.mockRestore();
}
});
});
describe("findExamplesSource — unreadable-candidates ERROR branch", () => {
// Coverage for audit.ts:429-434: when a mapped slug has multiple
// candidates and ALL of them exist but ALL statSync calls fail, the
// resolver must emit an ERROR warning (category:
// unreadable-candidates) and return null. This is materially
// different from the benign "no matching dir" warning because we
// can't actually tell whether the provenance is satisfied — the
// downstream consumer needs the ERROR level to route differently.
let root: string;
beforeEach(() => {
root = makeTmpTree();
});
afterEach(() => {
fs.rmSync(root, { recursive: true, force: true });
});
it("emits an ERROR (category: unreadable-candidates) when every mapped candidate exists but statSync fails on all of them", () => {
const slug = "synthetic-unreadable";
const mapped = ["cand-a", "cand-b"] as const;
makeExampleDir(root, "cand-a");
makeExampleDir(root, "cand-b");
const dirA = path.join(root, "examples", "integrations", "cand-a");
const dirB = path.join(root, "examples", "integrations", "cand-b");
const orig = fs.statSync;
const spy = vi.spyOn(fs, "statSync").mockImplementation(((
p: fs.PathLike,
options?: unknown,
) => {
if (typeof p === "string" && (p === dirA || p === dirB)) {
const e: NodeJS.ErrnoException = new Error("EACCES: injected");
e.code = "EACCES";
throw e;
}
return (orig as unknown as (p: fs.PathLike, o?: unknown) => unknown)(
p,
options,
);
}) as typeof fs.statSync);
try {
const cfg = makeConfig(root);
const sink: string[] = [];
const r = resolveExamplesSource(slug, mapped, cfg, sink);
expect(r.source).toBeNull();
// Structured classification signal: "all candidates unreadable"
// must surface as unreadableForSlug=true without relying on the
// human-readable warning wording.
expect(r.unreadableForSlug).toBe(true);
// The ERROR warning must be present, tagged with the category,
// and must name the slug so downstream consumers can route it.
expect(
sink.some((w) => /ERROR/.test(w) && w.includes(slug)),
`expected an ERROR warning for slug "${slug}" in sink: ${JSON.stringify(
sink,
)}`,
).toBe(true);
expect(sink.some((w) => w.includes("unreadable-candidates"))).toBe(true);
// Every candidate should have shown up in a statSync warning too
// (companion diagnostic emitted from the loop body).
expect(
sink.some((w) => w.includes("statSync") && w.includes("cand-a")),
).toBe(true);
expect(
sink.some((w) => w.includes("statSync") && w.includes("cand-b")),
).toBe(true);
} finally {
spy.mockRestore();
}
});
});
describe("isProgrammerBug classification", () => {
// Coverage for audit.ts:1206-1222. The top-level catch in main()
// uses isProgrammerBug to decide between "broken invariant, worth a
// bug report" diagnostic wording and "infrastructure / I/O" wording.
// Both land on EXIT_INTERNAL but the operator-facing message diverges.
it("classifies a bare TypeError as a programmer bug", () => {
expect(isProgrammerBug(new TypeError("invariant broken"))).toBe(true);
});
it("classifies ReferenceError and RangeError as programmer bugs too", () => {
expect(isProgrammerBug(new ReferenceError("x is not defined"))).toBe(true);
expect(isProgrammerBug(new RangeError("Maximum call stack"))).toBe(true);
});
it("classifies a plain Error (no .code) as NOT a programmer bug", () => {
// A bare Error without `.code` is neither an errno nor one of the
// recognised programmer-bug subclasses — falls through the guard
// and returns false.
expect(isProgrammerBug(new Error("something else"))).toBe(false);
});
it("classifies an ErrnoException-shaped Error as NOT a programmer bug", () => {
// Any Error carrying a string `.code` is treated as runtime I/O —
// including the rare case where a TypeError also picks up a
// `.code` field, in which case the errno check wins.
const e: NodeJS.ErrnoException = new Error("EACCES: permission denied");
e.code = "EACCES";
expect(isProgrammerBug(e)).toBe(false);
});
it("classifies a TypeError carrying a string .code as NOT a programmer bug (errno wins)", () => {
// This is the "weird subclass drift" case called out in the
// comment at audit.ts:1197-1202 — an errno-bearing TypeError is
// biased toward runtime, not programmer.
const e = new TypeError("weird drift") as TypeError & { code?: string };
e.code = "EIO";
expect(isProgrammerBug(e)).toBe(false);
});
it("classifies non-Error values as NOT programmer bugs", () => {
// Non-Error throws (strings, numbers, plain objects) don't satisfy
// the `instanceof Error` guard and return false.
expect(isProgrammerBug("string thrown")).toBe(false);
expect(isProgrammerBug(42)).toBe(false);
expect(isProgrammerBug({ message: "plain" })).toBe(false);
expect(isProgrammerBug(undefined)).toBe(false);
expect(isProgrammerBug(null)).toBe(false);
});
});
describe("UnreadableDirError", () => {
it("carries the dir and a human-readable message", () => {
const cause = new Error("EACCES: permission denied");
const e = new UnreadableDirError("/tmp/packages", cause);
expect(e).toBeInstanceOf(Error);
expect(e.name).toBe("UnreadableDirError");
expect(e.dir).toBe("/tmp/packages");
expect(e.message).toMatch(/^could not read \/tmp\/packages: EACCES/);
// ES2022 cause chain preserves the original ErrnoException so
// callers can still reach `.code` / `.errno` / `.syscall`.
expect((e as Error & { cause?: unknown }).cause).toBe(cause);
});
it("is thrown by listShowcasePackageSlugs when readdirSync fails", () => {
// Verify the error is raised at the right control-flow point:
// listShowcasePackageSlugs catches fs failures and rethrows as
// UnreadableDirError carrying the exact packages dir it tried.
const root = fs.mkdtempSync(path.join(os.tmpdir(), "audit-unreadable-"));
try {
const cfg = {
packagesDir: path.join(root, "does-not-exist"),
examplesIntegrationsDir: path.join(root, "examples", "integrations"),
repoRoot: root,
};
let thrown: unknown = null;
try {
listShowcasePackageSlugs(cfg);
} catch (e) {
thrown = e;
}
expect(thrown).toBeInstanceOf(UnreadableDirError);
const e = thrown as UnreadableDirError;
expect(e.dir).toBe(cfg.packagesDir);
expect(e.message).toContain(cfg.packagesDir);
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
});
it("preserves the non-Error cause via String() coercion in the message", () => {
// Defensive: an fs layer could, in principle, reject with a
// non-Error value. UnreadableDirError still produces a readable
// message via String(cause).
const e = new UnreadableDirError("/tmp/x", "bespoke-failure-token");
expect(e.message).toBe("could not read /tmp/x: bespoke-failure-token");
});
it("includes the errno .code in the rendered message when missing from cause.message", () => {
// Some callers/tests construct Errors that attach .code but do not
// embed the code in .message. UnreadableDirError surfaces the code
// in its rendered message so operators see e.g. EACCES immediately.
const cause = Object.assign(new Error("permission denied"), {
code: "EACCES",
});
const e = new UnreadableDirError("/x", cause);
expect(e.message).toBe("could not read /x: EACCES: permission denied");
});
it("does not double-prepend errno code when already in cause.message", () => {
// Node's fs errors embed the code in .message (e.g.
// "EACCES: permission denied, scandir ..."). Avoid "EACCES: EACCES:".
const cause = Object.assign(
new Error("EACCES: permission denied, scandir '/x'"),
{ code: "EACCES" },
);
const e = new UnreadableDirError("/x", cause);
expect(e.message).toBe(
"could not read /x: EACCES: permission denied, scandir '/x'",
);
expect(e.message).not.toMatch(/EACCES: EACCES/);
});
});
describe("canonicalizeForIsMain", () => {
it("returns a canonical (realpath) path when the file exists", () => {
// Two distinct input strings that refer to the same underlying file
// (one absolute, one with a redundant `.` segment) must canonicalize
// to the same result. This proves the helper normalizes inputs
// rather than merely passing them through — a stronger invariant
// than `canonicalize(f) === realpathSync(f)`, which would be
// trivially true for any pass-through implementation.
const root = fs.mkdtempSync(path.join(os.tmpdir(), "audit-canon-"));
try {
const f = path.join(root, "a.txt");
fs.writeFileSync(f, "x");
// Same file, two different string forms: plain absolute vs.
// absolute with a redundant `./` segment injected in the middle.
// Concatenate directly rather than via path.join (which would
// collapse `.` eagerly) so the inputs are genuinely distinct at
// the string level before the helper sees them.
const plain = f;
const dotted = `${root}${path.sep}.${path.sep}a.txt`;
expect(plain).not.toBe(dotted); // sanity: inputs are distinct strings
const canonPlain = canonicalizeForIsMain(plain);
const canonDotted = canonicalizeForIsMain(dotted);
expect(canonPlain).toEqual(canonDotted);
expect(canonPlain).toEqual({ ok: true, path: fs.realpathSync(f) });