@norskvideo/ctl-test-harness 0.1.2 → 0.1.4
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/daemon.d.ts +1 -0
- package/daemon.js +10 -1
- package/package.json +1 -1
- package/studio-state.d.ts +2 -0
- package/studio-state.js +29 -1
package/daemon.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { pollUntil } from "./poll.js";
|
|
2
2
|
import { makeStoreDir, makeTempDir, TEST_TMP_BASE } from "./temp-dir.js";
|
|
3
3
|
export declare const HEALTH_TIMEOUT_MS = 120000;
|
|
4
|
+
export declare const DAEMON_READY_TIMEOUT_MS = 60000;
|
|
4
5
|
export { makeStoreDir, makeTempDir, pollUntil, TEST_TMP_BASE };
|
|
5
6
|
export declare function requireLicenseFile(): string;
|
|
6
7
|
export declare function runCli(storeDir: string, ...args: string[]): Promise<{
|
package/daemon.js
CHANGED
|
@@ -11,6 +11,15 @@ import { pollUntil } from "./poll.js";
|
|
|
11
11
|
import { makeStoreDir, makeTempDir, TEST_TMP_BASE } from "./temp-dir.js";
|
|
12
12
|
process.env.NORSK_CTL_NO_MEDIA_DOWNLOAD = "1";
|
|
13
13
|
export const HEALTH_TIMEOUT_MS = 120_000;
|
|
14
|
+
// The daemon's readiness probe (/api/ready) cannot answer until `serve` has
|
|
15
|
+
// brought up its oauth2-proxy + nginx sidecar CONTAINERS — /api/* redirects
|
|
16
|
+
// through the proxy. On a cold or contended CI runner that bring-up finishes
|
|
17
|
+
// just past 10s (observed ~10.5s on the GPU box under nightly load), so a 10s
|
|
18
|
+
// window lost the photo-finish and failed every test at setup with "Daemon did
|
|
19
|
+
// not become ready ... Unable to connect". A healthy daemon still answers in
|
|
20
|
+
// under a second, so the wider window only costs wall-clock when startup is
|
|
21
|
+
// genuinely slow.
|
|
22
|
+
export const DAEMON_READY_TIMEOUT_MS = 60_000;
|
|
14
23
|
export { makeStoreDir, makeTempDir, pollUntil, TEST_TMP_BASE };
|
|
15
24
|
function splitLines(text) {
|
|
16
25
|
return text
|
|
@@ -89,7 +98,7 @@ export function startDaemon(storeDir, options) {
|
|
|
89
98
|
const ready = pollUntil(async () => {
|
|
90
99
|
const res = await fetch(`http://localhost:${port}/api/ready`, { signal: AbortSignal.timeout(1000) });
|
|
91
100
|
return res.ok;
|
|
92
|
-
}, { timeoutMs:
|
|
101
|
+
}, { timeoutMs: DAEMON_READY_TIMEOUT_MS, intervalMs: 500, label: "Daemon did not become ready" });
|
|
93
102
|
return { daemon, ready };
|
|
94
103
|
}
|
|
95
104
|
function runningContainers(names) {
|
package/package.json
CHANGED
package/studio-state.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export interface ComponentsResponse {
|
|
|
8
8
|
totalComponents: number;
|
|
9
9
|
}
|
|
10
10
|
export declare function applyTestHost(rawUrl: string): string;
|
|
11
|
+
export declare function mediaDirectUrl(rawUrl: string): string;
|
|
11
12
|
export declare function fetchComponents(studioHostPort: number): Promise<ComponentsResponse | null>;
|
|
12
13
|
export declare function fetchComponentState(studioHostPort: number, componentId: string): Promise<unknown | null>;
|
|
13
14
|
/** SRT-listener state shape — see
|
|
@@ -93,6 +94,7 @@ export declare function assertMultivariantHasRenditions(opts: {
|
|
|
93
94
|
expectedRenditionLabels: readonly string[];
|
|
94
95
|
timeoutMs?: number;
|
|
95
96
|
intervalMs?: number;
|
|
97
|
+
resolveFetchUrl?: (url: string) => string;
|
|
96
98
|
}): Promise<void>;
|
|
97
99
|
export declare function assertNoUnmapped(opts: {
|
|
98
100
|
studioHostPort: number;
|
package/studio-state.js
CHANGED
|
@@ -47,6 +47,33 @@ export function applyTestHost(rawUrl) {
|
|
|
47
47
|
}
|
|
48
48
|
return rawUrl;
|
|
49
49
|
}
|
|
50
|
+
// The default norsk media HTTP port inside the container. The daemon's nginx
|
|
51
|
+
// proxies `/instance/<id>/media/*` here; whip-driver hits the same port direct.
|
|
52
|
+
const MEDIA_HTTP_PORT = 8080;
|
|
53
|
+
// Rewrite a daemon-proxy-routed media URL to a direct hit on the Norsk media
|
|
54
|
+
// container over the daemon's `norsk-net` bridge. Studio advertises playback
|
|
55
|
+
// URLs as `http://<publicHost>/instance/<id>/media/<native>` — that route is
|
|
56
|
+
// served ONLY by the daemon's nginx proxy. A harness that runs proxy-less (and
|
|
57
|
+
// whose runner has joined norsk-net) can instead reach the media container
|
|
58
|
+
// directly by its compose service name: `http://<id>-media-1:8080/<native>`.
|
|
59
|
+
// The `/instance/<id>/media` prefix is the proxy's routing concern; Norsk's
|
|
60
|
+
// native path is what follows it. Falls back to applyTestHost for URLs that
|
|
61
|
+
// don't carry the proxy prefix. Mirrors whip-driver's dockerNetUrl.
|
|
62
|
+
export function mediaDirectUrl(rawUrl) {
|
|
63
|
+
try {
|
|
64
|
+
const u = new URL(rawUrl);
|
|
65
|
+
const m = u.pathname.match(/^\/instance\/([^/]+)\/media(\/.*)?$/);
|
|
66
|
+
if (m) {
|
|
67
|
+
const id = m[1];
|
|
68
|
+
const rest = m[2] ?? "/";
|
|
69
|
+
return `http://${id}-media-1:${MEDIA_HTTP_PORT}${rest}${u.search}`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// not a parseable URL — fall through
|
|
74
|
+
}
|
|
75
|
+
return applyTestHost(rawUrl);
|
|
76
|
+
}
|
|
50
77
|
export async function fetchComponents(studioHostPort) {
|
|
51
78
|
const r = await fetch(`http://${STUDIO_HOST}:${studioHostPort}/live/api/components`, {
|
|
52
79
|
signal: AbortSignal.timeout(5000),
|
|
@@ -182,6 +209,7 @@ export async function assertSrtConnected(opts) {
|
|
|
182
209
|
* `#EXT-X-MEDIA:TYPE=AUDIO ... URI="<rendition>/norsk.m3u8"` line. The caller
|
|
183
210
|
* passes the set of expected labels; the body must reference every one. */
|
|
184
211
|
export async function assertMultivariantHasRenditions(opts) {
|
|
212
|
+
const resolveFetchUrl = opts.resolveFetchUrl ?? applyTestHost;
|
|
185
213
|
let lastState = null;
|
|
186
214
|
let lastBody = null;
|
|
187
215
|
let lastFetchStatus = null;
|
|
@@ -193,7 +221,7 @@ export async function assertMultivariantHasRenditions(opts) {
|
|
|
193
221
|
if (!state?.url)
|
|
194
222
|
return false;
|
|
195
223
|
try {
|
|
196
|
-
const r = await fetch(
|
|
224
|
+
const r = await fetch(resolveFetchUrl(state.url), { signal: AbortSignal.timeout(5000) });
|
|
197
225
|
lastFetchStatus = r.status;
|
|
198
226
|
if (!r.ok)
|
|
199
227
|
return false;
|