scripts/setup-config.test.mjs
Rama main · réplica de lectura del repositorio original.
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import {
mkdtempSync,
readFileSync,
readdirSync,
statSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import {
createCloudflareConfig,
getCloudflareWorkersSubdomain,
readCloudflareConfig,
readEnv,
readJsonConfig,
upsertEnv,
validateUrl,
validateMcpUrl,
} from "./setup-config.mjs";
test("configuration CLI rejects public HTTP origins despite an HTTPS MCP override", () => {
const fixture = join(
mkdtempSync(join(tmpdir(), "devpush-url-validation-")),
"config.env",
);
writeFileSync(fixture, "");
for (const key of ["DEVPUSH_BASE_URL", "BETTER_AUTH_URL"]) {
for (const value of [
"http://devpush.example",
"http://localhost:7429",
"http://host.lima.internal:7429",
]) {
const env = Object.fromEntries(
Object.entries(process.env).filter(
([name]) =>
!name.startsWith("DEVPUSH_") && !name.startsWith("BETTER_AUTH_"),
),
);
Object.assign(env, {
BETTER_AUTH_SECRET: "a".repeat(40),
DEVPUSH_BASE_URL: "http://localhost:7429",
DEVPUSH_MCP_URL: "https://devpush.example/mcp",
[key]: value,
});
const result = spawnSync(
process.execPath,
["scripts/validate-config.mjs", "--env", fixture, "--smoke"],
{ env, encoding: "utf8" },
);
assert.equal(
result.status,
value === "http://devpush.example" ? 1 : 0,
result.stderr,
);
if (result.status === 1)
assert.match(
result.stderr,
/must use HTTPS except for local development hosts/,
);
}
}
});
test("validates canonical MCP endpoints independently of the control-plane origin", () => {
for (const value of [
"http://localhost:7429/mcp",
"http://host.lima.internal:7429/mcp",
"http://[::1]:7429/mcp",
"https://mcp.example.com/mcp",
])
assert.equal(validateMcpUrl(value), undefined);
for (const value of [
"https://user:secret@example.com/mcp",
"https://example.com/mcp?secret=yes",
"https://example.com/mcp?",
"https://example.com/mcp#",
"https://example.com/",
"https://example.com/mcp/",
"http://example.com/mcp",
"ftp://example.com/mcp",
])
assert.equal(typeof validateMcpUrl(value), "string");
});
test("creates a deployment-specific Cloudflare config", () => {
const config = JSON.parse(
createCloudflareConfig({
accountId: "account-123",
bucketName: "devpush-assets",
customDomain: "devpush.example.com",
databaseId: "11111111-1111-1111-1111-111111111111",
logDatabaseId: "22222222-2222-2222-2222-222222222222",
template: {
compatibility_date: "2026-08-29",
main: "src/server.ts",
},
vars: { DEVPUSH_BASE_URL: "https://devpush.example.com" },
workerName: "devpush-production",
}),
);
assert.equal(config.name, "devpush-production");
assert.equal(config.account_id, "account-123");
assert.equal(config.main, "src/server.ts");
assert.equal(config.compatibility_date, "2026-08-29");
assert.equal(config.workers_dev, false);
assert.deepEqual(config.routes, [
{ pattern: "devpush.example.com", custom_domain: true },
]);
assert.deepEqual(config.d1_databases, [
{
binding: "DEVPUSH_DATABASE",
database_id: "11111111-1111-1111-1111-111111111111",
database_name: "devpush",
migrations_dir: "src/server/db/migrations-sqlite",
},
{
binding: "DEVPUSH_LOG_DATABASE",
database_id: "22222222-2222-2222-2222-222222222222",
database_name: "devpush-logs",
migrations_dir: "src/server/db/migrations-logs",
},
]);
assert.equal(config.hyperdrive, undefined);
assert.deepEqual(config.r2_buckets, [
{ binding: "DEVPUSH_OBJECTS", bucket_name: "devpush-assets" },
]);
assert.deepEqual(config.vars, {
DEVPUSH_BASE_URL: "https://devpush.example.com",
});
assert.equal(JSON.stringify(config).includes("DATABASE_URL"), false);
});
test("reads deployment resources from a generated Cloudflare config", () => {
const directory = mkdtempSync(join(tmpdir(), "devpush-setup-"));
const path = join(directory, "wrangler.jsonc");
writeFileSync(
path,
createCloudflareConfig({
accountId: "account-123",
bucketName: "devpush-assets",
databaseId: "11111111-1111-1111-1111-111111111111",
logDatabaseId: "22222222-2222-2222-2222-222222222222",
workersDev: true,
vars: {
BETTER_AUTH_URL: "https://auth.example.com",
DEVPUSH_ADMIN_EMAIL: "admin@example.com",
DEVPUSH_BASE_URL: "https://devpush.example.com",
},
workerName: "devpush-production",
}),
);
assert.deepEqual(readCloudflareConfig(path), {
accountId: "account-123",
bucketName: "devpush-assets",
customDomain: "",
databaseId: "11111111-1111-1111-1111-111111111111",
databaseName: "devpush",
logDatabaseId: "22222222-2222-2222-2222-222222222222",
logDatabaseName: "devpush-logs",
vars: {
BETTER_AUTH_URL: "https://auth.example.com",
DEVPUSH_ADMIN_EMAIL: "admin@example.com",
DEVPUSH_BASE_URL: "https://devpush.example.com",
},
workerName: "devpush-production",
workersDev: true,
});
assert.equal(readJsonConfig(path).name, "devpush-production");
});
test("discovers the workers.dev account subdomain", async () => {
const requests = [];
const subdomain = await getCloudflareWorkersSubdomain({
accountId: "account-123",
apiToken: "secret-token",
fetchImpl: async (url, options) => {
requests.push({ url, options });
return {
ok: true,
json: async () => ({
result: { subdomain: "example-account" },
success: true,
}),
};
},
});
assert.equal(subdomain, "example-account");
assert.equal(
requests[0].url,
"https://api.cloudflare.com/client/v4/accounts/account-123/workers/subdomain",
);
assert.equal(
requests[0].options.headers.Authorization,
"Bearer secret-token",
);
});
test("explains when the Cloudflare account has no workers.dev subdomain", async () => {
await assert.rejects(
getCloudflareWorkersSubdomain({
accountId: "account-123",
apiToken: "secret-token",
fetchImpl: async () => ({
ok: false,
json: async () => ({
errors: [{ code: 10007, message: "Subdomain not found" }],
success: false,
}),
}),
}),
/has no workers\.dev subdomain/u,
);
});
test("reads comments and trailing commas from a JSONC template", () => {
const directory = mkdtempSync(join(tmpdir(), "devpush-setup-"));
const path = join(directory, "wrangler.template.jsonc");
writeFileSync(
path,
`{
// Shared Worker defaults.
"name": "devpush",
"observability": {
"enabled": true,
},
}`,
);
assert.deepEqual(readJsonConfig(path), {
name: "devpush",
observability: { enabled: true },
});
});
test("upserts env values without discarding unrelated configuration", () => {
const directory = mkdtempSync(join(tmpdir(), "devpush-setup-"));
const path = join(directory, ".env");
upsertEnv(path, { EXISTING: "first", MULTILINE: "line one\nline two" });
upsertEnv(path, { EXISTING: "second", NEW_VALUE: "hello world" });
assert.deepEqual(readEnv(path), {
EXISTING: "second",
MULTILINE: "line one\nline two",
NEW_VALUE: "hello world",
});
assert.equal(readFileSync(path, "utf8").match(/^EXISTING=/gmu)?.length, 1);
assert.equal(statSync(path).mode & 0o777, 0o600);
assert.deepEqual(readdirSync(directory), [".env"]);
});
test("setup reads standard dotenv quoting and replaces whole multiline assignments", () => {
const directory = mkdtempSync(join(tmpdir(), "devpush-env-quotes-"));
const path = join(directory, ".env");
writeFileSync(path, "# Keep this comment\nexport KEY='line one\nFAKE=value\nline three'\nUNRELATED=`literal \\\\n`\n");
assert.equal(readEnv(path).KEY, "line one\nFAKE=value\nline three");
const unrelated = readEnv(path).UNRELATED;
upsertEnv(path, { KEY: 'new "quoted" value', NEXT: "ready" });
assert.deepEqual(readEnv(path), { KEY: 'new "quoted" value', UNRELATED: unrelated, NEXT: "ready" });
assert.ok(readFileSync(path, "utf8").startsWith("# Keep this comment\n"));
assert.ok(!readFileSync(path, "utf8").includes("FAKE=value"));
});
test("setup preserves quote and backslash values when saving existing settings", () => {
const directory = mkdtempSync(join(tmpdir(), "devpush-env-roundtrip-"));
const path = join(directory, ".env");
const values = {
PRIVATE_KEY: "-----BEGIN KEY-----\nline1\nline2\n-----END KEY-----",
QUOTES: "a 'single' and \"double\" quote # literal",
LITERAL: String.raw`literal\npath\value`,
EMPTY: "",
};
upsertEnv(path, values);
assert.deepEqual(readEnv(path), values);
upsertEnv(path, { EMPTY: "changed" });
assert.deepEqual(readEnv(path), { ...values, EMPTY: "changed" });
});
test("setup leaves malformed multiline files untouched", () => {
const directory = mkdtempSync(join(tmpdir(), "devpush-env-malformed-"));
const path = join(directory, ".env");
const source = "KEY='unterminated\nOTHER=value\n";
writeFileSync(path, source);
assert.throws(() => upsertEnv(path, { OTHER: "new" }), /unterminated/);
assert.equal(readFileSync(path, "utf8"), source);
});
test("validates control-plane URLs", () => {
assert.equal(validateUrl("https://devpush.example.com"), undefined);
for (const value of [
"http://localhost:7429",
"http://host.lima.internal:7429",
"http://127.0.0.2:7429",
"http://[::1]:7429",
])
assert.equal(validateUrl(value), undefined);
for (const value of [
"http://devpush.example",
"https://user:secret@devpush.example",
"https://devpush.example?",
"https://devpush.example#",
])
assert.notEqual(validateUrl(value), undefined);
assert.equal(
validateUrl("ftp://example.com"),
"Use an http:// or https:// URL.",
);
assert.equal(
validateUrl("https://example.com/path"),
"Use an origin without a path, query, or fragment.",
);
});
test("D1 config separates main and log stores and rejects local placeholders for remote use", async () => {
const { getD1Bindings, localD1Ids } = await import("./d1-config.mjs");
const config = {
d1_databases: [
{
binding: "DEVPUSH_DATABASE",
database_name: "main",
database_id: localD1Ids[0],
migrations_dir: "main-migrations",
},
{
binding: "DEVPUSH_LOG_DATABASE",
database_name: "logs",
database_id: localD1Ids[1],
migrations_dir: "log-migrations",
},
],
};
assert.equal(getD1Bindings(config, { local: true }).length, 2);
assert.throws(() => getD1Bindings(config), /configured D1 database ID/);
const identical = structuredClone(config);
identical.d1_databases[1].database_id = identical.d1_databases[0].database_id;
assert.throws(
() => getD1Bindings(identical, { local: true }),
/different D1/,
);
const missing = structuredClone(config);
missing.d1_databases.pop();
assert.throws(
() => getD1Bindings(missing, { local: true }),
/exactly one DEVPUSH_LOG_DATABASE/,
);
const duplicate = structuredClone(config);
duplicate.d1_databases.push(duplicate.d1_databases[0]);
assert.throws(
() => getD1Bindings(duplicate, { local: true }),
/exactly one DEVPUSH_DATABASE/,
);
});
test("pending setup preserves requested database names without treating local IDs as configured resources", () => {
const directory = mkdtempSync(join(tmpdir(), "devpush-d1-setup-"));
const path = join(directory, "pending.jsonc");
writeFileSync(
path,
createCloudflareConfig({
databaseName: "custom-main",
logDatabaseName: "custom-logs",
}),
);
const pending = readCloudflareConfig(path);
assert.equal(pending.databaseName, "custom-main");
assert.equal(pending.logDatabaseName, "custom-logs");
assert.equal(pending.databaseId, "");
assert.equal(pending.logDatabaseId, "");
});
test("remote database configuration removes template D1 bindings but preserves R2", () => {
const config = JSON.parse(
createCloudflareConfig({
remoteDatabase: true,
bucketName: "objects",
template: {
d1_databases: [{ binding: "DEVPUSH_DATABASE" }],
compatibility_flags: ["nodejs_compat"],
},
}),
);
assert.equal(config.d1_databases, undefined);
assert.equal(config.r2_buckets[0].bucket_name, "objects");
assert.deepEqual(config.compatibility_flags, ["nodejs_compat"]);
});
test("Cloudflare configuration can omit image storage", () => {
const config = JSON.parse(createCloudflareConfig({
template: { r2_buckets: [{ binding: "DEVPUSH_OBJECTS", bucket_name: "old" }] },
databaseId: "main-id",
logDatabaseId: "log-id",
}));
assert.equal(config.r2_buckets, undefined);
assert.equal(config.d1_databases.length, 2);
});