1use super::{
7 BackupType, LegacySystemAnalysis, MigrationConfig, MigrationError, MigrationOperation,
8 MigrationPhase, MigrationPlan, MigrationStrategy, RiskLevel, RollbackPhase, RollbackPlan,
9 UserMigration, ValidationStep, ValidationType,
10};
11use std::collections::HashMap;
12use uuid::Uuid;
13
14pub async fn generate_migration_plan(
16 analysis: &LegacySystemAnalysis,
17 strategy: Option<MigrationStrategy>,
18 _config: &MigrationConfig,
19) -> Result<MigrationPlan, MigrationError> {
20 let selected_strategy = strategy.unwrap_or_else(|| analysis.recommended_strategy.clone());
21
22 let plan_id = Uuid::new_v4().to_string();
23
24 let mut plan = MigrationPlan {
25 id: plan_id,
26 source_analysis: analysis.clone(),
27 strategy: selected_strategy.clone(),
28 phases: Vec::new(),
29 role_mappings: HashMap::new(),
30 permission_mappings: HashMap::new(),
31 user_migrations: Vec::new(),
32 pre_validation_steps: Vec::new(),
33 post_validation_steps: Vec::new(),
34 rollback_plan: RollbackPlan {
35 phases: Vec::new(),
36 backup_locations: Vec::new(),
37 recovery_time_objective: chrono::Duration::hours(4),
38 manual_steps: Vec::new(),
39 },
40 estimated_duration: chrono::Duration::hours(1),
41 risk_level: assess_risk_level(analysis),
42 downtime_required: None,
43 };
44
45 match selected_strategy {
46 MigrationStrategy::DirectMapping => {
47 generate_direct_mapping_plan(&mut plan, analysis).await?;
48 }
49 MigrationStrategy::GradualMigration => {
50 generate_gradual_migration_plan(&mut plan, analysis).await?;
51 }
52 MigrationStrategy::Rebuild => {
53 generate_rebuild_plan(&mut plan, analysis).await?;
54 }
55 MigrationStrategy::Custom(ref description) => {
56 generate_custom_plan(&mut plan, analysis, description).await?;
57 }
58 }
59
60 generate_validation_steps(&mut plan, analysis);
62
63 generate_rollback_plan(&mut plan, analysis);
65
66 estimate_migration_duration(&mut plan);
68
69 Ok(plan)
70}
71
72async fn generate_direct_mapping_plan(
74 plan: &mut MigrationPlan,
75 analysis: &LegacySystemAnalysis,
76) -> Result<(), MigrationError> {
77 let backup_phase = MigrationPhase {
79 id: "backup".to_string(),
80 name: "Backup and Preparation".to_string(),
81 description: "Create backups and prepare for migration".to_string(),
82 order: 1,
83 operations: vec![
84 MigrationOperation::Backup {
85 backup_location: std::path::PathBuf::from("./backups/pre_migration"),
86 backup_type: BackupType::Full,
87 },
88 MigrationOperation::ValidateIntegrity {
89 validation_type: "pre_migration_check".to_string(),
90 parameters: HashMap::new(),
91 },
92 ],
93 dependencies: Vec::new(),
94 estimated_duration: chrono::Duration::minutes(30),
95 rollback_operations: Vec::new(),
96 };
97
98 let mut role_operations = Vec::new();
100 for role in &analysis.roles {
101 let role_id = format!("migrated_{}", role.id);
103 plan.role_mappings.insert(role.id.clone(), role_id.clone());
104
105 role_operations.push(MigrationOperation::CreateRole {
106 role_id: role_id.clone(),
107 name: role.name.clone(),
108 description: role.description.clone(),
109 permissions: role.permissions.clone(),
110 parent_role: role.parent_roles.first().map(|p| format!("migrated_{}", p)),
111 });
112 }
113
114 let roles_phase = MigrationPhase {
115 id: "create_roles".to_string(),
116 name: "Create Roles".to_string(),
117 description: "Create all roles in the new system".to_string(),
118 order: 2,
119 operations: role_operations,
120 dependencies: vec!["backup".to_string()],
121 estimated_duration: chrono::Duration::minutes(analysis.role_count as i64 * 2),
122 rollback_operations: Vec::new(),
123 };
124
125 let mut permission_operations = Vec::new();
127 for permission in &analysis.permissions {
128 let permission_id = format!("migrated_{}", permission.id);
129 plan.permission_mappings
130 .insert(permission.id.clone(), permission_id.clone());
131
132 permission_operations.push(MigrationOperation::CreatePermission {
133 permission_id: permission_id.clone(),
134 action: permission.action.clone(),
135 resource: permission.resource.clone(),
136 conditions: permission.conditions.clone(),
137 });
138 }
139
140 let permissions_phase = MigrationPhase {
141 id: "create_permissions".to_string(),
142 name: "Create Permissions".to_string(),
143 description: "Create all permissions in the new system".to_string(),
144 order: 3,
145 operations: permission_operations,
146 dependencies: vec!["create_roles".to_string()],
147 estimated_duration: chrono::Duration::minutes(analysis.permission_count as i64),
148 rollback_operations: Vec::new(),
149 };
150
151 let mut user_operations = Vec::new();
153 for assignment in &analysis.user_assignments {
154 if let Some(role_id) = &assignment.role_id
155 && let Some(new_role_id) = plan.role_mappings.get(role_id)
156 {
157 user_operations.push(MigrationOperation::AssignUserRole {
158 user_id: assignment.user_id.clone(),
159 role_id: new_role_id.clone(),
160 expiration: assignment.expiration,
161 });
162
163 plan.user_migrations.push(UserMigration {
164 user_id: assignment.user_id.clone(),
165 legacy_roles: vec![role_id.clone()],
166 legacy_permissions: assignment.permissions.clone(),
167 new_roles: vec![new_role_id.clone()],
168 migration_notes: Some("Direct mapping migration".to_string()),
169 });
170 }
171 }
172
173 let users_phase = MigrationPhase {
174 id: "migrate_users".to_string(),
175 name: "Migrate User Assignments".to_string(),
176 description: "Migrate all user role assignments".to_string(),
177 order: 4,
178 operations: user_operations,
179 dependencies: vec!["create_permissions".to_string()],
180 estimated_duration: chrono::Duration::minutes(analysis.user_assignment_count as i64),
181 rollback_operations: Vec::new(),
182 };
183
184 let validation_phase = MigrationPhase {
186 id: "final_validation".to_string(),
187 name: "Final Validation".to_string(),
188 description: "Validate migration completeness and integrity".to_string(),
189 order: 5,
190 operations: vec![MigrationOperation::ValidateIntegrity {
191 validation_type: "post_migration_check".to_string(),
192 parameters: HashMap::new(),
193 }],
194 dependencies: vec!["migrate_users".to_string()],
195 estimated_duration: chrono::Duration::minutes(15),
196 rollback_operations: Vec::new(),
197 };
198
199 plan.phases = vec![
200 backup_phase,
201 roles_phase,
202 permissions_phase,
203 users_phase,
204 validation_phase,
205 ];
206 plan.downtime_required = Some(chrono::Duration::minutes(10));
207
208 Ok(())
209}
210
211async fn generate_gradual_migration_plan(
213 plan: &mut MigrationPlan,
214 analysis: &LegacySystemAnalysis,
215) -> Result<(), MigrationError> {
216 let setup_phase = MigrationPhase {
218 id: "setup_parallel".to_string(),
219 name: "Setup Parallel Systems".to_string(),
220 description: "Setup new role system alongside existing system".to_string(),
221 order: 1,
222 operations: vec![MigrationOperation::Backup {
223 backup_location: std::path::PathBuf::from("./backups/pre_gradual_migration"),
224 backup_type: BackupType::Full,
225 }],
226 dependencies: Vec::new(),
227 estimated_duration: chrono::Duration::hours(1),
228 rollback_operations: Vec::new(),
229 };
230
231 let critical_roles = identify_critical_roles(analysis);
233 let mut critical_role_operations = Vec::new();
234
235 for role_id in &critical_roles {
236 if let Some(role) = analysis.roles.iter().find(|r| r.id == *role_id) {
237 let new_role_id = format!("migrated_{}", role.id);
238 plan.role_mappings
239 .insert(role.id.clone(), new_role_id.clone());
240
241 critical_role_operations.push(MigrationOperation::CreateRole {
242 role_id: new_role_id,
243 name: role.name.clone(),
244 description: role.description.clone(),
245 permissions: role.permissions.clone(),
246 parent_role: None, });
248 }
249 }
250
251 let critical_phase = MigrationPhase {
252 id: "migrate_critical_roles".to_string(),
253 name: "Migrate Critical Roles".to_string(),
254 description: "Migrate business-critical roles first".to_string(),
255 order: 2,
256 operations: critical_role_operations,
257 dependencies: vec!["setup_parallel".to_string()],
258 estimated_duration: chrono::Duration::minutes(critical_roles.len() as i64 * 5),
259 rollback_operations: Vec::new(),
260 };
261
262 let remaining_roles: Vec<_> = analysis
264 .roles
265 .iter()
266 .filter(|role| !critical_roles.contains(&role.id))
267 .collect();
268
269 let batch_size = 10;
270 let mut batch_phases = Vec::new();
271
272 for (batch_idx, batch) in remaining_roles.chunks(batch_size).enumerate() {
273 let mut batch_operations = Vec::new();
274
275 for role in batch {
276 let new_role_id = format!("migrated_{}", role.id);
277 plan.role_mappings
278 .insert(role.id.clone(), new_role_id.clone());
279
280 batch_operations.push(MigrationOperation::CreateRole {
281 role_id: new_role_id,
282 name: role.name.clone(),
283 description: role.description.clone(),
284 permissions: role.permissions.clone(),
285 parent_role: role.parent_roles.first().map(|p| format!("migrated_{}", p)),
286 });
287 }
288
289 let phase = MigrationPhase {
290 id: format!("migrate_batch_{}", batch_idx + 1),
291 name: format!("Migrate Role Batch {}", batch_idx + 1),
292 description: format!(
293 "Migrate roles in batch {} of {}",
294 batch_idx + 1,
295 remaining_roles.len().div_ceil(batch_size)
296 ),
297 order: 3 + batch_idx as u32,
298 operations: batch_operations,
299 dependencies: if batch_idx == 0 {
300 vec!["migrate_critical_roles".to_string()]
301 } else {
302 vec![format!("migrate_batch_{}", batch_idx)]
303 },
304 estimated_duration: chrono::Duration::minutes(batch.len() as i64 * 3),
305 rollback_operations: Vec::new(),
306 };
307
308 batch_phases.push(phase);
309 }
310
311 let user_batches: Vec<_> = analysis.user_assignments.chunks(50).collect();
313 let mut user_phases = Vec::new();
314
315 for (batch_idx, batch) in user_batches.iter().enumerate() {
316 let mut user_operations = Vec::new();
317
318 for assignment in *batch {
319 if let Some(role_id) = &assignment.role_id
320 && let Some(new_role_id) = plan.role_mappings.get(role_id)
321 {
322 user_operations.push(MigrationOperation::AssignUserRole {
323 user_id: assignment.user_id.clone(),
324 role_id: new_role_id.clone(),
325 expiration: assignment.expiration,
326 });
327 }
328 }
329
330 let phase = MigrationPhase {
331 id: format!("migrate_users_batch_{}", batch_idx + 1),
332 name: format!("Migrate User Batch {}", batch_idx + 1),
333 description: format!("Migrate user assignments in batch {}", batch_idx + 1),
334 order: 100 + batch_idx as u32,
335 operations: user_operations,
336 dependencies: vec![format!("migrate_batch_{}", batch_phases.len())],
337 estimated_duration: chrono::Duration::minutes(batch.len() as i64 * 2),
338 rollback_operations: Vec::new(),
339 };
340
341 user_phases.push(phase);
342 }
343
344 plan.phases = vec![setup_phase, critical_phase];
346 plan.phases.extend(batch_phases);
347 plan.phases.extend(user_phases);
348
349 plan.downtime_required = None;
351
352 Ok(())
353}
354
355async fn generate_rebuild_plan(
357 plan: &mut MigrationPlan,
358 analysis: &LegacySystemAnalysis,
359) -> Result<(), MigrationError> {
360 let analysis_phase = MigrationPhase {
362 id: "analyze_and_design".to_string(),
363 name: "Analyze and Design New Structure".to_string(),
364 description: "Analyze existing system and design optimized role structure".to_string(),
365 order: 1,
366 operations: vec![MigrationOperation::Backup {
367 backup_location: std::path::PathBuf::from("./backups/pre_rebuild"),
368 backup_type: BackupType::Full,
369 }],
370 dependencies: Vec::new(),
371 estimated_duration: chrono::Duration::hours(2),
372 rollback_operations: Vec::new(),
373 };
374
375 let consolidated_roles = consolidate_roles(analysis);
377 let mut role_operations = Vec::new();
378
379 for (new_role_id, role_data) in &consolidated_roles {
380 plan.role_mappings.extend(role_data.legacy_mappings.clone());
381
382 role_operations.push(MigrationOperation::CreateRole {
383 role_id: new_role_id.clone(),
384 name: role_data.name.clone(),
385 description: role_data.description.clone(),
386 permissions: role_data.permissions.clone(),
387 parent_role: role_data.parent_role.clone(),
388 });
389 }
390
391 let roles_phase = MigrationPhase {
392 id: "create_consolidated_roles".to_string(),
393 name: "Create Consolidated Roles".to_string(),
394 description: "Create optimized, consolidated role structure".to_string(),
395 order: 2,
396 operations: role_operations,
397 dependencies: vec!["analyze_and_design".to_string()],
398 estimated_duration: chrono::Duration::minutes(consolidated_roles.len() as i64 * 3),
399 rollback_operations: Vec::new(),
400 };
401
402 let mut user_operations = Vec::new();
404 for assignment in &analysis.user_assignments {
405 if let Some(role_id) = &assignment.role_id
406 && let Some(new_role_id) = plan.role_mappings.get(role_id)
407 {
408 user_operations.push(MigrationOperation::AssignUserRole {
409 user_id: assignment.user_id.clone(),
410 role_id: new_role_id.clone(),
411 expiration: assignment.expiration,
412 });
413
414 plan.user_migrations.push(UserMigration {
415 user_id: assignment.user_id.clone(),
416 legacy_roles: vec![role_id.clone()],
417 legacy_permissions: assignment.permissions.clone(),
418 new_roles: vec![new_role_id.clone()],
419 migration_notes: Some("Rebuilt with consolidated roles".to_string()),
420 });
421 }
422 }
423
424 let users_phase = MigrationPhase {
425 id: "migrate_to_new_structure".to_string(),
426 name: "Migrate to New Structure".to_string(),
427 description: "Migrate users to the new consolidated role structure".to_string(),
428 order: 3,
429 operations: user_operations,
430 dependencies: vec!["create_consolidated_roles".to_string()],
431 estimated_duration: chrono::Duration::minutes(analysis.user_assignment_count as i64),
432 rollback_operations: Vec::new(),
433 };
434
435 plan.phases = vec![analysis_phase, roles_phase, users_phase];
436 plan.downtime_required = Some(chrono::Duration::hours(1));
437
438 Ok(())
439}
440
441async fn generate_custom_plan(
443 plan: &mut MigrationPlan,
444 _analysis: &LegacySystemAnalysis,
445 _description: &str,
446) -> Result<(), MigrationError> {
447 let template_phase = MigrationPhase {
449 id: "custom_migration".to_string(),
450 name: "Custom Migration Template".to_string(),
451 description: "Template for custom migration - requires manual customization".to_string(),
452 order: 1,
453 operations: vec![MigrationOperation::Backup {
454 backup_location: std::path::PathBuf::from("./backups/pre_custom_migration"),
455 backup_type: BackupType::Full,
456 }],
457 dependencies: Vec::new(),
458 estimated_duration: chrono::Duration::hours(4),
459 rollback_operations: Vec::new(),
460 };
461
462 plan.phases = vec![template_phase];
463 plan.downtime_required = Some(chrono::Duration::hours(2));
464
465 plan.user_migrations.push(UserMigration {
467 user_id: "TEMPLATE".to_string(),
468 legacy_roles: vec!["REQUIRES_MANUAL_MAPPING".to_string()],
469 legacy_permissions: vec!["REQUIRES_MANUAL_MAPPING".to_string()],
470 new_roles: vec!["REQUIRES_MANUAL_MAPPING".to_string()],
471 migration_notes: Some(
472 "Custom migration plan requires manual customization based on specific requirements"
473 .to_string(),
474 ),
475 });
476
477 Ok(())
478}
479
480fn identify_critical_roles(analysis: &LegacySystemAnalysis) -> Vec<String> {
482 let mut critical_roles = Vec::new();
483
484 for role in &analysis.roles {
486 if role.permissions.len() > 5 {
487 critical_roles.push(role.id.clone());
488 }
489 }
490
491 let parent_roles: std::collections::HashSet<_> = analysis
493 .roles
494 .iter()
495 .flat_map(|role| &role.parent_roles)
496 .collect();
497
498 for parent_role in parent_roles {
499 if !critical_roles.contains(parent_role) {
500 critical_roles.push(parent_role.clone());
501 }
502 }
503
504 if critical_roles.is_empty() {
506 critical_roles.extend(analysis.roles.iter().take(3).map(|role| role.id.clone()));
507 }
508
509 critical_roles
510}
511
512#[derive(Debug, Clone)]
514struct ConsolidatedRole {
515 name: String,
516 description: Option<String>,
517 permissions: Vec<String>,
518 parent_role: Option<String>,
519 legacy_mappings: HashMap<String, String>,
520}
521
522fn consolidate_roles(analysis: &LegacySystemAnalysis) -> HashMap<String, ConsolidatedRole> {
524 let mut consolidated = HashMap::new();
525
526 let mut permission_groups: HashMap<Vec<String>, Vec<&super::LegacyRole>> = HashMap::new();
528
529 for role in &analysis.roles {
530 let mut permissions = role.permissions.clone();
531 permissions.sort();
532
533 permission_groups.entry(permissions).or_default().push(role);
534 }
535
536 for (permissions, roles) in permission_groups {
538 if roles.len() == 1 {
539 let role = roles[0];
541 let new_id = format!("consolidated_{}", role.id);
542 let mut mappings = HashMap::new();
543 mappings.insert(role.id.clone(), new_id.clone());
544
545 consolidated.insert(
546 new_id.clone(),
547 ConsolidatedRole {
548 name: role.name.clone(),
549 description: role.description.clone(),
550 permissions: role.permissions.clone(),
551 parent_role: role.parent_roles.first().cloned(),
552 legacy_mappings: mappings,
553 },
554 );
555 } else {
556 let consolidated_name = format!("Consolidated_{}", roles[0].name);
558 let new_id = format!("consolidated_group_{}", roles[0].id);
559 let mut mappings = HashMap::new();
560
561 for role in &roles {
562 mappings.insert(role.id.clone(), new_id.clone());
563 }
564
565 consolidated.insert(
566 new_id.clone(),
567 ConsolidatedRole {
568 name: consolidated_name,
569 description: Some(format!(
570 "Consolidated from: {}",
571 roles
572 .iter()
573 .map(|r| r.name.as_str())
574 .collect::<Vec<_>>()
575 .join(", ")
576 )),
577 permissions,
578 parent_role: None, legacy_mappings: mappings,
580 },
581 );
582 }
583 }
584
585 consolidated
586}
587
588fn generate_validation_steps(plan: &mut MigrationPlan, analysis: &LegacySystemAnalysis) {
590 plan.pre_validation_steps = vec![
592 ValidationStep {
593 id: "backup_validation".to_string(),
594 name: "Backup Validation".to_string(),
595 description: "Verify backup integrity and completeness".to_string(),
596 validation_type: ValidationType::Custom("backup_check".to_string()),
597 parameters: HashMap::new(),
598 required: true,
599 },
600 ValidationStep {
601 id: "system_health_check".to_string(),
602 name: "System Health Check".to_string(),
603 description: "Verify system is ready for migration".to_string(),
604 validation_type: ValidationType::Custom("health_check".to_string()),
605 parameters: HashMap::new(),
606 required: true,
607 },
608 ];
609
610 plan.post_validation_steps = vec![
612 ValidationStep {
613 id: "role_hierarchy_validation".to_string(),
614 name: "Role Hierarchy Validation".to_string(),
615 description: "Verify role hierarchy integrity".to_string(),
616 validation_type: ValidationType::HierarchyIntegrity,
617 parameters: HashMap::new(),
618 required: true,
619 },
620 ValidationStep {
621 id: "permission_consistency_validation".to_string(),
622 name: "Permission Consistency Validation".to_string(),
623 description: "Verify permission assignments are consistent".to_string(),
624 validation_type: ValidationType::PermissionConsistency,
625 parameters: HashMap::new(),
626 required: true,
627 },
628 ValidationStep {
629 id: "user_assignment_validation".to_string(),
630 name: "User Assignment Validation".to_string(),
631 description: "Verify all user assignments migrated correctly".to_string(),
632 validation_type: ValidationType::UserAssignmentValidity,
633 parameters: HashMap::new(),
634 required: true,
635 },
636 ValidationStep {
637 id: "privilege_escalation_check".to_string(),
638 name: "Privilege Escalation Check".to_string(),
639 description: "Verify no unintended privilege escalation occurred".to_string(),
640 validation_type: ValidationType::PrivilegeEscalationCheck,
641 parameters: HashMap::new(),
642 required: true,
643 },
644 ];
645
646 if !analysis.circular_dependencies.is_empty() {
648 plan.post_validation_steps.push(ValidationStep {
649 id: "circular_dependency_check".to_string(),
650 name: "Circular Dependency Check".to_string(),
651 description: "Verify circular dependencies were resolved".to_string(),
652 validation_type: ValidationType::Custom("circular_check".to_string()),
653 parameters: HashMap::new(),
654 required: true,
655 });
656 }
657}
658
659fn generate_rollback_plan(plan: &mut MigrationPlan, _analysis: &LegacySystemAnalysis) {
661 plan.rollback_plan = RollbackPlan {
662 phases: vec![
663 RollbackPhase {
664 id: "stop_migration".to_string(),
665 name: "Stop Migration Process".to_string(),
666 operations: vec![MigrationOperation::ValidateIntegrity {
667 validation_type: "stop_migration".to_string(),
668 parameters: HashMap::new(),
669 }],
670 order: 1,
671 },
672 RollbackPhase {
673 id: "restore_backup".to_string(),
674 name: "Restore from Backup".to_string(),
675 operations: vec![MigrationOperation::Backup {
676 backup_location: std::path::PathBuf::from("./restore"),
677 backup_type: BackupType::Full,
678 }],
679 order: 2,
680 },
681 ],
682 backup_locations: vec![
683 std::path::PathBuf::from("./backups/pre_migration"),
684 std::path::PathBuf::from("./backups/incremental"),
685 ],
686 recovery_time_objective: chrono::Duration::hours(2),
687 manual_steps: vec![
688 "Verify system state after rollback".to_string(),
689 "Check user access and permissions".to_string(),
690 "Validate application functionality".to_string(),
691 ],
692 };
693}
694
695fn assess_risk_level(analysis: &LegacySystemAnalysis) -> RiskLevel {
697 match analysis.complexity_score {
698 1..=3 => RiskLevel::Low,
699 4..=6 => RiskLevel::Medium,
700 7..=8 => RiskLevel::High,
701 _ => RiskLevel::Critical,
702 }
703}
704
705fn estimate_migration_duration(plan: &mut MigrationPlan) {
707 let total_duration = plan
708 .phases
709 .iter()
710 .map(|phase| phase.estimated_duration)
711 .fold(chrono::Duration::zero(), |acc, duration| acc + duration);
712
713 plan.estimated_duration = total_duration + (total_duration / 5);
715}
716
717#[cfg(test)]
718mod tests {
719 use super::*;
720 use crate::migration::{LegacyRole, LegacySystemType, LegacyUserAssignment};
721
722 fn create_test_analysis() -> LegacySystemAnalysis {
723 LegacySystemAnalysis {
724 system_type: LegacySystemType::BasicRbac,
725 role_count: 5,
726 permission_count: 10,
727 user_assignment_count: 3,
728 roles: vec![
729 LegacyRole {
730 id: "admin".to_string(),
731 name: "Administrator".to_string(),
732 description: Some("Admin role".to_string()),
733 permissions: vec![
734 "read".to_string(),
735 "write".to_string(),
736 "delete".to_string(),
737 "admin".to_string(),
738 "manage_users".to_string(),
739 "manage_system".to_string(),
740 ],
741 parent_roles: vec![],
742 metadata: HashMap::new(),
743 },
744 LegacyRole {
745 id: "moderator".to_string(),
746 name: "Moderator".to_string(),
747 description: Some("Moderator role".to_string()),
748 permissions: vec![
749 "read".to_string(),
750 "write".to_string(),
751 "moderate".to_string(),
752 ],
753 parent_roles: vec![],
754 metadata: HashMap::new(),
755 },
756 LegacyRole {
757 id: "user".to_string(),
758 name: "User".to_string(),
759 description: Some("User role".to_string()),
760 permissions: vec!["read".to_string()],
761 parent_roles: vec![],
762 metadata: HashMap::new(),
763 },
764 LegacyRole {
765 id: "guest".to_string(),
766 name: "Guest".to_string(),
767 description: Some("Guest role".to_string()),
768 permissions: vec!["read_public".to_string()],
769 parent_roles: vec![],
770 metadata: HashMap::new(),
771 },
772 LegacyRole {
773 id: "support".to_string(),
774 name: "Support".to_string(),
775 description: Some("Support role".to_string()),
776 permissions: vec!["read".to_string(), "support_tickets".to_string()],
777 parent_roles: vec![],
778 metadata: HashMap::new(),
779 },
780 ],
781 permissions: vec![],
782 user_assignments: vec![
783 LegacyUserAssignment {
784 user_id: "user1".to_string(),
785 role_id: Some("admin".to_string()),
786 permissions: vec!["admin:read".to_string(), "admin:write".to_string()],
787 attributes: HashMap::new(),
788 expiration: None,
789 },
790 LegacyUserAssignment {
791 user_id: "user2".to_string(),
792 role_id: Some("user".to_string()),
793 permissions: vec!["user:read".to_string()],
794 attributes: HashMap::new(),
795 expiration: None,
796 },
797 LegacyUserAssignment {
798 user_id: "user3".to_string(),
799 role_id: Some("moderator".to_string()),
800 permissions: vec!["mod:moderate".to_string()],
801 attributes: HashMap::new(),
802 expiration: None,
803 },
804 ],
805 hierarchy_depth: 0,
806 duplicates_found: false,
807 orphaned_permissions: vec![],
808 circular_dependencies: vec![],
809 custom_attributes: std::collections::HashSet::new(),
810 complexity_score: 8,
811 recommended_strategy: MigrationStrategy::GradualMigration,
812 }
813 }
814
815 #[tokio::test]
816 async fn test_generate_direct_mapping_plan() {
817 let analysis = create_test_analysis();
818 let config = MigrationConfig::default();
819
820 let plan =
821 generate_migration_plan(&analysis, Some(MigrationStrategy::DirectMapping), &config)
822 .await
823 .unwrap();
824
825 assert_eq!(plan.strategy, MigrationStrategy::DirectMapping);
826 assert_eq!(plan.phases.len(), 5); assert_eq!(plan.role_mappings.len(), 5); }
829
830 #[tokio::test]
831 async fn test_generate_gradual_migration_plan() {
832 let analysis = create_test_analysis();
833 let config = MigrationConfig::default();
834
835 let plan = generate_migration_plan(
836 &analysis,
837 Some(MigrationStrategy::GradualMigration),
838 &config,
839 )
840 .await
841 .unwrap();
842
843 assert_eq!(plan.strategy, MigrationStrategy::GradualMigration);
844 assert!(plan.phases.len() >= 3); }
846
847 #[test]
848 fn test_assess_risk_level() {
849 let mut analysis = create_test_analysis();
850
851 analysis.complexity_score = 2;
852 assert_eq!(assess_risk_level(&analysis), RiskLevel::Low);
853
854 analysis.complexity_score = 5;
855 assert_eq!(assess_risk_level(&analysis), RiskLevel::Medium);
856
857 analysis.complexity_score = 8;
858 assert_eq!(assess_risk_level(&analysis), RiskLevel::High);
859
860 analysis.complexity_score = 10;
861 assert_eq!(assess_risk_level(&analysis), RiskLevel::Critical);
862 }
863
864 #[test]
865 fn test_identify_critical_roles() {
866 let analysis = create_test_analysis();
867 let critical_roles = identify_critical_roles(&analysis);
868
869 assert!(critical_roles.contains(&"admin".to_string()));
871 }
872}
873
874