@norskvideo/ctl-test-harness 0.1.31 → 0.1.32

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 (3) hide show
  1. package/package.json +1 -1
  2. package/smoke.d.ts +4 -0
  3. package/smoke.js +17 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@norskvideo/ctl-test-harness",
3
- "version": "0.1.31",
3
+ "version": "0.1.32",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
package/smoke.d.ts CHANGED
@@ -137,6 +137,10 @@ export interface SmokeDeps {
137
137
  ensureNetwork(): ReturnType<typeof ensureRunnerOnNetwork>;
138
138
  supportsNoPublish(): Promise<boolean>;
139
139
  containerUser(): string | undefined;
140
+ /** Diagnostic dump for a satisfied-phase timeout, taken while the instance
141
+ * is still up. Optional so an existing custom-deps consumer keeps working;
142
+ * the default reads every container carrying the instance's label. */
143
+ diagnose?(instanceId: string): string;
140
144
  log(line: string): void;
141
145
  }
142
146
  /** Per-slug port band, above the product integration harnesses' own bands:
package/smoke.js CHANGED
@@ -20,6 +20,7 @@
20
20
  import { spawnSync } from "node:child_process";
21
21
  import { existsSync, writeFileSync } from "node:fs";
22
22
  import { basename, dirname, join } from "node:path";
23
+ import { dumpInstanceContainerLogs } from "./container-logs.js";
23
24
  import { ensureRunnerOnNetwork, netReachMode, STUDIO_INTERNAL_PORT, studioBaseFrom, } from "./container-net.js";
24
25
  import { cleanupDaemon, requireLicenseFile, runCli, startDaemon, } from "./daemon.js";
25
26
  import { hashSlot } from "./harness-config.js";
@@ -81,6 +82,7 @@ export const defaultSmokeDeps = {
81
82
  ensureNetwork: () => ensureRunnerOnNetwork(),
82
83
  supportsNoPublish: ctlSupportsNoPublish,
83
84
  containerUser: runnerContainerUser,
85
+ diagnose: (instanceId) => dumpInstanceContainerLogs(instanceId),
84
86
  log: (line) => console.log(`[smoke] ${line}`),
85
87
  };
86
88
  function describeObservation(o) {
@@ -304,11 +306,21 @@ export async function runProductSmoke(slug, spec, deps = defaultSmokeDeps) {
304
306
  });
305
307
  }
306
308
  for (const o of spec.observe) {
307
- await pollUntil(() => observationSatisfied(o, ctx, deps.readers), {
308
- timeoutMs: timeouts.observeMs,
309
- intervalMs: 1000,
310
- label: `${describeObservation(o)} never satisfied${vacuous ? "" : " while the sources pumped"}`,
311
- });
309
+ try {
310
+ await pollUntil(() => observationSatisfied(o, ctx, deps.readers), {
311
+ timeoutMs: timeouts.observeMs,
312
+ intervalMs: 1000,
313
+ label: `${describeObservation(o)} never satisfied${vacuous ? "" : " while the sources pumped"}`,
314
+ });
315
+ }
316
+ catch (e) {
317
+ // Only this phase: a not-yet or cleared failure is a statement about the
318
+ // observable itself, where a container dump adds noise, not cause. Here
319
+ // the predicate stayed false for a reason that lives in the containers
320
+ // -- and cleanup removes them moments from now.
321
+ const dump = deps.diagnose?.(instanceId) ?? "";
322
+ throw dump ? new Error(`${e instanceof Error ? e.message : String(e)}\n${dump}`, { cause: e }) : e;
323
+ }
312
324
  }
313
325
  deps.log(`${slug}: satisfied phase passed for ${spec.observe.length} observation(s)`);
314
326
  if (!vacuous) {