test(gitlab): Add GitLabIssueRef deserialization tests

Adds test coverage for the new GitLabIssueRef type used by the
MR closes_issues API endpoint:

- deserializes_gitlab_issue_ref: Single object with all fields
- deserializes_gitlab_issue_ref_array: Array of refs (typical API response)

Validates that cross-project references (different project_id values)
deserialize correctly, which is important for cross-project close links.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Taylor Eernisse
2026-02-05 00:03:47 -05:00
parent a76dc8089e
commit 976ad92ef0

View File

@@ -1,7 +1,5 @@
//! Tests for GitLab API response type deserialization.
use lore::gitlab::types::{ use lore::gitlab::types::{
GitLabAuthor, GitLabDiscussion, GitLabIssue, GitLabLabelEvent, GitLabLabelRef, GitLabAuthor, GitLabDiscussion, GitLabIssue, GitLabIssueRef, GitLabLabelEvent, GitLabLabelRef,
GitLabMergeRequest, GitLabMergeRequestRef, GitLabMilestone, GitLabMilestoneEvent, GitLabMergeRequest, GitLabMergeRequestRef, GitLabMilestone, GitLabMilestoneEvent,
GitLabMilestoneRef, GitLabNote, GitLabNotePosition, GitLabReferences, GitLabReviewer, GitLabMilestoneRef, GitLabNote, GitLabNotePosition, GitLabReferences, GitLabReviewer,
GitLabStateEvent, GitLabStateEvent,
@@ -212,7 +210,6 @@ fn handles_diffnote_type() {
#[test] #[test]
fn handles_missing_resolvable_field() { fn handles_missing_resolvable_field() {
// GitLab API sometimes omits resolvable/resolved fields entirely
let json = r#"{ let json = r#"{
"id": 12345, "id": 12345,
"type": null, "type": null,
@@ -229,7 +226,6 @@ fn handles_missing_resolvable_field() {
let note: GitLabNote = serde_json::from_str(json).expect("Failed to deserialize note"); let note: GitLabNote = serde_json::from_str(json).expect("Failed to deserialize note");
// Should default to false when not present
assert!(!note.resolvable); assert!(!note.resolvable);
assert!(!note.resolved); assert!(!note.resolved);
} }
@@ -258,7 +254,6 @@ fn deserializes_system_note() {
#[test] #[test]
fn deserializes_note_position_with_partial_fields() { fn deserializes_note_position_with_partial_fields() {
// DiffNote position can have partial data (e.g., new file with no old_path)
let json = r#"{ let json = r#"{
"old_path": null, "old_path": null,
"new_path": "src/new_file.rs", "new_path": "src/new_file.rs",
@@ -403,8 +398,6 @@ fn deserializes_gitlab_milestone() {
assert_eq!(milestone.due_date, Some("2024-04-01".to_string())); assert_eq!(milestone.due_date, Some("2024-04-01".to_string()));
} }
// === Checkpoint 2: Merge Request type tests ===
#[test] #[test]
fn deserializes_gitlab_merge_request_from_fixture() { fn deserializes_gitlab_merge_request_from_fixture() {
let json = include_str!("fixtures/gitlab_merge_request.json"); let json = include_str!("fixtures/gitlab_merge_request.json");
@@ -449,7 +442,6 @@ fn deserializes_gitlab_merge_request_with_references() {
#[test] #[test]
fn deserializes_gitlab_merge_request_minimal() { fn deserializes_gitlab_merge_request_minimal() {
// Test with minimal fields (no optional ones)
let json = r#"{ let json = r#"{
"id": 1, "id": 1,
"iid": 1, "iid": 1,
@@ -509,7 +501,6 @@ fn deserializes_gitlab_merge_request_with_draft() {
#[test] #[test]
fn deserializes_gitlab_merge_request_with_work_in_progress_fallback() { fn deserializes_gitlab_merge_request_with_work_in_progress_fallback() {
// Older GitLab instances use work_in_progress instead of draft
let json = r#"{ let json = r#"{
"id": 1, "id": 1,
"iid": 1, "iid": 1,
@@ -528,13 +519,11 @@ fn deserializes_gitlab_merge_request_with_work_in_progress_fallback() {
let mr: GitLabMergeRequest = serde_json::from_str(json).expect("Failed to deserialize WIP MR"); let mr: GitLabMergeRequest = serde_json::from_str(json).expect("Failed to deserialize WIP MR");
assert!(mr.work_in_progress); assert!(mr.work_in_progress);
// draft defaults to false when not present
assert!(!mr.draft); assert!(!mr.draft);
} }
#[test] #[test]
fn deserializes_gitlab_merge_request_with_locked_state() { fn deserializes_gitlab_merge_request_with_locked_state() {
// locked is a transitional state during merge
let json = r#"{ let json = r#"{
"id": 1, "id": 1,
"iid": 1, "iid": 1,
@@ -640,8 +629,6 @@ fn deserializes_diffnote_position_with_line_range() {
assert_eq!(range.end_line(), Some(15)); assert_eq!(range.end_line(), Some(15));
} }
// === Resource Event type tests ===
#[test] #[test]
fn deserializes_state_event_closed_by_mr() { fn deserializes_state_event_closed_by_mr() {
let json = r#"{ let json = r#"{
@@ -896,3 +883,60 @@ fn deserializes_milestone_ref() {
assert_eq!(ms_ref.iid, 5); assert_eq!(ms_ref.iid, 5);
assert_eq!(ms_ref.title, "v1.0"); assert_eq!(ms_ref.title, "v1.0");
} }
#[test]
fn deserializes_gitlab_issue_ref() {
let json = r#"{
"id": 5001,
"iid": 42,
"project_id": 100,
"title": "Fix authentication bug",
"state": "opened",
"web_url": "https://gitlab.example.com/group/project/-/issues/42"
}"#;
let issue_ref: GitLabIssueRef =
serde_json::from_str(json).expect("Failed to deserialize issue ref");
assert_eq!(issue_ref.id, 5001);
assert_eq!(issue_ref.iid, 42);
assert_eq!(issue_ref.project_id, 100);
assert_eq!(issue_ref.title, "Fix authentication bug");
assert_eq!(issue_ref.state, "opened");
assert_eq!(
issue_ref.web_url,
"https://gitlab.example.com/group/project/-/issues/42"
);
}
#[test]
fn deserializes_gitlab_issue_ref_array() {
let json = r#"[
{
"id": 5001,
"iid": 42,
"project_id": 100,
"title": "Issue one",
"state": "opened",
"web_url": "https://gitlab.example.com/-/issues/42"
},
{
"id": 5002,
"iid": 43,
"project_id": 200,
"title": "Issue two from another project",
"state": "closed",
"web_url": "https://gitlab.example.com/-/issues/43"
}
]"#;
let refs: Vec<GitLabIssueRef> =
serde_json::from_str(json).expect("Failed to deserialize issue ref array");
assert_eq!(refs.len(), 2);
assert_eq!(refs[0].iid, 42);
assert_eq!(refs[0].project_id, 100);
assert_eq!(refs[1].iid, 43);
assert_eq!(refs[1].project_id, 200);
assert_eq!(refs[1].state, "closed");
}