Skip to main content

aivcs_core/domain/ci/
verification.rs

1//! Verification link types.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use uuid::Uuid;
6
7/// Links a CI run to its verification status.
8///
9/// A verification link records whether a CI run (or a repair's rerun)
10/// has been verified and can be promoted.
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
12pub struct VerificationLink {
13    /// The CI run that was verified.
14    pub ci_run_id: Uuid,
15
16    /// Agent spec digest at the time of verification.
17    pub spec_digest: String,
18
19    /// Git SHA at the time of verification.
20    pub git_sha: String,
21
22    /// Whether verification passed.
23    pub verified: bool,
24
25    /// When verification completed.
26    pub verified_at: Option<DateTime<Utc>>,
27
28    /// Optional verification run ID (the rerun that confirmed the fix).
29    pub verification_run_id: Option<Uuid>,
30}
31
32impl VerificationLink {
33    /// Create a new pending verification link.
34    pub fn new(ci_run_id: Uuid, spec_digest: String, git_sha: String) -> Self {
35        Self {
36            ci_run_id,
37            spec_digest,
38            git_sha,
39            verified: false,
40            verified_at: None,
41            verification_run_id: None,
42        }
43    }
44
45    /// Mark as verified in-place.
46    pub fn verify(&mut self, verification_run_id: Uuid) {
47        self.verified = true;
48        self.verified_at = Some(Utc::now());
49        self.verification_run_id = Some(verification_run_id);
50    }
51
52    /// Return a verified copy, useful for builder-style chaining.
53    pub fn into_verified(mut self, verification_run_id: Uuid) -> Self {
54        self.verify(verification_run_id);
55        self
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[test]
64    fn test_verification_link_serde_roundtrip() {
65        let link = VerificationLink::new(
66            Uuid::new_v4(),
67            "spec-digest-abc".to_string(),
68            "abc123".to_string(),
69        );
70
71        let json = serde_json::to_string(&link).expect("serialize");
72        let deserialized: VerificationLink = serde_json::from_str(&json).expect("deserialize");
73        assert_eq!(link, deserialized);
74    }
75
76    #[test]
77    fn test_verification_link_new_defaults() {
78        let link = VerificationLink::new(Uuid::new_v4(), "spec".to_string(), "sha".to_string());
79        assert!(!link.verified);
80        assert!(link.verified_at.is_none());
81        assert!(link.verification_run_id.is_none());
82    }
83
84    #[test]
85    fn test_verification_link_verify() {
86        let mut link = VerificationLink::new(Uuid::new_v4(), "spec".to_string(), "sha".to_string());
87
88        let verify_run = Uuid::new_v4();
89        link.verify(verify_run);
90
91        assert!(link.verified);
92        assert!(link.verified_at.is_some());
93        assert_eq!(link.verification_run_id, Some(verify_run));
94    }
95
96    #[test]
97    fn test_verified_link_serde_roundtrip() {
98        let link = VerificationLink::new(Uuid::new_v4(), "spec".to_string(), "sha".to_string())
99            .into_verified(Uuid::new_v4());
100
101        let json = serde_json::to_string(&link).expect("serialize");
102        let deserialized: VerificationLink = serde_json::from_str(&json).expect("deserialize");
103        assert_eq!(link, deserialized);
104    }
105}