test-from-target
Test from target (class or method)
Given a target (class or class.method), an optional source file path, and a test file path, produce or update a Mocha-style test suite (TypeScript .test.ts) aligned with project patterns. Focus on full logical branch coverage, validation, and error propagation. Source lives under src/; tests run with npm test or npm run test:all (Mocha + --require tsx/cjs).
Input parameters
Supply as ordered lines (one per line, no prefixes):
- TARGET_NAME (${targetCode}) — Fully qualified target to test: Class or Class.method. Examples:
UsersModel.getActiveUsers,AuthController.login. Use the real method name (no__prefix); controllers expose private methods without__. - TEST_FILE_PATH (${testFile}) — Repo-relative path to the test file to create or update (must end with
.test.ts). - (Optional) SOURCE_FILE_PATH (${sourceFile}) — Repo-relative path to the implementation file (e.g.
src/controllers/auth.controller.ts) for context and import resolution.
Example:
AuthController.login
test/controllers/auth.controller.test.ts
src/controllers/auth.controller.ts
Output
- Markdown-fenced TypeScript test content (or delta additions) only.
- Apply changes directly to ${testFile} in the workspace (create/update the file). Chat output must match the saved file. Do not only suggest edits in chat.
- Never modify the production file (${sourceFile}) when generating tests. Do not add factories or hooks in production to inject mocks.
Context linking
If ${sourceFile} is supplied, output (before the code block) two plain lines:
Implementation: ${sourceFile}Test File: ${testFile}
If not supplied, skip these lines; do not guess paths. Relative imports in code are inferred from the provided source path.
Golden rules
- TypeScript/ESM:
import ... from '...'. Test files are.test.ts; source undersrc/(e.g.src/controllers/,src/lib/,src/model/). - Structure: Each class gets a top-level
describe('<ClassName>'). Each method gets a nesteddescribe('<methodName>')— use the real method name (e.g.login,getNations), not__methodName. - Organization: Inside each method block use optional
describe('Success')/describe('Error')when branches > 2; else individualitcases. - Sinon: Use for stubs/spies; prefer
sinon.assert.calledWithover manual argument inspection. - Async errors: No chai-as-promised; use try/catch for async error assertions.
- Controller private methods: Call via
(controller as any).methodName(...). Stub withsinon.stub(controller as any, 'methodName')(real name, no__). - Secrets/config: Never import production secrets or mutate real environment configs.
- No obsolete refs: Do not reference removed or project-specific instruction files; integrate any needed logic into the test design.
- SQL: Do not assert on raw SQL strings; only assert that the query method was called with objects of the expected shape (e.g. replacements). Use
transactionClientin assertions (nottransaction). StubqueryReturnFirstwhen the controller uses it; stubquerywhen it expects{ rows }. - File writes: Always update ${testFile} on disk; chat output must mirror the saved content.
- Production code: Never modify ${sourceFile}. Prefer: (a) complete mock config so constructors do not throw (e.g. pubSub options, getstream, sms), (b) minimal fake env when config is missing, (c) local HTTP server or fixtures under
test/only. - Run tests (entire file, mandatory): After writing tests, always run the full
${testFile}suite (e.g.npm test -- ${testFile}), not just newly added tests or filtereddescribe/itblocks. Iterate until all tests in that file pass. Fix failures by changing test code only, not production. - ESM stubs: Do not stub immutable ES Module default exports; Sinon cannot stub ESM bindings. Use HTTP server in test or indirection under
test/fixtures/only if that pattern exists (do not edit production). - Static utils: When encrypt/decrypt or similar static utilities are used, stub only those static methods (e.g.
Crypt.encryptAES); do not alter the class file. - Unreachable branches: If a branch is impossible to cover without changing production, add
// TODO: unreachable branch without code changeinstead of changing production. - PubSub/Notifications: Do not stub constructors or add production hooks. In the mock env provide config (e.g.
config.pubSubOptionswithtopicIdandauthentication: { projectId, credentials }) sonew NotificationsManager(env)(and any internal PubSub client) does not throw. AddmockNotificationManager.store.resolves()andmockNotificationManager.publish.resolves()so async flow completes. For other deps (e.g. Getstream, SMS), provide the corresponding config entries so constructors do not readundefined. - Rollback: If the controller does not wrap
rollbackin try/catch, whenrollbackrejects,nextis called with the rollback error. Assertnext(rollbackError)and do not expect logger.error for rollback in that case. - Logger: If the code under test uses
this.env.logger.warning, the mock env must providelogger.warning(not onlylogger.warn).
Coverage expectations
Cover: happy path, each conditional branch, invalid parameters, edge values (0, empty array, null, undefined), error propagation (next(error) in controllers, thrown errors in models/libs). Validate:
- HTTP status codes and response methods (
send,sendStatus,json,cookie,clearCookie). - Sequence and arguments of model/DB operations.
- Side effects: cookie set/cleared, session manager calls, grants updated, data transformations.
Folder-specific directives
Controllers (test/controllers/*.test.ts)
- Mock env with: session config, getstream/sms (or equivalent) config,
pgConnection,pgModels,session(andsessionManagerif used), and any other connections the controller uses (e.g.documentsConnection). - For code that builds a notifications/pub-sub manager, provide config (e.g.
config.pubSubOptionswithtopicIdandauthentication: { projectId, credentials }) so the constructor does not throw; addstore.resolves()andpublish.resolves()on the mock where needed. - Stub
env.session.checkAuthentication()to return a pass-through middleware when needed. - Call controller methods via
(controller as any).methodName(request, response, next)— real method name. - For methods that set cookies: assert the third argument of
response.cookie(options object); usesinon.match.objector check forexpireswhen testing persistent sessions. - Transactions: Stub
pgConnection.startTransactionwith.resolves(t)and use the sametforcommit(t)/rollback(t). In assertions usetransactionClient(nottransaction). ForupdateByKey, keys are an array (e.g.["id_us"],['id_up']); payload may include fields fromrequest.session(e.g.id_last_updater_up). - Error path: ensure
next(error)is called with the correct error (original or rollback error if rollback rejects and controller does not wrap it). - Assert only externally observable behavior (responses and env calls), not implementation details.
Lib utilities (test/lib/*.test.ts)
- Use Node
assertorchai.expectfor pure functions; exhaustive yet concise. - Cover boundaries: empty arrays, null/undefined, formatting edge cases.
- For validators: valid value, invalid formats, null, undefined, empty, malformed.
- Stub only when the function uses time or randomness (e.g.
sinon.useFakeTimers()). - Import paths: From
test/lib/use../../src/...(two levels to project root). Fromtest/lib/notifications/use../../../src/...and../../../config/...(three levels). - For classes with private methods: call via
(instance as any).methodName(...); stub withsinon.stub(instance as any, 'methodName'). If the class has a single-argument constructor (e.g. options object), use that; method names are real (e.g.sendRequest), not prefixed with__.
Postgres models (test/model/postgres/*.test.ts or test/pg-models/*.test.ts)
- Mock connection with stubbed methods:
query,queryReturnFirst,queryPaged,insert,updateByKey,startTransaction,commit,rollbackas used. Use.resolves(t)forstartTransactionso the sametis used forcommit/rollback. - Stub return shapes to match the actual API:
queryPagedreturns{ totalRowCount, rows }or{ total, rows };queryreturns{ rows }. Use aliased column names in mock rows when the SQL usesAS. - For each method: assert correct
replacementsand optionallysinon.match({ sql: sinon.match(/select/), replacements: [...] }). UsetransactionClientwhen the method passes a transaction. - Simulate DB responses:
{ rows: [...] },{ rows: [] },null, rejection. Align stub with how the model uses the result (e.g.result?.rows ?? []or throw when null). - Error path must bubble the same Error instance.
- Transactional flows: assert order
startTransaction→ updates/inserts →commit; error branch triggersrollback.
Notifications / domain logic (test/lib/notifications/*.test.ts, test/lib/*-notifications.test.ts)
- Do not change production to inject mocks. Provide mock config so the notifications/pub-sub manager constructor does not throw (e.g.
config.pubSubOptionswithtopicIdandauthentication: { projectId, credentials }). - Add
mockNotificationManager.store.resolves()andmockNotificationManager.publish.resolves()so async flow completes. - Call private methods via
(notifications as any).methodName(...); usedescribe('methodName', ...). - Assert payload structure and invocation count on store/publish mocks. Include failure of underlying store and error propagation.
- Import paths: From
test/lib/notifications/use../../../src/...and../../../config/...(three levels).
External service classes (e.g. Bridge, Getstream)
- If the class constructor takes a single options object and has a private method like
sendRequest, stub withsinon.stub(instance as any, 'sendRequest'). - If the class needs DB/config/logger, provide a complete mock (e.g.
pgConnection.queryReturnFirst,config.getstream) so constructors do not throw. - Restore stubs in
afterEach:sinon.restore().
Export / batch utilities
- Use a minimal fake env (e.g. stubbed
pgConnection,pgModels) instead of a full real Environment when config is missing or incomplete. - For helpers with private methods, call via
(helper as any).methodName(...). - If file writes or streams occur, stub fs or stream constructors. Validate produced output (e.g. CSV) with a sample subset.
- Include an edge case for larger input (e.g. 1000 items) to ensure no throw and transformation called per item.
Assertion strategy
sinon.assert.calledOnce(stub),sinon.assert.calledWith(stub, expectedArgs...)sinon.matchfor partial object or regex match of SQL. ForpgConnection.query/updateByKey, usetransactionClientin expected args (nottransaction).- For query calls, assert the shape passed by the code: e.g.
{ sql, replacements }or{ sql, replacements, transactionClient }. updateByKey: keys are an array; payload may include session-derived fields.- For arrays and order-sensitive logic, use deep equality.
- For dates:
sinon.match.dateorinstanceof Date. - Session/cookie: assert the third argument of
response.cookie(options object); usesinon.match.objector checkexpiresfor persistent sessions.
Error assertion pattern (async)
try {
await subject.method(args);
assert.fail('Expected method to throw');
} catch (err) {
assert.strictEqual(err, expectedError); // or shape assertions
}
Edge cases checklist
- Null / undefined inputs
- Empty arrays / empty objects
- Boundary numerics: 0, -1 (if invalid), large numbers
- Missing required properties
- Duplicate entries (where uniqueness applies)
- Partial updates (optional fields omitted)
- Upstream failure (e.g. token generation, notification push)
Session and auth
When controller endpoints require auth:
- Stub
env.session.checkAuthenticationto return(req, res, next) => next(). - Test unauthenticated path only if the controller itself branches on session absence; otherwise rely on middleware coverage elsewhere.
Generated file behavior
- If ${testFile} exists: Merge new test blocks without duplicating existing method
describe. If the method block already exists, add only missing branches/cases. Do not remove or rename existing tests. - If ${testFile} does not exist: Generate full skeleton (imports, top-level describe, method-level describes, coverage template). Use
// TODOfor logic that cannot be inferred.
In all cases, update the real ${testFile} on disk to match the produced output.
Import paths
- From
test/ortest/controllers/ortest/lib/:../../src/...(two levels to project root). - From
test/lib/notifications/:../../../src/...and../../../config/...(three levels).
Use static imports where possible. If loading in a hook, require('../../src/lib/...') with tsx/cjs resolves correctly. Use the project’s shared types/constants package (e.g. HttpResponseStatus) when the codebase does.
Output format
Provide only the test file content inside a fenced ```ts or ```typescript block. When updating, show only new or changed blocks preceded by a brief comment // Added tests for <method>.
Parameter mapping
- ${targetCode}: Class or Class.method to test. Use the real method name. If only Class.method is supplied, generate tests for that method but keep the class-level and method-level
describehierarchy. - ${testFile}: Must end with
.test.ts. Drives import depth: two levels (../../) fromtest/,test/controllers/, ortest/lib/; three levels (../../../) fromtest/lib/notifications/. - ${sourceFile}: Optional; use to compute relative import path. Source under
src/; use.tsor no extension per project resolution. Do not use.mjs.
Quality gates (self-check)
- All new describe blocks follow the pattern.
- No references to removed or project-specific instruction files.
- Sinon assertions used consistently.
- No hardcoded secrets or environment leakage.
- Edge cases addressed.
- Tests run and pass after writing.
When information is ambiguous
Insert a // TODO: comment with a concise note (max 60 chars) rather than guessing behavior that cannot be inferred from the code.
Return only the test code per the instructions above — no extra commentary beyond mandated context lines and added-block comments.
END OF SKILL.
More from marco-meini/cursor
yn-be-developer-typescript
Best practices, coding conventions, and patterns for backend projects using TypeScript. Use when writing code, tests, or new features in TypeScript backends with src/, Express, PostgreSQL/MongoDB, and Mocha+tsx.
33api-http-test
Run realistic HTTP API tests against real endpoints with project-scoped TOML profiles and auth credentials. Use when the user wants to simulate real API calls, validate auth flows, debug status codes/payloads, or initialize API test config with `/api-http-test install`.
3openapi-doc-from-controller
Creates or updates one OpenAPI YAML fragment from a single controller method. Use when documenting API endpoints, adding OpenAPI path items from Express routes, or when the user asks to document an endpoint or update docs from controller code.
2