R-012 — Campanha inválida ou expirada bloqueia cadastro de trial

TLDR: POST /v1/trials/register rejeita com 400 se o slug não corresponder a uma campanha com end_date >= hoje.

Given / When / Then

Dado que POST /v1/trials/register recebe um slug Quando não existe nenhuma Campaign com esse slug e end_date >= hoje Então a requisição falha com ValidationError {"slug": "Campanha não encontrada ou expirada."} e nenhum recurso é criado

Restrições

  • A busca usa end_date__gte=timezone.localdate() — campanhas com end_date igual a hoje ainda são válidas
  • O campo status da Campaign não é verificado nesta regra; apenas end_date e a existência do registro

Teste vinculado

tests/trials/test_service.py — test_register_rejects_unknown_slug, test_register_rejects_expired_campaign


R-013 — Campanha sem trial vinculado bloqueia cadastro

TLDR: Se a campanha existe mas não tem trial_id, o endpoint rejeita com 400.

Given / When / Then

Dado que a campanha identificada pelo slug existe e não está expirada Quando campaign.trial_id é nulo Então a requisição falha com ValidationError {"slug": "A campanha não possui um trial vinculado."} e nenhum recurso é criado

Restrições

  • Campanhas sem trial vinculado são uma má configuração administrativa; o endpoint não cria trial em branco
  • A validade do Trial em si (datas, status) não é verificada — é responsabilidade do administrador manter a consistência

Teste vinculado

tests/trials/test_service.py — test_register_rejects_campaign_without_trial


R-014 — Conta existente ou trial anterior bloqueia sem revelar o motivo

TLDR: Se o email ou phone já existem como User ou já foram usados em algum UserTrial, o endpoint retorna 201 e envia e-mail de rejeição — sem expor que o cadastro foi bloqueado.

Given / When / Then

Dado que os dados enviados passam pelas validações de campanha (R-012, R-013) Quando existe um User com o mesmo email ou phone ou existe um UserTrial (incluindo soft-deleted via all_objects) com esse email ou phone Então: - Um e-mail de rejeição é enviado via task Celery send_trial_negative_response - O endpoint retorna 201 sem criar nenhum recurso - O corpo da resposta não distingue a causa do bloqueio (prevenção de enumeração de contas)

Restrições

  • UserTrial.all_objects é usado para incluir registros com soft delete — um usuário que teve trial e depois foi deletado logicamente ainda é bloqueado
  • O retorno 201 é intencional: não deve ser alterado para 400, pois isso revelaria a existência da conta

Teste vinculado

tests/trials/test_service.py — test_register_sends_negative_response_when_user_exists, test_register_sends_negative_response_when_trial_already_used