adi_dev_workflow 1.1.0 → 1.2.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.
- package/bin/index.js +8 -8
- package/frameworks/agents/qa-staff-engineer.md +311 -0
- package/frameworks/agents/qa-validation-expert.md +458 -0
- package/frameworks/agents/tech-review-conformance.md +200 -0
- package/frameworks/commands/generate-prompt.md +33 -100
- package/frameworks/commands/ministack/README.md +2 -0
- package/frameworks/commands/ministack/code-review.md +2 -0
- package/frameworks/commands/ministack/generate-intent.md +2 -0
- package/frameworks/commands/ministack/generate-scope.md +2 -0
- package/frameworks/commands/ministack/generate-tasks.md +2 -0
- package/frameworks/commands/ministack/generate-tech-direction.md +2 -0
- package/frameworks/commands/ministack/run-ministack-tasks.md +3 -0
- package/frameworks/commands/ministack/run-ministack-withlinear.md +2 -0
- package/frameworks/commands/ministack/status.md +2 -0
- package/frameworks/commands/sdd/code-review.md +2 -0
- package/frameworks/commands/sdd/generate-prd.md +2 -0
- package/frameworks/commands/sdd/generate-task-plan.md +2 -0
- package/frameworks/commands/sdd/generate-tech-direction.md +2 -0
- package/frameworks/commands/sdd/generate-tech-spec.md +2 -0
- package/frameworks/commands/sdd/generate-tests.md +2 -0
- package/frameworks/commands/sdd/run_tasks.md +3 -0
- package/frameworks/commands/sdd/run_tasks_withlinear.md +2 -0
- package/frameworks/commands/sdd/status.md +2 -0
- package/frameworks/commands/sdd/validate-sdd.md +2 -0
- package/frameworks/commands/sync-tasks-to-linear.md +2 -0
- package/frameworks/commands/taskcard/generate-taskcard.md +106 -33
- package/frameworks/commands/taskcard/run-taskcard.md +2 -0
- package/frameworks/config/ai-framework-config.yaml +112 -0
- package/frameworks/skills/ministack-intent-expert/SKILL.md +15 -1
- package/frameworks/skills/ministack-scope-expert/SKILL.md +17 -1
- package/frameworks/skills/sdd-prd-expert/SKILL.md +14 -0
- package/frameworks/skills/sdd-task-plan-expert/SKILL.md +14 -0
- package/frameworks/skills/taskcard-expert/SKILL.md +30 -11
- package/frameworks/templates/prompt_template.md +207 -0
- package/package.json +28 -28
- package/src/cli.js +121 -121
- package/src/installer.js +155 -136
- package/src/transformer.js +86 -86
- package/frameworks/skills/ministack-tasks-expert/SKILL.md +0 -192
- package/frameworks/skills/ministack-tasks-expert/templates/task_plan_template.md +0 -78
- package/frameworks/skills/ministack-tasks-expert/templates/task_template.md +0 -103
- package/frameworks/skills/ministack-tech-direction-expert/SKILL.md +0 -218
- package/frameworks/skills/ministack-tech-direction-expert/evals/evals.json +0 -1
- package/frameworks/skills/ministack-tech-direction-expert/templates/tech_direction-template.md +0 -17
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/benchmark.json +0 -99
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/benchmark.md +0 -64
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/eval_metadata.json +0 -12
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/with_skill/grading.json +0 -32
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/with_skill/outputs/response.md +0 -134
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/with_skill/outputs/transcript.md +0 -68
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/with_skill/timing.json +0 -5
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/without_skill/grading.json +0 -32
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/without_skill/outputs/response.md +0 -525
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/without_skill/outputs/transcript.md +0 -30
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/without_skill/timing.json +0 -5
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/eval_metadata.json +0 -12
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/with_skill/grading.json +0 -32
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/with_skill/outputs/response.md +0 -1126
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/with_skill/outputs/transcript.md +0 -131
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/with_skill/timing.json +0 -5
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/without_skill/grading.json +0 -32
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/without_skill/outputs/response.md +0 -452
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/without_skill/outputs/transcript.md +0 -78
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/without_skill/timing.json +0 -5
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/eval_metadata.json +0 -12
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/with_skill/grading.json +0 -32
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/with_skill/outputs/response.md +0 -101
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/with_skill/outputs/transcript.md +0 -133
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/with_skill/timing.json +0 -5
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/without_skill/grading.json +0 -32
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/without_skill/outputs/response.md +0 -248
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/without_skill/outputs/transcript.md +0 -49
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/without_skill/timing.json +0 -5
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/review.html +0 -1325
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/benchmark.json +0 -94
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/benchmark.md +0 -67
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/eval_metadata.json +0 -12
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/with_skill/grading.json +0 -32
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/with_skill/outputs/response.md +0 -117
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/with_skill/outputs/transcript.md +0 -91
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/with_skill/timing.json +0 -1
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/without_skill/grading.json +0 -32
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/without_skill/outputs/response.md +0 -694
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/without_skill/outputs/transcript.md +0 -45
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/without_skill/timing.json +0 -1
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/eval_metadata.json +0 -12
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/with_skill/grading.json +0 -32
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/with_skill/outputs/response.md +0 -1087
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/with_skill/outputs/transcript.md +0 -124
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/with_skill/timing.json +0 -1
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/without_skill/grading.json +0 -32
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/without_skill/outputs/response.md +0 -458
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/without_skill/outputs/transcript.md +0 -84
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/without_skill/timing.json +0 -1
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/eval_metadata.json +0 -12
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/with_skill/grading.json +0 -32
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/with_skill/outputs/response.md +0 -70
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/with_skill/outputs/transcript.md +0 -148
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/with_skill/timing.json +0 -1
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/without_skill/grading.json +0 -32
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/without_skill/outputs/response.md +0 -249
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/without_skill/outputs/transcript.md +0 -80
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/without_skill/timing.json +0 -1
- package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/review.html +0 -1325
- package/frameworks/skills/sdd-tech-direction-expert/SKILL.md +0 -223
- package/frameworks/skills/sdd-tech-direction-expert/evals/evals.json +0 -1
- package/frameworks/skills/sdd-tech-direction-expert/templates/tech_direction-template.md +0 -23
- package/frameworks/skills/sdd-tech-spec-expert/SKILL.md +0 -304
- package/frameworks/skills/sdd-tech-spec-expert/evals/evals.json +0 -199
- package/frameworks/skills/sdd-tech-spec-expert/templates/spec_tech_template.md +0 -290
- package/frameworks/skills/sdd-tech-spec-expert/templates/tech_direction-template.md +0 -23
|
@@ -1,694 +0,0 @@
|
|
|
1
|
-
# TASK PLAN — Modulo de Usuario v1
|
|
2
|
-
|
|
3
|
-
## Informacoes Gerais
|
|
4
|
-
|
|
5
|
-
| Campo | Valor |
|
|
6
|
-
|-------|-------|
|
|
7
|
-
| **Feature** | Modulo de Usuario — Vakinha Burger |
|
|
8
|
-
| **SPEC_TECH** | `docs/feature-user/v1/spec_tech.md` |
|
|
9
|
-
| **Data** | 2026-03-08 |
|
|
10
|
-
| **Branch** | `user-feature` |
|
|
11
|
-
| **Total de Tasks** | 12 |
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Estrategia de Implementacao
|
|
16
|
-
|
|
17
|
-
A implementacao segue a ordem do fluxo de dependencias do projeto: **Config -> Database -> Identity -> Repository -> Service -> Handler -> Interceptor -> Testes**. Cada task e independente o suficiente para ser validada isoladamente, mas deve ser implementada na ordem sequencial definida.
|
|
18
|
-
|
|
19
|
-
**Convencao de prioridade:**
|
|
20
|
-
- **P0** — Bloqueante para tasks seguintes
|
|
21
|
-
- **P1** — Necessario para completar a feature
|
|
22
|
-
- **P2** — Qualidade e testes complementares
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## Tasks
|
|
27
|
-
|
|
28
|
-
### TASK 01 — Configuracao: Adicionar `jwt.expiration_hours`
|
|
29
|
-
|
|
30
|
-
**Prioridade:** P0
|
|
31
|
-
**Tipo:** Modificar arquivos existentes
|
|
32
|
-
|
|
33
|
-
**Arquivos a modificar:**
|
|
34
|
-
- `configs/config.yaml` — adicionar `expiration_hours: 24` dentro do bloco `jwt`
|
|
35
|
-
- `internal/infra/config/config.go` — adicionar campo `JWTExpirationHours int` na struct `Config` e leitura via `v.GetInt("jwt.expiration_hours")`
|
|
36
|
-
|
|
37
|
-
**Detalhes:**
|
|
38
|
-
1. Em `configs/config.yaml`, adicionar `expiration_hours: 24` sob a chave `jwt`
|
|
39
|
-
2. Em `config.go`, adicionar o campo `JWTExpirationHours int` na struct `Config`
|
|
40
|
-
3. No construtor `NewConfig`, adicionar `v.GetInt("jwt.expiration_hours")` para popular o campo
|
|
41
|
-
|
|
42
|
-
**Criterio de conclusao:**
|
|
43
|
-
- `Config` carrega `JWTExpirationHours` corretamente a partir do YAML
|
|
44
|
-
- Valor padrao: 24
|
|
45
|
-
- `make build` compila sem erro
|
|
46
|
-
|
|
47
|
-
**Dependencias:** Nenhuma
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
### TASK 02 — Migration: Adicionar coluna `endereco` na tabela `usuarios`
|
|
52
|
-
|
|
53
|
-
**Prioridade:** P0
|
|
54
|
-
**Tipo:** Criar arquivo novo (se nao existir) ou validar existente
|
|
55
|
-
|
|
56
|
-
**Arquivo:**
|
|
57
|
-
- `internal/db/migrations/002_add_endereco_usuarios.sql`
|
|
58
|
-
|
|
59
|
-
**Detalhes:**
|
|
60
|
-
1. Verificar se o arquivo ja existe (nota: conforme listagem do codebase, o arquivo `002_add_endereco_usuarios.sql` ja existe)
|
|
61
|
-
2. Validar que o conteudo corresponde ao especificado na SPEC_TECH secao 9.1:
|
|
62
|
-
```sql
|
|
63
|
-
ALTER TABLE usuarios ADD COLUMN endereco TEXT NOT NULL DEFAULT '';
|
|
64
|
-
```
|
|
65
|
-
3. Se ja existir com conteudo correto, esta task esta concluida
|
|
66
|
-
|
|
67
|
-
**Criterio de conclusao:**
|
|
68
|
-
- Migration executa sem erro na inicializacao
|
|
69
|
-
- Coluna `endereco` presente na tabela `usuarios` com default vazio
|
|
70
|
-
|
|
71
|
-
**Dependencias:** Nenhuma
|
|
72
|
-
|
|
73
|
-
---
|
|
74
|
-
|
|
75
|
-
### TASK 03 — Proto: Atualizar `user.proto` com novos endpoints
|
|
76
|
-
|
|
77
|
-
**Prioridade:** P0
|
|
78
|
-
**Tipo:** Modificar arquivo existente
|
|
79
|
-
|
|
80
|
-
**Arquivo a modificar:**
|
|
81
|
-
- `api/proto/v1/user.proto` — substituicao total conforme SPEC_TECH secao 8.1
|
|
82
|
-
|
|
83
|
-
**Detalhes:**
|
|
84
|
-
1. Atualizar mensagem `User` para incluir campo `address` (field 4)
|
|
85
|
-
2. Atualizar `CreateUserRequest` para incluir campo `address` (field 4)
|
|
86
|
-
3. Adicionar mensagens: `LoginRequest`, `LoginResponse`, `GetUserLoggedRequest`, `GetUserLoggedResponse`, `UpdateUserRequest` (com campos `optional`), `UpdateUserResponse`
|
|
87
|
-
4. Atualizar servico `UserService` com 4 RPCs: `CreateUser`, `Login`, `GetUserLogged`, `UpdateUser`
|
|
88
|
-
5. Remover `GetUser` (se existir) — substituido por `GetUserLogged`
|
|
89
|
-
6. Executar `make generate` para regenerar codigo
|
|
90
|
-
|
|
91
|
-
**Criterio de conclusao:**
|
|
92
|
-
- `make generate` executa sem erros
|
|
93
|
-
- Codigo gerado em `gen/proto/v1/` reflete as novas mensagens e servico
|
|
94
|
-
- `make lint` passa sem erros nos protobufs
|
|
95
|
-
|
|
96
|
-
**Dependencias:** Nenhuma
|
|
97
|
-
|
|
98
|
-
---
|
|
99
|
-
|
|
100
|
-
### TASK 04 — SQLC: Atualizar queries de usuario
|
|
101
|
-
|
|
102
|
-
**Prioridade:** P0
|
|
103
|
-
**Tipo:** Modificar arquivo existente
|
|
104
|
-
|
|
105
|
-
**Arquivo a modificar:**
|
|
106
|
-
- `internal/db/queries/user.sql` — substituicao total conforme SPEC_TECH secao 9.2
|
|
107
|
-
|
|
108
|
-
**Detalhes:**
|
|
109
|
-
1. Atualizar `CreateUser` para incluir `endereco`, `data_criacao`, `data_atualizacao` nos campos de INSERT
|
|
110
|
-
2. Manter `GetUserByID :one` — busca por ID
|
|
111
|
-
3. Adicionar `GetUserByEmail :one` — busca por email
|
|
112
|
-
4. Adicionar `UpdateUser :one` — update de `nome`, `email`, `senha_hash`, `endereco`, `data_atualizacao` com `RETURNING *`
|
|
113
|
-
5. Executar `make generate` para regenerar codigo SQLC
|
|
114
|
-
|
|
115
|
-
**Criterio de conclusao:**
|
|
116
|
-
- `make generate` executa sem erros
|
|
117
|
-
- Codigo gerado em `internal/db/sqlc/` reflete as novas queries
|
|
118
|
-
- Struct `CreateUserParams` inclui campo `Endereco`
|
|
119
|
-
|
|
120
|
-
**Dependencias:** TASK 02 (migration deve existir para SQLC reconhecer a coluna)
|
|
121
|
-
|
|
122
|
-
---
|
|
123
|
-
|
|
124
|
-
### TASK 05 — Identity: Criar pacote `internal/pkg/identity`
|
|
125
|
-
|
|
126
|
-
**Prioridade:** P0
|
|
127
|
-
**Tipo:** Criar arquivo novo
|
|
128
|
-
|
|
129
|
-
**Arquivo a criar:**
|
|
130
|
-
- `internal/pkg/identity/identity.go`
|
|
131
|
-
|
|
132
|
-
**Detalhes:**
|
|
133
|
-
1. Definir tipo `Claims` com `UserID string`, `Email string` e `jwt.RegisteredClaims`
|
|
134
|
-
2. Implementar `GenerateToken(userID, email, secret string, duration time.Duration) (string, time.Time, error)`
|
|
135
|
-
- Assinatura HS256
|
|
136
|
-
- Preencher `RegisteredClaims{ExpiresAt, IssuedAt}`
|
|
137
|
-
3. Implementar `ParseToken(tokenString, secret string) (*Claims, error)`
|
|
138
|
-
- Validar algoritmo HS256 (rejeitar outros metodos de assinatura)
|
|
139
|
-
4. Implementar `WithUserID(ctx context.Context, userID string) context.Context`
|
|
140
|
-
- Usar tipo `contextKey` unexportado: `type contextKey string`
|
|
141
|
-
- Constante unexportada: `const userIDKey contextKey = "user_id"`
|
|
142
|
-
5. Implementar `UserIDFromContext(ctx context.Context) (string, bool)`
|
|
143
|
-
6. Adicionar dependencia `github.com/golang-jwt/jwt/v5` ao `go.mod`
|
|
144
|
-
|
|
145
|
-
**Criterio de conclusao:**
|
|
146
|
-
- Todas as 4 funcoes publicas implementadas conforme assinaturas da SPEC_TECH secao 10.1
|
|
147
|
-
- `contextKey` e `userIDKey` sao unexportados
|
|
148
|
-
- `CGO_ENABLED=1 go build ./internal/pkg/identity/...` compila sem erro
|
|
149
|
-
|
|
150
|
-
**Dependencias:** Nenhuma
|
|
151
|
-
|
|
152
|
-
---
|
|
153
|
-
|
|
154
|
-
### TASK 06 — Repository: Estender `UserRepository`
|
|
155
|
-
|
|
156
|
-
**Prioridade:** P0
|
|
157
|
-
**Tipo:** Modificar arquivo existente
|
|
158
|
-
|
|
159
|
-
**Arquivo a modificar:**
|
|
160
|
-
- `internal/repository/user_repository.go`
|
|
161
|
-
|
|
162
|
-
**Detalhes:**
|
|
163
|
-
1. Adicionar campo `Address string` ao struct `User`
|
|
164
|
-
2. Atualizar mapeamento no metodo `Create()`:
|
|
165
|
-
- Incluir `Endereco` nos params SQLC
|
|
166
|
-
- Incluir `Address: sqlcUser.Endereco` no mapeamento de retorno
|
|
167
|
-
3. Atualizar mapeamento no metodo `GetByID()`:
|
|
168
|
-
- Incluir `Address: sqlcUser.Endereco` no mapeamento de retorno
|
|
169
|
-
4. Adicionar metodo `GetByEmail(ctx context.Context, email string) (*User, error)` a interface e implementacao:
|
|
170
|
-
- Chamar `r.queries.GetUserByEmail(ctx, email)`
|
|
171
|
-
- Mapear resultado SQLC -> dominio (incluindo `Address`)
|
|
172
|
-
- Erro: `"falha ao buscar usuario por email: %w"`
|
|
173
|
-
5. Adicionar metodo `Update(ctx context.Context, user *User) (*User, error)` a interface e implementacao:
|
|
174
|
-
- Mapear `*User` -> `sqlc.UpdateUserParams` (Nome, Email, SenhaHash, Endereco, DataAtualizacao, ID)
|
|
175
|
-
- Mapear resultado SQLC -> dominio
|
|
176
|
-
- Erro: `"falha ao atualizar usuario: %w"`
|
|
177
|
-
6. Mensagens de erro em portugues
|
|
178
|
-
|
|
179
|
-
**Criterio de conclusao:**
|
|
180
|
-
- Interface `UserRepository` tem 4 metodos: `Create`, `GetByID`, `GetByEmail`, `Update`
|
|
181
|
-
- Todos os mapeamentos incluem `Address`/`Endereco`
|
|
182
|
-
- `CGO_ENABLED=1 go build ./internal/repository/...` compila sem erro
|
|
183
|
-
|
|
184
|
-
**Dependencias:** TASK 04 (queries SQLC geradas)
|
|
185
|
-
|
|
186
|
-
---
|
|
187
|
-
|
|
188
|
-
### TASK 07 — Service: Estender `UserService`
|
|
189
|
-
|
|
190
|
-
**Prioridade:** P0
|
|
191
|
-
**Tipo:** Modificar arquivo existente
|
|
192
|
-
|
|
193
|
-
**Arquivo a modificar:**
|
|
194
|
-
- `internal/service/user_service.go`
|
|
195
|
-
|
|
196
|
-
**Detalhes:**
|
|
197
|
-
|
|
198
|
-
**7.1 — Definir variaveis de erro (10 erros, mensagens em portugues):**
|
|
199
|
-
```go
|
|
200
|
-
var (
|
|
201
|
-
ErrInvalidName = errors.New("nome nao pode estar vazio")
|
|
202
|
-
ErrInvalidEmail = errors.New("email invalido")
|
|
203
|
-
ErrEmailAlreadyExists = errors.New("email ja cadastrado")
|
|
204
|
-
ErrPasswordTooShort = errors.New("senha deve ter no minimo 8 caracteres")
|
|
205
|
-
ErrAddressRequired = errors.New("endereco nao pode estar vazio")
|
|
206
|
-
ErrInvalidCredentials = errors.New("credenciais invalidas")
|
|
207
|
-
ErrUserNotFound = errors.New("usuario nao encontrado")
|
|
208
|
-
ErrNoFieldsToUpdate = errors.New("ao menos um campo deve ser fornecido para atualizacao")
|
|
209
|
-
ErrCurrentPasswordRequired = errors.New("senha atual e obrigatoria quando nova senha e informada")
|
|
210
|
-
ErrCurrentPasswordWrong = errors.New("senha atual incorreta")
|
|
211
|
-
)
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
**7.2 — Definir novos tipos:**
|
|
215
|
-
- Atualizar `CreateUserInput` — adicionar `Address string`
|
|
216
|
-
- Criar `LoginResult{Token string, ExpiresAt time.Time, User *repository.User}`
|
|
217
|
-
- Criar `UpdateUserInput{Name, Email, Address, NewPassword, CurrentPassword *string}`
|
|
218
|
-
|
|
219
|
-
**7.3 — Atualizar construtor:**
|
|
220
|
-
- `NewUserService(repo repository.UserRepository, cfg *config.Config, logger *zap.Logger) UserService`
|
|
221
|
-
- Armazenar `cfg` e `logger` na struct `userService`
|
|
222
|
-
|
|
223
|
-
**7.4 — Atualizar interface `UserService` com 4 metodos:**
|
|
224
|
-
- `CreateUser(ctx context.Context, input CreateUserInput) (*repository.User, error)`
|
|
225
|
-
- `Login(ctx context.Context, email, password string) (*LoginResult, error)`
|
|
226
|
-
- `GetUserLogged(ctx context.Context, userID string) (*repository.User, error)`
|
|
227
|
-
- `UpdateUser(ctx context.Context, userID string, input UpdateUserInput) (*repository.User, error)`
|
|
228
|
-
|
|
229
|
-
**7.5 — Atualizar `CreateUser`:**
|
|
230
|
-
- Validacoes em ordem: name vazio -> email formato (`net/mail.ParseAddress`) -> password len < 8 -> address vazio
|
|
231
|
-
- Verificar email unico: `repo.GetByEmail(email)` — se encontrado: `ErrEmailAlreadyExists`
|
|
232
|
-
- Hash: `bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)`
|
|
233
|
-
- Persistir via `repo.Create` incluindo `Address`
|
|
234
|
-
- Log Info: `"usuario criado com sucesso"` com `zap.String("user_id", ...)` e `zap.String("email", ...)`
|
|
235
|
-
|
|
236
|
-
**7.6 — Implementar `Login`:**
|
|
237
|
-
- `repo.GetByEmail(email)` — se nao existe: `ErrInvalidCredentials` + Log Warn
|
|
238
|
-
- `bcrypt.CompareHashAndPassword(hash, password)` — se falha: `ErrInvalidCredentials` + Log Warn
|
|
239
|
-
- `identity.GenerateToken(userID, email, cfg.JWTSecret, time.Duration(cfg.JWTExpirationHours)*time.Hour)`
|
|
240
|
-
- Retornar `LoginResult` com `User.PasswordHash = ""`
|
|
241
|
-
- Log Info: `"login bem-sucedido"`
|
|
242
|
-
|
|
243
|
-
**7.7 — Implementar `GetUserLogged`:**
|
|
244
|
-
- `repo.GetByID(userID)` — se erro: `ErrUserNotFound`
|
|
245
|
-
- Limpar `user.PasswordHash = ""` antes de retornar
|
|
246
|
-
|
|
247
|
-
**7.8 — Implementar `UpdateUser`:**
|
|
248
|
-
- Validar ao menos 1 campo nao-nil -> `ErrNoFieldsToUpdate`
|
|
249
|
-
- `repo.GetByID(userID)` -> obter usuario atual
|
|
250
|
-
- Se `input.Email != nil`: `net/mail.ParseAddress` -> `ErrInvalidEmail`; `repo.GetByEmail` -> se existe e ID diferente: `ErrEmailAlreadyExists`
|
|
251
|
-
- Se `input.NewPassword != nil`:
|
|
252
|
-
- `len(*input.NewPassword) < 8` -> `ErrPasswordTooShort`
|
|
253
|
-
- `input.CurrentPassword == nil` -> `ErrCurrentPasswordRequired`
|
|
254
|
-
- `bcrypt.CompareHashAndPassword(currentUser.PasswordHash, *CurrentPassword)` -> `ErrCurrentPasswordWrong`
|
|
255
|
-
- `bcrypt.GenerateFromPassword(*NewPassword, DefaultCost)`
|
|
256
|
-
- Aplicar campos nao-nil sobre `currentUser`
|
|
257
|
-
- `repo.Update(updatedUser)`
|
|
258
|
-
- Limpar `PasswordHash` no retorno
|
|
259
|
-
- Log Info com campos atualizados (nomes dos campos, nao valores)
|
|
260
|
-
|
|
261
|
-
**Criterio de conclusao:**
|
|
262
|
-
- Interface `UserService` com 4 metodos
|
|
263
|
-
- 10 variaveis de erro definidas
|
|
264
|
-
- Logging em portugues em todos os caminhos relevantes
|
|
265
|
-
- Nenhuma senha, hash ou token logado
|
|
266
|
-
- `CGO_ENABLED=1 go build ./internal/service/...` compila sem erro
|
|
267
|
-
|
|
268
|
-
**Dependencias:** TASK 05 (identity), TASK 06 (repository), TASK 01 (config)
|
|
269
|
-
|
|
270
|
-
---
|
|
271
|
-
|
|
272
|
-
### TASK 08 — Handler: Estender `UserHandler` gRPC
|
|
273
|
-
|
|
274
|
-
**Prioridade:** P0
|
|
275
|
-
**Tipo:** Modificar arquivo existente
|
|
276
|
-
|
|
277
|
-
**Arquivo a modificar:**
|
|
278
|
-
- `internal/handler/grpc/user_handler.go`
|
|
279
|
-
|
|
280
|
-
**Detalhes:**
|
|
281
|
-
|
|
282
|
-
**8.1 — Atualizar `CreateUser`:**
|
|
283
|
-
- Incluir `Address` no mapeamento request -> `CreateUserInput`
|
|
284
|
-
- Resposta: `CreateUserResponse{id, name, email, created_at}` — sem `PasswordHash`
|
|
285
|
-
|
|
286
|
-
**8.2 — Implementar `Login`:**
|
|
287
|
-
- Mapear `LoginRequest{email, password}` para chamada `service.Login`
|
|
288
|
-
- Resposta: `LoginResponse{token, expires_at, id, name, email}`
|
|
289
|
-
- Formatar `expires_at` como string (RFC3339 ou similar)
|
|
290
|
-
|
|
291
|
-
**8.3 — Implementar `GetUserLogged`:**
|
|
292
|
-
- Extrair `userID` via `identity.UserIDFromContext(ctx)` — se ausente: `codes.Unauthenticated`
|
|
293
|
-
- Chamar `service.GetUserLogged(ctx, userID)`
|
|
294
|
-
- Resposta: `GetUserLoggedResponse{User{id, name, email, address, created_at, updated_at}}`
|
|
295
|
-
|
|
296
|
-
**8.4 — Implementar `UpdateUser`:**
|
|
297
|
-
- Extrair `userID` via `identity.UserIDFromContext(ctx)` — se ausente: `codes.Unauthenticated`
|
|
298
|
-
- Construir `UpdateUserInput` com ponteiros (campos `optional` do proto -> `*string`)
|
|
299
|
-
- Chamar `service.UpdateUser(ctx, userID, input)`
|
|
300
|
-
- Resposta: `UpdateUserResponse{User{...}}`
|
|
301
|
-
|
|
302
|
-
**8.5 — Remover `GetUser` (se existir) — substituido por `GetUserLogged`**
|
|
303
|
-
|
|
304
|
-
**8.6 — Criar funcao auxiliar `domainUserToProto(user *repository.User) *pb.User`**
|
|
305
|
-
|
|
306
|
-
**8.7 — Atualizar `mapServiceError` conforme SPEC_TECH secao 11.2:**
|
|
307
|
-
|
|
308
|
-
| Erro de Service | gRPC Code |
|
|
309
|
-
|----------------|-----------|
|
|
310
|
-
| `ErrInvalidName`, `ErrInvalidEmail`, `ErrPasswordTooShort`, `ErrAddressRequired`, `ErrNoFieldsToUpdate`, `ErrCurrentPasswordRequired`, `ErrCurrentPasswordWrong` | `codes.InvalidArgument` |
|
|
311
|
-
| `ErrEmailAlreadyExists` | `codes.AlreadyExists` |
|
|
312
|
-
| `ErrInvalidCredentials` | `codes.Unauthenticated` |
|
|
313
|
-
| `ErrUserNotFound` | `codes.NotFound` |
|
|
314
|
-
| Demais erros | `codes.Internal` com mensagem `"erro interno do servidor"` |
|
|
315
|
-
|
|
316
|
-
**Criterio de conclusao:**
|
|
317
|
-
- Handler implementa os 4 RPCs do `UserServiceServer`
|
|
318
|
-
- Mapeamento de erros completo
|
|
319
|
-
- `PasswordHash` nunca exposto em nenhuma response
|
|
320
|
-
- `user_id` extraido do contexto (nao de parametros) nos endpoints protegidos
|
|
321
|
-
- `CGO_ENABLED=1 go build ./internal/handler/grpc/...` compila sem erro
|
|
322
|
-
|
|
323
|
-
**Dependencias:** TASK 07 (service), TASK 05 (identity), TASK 03 (proto gerado)
|
|
324
|
-
|
|
325
|
-
---
|
|
326
|
-
|
|
327
|
-
### TASK 09 — Interceptor: Atualizar `AuthInterceptor`
|
|
328
|
-
|
|
329
|
-
**Prioridade:** P0
|
|
330
|
-
**Tipo:** Modificar arquivo existente
|
|
331
|
-
|
|
332
|
-
**Arquivo a modificar:**
|
|
333
|
-
- `internal/infra/grpc/interceptors/auth.go`
|
|
334
|
-
|
|
335
|
-
**Detalhes:**
|
|
336
|
-
1. Adicionar `/proto.v1.UserService/Login` em `skipMethods` (junto com `CreateUser` que ja deve estar la)
|
|
337
|
-
2. Substituir parse manual do token por `identity.ParseToken(tokenString, cfg.JWTSecret)`
|
|
338
|
-
3. Apos validacao bem-sucedida: `ctx = identity.WithUserID(ctx, claims.UserID)`
|
|
339
|
-
4. Passar `ctx` atualizado ao `handler(ctx, req)`
|
|
340
|
-
|
|
341
|
-
**Criterio de conclusao:**
|
|
342
|
-
- `Login` e `CreateUser` sao rotas publicas (sem verificacao de token)
|
|
343
|
-
- `GetUserLogged` e `UpdateUser` exigem token valido
|
|
344
|
-
- `user_id` do JWT e injetado no contexto via `identity.WithUserID`
|
|
345
|
-
- Token invalido/ausente retorna `codes.Unauthenticated`
|
|
346
|
-
- `CGO_ENABLED=1 go build ./internal/infra/grpc/interceptors/...` compila sem erro
|
|
347
|
-
|
|
348
|
-
**Dependencias:** TASK 05 (identity)
|
|
349
|
-
|
|
350
|
-
---
|
|
351
|
-
|
|
352
|
-
### TASK 10 — Testes Unitarios: Identity + Service + Handler + Interceptor
|
|
353
|
-
|
|
354
|
-
**Prioridade:** P1
|
|
355
|
-
**Tipo:** Criar + Modificar arquivos
|
|
356
|
-
|
|
357
|
-
**Arquivos a criar:**
|
|
358
|
-
- `internal/pkg/identity/identity_test.go`
|
|
359
|
-
- `internal/infra/grpc/interceptors/auth_test.go`
|
|
360
|
-
|
|
361
|
-
**Arquivos a modificar:**
|
|
362
|
-
- `internal/service/user_service_test.go`
|
|
363
|
-
- `internal/handler/grpc/user_handler_test.go`
|
|
364
|
-
|
|
365
|
-
**Detalhes:**
|
|
366
|
-
|
|
367
|
-
**10.1 — Identity tests (`identity_test.go`, 8 cenarios):**
|
|
368
|
-
|
|
369
|
-
| Teste | Cenario |
|
|
370
|
-
|-------|---------|
|
|
371
|
-
| `TestGenerateToken_Success` | Token nao vazio, `expiresAt` no futuro |
|
|
372
|
-
| `TestGenerateToken_TokenIsParseable` | `ParseToken(token, secret)` sem erro |
|
|
373
|
-
| `TestGenerateToken_ClaimsContainCorrectData` | `claims.UserID` e `claims.Email` corretos |
|
|
374
|
-
| `TestParseToken_WrongSecret` | Erro retornado |
|
|
375
|
-
| `TestParseToken_ExpiredToken` | Erro retornado |
|
|
376
|
-
| `TestParseToken_MalformedToken` | Erro retornado |
|
|
377
|
-
| `TestContextRoundtrip_UserID` | `WithUserID` + `UserIDFromContext` retorna `"uuid-ctx", true` |
|
|
378
|
-
| `TestUserIDFromContext_Missing` | Retorna `"", false` |
|
|
379
|
-
|
|
380
|
-
**10.2 — Service tests — CreateUser (atualizar existentes + novos, ~9 cenarios):**
|
|
381
|
-
|
|
382
|
-
| Teste | Cenario |
|
|
383
|
-
|-------|---------|
|
|
384
|
-
| `TestCreateUser_Success` | Atualizar para incluir `Address` |
|
|
385
|
-
| `TestCreateUser_PasswordIsHashed` | Existente — manter |
|
|
386
|
-
| `TestCreateUser_GeneratesValidUUID` | Existente — manter |
|
|
387
|
-
| `TestCreateUser_ErrorEmptyName` | Atualizar se necessario |
|
|
388
|
-
| `TestCreateUser_ErrorInvalidEmailFormat` | **Novo** — email invalido |
|
|
389
|
-
| `TestCreateUser_ErrorPasswordTooShort_7Chars` | **Novo** — boundary 7 chars |
|
|
390
|
-
| `TestCreateUser_PasswordExactly8Chars` | **Novo** — boundary 8 chars OK |
|
|
391
|
-
| `TestCreateUser_ErrorEmptyAddress` | **Novo** — address vazio |
|
|
392
|
-
| `TestCreateUser_ErrorRepositoryFailure` | Existente — manter |
|
|
393
|
-
|
|
394
|
-
**10.3 — Service tests — Login (6 cenarios):**
|
|
395
|
-
|
|
396
|
-
| Teste | Cenario |
|
|
397
|
-
|-------|---------|
|
|
398
|
-
| `TestLogin_Success` | `*LoginResult` com `Token` nao vazio, `ExpiresAt` no futuro |
|
|
399
|
-
| `TestLogin_ResultDoesNotExposePasswordHash` | `result.User.PasswordHash == ""` |
|
|
400
|
-
| `TestLogin_TokenExpiresAtRespectsConfig` | `result.ExpiresAt` ~= `now + JWTExpirationHours` (tolerancia 5s) |
|
|
401
|
-
| `TestLogin_EmailNotFound_GenericError` | `ErrInvalidCredentials` |
|
|
402
|
-
| `TestLogin_WrongPassword_GenericError` | `ErrInvalidCredentials` |
|
|
403
|
-
| `TestLogin_ErrorRepositoryFailure` | Erro interno propagado |
|
|
404
|
-
|
|
405
|
-
**10.4 — Service tests — GetUserLogged (3 cenarios):**
|
|
406
|
-
|
|
407
|
-
| Teste | Cenario |
|
|
408
|
-
|-------|---------|
|
|
409
|
-
| `TestGetUserLogged_Success` | `*repository.User` com `PasswordHash:""` |
|
|
410
|
-
| `TestGetUserLogged_UserNotFound` | `ErrUserNotFound` |
|
|
411
|
-
| `TestGetUserLogged_RepositoryError` | Erro interno propagado |
|
|
412
|
-
|
|
413
|
-
**10.5 — Service tests — UpdateUser (13 cenarios):**
|
|
414
|
-
|
|
415
|
-
| Teste | Cenario |
|
|
416
|
-
|-------|---------|
|
|
417
|
-
| `TestUpdateUser_NameOnly` | Apenas nome atualizado |
|
|
418
|
-
| `TestUpdateUser_EmailOnly` | Email atualizado |
|
|
419
|
-
| `TestUpdateUser_AddressOnly` | Endereco atualizado |
|
|
420
|
-
| `TestUpdateUser_PasswordChange_Success` | Novo hash diferente do anterior |
|
|
421
|
-
| `TestUpdateUser_MultipleFields` | Varios campos aplicados |
|
|
422
|
-
| `TestUpdateUser_NoFieldsToUpdate` | `ErrNoFieldsToUpdate` |
|
|
423
|
-
| `TestUpdateUser_InvalidEmailFormat` | `ErrInvalidEmail` |
|
|
424
|
-
| `TestUpdateUser_EmailAlreadyExists` | `ErrEmailAlreadyExists` |
|
|
425
|
-
| `TestUpdateUser_CurrentPasswordMissing` | `ErrCurrentPasswordRequired` |
|
|
426
|
-
| `TestUpdateUser_WrongCurrentPassword` | `ErrCurrentPasswordWrong` |
|
|
427
|
-
| `TestUpdateUser_NewPasswordTooShort` | `ErrPasswordTooShort` |
|
|
428
|
-
| `TestUpdateUser_UserNotFound` | `ErrUserNotFound` |
|
|
429
|
-
| `TestUpdateUser_RepositoryFailure` | Erro interno propagado |
|
|
430
|
-
|
|
431
|
-
**10.6 — Handler tests — CreateUser (8 cenarios):**
|
|
432
|
-
|
|
433
|
-
| Teste | Cenario |
|
|
434
|
-
|-------|---------|
|
|
435
|
-
| `TestUserHandler_CreateUser_Success` | Response com `id`, `name`, `email`, `created_at` |
|
|
436
|
-
| `TestUserHandler_CreateUser_ResponseDoesNotExposePasswordHash` | Sem hash na response |
|
|
437
|
-
| `TestUserHandler_CreateUser_EmptyName` | `codes.InvalidArgument` |
|
|
438
|
-
| `TestUserHandler_CreateUser_EmptyEmail` | `codes.InvalidArgument` |
|
|
439
|
-
| `TestUserHandler_CreateUser_ShortPassword` | `codes.InvalidArgument` |
|
|
440
|
-
| `TestUserHandler_CreateUser_EmptyAddress` | `codes.InvalidArgument` |
|
|
441
|
-
| `TestUserHandler_CreateUser_DuplicateEmail` | `codes.AlreadyExists` |
|
|
442
|
-
| `TestUserHandler_CreateUser_InternalError` | `codes.Internal` |
|
|
443
|
-
|
|
444
|
-
**10.7 — Handler tests — Login (3 cenarios):**
|
|
445
|
-
|
|
446
|
-
| Teste | Cenario |
|
|
447
|
-
|-------|---------|
|
|
448
|
-
| `TestUserHandler_Login_Success` | Response com `token`, `expires_at`, `id`, `name`, `email` |
|
|
449
|
-
| `TestUserHandler_Login_InvalidCredentials` | `codes.Unauthenticated` |
|
|
450
|
-
| `TestUserHandler_Login_InternalError` | `codes.Internal` |
|
|
451
|
-
|
|
452
|
-
**10.8 — Handler tests — GetUserLogged (3 cenarios):**
|
|
453
|
-
|
|
454
|
-
| Teste | Cenario |
|
|
455
|
-
|-------|---------|
|
|
456
|
-
| `TestUserHandler_GetUserLogged_Success` | Response com dados completos via JWT |
|
|
457
|
-
| `TestUserHandler_GetUserLogged_MissingUserID` | `codes.Unauthenticated` |
|
|
458
|
-
| `TestUserHandler_GetUserLogged_NotFound` | `codes.NotFound` |
|
|
459
|
-
|
|
460
|
-
**10.9 — Handler tests — UpdateUser (10 cenarios):**
|
|
461
|
-
|
|
462
|
-
| Teste | Cenario |
|
|
463
|
-
|-------|---------|
|
|
464
|
-
| `TestUserHandler_UpdateUser_NameOnly` | Response com `name` atualizado |
|
|
465
|
-
| `TestUserHandler_UpdateUser_EmailOnly` | Response com email atualizado |
|
|
466
|
-
| `TestUserHandler_UpdateUser_PasswordChange` | Response com sucesso |
|
|
467
|
-
| `TestUserHandler_UpdateUser_MultipleFields` | Todos os campos atualizados |
|
|
468
|
-
| `TestUserHandler_UpdateUser_NoFields` | `codes.InvalidArgument` |
|
|
469
|
-
| `TestUserHandler_UpdateUser_EmailAlreadyExists` | `codes.AlreadyExists` |
|
|
470
|
-
| `TestUserHandler_UpdateUser_CurrentPasswordMissing` | `codes.InvalidArgument` |
|
|
471
|
-
| `TestUserHandler_UpdateUser_WrongCurrentPassword` | `codes.InvalidArgument` |
|
|
472
|
-
| `TestUserHandler_UpdateUser_NewPasswordTooShort` | `codes.InvalidArgument` |
|
|
473
|
-
| `TestUserHandler_UpdateUser_UserNotFound` | `codes.NotFound` |
|
|
474
|
-
|
|
475
|
-
**10.10 — Interceptor tests (`auth_test.go`, 3 cenarios):**
|
|
476
|
-
|
|
477
|
-
| Teste | Cenario |
|
|
478
|
-
|-------|---------|
|
|
479
|
-
| `TestAuthInterceptor_ValidToken_InjectsUserID` | Handler recebe ctx com `identity.UserIDFromContext(ctx) == "uuid-123"` |
|
|
480
|
-
| `TestAuthInterceptor_SkipsLogin` | `/Login` e `/CreateUser` nao exigem token (table-driven) |
|
|
481
|
-
| `TestAuthInterceptor_ProtectedMethodsRequireAuth` | `GetUserLogged` e `UpdateUser` sem token retornam `Unauthenticated` (table-driven) |
|
|
482
|
-
|
|
483
|
-
**10.11 — Atualizar mocks:**
|
|
484
|
-
- `MockUserService` — adicionar `Login`, `GetUserLogged`, `UpdateUser`
|
|
485
|
-
- `MockUserRepository` — adicionar `GetByEmail`, `Update`
|
|
486
|
-
|
|
487
|
-
**Criterio de conclusao:**
|
|
488
|
-
- `CGO_ENABLED=1 go test ./internal/pkg/identity/... -v` — todos passam
|
|
489
|
-
- `CGO_ENABLED=1 go test ./internal/service/... -v` — todos passam
|
|
490
|
-
- `CGO_ENABLED=1 go test ./internal/handler/grpc/... -v` — todos passam
|
|
491
|
-
- `CGO_ENABLED=1 go test ./internal/infra/grpc/interceptors/... -v` — todos passam
|
|
492
|
-
|
|
493
|
-
**Dependencias:** TASK 05-09
|
|
494
|
-
|
|
495
|
-
---
|
|
496
|
-
|
|
497
|
-
### TASK 11 — Testes de Integracao: Repository + Migration
|
|
498
|
-
|
|
499
|
-
**Prioridade:** P1
|
|
500
|
-
**Tipo:** Criar arquivo novo
|
|
501
|
-
|
|
502
|
-
**Arquivo a criar:**
|
|
503
|
-
- `internal/repository/user_repository_integration_test.go`
|
|
504
|
-
|
|
505
|
-
**Detalhes:**
|
|
506
|
-
|
|
507
|
-
**11.1 — Setup:**
|
|
508
|
-
```go
|
|
509
|
-
func setupTestDB(t *testing.T) *sql.DB {
|
|
510
|
-
t.Helper()
|
|
511
|
-
db, err := database.NewDatabase(&config.Config{DatabasePath: filepath.Join(t.TempDir(), "test.db")})
|
|
512
|
-
require.NoError(t, err)
|
|
513
|
-
t.Cleanup(func() { db.Close() })
|
|
514
|
-
return db
|
|
515
|
-
}
|
|
516
|
-
```
|
|
517
|
-
|
|
518
|
-
**11.2 — Cenarios de Repository (12 testes):**
|
|
519
|
-
|
|
520
|
-
| Teste | Cenario |
|
|
521
|
-
|-------|---------|
|
|
522
|
-
| `TestUserRepository_Create_Success` | Todos os campos persistidos incluindo `Address` |
|
|
523
|
-
| `TestUserRepository_CreateAndGetByID` | Dados identicos ao inserido |
|
|
524
|
-
| `TestUserRepository_GetByEmail` | Retorna user correto com `Address` |
|
|
525
|
-
| `TestUserRepository_Create_DuplicateEmail` | Erro contendo `"UNIQUE constraint failed"` |
|
|
526
|
-
| `TestUserRepository_GetByID_NotFound` | Erro contendo `"nao encontrado"` |
|
|
527
|
-
| `TestUserRepository_GetByEmail_NotFound` | Erro contendo `"nao encontrado"` |
|
|
528
|
-
| `TestUserRepository_Update_NameOnly` | `Name == "Novo Nome"`, outros inalterados |
|
|
529
|
-
| `TestUserRepository_Update_Email` | `Email == "novo@x.com"` |
|
|
530
|
-
| `TestUserRepository_Update_PasswordHash` | `PasswordHash == novoHash` |
|
|
531
|
-
| `TestUserRepository_Update_Address` | `Address == "Nova Rua, 99"` |
|
|
532
|
-
| `TestUserRepository_Update_UpdatedAtChanges` | `UpdatedAt` posterior ao `CreatedAt` |
|
|
533
|
-
| `TestUserRepository_Create_GeneratesValidUUID` | `uuid.Parse(result.ID)` sem erro |
|
|
534
|
-
|
|
535
|
-
**11.3 — Testes de migracao (em `internal/infra/database/sqlite_test.go`):**
|
|
536
|
-
|
|
537
|
-
| Teste | Cenario |
|
|
538
|
-
|-------|---------|
|
|
539
|
-
| `TestRunMigrations_TableSchema_IncludesEndereco` | `PRAGMA table_info(usuarios)` lista `endereco` com tipo `TEXT` |
|
|
540
|
-
| `TestRunMigrations_EnderecocolumnDefaultEmpty` | INSERT sem `endereco` -> SELECT retorna `""` |
|
|
541
|
-
| `TestRunMigrations_IdempotentWithEndereco` | Segunda `NewDatabase` nao retorna erro |
|
|
542
|
-
|
|
543
|
-
**Criterio de conclusao:**
|
|
544
|
-
- `CGO_ENABLED=1 go test ./internal/repository/... -v` — todos passam
|
|
545
|
-
- Testes usam banco em diretorio temporario (sem efeitos colaterais)
|
|
546
|
-
|
|
547
|
-
**Dependencias:** TASK 02, 04, 06
|
|
548
|
-
|
|
549
|
-
---
|
|
550
|
-
|
|
551
|
-
### TASK 12 — Testes E2E: Fluxos completos
|
|
552
|
-
|
|
553
|
-
**Prioridade:** P2
|
|
554
|
-
**Tipo:** Criar arquivo novo
|
|
555
|
-
|
|
556
|
-
**Arquivo a criar:**
|
|
557
|
-
- `internal/e2e/user_e2e_test.go`
|
|
558
|
-
|
|
559
|
-
**Detalhes:**
|
|
560
|
-
|
|
561
|
-
**12.1 — Setup:**
|
|
562
|
-
- `setupE2EServer(t)` — servidor gRPC em porta efemera (`:0`)
|
|
563
|
-
- Banco SQLite em `t.TempDir()`
|
|
564
|
-
- Config, DB, Repo, Service, Handler, gRPC Server montados manualmente ou via FX
|
|
565
|
-
- Retorna `pb.UserServiceClient` + funcao de cleanup
|
|
566
|
-
|
|
567
|
-
**12.2 — Cenarios (5 fluxos E2E):**
|
|
568
|
-
|
|
569
|
-
| Teste | Fluxo |
|
|
570
|
-
|-------|-------|
|
|
571
|
-
| `TestE2E_CreateUser_FullFlow` | `CreateUser` com dados validos -> verificar response: UUID valido, `name`, `email`, `created_at` presentes, sem token (RN-6) |
|
|
572
|
-
| `TestE2E_LoginAndGetUserLogged_FullFlow` | `CreateUser` -> `Login` -> verificar token nao vazio -> `GetUserLogged` com Bearer token -> verificar dados completos (`id`, `name`, `email`, `address`) |
|
|
573
|
-
| `TestE2E_UpdateUser_PartialUpdate` | `CreateUser` -> `Login` -> `UpdateUser{name: "Novo Nome"}` com Bearer -> `GetUserLogged` -> confirmar `name` atualizado, demais inalterados |
|
|
574
|
-
| `TestE2E_ProtectedRoutes_RequireAuth` | `GetUserLogged` sem token -> `codes.Unauthenticated`; `UpdateUser` sem token -> `codes.Unauthenticated`; `CreateUser` e `Login` funcionam sem token |
|
|
575
|
-
| `TestE2E_UpdateUser_PasswordChangeAndReauth` | `CreateUser` -> `Login` (senha original) -> `UpdateUser{new_password, current_password}` -> `Login` nova senha -> sucesso; `Login` senha antiga -> `codes.Unauthenticated` |
|
|
576
|
-
|
|
577
|
-
**Criterio de conclusao:**
|
|
578
|
-
- `CGO_ENABLED=1 go test ./internal/e2e/... -v` — todos passam
|
|
579
|
-
- Testes isolados (banco temporario, porta efemera)
|
|
580
|
-
|
|
581
|
-
**Dependencias:** TASK 01-09 (implementacao completa)
|
|
582
|
-
|
|
583
|
-
---
|
|
584
|
-
|
|
585
|
-
## Ordem de Execucao
|
|
586
|
-
|
|
587
|
-
```
|
|
588
|
-
Fase 1 — Fundacao (paralelas):
|
|
589
|
-
TASK 01 (Config)
|
|
590
|
-
TASK 02 (Migration)
|
|
591
|
-
TASK 03 (Proto + make generate)
|
|
592
|
-
TASK 05 (Identity)
|
|
593
|
-
|
|
594
|
-
Fase 2 — Dados:
|
|
595
|
-
TASK 04 (SQLC queries + make generate) [depende: TASK 02]
|
|
596
|
-
|
|
597
|
-
Fase 3 — Camadas de negocio:
|
|
598
|
-
TASK 06 (Repository) [depende: TASK 04]
|
|
599
|
-
TASK 09 (Interceptor) [depende: TASK 05]
|
|
600
|
-
|
|
601
|
-
Fase 4 — Logica e apresentacao:
|
|
602
|
-
TASK 07 (Service) [depende: TASK 01, 05, 06]
|
|
603
|
-
TASK 08 (Handler) [depende: TASK 03, 05, 07]
|
|
604
|
-
|
|
605
|
-
Fase 5 — Testes:
|
|
606
|
-
TASK 10 (Unitarios) [depende: TASK 05-09]
|
|
607
|
-
TASK 11 (Integracao) [depende: TASK 02, 04, 06]
|
|
608
|
-
TASK 12 (E2E) [depende: TASK 01-09]
|
|
609
|
-
```
|
|
610
|
-
|
|
611
|
-
---
|
|
612
|
-
|
|
613
|
-
## Comandos de Validacao
|
|
614
|
-
|
|
615
|
-
| Fase | Comando |
|
|
616
|
-
|------|---------|
|
|
617
|
-
| Apos TASK 01 | `make build` |
|
|
618
|
-
| Apos TASK 03 | `make generate && make lint` |
|
|
619
|
-
| Apos TASK 04 | `make generate` |
|
|
620
|
-
| Apos TASK 05 | `CGO_ENABLED=1 go build ./internal/pkg/identity/...` |
|
|
621
|
-
| Apos TASK 06 | `CGO_ENABLED=1 go build ./internal/repository/...` |
|
|
622
|
-
| Apos TASK 07 | `CGO_ENABLED=1 go build ./internal/service/...` |
|
|
623
|
-
| Apos TASK 08 | `CGO_ENABLED=1 go build ./internal/handler/grpc/...` |
|
|
624
|
-
| Apos TASK 09 | `CGO_ENABLED=1 go build ./internal/infra/grpc/interceptors/...` |
|
|
625
|
-
| Apos TASK 10 | `CGO_ENABLED=1 go test ./internal/pkg/identity/... ./internal/service/... ./internal/handler/grpc/... ./internal/infra/grpc/interceptors/... -v` |
|
|
626
|
-
| Apos TASK 11 | `CGO_ENABLED=1 go test ./internal/repository/... -v` |
|
|
627
|
-
| Apos TASK 12 | `CGO_ENABLED=1 go test ./internal/e2e/... -v` |
|
|
628
|
-
| **Validacao final** | `make build && CGO_ENABLED=1 go test ./... -v` |
|
|
629
|
-
|
|
630
|
-
---
|
|
631
|
-
|
|
632
|
-
## Rastreabilidade SPEC_TECH -> Tasks
|
|
633
|
-
|
|
634
|
-
| Secao SPEC_TECH | Task(s) |
|
|
635
|
-
|-----------------|---------|
|
|
636
|
-
| 4.1 Tabela usuarios | TASK 02 |
|
|
637
|
-
| 4.2 Modelos Go | TASK 06, 07 |
|
|
638
|
-
| 4.3 JWT Claims | TASK 05 |
|
|
639
|
-
| 5.2 US-01 CreateUser | TASK 03, 04, 06, 07, 08 |
|
|
640
|
-
| 5.3 US-02 Login | TASK 03, 05, 07, 08 |
|
|
641
|
-
| 5.4 US-03 GetUserLogged | TASK 03, 05, 07, 08, 09 |
|
|
642
|
-
| 5.5 US-04 UpdateUser | TASK 03, 04, 07, 08, 09 |
|
|
643
|
-
| 6 Regras de Negocio (RN-1 a RN-12) | TASK 07 |
|
|
644
|
-
| 8 API Proto | TASK 03 |
|
|
645
|
-
| 9 Persistencia | TASK 02, 04 |
|
|
646
|
-
| 10.1 Identity | TASK 05 |
|
|
647
|
-
| 10.2 Interceptor | TASK 09 |
|
|
648
|
-
| 10.3 bcrypt | TASK 07 |
|
|
649
|
-
| 11 Tratamento de Erros | TASK 07, 08 |
|
|
650
|
-
| 12 Configuracao | TASK 01 |
|
|
651
|
-
| 13 Logging | TASK 07 |
|
|
652
|
-
| 14.1 Testes Unitarios | TASK 10 |
|
|
653
|
-
| 14.2 Testes Integracao | TASK 11 |
|
|
654
|
-
| 14.3 Testes E2E | TASK 12 |
|
|
655
|
-
|
|
656
|
-
---
|
|
657
|
-
|
|
658
|
-
## Rastreabilidade Criterios de Aceite -> Tasks
|
|
659
|
-
|
|
660
|
-
| CA | Tasks de Implementacao | Task de Teste |
|
|
661
|
-
|----|----------------------|---------------|
|
|
662
|
-
| CA-01 (Cadastro com sucesso) | TASK 03, 04, 06, 07, 08 | TASK 10, 11, 12 |
|
|
663
|
-
| CA-02 (Email unico) | TASK 06, 07, 08 | TASK 10, 11, 12 |
|
|
664
|
-
| CA-03 (Formato email) | TASK 07, 08 | TASK 10 |
|
|
665
|
-
| CA-04 (Senha >= 8) | TASK 07, 08 | TASK 10 |
|
|
666
|
-
| CA-05 (Nome obrigatorio) | TASK 07, 08 | TASK 10 |
|
|
667
|
-
| CA-06 (Endereco obrigatorio) | TASK 07, 08 | TASK 10 |
|
|
668
|
-
| CA-07 (Sem auto-login) | TASK 07, 08 | TASK 10, 12 |
|
|
669
|
-
| CA-08 (Login retorna token) | TASK 05, 07, 08 | TASK 10, 12 |
|
|
670
|
-
| CA-09 (Erro generico auth) | TASK 07, 08 | TASK 10, 12 |
|
|
671
|
-
| CA-10 (Consultar dados) | TASK 07, 08, 09 | TASK 10, 11, 12 |
|
|
672
|
-
| CA-11 (Rotas protegidas) | TASK 09 | TASK 10, 12 |
|
|
673
|
-
| CA-12 (Atualizacao parcial) | TASK 07, 08 | TASK 10, 11, 12 |
|
|
674
|
-
| CA-13 (Email unico update) | TASK 07, 08 | TASK 10 |
|
|
675
|
-
| CA-14 (Confirmar senha atual) | TASK 07, 08 | TASK 10, 12 |
|
|
676
|
-
| CA-15 (Nova senha >= 8) | TASK 07, 08 | TASK 10 |
|
|
677
|
-
| CA-16 (Nenhum campo) | TASK 07, 08 | TASK 10 |
|
|
678
|
-
|
|
679
|
-
---
|
|
680
|
-
|
|
681
|
-
## Resumo
|
|
682
|
-
|
|
683
|
-
| Metrica | Valor |
|
|
684
|
-
|---------|-------|
|
|
685
|
-
| Total de tasks | 12 |
|
|
686
|
-
| Tasks P0 (bloqueantes) | 9 |
|
|
687
|
-
| Tasks P1 (necessarias) | 2 |
|
|
688
|
-
| Tasks P2 (complementares) | 1 |
|
|
689
|
-
| Arquivos a criar | 4 (`identity.go`, `identity_test.go`, `auth_test.go`, `user_e2e_test.go`, `user_repository_integration_test.go`) |
|
|
690
|
-
| Arquivos a modificar | 7 (`config.yaml`, `config.go`, `user.proto`, `user.sql`, `user_repository.go`, `user_service.go`, `user_handler.go`, `auth.go`, `user_service_test.go`, `user_handler_test.go`) |
|
|
691
|
-
| Arquivos gerados (nao tocar) | `gen/proto/v1/`, `internal/db/sqlc/` |
|
|
692
|
-
| User Stories cobertas | US-01, US-02, US-03, US-04 |
|
|
693
|
-
| Criterios de Aceite cobertos | CA-01 a CA-16 |
|
|
694
|
-
| Regras de Negocio cobertas | RN-1 a RN-12 |
|