@jterrazz/test 11.0.0 → 12.0.1
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 +1 -1
- package/dist/index.d.ts +13 -3
- package/dist/index.js +139 -20
- package/dist/oxlint.cjs +325 -182
- package/dist/oxlint.js +325 -182
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -247,7 +247,7 @@ The handle destructures to `{ mobile, cleanup, udid }` (plus `backendUrl` with `
|
|
|
247
247
|
|
|
248
248
|
### Root auto-discovery
|
|
249
249
|
|
|
250
|
-
When `root` is absent, the framework walks up from the specification file to the
|
|
250
|
+
When `root` is absent, the framework walks up from the specification file to the **nearest** directory carrying `package.json` or `docker/compose.test.yaml`. In a workspace that is the member being tested, not the repository root above it. Pass `root` only when the convention does not fit. `root` is strictly the **project root** (compose detection + local-bin resolution, or the cwd of a `specification.website()` server command) — it is not a fixtures root; `.fixture()` resolves its own paths.
|
|
251
251
|
|
|
252
252
|
## Builder API
|
|
253
253
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1412,7 +1412,15 @@ interface OrchestratorOptions {
|
|
|
1412
1412
|
*/
|
|
1413
1413
|
services: Record<string, ServiceHandle>;
|
|
1414
1414
|
mode: 'e2e' | 'integration';
|
|
1415
|
-
|
|
1415
|
+
/**
|
|
1416
|
+
* The project root, already resolved. REQUIRED: the constructors derive it
|
|
1417
|
+
* by walking up from the calling specification file (CONVENTIONS A9), and
|
|
1418
|
+
* the orchestrator is handed the answer. Defaulting to `process.cwd()` here
|
|
1419
|
+
* meant one silent second opinion about what the project is — the runner
|
|
1420
|
+
* resolving from the caller, this from wherever the runner happened to be
|
|
1421
|
+
* launched, which in a workspace is rarely the same directory.
|
|
1422
|
+
*/
|
|
1423
|
+
root: string;
|
|
1416
1424
|
/** Compose project name — used for per-worker stack isolation. */
|
|
1417
1425
|
projectName?: string;
|
|
1418
1426
|
}
|
|
@@ -2016,8 +2024,9 @@ interface SqliteOptions {
|
|
|
2016
2024
|
*/
|
|
2017
2025
|
init?: string;
|
|
2018
2026
|
/**
|
|
2019
|
-
* Path to a Prisma schema directory or file
|
|
2020
|
-
* The adapter runs `prisma db push
|
|
2027
|
+
* Path to a Prisma schema directory or file, resolved against the current
|
|
2028
|
+
* working directory. The adapter runs `prisma db push --schema <path>` to
|
|
2029
|
+
* create the template, so the schema needs no Prisma config at the cwd.
|
|
2021
2030
|
* Mutually exclusive with `init`.
|
|
2022
2031
|
*/
|
|
2023
2032
|
prismaSchema?: string;
|
|
@@ -2035,6 +2044,7 @@ declare class SqliteHandle implements DatabasePort, ServiceHandle {
|
|
|
2035
2044
|
private workerDbPath;
|
|
2036
2045
|
private initSql;
|
|
2037
2046
|
private prismaSchema;
|
|
2047
|
+
private readonly options;
|
|
2038
2048
|
constructor(options?: SqliteOptions);
|
|
2039
2049
|
buildConnectionString(): string;
|
|
2040
2050
|
createDatabaseAdapter(): DatabasePort;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { i as match, n as Matcher, t as CaptureScope } from "./match.js";
|
|
2
2
|
import { a as isContract, c as mergeTextPreservingPlaceholders, d as structuralSubset, f as textEquals, i as defineContracts, l as renderExpected, n as contractsOf, o as isContracts, r as defineContract, s as mergePreservingPlaceholders, t as ContractQueue, u as structuralEquals } from "./queue.js";
|
|
3
|
-
import { closeSync, copyFileSync, cpSync, existsSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, realpathSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { basename, dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { closeSync, copyFileSync, cpSync, existsSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, readdirSync, realpathSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { parse } from "yaml";
|
|
6
6
|
import { execFile, execSync, spawn, spawnSync } from "node:child_process";
|
|
7
7
|
import { Client } from "pg";
|
|
@@ -12,6 +12,7 @@ import { createServer } from "node:http";
|
|
|
12
12
|
import { createServer as createServer$1 } from "node:net";
|
|
13
13
|
import { promisify } from "node:util";
|
|
14
14
|
import Database from "better-sqlite3";
|
|
15
|
+
import { createHash } from "node:crypto";
|
|
15
16
|
import { mockDeep } from "vitest-mock-extended";
|
|
16
17
|
import MockDatePackage from "mockdate";
|
|
17
18
|
//#region src/core/specification/shared/registry.ts
|
|
@@ -790,13 +791,55 @@ var FilesystemAccessor = class {
|
|
|
790
791
|
};
|
|
791
792
|
//#endregion
|
|
792
793
|
//#region src/core/specification/shared/caller.ts
|
|
794
|
+
/** Sibling module tests are callers, not internals (CONVENTIONS I2). */
|
|
795
|
+
const TEST_FILE = /\.test\.[cm]?[jt]s$/;
|
|
796
|
+
/** Where this very module sits inside the framework's SOURCE tree. */
|
|
797
|
+
const SOURCE_LOCATION = join("core", "specification", "shared");
|
|
798
|
+
/** The real path of `path`, or `path` itself when it cannot be resolved. */
|
|
799
|
+
function realPath(path) {
|
|
800
|
+
try {
|
|
801
|
+
return realpathSync(path);
|
|
802
|
+
} catch {
|
|
803
|
+
return path;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
793
806
|
/**
|
|
794
807
|
* Directory of the running framework module itself. From the built package
|
|
795
808
|
* this is `<package root>/dist`, whatever the resolution path — including a
|
|
796
809
|
* `file:` link, where the real path carries neither `node_modules` nor
|
|
797
810
|
* `/src/…` and would otherwise be mistaken for a caller frame.
|
|
798
811
|
*/
|
|
799
|
-
const FRAMEWORK_DIR = dirname(fileURLToPath(import.meta.url));
|
|
812
|
+
const FRAMEWORK_DIR = realPath(dirname(fileURLToPath(import.meta.url)));
|
|
813
|
+
/**
|
|
814
|
+
* The directory holding the framework's OWN modules: `<package>/dist` for a
|
|
815
|
+
* built install (everything is bundled there), `<repo>/src` when the framework
|
|
816
|
+
* runs from source — this module's known location inside that tree is what
|
|
817
|
+
* tells the two apart.
|
|
818
|
+
*/
|
|
819
|
+
const FRAMEWORK_TREE = FRAMEWORK_DIR.endsWith(`${sep}${SOURCE_LOCATION}`) ? resolve(FRAMEWORK_DIR, "../../..") : FRAMEWORK_DIR;
|
|
820
|
+
/** Is `path` inside `directory` (strictly — a directory is not inside itself)? */
|
|
821
|
+
function isInside(directory, path) {
|
|
822
|
+
const rest = relative(directory, path);
|
|
823
|
+
return rest !== "" && !rest.startsWith("..") && !isAbsolute(rest);
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Is this stack frame a module of the framework itself, rather than of the code
|
|
827
|
+
* calling it?
|
|
828
|
+
*
|
|
829
|
+
* Answered by IDENTITY: the frame is inside the framework's own directory.
|
|
830
|
+
* Answering it by substring — `/src/core/`, `/src/integrations/`, `/src/vitest/`
|
|
831
|
+
* — was answering a different question, "does this path look like the framework's
|
|
832
|
+
* layout", and a consumer that happens to have its own `src/core/` matched it.
|
|
833
|
+
* Its files were then skipped as framework internals, and fixture resolution
|
|
834
|
+
* anchored on the wrong directory (or fell through to the cwd) with nothing
|
|
835
|
+
* reported.
|
|
836
|
+
*
|
|
837
|
+
* @internal
|
|
838
|
+
*/
|
|
839
|
+
function isFrameworkFrame(filePath, frameworkTree = FRAMEWORK_TREE) {
|
|
840
|
+
if (TEST_FILE.test(filePath)) return false;
|
|
841
|
+
return isInside(realPath(frameworkTree), realPath(filePath));
|
|
842
|
+
}
|
|
800
843
|
/**
|
|
801
844
|
* Detect the directory of the first stack frame that lives outside this
|
|
802
845
|
* package. Used to anchor fixture resolution (`seeds/`, `requests/`,
|
|
@@ -813,7 +856,7 @@ function getCallerDir() {
|
|
|
813
856
|
if (!match?.groups?.filePath) continue;
|
|
814
857
|
const filePath = match.groups.filePath;
|
|
815
858
|
if (filePath.includes("node_modules")) continue;
|
|
816
|
-
if ((filePath
|
|
859
|
+
if (isFrameworkFrame(filePath)) continue;
|
|
817
860
|
return resolve(filePath, "..");
|
|
818
861
|
}
|
|
819
862
|
return process.cwd();
|
|
@@ -2583,7 +2626,7 @@ var Orchestrator = class {
|
|
|
2583
2626
|
constructor(options) {
|
|
2584
2627
|
this.services = options.services;
|
|
2585
2628
|
this.mode = options.mode;
|
|
2586
|
-
this.root = options.root
|
|
2629
|
+
this.root = options.root;
|
|
2587
2630
|
this.projectName = options.projectName;
|
|
2588
2631
|
}
|
|
2589
2632
|
/**
|
|
@@ -2804,23 +2847,39 @@ var Orchestrator = class {
|
|
|
2804
2847
|
};
|
|
2805
2848
|
//#endregion
|
|
2806
2849
|
//#region src/core/specification/shared/resolve.ts
|
|
2850
|
+
/** What makes a directory a project root — a manifest, or a test stack. */
|
|
2851
|
+
const ROOT_MARKERS = ["package.json", "docker/compose.test.yaml"];
|
|
2852
|
+
/**
|
|
2853
|
+
* The NEAREST ancestor of `startDir` (itself included) carrying a root marker,
|
|
2854
|
+
* `undefined` when none does.
|
|
2855
|
+
*
|
|
2856
|
+
* Both markers are probed at each ancestor, in ONE walk. Two walks — the
|
|
2857
|
+
* compose file all the way up, then `package.json` all the way up — made the
|
|
2858
|
+
* further directory win: in a workspace, a `docker/compose.test.yaml` at the
|
|
2859
|
+
* repository root outranked the `package.json` of the very package being
|
|
2860
|
+
* tested, and every path the runner resolved was measured from the wrong unit.
|
|
2861
|
+
* The unit is the nearest thing that declares itself a project.
|
|
2862
|
+
*
|
|
2863
|
+
* The existence probe is injected so the lint layer can pass its cached one
|
|
2864
|
+
* (A9's `a9w-redundant-root` derives the same root, and must derive it the
|
|
2865
|
+
* same way).
|
|
2866
|
+
*/
|
|
2867
|
+
function findRoot(startDir, exists) {
|
|
2868
|
+
let dir = startDir;
|
|
2869
|
+
for (;;) {
|
|
2870
|
+
if (ROOT_MARKERS.some((marker) => exists(resolve(dir, marker)))) return dir;
|
|
2871
|
+
const parent = dirname(dir);
|
|
2872
|
+
if (parent === dir) return;
|
|
2873
|
+
dir = parent;
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2807
2876
|
/**
|
|
2808
2877
|
* Auto-discover the project root from a starting directory (CONVENTIONS A9):
|
|
2809
|
-
* walk up to the first directory
|
|
2810
|
-
* if none,
|
|
2811
|
-
* if none, the starting directory itself.
|
|
2878
|
+
* walk up to the first directory that carries `package.json` or
|
|
2879
|
+
* `docker/compose.test.yaml`; if none does, the starting directory itself.
|
|
2812
2880
|
*/
|
|
2813
2881
|
function discoverRoot(startDir) {
|
|
2814
|
-
|
|
2815
|
-
let dir = startDir;
|
|
2816
|
-
for (;;) {
|
|
2817
|
-
if (existsSync(resolve(dir, marker))) return dir;
|
|
2818
|
-
const parent = dirname(dir);
|
|
2819
|
-
if (parent === dir) break;
|
|
2820
|
-
dir = parent;
|
|
2821
|
-
}
|
|
2822
|
-
}
|
|
2823
|
-
return startDir;
|
|
2882
|
+
return findRoot(startDir, existsSync) ?? startDir;
|
|
2824
2883
|
}
|
|
2825
2884
|
/**
|
|
2826
2885
|
* Resolve the project root for a specification. An explicit `root` option is
|
|
@@ -3861,6 +3920,64 @@ function isValidSqliteTemplate(path) {
|
|
|
3861
3920
|
closeSync(fd);
|
|
3862
3921
|
}
|
|
3863
3922
|
}
|
|
3923
|
+
/** The invocation used when no `prismaSchema` is declared — Prisma discovers its own schema. */
|
|
3924
|
+
const PRISMA_PUSH = "npx prisma db push --force-reset";
|
|
3925
|
+
/**
|
|
3926
|
+
* The `prisma db push` command line for a `prismaSchema` option.
|
|
3927
|
+
*
|
|
3928
|
+
* The declared path is resolved against `process.cwd()` and handed to the CLI
|
|
3929
|
+
* as `--schema`. Without the flag Prisma falls back to its own discovery
|
|
3930
|
+
* (`prisma.config.ts` / `package.json#prisma` at the cwd), which silently
|
|
3931
|
+
* ignores the option: a repo whose config does not sit at the cwd then fails
|
|
3932
|
+
* with "Could not find Prisma Schema that is required for this command" — but
|
|
3933
|
+
* only on a machine without a cached template, so the divergence hides until CI.
|
|
3934
|
+
*
|
|
3935
|
+
* `null` (no option) keeps the bare invocation, discovery and all.
|
|
3936
|
+
*/
|
|
3937
|
+
function prismaPushCommand(prismaSchema) {
|
|
3938
|
+
if (!prismaSchema) return PRISMA_PUSH;
|
|
3939
|
+
return `${PRISMA_PUSH} --schema ${JSON.stringify(resolve(prismaSchema))}`;
|
|
3940
|
+
}
|
|
3941
|
+
/** Every template file this package writes carries that prefix, then its key. */
|
|
3942
|
+
const TEMPLATE_PREFIX = "jterrazz-test-sqlite-template";
|
|
3943
|
+
/**
|
|
3944
|
+
* The schema source as bytes: a file's content, or every file under a
|
|
3945
|
+
* directory in a stable order (a Prisma `schema/` folder is a directory). Each
|
|
3946
|
+
* entry carries its own path, so moving a schema changes the fingerprint.
|
|
3947
|
+
* Anything unreadable degrades to a marker rather than throwing — the path is
|
|
3948
|
+
* already in the key, so two projects still never collide.
|
|
3949
|
+
*/
|
|
3950
|
+
function schemaBytes(path) {
|
|
3951
|
+
try {
|
|
3952
|
+
if (statSync(path).isDirectory()) return readdirSync(path).sort().map((entry) => schemaBytes(join(path, entry))).join("\0");
|
|
3953
|
+
return `${path}\0${readFileSync(path, "utf8")}`;
|
|
3954
|
+
} catch {
|
|
3955
|
+
return `${path}\0<unreadable>`;
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3958
|
+
/**
|
|
3959
|
+
* The name of the template database for a set of options — `<prefix>-<sha8>`,
|
|
3960
|
+
* the digest taken over the schema KIND, its resolved path and its content.
|
|
3961
|
+
*
|
|
3962
|
+
* The template lives in the machine-global OS tmpdir, so the name is the only
|
|
3963
|
+
* thing keeping two projects apart. A single fixed name meant the first project
|
|
3964
|
+
* to run on a machine built the template and every other project that day
|
|
3965
|
+
* silently inherited its tables — the file is header-valid, so the staleness
|
|
3966
|
+
* check accepts it and the schema never gets built. Keying on the schema makes
|
|
3967
|
+
* reuse mean "the same schema", which is what the cache was ever for.
|
|
3968
|
+
*/
|
|
3969
|
+
function sqliteTemplateName(options = {}) {
|
|
3970
|
+
let fingerprint;
|
|
3971
|
+
if (options.prismaSchema) {
|
|
3972
|
+
const path = resolve(options.prismaSchema);
|
|
3973
|
+
fingerprint = `prisma\0${path}\0${schemaBytes(path)}`;
|
|
3974
|
+
} else if (options.init) {
|
|
3975
|
+
const path = resolve(options.init);
|
|
3976
|
+
fingerprint = `init\0${path}\0${schemaBytes(path)}`;
|
|
3977
|
+
} else fingerprint = "empty";
|
|
3978
|
+
const key = createHash("sha256").update(fingerprint).digest("hex").slice(0, 8);
|
|
3979
|
+
return `${TEMPLATE_PREFIX}-${key}.sqlite`;
|
|
3980
|
+
}
|
|
3864
3981
|
var SqliteHandle = class {
|
|
3865
3982
|
type = "sqlite";
|
|
3866
3983
|
composeName = null;
|
|
@@ -3874,7 +3991,9 @@ var SqliteHandle = class {
|
|
|
3874
3991
|
workerDbPath = "";
|
|
3875
3992
|
initSql;
|
|
3876
3993
|
prismaSchema;
|
|
3994
|
+
options;
|
|
3877
3995
|
constructor(options = {}) {
|
|
3996
|
+
this.options = options;
|
|
3878
3997
|
this.initSql = options.init ?? null;
|
|
3879
3998
|
this.prismaSchema = options.prismaSchema ?? null;
|
|
3880
3999
|
}
|
|
@@ -3886,7 +4005,7 @@ var SqliteHandle = class {
|
|
|
3886
4005
|
}
|
|
3887
4006
|
async healthcheck() {}
|
|
3888
4007
|
async initialize() {
|
|
3889
|
-
this.templatePath = resolve(tmpdir(),
|
|
4008
|
+
this.templatePath = resolve(tmpdir(), sqliteTemplateName(this.options));
|
|
3890
4009
|
const lockPath = `${this.templatePath}.lock`;
|
|
3891
4010
|
if (existsSync(lockPath)) {
|
|
3892
4011
|
const start = Date.now();
|
|
@@ -3906,7 +4025,7 @@ var SqliteHandle = class {
|
|
|
3906
4025
|
writeFileSync(lockPath, process.pid.toString());
|
|
3907
4026
|
if (this.prismaSchema) {
|
|
3908
4027
|
const { execSync } = await import("node:child_process");
|
|
3909
|
-
execSync(
|
|
4028
|
+
execSync(prismaPushCommand(this.prismaSchema), {
|
|
3910
4029
|
env: {
|
|
3911
4030
|
...process.env,
|
|
3912
4031
|
DATABASE_URL: `file:${this.templatePath}`,
|