nestforge-generator 0.1.1 → 0.3.0

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.
Files changed (35) hide show
  1. package/README.md +39 -23
  2. package/README.pt-BR.md +39 -23
  3. package/dist/features/database.js +35 -0
  4. package/dist/features/database.js.map +1 -1
  5. package/dist/features/language.js +19 -1
  6. package/dist/features/language.js.map +1 -1
  7. package/dist/features/no-orm.js +493 -0
  8. package/dist/features/no-orm.js.map +1 -0
  9. package/dist/generator.js +27 -7
  10. package/dist/generator.js.map +1 -1
  11. package/dist/index.js +24 -13
  12. package/dist/index.js.map +1 -1
  13. package/dist/prompts.js +32 -22
  14. package/dist/prompts.js.map +1 -1
  15. package/package.json +2 -1
  16. package/templates/drizzle/README.md +2 -0
  17. package/templates/drizzle/README.pt-BR.md +2 -0
  18. package/templates/prisma/.github/workflows/ci.yml +24 -1
  19. package/templates/prisma/ARCHITECTURE.md +7 -1
  20. package/templates/prisma/ARCHITECTURE.pt-BR.md +7 -1
  21. package/templates/prisma/README.md +11 -7
  22. package/templates/prisma/README.pt-BR.md +11 -7
  23. package/templates/prisma/ROADMAP.md +5 -1
  24. package/templates/prisma/ROADMAP.pt-BR.md +5 -1
  25. package/templates/prisma/TESTING.md +25 -3
  26. package/templates/prisma/TESTING.pt-BR.md +25 -3
  27. package/templates/prisma/docker-compose.yml +29 -1
  28. package/templates/prisma/docs/adding-a-module.md +246 -230
  29. package/templates/prisma/docs/features-markers.md +11 -1
  30. package/templates/prisma/prisma/schema.prisma +4 -0
  31. package/templates/prisma/src/auth/token.service.ts +12 -3
  32. package/templates/prisma/src/health/indicators/prisma-health.indicator.spec.ts +39 -22
  33. package/templates/prisma/src/health/indicators/prisma-health.indicator.ts +24 -19
  34. package/templates/typeorm/README.md +2 -0
  35. package/templates/typeorm/README.pt-BR.md +2 -0
