1use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct Team {
8 pub id: String,
9 pub name: String,
10 pub organization_id: Option<String>,
11 pub members: Vec<TeamMember>,
12 pub created_at: DateTime<Utc>,
13 pub updated_at: DateTime<Utc>,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct TeamMember {
19 pub id: String,
20 pub name: String,
21 pub email: String,
22 pub role: TeamRole,
23 pub joined_at: DateTime<Utc>,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
28pub enum TeamRole {
29 Admin,
30 Member,
31 Viewer,
32}
33
34impl TeamRole {
35 pub fn as_str(&self) -> &'static str {
36 match self {
37 TeamRole::Admin => "admin",
38 TeamRole::Member => "member",
39 TeamRole::Viewer => "viewer",
40 }
41 }
42}
43
44impl std::str::FromStr for TeamRole {
45 type Err = String;
46
47 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
48 match s {
49 "admin" => Ok(TeamRole::Admin),
50 "member" => Ok(TeamRole::Member),
51 "viewer" => Ok(TeamRole::Viewer),
52 _ => Err(format!("Invalid team role: {}", s)),
53 }
54 }
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct TeamStandards {
60 pub id: String,
61 pub team_id: String,
62 pub code_review_rules: Vec<CodeReviewRule>,
63 pub templates: Vec<Template>,
64 pub steering_docs: Vec<SteeringDoc>,
65 pub compliance_requirements: Vec<ComplianceRequirement>,
66 pub version: u32,
67 pub created_at: DateTime<Utc>,
68 pub updated_at: DateTime<Utc>,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct CodeReviewRule {
74 pub id: String,
75 pub name: String,
76 pub description: String,
77 pub enabled: bool,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct Template {
83 pub id: String,
84 pub name: String,
85 pub description: String,
86 pub content: String,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct SteeringDoc {
92 pub id: String,
93 pub name: String,
94 pub content: String,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct ComplianceRequirement {
100 pub id: String,
101 pub name: String,
102 pub description: String,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct SharedRule {
108 pub id: String,
109 pub name: String,
110 pub description: String,
111 pub scope: RuleScope,
112 pub enforced: bool,
113 pub promoted_by: String,
114 pub promoted_at: DateTime<Utc>,
115 pub version: u32,
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
120pub enum RuleScope {
121 Project,
122 Team,
123 Organization,
124}
125
126impl RuleScope {
127 pub fn as_str(&self) -> &'static str {
128 match self {
129 RuleScope::Project => "project",
130 RuleScope::Team => "team",
131 RuleScope::Organization => "organization",
132 }
133 }
134}
135
136impl std::str::FromStr for RuleScope {
137 type Err = String;
138
139 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
140 match s {
141 "project" => Ok(RuleScope::Project),
142 "team" => Ok(RuleScope::Team),
143 "organization" => Ok(RuleScope::Organization),
144 _ => Err(format!("Invalid rule scope: {}", s)),
145 }
146 }
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct AdoptionMetrics {
152 pub rule_id: String,
153 pub total_members: u32,
154 pub adopting_members: u32,
155 pub adoption_percentage: f64,
156 pub adoption_trend: Vec<(DateTime<Utc>, f64)>,
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct EffectivenessMetrics {
162 pub rule_id: String,
163 pub positive_outcomes: u32,
164 pub negative_outcomes: u32,
165 pub effectiveness_score: f64,
166 pub impact_trend: Vec<(DateTime<Utc>, f64)>,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
171pub struct StandardsOverride {
172 pub project_id: String,
173 pub overridden_standards: Vec<String>,
174 pub created_at: DateTime<Utc>,
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize)]
179pub struct MergedStandards {
180 pub organization_standards: Option<TeamStandards>,
181 pub team_standards: Option<TeamStandards>,
182 pub project_standards: Option<TeamStandards>,
183 pub final_standards: TeamStandards,
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize)]
188pub struct AuditLogEntry {
189 pub id: String,
190 pub team_id: String,
191 pub user_id: String,
192 pub action: String,
193 pub resource: String,
194 pub result: String,
195 pub timestamp: DateTime<Utc>,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct TeamAnalyticsReport {
201 pub team_id: String,
202 pub total_members: u32,
203 pub adoption_metrics: Vec<AdoptionMetrics>,
204 pub effectiveness_metrics: Vec<EffectivenessMetrics>,
205 pub generated_at: DateTime<Utc>,
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 fn create_test_team() -> Team {
214 Team {
215 id: "team-1".to_string(),
216 name: "Test Team".to_string(),
217 organization_id: Some("org-1".to_string()),
218 members: vec![],
219 created_at: Utc::now(),
220 updated_at: Utc::now(),
221 }
222 }
223
224 fn create_test_member() -> TeamMember {
226 TeamMember {
227 id: "member-1".to_string(),
228 name: "John Doe".to_string(),
229 email: "john@example.com".to_string(),
230 role: TeamRole::Member,
231 joined_at: Utc::now(),
232 }
233 }
234
235 fn create_test_standards() -> TeamStandards {
237 TeamStandards {
238 id: "standards-1".to_string(),
239 team_id: "team-1".to_string(),
240 code_review_rules: vec![CodeReviewRule {
241 id: "rule-1".to_string(),
242 name: "Test Rule".to_string(),
243 description: "A test rule".to_string(),
244 enabled: true,
245 }],
246 templates: vec![Template {
247 id: "template-1".to_string(),
248 name: "Test Template".to_string(),
249 description: "A test template".to_string(),
250 content: "template content".to_string(),
251 }],
252 steering_docs: vec![SteeringDoc {
253 id: "doc-1".to_string(),
254 name: "Test Doc".to_string(),
255 content: "doc content".to_string(),
256 }],
257 compliance_requirements: vec![ComplianceRequirement {
258 id: "compliance-1".to_string(),
259 name: "Test Compliance".to_string(),
260 description: "A test compliance requirement".to_string(),
261 }],
262 version: 1,
263 created_at: Utc::now(),
264 updated_at: Utc::now(),
265 }
266 }
267
268 #[test]
269 fn test_team_serialization_to_json() {
270 let team = create_test_team();
271 let json = serde_json::to_string(&team).expect("Failed to serialize to JSON");
272 assert!(json.contains("\"id\":\"team-1\""));
273 assert!(json.contains("\"name\":\"Test Team\""));
274 }
275
276 #[test]
277 fn test_team_deserialization_from_json() {
278 let json = r#"{"id":"team-1","name":"Test Team","organization_id":"org-1","members":[],"created_at":"2024-01-01T00:00:00Z","updated_at":"2024-01-01T00:00:00Z"}"#;
279 let team: Team = serde_json::from_str(json).expect("Failed to deserialize from JSON");
280 assert_eq!(team.id, "team-1");
281 assert_eq!(team.name, "Test Team");
282 assert_eq!(team.organization_id, Some("org-1".to_string()));
283 }
284
285 #[test]
286 fn test_team_serialization_to_yaml() {
287 let team = create_test_team();
288 let yaml = serde_yaml::to_string(&team).expect("Failed to serialize to YAML");
289 assert!(yaml.contains("id: team-1"));
290 assert!(yaml.contains("name: Test Team"));
291 }
292
293 #[test]
294 fn test_team_deserialization_from_yaml() {
295 let yaml = r#"
296id: team-1
297name: Test Team
298organization_id: org-1
299members: []
300created_at: 2024-01-01T00:00:00Z
301updated_at: 2024-01-01T00:00:00Z
302"#;
303 let team: Team = serde_yaml::from_str(yaml).expect("Failed to deserialize from YAML");
304 assert_eq!(team.id, "team-1");
305 assert_eq!(team.name, "Test Team");
306 }
307
308 #[test]
309 fn test_team_member_serialization_to_json() {
310 let member = create_test_member();
311 let json = serde_json::to_string(&member).expect("Failed to serialize to JSON");
312 assert!(json.contains("\"id\":\"member-1\""));
313 assert!(json.contains("\"email\":\"john@example.com\""));
314 }
315
316 #[test]
317 fn test_team_member_deserialization_from_json() {
318 let json = r#"{"id":"member-1","name":"John Doe","email":"john@example.com","role":"Member","joined_at":"2024-01-01T00:00:00Z"}"#;
319 let member: TeamMember =
320 serde_json::from_str(json).expect("Failed to deserialize from JSON");
321 assert_eq!(member.id, "member-1");
322 assert_eq!(member.name, "John Doe");
323 assert_eq!(member.role, TeamRole::Member);
324 }
325
326 #[test]
327 fn test_team_role_as_str() {
328 assert_eq!(TeamRole::Admin.as_str(), "admin");
329 assert_eq!(TeamRole::Member.as_str(), "member");
330 assert_eq!(TeamRole::Viewer.as_str(), "viewer");
331 }
332
333 #[test]
334 fn test_team_role_from_str() {
335 use std::str::FromStr;
336 assert_eq!(TeamRole::from_str("admin"), Ok(TeamRole::Admin));
337 assert_eq!(TeamRole::from_str("member"), Ok(TeamRole::Member));
338 assert_eq!(TeamRole::from_str("viewer"), Ok(TeamRole::Viewer));
339 assert!(TeamRole::from_str("invalid").is_err());
340 }
341
342 #[test]
343 fn test_team_standards_serialization_to_json() {
344 let standards = create_test_standards();
345 let json = serde_json::to_string(&standards).expect("Failed to serialize to JSON");
346 assert!(json.contains("\"id\":\"standards-1\""));
347 assert!(json.contains("\"team_id\":\"team-1\""));
348 assert!(json.contains("\"version\":1"));
349 }
350
351 #[test]
352 fn test_team_standards_deserialization_from_json() {
353 let standards = create_test_standards();
354 let json = serde_json::to_string(&standards).expect("Failed to serialize");
355 let deserialized: TeamStandards =
356 serde_json::from_str(&json).expect("Failed to deserialize");
357 assert_eq!(deserialized.id, standards.id);
358 assert_eq!(deserialized.team_id, standards.team_id);
359 assert_eq!(deserialized.version, standards.version);
360 assert_eq!(deserialized.code_review_rules.len(), 1);
361 assert_eq!(deserialized.templates.len(), 1);
362 assert_eq!(deserialized.steering_docs.len(), 1);
363 assert_eq!(deserialized.compliance_requirements.len(), 1);
364 }
365
366 #[test]
367 fn test_team_standards_serialization_to_yaml() {
368 let standards = create_test_standards();
369 let yaml = serde_yaml::to_string(&standards).expect("Failed to serialize to YAML");
370 assert!(yaml.contains("id: standards-1"));
371 assert!(yaml.contains("team_id: team-1"));
372 assert!(yaml.contains("version: 1"));
373 }
374
375 #[test]
376 fn test_team_standards_deserialization_from_yaml() {
377 let standards = create_test_standards();
378 let yaml = serde_yaml::to_string(&standards).expect("Failed to serialize");
379 let deserialized: TeamStandards =
380 serde_yaml::from_str(&yaml).expect("Failed to deserialize");
381 assert_eq!(deserialized.id, standards.id);
382 assert_eq!(deserialized.team_id, standards.team_id);
383 assert_eq!(deserialized.version, standards.version);
384 }
385
386 #[test]
387 fn test_shared_rule_serialization_to_json() {
388 let rule = SharedRule {
389 id: "rule-1".to_string(),
390 name: "Test Rule".to_string(),
391 description: "A test rule".to_string(),
392 scope: RuleScope::Team,
393 enforced: true,
394 promoted_by: "admin-1".to_string(),
395 promoted_at: Utc::now(),
396 version: 1,
397 };
398 let json = serde_json::to_string(&rule).expect("Failed to serialize to JSON");
399 assert!(json.contains("\"id\":\"rule-1\""));
400 assert!(json.contains("\"scope\":\"Team\""));
401 }
402
403 #[test]
404 fn test_shared_rule_deserialization_from_json() {
405 let json = r#"{"id":"rule-1","name":"Test Rule","description":"A test rule","scope":"Team","enforced":true,"promoted_by":"admin-1","promoted_at":"2024-01-01T00:00:00Z","version":1}"#;
406 let rule: SharedRule = serde_json::from_str(json).expect("Failed to deserialize from JSON");
407 assert_eq!(rule.id, "rule-1");
408 assert_eq!(rule.scope, RuleScope::Team);
409 assert_eq!(rule.version, 1);
410 }
411
412 #[test]
413 fn test_rule_scope_as_str() {
414 assert_eq!(RuleScope::Project.as_str(), "project");
415 assert_eq!(RuleScope::Team.as_str(), "team");
416 assert_eq!(RuleScope::Organization.as_str(), "organization");
417 }
418
419 #[test]
420 fn test_rule_scope_from_str() {
421 use std::str::FromStr;
422 assert_eq!(RuleScope::from_str("project"), Ok(RuleScope::Project));
423 assert_eq!(RuleScope::from_str("team"), Ok(RuleScope::Team));
424 assert_eq!(
425 RuleScope::from_str("organization"),
426 Ok(RuleScope::Organization)
427 );
428 assert!(RuleScope::from_str("invalid").is_err());
429 }
430
431 #[test]
432 fn test_adoption_metrics_serialization() {
433 let metrics = AdoptionMetrics {
434 rule_id: "rule-1".to_string(),
435 total_members: 10,
436 adopting_members: 8,
437 adoption_percentage: 80.0,
438 adoption_trend: vec![(Utc::now(), 75.0), (Utc::now(), 80.0)],
439 };
440 let json = serde_json::to_string(&metrics).expect("Failed to serialize to JSON");
441 assert!(json.contains("\"rule_id\":\"rule-1\""));
442 assert!(json.contains("\"total_members\":10"));
443 assert!(json.contains("\"adopting_members\":8"));
444 }
445
446 #[test]
447 fn test_effectiveness_metrics_serialization() {
448 let metrics = EffectivenessMetrics {
449 rule_id: "rule-1".to_string(),
450 positive_outcomes: 15,
451 negative_outcomes: 2,
452 effectiveness_score: 0.88,
453 impact_trend: vec![(Utc::now(), 0.85), (Utc::now(), 0.88)],
454 };
455 let json = serde_json::to_string(&metrics).expect("Failed to serialize to JSON");
456 assert!(json.contains("\"rule_id\":\"rule-1\""));
457 assert!(json.contains("\"positive_outcomes\":15"));
458 }
459
460 #[test]
461 fn test_audit_log_entry_serialization() {
462 let entry = AuditLogEntry {
463 id: "log-1".to_string(),
464 team_id: "team-1".to_string(),
465 user_id: "user-1".to_string(),
466 action: "create_rule".to_string(),
467 resource: "rule-1".to_string(),
468 result: "success".to_string(),
469 timestamp: Utc::now(),
470 };
471 let json = serde_json::to_string(&entry).expect("Failed to serialize to JSON");
472 assert!(json.contains("\"id\":\"log-1\""));
473 assert!(json.contains("\"action\":\"create_rule\""));
474 }
475
476 #[test]
477 fn test_team_analytics_report_serialization() {
478 let report = TeamAnalyticsReport {
479 team_id: "team-1".to_string(),
480 total_members: 10,
481 adoption_metrics: vec![],
482 effectiveness_metrics: vec![],
483 generated_at: Utc::now(),
484 };
485 let json = serde_json::to_string(&report).expect("Failed to serialize to JSON");
486 assert!(json.contains("\"team_id\":\"team-1\""));
487 assert!(json.contains("\"total_members\":10"));
488 }
489
490 #[test]
491 fn test_round_trip_team_json() {
492 let original = create_test_team();
493 let json = serde_json::to_string(&original).expect("Failed to serialize");
494 let deserialized: Team = serde_json::from_str(&json).expect("Failed to deserialize");
495 assert_eq!(original.id, deserialized.id);
496 assert_eq!(original.name, deserialized.name);
497 assert_eq!(original.organization_id, deserialized.organization_id);
498 }
499
500 #[test]
501 fn test_round_trip_team_yaml() {
502 let original = create_test_team();
503 let yaml = serde_yaml::to_string(&original).expect("Failed to serialize");
504 let deserialized: Team = serde_yaml::from_str(&yaml).expect("Failed to deserialize");
505 assert_eq!(original.id, deserialized.id);
506 assert_eq!(original.name, deserialized.name);
507 assert_eq!(original.organization_id, deserialized.organization_id);
508 }
509
510 #[test]
511 fn test_round_trip_standards_json() {
512 let original = create_test_standards();
513 let json = serde_json::to_string(&original).expect("Failed to serialize");
514 let deserialized: TeamStandards =
515 serde_json::from_str(&json).expect("Failed to deserialize");
516 assert_eq!(original.id, deserialized.id);
517 assert_eq!(original.team_id, deserialized.team_id);
518 assert_eq!(original.version, deserialized.version);
519 assert_eq!(
520 original.code_review_rules.len(),
521 deserialized.code_review_rules.len()
522 );
523 }
524
525 #[test]
526 fn test_round_trip_standards_yaml() {
527 let original = create_test_standards();
528 let yaml = serde_yaml::to_string(&original).expect("Failed to serialize");
529 let deserialized: TeamStandards =
530 serde_yaml::from_str(&yaml).expect("Failed to deserialize");
531 assert_eq!(original.id, deserialized.id);
532 assert_eq!(original.team_id, deserialized.team_id);
533 assert_eq!(original.version, deserialized.version);
534 }
535
536 #[test]
537 fn test_team_member_with_different_roles() {
538 let admin = TeamMember {
539 id: "admin-1".to_string(),
540 name: "Admin User".to_string(),
541 email: "admin@example.com".to_string(),
542 role: TeamRole::Admin,
543 joined_at: Utc::now(),
544 };
545
546 let member = TeamMember {
547 id: "member-1".to_string(),
548 name: "Regular Member".to_string(),
549 email: "member@example.com".to_string(),
550 role: TeamRole::Member,
551 joined_at: Utc::now(),
552 };
553
554 let viewer = TeamMember {
555 id: "viewer-1".to_string(),
556 name: "Viewer User".to_string(),
557 email: "viewer@example.com".to_string(),
558 role: TeamRole::Viewer,
559 joined_at: Utc::now(),
560 };
561
562 assert_eq!(admin.role, TeamRole::Admin);
563 assert_eq!(member.role, TeamRole::Member);
564 assert_eq!(viewer.role, TeamRole::Viewer);
565 }
566
567 #[test]
568 fn test_standards_override_serialization() {
569 let override_data = StandardsOverride {
570 project_id: "project-1".to_string(),
571 overridden_standards: vec!["rule-1".to_string(), "rule-2".to_string()],
572 created_at: Utc::now(),
573 };
574 let json = serde_json::to_string(&override_data).expect("Failed to serialize to JSON");
575 assert!(json.contains("\"project_id\":\"project-1\""));
576 assert!(json.contains("\"overridden_standards\""));
577 }
578
579 #[test]
580 fn test_merged_standards_serialization() {
581 let merged = MergedStandards {
582 organization_standards: Some(create_test_standards()),
583 team_standards: Some(create_test_standards()),
584 project_standards: None,
585 final_standards: create_test_standards(),
586 };
587 let json = serde_json::to_string(&merged).expect("Failed to serialize to JSON");
588 assert!(json.contains("\"organization_standards\""));
589 assert!(json.contains("\"team_standards\""));
590 assert!(json.contains("\"final_standards\""));
591 }
592}