@quenty/nevermore-cli 4.41.0 → 4.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/commands/batch-command/batch-test-command.d.ts.map +1 -1
- package/dist/commands/batch-command/batch-test-command.js +20 -2
- package/dist/commands/batch-command/batch-test-command.js.map +1 -1
- package/dist/commands/test-command/test-command.d.ts.map +1 -1
- package/dist/commands/test-command/test-command.js +6 -4
- package/dist/commands/test-command/test-command.js.map +1 -1
- package/dist/utils/job-context/batch-script-job-context.d.ts.map +1 -1
- package/dist/utils/job-context/batch-script-job-context.js +5 -1
- package/dist/utils/job-context/batch-script-job-context.js.map +1 -1
- package/dist/utils/open-cloud/open-cloud-client.d.ts.map +1 -1
- package/dist/utils/open-cloud/open-cloud-client.js +23 -2
- package/dist/utils/open-cloud/open-cloud-client.js.map +1 -1
- package/dist/utils/testing/parsers/batch-log-parser.d.ts +24 -0
- package/dist/utils/testing/parsers/batch-log-parser.d.ts.map +1 -1
- package/dist/utils/testing/parsers/batch-log-parser.js +159 -40
- package/dist/utils/testing/parsers/batch-log-parser.js.map +1 -1
- package/dist/utils/testing/parsers/batch-log-parser.test.d.ts +2 -0
- package/dist/utils/testing/parsers/batch-log-parser.test.d.ts.map +1 -0
- package/dist/utils/testing/parsers/batch-log-parser.test.js +117 -0
- package/dist/utils/testing/parsers/batch-log-parser.test.js.map +1 -0
- package/dist/utils/testing/parsers/traceback-parser.d.ts +31 -0
- package/dist/utils/testing/parsers/traceback-parser.d.ts.map +1 -0
- package/dist/utils/testing/parsers/traceback-parser.js +91 -0
- package/dist/utils/testing/parsers/traceback-parser.js.map +1 -0
- package/dist/utils/testing/parsers/traceback-parser.test.d.ts +2 -0
- package/dist/utils/testing/parsers/traceback-parser.test.d.ts.map +1 -0
- package/dist/utils/testing/parsers/traceback-parser.test.js +45 -0
- package/dist/utils/testing/parsers/traceback-parser.test.js.map +1 -0
- package/dist/utils/testing/reporting/test-github-columns.d.ts.map +1 -1
- package/dist/utils/testing/reporting/test-github-columns.js +1 -0
- package/dist/utils/testing/reporting/test-github-columns.js.map +1 -1
- package/dist/utils/testing/runner/test-runner.d.ts +10 -0
- package/dist/utils/testing/runner/test-runner.d.ts.map +1 -1
- package/dist/utils/testing/runner/test-runner.js +28 -2
- package/dist/utils/testing/runner/test-runner.js.map +1 -1
- package/dist/utils/testing/runner/test-runner.test.d.ts +2 -0
- package/dist/utils/testing/runner/test-runner.test.d.ts.map +1 -0
- package/dist/utils/testing/runner/test-runner.test.js +18 -0
- package/dist/utils/testing/runner/test-runner.test.js.map +1 -0
- package/dist/utils/testing/test-log-parser.d.ts +32 -1
- package/dist/utils/testing/test-log-parser.d.ts.map +1 -1
- package/dist/utils/testing/test-log-parser.js +53 -9
- package/dist/utils/testing/test-log-parser.js.map +1 -1
- package/dist/utils/testing/test-log-parser.test.d.ts +2 -0
- package/dist/utils/testing/test-log-parser.test.d.ts.map +1 -0
- package/dist/utils/testing/test-log-parser.test.js +56 -0
- package/dist/utils/testing/test-log-parser.test.js.map +1 -0
- package/package.json +6 -6
- package/src/commands/batch-command/batch-test-command.ts +80 -58
- package/src/commands/test-command/test-command.ts +7 -4
- package/src/utils/job-context/batch-script-job-context.ts +5 -1
- package/src/utils/open-cloud/open-cloud-client.ts +28 -2
- package/src/utils/testing/parsers/batch-log-parser.test.ts +150 -0
- package/src/utils/testing/parsers/batch-log-parser.ts +201 -49
- package/src/utils/testing/parsers/traceback-parser.test.ts +64 -0
- package/src/utils/testing/parsers/traceback-parser.ts +122 -0
- package/src/utils/testing/reporting/test-github-columns.ts +1 -0
- package/src/utils/testing/runner/test-runner.test.ts +29 -0
- package/src/utils/testing/runner/test-runner.ts +37 -2
- package/src/utils/testing/test-log-parser.test.ts +70 -0
- package/src/utils/testing/test-log-parser.ts +80 -10
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { OutputHelper } from '@quenty/cli-output-helpers';
|
|
2
|
-
import { parseTestCounts } from '../test-log-parser.js';
|
|
2
|
+
import { evaluateTestOutcome, parseTestCounts, } from '../test-log-parser.js';
|
|
3
|
+
import { formatTracebacks, parseTracebacks } from './traceback-parser.js';
|
|
3
4
|
const BEGIN_MARKER = '===BATCH_TEST_BEGIN ';
|
|
4
5
|
const END_MARKER = '===BATCH_TEST_END ';
|
|
5
6
|
const SUMMARY_MARKER = '===BATCH_TEST_SUMMARY===';
|
|
@@ -27,10 +28,17 @@ export function parseBatchTestLogs(rawLogs, slugMap) {
|
|
|
27
28
|
}
|
|
28
29
|
// ── Pass 1: Extract per-package log sections from markers ──
|
|
29
30
|
const logSections = new Map();
|
|
31
|
+
/** Sections closed by an END whose BEGIN never arrived — start of output lost. */
|
|
32
|
+
const partialSections = new Set();
|
|
30
33
|
const markerDurations = new Map();
|
|
31
34
|
let currentSlug = null;
|
|
32
35
|
let currentLines = [];
|
|
33
36
|
let summaryLineIndex = -1;
|
|
37
|
+
let beginMarkersSeen = 0;
|
|
38
|
+
let endMarkersSeen = 0;
|
|
39
|
+
let strayEndMarkers = 0;
|
|
40
|
+
/** Only one section can have lost its head: the one the log was cut inside. */
|
|
41
|
+
let headSectionClaimed = false;
|
|
34
42
|
// Matches "<slug> PASS|FAIL [<durationMs>]" — slugs have no whitespace.
|
|
35
43
|
const endInnerPattern = /^(\S+)\s+(?:PASS|FAIL)(?:\s+(\d+))?$/;
|
|
36
44
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -38,43 +46,66 @@ export function parseBatchTestLogs(rawLogs, slugMap) {
|
|
|
38
46
|
if (trimmed.startsWith(BEGIN_MARKER) && trimmed.endsWith('===')) {
|
|
39
47
|
currentSlug = trimmed.slice(BEGIN_MARKER.length, -3);
|
|
40
48
|
currentLines = [];
|
|
49
|
+
beginMarkersSeen++;
|
|
41
50
|
continue;
|
|
42
51
|
}
|
|
43
52
|
if (trimmed.startsWith(END_MARKER) && trimmed.endsWith('===')) {
|
|
44
53
|
const inner = trimmed.slice(END_MARKER.length, -3);
|
|
54
|
+
endMarkersSeen++;
|
|
45
55
|
const match = endInnerPattern.exec(inner);
|
|
46
56
|
const endSlug = match ? match[1] : inner;
|
|
47
57
|
const durationStr = match?.[2];
|
|
48
|
-
|
|
49
|
-
|
|
58
|
+
// A section normally closes on the END matching its own BEGIN.
|
|
59
|
+
const closesOwnSection = endSlug === currentSlug;
|
|
60
|
+
// Open Cloud keeps only the tail of a long run's log, so BEGIN — printed
|
|
61
|
+
// first — can be lost while END and the summary survive. Recovering that
|
|
62
|
+
// means letting an END with no open section claim what precedes it, but
|
|
63
|
+
// only where the head can actually have been dropped: before any BEGIN
|
|
64
|
+
// has survived, and only once. Past that point the log is well-formed,
|
|
65
|
+
// and a BEGIN-less END is a message delivered out of order (the API does
|
|
66
|
+
// not order them), which must not be allowed to claim another package's
|
|
67
|
+
// output.
|
|
68
|
+
const closesDroppedHead = currentSlug === null && beginMarkersSeen === 0 && !headSectionClaimed;
|
|
69
|
+
if (closesOwnSection || closesDroppedHead) {
|
|
70
|
+
logSections.set(endSlug, currentLines.join('\n'));
|
|
71
|
+
if (closesDroppedHead) {
|
|
72
|
+
partialSections.add(endSlug);
|
|
73
|
+
headSectionClaimed = true;
|
|
74
|
+
}
|
|
50
75
|
if (durationStr !== undefined) {
|
|
51
|
-
markerDurations.set(
|
|
76
|
+
markerDurations.set(endSlug, parseInt(durationStr, 10));
|
|
52
77
|
}
|
|
53
78
|
currentSlug = null;
|
|
54
79
|
currentLines = [];
|
|
55
80
|
}
|
|
56
|
-
|
|
57
|
-
|
|
81
|
+
else {
|
|
82
|
+
// Reordered marker from another package. Ignore it without resetting
|
|
83
|
+
// state, so the section it interrupted still closes on its own END.
|
|
84
|
+
strayEndMarkers++;
|
|
85
|
+
}
|
|
58
86
|
continue;
|
|
59
87
|
}
|
|
60
88
|
if (trimmed === SUMMARY_MARKER) {
|
|
61
89
|
summaryLineIndex = i;
|
|
62
90
|
break;
|
|
63
91
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
92
|
+
// Accumulate unconditionally: output that precedes the first surviving
|
|
93
|
+
// BEGIN still belongs to whichever package's END closes it.
|
|
94
|
+
currentLines.push(lines[i]);
|
|
67
95
|
}
|
|
68
96
|
// ── Pass 2: Parse the JSON summary for authoritative pcall results ──
|
|
69
97
|
const summaryResults = new Map();
|
|
70
98
|
const summaryDurations = new Map();
|
|
71
99
|
if (summaryLineIndex >= 0 && summaryLineIndex + 1 < lines.length) {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
100
|
+
const entries = findSummaryEntries(lines, summaryLineIndex + 1);
|
|
101
|
+
if (entries === undefined) {
|
|
102
|
+
OutputHelper.verbose(`[batch-log-parser] Failed to parse JSON summary after line ${summaryLineIndex}: ` +
|
|
103
|
+
lines
|
|
104
|
+
.slice(summaryLineIndex + 1, summaryLineIndex + 3)
|
|
105
|
+
.join('\n')
|
|
106
|
+
.slice(0, 200));
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
78
109
|
for (const entry of entries) {
|
|
79
110
|
summaryResults.set(entry.slug, entry.success);
|
|
80
111
|
if (typeof entry.durationMs === 'number') {
|
|
@@ -86,38 +117,102 @@ export function parseBatchTestLogs(rawLogs, slugMap) {
|
|
|
86
117
|
if (failures.length > 0) {
|
|
87
118
|
console.error(`[batch-log-parser] Luau pcall failures: ${JSON.stringify(failures)}`);
|
|
88
119
|
}
|
|
89
|
-
console.error(`[batch-log-parser] Parsed ${entries.length} summary entries, ${failures.length} failures`);
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
OutputHelper.verbose(`[batch-log-parser] Failed to parse JSON summary: ${jsonLine.slice(0, 200)}`);
|
|
120
|
+
console.error(`[batch-log-parser] Parsed ${entries.length} summary entries, ${failures.length} pcall failures`);
|
|
93
121
|
}
|
|
94
122
|
}
|
|
95
123
|
// ── Warn when the batch produced no recognizable output ──
|
|
124
|
+
if (strayEndMarkers > 0) {
|
|
125
|
+
OutputHelper.verbose(`[batch-log-parser] Ignored ${strayEndMarkers} out-of-order END marker(s); ` +
|
|
126
|
+
'their sections closed on their own boundaries.');
|
|
127
|
+
}
|
|
96
128
|
const noOutputAtAll = logSections.size === 0 && summaryResults.size === 0;
|
|
97
129
|
if (noOutputAtAll) {
|
|
98
130
|
OutputHelper.warn(`[batch-log-parser] No batch markers or summary found in logs (${lines.length} lines, ${rawLogs.length} chars). ` +
|
|
99
131
|
'The batch script may not have started or produced output.');
|
|
100
132
|
}
|
|
133
|
+
// The summary is printed last, so it survives a log fetch that dropped the
|
|
134
|
+
// output above it. Summary present with no sections is exactly that case, and
|
|
135
|
+
// it is invisible to the check above — the results below would then be built
|
|
136
|
+
// from pcall exit status alone, knowing nothing about what actually ran.
|
|
137
|
+
const lostSectionOutput = logSections.size === 0 && summaryResults.size > 0;
|
|
138
|
+
if (lostSectionOutput) {
|
|
139
|
+
OutputHelper.warn(`[batch-log-parser] Received ${rawLogs.length} chars of output but could not ` +
|
|
140
|
+
`attribute any of it to a package section ` +
|
|
141
|
+
`(${beginMarkersSeen} BEGIN, ${endMarkersSeen} END markers; summary at line ` +
|
|
142
|
+
`${summaryLineIndex} of ${lines.length}). ` +
|
|
143
|
+
'Test counts are not derived from unattributed output.');
|
|
144
|
+
// BEGIN is printed first and the summary last, so their positions separate
|
|
145
|
+
// the two ways this happens: a log window that dropped the head keeps the
|
|
146
|
+
// summary at the end, while reordered messages put it early.
|
|
147
|
+
OutputHelper.warn(summaryLineIndex >= lines.length - 2
|
|
148
|
+
? '[batch-log-parser] Summary sits at the end of the log — consistent with the head being dropped by a log size/retention limit.'
|
|
149
|
+
: '[batch-log-parser] Summary sits early in the log — consistent with out-of-order messages ending the scan prematurely.');
|
|
150
|
+
}
|
|
101
151
|
// ── Pass 3: Combine log sections with summary results ──
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
|
|
152
|
+
// Output that arrived but could not be split into sections is still output.
|
|
153
|
+
// Reporting "(no output)" over it hides the very thing needed to diagnose the
|
|
154
|
+
// parse failure, so it is shown verbatim instead. Only safe to hand to a
|
|
155
|
+
// single package: with several, there is no way to say whose output this is.
|
|
156
|
+
const fallbackLogs = noOutputAtAll || lostSectionOutput ? rawLogs.trim() : '';
|
|
157
|
+
// The unattributed stream is the same text for every package, so it is
|
|
158
|
+
// attached once rather than repeated per package across a large log.
|
|
159
|
+
let unattributedClaimed = false;
|
|
105
160
|
for (const [packageName, slug] of slugMap) {
|
|
106
|
-
const
|
|
161
|
+
const attributedLogs = logSections.get(slug);
|
|
162
|
+
let sectionLogs = attributedLogs ?? '';
|
|
163
|
+
if (!attributedLogs && fallbackLogs && !unattributedClaimed) {
|
|
164
|
+
sectionLogs = fallbackLogs;
|
|
165
|
+
unattributedClaimed = true;
|
|
166
|
+
}
|
|
107
167
|
const summarySuccess = summaryResults.get(slug);
|
|
108
|
-
|
|
109
|
-
//
|
|
168
|
+
const reasons = [];
|
|
169
|
+
// The pcall result only proves the script did not throw. It is a floor, not
|
|
170
|
+
// a verdict — everything below can fail a run it called successful.
|
|
110
171
|
let success = summarySuccess ?? false;
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
172
|
+
if (summarySuccess === false) {
|
|
173
|
+
reasons.push('the batch runner reported a Luau error');
|
|
174
|
+
}
|
|
175
|
+
else if (summarySuccess === undefined) {
|
|
176
|
+
// Absent from the summary is a different fault from failing in it, and
|
|
177
|
+
// conflating them sends you hunting for a Luau error that never happened.
|
|
178
|
+
reasons.push('this package is missing from the batch summary');
|
|
179
|
+
}
|
|
180
|
+
if (attributedLogs !== undefined) {
|
|
181
|
+
const outcome = evaluateTestOutcome(attributedLogs, {
|
|
182
|
+
requireTestReport: true,
|
|
183
|
+
});
|
|
184
|
+
if (!outcome.success) {
|
|
114
185
|
success = false;
|
|
186
|
+
reasons.push(...outcome.failureReasons);
|
|
115
187
|
}
|
|
116
188
|
}
|
|
117
|
-
|
|
118
|
-
|
|
189
|
+
else {
|
|
190
|
+
// Judged unreadable rather than judged by content: whatever broke
|
|
191
|
+
// attribution is reason enough not to trust which package a line is from.
|
|
192
|
+
success = false;
|
|
193
|
+
reasons.push(`no output could be attributed to this package ` +
|
|
194
|
+
`(${rawLogs.length} chars received, ${beginMarkersSeen} BEGIN markers found)`);
|
|
195
|
+
}
|
|
196
|
+
if (partialSections.has(slug)) {
|
|
197
|
+
OutputHelper.warn(`[batch-log-parser] ${slug}: section closed by its END marker with no BEGIN — ` +
|
|
198
|
+
'the start of this output was dropped by the log window, so the section is partial.');
|
|
119
199
|
}
|
|
120
|
-
|
|
200
|
+
// Attribute by script path, not by log position: a deferred callback can
|
|
201
|
+
// fire during another package's section.
|
|
202
|
+
const tracebacks = parseTracebacks(sectionLogs);
|
|
203
|
+
const tracebackCount = tracebacks.reduce((sum, t) => sum + t.count, 0);
|
|
204
|
+
const owned = tracebacks.filter((t) => !t.owner || t.owner === slug);
|
|
205
|
+
if (tracebacks.length > owned.length) {
|
|
206
|
+
OutputHelper.verbose(`[batch-log-parser] ${slug}: ignored ${tracebacks.length - owned.length} traceback(s) belonging to other packages`);
|
|
207
|
+
}
|
|
208
|
+
if (owned.length > 0) {
|
|
209
|
+
OutputHelper.warn(`[batch-log-parser] ${slug}: ${owned.length} distinct traceback(s):\n` +
|
|
210
|
+
formatTracebacks(owned));
|
|
211
|
+
}
|
|
212
|
+
const error = reasons.length > 0 ? reasons.join('; ') : undefined;
|
|
213
|
+
const testCounts = attributedLogs
|
|
214
|
+
? parseTestCounts(attributedLogs)
|
|
215
|
+
: undefined;
|
|
121
216
|
// Prefer the JSON summary (authoritative, immune to log reordering);
|
|
122
217
|
// fall back to the END-marker value if the summary was truncated.
|
|
123
218
|
const durationMs = summaryDurations.get(slug) ?? markerDurations.get(slug);
|
|
@@ -127,20 +222,44 @@ export function parseBatchTestLogs(rawLogs, slugMap) {
|
|
|
127
222
|
logs: sectionLogs,
|
|
128
223
|
durationMs,
|
|
129
224
|
testCounts,
|
|
225
|
+
tracebackCount,
|
|
226
|
+
error,
|
|
130
227
|
});
|
|
131
228
|
}
|
|
132
229
|
return results;
|
|
133
230
|
}
|
|
134
231
|
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
232
|
+
* Find the summary array in the lines following the summary marker.
|
|
233
|
+
*
|
|
234
|
+
* The marker is printed last, but the API does not deliver messages in order,
|
|
235
|
+
* so unrelated output can land after it. Treating everything past the marker as
|
|
236
|
+
* one JSON blob therefore fails intermittently — the same batch parses on one
|
|
237
|
+
* run and not the next. Scan for the array instead of assuming it is alone.
|
|
138
238
|
*/
|
|
139
|
-
function
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
239
|
+
export function findSummaryEntries(lines, startIndex) {
|
|
240
|
+
const MAX_SPAN = 50;
|
|
241
|
+
for (let i = startIndex; i < lines.length; i++) {
|
|
242
|
+
if (!lines[i].trim().startsWith('[')) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
// Normally one line, but allow the array to span a few in case the runner
|
|
246
|
+
// or the transport breaks it up.
|
|
247
|
+
for (let end = i; end < Math.min(lines.length, i + MAX_SPAN); end++) {
|
|
248
|
+
try {
|
|
249
|
+
const parsed = JSON.parse(lines
|
|
250
|
+
.slice(i, end + 1)
|
|
251
|
+
.join('\n')
|
|
252
|
+
.trim());
|
|
253
|
+
if (Array.isArray(parsed)) {
|
|
254
|
+
return parsed;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
// Incomplete so far — grow the window and try again.
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return undefined;
|
|
145
263
|
}
|
|
264
|
+
export { countTracebacks } from '../test-log-parser.js';
|
|
146
265
|
//# sourceMappingURL=batch-log-parser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batch-log-parser.js","sourceRoot":"","sources":["../../../../src/utils/testing/parsers/batch-log-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,
|
|
1
|
+
{"version":3,"file":"batch-log-parser.js","sourceRoot":"","sources":["../../../../src/utils/testing/parsers/batch-log-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch-log-parser.test.d.ts","sourceRoot":"","sources":["../../../../src/utils/testing/parsers/batch-log-parser.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { countTracebacks, findSummaryEntries, parseBatchTestLogs, } from './batch-log-parser.js';
|
|
3
|
+
const SLUG_MAP = new Map([['egghunt2026', 'egghunt2026']]);
|
|
4
|
+
function buildLogs(section) {
|
|
5
|
+
return [
|
|
6
|
+
'===BATCH_TEST_BEGIN egghunt2026===',
|
|
7
|
+
section,
|
|
8
|
+
'===BATCH_TEST_END egghunt2026 PASS 1000===',
|
|
9
|
+
'===BATCH_TEST_SUMMARY===',
|
|
10
|
+
'[{"slug":"egghunt2026","success":true,"durationMs":1000}]',
|
|
11
|
+
].join('\n');
|
|
12
|
+
}
|
|
13
|
+
describe('parseBatchTestLogs', () => {
|
|
14
|
+
it('reads test counts out of a package section', () => {
|
|
15
|
+
const logs = buildLogs('Tests: 275 passed, 275 total');
|
|
16
|
+
const result = parseBatchTestLogs(logs, SLUG_MAP).get('egghunt2026');
|
|
17
|
+
expect(result?.testCounts).toEqual({ passed: 275, failed: 0, total: 275 });
|
|
18
|
+
});
|
|
19
|
+
it('attributes a section closed by END when its BEGIN was dropped', () => {
|
|
20
|
+
// Open Cloud keeps only the tail of a long run's log, so BEGIN — printed
|
|
21
|
+
// first — is the first casualty while END and the summary survive.
|
|
22
|
+
const logs = [
|
|
23
|
+
' ✓ some test that survived (3 ms)',
|
|
24
|
+
'Tests: 275 passed, 275 total',
|
|
25
|
+
'===BATCH_TEST_END egghunt2026 PASS 1000===',
|
|
26
|
+
'===BATCH_TEST_SUMMARY===',
|
|
27
|
+
'[{"slug":"egghunt2026","success":true,"durationMs":1000}]',
|
|
28
|
+
].join('\n');
|
|
29
|
+
const result = parseBatchTestLogs(logs, SLUG_MAP).get('egghunt2026');
|
|
30
|
+
expect(result?.success).toBe(true);
|
|
31
|
+
expect(result?.testCounts).toEqual({ passed: 275, failed: 0, total: 275 });
|
|
32
|
+
});
|
|
33
|
+
it('does not let an out-of-order END steal another package’s output', () => {
|
|
34
|
+
// The API does not deliver messages in order, so a stray END can arrive
|
|
35
|
+
// mid-section. Only the first section can have lost its head; past that the
|
|
36
|
+
// log is well-formed and a BEGIN-less END must not claim anything.
|
|
37
|
+
const twoPackages = new Map([
|
|
38
|
+
['alpha', 'alpha'],
|
|
39
|
+
['beta', 'beta'],
|
|
40
|
+
]);
|
|
41
|
+
const logs = [
|
|
42
|
+
'===BATCH_TEST_BEGIN alpha===',
|
|
43
|
+
'Tests: 10 passed, 10 total',
|
|
44
|
+
'===BATCH_TEST_END beta PASS 5===',
|
|
45
|
+
'===BATCH_TEST_END alpha PASS 10===',
|
|
46
|
+
'===BATCH_TEST_SUMMARY===',
|
|
47
|
+
'[{"slug":"alpha","success":true},{"slug":"beta","success":true}]',
|
|
48
|
+
].join('\n');
|
|
49
|
+
const results = parseBatchTestLogs(logs, twoPackages);
|
|
50
|
+
expect(results.get('alpha')?.testCounts?.total).toBe(10);
|
|
51
|
+
// beta never had a section of its own; it must not inherit alpha's.
|
|
52
|
+
expect(results.get('beta')?.testCounts).toBeUndefined();
|
|
53
|
+
expect(results.get('beta')?.success).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
it('fails a pass it cannot read, rather than trusting the pcall', () => {
|
|
56
|
+
// No END marker either, so there is no boundary to attribute output with.
|
|
57
|
+
// The pcall said success, but nothing here saw a test run.
|
|
58
|
+
const logs = [
|
|
59
|
+
'Tests: 275 passed, 275 total',
|
|
60
|
+
'===BATCH_TEST_SUMMARY===',
|
|
61
|
+
'[{"slug":"egghunt2026","success":true,"durationMs":1000}]',
|
|
62
|
+
].join('\n');
|
|
63
|
+
const result = parseBatchTestLogs(logs, SLUG_MAP).get('egghunt2026');
|
|
64
|
+
expect(result?.success).toBe(false);
|
|
65
|
+
expect(result?.error).toContain('no output could be attributed');
|
|
66
|
+
expect(result?.testCounts).toBeUndefined();
|
|
67
|
+
});
|
|
68
|
+
it('still shows output it could not attribute', () => {
|
|
69
|
+
// 330KB of real test output was being reported as "(no output)" because no
|
|
70
|
+
// BEGIN marker survived to delimit it.
|
|
71
|
+
const logs = [
|
|
72
|
+
'PlayerBadgeHelper: attempt to index nil',
|
|
73
|
+
'===BATCH_TEST_SUMMARY===',
|
|
74
|
+
'[{"slug":"egghunt2026","success":true,"durationMs":1000}]',
|
|
75
|
+
].join('\n');
|
|
76
|
+
const result = parseBatchTestLogs(logs, SLUG_MAP).get('egghunt2026');
|
|
77
|
+
expect(result?.logs).toContain('PlayerBadgeHelper');
|
|
78
|
+
});
|
|
79
|
+
it('counts tracebacks that jest reports as a clean pass', () => {
|
|
80
|
+
const logs = buildLogs([
|
|
81
|
+
'Tests: 155 passed, 155 total',
|
|
82
|
+
'PlayerBadgeHelper: attempt to index nil',
|
|
83
|
+
'Stack Begin',
|
|
84
|
+
"Script 'PlayerBadgeHelper', Line 365",
|
|
85
|
+
'Stack End',
|
|
86
|
+
].join('\n'));
|
|
87
|
+
const result = parseBatchTestLogs(logs, SLUG_MAP).get('egghunt2026');
|
|
88
|
+
expect(result?.testCounts?.failed).toBe(0);
|
|
89
|
+
expect(result?.tracebackCount).toBe(1);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
describe('findSummaryEntries', () => {
|
|
93
|
+
it('finds the summary even when output landed after it', () => {
|
|
94
|
+
// Messages are not delivered in order, so a stray line can follow the
|
|
95
|
+
// summary. Treating the whole remainder as one JSON blob made the same
|
|
96
|
+
// batch parse on one run and fail on the next.
|
|
97
|
+
const lines = [
|
|
98
|
+
'[{"slug":"alpha","success":true}]',
|
|
99
|
+
' ✓ a stray line that arrived late',
|
|
100
|
+
];
|
|
101
|
+
expect(findSummaryEntries(lines, 0)).toEqual([
|
|
102
|
+
{ slug: 'alpha', success: true },
|
|
103
|
+
]);
|
|
104
|
+
});
|
|
105
|
+
it('returns undefined when there is no array to find', () => {
|
|
106
|
+
expect(findSummaryEntries(['not json at all'], 0)).toBeUndefined();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
describe('countTracebacks', () => {
|
|
110
|
+
it('counts each traceback separately', () => {
|
|
111
|
+
expect(countTracebacks('Stack Begin\nfoo\nStack End\nStack Begin\n')).toBe(2);
|
|
112
|
+
});
|
|
113
|
+
it('is zero for empty output', () => {
|
|
114
|
+
expect(countTracebacks('')).toBe(0);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
//# sourceMappingURL=batch-log-parser.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch-log-parser.test.js","sourceRoot":"","sources":["../../../../src/utils/testing/parsers/batch-log-parser.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface AttributedTraceback {
|
|
2
|
+
/** First line of the error, before the stack. */
|
|
3
|
+
message: string;
|
|
4
|
+
/** Roblox instance path of the topmost frame, e.g. "ServerScriptService.Foo.Bar". */
|
|
5
|
+
source?: string;
|
|
6
|
+
/** Root of that path, which names the package the frame belongs to. */
|
|
7
|
+
owner?: string;
|
|
8
|
+
/** Frames kept for display, topmost first. */
|
|
9
|
+
frames: string[];
|
|
10
|
+
/** How many times this same traceback appeared. */
|
|
11
|
+
count: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Pull tracebacks out of log output and attribute each to its owning package.
|
|
15
|
+
*
|
|
16
|
+
* Attribution reads the script path in the frames rather than the position of
|
|
17
|
+
* the traceback in the log. Position is unreliable: a deferred callback can
|
|
18
|
+
* fire during a later package's section, which is why batch mode previously
|
|
19
|
+
* gave up on tracebacks entirely.
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseTracebacks(rawOutput: string): AttributedTraceback[];
|
|
22
|
+
/**
|
|
23
|
+
* Name the package a script path belongs to.
|
|
24
|
+
*
|
|
25
|
+
* Batch places hold each package under its own root, so the segment below the
|
|
26
|
+
* service is the package.
|
|
27
|
+
*/
|
|
28
|
+
export declare function ownerOf(scriptPath: string): string | undefined;
|
|
29
|
+
/** Render tracebacks for a human, newest information first and bounded. */
|
|
30
|
+
export declare function formatTracebacks(tracebacks: AttributedTraceback[], maxShown?: number): string;
|
|
31
|
+
//# sourceMappingURL=traceback-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traceback-parser.d.ts","sourceRoot":"","sources":["../../../../src/utils/testing/parsers/traceback-parser.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;CACf;AASD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,EAAE,CAmDxE;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG9D;AAED,2EAA2E;AAC3E,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,mBAAmB,EAAE,EACjC,QAAQ,SAAI,GACX,MAAM,CAuBR"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { OutputHelper } from '@quenty/cli-output-helpers';
|
|
2
|
+
const STACK_BEGIN = /^\s*Stack Begin\s*$/;
|
|
3
|
+
const STACK_END = /^\s*Stack End\s*$/;
|
|
4
|
+
// Script 'ServerScriptService.EggHunt2026.game.Server.Foo', Line 365 - function bar
|
|
5
|
+
const FRAME = /Script\s+'(?:\[string\s+")?([^'"\]]+)/;
|
|
6
|
+
const MAX_FRAMES = 4;
|
|
7
|
+
/**
|
|
8
|
+
* Pull tracebacks out of log output and attribute each to its owning package.
|
|
9
|
+
*
|
|
10
|
+
* Attribution reads the script path in the frames rather than the position of
|
|
11
|
+
* the traceback in the log. Position is unreliable: a deferred callback can
|
|
12
|
+
* fire during a later package's section, which is why batch mode previously
|
|
13
|
+
* gave up on tracebacks entirely.
|
|
14
|
+
*/
|
|
15
|
+
export function parseTracebacks(rawOutput) {
|
|
16
|
+
if (!rawOutput) {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
const lines = OutputHelper.stripAnsi(rawOutput).split('\n');
|
|
20
|
+
const bySignature = new Map();
|
|
21
|
+
for (let i = 0; i < lines.length; i++) {
|
|
22
|
+
if (!STACK_BEGIN.test(lines[i])) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
// The error text sits on the line above "Stack Begin".
|
|
26
|
+
const message = (lines[i - 1] ?? '').trim();
|
|
27
|
+
const frames = [];
|
|
28
|
+
let source;
|
|
29
|
+
for (let j = i + 1; j < lines.length && !STACK_END.test(lines[j]); j++) {
|
|
30
|
+
const frame = lines[j].trim();
|
|
31
|
+
if (!frame) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (frames.length < MAX_FRAMES) {
|
|
35
|
+
frames.push(frame);
|
|
36
|
+
}
|
|
37
|
+
if (!source) {
|
|
38
|
+
source = FRAME.exec(frame)?.[1];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// One bug fires per player and per frame, so the same traceback can appear
|
|
42
|
+
// hundreds of times. Collapse by signature and count instead of printing
|
|
43
|
+
// every copy.
|
|
44
|
+
const signature = `${message}::${frames[0] ?? ''}`;
|
|
45
|
+
const existing = bySignature.get(signature);
|
|
46
|
+
if (existing) {
|
|
47
|
+
existing.count++;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
bySignature.set(signature, {
|
|
51
|
+
message,
|
|
52
|
+
source,
|
|
53
|
+
owner: source ? ownerOf(source) : undefined,
|
|
54
|
+
frames,
|
|
55
|
+
count: 1,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return [...bySignature.values()];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Name the package a script path belongs to.
|
|
62
|
+
*
|
|
63
|
+
* Batch places hold each package under its own root, so the segment below the
|
|
64
|
+
* service is the package.
|
|
65
|
+
*/
|
|
66
|
+
export function ownerOf(scriptPath) {
|
|
67
|
+
const parts = scriptPath.split('.');
|
|
68
|
+
return parts.length >= 2 ? parts[1] : undefined;
|
|
69
|
+
}
|
|
70
|
+
/** Render tracebacks for a human, newest information first and bounded. */
|
|
71
|
+
export function formatTracebacks(tracebacks, maxShown = 5) {
|
|
72
|
+
if (tracebacks.length === 0) {
|
|
73
|
+
return '';
|
|
74
|
+
}
|
|
75
|
+
const lines = [];
|
|
76
|
+
const shown = tracebacks.slice(0, maxShown);
|
|
77
|
+
for (const traceback of shown) {
|
|
78
|
+
const where = traceback.source ? ` (${traceback.source})` : '';
|
|
79
|
+
const times = traceback.count > 1 ? ` ×${traceback.count}` : '';
|
|
80
|
+
lines.push(` ${traceback.message}${where}${times}`);
|
|
81
|
+
for (const frame of traceback.frames) {
|
|
82
|
+
lines.push(` ${frame}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const hidden = tracebacks.length - shown.length;
|
|
86
|
+
if (hidden > 0) {
|
|
87
|
+
lines.push(` ...and ${hidden} more distinct traceback(s)`);
|
|
88
|
+
}
|
|
89
|
+
return lines.join('\n');
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=traceback-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traceback-parser.js","sourceRoot":"","sources":["../../../../src/utils/testing/parsers/traceback-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traceback-parser.test.d.ts","sourceRoot":"","sources":["../../../../src/utils/testing/parsers/traceback-parser.test.ts"],"names":[],"mappings":""}
|