4bnode
Version:
4bnode is a CLI-powered backend development platform with a built-in visual dashboard to generate, manage, and test Node.js/Express APIs faster.
663 lines (603 loc) • 33.7 kB
JavaScript
#!/usr/bin/env node
// Lightweight assertion tests for the shared codegen layer. No test framework —
// run with `npm test`. Exits non-zero on first failure.
import fs from "fs";
import os from "os";
import path from "path";
import { fileURLToPath } from "url";
import assert from "assert";
import {
toCamelCase,
toPascalCase,
sanitizeName,
sanitizeFieldName,
addImportToContent,
insertCodeIntoContent,
buildInsertCode,
buildReadCode,
buildUpdateCode,
buildDeleteCode,
generateMongooseModel,
buildErrorHandlerFile,
buildRolesGuardFile,
buildValidateFile,
buildZodSchemaFile,
requiredFieldsFromZod,
extractFileFields,
wireValidateIntoRouteContent,
buildAuthMiddleware,
wireAuthIntoRouteContent,
securityWiring,
addImportAfterLastImport,
insertBeforeListenContent,
insertBeforeRoutesContent,
ERROR_HANDLER_MARKER,
normalizeAiPlan,
buildCrudRouteFile,
AI_PROVIDERS,
DEFAULT_AI_PROVIDER,
addDepsToPackageJson,
extractRequestFields,
buildOpenApiSpec,
buildDocsHtml,
buildDocsRouter,
MAIL_PROVIDERS,
buildMailerService,
isOfficialMailDomain,
OFFICIAL_MAIL_DOMAIN_PASSWORD,
OFFICIAL_MAIL_NOTIFY,
buildDiscoveryModule,
wireDiscoveryIntoIndex,
DISCOVERY_IMPORT,
} from "./codegen.js";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const root = path.join(__dirname, "..");
let passed = 0;
function test(name, fn) {
fn();
passed++;
console.log(` ✔ ${name}`);
}
// ── Name helpers ────────────────────────────────────────────────────────────
test("toCamelCase / toPascalCase", () => {
assert.equal(toCamelCase("user-profile"), "userProfile");
assert.equal(toPascalCase("user-profile"), "UserProfile");
assert.equal(toPascalCase("user"), "User");
});
test("sanitizeName accepts valid, rejects invalid", () => {
assert.equal(sanitizeName(" users "), "users");
assert.throws(() => sanitizeName("1bad"));
assert.throws(() => sanitizeName(""));
});
// ── String route helpers ────────────────────────────────────────────────────
test("addImportToContent is idempotent", () => {
const base = "import express from 'express';\nexport default router;\n";
const imp = "import User from '../models/user.js';";
const once = addImportToContent(base, imp);
assert.ok(once.startsWith(imp));
assert.equal(addImportToContent(once, imp), once, "should not duplicate");
});
test("insertCodeIntoContent inserts before export default router", () => {
const base = "const router = express.Router();\nexport default router;\n";
const out = insertCodeIntoContent(base, "router.get('/', (req,res)=>res.end());");
assert.ok(out.indexOf("router.get") < out.indexOf("export default router;"));
});
// ── CRUD builders ───────────────────────────────────────────────────────────
test("buildInsertCode wires fields + bcrypt for password", () => {
const code = buildInsertCode("User", ["email", "password"], "req.body");
assert.ok(code.includes("email: req.body.email"));
assert.ok(code.includes("bcrypt.hash(newData.password"));
assert.ok(code.includes("new User(newData)"));
});
test("buildInsertCode omits bcrypt when no password field", () => {
const code = buildInsertCode("Item", ["name"], "req.body");
assert.ok(!code.includes("bcrypt"));
});
test("buildRead/Update/Delete reference the model", () => {
assert.ok(buildReadCode("User").includes("User.find()"));
assert.ok(buildUpdateCode("User", ["name"], "req.body").includes("User.findByIdAndUpdate"));
assert.ok(buildDeleteCode("User").includes("User.findByIdAndDelete"));
});
// ── Mongoose model generation ───────────────────────────────────────────────
test("generateMongooseModel emits types, refs, indexes", () => {
const out = generateMongooseModel(
"Post",
[
{ name: "title", type: "String", required: true },
{ name: "author", type: "ObjectId", ref: "User" },
],
[{ fields: [{ field: "title", direction: "text" }], unique: false }]
);
assert.ok(out.includes("title: { type: String, required: true }"));
assert.ok(out.includes("author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }"));
assert.ok(out.includes("PostSchema.index({ title: 'text' });"));
assert.ok(out.includes("mongoose.model('Post', PostSchema)"));
});
// ── End-to-end file wrapper (lib/routeFile.js) ──────────────────────────────
test("routeFile.js inserts CRUD into a real file", async () => {
const { addImportToRoute, insertCodeIntoRoute } = await import("./routeFile.js");
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "4bnode-test-"));
const file = path.join(tmp, "users.js");
fs.writeFileSync(
file,
"import express from 'express';\nconst router = express.Router();\n\nexport default router;\n"
);
addImportToRoute(file, "import User from '../models/user.js';");
insertCodeIntoRoute(file, buildReadCode("User"));
const result = fs.readFileSync(file, "utf8");
assert.ok(result.includes("import User from '../models/user.js';"));
assert.ok(result.indexOf("User.find()") < result.indexOf("export default router;"));
fs.rmSync(tmp, { recursive: true, force: true });
});
// ── Phase 1: Security & Middleware ──────────────────────────────────────────
test("securityWiring composes imports + insertion blocks", () => {
const w = securityWiring(["helmet", "rateLimit", "errorHandler"]);
assert.ok(w.imports.includes("import helmet from 'helmet';"));
assert.ok(w.imports.includes("import rateLimit from 'express-rate-limit';"));
assert.ok(w.imports.some((i) => i.includes("errorHandler.js")));
assert.ok(w.beforeRoutes.includes("app.use(helmet("));
assert.ok(w.beforeRoutes.includes("contentSecurityPolicy:"));
assert.ok(w.beforeRoutes.includes("rateLimit({"));
assert.ok(w.beforeListen.includes("app.use(notFound)"));
assert.ok(w.beforeListen.includes("app.use(errorHandler)"));
});
test("securityWiring only includes selected features", () => {
const w = securityWiring(["helmet"]);
assert.equal(w.imports.length, 1);
assert.equal(w.beforeListen, "");
assert.ok(!w.beforeRoutes.includes("rateLimit"));
});
test("middleware file builders produce valid-looking modules", () => {
assert.ok(buildErrorHandlerFile().includes("export function errorHandler"));
assert.ok(buildRolesGuardFile().includes("export default roles"));
assert.ok(buildValidateFile().includes("schema.safeParse"));
});
test("buildZodSchemaFile maps mongoose types and optionality", () => {
const out = buildZodSchemaFile("user", [
{ name: "email", type: "String", required: true },
{ name: "age", type: "Number" },
{ name: "owner", type: "ObjectId", required: true },
{ name: "active", type: "Boolean" },
]);
assert.ok(out.includes("import { z } from 'zod';"));
assert.ok(out.includes("email: z.string(),"));
assert.ok(out.includes("age: z.coerce.number().optional(),"));
assert.ok(out.includes("owner: z.string().regex(/^[0-9a-fA-F]{24}$/, 'Invalid id'),"));
assert.ok(out.includes("active: z.coerce.boolean().optional(),"));
assert.ok(out.includes("export const userSchema"));
});
test("extractFileFields detects multer single/array/fields", () => {
assert.deepEqual(extractFileFields("upload.single('resume'), async"), [{ name: "resume" }]);
assert.deepEqual(extractFileFields("upload.array('photos', 5),"), [{ name: "photos" }]);
assert.deepEqual(
extractFileFields("upload.fields([{ name: 'a', maxCount: 1 }, { name: 'b' }]),"),
[{ name: "a" }, { name: "b" }]
);
assert.deepEqual(extractFileFields("async (req, res) => { res.json({}); }"), []);
});
test("requiredFieldsFromZod reads required-ness from .optional()", () => {
// Round-trip the actual generated validator format.
const src = buildZodSchemaFile("user", [
{ name: "email", type: "String", required: true },
{ name: "age", type: "Number" },
{ name: "owner", type: "ObjectId", required: true },
]);
const req = requiredFieldsFromZod(src);
assert.equal(req.email, true); // no .optional() → required
assert.equal(req.age, false); // .optional() → not required
assert.equal(req.owner, true); // chained .regex() but no .optional() → required
// Handles nested objects/arrays without ending the block early.
const nested = `z.object({ a: z.string(), b: z.array(z.object({ c: z.number() })).optional(), d: z.string() })`;
const r2 = requiredFieldsFromZod(nested);
assert.deepEqual(r2, { a: true, b: false, d: true });
// No z.object → null so callers fall back to the model.
assert.equal(requiredFieldsFromZod("export const x = 1;"), null);
});
test("wireValidateIntoRouteContent guards write endpoints only, idempotently", () => {
const route = [
"import express from 'express';",
"import User from '../models/user.js';",
"const router = express.Router();",
"router.get('/', async (req, res) => { res.json([]); });",
"router.post('/', async (req, res) => { res.json({}); });",
"router.put('/:id', upload.single('avatar'), async (req, res) => { res.json({}); });",
"router.patch('/:id', async (req, res) => { res.json({}); });",
"router.delete('/:id', async (req, res) => { res.json({}); });",
"export default router;",
].join("\n");
const out = wireValidateIntoRouteContent(route, "user");
assert.ok(out.includes("import validate from '../middleware/validate.js';"));
assert.ok(out.includes("import { userSchema } from '../validators/user.js';"));
assert.ok(out.includes("router.post('/', validate(userSchema), async (req, res)"), "POST wired (full schema)");
assert.ok(out.includes("router.put('/:id', upload.single('avatar'), validate(userSchema), async (req, res)"), "PUT wired after multer (full schema)");
assert.ok(out.includes("router.patch('/:id', validate(userSchema.partial()), async (req, res)"), "PATCH wired with .partial()");
assert.ok(out.includes("router.get('/', async (req, res)"), "GET untouched");
assert.ok(out.includes("router.delete('/:id', async (req, res)"), "DELETE untouched");
assert.equal(wireValidateIntoRouteContent(out, "user"), out, "idempotent");
});
test("buildAuthMiddleware + wireAuthIntoRouteContent protects every endpoint, auth-first, idempotently", () => {
assert.ok(buildAuthMiddleware().includes("jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'] })"));
assert.ok(buildAuthMiddleware().includes("export default auth"));
const route = [
"import express from 'express';",
"const router = express.Router();",
"router.get('/', async (req, res) => {});",
"router.post('/', validate(userSchema), async (req, res) => {});",
"router.put('/:id', upload.single('avatar'), async (req, res) => {});",
"export default router;",
].join("\n");
const out = wireAuthIntoRouteContent(route);
assert.ok(out.includes("import auth from '../middleware/auth.js';"));
assert.ok(out.includes("router.get('/', auth, async (req, res)"), "GET protected");
assert.ok(out.includes("router.post('/', auth, validate(userSchema), async (req, res)"), "auth before validate");
assert.ok(out.includes("router.put('/:id', auth, upload.single('avatar'), async (req, res)"), "auth before multer");
assert.equal(wireAuthIntoRouteContent(out), out, "idempotent");
});
// ── Pure index.js transforms (shared by CLI + dashboard) ────────────────────
test("addImportAfterLastImport inserts after last import, idempotently", () => {
const base = "import a from 'a';\nimport b from 'b';\n\nconst x = 1;\n";
const out = addImportAfterLastImport(base, "import c from 'c';");
assert.ok(/import b from 'b';\nimport c from 'c';/.test(out));
assert.equal(addImportAfterLastImport(out, "import c from 'c';"), out, "idempotent");
});
test("insertBeforeListenContent anchors on the start CALL, not the function def", () => {
const skeleton = `async function startServer(tryPort) {\n const listener = app.listen(tryPort);\n}\n\nstartServer(Number(port));\n`;
const out = insertBeforeListenContent(skeleton, "app.use(x);");
// function definition must be intact
assert.ok(out.includes("async function startServer(tryPort) {"));
// inserted code must be right before the bottom call, not inside the function
assert.ok(out.indexOf("app.use(x);") > out.indexOf("const listener"));
assert.ok(out.indexOf("app.use(x);") < out.lastIndexOf("startServer(Number(port));"));
});
test("insertBeforeListenContent keeps code above the error-handler marker", () => {
const withHandler = `app.use('/api/a', a);\n\n${ERROR_HANDLER_MARKER}\napp.use(notFound);\n\nstartServer(Number(port));\n`;
const out = insertBeforeListenContent(withHandler, "app.use('/api/b', b);");
assert.ok(out.indexOf("app.use('/api/b', b);") < out.indexOf(ERROR_HANDLER_MARKER));
});
test("insertBeforeRoutesContent inserts at the Start Server marker", () => {
const skeleton = `app.use(cors());\n\n// ── Start Server ──\nasync function startServer() {}\n`;
const out = insertBeforeRoutesContent(skeleton, "app.use(helmet());");
assert.ok(out.indexOf("app.use(helmet());") < out.indexOf("// ── Start Server ──"));
assert.ok(out.indexOf("app.use(helmet());") > out.indexOf("app.use(cors());"));
});
// ── Phase 2: AI Builder ─────────────────────────────────────────────────────
test("normalizeAiPlan keeps valid models/routes and coerces names", () => {
const out = normalizeAiPlan({
summary: "blog",
models: [
{
name: "blog-post",
fields: [
{ name: "title", type: "String", required: true },
{ name: "author", type: "ObjectId", ref: "user" },
],
},
],
routes: [{ name: "posts", model: "blog-post", operations: ["create", "read"] }],
});
assert.equal(out.models[0].name, "blogPost");
assert.equal(out.models[0].fields[1].ref, "User");
assert.equal(out.routes[0].model, "blogPost");
assert.deepEqual(out.routes[0].operations, ["create", "read"]);
});
test("normalizeAiPlan resolves route.model casing to the model file name (Linux-safe imports)", () => {
// LLM defines the model one way but references it with different capitalization
// in the route. The route's model must resolve to the EXACT model name so the
// generated import path matches the file on case-sensitive filesystems.
const out = normalizeAiPlan({
models: [{ name: "passcodeModel", fields: [{ name: "code", type: "String" }] }],
routes: [{ name: "passcodes", model: "PasscodeModel", operations: ["read"] }],
});
assert.equal(out.models[0].name, "passcodeModel");
assert.equal(out.routes[0].model, "passcodeModel"); // not "PasscodeModel"
// The import path in the generated route file must match the model file name.
const code = buildCrudRouteFile(out.routes[0].name, out.routes[0].model, ["code"], out.routes[0].operations);
assert.ok(code.includes("from '../models/passcodeModel.js'"));
assert.ok(!code.includes("PasscodeModel.js"));
});
test("normalizeAiPlan drops junk, dedupes, defaults operations", () => {
const out = normalizeAiPlan({
models: [
{ name: "1bad", fields: [{ name: "x", type: "String" }] }, // invalid name → dropped
{ name: "item", fields: [] }, // no fields → dropped
{ name: "good", fields: [{ name: "a", type: "Nope" }, { name: "a", type: "String" }] }, // bad type→String, dup→1
{ name: "good", fields: [{ name: "z", type: "String" }] }, // duplicate model → dropped
],
routes: [
{ name: "things", model: "good", operations: ["frobnicate"] }, // bad ops → default all
{ name: "things", model: "good", operations: ["read"] }, // dup route → dropped
{ name: "x", operations: ["read"] }, // no model → dropped
],
});
assert.equal(out.models.length, 1);
assert.equal(out.models[0].name, "good");
assert.equal(out.models[0].fields.length, 1);
assert.equal(out.models[0].fields[0].type, "String");
assert.equal(out.routes.length, 1);
assert.deepEqual(out.routes[0].operations, ["create", "read", "update", "delete"]);
});
test("buildCrudRouteFile produces a valid route module", () => {
const code = buildCrudRouteFile("posts", "post", ["title", "body"], ["create", "read"]);
assert.ok(code.startsWith("import express from 'express';"));
assert.ok(code.includes("import Post from '../models/post.js';"));
assert.ok(code.includes("const router = express.Router();"));
assert.ok(code.includes("title: req.body.title"));
assert.ok(code.includes("Post.find()"));
assert.ok(!code.includes("findByIdAndDelete"), "delete not requested");
assert.ok(code.trimEnd().endsWith("export default router;"));
});
test("buildCrudRouteFile adds bcrypt when a password field is written", () => {
const code = buildCrudRouteFile("users", "user", ["email", "password"], ["create"]);
assert.ok(code.includes("import bcrypt from 'bcrypt';"));
assert.ok(code.includes("bcrypt.hash(newData.password"));
});
test("AI_PROVIDERS defines openai + anthropic with required fields", () => {
for (const id of ["openai", "anthropic"]) {
const p = AI_PROVIDERS[id];
assert.ok(p, `${id} present`);
assert.equal(p.id, id);
assert.ok(p.keyEnv && p.defaultModel && p.label && p.keyPlaceholder, `${id} fields`);
}
assert.ok(AI_PROVIDERS[DEFAULT_AI_PROVIDER], "default provider is valid");
});
// ── Integration: index.js wiring keeps error handler last + def intact ──────
test("security wiring + later route keep correct order in index.js", async () => {
const { addImport, insertBeforeRoutes, insertBeforeListen, addRouteRegistration } =
await import("./indexFile.js");
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "4bnode-idx-"));
fs.copyFileSync(path.join(root, "skeleton", "index.js"), path.join(tmp, "index.js"));
fs.writeFileSync(path.join(tmp, ".env"), "PORT=3000\n");
const cwd = process.cwd();
process.chdir(tmp);
try {
const w = securityWiring(["helmet", "errorHandler"]);
w.imports.forEach(addImport);
insertBeforeRoutes(w.beforeRoutes);
insertBeforeListen(w.beforeListen);
// Register a route AFTER security — must land above the error handler.
addRouteRegistration(
"import orders from './src/routes/orders.js';",
"app.use('/api/orders', orders);"
);
const idx = fs.readFileSync(path.join(tmp, "index.js"), "utf8");
const routePos = idx.indexOf("app.use('/api/orders', orders);");
const notFoundPos = idx.indexOf("app.use(notFound)");
const startCallPos = idx.search(/^startServer\(Number/m);
assert.ok(routePos > -1 && notFoundPos > -1, "route + error handler present");
assert.ok(routePos < notFoundPos, "route must precede error handler");
assert.ok(notFoundPos < startCallPos, "error handler must precede start call");
// The startServer function definition must not have been corrupted.
assert.ok(
idx.includes("async function startServer(tryPort) {"),
"startServer function definition intact"
);
} finally {
process.chdir(cwd);
fs.rmSync(tmp, { recursive: true, force: true });
}
});
// ── Dependency bookkeeping ───────────────────────────────────────────────────
test("addDepsToPackageJson adds missing deps with known versions, idempotently", () => {
const start = JSON.stringify({ name: "x", dependencies: { express: "^5.2.1" } }, null, 2);
const r = addDepsToPackageJson(start, ["helmet", "express", "zod"]);
assert.ok(r.changed);
const p = JSON.parse(r.content);
assert.equal(p.dependencies.helmet, "^8.0.0");
assert.equal(p.dependencies.zod, "^3.23.0");
assert.equal(p.dependencies.express, "^5.2.1", "existing dep left untouched");
assert.equal(addDepsToPackageJson(r.content, ["helmet"]).changed, false, "idempotent");
});
test("addDepsToPackageJson supports devDependencies + unknown→latest", () => {
const out = addDepsToPackageJson(JSON.stringify({ name: "x" }), ["mongoose"], { dev: true });
assert.equal(JSON.parse(out.content).devDependencies.mongoose, "^8.8.0");
const unknown = addDepsToPackageJson(JSON.stringify({ name: "x" }), ["some-random-pkg"]);
assert.equal(JSON.parse(unknown.content).dependencies["some-random-pkg"], "latest");
});
// ── API Tester field detection ───────────────────────────────────────────────
test("extractRequestFields detects destructuring AND req.body.x access", () => {
assert.deepEqual(
extractRequestFields("const { email, name } = req.body;"),
["email", "name"]
);
// generated CRUD style — direct member access, no destructuring
const insert = buildInsertCode("User", ["email", "password"], "req.body");
assert.deepEqual(extractRequestFields(insert).sort(), ["email", "password"].sort());
// bracket access + dedupe
assert.deepEqual(
extractRequestFields("const x = req.body['title']; log(req.body.title); req.body.body;"),
["title", "body"]
);
// query source
assert.deepEqual(extractRequestFields("const { page } = req.query;", "query"), ["page"]);
// nothing
assert.deepEqual(extractRequestFields("res.json({ ok: true });"), []);
});
test("extractRequestFields works on a CRUD route (req.body.x)", () => {
const route = buildCrudRouteFile("posts", "Post", ["title", "views"], ["create"]);
assert.deepEqual(extractRequestFields(route).sort(), ["title", "views"].sort());
});
// ── API Docs (OpenAPI / Swagger) ─────────────────────────────────────────────
test("buildOpenApiSpec builds paths, params, requestBody, schemas, security", () => {
const spec = buildOpenApiSpec({
title: "MyApp",
endpoints: [
{ method: "GET", fullPath: "/api/users/:id", params: ["id"], queryFields: [], bodyFields: [] },
{ method: "POST", fullPath: "/api/users", params: [], queryFields: [], bodyFields: [{ name: "email", type: "String", required: true }, { name: "age", type: "Number" }], hasAuth: true },
],
models: [{ name: "user", fields: [{ name: "email", type: "String" }, { name: "age", type: "Number" }] }],
});
assert.equal(spec.openapi, "3.0.0");
assert.equal(spec.info.title, "MyApp");
// :id converted to {id}
assert.ok(spec.paths["/api/users/{id}"].get);
assert.deepEqual(spec.paths["/api/users/{id}"].get.parameters[0], { name: "id", in: "path", required: true, schema: { type: "string" } });
const post = spec.paths["/api/users"].post;
const schema = post.requestBody.content["application/json"].schema;
assert.deepEqual(schema.properties.email, { type: "string" });
assert.deepEqual(schema.properties.age, { type: "integer" });
assert.deepEqual(schema.required, ["email"]);
assert.deepEqual(post.security, [{ bearerAuth: [] }]);
assert.ok(spec.components.schemas.User);
assert.ok(spec.components.securitySchemes.bearerAuth);
});
test("buildOpenApiSpec uses multipart when file fields present", () => {
const spec = buildOpenApiSpec({ endpoints: [{ method: "POST", fullPath: "/api/upload", bodyFields: [], fileFields: [{ name: "avatar" }] }] });
const schema = spec.paths["/api/upload"].post.requestBody.content["multipart/form-data"].schema;
assert.deepEqual(schema.properties.avatar, { type: "string", format: "binary" });
assert.deepEqual(schema.required, ["avatar"]); // files are required by default
// ...unless the field opts out
const optional = buildOpenApiSpec({ endpoints: [{ method: "POST", fullPath: "/api/upload", bodyFields: [], fileFields: [{ name: "avatar", required: false }] }] });
assert.equal(optional.paths["/api/upload"].post.requestBody.content["multipart/form-data"].schema.required, undefined);
});
test("buildDocsHtml + buildDocsRouter are well-formed, self-contained (no CDN, no Swagger)", () => {
const html = buildDocsHtml("/docs/openapi.json");
assert.ok(html.includes("API Documentation"));
assert.ok(html.includes("SPEC_URL='/docs/openapi.json'"));
// Fully self-contained: no external URLs and no third-party doc branding.
assert.ok(!/https?:\/\//.test(html), "no external URLs in the docs page");
assert.ok(!/swagger/i.test(html), "no Swagger branding");
assert.ok(!/openapi/i.test(html.replace(/\/docs\/openapi\.json/g, "")), "no visible OpenAPI text");
const router = buildDocsRouter();
assert.ok(router.includes("openapi.json"));
assert.ok(router.includes("res.type('html')"));
assert.ok(router.trimEnd().endsWith("export default router;"));
});
// ── Email (Resend + SMTP) ────────────────────────────────────────────────────
test("MAIL_PROVIDERS has exactly IntraApp(Postmaster) + Custom SMTP", () => {
assert.deepEqual(Object.keys(MAIL_PROVIDERS).sort(), ["resend", "smtp"]);
assert.equal(MAIL_PROVIDERS.resend.label, "IntraApp(Postmaster)");
assert.equal(MAIL_PROVIDERS.resend.type, "resend");
assert.equal(MAIL_PROVIDERS.resend.apiKeyOnly, true);
assert.equal(MAIL_PROVIDERS.smtp.type, "smtp");
assert.equal(MAIL_PROVIDERS.smtp.port, 587);
});
test("official 4brains.in mail domain is gated", () => {
assert.ok(isOfficialMailDomain("alok@4brains.in"));
assert.ok(isOfficialMailDomain(" ALOK@4Brains.IN "));
assert.ok(!isOfficialMailDomain("alok@gmail.com"));
assert.ok(!isOfficialMailDomain("alok@sub.4brains.in.evil.com"));
assert.equal(OFFICIAL_MAIL_DOMAIN_PASSWORD, "99321239");
assert.equal(OFFICIAL_MAIL_NOTIFY, "pawan@4brains.in");
});
test("buildMailerService supports both Resend and SMTP backends", () => {
const s = buildMailerService();
assert.ok(s.includes("MAIL_PROVIDER"), "provider switch");
assert.ok(s.includes("await import('resend')") && s.includes("new Resend("), "Resend backend");
assert.ok(s.includes("await import('nodemailer')") && s.includes("createTransport"), "SMTP backend");
assert.ok(s.includes("process.env.RESEND_API_KEY") && s.includes("process.env.SMTP_HOST"));
// Runtime gate: 4brains.in from-address is blocked unless authorized.
assert.ok(s.includes("4brains") && s.includes("OFFICIAL_MAIL_AUTHORIZED"));
assert.ok(s.includes("export async function sendMail"));
assert.ok(s.includes("export function emailTemplate"));
assert.ok(s.includes("export async function verifyMailer"));
assert.ok(s.trimEnd().endsWith("};"));
});
// ── Security hardening ──────────────────────────────────────────────────────
test("buildReadCode paginates and never returns password", () => {
const out = buildReadCode("User");
assert.ok(out.includes(".select('-password')"), "list/detail exclude password");
assert.ok(out.includes("req.query.page") && out.includes("req.query.limit"), "paginated");
assert.ok(out.includes("countDocuments()"), "returns total");
});
test("create/update DTOs drop mass-assignable privilege fields", () => {
const fields = ["name", "email", "role", "isAdmin", "__proto__", "password"];
for (const code of [buildInsertCode("User", fields, "req.body"), buildUpdateCode("User", fields, "req.body")]) {
assert.ok(code.includes("name: req.body.name"), "keeps normal fields");
assert.ok(code.includes("email: req.body.email"), "keeps normal fields");
assert.ok(!/\brole:\s*req\.body\.role\b/.test(code), "drops role");
assert.ok(!/isAdmin:\s*req\.body\.isAdmin/.test(code), "drops isAdmin");
assert.ok(!code.includes("__proto__: req.body"), "drops __proto__");
assert.ok(code.includes("password: req.body.password"), "keeps password (hashed below)");
}
});
test("generateMongooseModel marks password select:false", () => {
const out = generateMongooseModel("User", [
{ name: "email", type: "String" },
{ name: "password", type: "String", required: true },
]);
assert.ok(/password:\s*\{[^}]*select:\s*false/.test(out), "password hidden by default");
assert.ok(!/email:\s*\{[^}]*select:\s*false/.test(out), "non-password fields untouched");
});
test("errorHandler hides 5xx internals in production", () => {
const src = buildErrorHandlerFile();
assert.ok(src.includes("err.expose"), "honors err.expose");
assert.ok(src.includes("status >= 400 && status < 500"), "only 4xx messages pass in prod");
});
test("buildAuthMiddleware pins algorithm and rejects non-access tokens", () => {
const src = buildAuthMiddleware();
assert.ok(src.includes("algorithms: ['HS256']"), "algorithm pinned");
assert.ok(src.includes("decoded.type") && src.includes("!== 'access'"), "type checked");
});
test("buildDocsRouter never sets the cookie to the stored hash", () => {
const src = buildDocsRouter();
assert.ok(src.includes("signSession()"), "signed session token");
assert.ok(src.includes("createHmac"), "HMAC-signed cookie");
assert.ok(!src.includes("docs_auth=' + expected"), "cookie is not the hash");
assert.ok(src.includes("scrypt$"), "supports salted scrypt");
});
test("sanitizeFieldName rejects prototype keys and bad identifiers", () => {
assert.equal(sanitizeFieldName(" email "), "email");
assert.equal(sanitizeFieldName("_id"), "_id");
for (const bad of ["__proto__", "constructor", "prototype", "a b", "a-b", "1x", "a } = req.body"]) {
assert.throws(() => sanitizeFieldName(bad), new RegExp("Invalid field name|reserved"), `should reject ${bad}`);
}
});
// ── Bonjour / mDNS discovery ────────────────────────────────────────────────
test("buildDiscoveryModule follows mDNS best practices (well-formed, safe TXT)", () => {
const mod = buildDiscoveryModule();
// Public lifecycle surface (each is an export).
["startAdvertising", "stopAdvertising", "destroyDiscovery", "browse", "getSelfState", "isDiscoveryEnabled"]
.forEach((fn) => assert.ok(new RegExp("export (async )?function " + fn + "\\b").test(mod), `exports ${fn}`));
// Bug-2 fix: SRV host is explicitly .local-suffixed.
assert.ok(mod.includes('base + ".local"'), "advertises an explicit .local host");
// Bug-1 fix: TXT holds only stable metadata — never an IP/address.
const txtBlock = mod.slice(mod.indexOf("const txt = {"), mod.indexOf("const txt = {") + 200);
assert.ok(/platform: os\.platform\(\)/.test(txtBlock), "TXT carries stable platform");
assert.ok(!/\bip\b|address/i.test(txtBlock), "TXT must not contain an IP/address");
// Goodbye packets on shutdown.
assert.ok(mod.includes("unpublishAll"), "sends goodbye packets via unpublishAll");
// Lazy, tolerant load so a missing package never crashes the app.
assert.ok(mod.includes('await import("bonjour-service")'), "imports bonjour-service lazily");
// The embedded module must stay backtick-free (it's carried in a template literal).
assert.ok(!mod.includes("`"), "generated module contains no backticks");
});
test("skeleton/src/discovery.js is generated from buildDiscoveryModule (no drift)", () => {
const onDisk = fs.readFileSync(path.join(root, "skeleton", "src", "discovery.js"), "utf8");
assert.equal(
onDisk,
buildDiscoveryModule(),
"skeleton/src/discovery.js is stale — regenerate it from buildDiscoveryModule()"
);
});
test("wireDiscoveryIntoIndex is idempotent and wires a bare index.js", () => {
// Already-wired skeleton index.js → unchanged (no duplicate handlers/imports).
const skeleton = fs.readFileSync(path.join(root, "skeleton", "index.js"), "utf8");
assert.equal(wireDiscoveryIntoIndex(skeleton), skeleton, "already-wired index is left untouched");
// A bare Express index.js gets all three edits.
const bare = [
'import express from "express";',
"const app = express();",
"const port = process.env.PORT || 3000;",
"app.listen(port, () => {",
" console.log(`listening on ${port}`);",
"});",
"",
].join("\n");
const wired = wireDiscoveryIntoIndex(bare);
assert.ok(wired.includes(DISCOVERY_IMPORT), "adds the discovery import");
assert.ok(wired.includes("startAdvertising({ port:"), "advertises inside the listen callback");
assert.ok(wired.includes('process.on("SIGINT"') && wired.includes("destroyDiscovery("), "adds graceful shutdown");
// Running twice must not double-apply anything.
assert.equal(wireDiscoveryIntoIndex(wired), wired, "second run is a no-op");
});
// ── Skeleton drift guard ────────────────────────────────────────────────────
test("skeleton codegen.js is in sync with lib/codegen.js", () => {
const src = fs.readFileSync(path.join(root, "lib", "codegen.js"), "utf8");
const copy = fs.readFileSync(path.join(root, "skeleton", ".4bnode", "codegen.js"), "utf8");
assert.equal(
src,
copy,
"skeleton/.4bnode/codegen.js is stale — run `npm run sync`"
);
});
console.log(`\n${passed} passed`);