@@ -1,230 +1,246 @@
1
- # Como adicionar um novo módulo
2
-
3
- Este guia mostra o passo a passo pra adicionar um recurso novo seguindo as convenções do NestForge, usando um módulo `posts` (posts de blog) como exemplo. Adapte os nomes pro seu caso.
4
-
5
- ## 1. Adicione o model no Prisma
6
-
7
- Em `prisma/schema.prisma`:
8
-
9
- ```prisma
10
- model Post {
11
- id String @id @default(uuid())
12
- title String
13
- content String
14
- authorId String
15
- author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
16
- createdAt DateTime @default(now())
17
- updatedAt DateTime @updatedAt
18
-
19
- @@map("posts")
20
- }
21
- ```
22
-
23
- Não esqueça de adicionar a relação inversa no `model User`:
24
-
25
- ```prisma
26
- posts Post[]
27
- ```
28
-
29
- Gere a migration:
30
-
31
- ```bash
32
- npx prisma migrate dev --name add_posts
33
- ```
34
-
35
- ## 2. Crie a estrutura de pastas
36
-
37
- ```
38
- src/posts/
39
- ├── dto/
40
- │ ├── create-post.dto.ts
41
- │ └── update-post.dto.ts
42
- ├── entities/
43
- │ └── post.entity.ts
44
- ├── posts.controller.ts
45
- ├── posts.service.ts
46
- └── posts.module.ts
47
- ```
48
-
49
- ## 3. DTOs com Zod
50
-
51
- `src/posts/dto/create-post.dto.ts`:
52
-
53
- ```ts
54
- import { z } from 'zod';
55
- import { createZodDto } from 'nestjs-zod';
56
-
57
- export const createPostSchema = z.object({
58
- title: z.string().min(3).describe('Título do post'),
59
- content: z.string().min(10).describe('Conteúdo do post'),
60
- });
61
-
62
- export class CreatePostDto extends createZodDto(createPostSchema) {}
63
- ```
64
-
65
- `src/posts/dto/update-post.dto.ts`:
66
-
67
- ```ts
68
- import { createZodDto } from 'nestjs-zod';
69
- import { createPostSchema } from './create-post.dto';
70
-
71
- export const updatePostSchema = createPostSchema.partial();
72
-
73
- export class UpdatePostDto extends createZodDto(updatePostSchema) {}
74
- ```
75
-
76
- ## 4. Entity (o que a API expõe)
77
-
78
- `src/posts/entities/post.entity.ts` mesmo se não houver nada sensível pra esconder agora, criar a entity já deixa o padrão pronto pra quando houver:
79
-
80
- ```ts
81
- export class PostEntity {
82
- id: string;
83
- title: string;
84
- content: string;
85
- authorId: string;
86
- createdAt: Date;
87
- updatedAt: Date;
88
-
89
- constructor(partial: Partial<PostEntity>) {
90
- Object.assign(this, partial);
91
- }
92
- }
93
- ```
94
-
95
- ## 5. Service (regra de negócio)
96
-
97
- `src/posts/posts.service.ts`:
98
-
99
- ```ts
100
- import { Injectable, NotFoundException } from '@nestjs/common';
101
- import { PrismaService } from '../database/prisma.service';
102
- import { CreatePostDto } from './dto/create-post.dto';
103
- import { UpdatePostDto } from './dto/update-post.dto';
104
- import { PostEntity } from './entities/post.entity';
105
-
106
- @Injectable()
107
- export class PostsService {
108
- constructor(private readonly prisma: PrismaService) {}
109
-
110
- async create(authorId: string, dto: CreatePostDto) {
111
- const post = await this.prisma.post.create({ data: { ...dto, authorId } });
112
- return new PostEntity(post);
113
- }
114
-
115
- async findOne(id: string) {
116
- const post = await this.prisma.post.findUnique({ where: { id } });
117
- if (!post) throw new NotFoundException('Post não encontrado');
118
- return new PostEntity(post);
119
- }
120
-
121
- async update(id: string, dto: UpdatePostDto) {
122
- await this.findOne(id);
123
- const post = await this.prisma.post.update({ where: { id }, data: dto });
124
- return new PostEntity(post);
125
- }
126
-
127
- async remove(id: string) {
128
- await this.findOne(id);
129
- await this.prisma.post.delete({ where: { id } });
130
- return { message: 'Post removido com sucesso' };
131
- }
132
- }
133
- ```
134
-
135
- ## 6. Controller (guards + permissions)
136
-
137
- Se o recurso precisa de controle de acesso, adicione a permission em `src/common/constants/permissions.ts` e no mapeamento `src/common/constants/role-permissions.ts` antes de usar:
138
-
139
- ```ts
140
- // permissions.ts
141
- export enum Permission {
142
- // ...existentes
143
- PostCreate = 'post:create',
144
- PostDelete = 'post:delete',
145
- }
146
- ```
147
-
148
- `src/posts/posts.controller.ts`:
149
-
150
- ```ts
151
- import { Body, Controller, Delete, Get, Param, Patch, Post } from '@nestjs/common';
152
- import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
153
- import { PostsService } from './posts.service';
154
- import { CreatePostDto } from './dto/create-post.dto';
155
- import { UpdatePostDto } from './dto/update-post.dto';
156
- import { Permissions } from '../common/decorators/permissions.decorator';
157
- import { Permission } from '../common/constants/permissions';
158
- import { CurrentUser } from '../common/decorators/current-user.decorator';
159
-
160
- @ApiTags('posts')
161
- @ApiBearerAuth()
162
- @Controller('posts')
163
- export class PostsController {
164
- constructor(private readonly postsService: PostsService) {}
165
-
166
- @Post()
167
- @Permissions(Permission.PostCreate)
168
- create(@CurrentUser() user: { id: string }, @Body() dto: CreatePostDto) {
169
- return this.postsService.create(user.id, dto);
170
- }
171
-
172
- @Get(':id')
173
- findOne(@Param('id') id: string) {
174
- return this.postsService.findOne(id);
175
- }
176
-
177
- @Patch(':id')
178
- update(@Param('id') id: string, @Body() dto: UpdatePostDto) {
179
- return this.postsService.update(id, dto);
180
- }
181
-
182
- @Delete(':id')
183
- @Permissions(Permission.PostDelete)
184
- remove(@Param('id') id: string) {
185
- return this.postsService.remove(id);
186
- }
187
- }
188
- ```
189
-
190
- ## 7. Module
191
-
192
- `src/posts/posts.module.ts`:
193
-
194
- ```ts
195
- import { Module } from '@nestjs/common';
196
- import { PostsService } from './posts.service';
197
- import { PostsController } from './posts.controller';
198
-
199
- @Module({
200
- controllers: [PostsController],
201
- providers: [PostsService],
202
- exports: [PostsService],
203
- })
204
- export class PostsModule {}
205
- ```
206
-
207
- Registre no `src/app.module.ts` (dentro do array `imports`):
208
-
209
- ```ts
210
- import { PostsModule } from './posts/posts.module';
211
- // ...
212
- PostsModule,
213
- ```
214
-
215
- ## 8. Testes
216
-
217
- - **Unitário** (`src/posts/posts.service.spec.ts`): mocke o `PrismaService` como em `src/users/users.service.spec.ts` — sem banco real.
218
- - **E2e** (`test/posts.e2e-spec.ts`): use os helpers de `test/utils/e2e-setup.ts` e `test/utils/clean-database.ts` (adicione `prisma.post.deleteMany()` na limpeza) e siga o padrão de `test/users.e2e-spec.ts`.
219
-
220
- ## Checklist rápido
221
-
222
- - [ ] Model no `schema.prisma` + migration
223
- - [ ] DTOs com Zod (`createZodDto`)
224
- - [ ] Entity (mesmo sem campo sensível ainda)
225
- - [ ] Service sem lógica no controller
226
- - [ ] Permissions novas cadastradas em `permissions.ts` e `role-permissions.ts`, se necessário
227
- - [ ] Module registrado no `AppModule`
228
- - [ ] Teste unitário do service
229
- - [ ] Teste e2e do fluxo principal
230
- - [ ] Atualizar `ROADMAP.md` se o módulo fechar um item do roadmap
1
+ # Como adicionar um novo módulo
2
+
3
+ Este guia mostra o passo a passo pra adicionar um recurso novo seguindo as convenções do NestForge, usando um módulo `posts` (posts de blog) como exemplo. Adapte os nomes pro seu caso.
4
+
5
+ ## 1. Adicione o model no Prisma
6
+
7
+ Em `prisma/schema.prisma`:
8
+
9
+ ```prisma
10
+ model Post {
11
+ // nestforge:feature:database:relational
12
+ id String @id @default(uuid())
13
+ // nestforge:feature:database:relational:end
14
+ // nestforge:feature:database:mongodb
15
+ id String @id @default(auto()) @map("_id") @db.ObjectId
16
+ // nestforge:feature:database:mongodb:end
17
+ title String
18
+ content String
19
+ // nestforge:feature:database:relational
20
+ authorId String
21
+ // nestforge:feature:database:relational:end
22
+ // nestforge:feature:database:mongodb
23
+ authorId String @db.ObjectId
24
+ // nestforge:feature:database:mongodb:end
25
+ author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
26
+ createdAt DateTime @default(now())
27
+ updatedAt DateTime @updatedAt
28
+
29
+ @@map("posts")
30
+ }
31
+ ```
32
+
33
+ Não esqueça de adicionar a relação inversa no `model User`:
34
+
35
+ ```prisma
36
+ posts Post[]
37
+ ```
38
+
39
+ Em PostgreSQL, MySQL ou SQLite, gere a migration:
40
+
41
+ ```bash
42
+ npx prisma migrate dev --name add_posts
43
+ ```
44
+
45
+ Em MongoDB, envie o schema diretamente:
46
+
47
+ ```bash
48
+ npm run prisma:push
49
+ ```
50
+
51
+ ## 2. Crie a estrutura de pastas
52
+
53
+ ```
54
+ src/posts/
55
+ ├── dto/
56
+ │ ├── create-post.dto.ts
57
+ │ └── update-post.dto.ts
58
+ ├── entities/
59
+ │ └── post.entity.ts
60
+ ├── posts.controller.ts
61
+ ├── posts.service.ts
62
+ └── posts.module.ts
63
+ ```
64
+
65
+ ## 3. DTOs com Zod
66
+
67
+ `src/posts/dto/create-post.dto.ts`:
68
+
69
+ ```ts
70
+ import { z } from 'zod';
71
+ import { createZodDto } from 'nestjs-zod';
72
+
73
+ export const createPostSchema = z.object({
74
+ title: z.string().min(3).describe('Título do post'),
75
+ content: z.string().min(10).describe('Conteúdo do post'),
76
+ });
77
+
78
+ export class CreatePostDto extends createZodDto(createPostSchema) {}
79
+ ```
80
+
81
+ `src/posts/dto/update-post.dto.ts`:
82
+
83
+ ```ts
84
+ import { createZodDto } from 'nestjs-zod';
85
+ import { createPostSchema } from './create-post.dto';
86
+
87
+ export const updatePostSchema = createPostSchema.partial();
88
+
89
+ export class UpdatePostDto extends createZodDto(updatePostSchema) {}
90
+ ```
91
+
92
+ ## 4. Entity (o que a API expõe)
93
+
94
+ `src/posts/entities/post.entity.ts` — mesmo se não houver nada sensível pra esconder agora, criar a entity já deixa o padrão pronto pra quando houver:
95
+
96
+ ```ts
97
+ export class PostEntity {
98
+ id: string;
99
+ title: string;
100
+ content: string;
101
+ authorId: string;
102
+ createdAt: Date;
103
+ updatedAt: Date;
104
+
105
+ constructor(partial: Partial<PostEntity>) {
106
+ Object.assign(this, partial);
107
+ }
108
+ }
109
+ ```
110
+
111
+ ## 5. Service (regra de negócio)
112
+
113
+ `src/posts/posts.service.ts`:
114
+
115
+ ```ts
116
+ import { Injectable, NotFoundException } from '@nestjs/common';
117
+ import { PrismaService } from '../database/prisma.service';
118
+ import { CreatePostDto } from './dto/create-post.dto';
119
+ import { UpdatePostDto } from './dto/update-post.dto';
120
+ import { PostEntity } from './entities/post.entity';
121
+
122
+ @Injectable()
123
+ export class PostsService {
124
+ constructor(private readonly prisma: PrismaService) {}
125
+
126
+ async create(authorId: string, dto: CreatePostDto) {
127
+ const post = await this.prisma.post.create({ data: { ...dto, authorId } });
128
+ return new PostEntity(post);
129
+ }
130
+
131
+ async findOne(id: string) {
132
+ const post = await this.prisma.post.findUnique({ where: { id } });
133
+ if (!post) throw new NotFoundException('Post não encontrado');
134
+ return new PostEntity(post);
135
+ }
136
+
137
+ async update(id: string, dto: UpdatePostDto) {
138
+ await this.findOne(id);
139
+ const post = await this.prisma.post.update({ where: { id }, data: dto });
140
+ return new PostEntity(post);
141
+ }
142
+
143
+ async remove(id: string) {
144
+ await this.findOne(id);
145
+ await this.prisma.post.delete({ where: { id } });
146
+ return { message: 'Post removido com sucesso' };
147
+ }
148
+ }
149
+ ```
150
+
151
+ ## 6. Controller (guards + permissions)
152
+
153
+ Se o recurso precisa de controle de acesso, adicione a permission em `src/common/constants/permissions.ts` e no mapeamento `src/common/constants/role-permissions.ts` antes de usar:
154
+
155
+ ```ts
156
+ // permissions.ts
157
+ export enum Permission {
158
+ // ...existentes
159
+ PostCreate = 'post:create',
160
+ PostDelete = 'post:delete',
161
+ }
162
+ ```
163
+
164
+ `src/posts/posts.controller.ts`:
165
+
166
+ ```ts
167
+ import { Body, Controller, Delete, Get, Param, Patch, Post } from '@nestjs/common';
168
+ import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
169
+ import { PostsService } from './posts.service';
170
+ import { CreatePostDto } from './dto/create-post.dto';
171
+ import { UpdatePostDto } from './dto/update-post.dto';
172
+ import { Permissions } from '../common/decorators/permissions.decorator';
173
+ import { Permission } from '../common/constants/permissions';
174
+ import { CurrentUser } from '../common/decorators/current-user.decorator';
175
+
176
+ @ApiTags('posts')
177
+ @ApiBearerAuth()
178
+ @Controller('posts')
179
+ export class PostsController {
180
+ constructor(private readonly postsService: PostsService) {}
181
+
182
+ @Post()
183
+ @Permissions(Permission.PostCreate)
184
+ create(@CurrentUser() user: { id: string }, @Body() dto: CreatePostDto) {
185
+ return this.postsService.create(user.id, dto);
186
+ }
187
+
188
+ @Get(':id')
189
+ findOne(@Param('id') id: string) {
190
+ return this.postsService.findOne(id);
191
+ }
192
+
193
+ @Patch(':id')
194
+ update(@Param('id') id: string, @Body() dto: UpdatePostDto) {
195
+ return this.postsService.update(id, dto);
196
+ }
197
+
198
+ @Delete(':id')
199
+ @Permissions(Permission.PostDelete)
200
+ remove(@Param('id') id: string) {
201
+ return this.postsService.remove(id);
202
+ }
203
+ }
204
+ ```
205
+
206
+ ## 7. Module
207
+
208
+ `src/posts/posts.module.ts`:
209
+
210
+ ```ts
211
+ import { Module } from '@nestjs/common';
212
+ import { PostsService } from './posts.service';
213
+ import { PostsController } from './posts.controller';
214
+
215
+ @Module({
216
+ controllers: [PostsController],
217
+ providers: [PostsService],
218
+ exports: [PostsService],
219
+ })
220
+ export class PostsModule {}
221
+ ```
222
+
223
+ Registre no `src/app.module.ts` (dentro do array `imports`):
224
+
225
+ ```ts
226
+ import { PostsModule } from './posts/posts.module';
227
+ // ...
228
+ PostsModule,
229
+ ```
230
+
231
+ ## 8. Testes
232
+
233
+ - **Unitário** (`src/posts/posts.service.spec.ts`): mocke o `PrismaService` como em `src/users/users.service.spec.ts` — sem banco real.
234
+ - **E2e** (`test/posts.e2e-spec.ts`): use os helpers de `test/utils/e2e-setup.ts` e `test/utils/clean-database.ts` (adicione `prisma.post.deleteMany()` na limpeza) e siga o padrão de `test/users.e2e-spec.ts`.
235
+
236
+ ## Checklist rápido
237
+
238
+ - [ ] Model no `schema.prisma` + migration ou `prisma:push`
239
+ - [ ] DTOs com Zod (`createZodDto`)
240
+ - [ ] Entity (mesmo sem campo sensível ainda)
241
+ - [ ] Service sem lógica no controller
242
+ - [ ] Permissions novas cadastradas em `permissions.ts` e `role-permissions.ts`, se necessário
243
+ - [ ] Module registrado no `AppModule`
244
+ - [ ] Teste unitário do service
245
+ - [ ] Teste e2e do fluxo principal
246
+ - [ ] Atualizar `ROADMAP.md` se o módulo fechar um item do roadmap
@@ -21,6 +21,16 @@ export class UsersController {
21
21
 
22
22
  O nome depois de `nestforge:feature:` tem que ser exatamente o mesmo nas duas linhas (abertura e `:end`), e bater com o nome usado no array `features` da CLI (`docker`, `swagger`, `validation`, `redis`, `rbac`).
23
23
 
24
+ Marcadores de banco são adicionados automaticamente pelo gerador:
25
+
26
+ * `database:postgres`;
27
+ * `database:mysql`;
28
+ * `database:sqlite`;
29
+ * `database:mongodb`;
30
+ * `database:relational`, habilitado em PostgreSQL, MySQL e SQLite.
31
+
32
+ Use `database:relational` quando o mesmo trecho SQL serve para todos os bancos relacionais, como a etapa `prisma migrate deploy`. Use o marcador específico quando provider, URL, health check ou infraestrutura dependem do banco selecionado.
33
+
24
34
  Funciona em qualquer arquivo cuja linguagem use `//` para comentário (`.ts`, `.js`). **Não funciona em JSON** (`package.json`) — pra dependências, ver a seção final deste documento.
25
35
 
26
36
  ## Marcador de arquivo inteiro
@@ -47,4 +57,4 @@ JSON não aceita comentário, então marcador não funciona ali. Em vez disso, o
47
57
  - [ ] Todo trecho de código exclusivo do recurso está marcado (bloco ou arquivo inteiro)
48
58
  - [ ] O nome do marcador bate com o valor usado em `features` na CLI (`packages/cli/src/prompts.ts`)
49
59
  - [ ] Dependências novas do `package.json` estão em `FEATURE_DEPENDENCIES` (`packages/cli/src/features/dependencies.ts`)
50
- - [ ] Testado gerando o projeto com o recurso ligado **e** desligado — o projeto tem que compilar/rodar dos dois jeitos
60
+ - [ ] Testado gerando o projeto com o recurso ligado **e** desligado — o projeto tem que compilar/rodar dos dois jeitos
@@ -97,6 +97,10 @@ model Session {
97
97
  data String
98
98
  // nestforge:feature:database:sqlite:end
99
99
 
100
+ // nestforge:feature:database:mongodb
101
+ data String
102
+ // nestforge:feature:database:mongodb:end
103
+
100
104
  expiresAt DateTime
101
105
 
102
106
  @@map("sessions")
@@ -1,6 +1,7 @@
1
1
  // nestforge:feature-file:auth:token
2
2
  import { Injectable, UnauthorizedException } from '@nestjs/common';
3
3
  import { JwtService } from '@nestjs/jwt';
4
+ import type { JwtSignOptions } from '@nestjs/jwt';
4
5
  import { createHash } from 'crypto';
5
6
  import { PrismaService } from '../database/prisma.service';
6
7
 
@@ -14,14 +15,22 @@ export class TokenService {
14
15
  async issueTokens(userId: string, email: string, role: string) {
15
16
  const payload = { sub: userId, email, role };
16
17
 
18
+ const accessTokenExpiresIn = (
19
+ process.env.JWT_ACCESS_EXPIRES_IN ?? '15m'
20
+ ) as JwtSignOptions['expiresIn'];
21
+
22
+ const refreshTokenExpiresIn = (
23
+ process.env.JWT_REFRESH_EXPIRES_IN ?? '7d'
24
+ ) as JwtSignOptions['expiresIn'];
25
+
17
26
  const accessToken = this.jwtService.sign(payload, {
18
27
  secret: process.env.JWT_ACCESS_SECRET,
19
- expiresIn: process.env.JWT_ACCESS_EXPIRES_IN ?? '15m',
28
+ expiresIn: accessTokenExpiresIn,
20
29
  });
21
30
 
22
31
  const refreshToken = this.jwtService.sign(payload, {
23
32
  secret: process.env.JWT_REFRESH_SECRET,
24
- expiresIn: process.env.JWT_REFRESH_EXPIRES_IN ?? '7d',
33
+ expiresIn: refreshTokenExpiresIn,
25
34
  });
26
35
 
27
36
  const expiresAt = new Date();
@@ -81,4 +90,4 @@ export class TokenService {
81
90
  private hashToken(token: string): string {
82
91
  return createHash('sha256').update(token).digest('hex');
83
92
  }
84
- }
93
+ }
@@ -1,22 +1,39 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import { HealthCheckError } from '@nestjs/terminus';
3
- import { PrismaHealthIndicator } from './prisma-health.indicator';
4
- import { PrismaService } from '../../database/prisma.service';
5
-
6
- describe('PrismaHealthIndicator', () => {
7
- it('retorna status up quando a query executa com sucesso', async () => {
8
- const prisma = { $queryRaw: vi.fn().mockResolvedValue([{ '?column?': 1 }]) };
9
- const indicator = new PrismaHealthIndicator(prisma as unknown as PrismaService);
10
-
11
- const result = await indicator.isHealthy('database');
12
-
13
- expect(result).toEqual({ database: { status: 'up' } });
14
- });
15
-
16
- it('lança HealthCheckError quando a query falha', async () => {
17
- const prisma = { $queryRaw: vi.fn().mockRejectedValue(new Error('conexão recusada')) };
18
- const indicator = new PrismaHealthIndicator(prisma as unknown as PrismaService);
19
-
20
- await expect(indicator.isHealthy('database')).rejects.toBeInstanceOf(HealthCheckError);
21
- });
22
- });
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { HealthCheckError } from '@nestjs/terminus';
3
+ import { PrismaHealthIndicator } from './prisma-health.indicator';
4
+ import { PrismaService } from '../../database/prisma.service';
5
+
6
+ describe('PrismaHealthIndicator', () => {
7
+ it('retorna status up quando a query executa com sucesso', async () => {
8
+ const prisma = {
9
+ // nestforge:feature:database:relational
10
+ $queryRaw: vi.fn().mockResolvedValue([{ '?column?': 1 }]),
11
+ // nestforge:feature:database:relational:end
12
+ // nestforge:feature:database:mongodb
13
+ $runCommandRaw: vi.fn().mockResolvedValue({ ok: 1 }),
14
+ // nestforge:feature:database:mongodb:end
15
+ };
16
+ const indicator = new PrismaHealthIndicator(prisma as unknown as PrismaService);
17
+
18
+ const result = await indicator.isHealthy('database');
19
+
20
+ expect(result).toEqual({ database: { status: 'up' } });
21
+ // nestforge:feature:database:mongodb
22
+ expect(prisma.$runCommandRaw).toHaveBeenCalledWith({ ping: 1 });
23
+ // nestforge:feature:database:mongodb:end
24
+ });
25
+
26
+ it('lança HealthCheckError quando a query falha', async () => {
27
+ const prisma = {
28
+ // nestforge:feature:database:relational
29
+ $queryRaw: vi.fn().mockRejectedValue(new Error('conexão recusada')),
30
+ // nestforge:feature:database:relational:end
31
+ // nestforge:feature:database:mongodb
32
+ $runCommandRaw: vi.fn().mockRejectedValue(new Error('conexão recusada')),
33
+ // nestforge:feature:database:mongodb:end
34
+ };
35
+ const indicator = new PrismaHealthIndicator(prisma as unknown as PrismaService);
36
+
37
+ await expect(indicator.isHealthy('database')).rejects.toBeInstanceOf(HealthCheckError);
38
+ });
39
+ });