@quenty/nevermore-cli 4.43.4 → 4.45.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.
Files changed (147) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/build-scripts/{combine-test-places.luau → combine-test-places.lua} +3 -3
  3. package/build-scripts/{transform-inject-deploy-metadata.luau → transform-inject-deploy-metadata.lua} +3 -3
  4. package/dist/commands/batch-command/batch-deploy-command.js +4 -4
  5. package/dist/commands/batch-command/batch-deploy-command.js.map +1 -1
  6. package/dist/commands/batch-command/batch-test-command.d.ts.map +1 -1
  7. package/dist/commands/batch-command/batch-test-command.js +76 -19
  8. package/dist/commands/batch-command/batch-test-command.js.map +1 -1
  9. package/dist/commands/deploy-command/deploy-init-utils.d.ts.map +1 -1
  10. package/dist/commands/deploy-command/deploy-init-utils.js +7 -1
  11. package/dist/commands/deploy-command/deploy-init-utils.js.map +1 -1
  12. package/dist/commands/deploy-command/deploy-init.d.ts.map +1 -1
  13. package/dist/commands/deploy-command/deploy-init.js +24 -7
  14. package/dist/commands/deploy-command/deploy-init.js.map +1 -1
  15. package/dist/commands/deploy-command/index.js +1 -1
  16. package/dist/commands/deploy-command/index.js.map +1 -1
  17. package/dist/utils/deploy/deploy-github-columns.d.ts +12 -2
  18. package/dist/utils/deploy/deploy-github-columns.d.ts.map +1 -1
  19. package/dist/utils/deploy/deploy-github-columns.js +11 -4
  20. package/dist/utils/deploy/deploy-github-columns.js.map +1 -1
  21. package/dist/utils/deploy/deploy-metadata.js +1 -1
  22. package/dist/utils/deploy/deploy-metadata.js.map +1 -1
  23. package/dist/utils/job-context/base-job-context.d.ts +2 -2
  24. package/dist/utils/job-context/base-job-context.d.ts.map +1 -1
  25. package/dist/utils/job-context/base-job-context.js +1 -1
  26. package/dist/utils/job-context/base-job-context.js.map +1 -1
  27. package/dist/utils/job-context/batch-script-job-context.d.ts +37 -2
  28. package/dist/utils/job-context/batch-script-job-context.d.ts.map +1 -1
  29. package/dist/utils/job-context/batch-script-job-context.js +59 -22
  30. package/dist/utils/job-context/batch-script-job-context.js.map +1 -1
  31. package/dist/utils/job-context/cloud-job-context.d.ts +2 -2
  32. package/dist/utils/job-context/cloud-job-context.d.ts.map +1 -1
  33. package/dist/utils/job-context/cloud-job-context.js +10 -5
  34. package/dist/utils/job-context/cloud-job-context.js.map +1 -1
  35. package/dist/utils/job-context/cloud-job-context.test.d.ts +7 -0
  36. package/dist/utils/job-context/cloud-job-context.test.d.ts.map +1 -0
  37. package/dist/utils/job-context/cloud-job-context.test.js +91 -0
  38. package/dist/utils/job-context/cloud-job-context.test.js.map +1 -0
  39. package/dist/utils/job-context/job-context.d.ts +70 -2
  40. package/dist/utils/job-context/job-context.d.ts.map +1 -1
  41. package/dist/utils/job-context/local-job-context.d.ts +2 -2
  42. package/dist/utils/job-context/local-job-context.d.ts.map +1 -1
  43. package/dist/utils/job-context/local-job-context.js +4 -2
  44. package/dist/utils/job-context/local-job-context.js.map +1 -1
  45. package/dist/utils/job-context/local-job-context.test.d.ts +7 -0
  46. package/dist/utils/job-context/local-job-context.test.d.ts.map +1 -0
  47. package/dist/utils/job-context/local-job-context.test.js +68 -0
  48. package/dist/utils/job-context/local-job-context.test.js.map +1 -0
  49. package/dist/utils/open-cloud/open-cloud-client.d.ts +50 -7
  50. package/dist/utils/open-cloud/open-cloud-client.d.ts.map +1 -1
  51. package/dist/utils/open-cloud/open-cloud-client.js +49 -13
  52. package/dist/utils/open-cloud/open-cloud-client.js.map +1 -1
  53. package/dist/utils/open-cloud/open-cloud-client.test.js +125 -1
  54. package/dist/utils/open-cloud/open-cloud-client.test.js.map +1 -1
  55. package/dist/utils/open-cloud/open-cloud-logs.characterization.test.d.ts +22 -0
  56. package/dist/utils/open-cloud/open-cloud-logs.characterization.test.d.ts.map +1 -0
  57. package/dist/utils/open-cloud/open-cloud-logs.characterization.test.js +202 -0
  58. package/dist/utils/open-cloud/open-cloud-logs.characterization.test.js.map +1 -0
  59. package/dist/utils/package-manager-utils.d.ts.map +1 -1
  60. package/dist/utils/package-manager-utils.js +32 -1
  61. package/dist/utils/package-manager-utils.js.map +1 -1
  62. package/dist/utils/package-manager-utils.test.js +25 -0
  63. package/dist/utils/package-manager-utils.test.js.map +1 -1
  64. package/dist/utils/testing/batch-run.e2e.test.d.ts +17 -0
  65. package/dist/utils/testing/batch-run.e2e.test.d.ts.map +1 -0
  66. package/dist/utils/testing/batch-run.e2e.test.js +343 -0
  67. package/dist/utils/testing/batch-run.e2e.test.js.map +1 -0
  68. package/dist/utils/testing/batch-test-runner-template.test.d.ts +11 -0
  69. package/dist/utils/testing/batch-test-runner-template.test.d.ts.map +1 -0
  70. package/dist/utils/testing/batch-test-runner-template.test.js +38 -0
  71. package/dist/utils/testing/batch-test-runner-template.test.js.map +1 -0
  72. package/dist/utils/testing/log-fetch-stats.d.ts +40 -0
  73. package/dist/utils/testing/log-fetch-stats.d.ts.map +1 -0
  74. package/dist/utils/testing/log-fetch-stats.js +18 -0
  75. package/dist/utils/testing/log-fetch-stats.js.map +1 -0
  76. package/dist/utils/testing/log-fetch-stats.test.d.ts +2 -0
  77. package/dist/utils/testing/log-fetch-stats.test.d.ts.map +1 -0
  78. package/dist/utils/testing/log-fetch-stats.test.js +41 -0
  79. package/dist/utils/testing/log-fetch-stats.test.js.map +1 -0
  80. package/dist/utils/testing/parsers/batch-log-parser.d.ts +126 -1
  81. package/dist/utils/testing/parsers/batch-log-parser.d.ts.map +1 -1
  82. package/dist/utils/testing/parsers/batch-log-parser.js +440 -103
  83. package/dist/utils/testing/parsers/batch-log-parser.js.map +1 -1
  84. package/dist/utils/testing/parsers/batch-log-parser.test.js +494 -1
  85. package/dist/utils/testing/parsers/batch-log-parser.test.js.map +1 -1
  86. package/dist/utils/testing/parsers/batch-log-renderer.d.ts +79 -0
  87. package/dist/utils/testing/parsers/batch-log-renderer.d.ts.map +1 -0
  88. package/dist/utils/testing/parsers/batch-log-renderer.js +241 -0
  89. package/dist/utils/testing/parsers/batch-log-renderer.js.map +1 -0
  90. package/dist/utils/testing/parsers/batch-log-renderer.test.d.ts +2 -0
  91. package/dist/utils/testing/parsers/batch-log-renderer.test.d.ts.map +1 -0
  92. package/dist/utils/testing/parsers/batch-log-renderer.test.js +202 -0
  93. package/dist/utils/testing/parsers/batch-log-renderer.test.js.map +1 -0
  94. package/dist/utils/testing/runner/combined-project-generator.js +1 -1
  95. package/dist/utils/testing/runner/combined-project-generator.js.map +1 -1
  96. package/dist/utils/testing/runner/test-runner.d.ts +24 -0
  97. package/dist/utils/testing/runner/test-runner.d.ts.map +1 -1
  98. package/dist/utils/testing/runner/test-runner.js +166 -6
  99. package/dist/utils/testing/runner/test-runner.js.map +1 -1
  100. package/dist/utils/testing/runner/test-runner.test.js +228 -2
  101. package/dist/utils/testing/runner/test-runner.test.js.map +1 -1
  102. package/dist/utils/testing/structured-test-results.d.ts +77 -0
  103. package/dist/utils/testing/structured-test-results.d.ts.map +1 -0
  104. package/dist/utils/testing/structured-test-results.js +140 -0
  105. package/dist/utils/testing/structured-test-results.js.map +1 -0
  106. package/dist/utils/testing/structured-test-results.test.d.ts +7 -0
  107. package/dist/utils/testing/structured-test-results.test.d.ts.map +1 -0
  108. package/dist/utils/testing/structured-test-results.test.js +179 -0
  109. package/dist/utils/testing/structured-test-results.test.js.map +1 -0
  110. package/package.json +6 -6
  111. package/src/commands/batch-command/batch-deploy-command.ts +4 -4
  112. package/src/commands/batch-command/batch-test-command.ts +94 -19
  113. package/src/commands/deploy-command/deploy-init-utils.ts +7 -1
  114. package/src/commands/deploy-command/deploy-init.ts +41 -11
  115. package/src/commands/deploy-command/index.ts +1 -1
  116. package/src/utils/deploy/deploy-github-columns.ts +14 -4
  117. package/src/utils/deploy/deploy-metadata.ts +1 -1
  118. package/src/utils/job-context/base-job-context.ts +3 -2
  119. package/src/utils/job-context/batch-script-job-context.ts +98 -17
  120. package/src/utils/job-context/cloud-job-context.test.ts +112 -0
  121. package/src/utils/job-context/cloud-job-context.ts +12 -6
  122. package/src/utils/job-context/job-context.ts +71 -2
  123. package/src/utils/job-context/local-job-context.test.ts +89 -0
  124. package/src/utils/job-context/local-job-context.ts +7 -3
  125. package/src/utils/open-cloud/open-cloud-client.test.ts +155 -1
  126. package/src/utils/open-cloud/open-cloud-client.ts +94 -18
  127. package/src/utils/open-cloud/open-cloud-logs.characterization.test.ts +277 -0
  128. package/src/utils/package-manager-utils.test.ts +27 -0
  129. package/src/utils/package-manager-utils.ts +34 -1
  130. package/src/utils/testing/batch-run.e2e.test.ts +425 -0
  131. package/src/utils/testing/batch-test-runner-template.test.ts +62 -0
  132. package/src/utils/testing/log-fetch-stats.test.ts +48 -0
  133. package/src/utils/testing/log-fetch-stats.ts +55 -0
  134. package/src/utils/testing/parsers/batch-log-parser.test.ts +660 -1
  135. package/src/utils/testing/parsers/batch-log-parser.ts +721 -116
  136. package/src/utils/testing/parsers/batch-log-renderer.test.ts +330 -0
  137. package/src/utils/testing/parsers/batch-log-renderer.ts +329 -0
  138. package/src/utils/testing/runner/combined-project-generator.ts +1 -1
  139. package/src/utils/testing/runner/test-runner.test.ts +332 -2
  140. package/src/utils/testing/runner/test-runner.ts +231 -9
  141. package/src/utils/testing/structured-test-results.test.ts +267 -0
  142. package/src/utils/testing/structured-test-results.ts +199 -0
  143. package/templates/batch-test-runner.lua +242 -0
  144. package/tsconfig.tsbuildinfo +1 -1
  145. package/templates/batch-test-runner.luau +0 -137
  146. /package/build-scripts/{smoke-test-server.luau → smoke-test-server.lua} +0 -0
  147. /package/build-scripts/{transform-rojo-merge-place.luau → transform-rojo-merge-place.lua} +0 -0
