//! L8 tests for the Git DTO (de)serialisation contract: camelCase on the //! wire, transparent list shapes, `From<…Output>` conversions, and request //! DTO deserialisation. use app_tauri_lib::dto::{ GitBranchesDto, GitCheckoutRequestDto, GitCommitDto, GitCommitListDto, GitCommitRequestDto, GitFileStatusDto, GitStageRequestDto, GitStatusListDto, GraphCommitDto, GraphCommitListDto, }; use application::{GitBranchesOutput, GitCommitOutput, GitGraphOutput, GitLogOutput, GitStatusOutput}; use domain::ports::{GitCommitInfo, GitFileStatus, GraphCommit}; use serde_json::json; use uuid::Uuid; // --------------------------------------------------------------------------- // GitFileStatusDto serialisation // --------------------------------------------------------------------------- #[test] fn git_file_status_dto_serialises_camelcase() { let dto = GitFileStatusDto { path: "src/main.rs".to_owned(), staged: true, }; let v = serde_json::to_value(&dto).unwrap(); assert_eq!(v["path"], "src/main.rs"); assert_eq!(v["staged"], true); // no snake_case leak — both fields are single words so no renaming needed, // but verify no extra/unexpected keys. assert!(v.get("path").is_some()); assert!(v.get("staged").is_some()); } #[test] fn git_file_status_from_domain_type() { let domain = GitFileStatus { path: "README.md".to_owned(), staged: false, }; let dto = GitFileStatusDto::from(domain); assert_eq!(dto.path, "README.md"); assert!(!dto.staged); } // --------------------------------------------------------------------------- // GitStatusListDto — transparent array // --------------------------------------------------------------------------- #[test] fn git_status_list_dto_is_transparent_array() { let out = GitStatusOutput { entries: vec![ GitFileStatus { path: "a.rs".to_owned(), staged: true, }, GitFileStatus { path: "b.rs".to_owned(), staged: false, }, ], }; let dto = GitStatusListDto::from(out); let v = serde_json::to_value(&dto).unwrap(); let arr = v.as_array().expect("transparent array"); assert_eq!(arr.len(), 2); assert_eq!(arr[0]["path"], "a.rs"); assert_eq!(arr[0]["staged"], true); assert_eq!(arr[1]["path"], "b.rs"); assert_eq!(arr[1]["staged"], false); } // --------------------------------------------------------------------------- // GitCommitDto serialisation & From impls // --------------------------------------------------------------------------- #[test] fn git_commit_dto_serialises_camelcase() { let dto = GitCommitDto { hash: "abc123".to_owned(), summary: "feat: add git support".to_owned(), }; let v = serde_json::to_value(&dto).unwrap(); assert_eq!(v["hash"], "abc123"); assert_eq!(v["summary"], "feat: add git support"); } #[test] fn git_commit_dto_from_commit_info() { let info = GitCommitInfo { hash: "deadbeef".to_owned(), summary: "fix: something".to_owned(), }; let dto = GitCommitDto::from(info); assert_eq!(dto.hash, "deadbeef"); assert_eq!(dto.summary, "fix: something"); } #[test] fn git_commit_dto_from_commit_output() { let out = GitCommitOutput { commit: GitCommitInfo { hash: "cafebabe".to_owned(), summary: "chore: cleanup".to_owned(), }, }; let dto = GitCommitDto::from(out); assert_eq!(dto.hash, "cafebabe"); assert_eq!(dto.summary, "chore: cleanup"); } // --------------------------------------------------------------------------- // GitCommitListDto — transparent array // --------------------------------------------------------------------------- #[test] fn git_commit_list_dto_is_transparent_array() { let out = GitLogOutput { commits: vec![ GitCommitInfo { hash: "aaa".to_owned(), summary: "first".to_owned(), }, GitCommitInfo { hash: "bbb".to_owned(), summary: "second".to_owned(), }, ], }; let dto = GitCommitListDto::from(out); let v = serde_json::to_value(&dto).unwrap(); let arr = v.as_array().expect("transparent array"); assert_eq!(arr.len(), 2); assert_eq!(arr[0]["hash"], "aaa"); assert_eq!(arr[0]["summary"], "first"); } // --------------------------------------------------------------------------- // GitBranchesDto serialisation (incl. current: null) // --------------------------------------------------------------------------- #[test] fn git_branches_dto_serialises_with_current() { let out = GitBranchesOutput { branches: vec!["main".to_owned(), "feature/x".to_owned()], current: Some("main".to_owned()), }; let dto = GitBranchesDto::from(out); let v = serde_json::to_value(&dto).unwrap(); assert_eq!(v["branches"][0], "main"); assert_eq!(v["branches"][1], "feature/x"); assert_eq!(v["current"], "main"); } #[test] fn git_branches_dto_serialises_null_current() { let out = GitBranchesOutput { branches: vec![], current: None, }; let dto = GitBranchesDto::from(out); let v = serde_json::to_value(&dto).unwrap(); assert!(v["current"].is_null(), "current must be null when None"); } // --------------------------------------------------------------------------- // GraphCommitDto serialisation & From impls // --------------------------------------------------------------------------- #[test] fn graph_commit_dto_serialises_camelcase() { let commit = GraphCommit { hash: "abc123".to_owned(), summary: "feat: graph".to_owned(), parents: vec!["deadbeef".to_owned()], refs: vec!["main".to_owned(), "tag: v1.0".to_owned()], author: "Alice".to_owned(), timestamp: 1_700_000_000, }; let dto = GraphCommitDto::from(commit); let v = serde_json::to_value(&dto).unwrap(); assert_eq!(v["hash"], "abc123"); assert_eq!(v["summary"], "feat: graph"); assert_eq!(v["parents"][0], "deadbeef"); assert_eq!(v["refs"][0], "main"); assert_eq!(v["refs"][1], "tag: v1.0"); assert_eq!(v["author"], "Alice"); assert_eq!(v["timestamp"], 1_700_000_000_i64); // No snake_case leaks for multi-word fields — none here but verify shape. assert!(v.get("hash").is_some()); } #[test] fn graph_commit_list_dto_is_transparent_array() { let out = GitGraphOutput { commits: vec![ GraphCommit { hash: "aaa".to_owned(), summary: "first".to_owned(), parents: vec![], refs: vec!["main".to_owned()], author: "Bob".to_owned(), timestamp: 1000, }, GraphCommit { hash: "bbb".to_owned(), summary: "second".to_owned(), parents: vec!["aaa".to_owned()], refs: vec![], author: "Bob".to_owned(), timestamp: 999, }, ], }; let dto = GraphCommitListDto::from(out); let v = serde_json::to_value(&dto).unwrap(); let arr = v.as_array().expect("transparent array"); assert_eq!(arr.len(), 2); assert_eq!(arr[0]["hash"], "aaa"); assert_eq!(arr[0]["refs"][0], "main"); assert_eq!(arr[1]["hash"], "bbb"); assert!(arr[1]["parents"][0] == "aaa"); } // --------------------------------------------------------------------------- // Request DTO deserialisation (camelCase) // --------------------------------------------------------------------------- #[test] fn git_stage_request_deserialises_camelcase() { let project_id = Uuid::from_u128(1).to_string(); let raw = json!({ "projectId": project_id, "path": "src/lib.rs" }); let dto: GitStageRequestDto = serde_json::from_value(raw).unwrap(); assert_eq!(dto.project_id, project_id); assert_eq!(dto.path, "src/lib.rs"); } #[test] fn git_commit_request_deserialises_camelcase() { let project_id = Uuid::from_u128(2).to_string(); let raw = json!({ "projectId": project_id, "message": "feat: initial commit" }); let dto: GitCommitRequestDto = serde_json::from_value(raw).unwrap(); assert_eq!(dto.project_id, project_id); assert_eq!(dto.message, "feat: initial commit"); } #[test] fn git_checkout_request_deserialises_camelcase() { let project_id = Uuid::from_u128(3).to_string(); let raw = json!({ "projectId": project_id, "branch": "feature/awesome" }); let dto: GitCheckoutRequestDto = serde_json::from_value(raw).unwrap(); assert_eq!(dto.project_id, project_id); assert_eq!(dto.branch, "feature/awesome"); }