maestro-bundle 1.0.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 (60) hide show
  1. package/README.md +91 -0
  2. package/package.json +25 -0
  3. package/src/cli.mjs +212 -0
  4. package/templates/bundle-ai-agents/.spec/constitution.md +33 -0
  5. package/templates/bundle-ai-agents/AGENTS.md +140 -0
  6. package/templates/bundle-ai-agents/skills/agent-orchestration/SKILL.md +132 -0
  7. package/templates/bundle-ai-agents/skills/api-design/SKILL.md +100 -0
  8. package/templates/bundle-ai-agents/skills/clean-architecture/SKILL.md +99 -0
  9. package/templates/bundle-ai-agents/skills/context-engineering/SKILL.md +98 -0
  10. package/templates/bundle-ai-agents/skills/database-modeling/SKILL.md +59 -0
  11. package/templates/bundle-ai-agents/skills/docker-containerization/SKILL.md +114 -0
  12. package/templates/bundle-ai-agents/skills/eval-testing/SKILL.md +115 -0
  13. package/templates/bundle-ai-agents/skills/memory-management/SKILL.md +106 -0
  14. package/templates/bundle-ai-agents/skills/prompt-engineering/SKILL.md +66 -0
  15. package/templates/bundle-ai-agents/skills/rag-pipeline/SKILL.md +128 -0
  16. package/templates/bundle-ai-agents/skills/testing-strategy/SKILL.md +95 -0
  17. package/templates/bundle-base/AGENTS.md +118 -0
  18. package/templates/bundle-base/skills/branch-strategy/SKILL.md +42 -0
  19. package/templates/bundle-base/skills/code-review/SKILL.md +54 -0
  20. package/templates/bundle-base/skills/commit-pattern/SKILL.md +58 -0
  21. package/templates/bundle-data-pipeline/.spec/constitution.md +32 -0
  22. package/templates/bundle-data-pipeline/AGENTS.md +115 -0
  23. package/templates/bundle-data-pipeline/skills/data-preprocessing/SKILL.md +75 -0
  24. package/templates/bundle-data-pipeline/skills/docker-containerization/SKILL.md +114 -0
  25. package/templates/bundle-data-pipeline/skills/feature-engineering/SKILL.md +76 -0
  26. package/templates/bundle-data-pipeline/skills/mlops-pipeline/SKILL.md +77 -0
  27. package/templates/bundle-data-pipeline/skills/model-training/SKILL.md +68 -0
  28. package/templates/bundle-data-pipeline/skills/rag-pipeline/SKILL.md +128 -0
  29. package/templates/bundle-frontend-spa/.spec/constitution.md +32 -0
  30. package/templates/bundle-frontend-spa/AGENTS.md +107 -0
  31. package/templates/bundle-frontend-spa/skills/authentication/SKILL.md +90 -0
  32. package/templates/bundle-frontend-spa/skills/component-design/SKILL.md +115 -0
  33. package/templates/bundle-frontend-spa/skills/e2e-testing/SKILL.md +101 -0
  34. package/templates/bundle-frontend-spa/skills/integration-api/SKILL.md +95 -0
  35. package/templates/bundle-frontend-spa/skills/react-patterns/SKILL.md +130 -0
  36. package/templates/bundle-frontend-spa/skills/responsive-layout/SKILL.md +65 -0
  37. package/templates/bundle-frontend-spa/skills/state-management/SKILL.md +86 -0
  38. package/templates/bundle-jhipster-microservices/.spec/constitution.md +37 -0
  39. package/templates/bundle-jhipster-microservices/AGENTS.md +307 -0
  40. package/templates/bundle-jhipster-microservices/skills/ci-cd-pipeline/SKILL.md +112 -0
  41. package/templates/bundle-jhipster-microservices/skills/clean-architecture/SKILL.md +99 -0
  42. package/templates/bundle-jhipster-microservices/skills/ddd-tactical/SKILL.md +138 -0
  43. package/templates/bundle-jhipster-microservices/skills/jhipster-angular/SKILL.md +97 -0
  44. package/templates/bundle-jhipster-microservices/skills/jhipster-docker-k8s/SKILL.md +183 -0
  45. package/templates/bundle-jhipster-microservices/skills/jhipster-entities/SKILL.md +87 -0
  46. package/templates/bundle-jhipster-microservices/skills/jhipster-gateway/SKILL.md +96 -0
  47. package/templates/bundle-jhipster-microservices/skills/jhipster-kafka/SKILL.md +145 -0
  48. package/templates/bundle-jhipster-microservices/skills/jhipster-registry/SKILL.md +83 -0
  49. package/templates/bundle-jhipster-microservices/skills/jhipster-service/SKILL.md +131 -0
  50. package/templates/bundle-jhipster-microservices/skills/testing-strategy/SKILL.md +95 -0
  51. package/templates/bundle-jhipster-monorepo/.spec/constitution.md +32 -0
  52. package/templates/bundle-jhipster-monorepo/AGENTS.md +227 -0
  53. package/templates/bundle-jhipster-monorepo/skills/clean-architecture/SKILL.md +99 -0
  54. package/templates/bundle-jhipster-monorepo/skills/ddd-tactical/SKILL.md +138 -0
  55. package/templates/bundle-jhipster-monorepo/skills/jhipster-angular/SKILL.md +166 -0
  56. package/templates/bundle-jhipster-monorepo/skills/jhipster-entities/SKILL.md +141 -0
  57. package/templates/bundle-jhipster-monorepo/skills/jhipster-liquibase/SKILL.md +95 -0
  58. package/templates/bundle-jhipster-monorepo/skills/jhipster-security/SKILL.md +89 -0
  59. package/templates/bundle-jhipster-monorepo/skills/jhipster-spring/SKILL.md +155 -0
  60. package/templates/bundle-jhipster-monorepo/skills/testing-strategy/SKILL.md +95 -0