@@ -2,6 +2,9 @@ import * as fs from 'fs/promises';
2
2
  import * as path from 'path';
3
3
  import { randomUUID } from 'crypto';
4
4
  import { parseTestLogs, parseTestCounts, } from '../test-log-parser.js';
5
+ import { describeLogVolume } from '../log-fetch-stats.js';
6
+ import { describeUnexplainedVerdict, findStructuredTestResults, structuredFailureReasons, toParsedTestCounts, } from '../structured-test-results.js';
7
+ import { OutputHelper } from '@quenty/cli-output-helpers';
5
8
  import { buildDeployMetadataAttributes, gatherGitDeployInfo, injectDeployMetadataInPlaceAsync, packageUsesManifestAsync, } from '../../deploy/deploy-metadata.js';
6
9
  import { readPackageVersionAsync } from '../../nevermore-cli-utils.js';
7
10
  /**
@@ -64,26 +67,183 @@ export async function runSingleTestAsync(context, options) {
64
67
  packageName,
65
68
  timeoutMs,
66
69
  });
67
- const rawLogs = await context.getLogsAsync(deployment);
70
+ const logs = await context.getLogsAsync(deployment);
71
+ const rawLogs = logs.text;
72
+ const logVolume = describeLogVolume(rawLogs, logs.stats);
73
+ // The runner used to announce a failing suite by throwing, which failed the
74
+ // task. It returns its verdict now, so the verdict has to be read: without
75
+ // this, a failing suite whose report fell outside the truncated log window
76
+ // would come back a pass.
77
+ //
78
+ // A context that already resolved the results (aggregated batch, where one
79
+ // execution's return value belongs to no single package) hands them over
80
+ // directly; otherwise they are decoded from what the script returned.
81
+ const structured = result.testResults ?? findStructuredTestResults(result.returnValues);
68
82
  // A probe script is arbitrary Luau with no jest in it, so demanding a test
69
83
  // report would fail every --script-text run. Everything else must prove a
70
- // runner spoke before it can pass.
84
+ // runner spoke before it can pass — and returned results are that proof,
85
+ // where a scraped report is only what survived truncation.
71
86
  const parsed = parseTestLogs(rawLogs, {
72
- requireTestReport: scriptText === undefined,
87
+ requireTestReport: scriptText === undefined && structured === undefined,
88
+ });
89
+ // A run that reports it never reached jest, for a package that has a
90
+ // jest.config on disk, did not test anything — the built place lost the
91
+ // config. Before results were returned, the required jest report caught this
92
+ // as "nothing proves any test ran"; a smoke-test result would otherwise
93
+ // retire that check and pass with zero tests.
94
+ const smokeTestedWithSpecs = structured !== undefined &&
95
+ !structured.ranJest &&
96
+ scriptText === undefined &&
97
+ (await packageHasJestConfigAsync(packagePath));
98
+ const reasons = mergeFailureReasons(result.errorMessage, [
99
+ ...(smokeTestedWithSpecs
100
+ ? [
101
+ 'this package has a jest.config but the run reported no jest.config ' +
102
+ 'in the built place, so none of its specs ran',
103
+ ]
104
+ : []),
105
+ ...(structured ? structuredFailureReasons(structured) : []),
106
+ ...parsed.failureReasons,
107
+ // Said only when there was log text and it did not hold up: the reasons
108
+ // above are then statements about what arrived, and how much arrived is
109
+ // the first thing to check. With no text at all the verdict came from the
110
+ // absence of it, which the reason for that absence already explains —
111
+ // saying "0 chars received" beside it only contradicts the fetch that
112
+ // reported the whole window.
113
+ ...(structured === undefined &&
114
+ parsed.failureReasons.length > 0 &&
115
+ rawLogs.length > 0
116
+ ? [`verdict read from log text alone (${logVolume})`]
117
+ : []),
118
+ ]);
119
+ // Returned counts outrank scraped ones: same numbers when the log
120
+ // survived, real numbers when it did not.
121
+ const scrapedCounts = parseTestCounts(parsed.logs);
122
+ const testCounts = structured
123
+ ? toParsedTestCounts(structured)
124
+ : scrapedCounts;
125
+ reportCountsProvenance({
126
+ packageName,
127
+ structured,
128
+ scrapedCounts,
129
+ logVolume,
130
+ // A probe is not a test run, so it has no results to be missing. Neither
131
+ // is one package of an aggregated batch, whose context reports the whole
132
+ // batch's provenance once, as a group — repeating it per package printed
133
+ // the same fetch stats 78 times over.
134
+ silent: scriptText !== undefined ||
135
+ result.testResults !== undefined ||
136
+ (options.suppressCountsProvenance ?? false),
137
+ hadReturnChannel: result.returnValues !== undefined,
73
138
  });
74
- const reasons = mergeFailureReasons(result.errorMessage, parsed.failureReasons);
75
139
  return {
76
- success: result.success && parsed.success,
140
+ success: result.success &&
141
+ parsed.success &&
142
+ (structured?.success ?? true) &&
143
+ !smokeTestedWithSpecs,
77
144
  logs: parsed.logs,
78
- testCounts: parseTestCounts(parsed.logs),
145
+ testCounts,
146
+ countsSource: structured
147
+ ? 'returned'
148
+ : scrapedCounts
149
+ ? 'scraped'
150
+ : undefined,
79
151
  durationMs: result.durationMs,
80
152
  error: reasons.length > 0 ? reasons.join('; ') : undefined,
153
+ logsLost: result.logsLost,
81
154
  };
82
155
  }
83
156
  finally {
84
157
  await context.releaseAsync(deployment);
85
158
  }
86
159
  }
160
+ /**
161
+ * Say where a run's counts came from, and complain when they had to be scraped.
162
+ *
163
+ * The structured channel exists because Open Cloud truncates a long run's logs.
164
+ * A channel that is plumbed but not flowing produces output identical to one
165
+ * that works, so the fallback is a warning rather than silence — the first
166
+ * version of this shipped inert and looked green.
167
+ */
168
+ function reportCountsProvenance(options) {
169
+ const { packageName, structured, scrapedCounts, silent, hadReturnChannel, logVolume, } = options;
170
+ if (silent) {
171
+ return;
172
+ }
173
+ if (structured) {
174
+ OutputHelper.info(`${packageName}: counts returned by the run — ` +
175
+ `${structured.passed} passed, ${structured.failed} failed, ` +
176
+ `${structured.total} total.`);
177
+ const unexplained = describeUnexplainedVerdict(structured);
178
+ if (unexplained) {
179
+ OutputHelper.warn(`${packageName}: ${unexplained}`);
180
+ }
181
+ // A suite that runs and counts nothing is not the same as one that passed.
182
+ if (structured.ranJest && structured.total === 0) {
183
+ OutputHelper.warn(`${packageName}: jest ran but found no tests to run. If this package has ` +
184
+ `specs, they are not reaching the test place.`);
185
+ }
186
+ // Two channels reporting the same run must agree. When they do not, one is
187
+ // lying and neither total stands on its own.
188
+ if (scrapedCounts && scrapedCounts.total !== structured.total) {
189
+ OutputHelper.warn(`${packageName}: returned counts disagree with the log — the run returned ` +
190
+ `${structured.passed}/${structured.failed}/${structured.total} ` +
191
+ `(passed/failed/total), its jest report says ` +
192
+ `${scrapedCounts.passed}/${scrapedCounts.failed}/${scrapedCounts.total}. ` +
193
+ `Reporting the returned counts; one of the two channels is wrong.`);
194
+ }
195
+ return;
196
+ }
197
+ OutputHelper.warn(`${packageName}: the run returned no test results, so its counts were ` +
198
+ (scrapedCounts ? 'scraped from log text' : 'unavailable') +
199
+ ` — the channel Open Cloud truncates on long runs (${logVolume}). ` +
200
+ (hadReturnChannel
201
+ ? 'The run delivered a return channel but nothing recognizable in it: the ' +
202
+ 'test script should end with "return results" (see docs/testing/testing.md).'
203
+ : 'No return channel was delivered at all, so the transport lost it.'));
204
+ }
205
+ /**
206
+ * Whether this package ships a jest.config, i.e. whether it has specs to run.
207
+ *
208
+ * Deliberately a bounded walk, not a recursive glob: every package under `src/`
209
+ * has a symlinked, self-referential `node_modules`, so recursive search here
210
+ * hangs. Two levels covers the layouts in use (`src/jest.config.lua` for a
211
+ * package, `src/modules/jest.config.lua` for a game) without needing a list of
212
+ * them.
213
+ */
214
+ export async function packageHasJestConfigAsync(packagePath) {
215
+ async function scanAsync(dir, depth) {
216
+ let entries;
217
+ try {
218
+ entries = await fs.readdir(dir, { withFileTypes: true });
219
+ }
220
+ catch {
221
+ return false;
222
+ }
223
+ for (const entry of entries) {
224
+ if (entry.isFile() && entry.name.startsWith('jest.config')) {
225
+ return true;
226
+ }
227
+ }
228
+ if (depth === 0) {
229
+ return false;
230
+ }
231
+ for (const entry of entries) {
232
+ if (!entry.isDirectory()) {
233
+ continue;
234
+ }
235
+ // `test` holds the test place, never the specs the place runs.
236
+ if (entry.name === 'node_modules' || entry.name === 'test') {
237
+ continue;
238
+ }
239
+ if (await scanAsync(path.join(dir, entry.name), depth - 1)) {
240
+ return true;
241
+ }
242
+ }
243
+ return false;
244
+ }
245
+ return scanAsync(packagePath, 2);
246
+ }
87
247
  /**
88
248
  * Read a test script from the deploy target's configured script path.
89
249
  */
