use lore::gitlab::transformers::merge_request::transform_merge_request; use lore::gitlab::types::{GitLabAuthor, GitLabMergeRequest, GitLabReferences, GitLabReviewer}; fn make_test_mr() -> GitLabMergeRequest { GitLabMergeRequest { id: 12345, iid: 42, project_id: 100, title: "Add user authentication".to_string(), description: Some("Implements JWT auth flow".to_string()), state: "merged".to_string(), draft: false, work_in_progress: false, source_branch: "feature/auth".to_string(), target_branch: "main".to_string(), sha: Some("abc123def456".to_string()), references: Some(GitLabReferences { short: "!42".to_string(), full: "group/project!42".to_string(), }), detailed_merge_status: Some("mergeable".to_string()), merge_status_legacy: Some("can_be_merged".to_string()), created_at: "2024-01-15T10:00:00.000Z".to_string(), updated_at: "2024-01-20T14:30:00.000Z".to_string(), merged_at: Some("2024-01-20T14:30:00.000Z".to_string()), closed_at: None, author: GitLabAuthor { id: 1, username: "johndoe".to_string(), name: "John Doe".to_string(), }, merge_user: Some(GitLabAuthor { id: 2, username: "janedoe".to_string(), name: "Jane Doe".to_string(), }), merged_by: Some(GitLabAuthor { id: 2, username: "janedoe".to_string(), name: "Jane Doe".to_string(), }), labels: vec!["enhancement".to_string(), "auth".to_string()], assignees: vec![GitLabAuthor { id: 3, username: "bob".to_string(), name: "Bob Smith".to_string(), }], reviewers: vec![GitLabReviewer { id: 4, username: "alice".to_string(), name: "Alice Wong".to_string(), }], web_url: "https://gitlab.example.com/group/project/-/merge_requests/42".to_string(), merge_commit_sha: Some("merge1234567890".to_string()), squash_commit_sha: None, } } #[test] fn transforms_mr_with_all_fields() { let mr = make_test_mr(); let result = transform_merge_request(&mr, 200).unwrap(); assert_eq!(result.merge_request.gitlab_id, 12345); assert_eq!(result.merge_request.iid, 42); assert_eq!(result.merge_request.project_id, 200); assert_eq!(result.merge_request.title, "Add user authentication"); assert_eq!( result.merge_request.description, Some("Implements JWT auth flow".to_string()) ); assert_eq!(result.merge_request.state, "merged"); assert!(!result.merge_request.draft); assert_eq!(result.merge_request.author_username, "johndoe"); assert_eq!(result.merge_request.source_branch, "feature/auth"); assert_eq!(result.merge_request.target_branch, "main"); assert_eq!( result.merge_request.head_sha, Some("abc123def456".to_string()) ); assert_eq!( result.merge_request.references_short, Some("!42".to_string()) ); assert_eq!( result.merge_request.references_full, Some("group/project!42".to_string()) ); assert_eq!( result.merge_request.detailed_merge_status, Some("mergeable".to_string()) ); assert_eq!( result.merge_request.merge_user_username, Some("janedoe".to_string()) ); assert_eq!( result.merge_request.web_url, "https://gitlab.example.com/group/project/-/merge_requests/42" ); } #[test] fn preserves_commit_shas() { let mr = make_test_mr(); let result = transform_merge_request(&mr, 200).unwrap(); assert_eq!( result.merge_request.merge_commit_sha, Some("merge1234567890".to_string()) ); assert!(result.merge_request.squash_commit_sha.is_none()); } #[test] fn parses_timestamps_to_ms_epoch() { let mr = make_test_mr(); let result = transform_merge_request(&mr, 200).unwrap(); assert_eq!(result.merge_request.created_at, 1705312800000); assert_eq!(result.merge_request.updated_at, 1705761000000); assert_eq!(result.merge_request.merged_at, Some(1705761000000)); } #[test] fn handles_timezone_offset_timestamps() { let mut mr = make_test_mr(); mr.created_at = "2024-01-15T05:00:00-05:00".to_string(); let result = transform_merge_request(&mr, 200).unwrap(); assert_eq!(result.merge_request.created_at, 1705312800000); } #[test] fn sets_last_seen_at_to_current_time() { let mr = make_test_mr(); let before = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_millis() as i64; let result = transform_merge_request(&mr, 200).unwrap(); let after = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_millis() as i64; assert!(result.merge_request.last_seen_at >= before); assert!(result.merge_request.last_seen_at <= after); } #[test] fn extracts_label_names() { let mr = make_test_mr(); let result = transform_merge_request(&mr, 200).unwrap(); assert_eq!(result.label_names.len(), 2); assert_eq!(result.label_names[0], "enhancement"); assert_eq!(result.label_names[1], "auth"); } #[test] fn handles_empty_labels() { let mut mr = make_test_mr(); mr.labels = vec![]; let result = transform_merge_request(&mr, 200).unwrap(); assert!(result.label_names.is_empty()); } #[test] fn extracts_assignee_usernames() { let mr = make_test_mr(); let result = transform_merge_request(&mr, 200).unwrap(); assert_eq!(result.assignee_usernames.len(), 1); assert_eq!(result.assignee_usernames[0], "bob"); } #[test] fn extracts_reviewer_usernames() { let mr = make_test_mr(); let result = transform_merge_request(&mr, 200).unwrap(); assert_eq!(result.reviewer_usernames.len(), 1); assert_eq!(result.reviewer_usernames[0], "alice"); } #[test] fn handles_empty_assignees_and_reviewers() { let mut mr = make_test_mr(); mr.assignees = vec![]; mr.reviewers = vec![]; let result = transform_merge_request(&mr, 200).unwrap(); assert!(result.assignee_usernames.is_empty()); assert!(result.reviewer_usernames.is_empty()); } #[test] fn draft_prefers_draft_field() { let mut mr = make_test_mr(); mr.draft = true; mr.work_in_progress = false; let result = transform_merge_request(&mr, 200).unwrap(); assert!(result.merge_request.draft); } #[test] fn draft_falls_back_to_work_in_progress() { let mut mr = make_test_mr(); mr.draft = false; mr.work_in_progress = true; let result = transform_merge_request(&mr, 200).unwrap(); assert!(result.merge_request.draft); } #[test] fn draft_false_when_both_false() { let mut mr = make_test_mr(); mr.draft = false; mr.work_in_progress = false; let result = transform_merge_request(&mr, 200).unwrap(); assert!(!result.merge_request.draft); } #[test] fn detailed_merge_status_prefers_non_legacy() { let mut mr = make_test_mr(); mr.detailed_merge_status = Some("checking".to_string()); mr.merge_status_legacy = Some("can_be_merged".to_string()); let result = transform_merge_request(&mr, 200).unwrap(); assert_eq!( result.merge_request.detailed_merge_status, Some("checking".to_string()) ); } #[test] fn detailed_merge_status_falls_back_to_legacy() { let mut mr = make_test_mr(); mr.detailed_merge_status = None; mr.merge_status_legacy = Some("can_be_merged".to_string()); let result = transform_merge_request(&mr, 200).unwrap(); assert_eq!( result.merge_request.detailed_merge_status, Some("can_be_merged".to_string()) ); } #[test] fn merge_user_prefers_merge_user_field() { let mut mr = make_test_mr(); mr.merge_user = Some(GitLabAuthor { id: 10, username: "merge_user_name".to_string(), name: "Merge User".to_string(), }); mr.merged_by = Some(GitLabAuthor { id: 11, username: "merged_by_name".to_string(), name: "Merged By".to_string(), }); let result = transform_merge_request(&mr, 200).unwrap(); assert_eq!( result.merge_request.merge_user_username, Some("merge_user_name".to_string()) ); } #[test] fn merge_user_falls_back_to_merged_by() { let mut mr = make_test_mr(); mr.merge_user = None; mr.merged_by = Some(GitLabAuthor { id: 11, username: "merged_by_name".to_string(), name: "Merged By".to_string(), }); let result = transform_merge_request(&mr, 200).unwrap(); assert_eq!( result.merge_request.merge_user_username, Some("merged_by_name".to_string()) ); } #[test] fn handles_missing_references() { let mut mr = make_test_mr(); mr.references = None; let result = transform_merge_request(&mr, 200).unwrap(); assert!(result.merge_request.references_short.is_none()); assert!(result.merge_request.references_full.is_none()); } #[test] fn handles_missing_sha() { let mut mr = make_test_mr(); mr.sha = None; let result = transform_merge_request(&mr, 200).unwrap(); assert!(result.merge_request.head_sha.is_none()); } #[test] fn handles_missing_description() { let mut mr = make_test_mr(); mr.description = None; let result = transform_merge_request(&mr, 200).unwrap(); assert!(result.merge_request.description.is_none()); } #[test] fn handles_closed_at_timestamp() { let mut mr = make_test_mr(); mr.state = "closed".to_string(); mr.merged_at = None; mr.closed_at = Some("2024-01-18T12:00:00.000Z".to_string()); let result = transform_merge_request(&mr, 200).unwrap(); assert!(result.merge_request.merged_at.is_none()); assert_eq!(result.merge_request.closed_at, Some(1705579200000)); } #[test] fn passes_through_locked_state() { let mut mr = make_test_mr(); mr.state = "locked".to_string(); let result = transform_merge_request(&mr, 200).unwrap(); assert_eq!(result.merge_request.state, "locked"); } #[test] fn returns_error_for_invalid_created_at() { let mut mr = make_test_mr(); mr.created_at = "not-a-timestamp".to_string(); let result = transform_merge_request(&mr, 200); assert!(result.is_err()); let err = result.unwrap_err(); assert!(err.contains("not-a-timestamp")); } #[test] fn returns_error_for_invalid_updated_at() { let mut mr = make_test_mr(); mr.updated_at = "invalid".to_string(); let result = transform_merge_request(&mr, 200); assert!(result.is_err()); } #[test] fn returns_error_for_invalid_merged_at() { let mut mr = make_test_mr(); mr.merged_at = Some("bad-timestamp".to_string()); let result = transform_merge_request(&mr, 200); assert!(result.is_err()); } #[test] fn returns_error_for_invalid_closed_at() { let mut mr = make_test_mr(); mr.closed_at = Some("garbage".to_string()); let result = transform_merge_request(&mr, 200); assert!(result.is_err()); }