UNPKG

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
#!/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`);