adi_dev_workflow 1.1.1 → 1.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 (98) hide show
  1. package/bin/index.js +8 -8
  2. package/frameworks/agents/qa-staff-engineer.md +311 -311
  3. package/frameworks/agents/qa-validation-expert.md +458 -458
  4. package/frameworks/agents/tech-review-conformance.md +200 -200
  5. package/frameworks/commands/ministack/README.md +2 -0
  6. package/frameworks/commands/ministack/code-review.md +2 -0
  7. package/frameworks/commands/ministack/generate-intent.md +2 -0
  8. package/frameworks/commands/ministack/generate-scope.md +2 -0
  9. package/frameworks/commands/ministack/generate-tasks.md +2 -0
  10. package/frameworks/commands/ministack/generate-tech-direction.md +2 -0
  11. package/frameworks/commands/ministack/run-ministack-tasks.md +3 -0
  12. package/frameworks/commands/ministack/run-ministack-withlinear.md +2 -0
  13. package/frameworks/commands/ministack/status.md +2 -0
  14. package/frameworks/commands/sdd/code-review.md +2 -0
  15. package/frameworks/commands/sdd/generate-prd.md +2 -0
  16. package/frameworks/commands/sdd/generate-task-plan.md +2 -0
  17. package/frameworks/commands/sdd/generate-tech-direction.md +2 -0
  18. package/frameworks/commands/sdd/generate-tech-spec.md +2 -0
  19. package/frameworks/commands/sdd/generate-tests.md +2 -0
  20. package/frameworks/commands/sdd/run_tasks.md +3 -0
  21. package/frameworks/commands/sdd/run_tasks_withlinear.md +2 -0
  22. package/frameworks/commands/sdd/status.md +2 -0
  23. package/frameworks/commands/sdd/validate-sdd.md +2 -0
  24. package/frameworks/commands/sync-tasks-to-linear.md +2 -0
  25. package/frameworks/commands/taskcard/generate-taskcard.md +2 -0
  26. package/frameworks/commands/taskcard/run-taskcard.md +2 -0
  27. package/frameworks/config/ai-framework-config.yaml +112 -0
  28. package/frameworks/skills/ministack-tasks-expert/SKILL.md +204 -204
  29. package/frameworks/skills/ministack-tasks-expert/templates/task_plan_template.md +78 -78
  30. package/frameworks/skills/ministack-tasks-expert/templates/task_template.md +103 -103
  31. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/benchmark.json +99 -99
  32. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/benchmark.md +64 -64
  33. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/eval_metadata.json +12 -12
  34. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/with_skill/grading.json +32 -32
  35. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/with_skill/outputs/response.md +134 -134
  36. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/with_skill/outputs/transcript.md +68 -68
  37. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/with_skill/timing.json +5 -5
  38. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/without_skill/grading.json +32 -32
  39. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/without_skill/outputs/response.md +525 -525
  40. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/without_skill/outputs/transcript.md +30 -30
  41. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/without_skill/timing.json +5 -5
  42. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/eval_metadata.json +12 -12
  43. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/with_skill/grading.json +32 -32
  44. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/with_skill/outputs/response.md +1126 -1126
  45. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/with_skill/outputs/transcript.md +131 -131
  46. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/with_skill/timing.json +5 -5
  47. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/without_skill/grading.json +32 -32
  48. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/without_skill/outputs/response.md +452 -452
  49. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/without_skill/outputs/transcript.md +78 -78
  50. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/without_skill/timing.json +5 -5
  51. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/eval_metadata.json +12 -12
  52. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/with_skill/grading.json +32 -32
  53. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/with_skill/outputs/response.md +101 -101
  54. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/with_skill/outputs/transcript.md +133 -133
  55. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/with_skill/timing.json +5 -5
  56. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/without_skill/grading.json +32 -32
  57. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/without_skill/outputs/response.md +248 -248
  58. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/without_skill/outputs/transcript.md +49 -49
  59. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/without_skill/timing.json +5 -5
  60. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/review.html +1325 -1325
  61. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/benchmark.json +94 -94
  62. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/benchmark.md +67 -67
  63. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/eval_metadata.json +12 -12
  64. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/with_skill/grading.json +32 -32
  65. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/with_skill/outputs/response.md +117 -117
  66. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/with_skill/outputs/transcript.md +91 -91
  67. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/with_skill/timing.json +1 -1
  68. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/without_skill/grading.json +32 -32
  69. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/without_skill/outputs/response.md +694 -694
  70. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/without_skill/outputs/transcript.md +45 -45
  71. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/without_skill/timing.json +1 -1
  72. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/eval_metadata.json +12 -12
  73. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/with_skill/grading.json +32 -32
  74. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/with_skill/outputs/response.md +1087 -1087
  75. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/with_skill/outputs/transcript.md +124 -124
  76. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/with_skill/timing.json +1 -1
  77. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/without_skill/grading.json +32 -32
  78. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/without_skill/outputs/response.md +458 -458
  79. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/without_skill/outputs/transcript.md +84 -84
  80. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/without_skill/timing.json +1 -1
  81. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/eval_metadata.json +12 -12
  82. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/with_skill/grading.json +32 -32
  83. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/with_skill/outputs/response.md +70 -70
  84. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/with_skill/outputs/transcript.md +148 -148
  85. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/with_skill/timing.json +1 -1
  86. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/without_skill/grading.json +32 -32
  87. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/without_skill/outputs/response.md +249 -249
  88. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/without_skill/outputs/transcript.md +80 -80
  89. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/without_skill/timing.json +1 -1
  90. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/review.html +1325 -1325
  91. package/frameworks/skills/sdd-tech-spec-expert/SKILL.md +317 -317
  92. package/frameworks/skills/sdd-tech-spec-expert/evals/evals.json +199 -199
  93. package/frameworks/skills/sdd-tech-spec-expert/templates/spec_tech_template.md +290 -290
  94. package/frameworks/skills/sdd-tech-spec-expert/templates/tech_direction-template.md +23 -23
  95. package/package.json +28 -28
  96. package/src/cli.js +121 -121
  97. package/src/installer.js +155 -136
  98. package/src/transformer.js +86 -86
