@specific.dev/spectest 0.120.1 → 0.122.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/README.md +19 -0
- package/dist/components/k3s-nodes.d.ts +5 -0
- package/dist/components/k3s-nodes.js +37 -0
- package/dist/components/k3s.d.ts +7 -41
- package/dist/components/k3s.js +260 -215
- package/dist/components/supabase-coverage.d.ts +7 -0
- package/dist/components/supabase-coverage.js +375 -0
- package/dist/components/supabase.js +30 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -3
- package/package.json +1 -1
- package/src/components/internal-fetch.test.ts +68 -0
- package/src/components/k3s-concurrency.test.ts +76 -3
- package/src/components/k3s-docker.test.ts +140 -0
- package/src/components/k3s-nodes.test.ts +73 -0
- package/src/components/k3s-nodes.ts +35 -0
- package/src/components/k3s-registry.test.ts +13 -8
- package/src/components/k3s.ts +268 -223
- package/src/components/supabase-coverage.test.ts +261 -0
- package/src/components/supabase-coverage.ts +373 -0
- package/src/components/supabase.ts +30 -7
- package/src/index.ts +11 -4
|
@@ -75,6 +75,7 @@ import { microsoft } from "./microsoft.js";
|
|
|
75
75
|
import { SUPABASE_WEBHOOKS_SQL } from "./supabase-webhooks.js";
|
|
76
76
|
import { postgresTls } from "./postgres-tls.js";
|
|
77
77
|
import { supabasePoolerService } from "./supabase-pooler.js";
|
|
78
|
+
import { SUPABASE_COVERAGE_COLLECTOR, supabaseFunctionsCoverage } from "./supabase-coverage.js";
|
|
78
79
|
import {
|
|
79
80
|
mapSupabaseConfig,
|
|
80
81
|
parseSupabaseConfigFile,
|
|
@@ -1065,6 +1066,8 @@ services:
|
|
|
1065
1066
|
// ──────────────────────────────────────────────────────────────────────────
|
|
1066
1067
|
|
|
1067
1068
|
const FUNCTIONS_MAIN_TS = String.raw`// spectest-generated edge-functions router. Do not edit.
|
|
1069
|
+
import { EdgeCoverage } from "./coverage.js";
|
|
1070
|
+
const edgeCoverage = new EdgeCoverage();
|
|
1068
1071
|
const FUNCTIONS_DIR = Deno.env.get("FUNCTIONS_DIR") ?? "/repo/supabase/functions";
|
|
1069
1072
|
const JWT_SECRET = Deno.env.get("JWT_SECRET") ?? "";
|
|
1070
1073
|
const VERIFY_JWT = Deno.env.get("VERIFY_JWT") === "true";
|
|
@@ -1129,8 +1132,10 @@ const WORKER_TIMEOUT_MS = 24 * 60 * 60 * 1000;
|
|
|
1129
1132
|
* isolates, and an isolate reaped for idleness is rebooted by the next
|
|
1130
1133
|
* test that calls the function — a boot's worth of dirtied memory in that
|
|
1131
1134
|
* test's own snapshot diff. */
|
|
1132
|
-
function workerFor(name: string) {
|
|
1135
|
+
async function workerFor(name: string) {
|
|
1136
|
+
const maybeEntrypoint = await edgeCoverage.wrap(FUNCTIONS_DIR + "/" + name + "/index.ts");
|
|
1133
1137
|
return EdgeRuntime.userWorkers.create({
|
|
1138
|
+
maybeEntrypoint,
|
|
1134
1139
|
servicePath: FUNCTIONS_DIR + "/" + name,
|
|
1135
1140
|
memoryLimitMb: 256,
|
|
1136
1141
|
workerTimeoutMs: WORKER_TIMEOUT_MS,
|
|
@@ -1353,6 +1358,7 @@ async function sharedWorker(): Promise<any | null> {
|
|
|
1353
1358
|
try {
|
|
1354
1359
|
await createShared();
|
|
1355
1360
|
} catch (e) {
|
|
1361
|
+
await edgeCoverage.forgetUnstarted(SHARED_DIR + "/index.ts");
|
|
1356
1362
|
reason = "the shared isolate failed to boot: " + String(e);
|
|
1357
1363
|
}
|
|
1358
1364
|
}
|
|
@@ -1364,8 +1370,10 @@ async function sharedWorker(): Promise<any | null> {
|
|
|
1364
1370
|
return spec.reason === null ? createShared() : null;
|
|
1365
1371
|
}
|
|
1366
1372
|
|
|
1367
|
-
function createShared() {
|
|
1373
|
+
async function createShared() {
|
|
1374
|
+
const maybeEntrypoint = await edgeCoverage.wrap(SHARED_DIR + "/index.ts");
|
|
1368
1375
|
return EdgeRuntime.userWorkers.create({
|
|
1376
|
+
maybeEntrypoint,
|
|
1369
1377
|
servicePath: SHARED_DIR,
|
|
1370
1378
|
// A cap on the one isolate that holds every function, not a reservation.
|
|
1371
1379
|
memoryLimitMb: 2048,
|
|
@@ -1393,8 +1401,8 @@ function createShared() {
|
|
|
1393
1401
|
async function warmAll(): Promise<{ mode: "shared" | "per-function"; warmed: string[]; failed: Record<string, string> }> {
|
|
1394
1402
|
const names = await functionNames();
|
|
1395
1403
|
if ((await sharedWorker()) !== null) return { mode: "shared", warmed: names, failed: {} };
|
|
1396
|
-
//
|
|
1397
|
-
//
|
|
1404
|
+
// Each worker starts behind a generated gate; attach and initialize them
|
|
1405
|
+
// concurrently, then release each only after precise coverage has started.
|
|
1398
1406
|
const results = await Promise.allSettled(names.map((name) => workerFor(name)));
|
|
1399
1407
|
const warmed: string[] = [];
|
|
1400
1408
|
const failed: Record<string, string> = {};
|
|
@@ -1411,8 +1419,16 @@ Deno.serve(async (req: Request) => {
|
|
|
1411
1419
|
// Ready probe — answered before any auth check, so bring-up never needs a
|
|
1412
1420
|
// token, and never treated as a function name.
|
|
1413
1421
|
if (pathname === "/_spectest/health") return new Response("ok");
|
|
1422
|
+
if (pathname === "/_spectest/coverage" && req.method === "POST") {
|
|
1423
|
+
try { return json(await edgeCoverage.capture()); }
|
|
1424
|
+
catch (error) { return json({ error: String(error) }, 500); }
|
|
1425
|
+
}
|
|
1414
1426
|
// Setup-time warm-up (see warmAll); never treated as a function name.
|
|
1415
|
-
if (pathname === "/_spectest/warm")
|
|
1427
|
+
if (pathname === "/_spectest/warm") {
|
|
1428
|
+
const warmed = await warmAll();
|
|
1429
|
+
await edgeCoverage.ready();
|
|
1430
|
+
return json(warmed);
|
|
1431
|
+
}
|
|
1416
1432
|
|
|
1417
1433
|
const name = pathname.split("/")[1] ?? "";
|
|
1418
1434
|
if (name === "") return json({ msg: "missing function name in request" }, 400);
|
|
@@ -2286,8 +2302,15 @@ CREATE SCHEMA IF NOT EXISTS _supavisor AUTHORIZATION supabase_admin;
|
|
|
2286
2302
|
volumes: [{ source: projectRootPath(), target: REPO_MOUNT, readOnly: true, reachExempt: true }],
|
|
2287
2303
|
// Our router (see FUNCTIONS_MAIN_TS) sits outside that mount, so
|
|
2288
2304
|
// the project never has to carry a `main/` function of ours.
|
|
2289
|
-
files: [
|
|
2290
|
-
|
|
2305
|
+
files: [
|
|
2306
|
+
{ path: "/home/deno/main/index.ts", content: FUNCTIONS_MAIN_TS },
|
|
2307
|
+
{ path: "/home/deno/main/coverage.js", content: SUPABASE_COVERAGE_COLLECTOR },
|
|
2308
|
+
],
|
|
2309
|
+
// Generated entry gates hold project imports until coverage starts;
|
|
2310
|
+
// the inspector stays live and is not exposed to peer containers.
|
|
2311
|
+
args: ["start", "--main-service", "/home/deno/main", "--port", "9000", "--inspect=127.0.0.1:9229"],
|
|
2312
|
+
workdir: REPO_MOUNT,
|
|
2313
|
+
coverage: { adapters: [supabaseFunctionsCoverage(fnRelPath)] },
|
|
2291
2314
|
env: {
|
|
2292
2315
|
JWT_SECRET: jwtSecret,
|
|
2293
2316
|
// Where the router looks for `<name>/`, inside the repo mount.
|
package/src/index.ts
CHANGED
|
@@ -895,7 +895,11 @@ export interface ServiceGroupInput<
|
|
|
895
895
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
896
896
|
H extends Record<string, any> = Record<string, never>,
|
|
897
897
|
Primary extends keyof P & string = keyof P & string,
|
|
898
|
-
> {
|
|
898
|
+
> extends Partial<ServiceConfig> {
|
|
899
|
+
// ServiceConfig fields on the group override its primary container. This
|
|
900
|
+
// preserves the usual `{ ...component(), hostnames: [...] }` customization
|
|
901
|
+
// when a component expands to multiple services. Helpers/setup below are
|
|
902
|
+
// group hooks, not container overrides.
|
|
899
903
|
/**
|
|
900
904
|
* Build the group's member services, keyed by RELATIVE part name
|
|
901
905
|
* (`db`, `auth`, …). Called at expansion time with the final naming
|
|
@@ -1127,14 +1131,17 @@ function expandServiceGroups(input: InputServicesMap): ServicesMap {
|
|
|
1127
1131
|
);
|
|
1128
1132
|
}
|
|
1129
1133
|
}
|
|
1134
|
+
const { primary: _primary, services: _services, helpers: _helpers,
|
|
1135
|
+
setup: _setup, expectKey: _expectKey, [SERVICE_GROUP]: _marker,
|
|
1136
|
+
...primaryOverrides } = entry;
|
|
1130
1137
|
for (const [part, svc] of Object.entries(parts)) {
|
|
1131
1138
|
const finalKey = naming.key(part);
|
|
1132
1139
|
claim(finalKey, owner);
|
|
1133
1140
|
// Rewrite relative dependsOn to final keys; a spread keeps function
|
|
1134
1141
|
// fields (helpers/setup) and the enumerable-symbol ingress decls.
|
|
1135
|
-
const final: ServiceConfig = { ...svc };
|
|
1136
|
-
if (
|
|
1137
|
-
final.dependsOn =
|
|
1142
|
+
const final: ServiceConfig = { ...svc, ...(part === entry.primary ? primaryOverrides : {}) };
|
|
1143
|
+
if (final.dependsOn?.length) {
|
|
1144
|
+
final.dependsOn = final.dependsOn.map((d) =>
|
|
1138
1145
|
partKeys.has(d) ? naming.key(d) : d,
|
|
1139
1146
|
);
|
|
1140
1147
|
}
|