Project

General

Profile

Feature #49 » qa49-proof.txt

Redmine Admin, 06/11/2026 07:11 PM

 
========================================================================
QA #49 — Backend : enrichir le draft de ticket avec classification &
estimation de complexité (epic #26)
Statut Redmine au démarrage : QA (id 10) — OK pour QA.
Surface : backend uniquement (api/, FastAPI hexagonal).
Branche testée : feat/49-draft-classification-complexity
Commit : 966a5d7 feat(intake): enrich ticket draft with classification
& complexity (#49)
PR : #24 -> dev
Date QA : 2026-06-11
(Secrets filtrés : aucune clé API / token / URL de DB / mot de passe
n'apparaît dans cette preuve.)
========================================================================

------------------------------------------------------------------------
1. GATE BACKEND (api/.venv, PYTHONPATH=src sur la source du worktree)
DB éphémère : pgvector/pgvector:pg16 + alembic upgrade head
------------------------------------------------------------------------
alembic upgrade head ......... 0001 -> 0009 : OK (toutes migrations)

python -m pytest -q (run 1) : 388 passed, 1 skipped, 6 warnings (38.41s)
python -m pytest -q (run 2) : 388 passed, 1 skipped, 6 warnings (37.79s)
-> idempotent x2 sur DB partagée : OK

ruff check . ................. All checks passed!
mypy src ..................... Success: no issues found in 109 source files

------------------------------------------------------------------------
2. TESTS CIBLÉS PAR CRITÈRE D'ACCEPTATION (tous PASSED)
------------------------------------------------------------------------
[Domaine — value objects]
test_complexity_clamps_score_into_range
test_complexity_normalises_on_build
test_draft_defaults_to_zero_complexity_for_legacy_callers
test_draft_carries_explicit_complexity_and_classification

[Infra — MistralTicketSpecWriter (draft_ticket)]
test_draft_clamps_complexity_and_defaults_missing_to_zero
test_draft_unparseable_complexity_score_degrades_to_zero
test_draft_french_localizes_complexity_explanation_instruction
test_draft_french_language_instructs_french_values
test_draft_english_language_instructs_english_values

[Application — create_ticket_from_draft]
test_persists_classification_and_complexity_in_description
test_bf_disagree_marker_added_when_client_changed_classification
test_no_bf_disagree_marker_when_classification_unchanged
test_no_bf_disagree_marker_when_no_proposal_given
test_optional_argumentation_persisted_when_present
test_empty_argumentation_is_non_blocking_and_omitted
test_complexity_explanation_persisted_in_client_language

[Présentation / API + multi-tenant]
test_create_flags_bf_disagree_when_client_reclassifies
test_create_empty_argumentation_is_non_blocking
test_create_returns_409_when_project_not_linked
test_other_client_cannot_draft (isolation multi-tenant)

------------------------------------------------------------------------
3. SMOKE END-TO-END — rendu réel de la description Redmine
(_render_description, exécuté en direct)
------------------------------------------------------------------------
CAS A — FR, classification INCHANGÉE (feature), argumentation présente :

Le client veut exporter ses tickets en CSV.

## Acceptance criteria
- bouton export
- fichier .csv valide

## Classification
- feature

## Complexity
- 4/10 — Logique simple, nouvel endpoint et un bouton UI.

## Argumentation
Priorité client élevée.

-> classification + note/10 + explication FR persistées ; PAS de
bf-disagree (classif inchangée) ; argumentation persistée.

CAS B — EN, client CHANGE bug -> feature, argumentation vide (espaces),
score proposé 15 (hors borne) :

Login is broken.

## Acceptance criteria
- login works

## Classification
- feature
- `bf-disagree`

## Complexity
- 10/10 — Out-of-range score should clamp to 10

-> token `bf-disagree` estampillé (classif finale != proposition) ;
argumentation vide => section omise (non-bloquant) ;
score 15 clampé à 10/10.

------------------------------------------------------------------------
4. SYNTHÈSE PAR CRITÈRE D'ACCEPTATION
------------------------------------------------------------------------
AC1 draft_ticket renvoie classification (bug|feature) + complexity
(note/10 + explication) par défaut .................... OK
AC2 explication de complexité dans la langue du client (FR/EN) ... OK
AC3 create_ticket_from_draft persiste classification + note +
explication sur le ticket Redmine .................... OK
AC4 classification finale != proposition => `bf-disagree` ........ OK
(label = token machine-greppable dans le bloc metadata de la
description ; Redmine n'a pas de modèle de label de 1ère classe
ici et les custom fields 2-9 sont réservés Jenkins — décision de
conception du dev, critère satisfait sous cette lecture.)
AC5 argumentation optionnelle persistée ; vide/espaces =>
non-bloquant, section omise ......................... OK
AC6 isolation multi-tenant (un client n'écrit que ses tickets) ... OK
AC7 gate vert pytest x2 + ruff + mypy, TDD .................. OK

VERDICT : PASS — tous les critères d'acceptation OK.
========================================================================
    (1-1/1)