koprogo_api/domain/entities/
consent.rs1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5const VALID_CONSENT_TYPES: [&str; 2] = ["privacy_policy", "terms"];
7
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
14pub struct ConsentRecord {
15 pub id: Uuid,
16 pub user_id: Uuid,
17 pub organization_id: Uuid,
18 pub consent_type: String,
19 pub accepted_at: DateTime<Utc>,
20 pub ip_address: Option<String>,
21 pub user_agent: Option<String>,
22 pub policy_version: String,
23 pub created_at: DateTime<Utc>,
24 pub updated_at: DateTime<Utc>,
25}
26
27impl ConsentRecord {
28 pub fn new(
38 user_id: Uuid,
39 organization_id: Uuid,
40 consent_type: &str,
41 ip_address: Option<String>,
42 user_agent: Option<String>,
43 policy_version: Option<String>,
44 ) -> Result<Self, String> {
45 if !VALID_CONSENT_TYPES.contains(&consent_type) {
47 return Err(format!(
48 "Invalid consent type '{}'. Must be one of: {}",
49 consent_type,
50 VALID_CONSENT_TYPES.join(", ")
51 ));
52 }
53
54 let version = policy_version.unwrap_or_else(|| "1.0".to_string());
55 if version.is_empty() {
56 return Err("Policy version cannot be empty".to_string());
57 }
58
59 let now = Utc::now();
60
61 Ok(Self {
62 id: Uuid::new_v4(),
63 user_id,
64 organization_id,
65 consent_type: consent_type.to_string(),
66 accepted_at: now,
67 ip_address,
68 user_agent,
69 policy_version: version,
70 created_at: now,
71 updated_at: now,
72 })
73 }
74
75 pub fn is_privacy_policy(&self) -> bool {
77 self.consent_type == "privacy_policy"
78 }
79
80 pub fn is_terms(&self) -> bool {
82 self.consent_type == "terms"
83 }
84}
85
86#[derive(Debug, Clone, Default, Serialize, Deserialize)]
88pub struct ConsentStatus {
89 pub privacy_policy_accepted: bool,
90 pub terms_accepted: bool,
91 pub privacy_policy_accepted_at: Option<DateTime<Utc>>,
92 pub terms_accepted_at: Option<DateTime<Utc>>,
93 pub privacy_policy_version: Option<String>,
94 pub terms_version: Option<String>,
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn test_new_privacy_policy_consent() {
103 let user_id = Uuid::new_v4();
104 let org_id = Uuid::new_v4();
105 let record = ConsentRecord::new(
106 user_id,
107 org_id,
108 "privacy_policy",
109 Some("192.168.1.1".to_string()),
110 Some("Mozilla/5.0".to_string()),
111 Some("1.2".to_string()),
112 )
113 .unwrap();
114
115 assert_eq!(record.user_id, user_id);
116 assert_eq!(record.organization_id, org_id);
117 assert_eq!(record.consent_type, "privacy_policy");
118 assert_eq!(record.policy_version, "1.2");
119 assert!(record.is_privacy_policy());
120 assert!(!record.is_terms());
121 assert_eq!(record.ip_address.as_deref(), Some("192.168.1.1"));
122 assert_eq!(record.user_agent.as_deref(), Some("Mozilla/5.0"));
123 }
124
125 #[test]
126 fn test_new_terms_consent() {
127 let record =
128 ConsentRecord::new(Uuid::new_v4(), Uuid::new_v4(), "terms", None, None, None).unwrap();
129
130 assert_eq!(record.consent_type, "terms");
131 assert_eq!(record.policy_version, "1.0"); assert!(record.is_terms());
133 assert!(!record.is_privacy_policy());
134 }
135
136 #[test]
137 fn test_invalid_consent_type() {
138 let result = ConsentRecord::new(
139 Uuid::new_v4(),
140 Uuid::new_v4(),
141 "invalid_type",
142 None,
143 None,
144 None,
145 );
146 assert!(result.is_err());
147 assert!(result
148 .unwrap_err()
149 .contains("Invalid consent type 'invalid_type'"));
150 }
151
152 #[test]
153 fn test_empty_policy_version() {
154 let result = ConsentRecord::new(
155 Uuid::new_v4(),
156 Uuid::new_v4(),
157 "privacy_policy",
158 None,
159 None,
160 Some("".to_string()),
161 );
162 assert!(result.is_err());
163 assert!(result
164 .unwrap_err()
165 .contains("Policy version cannot be empty"));
166 }
167
168 #[test]
169 fn test_consent_generates_unique_ids() {
170 let r1 =
171 ConsentRecord::new(Uuid::new_v4(), Uuid::new_v4(), "terms", None, None, None).unwrap();
172 let r2 =
173 ConsentRecord::new(Uuid::new_v4(), Uuid::new_v4(), "terms", None, None, None).unwrap();
174 assert_ne!(r1.id, r2.id);
175 }
176
177 #[test]
178 fn test_consent_status_default() {
179 let status = ConsentStatus::default();
180 assert!(!status.privacy_policy_accepted);
181 assert!(!status.terms_accepted);
182 assert!(status.privacy_policy_accepted_at.is_none());
183 assert!(status.terms_accepted_at.is_none());
184 }
185}