@mrciphersmith/keryx 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/cli.js +13362 -7078
- package/dist/core.js +11706 -11330
- package/package.json +1 -1
- package/src/gdskills/bundled/agents/go-code-auditor.md +1 -1
- package/src/gdskills/bundled/agents/python-code-auditor.md +1 -1
- package/src/gdskills/bundled/install-manifest.json +271 -4
- package/src/gdskills/bundled/rules/core/model-selection.mdc +51 -0
- package/src/gdskills/bundled/skills/review/review-jev-comments/SKILL.md +184 -0
- package/src/gdskills/bundled/skills/review/review-jev-docs/SKILL.md +189 -0
- package/src/gdskills/bundled/skills/review/review-jev-risk/SKILL.md +190 -0
- package/src/gdskills/bundled/skills/review/review-jev-rules/SKILL.md +267 -0
- package/src/gdskills/bundled/skills/review/review-jev-scenarios/SKILL.md +187 -0
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.detail.md +39 -0
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +1 -1
- package/src/gdskills/bundled/stacks/angular/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/angular/governance/eval.json +1751 -0
- package/src/gdskills/bundled/stacks/angular/governance/scout.json +32 -0
- package/src/gdskills/bundled/stacks/angular/pack.json +55 -0
- package/src/gdskills/bundled/stacks/angular/rules/coding-style.mdc +82 -0
- package/src/gdskills/bundled/stacks/angular/rules/patterns.mdc +84 -0
- package/src/gdskills/bundled/stacks/angular/rules/security.mdc +70 -0
- package/src/gdskills/bundled/stacks/angular/rules/testing.mdc +73 -0
- package/src/gdskills/bundled/stacks/angular/skills/angular-build-fix/SKILL.md +127 -0
- package/src/gdskills/bundled/stacks/angular/skills/angular-build-fix/evals.json +72 -0
- package/src/gdskills/bundled/stacks/angular/skills/angular-code-review/SKILL.md +98 -0
- package/src/gdskills/bundled/stacks/angular/skills/angular-code-review/evals.json +73 -0
- package/src/gdskills/bundled/stacks/angular/skills/angular-implementation/SKILL.md +112 -0
- package/src/gdskills/bundled/stacks/angular/skills/angular-implementation/evals.json +74 -0
- package/src/gdskills/bundled/stacks/angular/skills/angular-testing/SKILL.md +102 -0
- package/src/gdskills/bundled/stacks/angular/skills/angular-testing/evals.json +71 -0
- package/src/gdskills/bundled/stacks/mobx/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/mobx/governance/eval.json +904 -0
- package/src/gdskills/bundled/stacks/mobx/governance/scout.json +18 -0
- package/src/gdskills/bundled/stacks/mobx/pack.json +28 -0
- package/src/gdskills/bundled/stacks/mobx/rules/coding-style.mdc +91 -0
- package/src/gdskills/bundled/stacks/mobx/rules/patterns.mdc +122 -0
- package/src/gdskills/bundled/stacks/mobx/rules/security.mdc +56 -0
- package/src/gdskills/bundled/stacks/mobx/rules/testing.mdc +63 -0
- package/src/gdskills/bundled/stacks/mobx/skills/mobx-observable-testing/SKILL.md +124 -0
- package/src/gdskills/bundled/stacks/mobx/skills/mobx-observable-testing/evals.json +73 -0
- package/src/gdskills/bundled/stacks/mobx/skills/mobx-store-implementation/SKILL.md +149 -0
- package/src/gdskills/bundled/stacks/mobx/skills/mobx-store-implementation/evals.json +74 -0
- package/src/gdskills/bundled/stacks/nestjs/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/nestjs/governance/eval.json +1308 -0
- package/src/gdskills/bundled/stacks/nestjs/governance/scout.json +34 -0
- package/src/gdskills/bundled/stacks/nestjs/pack.json +53 -0
- package/src/gdskills/bundled/stacks/nestjs/rules/coding-style.mdc +70 -0
- package/src/gdskills/bundled/stacks/nestjs/rules/patterns.mdc +83 -0
- package/src/gdskills/bundled/stacks/nestjs/rules/security.mdc +73 -0
- package/src/gdskills/bundled/stacks/nestjs/rules/testing.mdc +69 -0
- package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-build-fix/SKILL.md +157 -0
- package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-build-fix/evals.json +70 -0
- package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-implementation/SKILL.md +129 -0
- package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-implementation/evals.json +71 -0
- package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-testing/SKILL.md +143 -0
- package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-testing/evals.json +69 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/governance/eval.json +2413 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/governance/scout.json +42 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/pack.json +42 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/rules/coding-style.mdc +69 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/rules/patterns.mdc +88 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/rules/security.mdc +72 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/rules/testing.mdc +64 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-build-fix/SKILL.md +147 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-build-fix/evals.json +75 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-code-review/SKILL.md +118 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-code-review/evals.json +76 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-implementation/SKILL.md +135 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-implementation/evals.json +78 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-testing/SKILL.md +116 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-testing/evals.json +75 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-upgrade-migration/SKILL.md +134 -0
- package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-upgrade-migration/evals.json +76 -0
- package/src/gdskills/bundled/stacks/vue/agent-refs.json +4 -0
- package/src/gdskills/bundled/stacks/vue/governance/eval.json +2215 -0
- package/src/gdskills/bundled/stacks/vue/governance/scout.json +42 -0
- package/src/gdskills/bundled/stacks/vue/pack.json +42 -0
- package/src/gdskills/bundled/stacks/vue/rules/coding-style.mdc +73 -0
- package/src/gdskills/bundled/stacks/vue/rules/patterns.mdc +84 -0
- package/src/gdskills/bundled/stacks/vue/rules/security.mdc +60 -0
- package/src/gdskills/bundled/stacks/vue/rules/testing.mdc +69 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue-build-fix/SKILL.md +137 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue-build-fix/evals.json +72 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue-code-review/SKILL.md +120 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue-code-review/evals.json +71 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue-implementation/SKILL.md +122 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue-implementation/evals.json +72 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue-testing/SKILL.md +115 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue-testing/evals.json +72 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue2-to-vue3-migration/SKILL.md +135 -0
- package/src/gdskills/bundled/stacks/vue/skills/vue2-to-vue3-migration/evals.json +71 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nestjs-implementation
|
|
3
|
+
description: "Use when building or extending a NestJS module, controller, guard, interceptor, pipe, or exception filter -- choosing provider scope and registering a cross-cutting concern globally via APP_GUARD/APP_INTERCEPTOR/APP_PIPE/APP_FILTER, and keeping controllers thin with business rules delegated downstream. Scoped to authoring new behavior in an app that already boots; excludes selecting field-level checks on a request body, a finished-diff review pass, and diagnosing why an already-written module stops the app from starting."
|
|
4
|
+
triggers:
|
|
5
|
+
- "add a new NestJS module for this feature"
|
|
6
|
+
- "wire this provider into the NestJS DI container"
|
|
7
|
+
- "implement a NestJS guard for this route"
|
|
8
|
+
- "register a global interceptor in NestJS"
|
|
9
|
+
- "create an exception filter for this NestJS app"
|
|
10
|
+
- "should this NestJS provider be request-scoped"
|
|
11
|
+
- "move this logic out of the controller into a dedicated provider"
|
|
12
|
+
metadata:
|
|
13
|
+
origin: authored
|
|
14
|
+
category: implement
|
|
15
|
+
version: "1.0.0"
|
|
16
|
+
compatible_harnesses: "claude,codex,cursor,zed,opencode"
|
|
17
|
+
license: "MIT"
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# NestJS implementation (modules, providers, DI, request pipeline)
|
|
21
|
+
|
|
22
|
+
Build or extend a NestJS module: controllers, services, and the
|
|
23
|
+
guards/interceptors/pipes/filters that make up the request pipeline around
|
|
24
|
+
them, wired through Nest's dependency injection. See `rules/patterns.mdc`
|
|
25
|
+
for the module/provider design this skill applies, `rules/coding-style.mdc`
|
|
26
|
+
for naming and typing, and `rules/security.mdc` for the AuthN/AuthZ and
|
|
27
|
+
input-validation shape a new endpoint must satisfy. DTO decorator choice is
|
|
28
|
+
the existing `nestjs-dto.mdc` rule's ground, not repeated here.
|
|
29
|
+
|
|
30
|
+
## Workflow
|
|
31
|
+
|
|
32
|
+
### Step 1: Discover the project's own conventions
|
|
33
|
+
|
|
34
|
+
Read the nearest existing feature module (a sibling `<feature>/` directory
|
|
35
|
+
under `src/`) before writing a new one. Match its file-splitting convention
|
|
36
|
+
(one module per feature vs. sub-modules), its DTO/validation setup, its
|
|
37
|
+
exception-filter and guard registration style (global via `APP_GUARD`/
|
|
38
|
+
`APP_FILTER` vs. per-controller `@UseGuards`), and its provider-scope
|
|
39
|
+
choices. Do not introduce a different pattern in one module without a
|
|
40
|
+
concrete reason stated in the change.
|
|
41
|
+
|
|
42
|
+
### Step 2: Design the module shape
|
|
43
|
+
|
|
44
|
+
- Identify what the new module owns: which controller(s), which
|
|
45
|
+
service(s), which providers a service needs (repository, HTTP client,
|
|
46
|
+
config).
|
|
47
|
+
- Decide what the module needs to `import` from other feature modules, and
|
|
48
|
+
what it needs to `export` if another module will consume one of its
|
|
49
|
+
providers.
|
|
50
|
+
- Default every new provider to the framework's default (Singleton) scope
|
|
51
|
+
unless it genuinely needs per-request state — see `rules/patterns.mdc`
|
|
52
|
+
on `Scope.REQUEST`'s real cost.
|
|
53
|
+
|
|
54
|
+
### Step 3: Implement the controller-service split
|
|
55
|
+
|
|
56
|
+
- The controller method: validate input via a typed DTO class (see the
|
|
57
|
+
existing `nestjs-dto.mdc` rule for the decorator set), call exactly one
|
|
58
|
+
service method for the actual work, and shape the response.
|
|
59
|
+
- The service method: an explicit return type, business logic, and a typed
|
|
60
|
+
NestJS `HttpException` subclass (or a domain error the controller/filter
|
|
61
|
+
translates) on failure — never a controller that reaches into a
|
|
62
|
+
repository directly (see `rules/patterns.mdc`, "Controllers vs.
|
|
63
|
+
services").
|
|
64
|
+
|
|
65
|
+
### Step 4: Add cross-cutting concerns at the right layer
|
|
66
|
+
|
|
67
|
+
- A concern that applies to (nearly) every route in the app (auth, request
|
|
68
|
+
logging, response shaping) goes in a guard/interceptor/pipe registered
|
|
69
|
+
globally as an `APP_GUARD`/`APP_INTERCEPTOR`/`APP_PIPE`/`APP_FILTER`
|
|
70
|
+
provider in a module's `providers` array, not `app.useGlobalGuards()` on
|
|
71
|
+
the bootstrapped instance -- the `APP_*` token form keeps the provider
|
|
72
|
+
inside Nest's own DI graph so it can inject other providers (e.g. a
|
|
73
|
+
guard that injects `Reflector` or a `UsersService`).
|
|
74
|
+
- A concern scoped to one controller or route goes on that controller/
|
|
75
|
+
method with `@UseGuards()`/`@UseInterceptors()`/`@UsePipes()`.
|
|
76
|
+
- When a global guard protects most routes, mark the deliberate exceptions
|
|
77
|
+
with a custom decorator backed by `SetMetadata` and read via `Reflector`
|
|
78
|
+
in the guard (e.g. a `@Public()` decorator) rather than leaving some
|
|
79
|
+
controllers undecorated.
|
|
80
|
+
|
|
81
|
+
### Step 5: Verify
|
|
82
|
+
|
|
83
|
+
Run the project's own build/type-check/test scripts (see Verification
|
|
84
|
+
below) and confirm the new module compiles into the app's DI graph with no
|
|
85
|
+
`UnknownDependenciesException`/circular-dependency warning at startup --
|
|
86
|
+
not just that `tsc` is clean.
|
|
87
|
+
|
|
88
|
+
## Rules
|
|
89
|
+
|
|
90
|
+
- Follow `rules/coding-style.mdc` for naming, file-per-role layout, and
|
|
91
|
+
typing; `rules/patterns.mdc` for module/provider/guard design; and
|
|
92
|
+
`rules/security.mdc` for what a new endpoint's validation and auth must
|
|
93
|
+
cover before it is considered done.
|
|
94
|
+
- ALWAYS keep the controller thin: parse/validate, delegate to one service
|
|
95
|
+
call, shape the response.
|
|
96
|
+
- ALWAYS register a cross-cutting concern once (globally via an `APP_*`
|
|
97
|
+
token, or via a reusable decorator), never copy-pasted into every
|
|
98
|
+
controller that needs it.
|
|
99
|
+
- NEVER inject a `REQUEST`-scoped or `TRANSIENT` provider into a Singleton
|
|
100
|
+
provider -- it silently makes the Singleton request-scoped too (Nest
|
|
101
|
+
propagates scope up the injection graph) or throws at resolution time,
|
|
102
|
+
and either way defeats the point of a Singleton service.
|
|
103
|
+
- NEVER put a repository/ORM call directly in a controller method.
|
|
104
|
+
|
|
105
|
+
## Red Flags
|
|
106
|
+
|
|
107
|
+
| Rationalization | Why it is wrong |
|
|
108
|
+
|---|---|
|
|
109
|
+
| "I'll just call the repository from the controller, it's one line" | It's untestable without the HTTP stack and duplicates the service's job the next time this logic is needed elsewhere |
|
|
110
|
+
| "I'll decorate every controller with this guard instead of making it global" | Any new controller anyone adds later silently starts unprotected; a global `APP_GUARD` with an explicit `@Public()` opt-out fails safe instead |
|
|
111
|
+
| "This provider doesn't need DI, I'll just `new` it inline in the service" | It bypasses testability via `overrideProvider` and any lifecycle hooks (`OnModuleInit`/`OnModuleDestroy`) the class relies on |
|
|
112
|
+
| "I'll make this provider REQUEST-scoped just in case" | Request scope re-instantiates the whole injection subtree per request; only reach for it when per-request state is a genuine requirement |
|
|
113
|
+
|
|
114
|
+
## Verification
|
|
115
|
+
|
|
116
|
+
Do not report the implementation done until all of the following hold:
|
|
117
|
+
|
|
118
|
+
- The project's build command (`nest build` or its `package.json`
|
|
119
|
+
equivalent) and `tsc --noEmit` both exit 0.
|
|
120
|
+
- The app boots locally (or the project's e2e bootstrap test passes) with
|
|
121
|
+
no `UnknownDependenciesException`, circular-dependency warning, or
|
|
122
|
+
missing-export error at startup.
|
|
123
|
+
- Every new/changed controller method that accepts user input has a
|
|
124
|
+
validated DTO and, where the endpoint should not be public, a guard --
|
|
125
|
+
either explicit or inherited from a global `APP_GUARD`.
|
|
126
|
+
- No business logic or direct repository/ORM call was added to a
|
|
127
|
+
controller.
|
|
128
|
+
- `git status` shows changes confined to the module(s) the feature
|
|
129
|
+
actually touches.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"triggers": {
|
|
3
|
+
"positive": [
|
|
4
|
+
"We're adding an invoices feature to our NestJS API -- I need the module scaffolding: a controller, a service, and the provider wired up.",
|
|
5
|
+
"I need to wire OrdersService into the DI container for this new module",
|
|
6
|
+
"Implement a NestJS guard that checks the JWT and only allows admins through",
|
|
7
|
+
"Every response from our NestJS app should get its duration logged automatically -- I need an interceptor wired in globally rather than per-controller.",
|
|
8
|
+
"Right now if anything throws in our NestJS app, the client gets the raw stack trace back in the response -- I want every uncaught error across the whole app to come back as a clean, generic error instead.",
|
|
9
|
+
"A NestJS provider I'm writing reads something off the incoming request -- should that make it request-scoped, or is the default singleton still fine?",
|
|
10
|
+
"Move this business logic out of the controller and into a proper service method"
|
|
11
|
+
],
|
|
12
|
+
"negative": [
|
|
13
|
+
"Review this NestJS PR for missing DTO validation and N+1 queries in the repository layer",
|
|
14
|
+
"Fix this NestJS build error: Nest can't resolve dependencies of UsersService",
|
|
15
|
+
"Write @nestjs/testing unit tests for this NestJS service with a mocked repository",
|
|
16
|
+
"Add class-validator decorators to this DTO for the new signup endpoint",
|
|
17
|
+
"Implement a React component that displays the user's profile card",
|
|
18
|
+
"Fix this tsc module resolution error in a plain Node.js script with no framework"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"scenarios": [
|
|
22
|
+
{
|
|
23
|
+
"id": "global-guard-with-public-escape-hatch",
|
|
24
|
+
"prompt": "I want to protect most routes in my NestJS app with a JWT auth guard, but let a few endpoints stay public. What's the right way to set this up?",
|
|
25
|
+
"strictness": "high",
|
|
26
|
+
"expected_behavior": [
|
|
27
|
+
{
|
|
28
|
+
"grader": "judge",
|
|
29
|
+
"rubric": "A correct answer registers the auth guard globally as an APP_GUARD provider (not app.useGlobalGuards() on the bootstrapped instance) so it stays inside Nest's DI graph, and adds a custom metadata decorator (e.g. a Public() decorator built with SetMetadata) that the guard checks via Reflector to let specific routes opt out, rather than protecting routes one controller at a time.",
|
|
30
|
+
"pass_criteria": [
|
|
31
|
+
"Names registering the guard globally via the APP_GUARD provider token in a module's providers array, not app.useGlobalGuards() called on the app instance",
|
|
32
|
+
"Names creating a custom decorator (e.g. @Public()) built with SetMetadata, and reading that metadata inside the guard via Reflector to allow the exception"
|
|
33
|
+
],
|
|
34
|
+
"fail_criteria": [
|
|
35
|
+
"Recommends decorating every protected controller individually with @UseGuards(...) instead of registering the guard globally -- naming @UseGuards for the few endpoints that need controller-specific behavior on top of the global guard is not this failure, only proposing it as the whole mechanism for making most routes protected"
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"calibration": {
|
|
40
|
+
"known_right": "Register the guard globally so it applies to every route by default, then carve out the public ones explicitly. Register it as an APP_GUARD provider (not app.useGlobalGuards() in main.ts) so it stays part of Nest's DI graph and can inject things like Reflector or a UsersService:\n\n@Module({\n providers: [{ provide: APP_GUARD, useClass: JwtAuthGuard }],\n})\nexport class AppModule {}\n\nThen add a metadata-based escape hatch: a decorator built on SetMetadata, e.g.\n\nexport const IS_PUBLIC_KEY = 'isPublic';\nexport const Public = () => SetMetadata(IS_PUBLIC_KEY, true);\n\nand have JwtAuthGuard read that metadata with Reflector before enforcing auth:\n\ncanActivate(context: ExecutionContext) {\n const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [\n context.getHandler(),\n context.getClass(),\n ]);\n if (isPublic) return true;\n // ... normal auth check\n}\n\nNow every new controller is protected by default, and only routes explicitly marked @Public() opt out -- adding a new controller and forgetting to guard it can't silently leave it open.",
|
|
41
|
+
"known_wrong": "Easiest way: just add @UseGuards(JwtAuthGuard) to every controller that should be protected, and skip it on the couple of public ones. No need to touch app.module.ts at all, and you don't need any Reflector metadata stuff -- just remember which controllers need the decorator.",
|
|
42
|
+
"vague": "Set the guard up so it applies to most routes by default, and give the public ones a way to opt out of it.",
|
|
43
|
+
"subtle_wrong": "Call app.useGlobalGuards(new JwtAuthGuard()) in main.ts right after creating the app, that applies it to every route without touching each controller. For the public ones, just check the request path against an allow-list inside the guard's canActivate instead of a decorator, since that's simpler than wiring up SetMetadata."
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "request-scoped-into-singleton",
|
|
48
|
+
"prompt": "I have a small provider that reads the current user off the request (it's REQUEST-scoped). I want to inject it directly into UsersService, which is a plain singleton used all over the app. Is that fine to do as-is?",
|
|
49
|
+
"strictness": "high",
|
|
50
|
+
"expected_behavior": [
|
|
51
|
+
{
|
|
52
|
+
"grader": "judge",
|
|
53
|
+
"rubric": "A correct answer explains that Nest propagates provider scope up the dependency graph, so injecting a REQUEST-scoped provider directly into UsersService makes UsersService itself request-scoped -- re-instantiated per request along with everything that depends on it -- and recommends not injecting the request-scoped dependency straight into the broadly-shared singleton, instead passing the needed value explicitly as an argument from a layer that is already request-scoped, or isolating the request-scoped need into its own narrow provider.",
|
|
54
|
+
"pass_criteria": [
|
|
55
|
+
"States that injecting the REQUEST-scoped provider into UsersService makes UsersService (and its own consumers) request-scoped too, because Nest propagates scope up the injection graph -- not an isolated, contained change",
|
|
56
|
+
"Recommends against injecting the request-scoped provider directly into the shared singleton, proposing instead to pass the needed value explicitly (e.g. as a method argument from the controller) or to isolate the request-scoped logic in a small dedicated provider instead of UsersService"
|
|
57
|
+
],
|
|
58
|
+
"fail_criteria": [
|
|
59
|
+
"States or implies it is fine to inject the REQUEST-scoped provider into UsersService with no mention of the scope-propagation consequence for UsersService and its own consumers"
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
"calibration": {
|
|
64
|
+
"known_right": "No -- doing that directly will make UsersService itself request-scoped. Nest propagates provider scope up the dependency graph: once UsersService depends on a REQUEST-scoped provider, Nest has to re-instantiate UsersService (and everything downstream that injects UsersService) on every request instead of once. Since UsersService is used all over the app as a shared singleton, that's a much bigger behavioral and performance change than it looks like from the diff. Better options: pass the current user in explicitly as a method argument from the controller (which already has it from the guard-populated request), rather than injecting the REQUEST-scoped provider into UsersService at all; or, if several places genuinely need the current-user-aware behavior, pull that slice of logic into its own small REQUEST-scoped provider instead of adding the dependency to the shared singleton.",
|
|
65
|
+
"known_wrong": "Yeah that's fine, just inject it in the constructor like any other provider -- Nest's DI handles the wiring automatically so there's nothing special to worry about with the scopes being different.",
|
|
66
|
+
"vague": "Be careful with mixing scopes like that, it can cause unexpected behavior, so you might want to rethink the design a bit.",
|
|
67
|
+
"subtle_wrong": "It'll work at runtime since Nest resolves the dependency graph either way, but to keep things clean I'd just mark UsersService as @Injectable({ scope: Scope.REQUEST }) too so the scopes match up consistently -- that avoids any DI errors and keeps the code simple."
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nestjs-testing
|
|
3
|
+
description: "Use when writing or debugging a NestJS test built through Nest's own testing-module builder: a unit test that swaps a provider's real dependency (a repository, an HTTP client) for a fake or mock double so the unit under test runs in isolation, or an e2e test that boots a real Nest application and drives it with supertest through the actual guard/pipe/filter pipeline. Scoped to a test compiled through Nest's own testing module; excludes picking field-level checks for a request body, authoring the feature itself, and a framework-agnostic unit test that never touches Nest's module system."
|
|
4
|
+
triggers:
|
|
5
|
+
- "write unit tests for this NestJS provider"
|
|
6
|
+
- "replace a provider's real dependency with a fake in a NestJS unit test"
|
|
7
|
+
- "write an e2e test for this NestJS endpoint with supertest"
|
|
8
|
+
- "exercise this NestJS guard in isolation with an overridden Reflector"
|
|
9
|
+
- "the NestJS test module won't compile because a provider it needs is missing"
|
|
10
|
+
- "my NestJS e2e test suite leaves the process hanging after it finishes"
|
|
11
|
+
metadata:
|
|
12
|
+
origin: authored
|
|
13
|
+
category: test
|
|
14
|
+
version: "1.0.0"
|
|
15
|
+
compatible_harnesses: "claude,codex,cursor,zed,opencode"
|
|
16
|
+
license: "MIT"
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# NestJS testing (@nestjs/testing unit and e2e specs)
|
|
20
|
+
|
|
21
|
+
Write or fix a NestJS unit or end-to-end test using `@nestjs/testing`'s
|
|
22
|
+
module-compiled style: build the unit under test through a real (or
|
|
23
|
+
overridden) Nest DI graph rather than hand-constructing it. See
|
|
24
|
+
`rules/testing.mdc` for the full layout, mocking, and determinism
|
|
25
|
+
conventions this skill applies.
|
|
26
|
+
|
|
27
|
+
## Workflow
|
|
28
|
+
|
|
29
|
+
### Step 1: Determine whether this is a unit or e2e test
|
|
30
|
+
|
|
31
|
+
- **Unit**: exercising one provider (service, guard, interceptor, pipe) in
|
|
32
|
+
isolation, with its dependencies replaced by test doubles. Lives beside
|
|
33
|
+
the source as `<name>.spec.ts`.
|
|
34
|
+
- **E2E**: exercising a real HTTP request through the full pipeline
|
|
35
|
+
(guards, pipes, interceptors, filters, controller, service). Lives under
|
|
36
|
+
`test/` as `<feature>.e2e-spec.ts`.
|
|
37
|
+
|
|
38
|
+
If unsure which the request wants, default to a unit test for a single
|
|
39
|
+
provider/class and an e2e test for "does this endpoint work end to end".
|
|
40
|
+
|
|
41
|
+
### Step 2: Build the testing module
|
|
42
|
+
|
|
43
|
+
Two equally valid ways to get a test double in, depending on how the module
|
|
44
|
+
is assembled -- pick one, never both for the same provider:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// (a) hand-assembled providers array (the common case for a focused unit
|
|
48
|
+
// test): declare the mock directly, no override needed -- you already
|
|
49
|
+
// control the whole providers list.
|
|
50
|
+
const moduleRef = await Test.createTestingModule({
|
|
51
|
+
providers: [UsersService, { provide: UsersRepository, useValue: mockRepo }],
|
|
52
|
+
}).compile();
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// (b) importing a real module you don't want to hand-reassemble (an
|
|
57
|
+
// e2e-style compile): `.overrideProvider(Token).useValue(...)` (or
|
|
58
|
+
// `.useFactory`/`.useClass`) swaps ONE provider UsersModule already
|
|
59
|
+
// declares, without rewriting the whole module's provider list yourself.
|
|
60
|
+
const moduleRef = await Test.createTestingModule({ imports: [UsersModule] })
|
|
61
|
+
.overrideProvider(UsersRepository)
|
|
62
|
+
.useValue(mockRepo)
|
|
63
|
+
.compile();
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Either way: never `new` the class under test directly with hand-built fakes
|
|
67
|
+
when the test needs to prove the module's own DI wiring (exports,
|
|
68
|
+
provider registration) actually resolves -- that's what
|
|
69
|
+
`Test.createTestingModule` is for, and `new` skips it entirely. Never
|
|
70
|
+
monkey-patch the compiled module's internals either. The same
|
|
71
|
+
`.overrideGuard()`/`.overrideInterceptor()`/`.overridePipe()`/
|
|
72
|
+
`.overrideFilter()` methods exist for testing a controller/module with one
|
|
73
|
+
of those swapped out.
|
|
74
|
+
|
|
75
|
+
### Step 3 (unit): Get the instance and assert
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
const service = moduleRef.get(UsersService);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Assert against the service's own return value/thrown exception. Mock only
|
|
82
|
+
at the injected-provider boundary (repository, HTTP client) -- not by
|
|
83
|
+
stubbing a private method on the class under test.
|
|
84
|
+
|
|
85
|
+
### Step 3 (e2e): Boot a real application and drive it with supertest
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const app = moduleRef.createNestApplication();
|
|
89
|
+
await app.init();
|
|
90
|
+
// ...
|
|
91
|
+
await request(app.getHttpServer()).get('/users').expect(200);
|
|
92
|
+
// ...
|
|
93
|
+
await app.close();
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Assert on HTTP status and response shape via `supertest`, not by reaching
|
|
97
|
+
back into the service's return value -- the point of an e2e spec is that
|
|
98
|
+
guards/pipes/filters actually ran. Always close the app (`afterAll(() =>
|
|
99
|
+
app.close())`) to release DB connections and timers.
|
|
100
|
+
|
|
101
|
+
### Step 4: Verify
|
|
102
|
+
|
|
103
|
+
Run the project's unit and e2e test scripts (see Verification below) and
|
|
104
|
+
confirm the new/changed spec fails without the fix and passes with it, and
|
|
105
|
+
that no other spec started flaking or hanging (a common symptom of a
|
|
106
|
+
missing `app.close()`).
|
|
107
|
+
|
|
108
|
+
## Rules
|
|
109
|
+
|
|
110
|
+
- Follow `rules/testing.mdc` for layout, mocking boundary, and determinism.
|
|
111
|
+
- ALWAYS build the unit under test through `Test.createTestingModule(...)
|
|
112
|
+
.compile()`, not a hand-constructed instance.
|
|
113
|
+
- ALWAYS call `app.close()` in `afterAll` for every e2e spec that calls
|
|
114
|
+
`createNestApplication()`.
|
|
115
|
+
- NEVER delete or `.skip()` a failing spec to reach a green suite --
|
|
116
|
+
diagnose whether the source or the test's expectation is stale (same
|
|
117
|
+
rule as any build-fix skill) and fix that instead.
|
|
118
|
+
- NEVER assert against a mocked provider's internal call count as the
|
|
119
|
+
entire test -- assert the actual behavior (return value, thrown
|
|
120
|
+
exception, HTTP response) the mock enables you to isolate.
|
|
121
|
+
|
|
122
|
+
## Red Flags
|
|
123
|
+
|
|
124
|
+
| Rationalization | Why it is wrong |
|
|
125
|
+
|---|---|
|
|
126
|
+
| "I'll just `new UsersService(mockRepo)` directly, it's simpler than the testing module" | Fine for a pure unit test of the service's own logic (NestJS's own docs show this too) -- wrong specifically when the test needs to prove the MODULE's own DI wiring (exports, provider registration, guard/pipe pipeline) actually resolves, since `new` bypasses that wiring entirely and can pass while the real module would fail to compile |
|
|
127
|
+
| "I'll stub the private method that calls the repository instead of mocking the repository" | Reaches inside the unit under test instead of mocking its actual dependency boundary; couples the test to an implementation detail |
|
|
128
|
+
| "This e2e spec is slow because of app.init()/app.close(), I'll skip close() to speed it up" | Leaks open DB connections/timers across spec files; a later suite hanging or flaking is the actual cost |
|
|
129
|
+
| "This test keeps failing after my change, I'll just skip it for now" | Hides a real regression or a stale test expectation; determine which one it is and fix that, don't silence the signal |
|
|
130
|
+
|
|
131
|
+
## Verification
|
|
132
|
+
|
|
133
|
+
Do not report the test work done until all of the following hold:
|
|
134
|
+
|
|
135
|
+
- The project's unit test script and (when e2e specs were touched) its
|
|
136
|
+
`test:e2e` script both pass.
|
|
137
|
+
- Every `createNestApplication()` in a touched e2e spec has a matching
|
|
138
|
+
`app.close()`.
|
|
139
|
+
- No dependency of the unit under test was replaced by monkey-patching or
|
|
140
|
+
hand-construction instead of `Test.createTestingModule`/`override*`.
|
|
141
|
+
- No spec was deleted or `.skip()`-ed to reach green.
|
|
142
|
+
- `git status` shows changes confined to the spec file(s) and, if the test
|
|
143
|
+
exposed a real bug, the source file(s) the fix required.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"triggers": {
|
|
3
|
+
"positive": [
|
|
4
|
+
"I need a unit test for OrdersService that swaps in a fake for its Repository dependency instead of hitting the real database",
|
|
5
|
+
"How do I replace the payment gateway provider with a stub when testing this NestJS module, without making real API calls",
|
|
6
|
+
"I need to drive the real POST /users route through supertest against a fully bootstrapped NestJS app instance, hitting actual guards and pipes instead of mocking the controller directly.",
|
|
7
|
+
"I want to unit test a NestJS route guard that checks role metadata, but without booting the whole app -- just fake out whatever it pulls from Reflector.",
|
|
8
|
+
"My NestJS test throws saying a provider is missing when the test module tries to compile -- what's wrong with my test setup",
|
|
9
|
+
"My NestJS e2e test suite hangs and never exits after the run finishes, even though every test passes"
|
|
10
|
+
],
|
|
11
|
+
"negative": [
|
|
12
|
+
"Fix this NestJS build error: Nest can't resolve dependencies of UsersService",
|
|
13
|
+
"Add a new NestJS module with a controller and service for invoices",
|
|
14
|
+
"Write plain Jest tests for a date-formatting utility function with no NestJS dependencies",
|
|
15
|
+
"Review this NestJS PR for missing DTO validation and N+1 queries",
|
|
16
|
+
"Write Vue component tests using Vue Test Utils and Vitest",
|
|
17
|
+
"Add class-validator decorators to this DTO for the new signup endpoint"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"scenarios": [
|
|
21
|
+
{
|
|
22
|
+
"id": "mock-dependency-boundary",
|
|
23
|
+
"prompt": "I'm writing a unit test for UsersService, which depends on UsersRepository. What's the right way to set this up with @nestjs/testing?",
|
|
24
|
+
"strictness": "high",
|
|
25
|
+
"expected_behavior": [
|
|
26
|
+
{
|
|
27
|
+
"grader": "judge",
|
|
28
|
+
"rubric": "A correct answer replaces UsersRepository with a test double -- via a { provide: UsersRepository, useValue/useFactory/useClass } entry in a hand-assembled Test.createTestingModule providers array, or via .overrideProvider(UsersRepository) on a module built from imports -- mocking at the injected-dependency boundary rather than stubbing an internal method of UsersService itself. A plain `new UsersService(fakeRepo)` is also an acceptable answer for testing the service's own logic in isolation, since NestJS's own docs show this pattern too, as long as UsersRepository itself (not an internal method) is what gets faked.",
|
|
29
|
+
"pass_criteria": [
|
|
30
|
+
"States that UsersRepository should be replaced with a test double -- either declared directly in a Test.createTestingModule providers array, via .overrideProvider(UsersRepository), or passed as a plain fake to `new UsersService(fakeRepo)` -- rather than by stubbing a private/internal method on UsersService"
|
|
31
|
+
],
|
|
32
|
+
"fail_criteria": [
|
|
33
|
+
"Recommends stubbing or spying on a private/internal method of UsersService itself (e.g. the method that calls the repository) instead of faking UsersRepository at its own boundary, regardless of which construction approach is used"
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"calibration": {
|
|
38
|
+
"known_right": "Build UsersService with UsersRepository replaced by a fake, mocking at the repository boundary rather than reaching inside UsersService itself:\n\nconst moduleRef = await Test.createTestingModule({\n providers: [\n UsersService,\n { provide: UsersRepository, useValue: mockRepo },\n ],\n}).compile();\n\nconst service = moduleRef.get(UsersService);\n\nThat exercises the same DI resolution the real app uses. A plain `const service = new UsersService(mockRepo);` works just as well here too, since this is testing UsersService's own logic in isolation, not the module's DI wiring -- either way, the fake stands in for UsersRepository, not for a method inside UsersService. Then assert against UsersService's own return values or thrown exceptions using the mock's canned responses.",
|
|
39
|
+
"known_wrong": "Just call the real UsersService with the real UsersRepository wired up as-is, and let the test hit whatever backing store UsersRepository actually uses in this environment -- no mock needed, it'll just use the same repository the app does at runtime.",
|
|
40
|
+
"vague": "Set the test up so UsersRepository is replaced with something fake, so you can test UsersService's logic on its own.",
|
|
41
|
+
"subtle_wrong": "Use Test.createTestingModule with just UsersService in providers, then inside the test call jest.spyOn(service, 'findUserRecord') (the private method that talks to the repository) and mock its return value directly -- that way you don't need to bother wiring up a UsersRepository provider override at all."
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": "e2e-hanging-suite",
|
|
46
|
+
"prompt": "My NestJS e2e test suite finishes but the process hangs and I have to Ctrl+C it. What's the likely cause and the right fix?",
|
|
47
|
+
"strictness": "high",
|
|
48
|
+
"expected_behavior": [
|
|
49
|
+
{
|
|
50
|
+
"grader": "judge",
|
|
51
|
+
"rubric": "A correct answer identifies a missing app.close() call as the likely cause -- an e2e spec that calls moduleRef.createNestApplication()/app.init() but never closes the app leaks open resources like DB connections and timers -- and fixes it by calling app.close() in an afterAll hook, rather than forcing the test runner to exit.",
|
|
52
|
+
"pass_criteria": [
|
|
53
|
+
"Identifies a missing app.close() (after createNestApplication()/app.init()) as the likely cause of the hang, naming that it leaks open resources such as DB connections or timers",
|
|
54
|
+
"States the concrete fix: call app.close() inside an afterAll hook for every e2e spec that created the app"
|
|
55
|
+
],
|
|
56
|
+
"fail_criteria": [
|
|
57
|
+
"Recommends forcing the test runner to exit (e.g. a force-exit flag or calling process.exit in the test) as the fix instead of closing the application properly"
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
"calibration": {
|
|
62
|
+
"known_right": "That's almost always a missing app.close(). An e2e spec that does moduleRef.createNestApplication() and app.init() but never closes the app leaves open handles behind -- DB connections, timers, anything the app opened during bootstrap -- and the test runner can't exit cleanly because those handles are still alive. Fix it by closing the app once the spec file is done:\n\nafterAll(async () => {\n await app.close();\n});\n\nCheck every e2e spec file that calls createNestApplication() has a matching close in its afterAll, not just the one you're currently debugging -- it's easy for one spec file to have it and a newer one to be missing it.",
|
|
63
|
+
"known_wrong": "That's just Jest being slow to exit sometimes, nothing to actually fix -- add --forceExit to the test script in package.json and it'll stop hanging. A lot of NestJS projects run with forceExit anyway.",
|
|
64
|
+
"vague": "Something is probably still open after the tests finish, so you'd want to make sure everything gets cleaned up properly at the end.",
|
|
65
|
+
"subtle_wrong": "Likely an open DB connection somewhere. Rather than chasing down which spec is missing the cleanup, just add --detectOpenHandles --forceExit to the e2e test script -- forceExit will make the process exit regardless, and detectOpenHandles at least tells you what's open if you want to look into it later."
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agents": [],
|
|
3
|
+
"note": "honest gate (flow 318, T13): every skill fails the trigger-accuracy check with real same-pack/cross-pack collisions (falsePositive implementation 3/6, testing 4/6, code-review 3/6, build-fix 1/6, upgrade-migration 3/6) -- see src/gdskills/bundled/stacks/nextjs-nuxt/governance/eval.json and the flow 318 journal for the root cause (the same-pack-siblings-never-block selection rule against a 5-skill meta-framework pack sharing heavy vocabulary, plus the negation-unaware scorer indexing this pack's own disclaimer clauses as positive vocabulary). Stays experimental; no generated agent pair."
|
|
4
|
+
}
|