feat(tickets): ajoute les pièces jointes sur tickets (#108)
Stockage flat côté ticket + métadonnées d'attachments, lecture exposée côté backend/MCP, et UI minimale de liste/ajout dans TicketDetail. Traverse le domaine (Issue, ports), l'application (usecases + assistant de ticket), les adaptateurs infra/MCP (issues store, orchestrateur), les DTO backend/web- server/app-tauri, et le frontend (domain/ports/adapters/hooks/UI). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -2,9 +2,9 @@ use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use domain::{
|
||||
AgentId, AgentIssueRef, AgentIssueRole, Issue, IssueActor, IssueId, IssueListFilter,
|
||||
IssueNumberAllocator, IssuePriority, IssueRef, IssueStatus, IssueStore, IssueStoreError,
|
||||
MarkdownDoc, ProjectPath, SprintId,
|
||||
AgentId, AgentIssueRef, AgentIssueRole, Issue, IssueActor, IssueAttachmentId, IssueId,
|
||||
IssueListFilter, IssueNumberAllocator, IssuePriority, IssueRef, IssueStatus, IssueStore,
|
||||
IssueStoreError, LocalPath, MarkdownDoc, ProjectPath, SprintId,
|
||||
};
|
||||
use infrastructure::{FsIssueNumberAllocator, FsIssueStore};
|
||||
use uuid::Uuid;
|
||||
@ -402,6 +402,66 @@ async fn issue_store_persists_and_filters_sprint_membership() {
|
||||
assert_eq!(rows[0].sprint, Some(sprint_id));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn issue_store_adds_reads_and_marks_attachment_summarized() {
|
||||
let tmp = TempDir::new();
|
||||
let root = tmp.root();
|
||||
let store = FsIssueStore::new();
|
||||
let issue = issue(&root, 7, "Attachment ticket");
|
||||
store.create(&root, &issue).await.unwrap();
|
||||
let source = tmp.child("source-note.txt");
|
||||
std::fs::write(&source, "attachment payload").unwrap();
|
||||
let attachment_id = IssueAttachmentId::new("att_1".to_owned()).unwrap();
|
||||
|
||||
let updated = store
|
||||
.add_attachment_from_path(
|
||||
&root,
|
||||
issue.reference(),
|
||||
&LocalPath::new(source.to_string_lossy().to_string()),
|
||||
attachment_id.clone(),
|
||||
"source-note.txt".to_owned(),
|
||||
"text/plain".to_owned(),
|
||||
IssueActor::User,
|
||||
2_000,
|
||||
issue.version,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(updated.attachments.len(), 1);
|
||||
assert_eq!(updated.attachments[0].filename, "source-note.txt");
|
||||
assert_eq!(updated.attachments[0].mime, "text/plain");
|
||||
assert_eq!(updated.attachments[0].size_bytes, 18);
|
||||
assert!(!updated.attachments[0].summarized_in_carnet);
|
||||
assert!(tmp
|
||||
.child(".ideai/tickets/7/attachments/att_1-source-note.txt")
|
||||
.exists());
|
||||
|
||||
let content = store
|
||||
.read_attachment(&root, issue.reference(), &attachment_id)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(content.attachment.id, attachment_id);
|
||||
assert_eq!(content.bytes, b"attachment payload");
|
||||
|
||||
let summarized = store
|
||||
.mark_attachment_summarized(
|
||||
&root,
|
||||
issue.reference(),
|
||||
&attachment_id,
|
||||
IssueActor::System,
|
||||
3_000,
|
||||
updated.version,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let attachment = &summarized.attachments[0];
|
||||
assert!(attachment.summarized_in_carnet);
|
||||
assert_eq!(attachment.summarized_by, Some(IssueActor::System));
|
||||
assert_eq!(attachment.summarized_at, Some(3_000));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn issue_store_delete_removes_files_and_index_entry() {
|
||||
let tmp = TempDir::new();
|
||||
|
||||
Reference in New Issue
Block a user