Initial Commit
This commit is contained in:
50
packages/foundation/core/env/tests/dep-graph.test.ts
vendored
Normal file
50
packages/foundation/core/env/tests/dep-graph.test.ts
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
|
||||
const pkg = JSON.parse(
|
||||
readFileSync(join(__dirname, "..", "package.json"), "utf8")
|
||||
) as {
|
||||
dependencies?: Record<string, string>;
|
||||
devDependencies?: Record<string, string>;
|
||||
};
|
||||
|
||||
describe("dep-graph", () => {
|
||||
it("has required runtime deps", { timeout: 5000 }, () => {
|
||||
const deps = pkg.dependencies ?? {};
|
||||
expect(deps).toHaveProperty("@suaveplan/error");
|
||||
expect(deps).toHaveProperty("@suaveplan/types");
|
||||
expect(deps).toHaveProperty("tslib");
|
||||
expect(deps).toHaveProperty("zod");
|
||||
});
|
||||
|
||||
it("runtime deps use workspace:^ for internal packages", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const deps = pkg.dependencies ?? {};
|
||||
expect(deps["@suaveplan/error"]).toBe("workspace:^");
|
||||
expect(deps["@suaveplan/types"]).toBe("workspace:^");
|
||||
});
|
||||
|
||||
it("does not have @opentelemetry/api as a runtime dep", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const deps = pkg.dependencies ?? {};
|
||||
// OTel is intentionally not a runtime dep for this package
|
||||
expect(Object.keys(deps)).not.toContain("@opentelemetry/api");
|
||||
});
|
||||
|
||||
it("has fast-check in devDependencies for property tests", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const devDeps = pkg.devDependencies ?? {};
|
||||
expect(devDeps).toHaveProperty("fast-check");
|
||||
});
|
||||
|
||||
it("has mitata in devDependencies for benchmarks", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const devDeps = pkg.devDependencies ?? {};
|
||||
expect(devDeps).toHaveProperty("mitata");
|
||||
});
|
||||
});
|
||||
65
packages/foundation/core/env/tests/no-disallowed.test.ts
vendored
Normal file
65
packages/foundation/core/env/tests/no-disallowed.test.ts
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
import { readdirSync, readFileSync, statSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
|
||||
const SRC = join(__dirname, "..", "src");
|
||||
|
||||
/**
|
||||
* Files approved to use node:fs.
|
||||
* dotenvLoader uses readFileSync to read .env files — this is intentional
|
||||
* and approved. The loader is explicitly documented as a Node.js loader.
|
||||
*/
|
||||
const APPROVED_FS_FILES = new Set([
|
||||
join(SRC, "loaders", "dotenv", "dotenv.ts"),
|
||||
]);
|
||||
|
||||
const BANNED: ReadonlyArray<{ pattern: RegExp; label: string }> = [
|
||||
{ pattern: /\bMath\.random\b/, label: "Math.random" },
|
||||
{ pattern: /\bDate\.now\b/, label: "Date.now" },
|
||||
{ pattern: /\bcrypto\.getRandomValues\b/, label: "crypto.getRandomValues" },
|
||||
];
|
||||
|
||||
function* walk(dir: string): Generator<string> {
|
||||
for (const entry of readdirSync(dir)) {
|
||||
const full = join(dir, entry);
|
||||
const st = statSync(full);
|
||||
if (st.isDirectory()) {
|
||||
if (entry === "__benchmarks__") continue;
|
||||
yield* walk(full);
|
||||
} else if (full.endsWith(".ts") && !full.endsWith(".test.ts")) {
|
||||
yield full;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe("no-disallowed", () => {
|
||||
it("src/ contains no Math.random, Date.now, or crypto.getRandomValues", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const hits: string[] = [];
|
||||
for (const file of walk(SRC)) {
|
||||
const content = readFileSync(file, "utf8");
|
||||
for (const { pattern, label } of BANNED) {
|
||||
if (pattern.test(content)) {
|
||||
hits.push(`${file}: ${label}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
expect(hits).toEqual([]);
|
||||
});
|
||||
|
||||
it("node:fs usage is limited to approved files (dotenv loader)", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const unapproved: string[] = [];
|
||||
const fsPattern = /from ['"]node:fs['"]/;
|
||||
for (const file of walk(SRC)) {
|
||||
if (APPROVED_FS_FILES.has(file)) continue;
|
||||
const content = readFileSync(file, "utf8");
|
||||
if (fsPattern.test(content)) {
|
||||
unapproved.push(file);
|
||||
}
|
||||
}
|
||||
expect(unapproved).toEqual([]);
|
||||
});
|
||||
});
|
||||
8
packages/foundation/core/env/tests/setup/polyfills.ts
vendored
Normal file
8
packages/foundation/core/env/tests/setup/polyfills.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// Ensure `globalThis.crypto` is available in bare-Node vitest runs.
|
||||
import { webcrypto } from "node:crypto";
|
||||
|
||||
type WritableGlobal = { crypto?: Crypto };
|
||||
const g = globalThis as WritableGlobal;
|
||||
if (g.crypto === undefined) {
|
||||
g.crypto = webcrypto as unknown as Crypto;
|
||||
}
|
||||
94
packages/foundation/core/env/tests/tree-shake.test.ts
vendored
Normal file
94
packages/foundation/core/env/tests/tree-shake.test.ts
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
|
||||
describe("tree-shake", () => {
|
||||
it("sideEffects is false", { timeout: 5000 }, () => {
|
||||
const pkgPath = join(__dirname, "..", "package.json");
|
||||
const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as {
|
||||
sideEffects?: boolean;
|
||||
};
|
||||
expect(pkg.sideEffects).toBe(false);
|
||||
});
|
||||
|
||||
it("exposes expected subpath exports", { timeout: 5000 }, () => {
|
||||
const pkgPath = join(__dirname, "..", "package.json");
|
||||
const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as {
|
||||
exports?: Record<string, unknown>;
|
||||
};
|
||||
const keys = Object.keys(pkg.exports ?? {});
|
||||
expect(keys).toContain(".");
|
||||
expect(keys).toContain("./core");
|
||||
expect(keys).toContain("./fields");
|
||||
expect(keys).toContain("./loaders");
|
||||
expect(keys).toContain("./errors");
|
||||
expect(keys).toContain("./schemas");
|
||||
expect(keys).toContain("./package.json");
|
||||
});
|
||||
|
||||
it("main export works — createEnv is callable", {
|
||||
timeout: 30000,
|
||||
}, async () => {
|
||||
const { createEnv, str, num, bool } = await import("../src/index.js");
|
||||
const env = createEnv({
|
||||
schema: {
|
||||
HOST: str().default("localhost"),
|
||||
PORT: num().default(3000),
|
||||
DEBUG: bool().default(false),
|
||||
},
|
||||
source: {},
|
||||
});
|
||||
expect(env.HOST).toBe("localhost");
|
||||
expect(env.PORT).toBe(3000);
|
||||
expect(env.DEBUG).toBe(false);
|
||||
});
|
||||
|
||||
it("errors subpath works", { timeout: 30000 }, async () => {
|
||||
const { EnvValidationError, CoercionError } = await import(
|
||||
"../src/errors/env-error.js"
|
||||
);
|
||||
const { ENV_CODES } = await import("../src/errors/error-codes.js");
|
||||
expect(ENV_CODES.VALIDATION_FAILED).toBe("ENV.VALIDATION_FAILED");
|
||||
expect(ENV_CODES.COERCE_ERROR).toBe("ENV.COERCE_ERROR");
|
||||
const err = new CoercionError("KEY", "bad", "number");
|
||||
expect(err).toBeInstanceOf(CoercionError);
|
||||
const validErr = new EnvValidationError([
|
||||
{ key: "PORT", message: "Required variable is missing" },
|
||||
]);
|
||||
expect(validErr).toBeInstanceOf(EnvValidationError);
|
||||
});
|
||||
|
||||
it("loaders subpath works", { timeout: 30000 }, async () => {
|
||||
const { objectLoader, parseDotenv } = await import(
|
||||
"../src/loaders/dotenv/dotenv.js"
|
||||
);
|
||||
const result = parseDotenv("KEY=value\nOTHER=foo");
|
||||
expect(result).toEqual({ KEY: "value", OTHER: "foo" });
|
||||
const { objectLoader: ol } = await import(
|
||||
"../src/loaders/object/object.js"
|
||||
);
|
||||
const loader = ol({ source: { A: "1" } });
|
||||
expect(loader.load().values).toEqual({ A: "1" });
|
||||
// suppress unused warning
|
||||
void objectLoader;
|
||||
});
|
||||
|
||||
it("schemas subpath works", { timeout: 30000 }, async () => {
|
||||
const { FieldConfigSchema, ValidationResultSchema } = await import(
|
||||
"../src/schemas/env.schemas.js"
|
||||
);
|
||||
const result = FieldConfigSchema.safeParse({
|
||||
isRequired: true,
|
||||
hasDefault: false,
|
||||
isSensitive: false,
|
||||
rules: [],
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
const vr = ValidationResultSchema.safeParse({
|
||||
success: true,
|
||||
data: {},
|
||||
errors: [],
|
||||
});
|
||||
expect(vr.success).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user