@@ -1,525 +1,525 @@
1
- # TASK PLAN -- Modulo de Usuario v1
2
-
3
- **Baseado em:** `docs/feature-user/v1/spec_tech.md`
4
- **Data:** 2026-03-07
5
- **Branch:** `user-feature`
6
-
7
- ---
8
-
9
- ## Visao Geral
10
-
11
- Este Task Plan detalha a ordem de implementacao da feature de usuario conforme definido no SPEC_TECH v1. As tasks estao organizadas em ordem de dependencia (bottom-up), seguindo o fluxo: Infraestrutura -> Persistencia -> Repository -> Service -> Handler -> Testes de Integracao/E2E.
12
-
13
- ---
14
-
15
- ## Task 1: Pacote Identity (JWT + Contexto)
16
-
17
- **Arquivos a CRIAR:**
18
- - `internal/pkg/identity/identity.go`
19
- - `internal/pkg/identity/identity_test.go`
20
-
21
- **Descricao:**
22
- Criar o pacote `internal/pkg/identity` com as seguintes funcoes publicas:
23
-
24
- 1. **Tipo `Claims`** -- struct com `UserID`, `Email` e `jwt.RegisteredClaims`
25
- 2. **`GenerateToken(userID, email, secret string, duration time.Duration) (string, time.Time, error)`** -- gera JWT assinado com HS256
26
- 3. **`ParseToken(tokenString, secret string) (*Claims, error)`** -- valida e extrai claims; rejeita algoritmos que nao sejam HS256
27
- 4. **`WithUserID(ctx context.Context, userID string) context.Context`** -- injeta user_id no contexto via chave unexportada
28
- 5. **`UserIDFromContext(ctx context.Context) (string, bool)`** -- recupera user_id do contexto
29
-
30
- **Testes unitarios (`identity_test.go`):**
31
- - `TestGenerateToken_Success` -- token nao vazio, expiresAt no futuro
32
- - `TestGenerateToken_TokenIsParseable` -- roundtrip generate -> parse
33
- - `TestGenerateToken_ClaimsContainCorrectData` -- userID e email corretos
34
- - `TestParseToken_WrongSecret` -- erro retornado
35
- - `TestParseToken_ExpiredToken` -- erro retornado
36
- - `TestParseToken_MalformedToken` -- erro retornado
37
- - `TestContextRoundtrip_UserID` -- WithUserID + UserIDFromContext retorna valor correto
38
- - `TestUserIDFromContext_Missing` -- retorna "", false
39
-
40
- **Criterio de conclusao:** Todos os testes passando com `CGO_ENABLED=1 go test ./internal/pkg/identity/... -v`
41
-
42
- **Dependencias:** Nenhuma (pacote isolado).
43
-
44
- ---
45
-
46
- ## Task 2: Configuracao -- Adicionar JWTExpirationHours
47
-
48
- **Arquivos a MODIFICAR:**
49
- - `internal/infra/config/config.go`
50
- - `configs/config.yaml`
51
-
52
- **Descricao:**
53
-
54
- 1. Em `config.go`: adicionar campo `JWTExpirationHours int` na struct `Config` e ler via `v.GetInt("jwt.expiration_hours")`
55
- 2. Em `config.yaml`: adicionar `expiration_hours: 24` dentro da secao `jwt`
56
-
57
- **Criterio de conclusao:** `make build` compila sem erros; config carrega o novo campo corretamente.
58
-
59
- **Dependencias:** Nenhuma.
60
-
61
- ---
62
-
63
- ## Task 3: Migracao -- Adicionar coluna `endereco`
64
-
65
- **Arquivos a CRIAR:**
66
- - `internal/db/migrations/002_add_endereco_usuarios.sql`
67
-
68
- **Descricao:**
69
-
70
- Criar migracao com:
71
- ```sql
72
- ALTER TABLE usuarios ADD COLUMN endereco TEXT NOT NULL DEFAULT '';
73
- ```
74
-
75
- Seguir a convencao do projeto (arquivo unico `.sql`, sem `.up`/`.down`).
76
-
77
- **Criterio de conclusao:** Aplicacao inicia sem erros; `PRAGMA table_info(usuarios)` lista coluna `endereco`.
78
-
79
- **Dependencias:** Nenhuma.
80
-
81
- ---
82
-
83
- ## Task 4: Queries SQLC -- Atualizar queries de usuario
84
-
85
- **Arquivos a MODIFICAR:**
86
- - `internal/db/queries/user.sql` (substituicao total do conteudo)
87
-
88
- **Descricao:**
89
-
90
- Substituir o conteudo do arquivo de queries por:
91
-
92
- 1. **`CreateUser :one`** -- INSERT com campos `id, nome, email, senha_hash, endereco, data_criacao, data_atualizacao` + RETURNING *
93
- 2. **`GetUserByID :one`** -- SELECT * WHERE id = ? LIMIT 1
94
- 3. **`GetUserByEmail :one`** -- SELECT * WHERE email = ? LIMIT 1
95
- 4. **`UpdateUser :one`** -- UPDATE SET nome, email, senha_hash, endereco, data_atualizacao WHERE id = ? RETURNING *
96
-
97
- **Pos-execucao:** Executar `make generate` para regenerar `internal/db/sqlc/`.
98
-
99
- **Criterio de conclusao:** `make generate` executa sem erros; codigo gerado em `internal/db/sqlc/` contem as novas queries.
100
-
101
- **Dependencias:** Task 3 (migracao deve existir para o schema estar correto).
102
-
103
- ---
104
-
105
- ## Task 5: Protobuf -- Atualizar contratos gRPC
106
-
107
- **Arquivos a MODIFICAR:**
108
- - `api/proto/v1/user.proto` (substituicao total do conteudo)
109
-
110
- **Descricao:**
111
-
112
- Substituir o conteudo do proto conforme SPEC_TECH secao 8.1:
113
-
114
- 1. Mensagem `User` com campos: id, name, email, address, created_at, updated_at
115
- 2. `CreateUserRequest` com name, email, password, address
116
- 3. `CreateUserResponse` com id, name, email, created_at
117
- 4. `LoginRequest` com email, password
118
- 5. `LoginResponse` com token, expires_at, id, name, email
119
- 6. `GetUserLoggedRequest` (vazio)
120
- 7. `GetUserLoggedResponse` com User
121
- 8. `UpdateUserRequest` com optional string para name, email, address, new_password, current_password
122
- 9. `UpdateUserResponse` com User
123
- 10. Servico `UserService` com 4 RPCs: CreateUser, Login, GetUserLogged, UpdateUser
124
-
125
- **Pos-execucao:** Executar `make generate` para regenerar `gen/proto/v1/`.
126
-
127
- **Criterio de conclusao:** `make generate` e `make build` executam sem erros.
128
-
129
- **Dependencias:** Nenhuma (pode ser feita em paralelo com Tasks 1-4).
130
-
131
- ---
132
-
133
- ## Task 6: Repository -- Estender UserRepository
134
-
135
- **Arquivos a MODIFICAR:**
136
- - `internal/repository/user_repository.go`
137
-
138
- **Descricao:**
139
-
140
- 1. Adicionar campo `Address string` ao modelo `User`
141
- 2. Adicionar metodos a interface `UserRepository`:
142
- - `GetByEmail(ctx context.Context, email string) (*User, error)`
143
- - `Update(ctx context.Context, user *User) (*User, error)`
144
- 3. Implementar os novos metodos na struct `userRepository`
145
- 4. Atualizar mapeamento SQLC->dominio no `Create` e `GetByID` para incluir `Address` (endereco -> Address)
146
- 5. Implementar mapeamento no `GetByEmail` e `Update`
147
-
148
- **Criterio de conclusao:** `make build` compila sem erros.
149
-
150
- **Dependencias:** Task 4 (queries SQLC regeneradas).
151
-
152
- ---
153
-
154
- ## Task 7: Service -- Estender UserService
155
-
156
- **Arquivos a MODIFICAR:**
157
- - `internal/service/user_service.go`
158
-
159
- **Descricao:**
160
-
161
- 1. **Adicionar dependencias ao construtor:**
162
- - `*config.Config` e `*zap.Logger` como parametros de `NewUserService`
163
- - FX resolve automaticamente (sem alterar `fx.go`)
164
-
165
- 2. **Adicionar variaveis de erro exportadas:**
166
- - `ErrInvalidName`, `ErrInvalidEmail`, `ErrEmailAlreadyExists`, `ErrPasswordTooShort`, `ErrAddressRequired`
167
- - `ErrInvalidCredentials`, `ErrUserNotFound`, `ErrNoFieldsToUpdate`
168
- - `ErrCurrentPasswordRequired`, `ErrCurrentPasswordWrong`
169
-
170
- 3. **Adicionar tipos:**
171
- - `CreateUserInput` -- atualizar com campo `Address`
172
- - `LoginResult` -- novo (Token, ExpiresAt, User)
173
- - `UpdateUserInput` -- novo (ponteiros *string para campos opcionais)
174
-
175
- 4. **Adicionar metodos a interface `UserService`:**
176
- - `Login(ctx context.Context, email, password string) (*LoginResult, error)`
177
- - `GetUserLogged(ctx context.Context, userID string) (*repository.User, error)`
178
- - `UpdateUser(ctx context.Context, userID string, input UpdateUserInput) (*repository.User, error)`
179
-
180
- 5. **Implementar `CreateUser` (atualizar):**
181
- - Adicionar validacoes: name vazio, email (net/mail), senha >= 8 chars, address vazio
182
- - Verificar email unico via `repo.GetByEmail`
183
- - Hash com bcrypt, UUID, timestamps
184
- - Persistir via `repo.Create`
185
- - Logging: Info "usuario criado com sucesso"
186
-
187
- 6. **Implementar `Login`:**
188
- - `repo.GetByEmail` -- se nao existe: `ErrInvalidCredentials`
189
- - `bcrypt.CompareHashAndPassword` -- se falha: `ErrInvalidCredentials`
190
- - `identity.GenerateToken(userID, email, secret, duration)`
191
- - Limpar PasswordHash antes de retornar
192
- - Logging: Info "login bem-sucedido" / Warn "tentativa de login falha"
193
-
194
- 7. **Implementar `GetUserLogged`:**
195
- - `repo.GetByID(userID)` -- se nao encontra: `ErrUserNotFound`
196
- - Limpar PasswordHash
197
- - Retornar User
198
-
199
- 8. **Implementar `UpdateUser`:**
200
- - Validar ao menos 1 campo nao-nil
201
- - Buscar user atual via `repo.GetByID`
202
- - Se email: validar formato + unicidade
203
- - Se new_password: exigir current_password, validar tamanho, verificar hash, gerar novo hash
204
- - Aplicar campos nao-nil sobre user atual
205
- - Persistir via `repo.Update`
206
- - Limpar PasswordHash antes de retornar
207
- - Logging: Info "dados atualizados" / Info "senha alterada"
208
-
209
- **Criterio de conclusao:** `make build` compila sem erros.
210
-
211
- **Dependencias:** Task 1 (identity), Task 2 (config), Task 6 (repository).
212
-
213
- ---
214
-
215
- ## Task 8: Interceptor Auth -- Integrar com identity
216
-
217
- **Arquivos a MODIFICAR:**
218
- - `internal/infra/grpc/interceptors/auth.go`
219
-
220
- **Descricao:**
221
-
222
- 1. Adicionar `/proto.v1.UserService/Login` em `skipMethods`
223
- 2. Substituir parse manual do token por `identity.ParseToken(tokenString, cfg.JWTSecret)`
224
- 3. Apos validacao bem-sucedida: `ctx = identity.WithUserID(ctx, claims.UserID)`
225
- 4. Passar ctx atualizado ao handler
226
-
227
- **Criterio de conclusao:** `make build` compila sem erros.
228
-
229
- **Dependencias:** Task 1 (identity).
230
-
231
- ---
232
-
233
- ## Task 9: Handler gRPC -- Estender UserHandler
234
-
235
- **Arquivos a MODIFICAR:**
236
- - `internal/handler/grpc/user_handler.go`
237
-
238
- **Descricao:**
239
-
240
- 1. **Atualizar `CreateUser`:**
241
- - Extrair `address` do request
242
- - Mapear response com `CreateUserResponse{id, name, email, created_at}`
243
-
244
- 2. **Implementar `Login`:**
245
- - Extrair email e password do request
246
- - Chamar `service.Login`
247
- - Retornar `LoginResponse{token, expires_at, id, name, email}`
248
-
249
- 3. **Implementar `GetUserLogged`:**
250
- - `identity.UserIDFromContext(ctx)` -- se ausente: `codes.Unauthenticated`
251
- - Chamar `service.GetUserLogged(userID)`
252
- - Retornar `GetUserLoggedResponse{User{...}}`
253
-
254
- 4. **Implementar `UpdateUser`:**
255
- - `identity.UserIDFromContext(ctx)` -- se ausente: `codes.Unauthenticated`
256
- - Construir `UpdateUserInput` com ponteiros (nil = campo ausente)
257
- - Chamar `service.UpdateUser(userID, input)`
258
- - Retornar `UpdateUserResponse{User{...}}`
259
-
260
- 5. **Remover `GetUser`** (substituido por `GetUserLogged`)
261
-
262
- 6. **Atualizar `mapServiceError`:**
263
- - Adicionar mapeamento para todos os novos erros conforme SPEC_TECH secao 11.2
264
-
265
- 7. **Criar funcao auxiliar `domainUserToProto`:**
266
- - Converte `*repository.User` para `*pb.User`
267
-
268
- **Criterio de conclusao:** `make build` compila sem erros.
269
-
270
- **Dependencias:** Task 5 (proto gerado), Task 7 (service).
271
-
272
- ---
273
-
274
- ## Task 10: Configuracao de Rotas Publicas
275
-
276
- **Arquivos a MODIFICAR:**
277
- - `configs/config.yaml`
278
-
279
- **Descricao:**
280
-
281
- Garantir que `skip_methods` no config inclua:
282
- - `/proto.v1.UserService/CreateUser`
283
- - `/proto.v1.UserService/Login`
284
-
285
- **Criterio de conclusao:** Servidor inicia sem erros; rotas publicas acessiveis sem token.
286
-
287
- **Dependencias:** Task 8 (interceptor), Task 9 (handler).
288
-
289
- ---
290
-
291
- ## Task 11: Testes Unitarios -- Service
292
-
293
- **Arquivos a MODIFICAR:**
294
- - `internal/service/user_service_test.go`
295
-
296
- **Descricao:**
297
-
298
- Atualizar mock `MockUserRepository` com metodos `GetByEmail` e `Update`.
299
-
300
- **CreateUser (atualizar existentes + novos):**
301
- - `TestCreateUser_Success` -- incluir Address
302
- - `TestCreateUser_PasswordIsHashed` -- bcrypt roundtrip
303
- - `TestCreateUser_GeneratesValidUUID`
304
- - `TestCreateUser_ErrorEmptyName`
305
- - `TestCreateUser_ErrorInvalidEmailFormat` (novo)
306
- - `TestCreateUser_ErrorPasswordTooShort_7Chars` (novo)
307
- - `TestCreateUser_PasswordExactly8Chars` (novo)
308
- - `TestCreateUser_ErrorEmptyAddress` (novo)
309
- - `TestCreateUser_ErrorRepositoryFailure`
310
-
311
- **Login (novos):**
312
- - `TestLogin_Success`
313
- - `TestLogin_ResultDoesNotExposePasswordHash`
314
- - `TestLogin_TokenExpiresAtRespectsConfig`
315
- - `TestLogin_EmailNotFound_GenericError`
316
- - `TestLogin_WrongPassword_GenericError`
317
- - `TestLogin_ErrorRepositoryFailure`
318
-
319
- **GetUserLogged (novos):**
320
- - `TestGetUserLogged_Success`
321
- - `TestGetUserLogged_UserNotFound`
322
- - `TestGetUserLogged_RepositoryError`
323
-
324
- **UpdateUser (novos):**
325
- - `TestUpdateUser_NameOnly`
326
- - `TestUpdateUser_EmailOnly`
327
- - `TestUpdateUser_AddressOnly`
328
- - `TestUpdateUser_PasswordChange_Success`
329
- - `TestUpdateUser_MultipleFields`
330
- - `TestUpdateUser_NoFieldsToUpdate`
331
- - `TestUpdateUser_InvalidEmailFormat`
332
- - `TestUpdateUser_EmailAlreadyExists`
333
- - `TestUpdateUser_CurrentPasswordMissing`
334
- - `TestUpdateUser_WrongCurrentPassword`
335
- - `TestUpdateUser_NewPasswordTooShort`
336
- - `TestUpdateUser_UserNotFound`
337
- - `TestUpdateUser_RepositoryFailure`
338
-
339
- **Criterio de conclusao:** `CGO_ENABLED=1 go test ./internal/service/... -v` -- todos passando.
340
-
341
- **Dependencias:** Task 7 (service implementado).
342
-
343
- ---
344
-
345
- ## Task 12: Testes Unitarios -- Handler
346
-
347
- **Arquivos a MODIFICAR:**
348
- - `internal/handler/grpc/user_handler_test.go`
349
-
350
- **Descricao:**
351
-
352
- Atualizar mock `MockUserService` com metodos `Login`, `GetUserLogged`, `UpdateUser`. Remover testes de `GetUser`.
353
-
354
- **CreateUser:**
355
- - `TestUserHandler_CreateUser_Success`
356
- - `TestUserHandler_CreateUser_ResponseDoesNotExposePasswordHash`
357
- - `TestUserHandler_CreateUser_EmptyName`
358
- - `TestUserHandler_CreateUser_EmptyEmail`
359
- - `TestUserHandler_CreateUser_ShortPassword`
360
- - `TestUserHandler_CreateUser_EmptyAddress`
361
- - `TestUserHandler_CreateUser_DuplicateEmail`
362
- - `TestUserHandler_CreateUser_InternalError`
363
-
364
- **Login:**
365
- - `TestUserHandler_Login_Success`
366
- - `TestUserHandler_Login_InvalidCredentials`
367
- - `TestUserHandler_Login_InternalError`
368
-
369
- **GetUserLogged:**
370
- - `TestUserHandler_GetUserLogged_Success`
371
- - `TestUserHandler_GetUserLogged_MissingUserID`
372
- - `TestUserHandler_GetUserLogged_NotFound`
373
-
374
- **UpdateUser:**
375
- - `TestUserHandler_UpdateUser_NameOnly`
376
- - `TestUserHandler_UpdateUser_EmailOnly`
377
- - `TestUserHandler_UpdateUser_PasswordChange`
378
- - `TestUserHandler_UpdateUser_MultipleFields`
379
- - `TestUserHandler_UpdateUser_NoFields`
380
- - `TestUserHandler_UpdateUser_EmailAlreadyExists`
381
- - `TestUserHandler_UpdateUser_CurrentPasswordMissing`
382
- - `TestUserHandler_UpdateUser_WrongCurrentPassword`
383
- - `TestUserHandler_UpdateUser_NewPasswordTooShort`
384
- - `TestUserHandler_UpdateUser_UserNotFound`
385
-
386
- **Criterio de conclusao:** `CGO_ENABLED=1 go test ./internal/handler/grpc/... -v` -- todos passando.
387
-
388
- **Dependencias:** Task 9 (handler implementado).
389
-
390
- ---
391
-
392
- ## Task 13: Testes Unitarios -- Interceptor Auth
393
-
394
- **Arquivos a MODIFICAR:**
395
- - `internal/infra/grpc/interceptors/auth_test.go`
396
-
397
- **Descricao:**
398
-
399
- Adicionar testes:
400
- - `TestAuthInterceptor_ValidToken_InjectsUserID` -- ctx contem user_id correto
401
- - `TestAuthInterceptor_SkipsLogin` -- table-driven com CreateUser e Login
402
- - `TestAuthInterceptor_ProtectedMethodsRequireAuth` -- table-driven com GetUserLogged e UpdateUser
403
-
404
- **Criterio de conclusao:** `CGO_ENABLED=1 go test ./internal/infra/grpc/interceptors/... -v` -- todos passando.
405
-
406
- **Dependencias:** Task 8 (interceptor atualizado).
407
-
408
- ---
409
-
410
- ## Task 14: Testes de Integracao -- Repository
411
-
412
- **Arquivos a CRIAR:**
413
- - `internal/repository/user_repository_integration_test.go`
414
-
415
- **Descricao:**
416
-
417
- Setup com SQLite em `t.TempDir()` + migracoes aplicadas via `database.NewDatabase`.
418
-
419
- Testes:
420
- - `TestUserRepository_Create_Success` -- todos os campos persistidos
421
- - `TestUserRepository_CreateAndGetByID` -- roundtrip create + get
422
- - `TestUserRepository_GetByEmail` -- retorna user correto com Address
423
- - `TestUserRepository_Create_DuplicateEmail` -- erro UNIQUE constraint
424
- - `TestUserRepository_GetByID_NotFound` -- erro
425
- - `TestUserRepository_GetByEmail_NotFound` -- erro
426
- - `TestUserRepository_Update_NameOnly`
427
- - `TestUserRepository_Update_Email`
428
- - `TestUserRepository_Update_PasswordHash`
429
- - `TestUserRepository_Update_Address`
430
- - `TestUserRepository_Update_UpdatedAtChanges`
431
- - `TestUserRepository_Create_GeneratesValidUUID`
432
-
433
- **Criterio de conclusao:** `CGO_ENABLED=1 go test ./internal/repository/... -v -tags=integration` -- todos passando.
434
-
435
- **Dependencias:** Task 6 (repository implementado), Task 3 (migracao).
436
-
437
- ---
438
-
439
- ## Task 15: Testes de Integracao -- Migracao
440
-
441
- **Arquivos a MODIFICAR:**
442
- - `internal/infra/database/sqlite_test.go`
443
-
444
- **Descricao:**
445
-
446
- Adicionar testes:
447
- - `TestRunMigrations_TableSchema_IncludesEndereco` -- PRAGMA table_info confirma coluna
448
- - `TestRunMigrations_EnderecocolumnDefaultEmpty` -- INSERT sem endereco retorna ""
449
- - `TestRunMigrations_IdempotentWithEndereco` -- segunda chamada NewDatabase sem erro
450
-
451
- **Criterio de conclusao:** `CGO_ENABLED=1 go test ./internal/infra/database/... -v` -- todos passando.
452
-
453
- **Dependencias:** Task 3 (migracao criada).
454
-
455
- ---
456
-
457
- ## Task 16: Testes E2E
458
-
459
- **Arquivos a CRIAR:**
460
- - `internal/e2e/user_e2e_test.go`
461
-
462
- **Descricao:**
463
-
464
- Setup: servidor gRPC real em porta efemera + cliente gRPC + SQLite em `t.TempDir()`.
465
-
466
- Fluxos:
467
- - `TestE2E_CreateUser_FullFlow` -- cadastro completo, verifica response sem token, UUID valido
468
- - `TestE2E_LoginAndGetUserLogged_FullFlow` -- login + consulta dados com Bearer token
469
- - `TestE2E_UpdateUser_PartialUpdate` -- login + update nome + get confirma persistencia
470
- - `TestE2E_ProtectedRoutes_RequireAuth` -- GetUserLogged e UpdateUser sem token retornam Unauthenticated
471
- - `TestE2E_UpdateUser_PasswordChangeAndReauth` -- troca senha + login nova senha ok + login senha antiga falha
472
-
473
- **Criterio de conclusao:** `CGO_ENABLED=1 go test ./internal/e2e/... -v` -- todos passando.
474
-
475
- **Dependencias:** Todas as tasks anteriores (1-15).
476
-
477
- ---
478
-
479
- ## Task 17: Validacao Final
480
-
481
- **Descricao:**
482
-
483
- 1. Executar `make build` -- compilacao sem erros
484
- 2. Executar `make test` -- todos os testes passando
485
- 3. Executar `make lint` -- sem problemas de lint nos protos
486
- 4. Verificar que `PasswordHash` e tokens JWT nao aparecem em nenhum log ou response
487
- 5. Verificar que rotas publicas (`CreateUser`, `Login`) funcionam sem token
488
- 6. Verificar que rotas protegidas (`GetUserLogged`, `UpdateUser`) exigem token valido
489
-
490
- **Criterio de conclusao:** Todos os checks passam. Feature pronta para code review.
491
-
492
- **Dependencias:** Task 16.
493
-
494
- ---
495
-
496
- ## Diagrama de Dependencias
497
-
498
- ```
499
- Task 1 (identity) ─────────────┬──────> Task 7 (service) ──────> Task 9 (handler) ──> Task 12 (test handler)
500
- │ │ │
501
- Task 2 (config) ────────────────┘ │ │
502
- ▼ ▼
503
- Task 3 (migracao) ──> Task 4 (sqlc) ──> Task 6 (repo) ──> Task 11 (test service) Task 10 (rotas publicas)
504
- │ │
505
- ▼ ▼
506
- Task 15 (test migracao) Task 14 (test integracao repo)
507
-
508
- Task 1 ──> Task 8 (interceptor) ──> Task 13 (test interceptor)
509
-
510
- Todas as tasks ──> Task 16 (E2E) ──> Task 17 (validacao final)
511
- ```
512
-
513
- ## Resumo de Execucao
514
-
515
- | Fase | Tasks | Descricao |
516
- |------|-------|-----------|
517
- | **Fase 1 -- Infraestrutura** | Tasks 1, 2, 3 | Identity, Config, Migracao (paralelas) |
518
- | **Fase 2 -- Persistencia** | Tasks 4, 5 | Queries SQLC, Protobuf (paralelas) |
519
- | **Fase 3 -- Camadas de Negocio** | Tasks 6, 7, 8 | Repository, Service, Interceptor |
520
- | **Fase 4 -- Exposicao** | Tasks 9, 10 | Handler gRPC, Rotas publicas |
521
- | **Fase 5 -- Testes Unitarios** | Tasks 11, 12, 13 | Testes service, handler, interceptor (paralelas) |
522
- | **Fase 6 -- Testes Integracao** | Tasks 14, 15 | Repository + Migracao (paralelas) |
523
- | **Fase 7 -- Testes E2E + Validacao** | Tasks 16, 17 | Fluxos completos + validacao final |
524
-
525
- **Total: 17 tasks organizadas em 7 fases.**
1
+ # TASK PLAN -- Modulo de Usuario v1
2
+
3
+ **Baseado em:** `docs/feature-user/v1/spec_tech.md`
4
+ **Data:** 2026-03-07
5
+ **Branch:** `user-feature`
6
+
7
+ ---
8
+
9
+ ## Visao Geral
10
+
11
+ Este Task Plan detalha a ordem de implementacao da feature de usuario conforme definido no SPEC_TECH v1. As tasks estao organizadas em ordem de dependencia (bottom-up), seguindo o fluxo: Infraestrutura -> Persistencia -> Repository -> Service -> Handler -> Testes de Integracao/E2E.
12
+
13
+ ---
14
+
15
+ ## Task 1: Pacote Identity (JWT + Contexto)
16
+
17
+ **Arquivos a CRIAR:**
18
+ - `internal/pkg/identity/identity.go`
19
+ - `internal/pkg/identity/identity_test.go`
20
+
21
+ **Descricao:**
22
+ Criar o pacote `internal/pkg/identity` com as seguintes funcoes publicas:
23
+
24
+ 1. **Tipo `Claims`** -- struct com `UserID`, `Email` e `jwt.RegisteredClaims`
25
+ 2. **`GenerateToken(userID, email, secret string, duration time.Duration) (string, time.Time, error)`** -- gera JWT assinado com HS256
26
+ 3. **`ParseToken(tokenString, secret string) (*Claims, error)`** -- valida e extrai claims; rejeita algoritmos que nao sejam HS256
27
+ 4. **`WithUserID(ctx context.Context, userID string) context.Context`** -- injeta user_id no contexto via chave unexportada
28
+ 5. **`UserIDFromContext(ctx context.Context) (string, bool)`** -- recupera user_id do contexto
29
+
30
+ **Testes unitarios (`identity_test.go`):**
31
+ - `TestGenerateToken_Success` -- token nao vazio, expiresAt no futuro
32
+ - `TestGenerateToken_TokenIsParseable` -- roundtrip generate -> parse
33
+ - `TestGenerateToken_ClaimsContainCorrectData` -- userID e email corretos
34
+ - `TestParseToken_WrongSecret` -- erro retornado
35
+ - `TestParseToken_ExpiredToken` -- erro retornado
36
+ - `TestParseToken_MalformedToken` -- erro retornado
37
+ - `TestContextRoundtrip_UserID` -- WithUserID + UserIDFromContext retorna valor correto
38
+ - `TestUserIDFromContext_Missing` -- retorna "", false
39
+
40
+ **Criterio de conclusao:** Todos os testes passando com `CGO_ENABLED=1 go test ./internal/pkg/identity/... -v`
41
+
42
+ **Dependencias:** Nenhuma (pacote isolado).
43
+
44
+ ---
45
+
46
+ ## Task 2: Configuracao -- Adicionar JWTExpirationHours
47
+
48
+ **Arquivos a MODIFICAR:**
49
+ - `internal/infra/config/config.go`
50
+ - `configs/config.yaml`
51
+
52
+ **Descricao:**
53
+
54
+ 1. Em `config.go`: adicionar campo `JWTExpirationHours int` na struct `Config` e ler via `v.GetInt("jwt.expiration_hours")`
55
+ 2. Em `config.yaml`: adicionar `expiration_hours: 24` dentro da secao `jwt`
56
+
57
+ **Criterio de conclusao:** `make build` compila sem erros; config carrega o novo campo corretamente.
58
+
59
+ **Dependencias:** Nenhuma.
60
+
61
+ ---
62
+
63
+ ## Task 3: Migracao -- Adicionar coluna `endereco`
64
+
65
+ **Arquivos a CRIAR:**
66
+ - `internal/db/migrations/002_add_endereco_usuarios.sql`
67
+
68
+ **Descricao:**
69
+
70
+ Criar migracao com:
71
+ ```sql
72
+ ALTER TABLE usuarios ADD COLUMN endereco TEXT NOT NULL DEFAULT '';
73
+ ```
74
+
75
+ Seguir a convencao do projeto (arquivo unico `.sql`, sem `.up`/`.down`).
76
+
77
+ **Criterio de conclusao:** Aplicacao inicia sem erros; `PRAGMA table_info(usuarios)` lista coluna `endereco`.
78
+
79
+ **Dependencias:** Nenhuma.
80
+
81
+ ---
82
+
83
+ ## Task 4: Queries SQLC -- Atualizar queries de usuario
84
+
85
+ **Arquivos a MODIFICAR:**
86
+ - `internal/db/queries/user.sql` (substituicao total do conteudo)
87
+
88
+ **Descricao:**
89
+
90
+ Substituir o conteudo do arquivo de queries por:
91
+
92
+ 1. **`CreateUser :one`** -- INSERT com campos `id, nome, email, senha_hash, endereco, data_criacao, data_atualizacao` + RETURNING *
93
+ 2. **`GetUserByID :one`** -- SELECT * WHERE id = ? LIMIT 1
94
+ 3. **`GetUserByEmail :one`** -- SELECT * WHERE email = ? LIMIT 1
95
+ 4. **`UpdateUser :one`** -- UPDATE SET nome, email, senha_hash, endereco, data_atualizacao WHERE id = ? RETURNING *
96
+
97
+ **Pos-execucao:** Executar `make generate` para regenerar `internal/db/sqlc/`.
98
+
99
+ **Criterio de conclusao:** `make generate` executa sem erros; codigo gerado em `internal/db/sqlc/` contem as novas queries.
100
+
101
+ **Dependencias:** Task 3 (migracao deve existir para o schema estar correto).
102
+
103
+ ---
104
+
105
+ ## Task 5: Protobuf -- Atualizar contratos gRPC
106
+
107
+ **Arquivos a MODIFICAR:**
108
+ - `api/proto/v1/user.proto` (substituicao total do conteudo)
109
+
110
+ **Descricao:**
111
+
112
+ Substituir o conteudo do proto conforme SPEC_TECH secao 8.1:
113
+
114
+ 1. Mensagem `User` com campos: id, name, email, address, created_at, updated_at
115
+ 2. `CreateUserRequest` com name, email, password, address
116
+ 3. `CreateUserResponse` com id, name, email, created_at
117
+ 4. `LoginRequest` com email, password
118
+ 5. `LoginResponse` com token, expires_at, id, name, email
119
+ 6. `GetUserLoggedRequest` (vazio)
120
+ 7. `GetUserLoggedResponse` com User
121
+ 8. `UpdateUserRequest` com optional string para name, email, address, new_password, current_password
122
+ 9. `UpdateUserResponse` com User
123
+ 10. Servico `UserService` com 4 RPCs: CreateUser, Login, GetUserLogged, UpdateUser
124
+
125
+ **Pos-execucao:** Executar `make generate` para regenerar `gen/proto/v1/`.
126
+
127
+ **Criterio de conclusao:** `make generate` e `make build` executam sem erros.
128
+
129
+ **Dependencias:** Nenhuma (pode ser feita em paralelo com Tasks 1-4).
130
+
131
+ ---
132
+
133
+ ## Task 6: Repository -- Estender UserRepository
134
+
135
+ **Arquivos a MODIFICAR:**
136
+ - `internal/repository/user_repository.go`
137
+
138
+ **Descricao:**
139
+
140
+ 1. Adicionar campo `Address string` ao modelo `User`
141
+ 2. Adicionar metodos a interface `UserRepository`:
142
+ - `GetByEmail(ctx context.Context, email string) (*User, error)`
143
+ - `Update(ctx context.Context, user *User) (*User, error)`
144
+ 3. Implementar os novos metodos na struct `userRepository`
145
+ 4. Atualizar mapeamento SQLC->dominio no `Create` e `GetByID` para incluir `Address` (endereco -> Address)
146
+ 5. Implementar mapeamento no `GetByEmail` e `Update`
147
+
148
+ **Criterio de conclusao:** `make build` compila sem erros.
149
+
150
+ **Dependencias:** Task 4 (queries SQLC regeneradas).
151
+
152
+ ---
153
+
154
+ ## Task 7: Service -- Estender UserService
155
+
156
+ **Arquivos a MODIFICAR:**
157
+ - `internal/service/user_service.go`
158
+
159
+ **Descricao:**
160
+
161
+ 1. **Adicionar dependencias ao construtor:**
162
+ - `*config.Config` e `*zap.Logger` como parametros de `NewUserService`
163
+ - FX resolve automaticamente (sem alterar `fx.go`)
164
+
165
+ 2. **Adicionar variaveis de erro exportadas:**
166
+ - `ErrInvalidName`, `ErrInvalidEmail`, `ErrEmailAlreadyExists`, `ErrPasswordTooShort`, `ErrAddressRequired`
167
+ - `ErrInvalidCredentials`, `ErrUserNotFound`, `ErrNoFieldsToUpdate`
168
+ - `ErrCurrentPasswordRequired`, `ErrCurrentPasswordWrong`
169
+
170
+ 3. **Adicionar tipos:**
171
+ - `CreateUserInput` -- atualizar com campo `Address`
172
+ - `LoginResult` -- novo (Token, ExpiresAt, User)
173
+ - `UpdateUserInput` -- novo (ponteiros *string para campos opcionais)
174
+
175
+ 4. **Adicionar metodos a interface `UserService`:**
176
+ - `Login(ctx context.Context, email, password string) (*LoginResult, error)`
177
+ - `GetUserLogged(ctx context.Context, userID string) (*repository.User, error)`
178
+ - `UpdateUser(ctx context.Context, userID string, input UpdateUserInput) (*repository.User, error)`
179
+
180
+ 5. **Implementar `CreateUser` (atualizar):**
181
+ - Adicionar validacoes: name vazio, email (net/mail), senha >= 8 chars, address vazio
182
+ - Verificar email unico via `repo.GetByEmail`
183
+ - Hash com bcrypt, UUID, timestamps
184
+ - Persistir via `repo.Create`
185
+ - Logging: Info "usuario criado com sucesso"
186
+
187
+ 6. **Implementar `Login`:**
188
+ - `repo.GetByEmail` -- se nao existe: `ErrInvalidCredentials`
189
+ - `bcrypt.CompareHashAndPassword` -- se falha: `ErrInvalidCredentials`
190
+ - `identity.GenerateToken(userID, email, secret, duration)`
191
+ - Limpar PasswordHash antes de retornar
192
+ - Logging: Info "login bem-sucedido" / Warn "tentativa de login falha"
193
+
194
+ 7. **Implementar `GetUserLogged`:**
195
+ - `repo.GetByID(userID)` -- se nao encontra: `ErrUserNotFound`
196
+ - Limpar PasswordHash
197
+ - Retornar User
198
+
199
+ 8. **Implementar `UpdateUser`:**
200
+ - Validar ao menos 1 campo nao-nil
201
+ - Buscar user atual via `repo.GetByID`
202
+ - Se email: validar formato + unicidade
203
+ - Se new_password: exigir current_password, validar tamanho, verificar hash, gerar novo hash
204
+ - Aplicar campos nao-nil sobre user atual
205
+ - Persistir via `repo.Update`
206
+ - Limpar PasswordHash antes de retornar
207
+ - Logging: Info "dados atualizados" / Info "senha alterada"
208
+
209
+ **Criterio de conclusao:** `make build` compila sem erros.
210
+
211
+ **Dependencias:** Task 1 (identity), Task 2 (config), Task 6 (repository).
212
+
213
+ ---
214
+
215
+ ## Task 8: Interceptor Auth -- Integrar com identity
216
+
217
+ **Arquivos a MODIFICAR:**
218
+ - `internal/infra/grpc/interceptors/auth.go`
219
+
220
+ **Descricao:**
221
+
222
+ 1. Adicionar `/proto.v1.UserService/Login` em `skipMethods`
223
+ 2. Substituir parse manual do token por `identity.ParseToken(tokenString, cfg.JWTSecret)`
224
+ 3. Apos validacao bem-sucedida: `ctx = identity.WithUserID(ctx, claims.UserID)`
225
+ 4. Passar ctx atualizado ao handler
226
+
227
+ **Criterio de conclusao:** `make build` compila sem erros.
228
+
229
+ **Dependencias:** Task 1 (identity).
230
+
231
+ ---
232
+
233
+ ## Task 9: Handler gRPC -- Estender UserHandler
234
+
235
+ **Arquivos a MODIFICAR:**
236
+ - `internal/handler/grpc/user_handler.go`
237
+
238
+ **Descricao:**
239
+
240
+ 1. **Atualizar `CreateUser`:**
241
+ - Extrair `address` do request
242
+ - Mapear response com `CreateUserResponse{id, name, email, created_at}`
243
+
244
+ 2. **Implementar `Login`:**
245
+ - Extrair email e password do request
246
+ - Chamar `service.Login`
247
+ - Retornar `LoginResponse{token, expires_at, id, name, email}`
248
+
249
+ 3. **Implementar `GetUserLogged`:**
250
+ - `identity.UserIDFromContext(ctx)` -- se ausente: `codes.Unauthenticated`
251
+ - Chamar `service.GetUserLogged(userID)`
252
+ - Retornar `GetUserLoggedResponse{User{...}}`
253
+
254
+ 4. **Implementar `UpdateUser`:**
255
+ - `identity.UserIDFromContext(ctx)` -- se ausente: `codes.Unauthenticated`
256
+ - Construir `UpdateUserInput` com ponteiros (nil = campo ausente)
257
+ - Chamar `service.UpdateUser(userID, input)`
258
+ - Retornar `UpdateUserResponse{User{...}}`
259
+
260
+ 5. **Remover `GetUser`** (substituido por `GetUserLogged`)
261
+
262
+ 6. **Atualizar `mapServiceError`:**
263
+ - Adicionar mapeamento para todos os novos erros conforme SPEC_TECH secao 11.2
264
+
265
+ 7. **Criar funcao auxiliar `domainUserToProto`:**
266
+ - Converte `*repository.User` para `*pb.User`
267
+
268
+ **Criterio de conclusao:** `make build` compila sem erros.
269
+
270
+ **Dependencias:** Task 5 (proto gerado), Task 7 (service).
271
+
272
+ ---
273
+
274
+ ## Task 10: Configuracao de Rotas Publicas
275
+
276
+ **Arquivos a MODIFICAR:**
277
+ - `configs/config.yaml`
278
+
279
+ **Descricao:**
280
+
281
+ Garantir que `skip_methods` no config inclua:
282
+ - `/proto.v1.UserService/CreateUser`
283
+ - `/proto.v1.UserService/Login`
284
+
285
+ **Criterio de conclusao:** Servidor inicia sem erros; rotas publicas acessiveis sem token.
286
+
287
+ **Dependencias:** Task 8 (interceptor), Task 9 (handler).
288
+
289
+ ---
290
+
291
+ ## Task 11: Testes Unitarios -- Service
292
+
293
+ **Arquivos a MODIFICAR:**
294
+ - `internal/service/user_service_test.go`
295
+
296
+ **Descricao:**
297
+
298
+ Atualizar mock `MockUserRepository` com metodos `GetByEmail` e `Update`.
299
+
300
+ **CreateUser (atualizar existentes + novos):**
301
+ - `TestCreateUser_Success` -- incluir Address
302
+ - `TestCreateUser_PasswordIsHashed` -- bcrypt roundtrip
303
+ - `TestCreateUser_GeneratesValidUUID`
304
+ - `TestCreateUser_ErrorEmptyName`
305
+ - `TestCreateUser_ErrorInvalidEmailFormat` (novo)
306
+ - `TestCreateUser_ErrorPasswordTooShort_7Chars` (novo)
307
+ - `TestCreateUser_PasswordExactly8Chars` (novo)
308
+ - `TestCreateUser_ErrorEmptyAddress` (novo)
309
+ - `TestCreateUser_ErrorRepositoryFailure`
310
+
311
+ **Login (novos):**
312
+ - `TestLogin_Success`
313
+ - `TestLogin_ResultDoesNotExposePasswordHash`
314
+ - `TestLogin_TokenExpiresAtRespectsConfig`
315
+ - `TestLogin_EmailNotFound_GenericError`
316
+ - `TestLogin_WrongPassword_GenericError`
317
+ - `TestLogin_ErrorRepositoryFailure`
318
+
319
+ **GetUserLogged (novos):**
320
+ - `TestGetUserLogged_Success`
321
+ - `TestGetUserLogged_UserNotFound`
322
+ - `TestGetUserLogged_RepositoryError`
323
+
324
+ **UpdateUser (novos):**
325
+ - `TestUpdateUser_NameOnly`
326
+ - `TestUpdateUser_EmailOnly`
327
+ - `TestUpdateUser_AddressOnly`
328
+ - `TestUpdateUser_PasswordChange_Success`
329
+ - `TestUpdateUser_MultipleFields`
330
+ - `TestUpdateUser_NoFieldsToUpdate`
331
+ - `TestUpdateUser_InvalidEmailFormat`
332
+ - `TestUpdateUser_EmailAlreadyExists`
333
+ - `TestUpdateUser_CurrentPasswordMissing`
334
+ - `TestUpdateUser_WrongCurrentPassword`
335
+ - `TestUpdateUser_NewPasswordTooShort`
336
+ - `TestUpdateUser_UserNotFound`
337
+ - `TestUpdateUser_RepositoryFailure`
338
+
339
+ **Criterio de conclusao:** `CGO_ENABLED=1 go test ./internal/service/... -v` -- todos passando.
340
+
341
+ **Dependencias:** Task 7 (service implementado).
342
+
343
+ ---
344
+
345
+ ## Task 12: Testes Unitarios -- Handler
346
+
347
+ **Arquivos a MODIFICAR:**
348
+ - `internal/handler/grpc/user_handler_test.go`
349
+
350
+ **Descricao:**
351
+
352
+ Atualizar mock `MockUserService` com metodos `Login`, `GetUserLogged`, `UpdateUser`. Remover testes de `GetUser`.
353
+
354
+ **CreateUser:**
355
+ - `TestUserHandler_CreateUser_Success`
356
+ - `TestUserHandler_CreateUser_ResponseDoesNotExposePasswordHash`
357
+ - `TestUserHandler_CreateUser_EmptyName`
358
+ - `TestUserHandler_CreateUser_EmptyEmail`
359
+ - `TestUserHandler_CreateUser_ShortPassword`
360
+ - `TestUserHandler_CreateUser_EmptyAddress`
361
+ - `TestUserHandler_CreateUser_DuplicateEmail`
362
+ - `TestUserHandler_CreateUser_InternalError`
363
+
364
+ **Login:**
365
+ - `TestUserHandler_Login_Success`
366
+ - `TestUserHandler_Login_InvalidCredentials`
367
+ - `TestUserHandler_Login_InternalError`
368
+
369
+ **GetUserLogged:**
370
+ - `TestUserHandler_GetUserLogged_Success`
371
+ - `TestUserHandler_GetUserLogged_MissingUserID`
372
+ - `TestUserHandler_GetUserLogged_NotFound`
373
+
374
+ **UpdateUser:**
375
+ - `TestUserHandler_UpdateUser_NameOnly`
376
+ - `TestUserHandler_UpdateUser_EmailOnly`
377
+ - `TestUserHandler_UpdateUser_PasswordChange`
378
+ - `TestUserHandler_UpdateUser_MultipleFields`
379
+ - `TestUserHandler_UpdateUser_NoFields`
380
+ - `TestUserHandler_UpdateUser_EmailAlreadyExists`
381
+ - `TestUserHandler_UpdateUser_CurrentPasswordMissing`
382
+ - `TestUserHandler_UpdateUser_WrongCurrentPassword`
383
+ - `TestUserHandler_UpdateUser_NewPasswordTooShort`
384
+ - `TestUserHandler_UpdateUser_UserNotFound`
385
+
386
+ **Criterio de conclusao:** `CGO_ENABLED=1 go test ./internal/handler/grpc/... -v` -- todos passando.
387
+
388
+ **Dependencias:** Task 9 (handler implementado).
389
+
390
+ ---
391
+
392
+ ## Task 13: Testes Unitarios -- Interceptor Auth
393
+
394
+ **Arquivos a MODIFICAR:**
395
+ - `internal/infra/grpc/interceptors/auth_test.go`
396
+
397
+ **Descricao:**
398
+
399
+ Adicionar testes:
400
+ - `TestAuthInterceptor_ValidToken_InjectsUserID` -- ctx contem user_id correto
401
+ - `TestAuthInterceptor_SkipsLogin` -- table-driven com CreateUser e Login
402
+ - `TestAuthInterceptor_ProtectedMethodsRequireAuth` -- table-driven com GetUserLogged e UpdateUser
403
+
404
+ **Criterio de conclusao:** `CGO_ENABLED=1 go test ./internal/infra/grpc/interceptors/... -v` -- todos passando.
405
+
406
+ **Dependencias:** Task 8 (interceptor atualizado).
407
+
408
+ ---
409
+
410
+ ## Task 14: Testes de Integracao -- Repository
411
+
412
+ **Arquivos a CRIAR:**
413
+ - `internal/repository/user_repository_integration_test.go`
414
+
415
+ **Descricao:**
416
+
417
+ Setup com SQLite em `t.TempDir()` + migracoes aplicadas via `database.NewDatabase`.
418
+
419
+ Testes:
420
+ - `TestUserRepository_Create_Success` -- todos os campos persistidos
421
+ - `TestUserRepository_CreateAndGetByID` -- roundtrip create + get
422
+ - `TestUserRepository_GetByEmail` -- retorna user correto com Address
423
+ - `TestUserRepository_Create_DuplicateEmail` -- erro UNIQUE constraint
424
+ - `TestUserRepository_GetByID_NotFound` -- erro
425
+ - `TestUserRepository_GetByEmail_NotFound` -- erro
426
+ - `TestUserRepository_Update_NameOnly`
427
+ - `TestUserRepository_Update_Email`
428
+ - `TestUserRepository_Update_PasswordHash`
429
+ - `TestUserRepository_Update_Address`
430
+ - `TestUserRepository_Update_UpdatedAtChanges`
431
+ - `TestUserRepository_Create_GeneratesValidUUID`
432
+
433
+ **Criterio de conclusao:** `CGO_ENABLED=1 go test ./internal/repository/... -v -tags=integration` -- todos passando.
434
+
435
+ **Dependencias:** Task 6 (repository implementado), Task 3 (migracao).
436
+
437
+ ---
438
+
439
+ ## Task 15: Testes de Integracao -- Migracao
440
+
441
+ **Arquivos a MODIFICAR:**
442
+ - `internal/infra/database/sqlite_test.go`
443
+
444
+ **Descricao:**
445
+
446
+ Adicionar testes:
447
+ - `TestRunMigrations_TableSchema_IncludesEndereco` -- PRAGMA table_info confirma coluna
448
+ - `TestRunMigrations_EnderecocolumnDefaultEmpty` -- INSERT sem endereco retorna ""
449
+ - `TestRunMigrations_IdempotentWithEndereco` -- segunda chamada NewDatabase sem erro
450
+
451
+ **Criterio de conclusao:** `CGO_ENABLED=1 go test ./internal/infra/database/... -v` -- todos passando.
452
+
453
+ **Dependencias:** Task 3 (migracao criada).
454
+
455
+ ---
456
+
457
+ ## Task 16: Testes E2E
458
+
459
+ **Arquivos a CRIAR:**
460
+ - `internal/e2e/user_e2e_test.go`
461
+
462
+ **Descricao:**
463
+
464
+ Setup: servidor gRPC real em porta efemera + cliente gRPC + SQLite em `t.TempDir()`.
465
+
466
+ Fluxos:
467
+ - `TestE2E_CreateUser_FullFlow` -- cadastro completo, verifica response sem token, UUID valido
468
+ - `TestE2E_LoginAndGetUserLogged_FullFlow` -- login + consulta dados com Bearer token
469
+ - `TestE2E_UpdateUser_PartialUpdate` -- login + update nome + get confirma persistencia
470
+ - `TestE2E_ProtectedRoutes_RequireAuth` -- GetUserLogged e UpdateUser sem token retornam Unauthenticated
471
+ - `TestE2E_UpdateUser_PasswordChangeAndReauth` -- troca senha + login nova senha ok + login senha antiga falha
472
+
473
+ **Criterio de conclusao:** `CGO_ENABLED=1 go test ./internal/e2e/... -v` -- todos passando.
474
+
475
+ **Dependencias:** Todas as tasks anteriores (1-15).
476
+
477
+ ---
478
+
479
+ ## Task 17: Validacao Final
480
+
481
+ **Descricao:**
482
+
483
+ 1. Executar `make build` -- compilacao sem erros
484
+ 2. Executar `make test` -- todos os testes passando
485
+ 3. Executar `make lint` -- sem problemas de lint nos protos
486
+ 4. Verificar que `PasswordHash` e tokens JWT nao aparecem em nenhum log ou response
487
+ 5. Verificar que rotas publicas (`CreateUser`, `Login`) funcionam sem token
488
+ 6. Verificar que rotas protegidas (`GetUserLogged`, `UpdateUser`) exigem token valido
489
+
490
+ **Criterio de conclusao:** Todos os checks passam. Feature pronta para code review.
491
+
492
+ **Dependencias:** Task 16.
493
+
494
+ ---
495
+
496
+ ## Diagrama de Dependencias
497
+
498
+ ```
499
+ Task 1 (identity) ─────────────┬──────> Task 7 (service) ──────> Task 9 (handler) ──> Task 12 (test handler)
500
+ │ │ │
501
+ Task 2 (config) ────────────────┘ │ │
502
+ ▼ ▼
503
+ Task 3 (migracao) ──> Task 4 (sqlc) ──> Task 6 (repo) ──> Task 11 (test service) Task 10 (rotas publicas)
504
+ │ │
505
+ ▼ ▼
506
+ Task 15 (test migracao) Task 14 (test integracao repo)
507
+
508
+ Task 1 ──> Task 8 (interceptor) ──> Task 13 (test interceptor)
509
+
510
+ Todas as tasks ──> Task 16 (E2E) ──> Task 17 (validacao final)
511
+ ```
512
+
513
+ ## Resumo de Execucao
514
+
515
+ | Fase | Tasks | Descricao |
516
+ |------|-------|-----------|
517
+ | **Fase 1 -- Infraestrutura** | Tasks 1, 2, 3 | Identity, Config, Migracao (paralelas) |
518
+ | **Fase 2 -- Persistencia** | Tasks 4, 5 | Queries SQLC, Protobuf (paralelas) |
519
+ | **Fase 3 -- Camadas de Negocio** | Tasks 6, 7, 8 | Repository, Service, Interceptor |
520
+ | **Fase 4 -- Exposicao** | Tasks 9, 10 | Handler gRPC, Rotas publicas |
521
+ | **Fase 5 -- Testes Unitarios** | Tasks 11, 12, 13 | Testes service, handler, interceptor (paralelas) |
522
+ | **Fase 6 -- Testes Integracao** | Tasks 14, 15 | Repository + Migracao (paralelas) |
523
+ | **Fase 7 -- Testes E2E + Validacao** | Tasks 16, 17 | Fluxos completos + validacao final |
524
+
525
+ **Total: 17 tasks organizadas em 7 fases.**