feat(attachments): fondation pipeline durable d'attachments agent/chat + sandbox-safe (#154, QA verte)
Socle #154 : entité attachment, store, contrat DTO et routage session. Modules : domain/chat_attachment, application/chat_attachments, infrastructure/chat_attachments, app-tauri (commands/lib), backend dto.
This commit is contained in:
@ -6,11 +6,14 @@
|
||||
//! - non-regression: every `TerminalSessionDto` construction path now serialises a
|
||||
//! `cellKind` and PTY paths keep `"pty"`.
|
||||
|
||||
use app_tauri_lib::dto::{CellKind, ReattachChatDto, ReplyChunk, TerminalSessionDto};
|
||||
use app_tauri_lib::dto::{
|
||||
CellKind, ChatAttachmentDto, ChatAttachmentInputDto, ImportChatAttachmentsRequestDto,
|
||||
ImportChatAttachmentsResponseDto, ReattachChatDto, ReplyChunk, TerminalSessionDto,
|
||||
};
|
||||
use application::{LaunchAgentOutput, StructuredSessionDescriptor};
|
||||
use domain::project::ProjectPath;
|
||||
use domain::SessionId;
|
||||
use domain::{AgentId, NodeId, PtySize, SessionKind, SessionStatus, TerminalSession};
|
||||
use domain::{ChatAttachmentSourceKind, SessionId};
|
||||
use serde_json::json;
|
||||
use uuid::Uuid;
|
||||
|
||||
@ -103,6 +106,84 @@ fn reply_chunk_rejects_snake_case_tag() {
|
||||
assert!(r.is_err(), "snake_case kind must not deserialise");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Chat attachments — structured input + durable metadata, camelCase
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[test]
|
||||
fn chat_attachment_input_dto_deserialises_camel_case() {
|
||||
let dto: ChatAttachmentInputDto = serde_json::from_value(json!({
|
||||
"path": "/tmp/picked.png",
|
||||
"mime": "image/png",
|
||||
"sourceKind": "clipboard"
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(dto.path, "/tmp/picked.png");
|
||||
assert_eq!(dto.mime.as_deref(), Some("image/png"));
|
||||
assert_eq!(dto.source_kind, Some(ChatAttachmentSourceKind::Clipboard));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chat_attachment_dto_serialises_camel_case() {
|
||||
let dto = ChatAttachmentDto {
|
||||
id: "attach-1".to_owned(),
|
||||
filename: "picked.png".to_owned(),
|
||||
mime: "image/png".to_owned(),
|
||||
size_bytes: 9,
|
||||
source_kind: ChatAttachmentSourceKind::LocalFile,
|
||||
storage_path: "agent-chat/session/attach-1-picked.png".to_owned(),
|
||||
readable_path: "/project/.ideai/attachments/agent-chat/session/attach-1-picked.png"
|
||||
.to_owned(),
|
||||
created_at: 42,
|
||||
};
|
||||
|
||||
let v = serde_json::to_value(dto).unwrap();
|
||||
assert_eq!(v["sizeBytes"], 9);
|
||||
assert_eq!(v["sourceKind"], "localFile");
|
||||
assert_eq!(v["storagePath"], "agent-chat/session/attach-1-picked.png");
|
||||
assert!(v.get("readable_path").is_none(), "no snake_case leak");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_chat_attachments_request_response_use_camel_case() {
|
||||
let request: ImportChatAttachmentsRequestDto = serde_json::from_value(json!({
|
||||
"sessionId": "sess-1",
|
||||
"attachmentPaths": ["/tmp/legacy.txt"],
|
||||
"attachments": [{
|
||||
"path": "/tmp/picked.png",
|
||||
"mime": "image/png",
|
||||
"sourceKind": "dragDrop"
|
||||
}]
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(request.session_id, "sess-1");
|
||||
assert_eq!(request.attachment_paths, vec!["/tmp/legacy.txt"]);
|
||||
assert_eq!(
|
||||
request.attachments[0].source_kind,
|
||||
Some(ChatAttachmentSourceKind::DragDrop)
|
||||
);
|
||||
|
||||
let response = ImportChatAttachmentsResponseDto {
|
||||
attachments: vec![ChatAttachmentDto {
|
||||
id: "attach-1".to_owned(),
|
||||
filename: "picked.png".to_owned(),
|
||||
mime: "image/png".to_owned(),
|
||||
size_bytes: 9,
|
||||
source_kind: ChatAttachmentSourceKind::LocalFile,
|
||||
storage_path: "agent-chat/session/attach-1-picked.png".to_owned(),
|
||||
readable_path: "/project/.ideai/attachments/agent-chat/session/attach-1-picked.png"
|
||||
.to_owned(),
|
||||
created_at: 42,
|
||||
}],
|
||||
};
|
||||
let v = serde_json::to_value(response).unwrap();
|
||||
|
||||
assert_eq!(v["attachments"][0]["sizeBytes"], 9);
|
||||
assert!(v.get("attachment_paths").is_none(), "no snake_case leak");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ReattachChatDto — typed scrollback, camelCase (zone 5)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user