@@ -1 +1 @@
1
- {"version":3,"file":"test-runner.js","sourceRoot":"","sources":["../../../../src/utils/testing/runner/test-runner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"test-runner.js","sourceRoot":"","sources":["../../../../src/utils/testing/runner/test-runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAGpC,OAAO,EAEL,aAAa,EACb,eAAe,GAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAEL,0BAA0B,EAC1B,yBAAyB,EACzB,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EACL,6BAA6B,EAC7B,mBAAmB,EACnB,gCAAgC,EAChC,wBAAwB,GACzB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAwBvE;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CACjC,YAAgC,EAChC,UAAoB;IAEpB,OAAO;QACL,GAAG,IAAI,GAAG,CAAC;YACT,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACjD,GAAG,UAAU;SACd,CAAC;KACH,CAAC;AACJ,CAAC;AAqBD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAAmB,EACnB,OAA0B;IAE1B,MAAM,EACJ,WAAW,EACX,WAAW;IACX,0EAA0E;IAC1E,sEAAsE;IACtE,sDAAsD;IACtD,SAAS,GAAG,OAAO,EACnB,UAAU,EACV,MAAM,GACP,GAAG,OAAO,CAAC;IAEZ,MAAM,SAAS,GAAG,UAAU,EAAE,CAAC;IAC/B,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC;QAC/C,MAAM;QACN,cAAc,EAAE,QAAQ,SAAS,OAAO;QACxC,WAAW;QACX,WAAW;KACZ,CAAC,CAAC;IAEH,8EAA8E;IAC9E,4EAA4E;IAC5E,0EAA0E;IAC1E,IAAI,MAAM,wBAAwB,CAAC,WAAW,CAAC,EAAE,CAAC;QAChD,MAAM,gCAAgC,CACpC,UAAU,CAAC,QAAQ,EACnB,6BAA6B,CAAC,mBAAmB,EAAE,EAAE;YACnD,MAAM,EAAE,MAAM;YACd,SAAS,EAAE,KAAK;YAChB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,OAAO,EAAE,UAAU,CAAC,MAAM,CAAC,OAAO;YAClC,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,UAAU;YACxC,cAAc,EAAE,MAAM,uBAAuB,CAAC,WAAW,CAAC;SAC3D,CAAC,CACH,CAAC;IACJ,CAAC;IAED,MAAM,aAAa,GACjB,UAAU;QACV,CAAC,MAAM,mBAAmB,CAAC,WAAW,EAAE,UAAU,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;IAE7E,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,qBAAqB,CAAC;QACrD,UAAU;QACV,WAAW;QACX,WAAW;KACZ,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,UAAU,EAAE;YACtD,aAAa;YACb,WAAW;YACX,SAAS;SACV,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;QACpD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC;QAC1B,MAAM,SAAS,GAAG,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAEzD,4EAA4E;QAC5E,2EAA2E;QAC3E,2EAA2E;QAC3E,0BAA0B;QAC1B,EAAE;QACF,2EAA2E;QAC3E,yEAAyE;QACzE,sEAAsE;QACtE,MAAM,UAAU,GACd,MAAM,CAAC,WAAW,IAAI,yBAAyB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAEvE,2EAA2E;QAC3E,0EAA0E;QAC1E,yEAAyE;QACzE,2DAA2D;QAC3D,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,EAAE;YACpC,iBAAiB,EAAE,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,SAAS;SACxE,CAAC,CAAC;QAEH,qEAAqE;QACrE,wEAAwE;QACxE,6EAA6E;QAC7E,wEAAwE;QACxE,8CAA8C;QAC9C,MAAM,oBAAoB,GACxB,UAAU,KAAK,SAAS;YACxB,CAAC,UAAU,CAAC,OAAO;YACnB,UAAU,KAAK,SAAS;YACxB,CAAC,MAAM,yBAAyB,CAAC,WAAW,CAAC,CAAC,CAAC;QAEjD,MAAM,OAAO,GAAG,mBAAmB,CAAC,MAAM,CAAC,YAAY,EAAE;YACvD,GAAG,CAAC,oBAAoB;gBACtB,CAAC,CAAC;oBACE,qEAAqE;wBACnE,8CAA8C;iBACjD;gBACH,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,wBAAwB,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3D,GAAG,MAAM,CAAC,cAAc;YACxB,wEAAwE;YACxE,wEAAwE;YACxE,0EAA0E;YAC1E,sEAAsE;YACtE,sEAAsE;YACtE,6BAA6B;YAC7B,GAAG,CAAC,UAAU,KAAK,SAAS;gBAC5B,MAAM,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC;gBAChC,OAAO,CAAC,MAAM,GAAG,CAAC;gBAChB,CAAC,CAAC,CAAC,qCAAqC,SAAS,GAAG,CAAC;gBACrD,CAAC,CAAC,EAAE,CAAC;SACR,CAAC,CAAC;QAEH,kEAAkE;QAClE,0CAA0C;QAC1C,MAAM,aAAa,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnD,MAAM,UAAU,GAAG,UAAU;YAC3B,CAAC,CAAC,kBAAkB,CAAC,UAAU,CAAC;YAChC,CAAC,CAAC,aAAa,CAAC;QAElB,sBAAsB,CAAC;YACrB,WAAW;YACX,UAAU;YACV,aAAa;YACb,SAAS;YACT,yEAAyE;YACzE,yEAAyE;YACzE,yEAAyE;YACzE,sCAAsC;YACtC,MAAM,EACJ,UAAU,KAAK,SAAS;gBACxB,MAAM,CAAC,WAAW,KAAK,SAAS;gBAChC,CAAC,OAAO,CAAC,wBAAwB,IAAI,KAAK,CAAC;YAC7C,gBAAgB,EAAE,MAAM,CAAC,YAAY,KAAK,SAAS;SACpD,CAAC,CAAC;QAEH,OAAO;YACL,OAAO,EACL,MAAM,CAAC,OAAO;gBACd,MAAM,CAAC,OAAO;gBACd,CAAC,UAAU,EAAE,OAAO,IAAI,IAAI,CAAC;gBAC7B,CAAC,oBAAoB;YACvB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,UAAU;YACV,YAAY,EAAE,UAAU;gBACtB,CAAC,CAAC,UAAU;gBACZ,CAAC,CAAC,aAAa;oBACf,CAAC,CAAC,SAAS;oBACX,CAAC,CAAC,SAAS;YACb,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,KAAK,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;YAC1D,QAAQ,EAAE,MAAM,CAAC,QAAQ;SAC1B,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,sBAAsB,CAAC,OAQ/B;IACC,MAAM,EACJ,WAAW,EACX,UAAU,EACV,aAAa,EACb,MAAM,EACN,gBAAgB,EAChB,SAAS,GACV,GAAG,OAAO,CAAC;IAEZ,IAAI,MAAM,EAAE,CAAC;QACX,OAAO;IACT,CAAC;IAED,IAAI,UAAU,EAAE,CAAC;QACf,YAAY,CAAC,IAAI,CACf,GAAG,WAAW,iCAAiC;YAC7C,GAAG,UAAU,CAAC,MAAM,YAAY,UAAU,CAAC,MAAM,WAAW;YAC5D,GAAG,UAAU,CAAC,KAAK,SAAS,CAC/B,CAAC;QAEF,MAAM,WAAW,GAAG,0BAA0B,CAAC,UAAU,CAAC,CAAC;QAC3D,IAAI,WAAW,EAAE,CAAC;YAChB,YAAY,CAAC,IAAI,CAAC,GAAG,WAAW,KAAK,WAAW,EAAE,CAAC,CAAC;QACtD,CAAC;QAED,2EAA2E;QAC3E,IAAI,UAAU,CAAC,OAAO,IAAI,UAAU,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;YACjD,YAAY,CAAC,IAAI,CACf,GAAG,WAAW,4DAA4D;gBACxE,8CAA8C,CACjD,CAAC;QACJ,CAAC;QAED,2EAA2E;QAC3E,6CAA6C;QAC7C,IAAI,aAAa,IAAI,aAAa,CAAC,KAAK,KAAK,UAAU,CAAC,KAAK,EAAE,CAAC;YAC9D,YAAY,CAAC,IAAI,CACf,GAAG,WAAW,6DAA6D;gBACzE,GAAG,UAAU,CAAC,MAAM,IAAI,UAAU,CAAC,MAAM,IAAI,UAAU,CAAC,KAAK,GAAG;gBAChE,8CAA8C;gBAC9C,GAAG,aAAa,CAAC,MAAM,IAAI,aAAa,CAAC,MAAM,IAAI,aAAa,CAAC,KAAK,IAAI;gBAC1E,kEAAkE,CACrE,CAAC;QACJ,CAAC;QACD,OAAO;IACT,CAAC;IAED,YAAY,CAAC,IAAI,CACf,GAAG,WAAW,yDAAyD;QACrE,CAAC,aAAa,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,aAAa,CAAC;QACzD,qDAAqD,SAAS,KAAK;QACnE,CAAC,gBAAgB;YACf,CAAC,CAAC,yEAAyE;gBACzE,6EAA6E;YAC/E,CAAC,CAAC,mEAAmE,CAAC,CAC3E,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,WAAmB;IAEnB,KAAK,UAAU,SAAS,CAAC,GAAW,EAAE,KAAa;QACjD,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;gBAC3D,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QAED,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YAChB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACzB,SAAS;YACX,CAAC;YACD,+DAA+D;YAC/D,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC3D,SAAS;YACX,CAAC;YACD,IAAI,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;gBAC3D,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,WAAmB,EACnB,UAA8B;IAE9B,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CACb,mDAAmD,WAAW,2EAA2E,CAC1I,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACvD,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,0BAA0B,QAAQ,EAAE,CAAC,CAAC;IACxD,CAAC;AACH,CAAC"}
@@ -1,5 +1,196 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { mergeFailureReasons } from './test-runner.js';
1
+ import * as fs from 'fs/promises';
2
+ import * as os from 'os';
3
+ import * as path from 'path';
4
+ import { afterAll, afterEach, beforeAll, describe, expect, it, vi, } from 'vitest';
5
+ import { OutputHelper } from '@quenty/cli-output-helpers';
6
+ import { mergeFailureReasons, runSingleTestAsync } from './test-runner.js';
7
+ import { TEST_RESULTS_FORMAT } from '../structured-test-results.js';
8
+ const PASSING_LOGS = [
9
+ 'Test Suites: 3 passed, 3 total',
10
+ 'Tests: 25 passed, 25 total',
11
+ ].join('\n');
12
+ function structuredResults(overrides = {}) {
13
+ return {
14
+ format: TEST_RESULTS_FORMAT,
15
+ success: true,
16
+ ranJest: true,
17
+ passed: 25,
18
+ failed: 0,
19
+ skipped: 0,
20
+ total: 25,
21
+ suitesPassed: 3,
22
+ suitesFailed: 0,
23
+ suitesTotal: 3,
24
+ failures: [],
25
+ omittedFailures: 0,
26
+ ...overrides,
27
+ };
28
+ }
29
+ /** Collect what the runner said out loud, so silence can be asserted on. */
30
+ function captureWarnings() {
31
+ const warnings = [];
32
+ vi.spyOn(OutputHelper, 'warn').mockImplementation((message) => {
33
+ warnings.push(message);
34
+ });
35
+ vi.spyOn(OutputHelper, 'info').mockImplementation(() => { });
36
+ return () => warnings;
37
+ }
38
+ afterEach(() => {
39
+ vi.restoreAllMocks();
40
+ });
41
+ /**
42
+ * A context that runs nothing: it hands back the run result and logs the test
43
+ * wants to reason about. Everything else is the minimum the runner touches.
44
+ */
45
+ function createContext(run, logs) {
46
+ return {
47
+ buildPlaceAsync: async (options) => ({
48
+ rbxlPath: 'unused.rbxl',
49
+ target: options.target,
50
+ }),
51
+ deployBuiltPlaceAsync: async () => ({}),
52
+ runScriptAsync: async () => run,
53
+ getLogsAsync: async () => ({ text: logs, messages: [] }),
54
+ releaseAsync: async () => { },
55
+ releaseBuiltPlaceAsync: async () => { },
56
+ disposeAsync: async () => { },
57
+ };
58
+ }
59
+ describe('runSingleTestAsync', () => {
60
+ let packagePath;
61
+ beforeAll(async () => {
62
+ packagePath = await fs.mkdtemp(path.join(os.tmpdir(), 'nevermore-test-'));
63
+ await fs.writeFile(path.join(packagePath, 'ServerMain.server.lua'), 'return nil\n');
64
+ });
65
+ afterAll(async () => {
66
+ await fs.rm(packagePath, { recursive: true, force: true });
67
+ });
68
+ async function runAsync(run, logs) {
69
+ return runSingleTestAsync(createContext(run, logs), {
70
+ packagePath,
71
+ packageName: 'maid',
72
+ target: {
73
+ scriptTemplate: 'ServerMain.server.lua',
74
+ },
75
+ });
76
+ }
77
+ it('fails a run whose returned results say it failed', async () => {
78
+ // The runner used to announce this by throwing, which failed the task. Now
79
+ // it returns the verdict, and these logs are what a truncated window leaves
80
+ // behind: a jest report from a suite that passed and no sign of the one
81
+ // that did not.
82
+ const result = await runAsync({
83
+ success: true,
84
+ returnValues: [structuredResults({ success: false, failed: 2 })],
85
+ }, PASSING_LOGS);
86
+ expect(result.success).toBe(false);
87
+ expect(result.error).toContain('2 test(s) failed');
88
+ });
89
+ it('reports the counts the run returned, not the ones in the log', async () => {
90
+ const result = await runAsync({
91
+ success: true,
92
+ returnValues: [structuredResults({ passed: 400, total: 400 })],
93
+ }, PASSING_LOGS);
94
+ expect(result.success).toBe(true);
95
+ expect(result.testCounts).toEqual({ passed: 400, failed: 0, total: 400 });
96
+ });
97
+ it('accepts returned results as proof the runner ran', async () => {
98
+ // Demanding a jest report in the logs is a stand-in for that proof, and it
99
+ // is the first thing a truncated log window costs.
100
+ const result = await runAsync({ success: true, returnValues: [structuredResults()] }, '(logs truncated)');
101
+ expect(result.success).toBe(true);
102
+ });
103
+ it('still fails a run with a traceback in its logs', async () => {
104
+ // Jest cannot count a deferred-callback crash, so passing results are no
105
+ // reason to stop reading the logs.
106
+ const result = await runAsync({ success: true, returnValues: [structuredResults()] }, `${PASSING_LOGS}\nStack Begin\nScript 'maid.spec', Line 4\nStack End`);
107
+ expect(result.success).toBe(false);
108
+ });
109
+ it('falls back to the logs for a script that returned nothing', async () => {
110
+ // A test script written before results were returned. It must keep working,
111
+ // and it must not start passing when its logs say otherwise.
112
+ const failing = await runAsync({ success: true, returnValues: [] }, 'Tests: 2 failed, 23 passed, 25 total');
113
+ expect(failing.success).toBe(false);
114
+ expect(failing.error).toContain('2 test(s) failed');
115
+ const passing = await runAsync({ success: true, returnValues: [] }, PASSING_LOGS);
116
+ expect(passing.success).toBe(true);
117
+ expect(passing.testCounts).toEqual({ passed: 25, failed: 0, total: 25 });
118
+ });
119
+ it('fails a script that returned nothing and logged nothing', async () => {
120
+ const result = await runAsync({ success: true, returnValues: undefined }, '');
121
+ expect(result.success).toBe(false);
122
+ expect(result.error).toContain('nothing proves any test ran');
123
+ });
124
+ it('records where the counts came from', async () => {
125
+ const returned = await runAsync({ success: true, returnValues: [structuredResults()] }, PASSING_LOGS);
126
+ expect(returned.countsSource).toBe('returned');
127
+ const scraped = await runAsync({ success: true, returnValues: [] }, PASSING_LOGS);
128
+ expect(scraped.countsSource).toBe('scraped');
129
+ });
130
+ it('warns out loud when the run returned no results', async () => {
131
+ // Silence here is what let the structured channel ship inert: a run that
132
+ // fell back to scraping produced output identical to one that did not.
133
+ const warnings = captureWarnings();
134
+ await runAsync({ success: true, returnValues: [] }, PASSING_LOGS);
135
+ expect(warnings().some((w) => w.includes('returned no test results'))).toBe(true);
136
+ });
137
+ it('fails a package with specs whose run never reached jest', async () => {
138
+ // A Rojo regression that stops shipping jest.config turns a real suite into
139
+ // a smoke test. Before results were returned, the required jest report caught
140
+ // that; a smoke-test result retires the report and would otherwise pass with
141
+ // zero tests.
142
+ await fs.mkdir(path.join(packagePath, 'src'), { recursive: true });
143
+ await fs.writeFile(path.join(packagePath, 'src', 'jest.config.lua'), 'return {}\n');
144
+ const result = await runAsync({
145
+ success: true,
146
+ returnValues: [
147
+ structuredResults({ ranJest: false, passed: 0, total: 0 }),
148
+ ],
149
+ }, '[NevermoreTestRunner] No jest.config found — smoke test passed (boot success)');
150
+ expect(result.success).toBe(false);
151
+ expect(result.error).toContain('none of its specs ran');
152
+ await fs.rm(path.join(packagePath, 'src'), {
153
+ recursive: true,
154
+ force: true,
155
+ });
156
+ });
157
+ it('passes a package with no specs that smoke tested', async () => {
158
+ // No jest.config on disk, so a smoke test is the whole contract.
159
+ const result = await runAsync({
160
+ success: true,
161
+ returnValues: [
162
+ structuredResults({ ranJest: false, passed: 0, total: 0 }),
163
+ ],
164
+ }, '[NevermoreTestRunner] No jest.config found — smoke test passed (boot success)');
165
+ expect(result.success).toBe(true);
166
+ });
167
+ it('takes results a context resolved for it, without re-reporting', async () => {
168
+ // Aggregated batch mode: one execution covers every package, so the batch
169
+ // log parser splits the per-package results out and has already reported
170
+ // provenance for the whole batch.
171
+ const warnings = captureWarnings();
172
+ const result = await runAsync({
173
+ success: true,
174
+ testResults: {
175
+ success: false,
176
+ ranJest: true,
177
+ passed: 273,
178
+ failed: 2,
179
+ skipped: 0,
180
+ total: 275,
181
+ suitesPassed: 8,
182
+ suitesFailed: 1,
183
+ suitesTotal: 9,
184
+ failures: [],
185
+ omittedFailures: 0,
186
+ },
187
+ }, '(this package’s section was truncated away)');
188
+ expect(result.success).toBe(false);
189
+ expect(result.countsSource).toBe('returned');
190
+ expect(result.testCounts).toEqual({ passed: 273, failed: 2, total: 275 });
191
+ expect(warnings()).toHaveLength(0);
192
+ });
193
+ });
3
194
  describe('mergeFailureReasons', () => {
4
195
  it('does not repeat reasons the context already reported', () => {
5
196
  // Batch mode evaluates the same logs upstream, so both sources describe the
@@ -15,4 +206,39 @@ describe('mergeFailureReasons', () => {
15
206
  expect(mergeFailureReasons(undefined, [])).toEqual([]);
16
207
  });
17
208
  });
209
+ describe('runSingleTestAsync log volume reporting', () => {
210
+ let packagePath;
211
+ beforeAll(async () => {
212
+ packagePath = await fs.mkdtemp(path.join(os.tmpdir(), 'volume-'));
213
+ await fs.writeFile(path.join(packagePath, 'ServerMain.server.lua'), 'return nil\n');
214
+ });
215
+ afterAll(async () => {
216
+ await fs.rm(packagePath, { recursive: true, force: true });
217
+ });
218
+ function runAsync(logs) {
219
+ return runSingleTestAsync(createContext({ success: true }, logs), {
220
+ packagePath,
221
+ packageName: 'gameconfig',
222
+ target: {
223
+ scriptTemplate: 'ServerMain.server.lua',
224
+ },
225
+ });
226
+ }
227
+ it('reports how much log text a verdict was read from', async () => {
228
+ const result = await runAsync('some output with no jest report');
229
+ expect(result.success).toBe(false);
230
+ expect(result.error).toContain('verdict read from log text alone');
231
+ expect(result.error).toContain('31 chars, 1 line(s) received');
232
+ });
233
+ it('says nothing about log volume when there was no log text', async () => {
234
+ // A batch package whose section was lost has no text of its own. Reporting
235
+ // "0 chars received" here contradicts the fetch stats already printed for
236
+ // the whole batch window, and describes a judgement never made: the verdict
237
+ // came from the output being absent, which its own reason already says.
238
+ const result = await runAsync('');
239
+ expect(result.success).toBe(false);
240
+ expect(result.error).not.toContain('verdict read from log text alone');
241
+ expect(result.error).not.toContain('0 chars, 0 line(s)');
242
+ });
243
+ });
18
244
  //# sourceMappingURL=test-runner.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"test-runner.test.js","sourceRoot":"","sources":["../../../../src/utils/testing/runner/test-runner.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,4EAA4E;QAC5E,iEAAiE;QACjE,MAAM,MAAM,GAAG,mBAAmB,CAChC,2CAA2C,EAC3C,CAAC,wBAAwB,EAAE,mBAAmB,CAAC,CAChD,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,wBAAwB,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,MAAM,GAAG,mBAAmB,CAChC,wCAAwC,EACxC,CAAC,mBAAmB,CAAC,CACtB,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"test-runner.test.js","sourceRoot":"","sources":["../../../../src/utils/testing/runner/test-runner.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ import { type ParsedTestCounts } from './test-log-parser.js';
2
+ /**
3
+ * Tag NevermoreTestRunnerUtils stamps on the results table a test script
4
+ * returns. A test place also runs probe scripts (`--script-text`) that return
5
+ * whatever they like, so results are recognized by this rather than by shape.
6
+ */
7
+ export declare const TEST_RESULTS_FORMAT = "nevermore-test-results@1";
8
+ /** One failed test, or one suite that failed before its tests could run. */
9
+ export interface StructuredTestFailure {
10
+ name: string;
11
+ message?: string;
12
+ }
13
+ /**
14
+ * What a test run says about itself, returned as a value instead of printed.
15
+ *
16
+ * Open Cloud truncates a long run's logs, so counts scraped out of that text go
17
+ * missing on exactly the runs where they matter most. Mirrors the
18
+ * `TestRunResults` table in `NevermoreTestRunnerUtils`.
19
+ */
20
+ export interface StructuredTestResults {
21
+ success: boolean;
22
+ /** False for a smoke test, whose counts are all zero because nothing counted. */
23
+ ranJest: boolean;
24
+ passed: number;
25
+ failed: number;
26
+ /** Pending plus todo. */
27
+ skipped: number;
28
+ total: number;
29
+ suitesPassed: number;
30
+ suitesFailed: number;
31
+ suitesTotal: number;
32
+ /** Capped by the runner; `omittedFailures` says how many did not fit. */
33
+ failures: StructuredTestFailure[];
34
+ omittedFailures: number;
35
+ /** Why the run failed when no individual test can say so. */
36
+ error?: string;
37
+ }
38
+ /**
39
+ * Decode a single returned value into results, or undefined if it is not one.
40
+ *
41
+ * Fields are read defensively rather than trusted: the value crossed a
42
+ * transport, and the two transports spell exotic Luau types differently, so a
43
+ * malformed field must not become a verdict.
44
+ */
45
+ export declare function decodeStructuredTestResults(value: unknown): StructuredTestResults | undefined;
46
+ /**
47
+ * Find the results a run returned, if it returned any.
48
+ *
49
+ * `undefined` covers both "the transport delivered no return channel" and "the
50
+ * script returned nothing recognizable" — a caller falls back to the logs
51
+ * either way, which is what a test script written before this convention gets.
52
+ */
53
+ export declare function findStructuredTestResults(returnValues: unknown[] | undefined): StructuredTestResults | undefined;
54
+ /**
55
+ * Describe a verdict the run's own counts do not support, for a warning.
56
+ *
57
+ * A run reporting failure while nothing in it failed is self-contradicting, and
58
+ * it is precisely what a runner reading the wrong field produces: the counts
59
+ * stay plausible, so nothing looks wrong except that every package fails at
60
+ * once. That shipped once — a runner consulted jest-lua's inverted
61
+ * `AggregatedResult.success` and failed every passing suite.
62
+ *
63
+ * Reported, never corrected. A runner that says it failed may know something its
64
+ * counts cannot express (an interrupted run, a snapshot check, a reporter
65
+ * error), so the verdict stands and the contradiction is made loud instead.
66
+ */
67
+ export declare function describeUnexplainedVerdict(results: StructuredTestResults): string | undefined;
68
+ /** Counts in the shape the reporters already render. */
69
+ export declare function toParsedTestCounts(results: StructuredTestResults): ParsedTestCounts;
70
+ /**
71
+ * Why the runner says the run failed. Empty when it says it passed.
72
+ *
73
+ * Phrased to match the log parser's wording so that merging the two sources
74
+ * collapses the overlap instead of reporting every failure twice.
75
+ */
76
+ export declare function structuredFailureReasons(results: StructuredTestResults): string[];
77
+ //# sourceMappingURL=structured-test-results.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structured-test-results.d.ts","sourceRoot":"","sources":["../../../src/utils/testing/structured-test-results.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,6BAA6B,CAAC;AAE9D,4EAA4E;AAC5E,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,iFAAiF;IACjF,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,QAAQ,EAAE,qBAAqB,EAAE,CAAC;IAClC,eAAe,EAAE,MAAM,CAAC;IACxB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA6BD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,OAAO,GACb,qBAAqB,GAAG,SAAS,CA0BnC;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,OAAO,EAAE,GAAG,SAAS,GAClC,qBAAqB,GAAG,SAAS,CAYnC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,qBAAqB,GAC7B,MAAM,GAAG,SAAS,CAapB;AAED,wDAAwD;AACxD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,qBAAqB,GAC7B,gBAAgB,CAMlB;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,qBAAqB,GAC7B,MAAM,EAAE,CAqBV"}