feat(tickets): checkpoint backend V1 du système de tickets (T1-T5)
Checkpoint de travail — QA formelle encore à venir (après reset Codex). `cargo build` workspace OK, tests ticket/issue verts (domaine, store, use cases, app-tauri 47/51). Le domaine s'appelle `Issue`, exposé « ticket » côté MCP/UI. - T1 domaine : entité Issue (statut, priorité, liens, carnet), events, ids, ports. - T2 infra : store FS Markdown + allocator de références #N. - T3 application : use cases Issue (create/read/list/update/status/ priority/carnet/link/unlink/assign) + erreurs dédiées. - T4 surface MCP : 10 outils publics idea_ticket_* (23 outils au total), mappés vers les use cases Issue. - T5 app-tauri : commandes Tauri ticket_* + DTOs d'events Issue + câblage state.rs. Dette de test PRÉ-EXISTANTE héritée de develop (4 tests app-tauri mcp_e2e_loopback / mcp_serve_peer rouges) hors périmètre tickets, non traitée ici. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
120
crates/domain/tests/issue.rs
Normal file
120
crates/domain/tests/issue.rs
Normal file
@ -0,0 +1,120 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use domain::{
|
||||
AgentIssueRef, AgentIssueRole, Issue, IssueActor, IssueError, IssueId, IssueLink,
|
||||
IssueLinkKind, IssueNumber, IssuePriority, IssueRef, IssueStatus, IssueVersion, MarkdownDoc,
|
||||
};
|
||||
|
||||
fn issue_number(n: u64) -> IssueNumber {
|
||||
IssueNumber::new(n).unwrap()
|
||||
}
|
||||
|
||||
fn issue() -> Issue {
|
||||
Issue::new(
|
||||
IssueId::new_random(),
|
||||
issue_number(7),
|
||||
"Fix boot reconcile",
|
||||
MarkdownDoc::new("Description"),
|
||||
IssueStatus::Open,
|
||||
IssuePriority::Medium,
|
||||
MarkdownDoc::new("Carnet"),
|
||||
vec![IssueLink {
|
||||
target: IssueRef::from(issue_number(8)),
|
||||
kind: IssueLinkKind::RelatesTo,
|
||||
}],
|
||||
vec![AgentIssueRef {
|
||||
agent_id: domain::AgentId::new_random(),
|
||||
role: AgentIssueRole::Assigned,
|
||||
}],
|
||||
IssueActor::User,
|
||||
1_000,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_ref_parses_and_formats_strict_hash_number() {
|
||||
let reference = IssueRef::from_str("#42").unwrap();
|
||||
|
||||
assert_eq!(reference.number().get(), 42);
|
||||
assert_eq!(reference.to_string(), "#42");
|
||||
assert_eq!(
|
||||
IssueRef::from_str("42"),
|
||||
Err(IssueError::InvalidRef("42".into()))
|
||||
);
|
||||
assert_eq!(IssueRef::from_str("#0"), Err(IssueError::InvalidNumber));
|
||||
assert_eq!(
|
||||
IssueRef::from_str("#abc"),
|
||||
Err(IssueError::InvalidRef("#abc".into()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_number_must_be_positive() {
|
||||
assert_eq!(IssueNumber::new(0), Err(IssueError::InvalidNumber));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_title_must_not_be_empty() {
|
||||
let err = Issue::new(
|
||||
IssueId::new_random(),
|
||||
issue_number(1),
|
||||
" ",
|
||||
MarkdownDoc::new("Description"),
|
||||
IssueStatus::Open,
|
||||
IssuePriority::Medium,
|
||||
MarkdownDoc::default(),
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
IssueActor::User,
|
||||
1,
|
||||
)
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(err, IssueError::EmptyTitle);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_rejects_self_link() {
|
||||
let err = Issue::new(
|
||||
IssueId::new_random(),
|
||||
issue_number(9),
|
||||
"Self link",
|
||||
MarkdownDoc::new("Description"),
|
||||
IssueStatus::Open,
|
||||
IssuePriority::Medium,
|
||||
MarkdownDoc::default(),
|
||||
vec![IssueLink {
|
||||
target: IssueRef::from(issue_number(9)),
|
||||
kind: IssueLinkKind::Blocks,
|
||||
}],
|
||||
Vec::new(),
|
||||
IssueActor::User,
|
||||
1,
|
||||
)
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
err,
|
||||
IssueError::SelfLink {
|
||||
reference: IssueRef::from(issue_number(9))
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_mutation_increments_version_and_updates_actor_time() {
|
||||
let issue = issue();
|
||||
assert_eq!(issue.version, IssueVersion::INITIAL);
|
||||
|
||||
let updated = issue
|
||||
.mutate(IssueActor::System, 2_000, |i| {
|
||||
i.status = IssueStatus::InProgress;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(updated.version.get(), 2);
|
||||
assert_eq!(updated.status, IssueStatus::InProgress);
|
||||
assert_eq!(updated.updated_by, IssueActor::System);
|
||||
assert_eq!(updated.updated_at, 2_000);
|
||||
}
|
||||
Reference in New Issue
Block a user