@@ -0,0 +1,141 @@
1
+ ---
2
+ name: jhipster-entities
3
+ description: Criar e gerenciar entidades JHipster com JDL, relationships, enums, validações e geração de código. Use quando precisar criar entidades, definir modelo de dados, ou gerar código com JHipster.
4
+ ---
5
+
6
+ # JHipster Entities
7
+
8
+ ## Fluxo
9
+
10
+ ```
11
+ 1. Definir entidade no JDL
12
+ 2. Rodar jhipster import-jdl
13
+ 3. Enriquecer entidade gerada com comportamento DDD
14
+ 4. Ajustar Liquibase changeset se necessário
15
+ 5. Criar/ajustar testes
16
+ ```
17
+
18
+ ## JDL — Sintaxe
19
+
20
+ ```jdl
21
+ entity NomeDaEntidade {
22
+ campo TipoDoCampo validacao
23
+ }
24
+ ```
25
+
26
+ ### Tipos disponíveis
27
+ | Tipo JDL | Java | DB |
28
+ |---|---|---|
29
+ | String | String | varchar(255) |
30
+ | Integer | Integer | integer |
31
+ | Long | Long | bigint |
32
+ | Float | Float | float |
33
+ | Double | Double | double |
34
+ | BigDecimal | BigDecimal | decimal |
35
+ | Boolean | Boolean | boolean |
36
+ | LocalDate | LocalDate | date |
37
+ | Instant | Instant | timestamp |
38
+ | ZonedDateTime | ZonedDateTime | timestamp |
39
+ | Duration | Duration | bigint |
40
+ | UUID | UUID | uuid |
41
+ | Blob | byte[] | blob |
42
+ | AnyBlob | byte[] | blob |
43
+ | ImageBlob | byte[] | blob |
44
+ | TextBlob | String | clob |
45
+
46
+ ### Validações
47
+ ```jdl
48
+ entity Product {
49
+ name String required minlength(3) maxlength(100)
50
+ price BigDecimal required min(0)
51
+ description TextBlob
52
+ active Boolean required
53
+ sku String required unique pattern(/^[A-Z]{3}-[0-9]{4}$/)
54
+ }
55
+ ```
56
+
57
+ ### Relationships
58
+ ```jdl
59
+ relationship OneToMany {
60
+ Department{employees} to Employee{department(name) required}
61
+ }
62
+
63
+ relationship ManyToMany {
64
+ Project{members(login)} to User with builtInEntity
65
+ }
66
+
67
+ relationship OneToOne {
68
+ Employee{address} to Address with jpaDerivedIdentifier
69
+ }
70
+ ```
71
+
72
+ ### Enums
73
+ ```jdl
74
+ enum Status {
75
+ ACTIVE("Ativo"),
76
+ INACTIVE("Inativo"),
77
+ SUSPENDED("Suspenso")
78
+ }
79
+ ```
80
+
81
+ ## Gerar código
82
+
83
+ ```bash
84
+ # Gerar a partir do JDL
85
+ jhipster import-jdl jhipster-jdl.jdl
86
+
87
+ # Gerar entidade individual
88
+ jhipster entity Demand
89
+ ```
90
+
91
+ ## Enriquecer com DDD
92
+
93
+ Após gerar, adicionar comportamento à entidade:
94
+
95
+ ```java
96
+ @Entity
97
+ @Table(name = "demand")
98
+ public class Demand implements Serializable {
99
+
100
+ // Campos gerados pelo JHipster (manter)
101
+ @Id
102
+ @GeneratedValue(strategy = GenerationType.SEQUENCE)
103
+ private Long id;
104
+
105
+ @NotNull
106
+ @Lob
107
+ private String description;
108
+
109
+ @NotNull
110
+ @Enumerated(EnumType.STRING)
111
+ private DemandStatus status;
112
+
113
+ @OneToMany(mappedBy = "demand", cascade = CascadeType.ALL, orphanRemoval = true)
114
+ private List<Task> tasks = new ArrayList<>();
115
+
116
+ // ADICIONAR — comportamento rico
117
+ public void addTask(Task task) {
118
+ if (this.tasks.size() >= 20) {
119
+ throw new BusinessException("Máximo 20 tasks por demanda");
120
+ }
121
+ this.tasks.add(task);
122
+ task.setDemand(this);
123
+ }
124
+
125
+ public void removeTask(Task task) {
126
+ this.tasks.remove(task);
127
+ task.setDemand(null);
128
+ }
129
+
130
+ public boolean isCompletable() {
131
+ return this.tasks.stream().allMatch(t -> t.getStatus() == TaskStatus.COMPLETED);
132
+ }
133
+
134
+ public void complete() {
135
+ if (!isCompletable()) {
136
+ throw new BusinessException("Nem todas as tasks foram concluídas");
137
+ }
138
+ this.status = DemandStatus.COMPLETED;
139
+ }
140
+ }
141
+ ```
@@ -0,0 +1,95 @@
1
+ ---
2
+ name: jhipster-liquibase
3
+ description: Gerenciar migrations de banco com Liquibase no JHipster. Use quando precisar criar tabelas, alterar schema, adicionar colunas, ou criar índices.
4
+ ---
5
+
6
+ # Liquibase no JHipster
7
+
8
+ ## Estrutura
9
+
10
+ ```
11
+ src/main/resources/config/liquibase/
12
+ ├── master.xml # Arquivo principal (lista todos os changelogs)
13
+ ├── changelog/
14
+ │ ├── 00000000000000_initial_schema.xml
15
+ │ ├── 20260327100000_added_entity_Demand.xml
16
+ │ ├── 20260327100100_added_entity_Task.xml
17
+ │ └── 20260327120000_added_column_priority.xml
18
+ └── data/
19
+ ├── authority.csv # Dados iniciais
20
+ └── user.csv
21
+ ```
22
+
23
+ ## Criar changeset
24
+
25
+ ### Nova tabela
26
+ ```xml
27
+ <changeSet id="20260327-1" author="dev">
28
+ <createTable tableName="demand">
29
+ <column name="id" type="bigint" autoIncrement="true">
30
+ <constraints primaryKey="true" nullable="false"/>
31
+ </column>
32
+ <column name="description" type="clob">
33
+ <constraints nullable="false"/>
34
+ </column>
35
+ <column name="status" type="varchar(20)">
36
+ <constraints nullable="false"/>
37
+ </column>
38
+ <column name="priority" type="varchar(20)">
39
+ <constraints nullable="false"/>
40
+ </column>
41
+ <column name="created_at" type="timestamp">
42
+ <constraints nullable="false"/>
43
+ </column>
44
+ <column name="completed_at" type="timestamp"/>
45
+ </createTable>
46
+
47
+ <createIndex indexName="idx_demand_status" tableName="demand">
48
+ <column name="status"/>
49
+ </createIndex>
50
+ </changeSet>
51
+ ```
52
+
53
+ ### Adicionar coluna
54
+ ```xml
55
+ <changeSet id="20260327-2" author="dev">
56
+ <addColumn tableName="demand">
57
+ <column name="requester" type="varchar(100)">
58
+ <constraints nullable="false"/>
59
+ </column>
60
+ </addColumn>
61
+ </changeSet>
62
+ ```
63
+
64
+ ### Foreign key
65
+ ```xml
66
+ <changeSet id="20260327-3" author="dev">
67
+ <addForeignKeyConstraint
68
+ baseTableName="task"
69
+ baseColumnNames="demand_id"
70
+ constraintName="fk_task_demand"
71
+ referencedTableName="demand"
72
+ referencedColumnNames="id"/>
73
+ </changeSet>
74
+ ```
75
+
76
+ ## Regras
77
+
78
+ - IDs de changeset: `YYYYMMDD-N` (data + sequencial)
79
+ - Nunca modificar changeset já executado em produção
80
+ - Sempre adicionar rollback:
81
+ ```xml
82
+ <changeSet id="20260327-1" author="dev">
83
+ <addColumn tableName="demand">
84
+ <column name="priority" type="varchar(20)"/>
85
+ </addColumn>
86
+ <rollback>
87
+ <dropColumn tableName="demand" columnName="priority"/>
88
+ </rollback>
89
+ </changeSet>
90
+ ```
91
+ - Testar migration com `./mvnw liquibase:updateSQL` antes de rodar
92
+ - Registrar no `master.xml`:
93
+ ```xml
94
+ <include file="config/liquibase/changelog/20260327120000_added_column_priority.xml" relativeToChangelogFile="false"/>
95
+ ```
@@ -0,0 +1,89 @@
1
+ ---
2
+ name: jhipster-security
3
+ description: Configurar segurança no JHipster com JWT, OAuth2, roles e permissões. Use quando precisar implementar autenticação, autorização, roles, ou proteger endpoints.
4
+ ---
5
+
6
+ # JHipster Security
7
+
8
+ ## JWT (padrão monorepo)
9
+
10
+ O JHipster gera JWT automaticamente. Customizar:
11
+
12
+ ### Proteger endpoints por role
13
+
14
+ ```java
15
+ @Configuration
16
+ public class SecurityConfiguration {
17
+
18
+ @Bean
19
+ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
20
+ http
21
+ .authorizeHttpRequests(auth -> auth
22
+ .requestMatchers("/api/public/**").permitAll()
23
+ .requestMatchers("/api/admin/**").hasAuthority(AuthoritiesConstants.ADMIN)
24
+ .requestMatchers("/api/demands/**").hasAnyAuthority(
25
+ AuthoritiesConstants.USER, AuthoritiesConstants.ADMIN)
26
+ .requestMatchers("/api/agents/**").hasAuthority("ROLE_AGENT")
27
+ .requestMatchers("/api/**").authenticated()
28
+ );
29
+ return http.build();
30
+ }
31
+ }
32
+ ```
33
+
34
+ ### Criar roles customizadas
35
+
36
+ ```java
37
+ public final class AuthoritiesConstants {
38
+ public static final String ADMIN = "ROLE_ADMIN";
39
+ public static final String USER = "ROLE_USER";
40
+ public static final String AGENT = "ROLE_AGENT"; // Agentes AI
41
+ public static final String TECH_LEAD = "ROLE_TECH_LEAD"; // Aprovador de merge
42
+ }
43
+ ```
44
+
45
+ ### Segurança no método
46
+
47
+ ```java
48
+ @Service
49
+ public class DemandServiceImpl implements DemandService {
50
+
51
+ @PreAuthorize("hasAuthority('ROLE_TECH_LEAD')")
52
+ public void approveMerge(Long demandId) {
53
+ // Somente tech lead pode aprovar merge
54
+ }
55
+
56
+ @PreAuthorize("#login == authentication.name or hasAuthority('ROLE_ADMIN')")
57
+ public DemandDTO findByUser(String login) {
58
+ // Usuário vê só suas demands, admin vê todas
59
+ }
60
+ }
61
+ ```
62
+
63
+ ## OAuth2 + Keycloak (recomendado para microservices)
64
+
65
+ ```yaml
66
+ # application.yml
67
+ spring:
68
+ security:
69
+ oauth2:
70
+ client:
71
+ provider:
72
+ oidc:
73
+ issuer-uri: http://keycloak:9080/realms/jhipster
74
+ registration:
75
+ oidc:
76
+ client-id: web_app
77
+ client-secret: web_app
78
+ scope: openid,profile,email
79
+ ```
80
+
81
+ ## Checklist de segurança
82
+
83
+ - [ ] Rate limiting nos endpoints públicos
84
+ - [ ] CORS configurado corretamente (não `*` em prod)
85
+ - [ ] CSRF habilitado para browser, desabilitado para API
86
+ - [ ] Secrets em variáveis de ambiente, nunca no código
87
+ - [ ] Senhas com BCrypt (padrão JHipster)
88
+ - [ ] Audit trail para operações críticas
89
+ - [ ] HTTPS em produção
@@ -0,0 +1,155 @@
1
+ ---
2
+ name: jhipster-spring
3
+ description: Desenvolver backend Spring Boot no JHipster com services, repositories, DTOs, MapStruct e Spring Security. Use quando precisar criar endpoints, services, ou customizar o backend JHipster.
4
+ ---
5
+
6
+ # JHipster Spring Boot
7
+
8
+ ## Camadas no JHipster
9
+
10
+ ```
11
+ Controller (web/rest/) → Service (service/) → Repository (repository/) → Entity (domain/)
12
+
13
+ DTO + Mapper (service/dto/ + service/mapper/)
14
+ ```
15
+
16
+ ## Controller — REST API
17
+
18
+ ```java
19
+ @RestController
20
+ @RequestMapping("/api/v1/demands")
21
+ public class DemandResource {
22
+
23
+ private final DemandService demandService;
24
+
25
+ public DemandResource(DemandService demandService) {
26
+ this.demandService = demandService;
27
+ }
28
+
29
+ @GetMapping
30
+ public ResponseEntity<List<DemandDTO>> getAllDemands(
31
+ @RequestParam(required = false) DemandStatus status,
32
+ @org.springdoc.core.annotations.ParameterObject Pageable pageable) {
33
+ Page<DemandDTO> page = demandService.findAll(status, pageable);
34
+ return ResponseEntity.ok()
35
+ .headers(PaginationUtil.generatePaginationHttpHeaders(
36
+ ServletUriComponentsBuilder.fromCurrentRequest(), page))
37
+ .body(page.getContent());
38
+ }
39
+
40
+ @PostMapping
41
+ @ResponseStatus(HttpStatus.CREATED)
42
+ public DemandDTO createDemand(@Valid @RequestBody CreateDemandDTO dto) {
43
+ return demandService.create(dto);
44
+ }
45
+
46
+ @GetMapping("/{id}")
47
+ public ResponseEntity<DemandDTO> getDemand(@PathVariable Long id) {
48
+ return ResponseUtil.wrapOrNotFound(demandService.findOne(id));
49
+ }
50
+
51
+ @PutMapping("/{id}")
52
+ public DemandDTO updateDemand(@PathVariable Long id, @Valid @RequestBody UpdateDemandDTO dto) {
53
+ return demandService.update(id, dto);
54
+ }
55
+
56
+ @DeleteMapping("/{id}")
57
+ @ResponseStatus(HttpStatus.NO_CONTENT)
58
+ public void deleteDemand(@PathVariable Long id) {
59
+ demandService.delete(id);
60
+ }
61
+ }
62
+ ```
63
+
64
+ ## Service — Lógica de aplicação
65
+
66
+ ```java
67
+ @Service
68
+ @Transactional
69
+ public class DemandServiceImpl implements DemandService {
70
+
71
+ private final DemandRepository demandRepository;
72
+ private final DemandMapper demandMapper;
73
+
74
+ public DemandServiceImpl(DemandRepository demandRepository, DemandMapper demandMapper) {
75
+ this.demandRepository = demandRepository;
76
+ this.demandMapper = demandMapper;
77
+ }
78
+
79
+ @Override
80
+ public DemandDTO create(CreateDemandDTO dto) {
81
+ Demand demand = new Demand();
82
+ demand.setDescription(dto.description());
83
+ demand.setStatus(DemandStatus.CREATED);
84
+ demand.setPriority(dto.priority());
85
+ demand.setCreatedAt(Instant.now());
86
+ demand = demandRepository.save(demand);
87
+ return demandMapper.toDto(demand);
88
+ }
89
+
90
+ @Override
91
+ @Transactional(readOnly = true)
92
+ public Page<DemandDTO> findAll(DemandStatus status, Pageable pageable) {
93
+ if (status != null) {
94
+ return demandRepository.findByStatus(status, pageable).map(demandMapper::toDto);
95
+ }
96
+ return demandRepository.findAll(pageable).map(demandMapper::toDto);
97
+ }
98
+ }
99
+ ```
100
+
101
+ ## DTO — Records (Java 21)
102
+
103
+ ```java
104
+ public record CreateDemandDTO(
105
+ @NotBlank @Size(min = 10) String description,
106
+ @NotNull Priority priority
107
+ ) {}
108
+
109
+ public record DemandDTO(
110
+ Long id,
111
+ String description,
112
+ DemandStatus status,
113
+ Priority priority,
114
+ Instant createdAt,
115
+ List<TaskDTO> tasks
116
+ ) {}
117
+ ```
118
+
119
+ ## MapStruct Mapper
120
+
121
+ ```java
122
+ @Mapper(componentModel = "spring", uses = {TaskMapper.class})
123
+ public interface DemandMapper extends EntityMapper<DemandDTO, Demand> {
124
+
125
+ @Mapping(target = "tasks", source = "tasks")
126
+ DemandDTO toDto(Demand demand);
127
+
128
+ @Mapping(target = "id", ignore = true)
129
+ @Mapping(target = "tasks", ignore = true)
130
+ Demand toEntity(CreateDemandDTO dto);
131
+ }
132
+ ```
133
+
134
+ ## Repository — Spring Data JPA
135
+
136
+ ```java
137
+ @Repository
138
+ public interface DemandRepository extends JpaRepository<Demand, Long>, JpaSpecificationExecutor<Demand> {
139
+
140
+ Page<Demand> findByStatus(DemandStatus status, Pageable pageable);
141
+
142
+ @Query("SELECT d FROM Demand d LEFT JOIN FETCH d.tasks WHERE d.id = :id")
143
+ Optional<Demand> findByIdWithTasks(@Param("id") Long id);
144
+
145
+ long countByStatus(DemandStatus status);
146
+ }
147
+ ```
148
+
149
+ ## Regras
150
+
151
+ - Constructor injection sempre (nunca `@Autowired` em campo)
152
+ - `@Transactional(readOnly = true)` em queries
153
+ - `@Transactional` apenas no Service, nunca no Controller
154
+ - DTOs na API, Entities no domínio — nunca misturar
155
+ - Validação com Bean Validation (`@Valid`) no Controller
@@ -0,0 +1,95 @@
1
+ ---
2
+ name: testing-strategy
3
+ description: Implementar estratégia de testes com unitários, integração e e2e usando Pytest ou JUnit. Use quando for escrever testes, definir estratégia de testes, ou melhorar cobertura.
4
+ ---
5
+
6
+ # Estratégia de Testes
7
+
8
+ ## Pirâmide
9
+
10
+ ```
11
+ / E2E \ Poucos, lentos, caros
12
+ / Integr. \ Moderados
13
+ / Unitários \ Muitos, rápidos, baratos
14
+ ```
15
+
16
+ ## Testes Unitários — Domínio
17
+
18
+ Testar regras de negócio sem infraestrutura.
19
+
20
+ ```python
21
+ # tests/domain/test_demand.py
22
+ class TestDemand:
23
+ def test_should_decompose_new_demand(self):
24
+ demand = Demand(id=DemandId.generate(), description="Criar CRUD")
25
+ planner = FakePlanner(tasks=[Task(...), Task(...)])
26
+
27
+ tasks = demand.decompose(planner)
28
+
29
+ assert len(tasks) == 2
30
+ assert demand.status == DemandStatus.PLANNED
31
+
32
+ def test_should_reject_decompose_if_already_planned(self):
33
+ demand = Demand(id=DemandId.generate(), description="Criar CRUD")
34
+ demand.decompose(FakePlanner(tasks=[Task(...)]))
35
+
36
+ with pytest.raises(DemandAlreadyDecomposedException):
37
+ demand.decompose(FakePlanner(tasks=[]))
38
+
39
+ def test_should_not_allow_more_than_20_tasks(self):
40
+ demand = Demand(id=DemandId.generate(), description="Mega projeto")
41
+ for i in range(20):
42
+ demand.add_task(Task(...))
43
+
44
+ with pytest.raises(TooManyTasksException):
45
+ demand.add_task(Task(...))
46
+ ```
47
+
48
+ ## Testes Unitários — Value Objects
49
+
50
+ ```python
51
+ class TestComplianceScore:
52
+ def test_passing_score(self):
53
+ score = ComplianceScore(85.0)
54
+ assert score.is_passing() is True
55
+
56
+ def test_failing_score(self):
57
+ score = ComplianceScore(60.0)
58
+ assert score.is_passing() is False
59
+
60
+ def test_invalid_score_raises(self):
61
+ with pytest.raises(ValueError):
62
+ ComplianceScore(150.0)
63
+ ```
64
+
65
+ ## Testes de Integração — Repositórios
66
+
67
+ ```python
68
+ # tests/infrastructure/test_pg_demand_repository.py
69
+ @pytest.fixture
70
+ def db_session():
71
+ engine = create_engine(TEST_DATABASE_URL)
72
+ with Session(engine) as session:
73
+ yield session
74
+ session.rollback()
75
+
76
+ class TestPgDemandRepository:
77
+ def test_should_save_and_find_demand(self, db_session):
78
+ repo = PgDemandRepository(db_session)
79
+ demand = Demand(id=DemandId.generate(), description="Test")
80
+
81
+ repo.save(demand)
82
+ found = repo.find_by_id(demand.id)
83
+
84
+ assert found.description == "Test"
85
+ ```
86
+
87
+ ## Naming
88
+
89
+ ```
90
+ test_should_<resultado>_when_<condição>
91
+
92
+ test_should_return_error_when_email_is_invalid
93
+ test_should_decompose_demand_when_status_is_created
94
+ test_should_reject_merge_when_conflicts_exist
95
+ ```