scirs2_core/versioning/
deprecation.rs

1//! # Deprecation Management
2//!
3//! Comprehensive deprecation management system for API lifecycle
4//! management and transition planning in enterprise environments.
5
6use super::Version;
7use crate::error::CoreError;
8use std::collections::HashMap;
9
10#[cfg(feature = "serialization")]
11use serde::{Deserialize, Serialize};
12
13/// Deprecation policy configuration
14#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
15#[derive(Debug, Clone)]
16pub struct DeprecationPolicy {
17    /// Default deprecation period in days
18    pub default_deprecation_period: u32,
19    /// Grace period after end of life
20    pub grace_period: u32,
21    /// Deprecation notice period before end of life
22    pub notice_period: u32,
23    /// Automatic deprecation rules
24    pub auto_deprecation_rules: Vec<AutoDeprecationRule>,
25    /// Support level during deprecation
26    pub deprecation_support_level: SupportLevel,
27    /// Migration assistance provided
28    pub migration_assistance: bool,
29}
30
31impl Default for DeprecationPolicy {
32    fn default() -> Self {
33        Self {
34            default_deprecation_period: 365, // 1 year
35            grace_period: 90,                // 3 months
36            notice_period: 180,              // 6 months
37            auto_deprecation_rules: vec![
38                AutoDeprecationRule::MajorVersionSuperseded {
39                    versions_to_keep: 2,
40                },
41                AutoDeprecationRule::AgeBasedDeprecation { maxage_days: 1095 }, // 3 years
42            ],
43            deprecation_support_level: SupportLevel::SecurityOnly,
44            migration_assistance: true,
45        }
46    }
47}
48
49/// Support levels during deprecation
50#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum SupportLevel {
53    /// Full support continues
54    Full,
55    /// Maintenance only (bug fixes)
56    MaintenanceOnly,
57    /// Security updates only
58    SecurityOnly,
59    /// No support
60    None,
61}
62
63/// Automatic deprecation rules
64#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
65#[derive(Debug, Clone)]
66pub enum AutoDeprecationRule {
67    /// Deprecate when superseded by newer major versions
68    MajorVersionSuperseded { versions_to_keep: u32 },
69    /// Deprecate based on age
70    AgeBasedDeprecation { maxage_days: u32 },
71    /// Deprecate when usage falls below threshold
72    UsageBasedDeprecation { min_usage_percent: f64 },
73    /// Deprecate unstable versions after stable release
74    StableVersionReleased,
75}
76
77/// Deprecation status for a version
78#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
79#[derive(Debug, Clone)]
80pub struct DeprecationStatus {
81    /// Version being deprecated
82    pub version: Version,
83    /// Current deprecation phase
84    pub phase: DeprecationPhase,
85    /// Deprecation announcement date
86    pub announced_date: chrono::DateTime<chrono::Utc>,
87    /// Planned end of life date
88    pub end_of_life_date: chrono::DateTime<chrono::Utc>,
89    /// Actual end of life date (if reached)
90    pub actual_end_of_life: Option<chrono::DateTime<chrono::Utc>>,
91    /// Reason for deprecation
92    pub reason: DeprecationReason,
93    /// Recommended replacement version
94    pub replacement_version: Option<Version>,
95    /// Migration guide URL or content
96    pub migration_guide: Option<String>,
97    /// Support level during deprecation
98    pub support_level: SupportLevel,
99    /// Usage metrics at time of deprecation
100    pub usage_metrics: Option<UsageMetrics>,
101}
102
103/// Phases of deprecation lifecycle
104#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
105#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
106pub enum DeprecationPhase {
107    /// Actively supported
108    Active,
109    /// Deprecation announced but still supported
110    Announced,
111    /// In deprecation period
112    Deprecated,
113    /// End of life reached, no support
114    EndOfLife,
115    /// Completely removed
116    Removed,
117}
118
119impl DeprecationPhase {
120    /// Get the string representation
121    pub const fn as_str(&self) -> &'static str {
122        match self {
123            DeprecationPhase::Active => "active",
124            DeprecationPhase::Announced => "announced",
125            DeprecationPhase::Deprecated => "deprecated",
126            DeprecationPhase::EndOfLife => "end_of_life",
127            DeprecationPhase::Removed => "removed",
128        }
129    }
130
131    /// Check if version is still supported
132    pub fn is_supported(&self) -> bool {
133        matches!(
134            self,
135            DeprecationPhase::Active | DeprecationPhase::Announced | DeprecationPhase::Deprecated
136        )
137    }
138}
139
140/// Reasons for deprecation
141#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
142#[derive(Debug, Clone)]
143pub enum DeprecationReason {
144    /// Superseded by newer version
145    SupersededBy(Version),
146    /// Security vulnerabilities
147    SecurityConcerns,
148    /// Performance issues
149    PerformanceIssues,
150    /// Maintenance burden
151    MaintenanceBurden,
152    /// Low usage
153    LowUsage,
154    /// Technology obsolescence
155    TechnologyObsolescence,
156    /// Business decision
157    BusinessDecision(String),
158    /// End of vendor support
159    VendorEndOfSupport,
160}
161
162/// Usage metrics for deprecation decisions
163#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
164#[derive(Debug, Clone)]
165pub struct UsageMetrics {
166    /// Number of active users/clients
167    pub active_users: u64,
168    /// Percentage of total API usage
169    pub usage_percentage: f64,
170    /// Download/installation count
171    pub download_count: u64,
172    /// Last recorded usage
173    pub last_usage: chrono::DateTime<chrono::Utc>,
174    /// Usage trend (increasing/decreasing)
175    pub trend: UsageTrend,
176}
177
178/// Usage trend indicators
179#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
180#[derive(Debug, Clone, Copy, PartialEq, Eq)]
181pub enum UsageTrend {
182    /// Usage is increasing
183    Increasing,
184    /// Usage is stable
185    Stable,
186    /// Usage is decreasing
187    Decreasing,
188    /// Usage is rapidly declining
189    Declining,
190}
191
192/// Deprecation announcement
193#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
194#[derive(Debug, Clone)]
195pub struct DeprecationAnnouncement {
196    /// Version being deprecated
197    pub version: Version,
198    /// Announcement date
199    pub announcement_date: chrono::DateTime<chrono::Utc>,
200    /// Deprecation timeline
201    pub timeline: DeprecationTimeline,
202    /// Announcement message
203    pub message: String,
204    /// Migration instructions
205    pub migration_instructions: Option<String>,
206    /// Contact information for support
207    pub support_contact: Option<String>,
208    /// Communication channels used
209    pub communication_channels: Vec<CommunicationChannel>,
210}
211
212/// Deprecation timeline
213#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
214#[derive(Debug, Clone)]
215pub struct DeprecationTimeline {
216    /// When deprecation was announced
217    pub announced: chrono::DateTime<chrono::Utc>,
218    /// When version enters deprecated phase
219    pub deprecated_date: chrono::DateTime<chrono::Utc>,
220    /// When version reaches end of life
221    pub end_of_life: chrono::DateTime<chrono::Utc>,
222    /// When version will be removed
223    pub removal_date: Option<chrono::DateTime<chrono::Utc>>,
224    /// Milestone dates
225    pub milestones: Vec<DeprecationMilestone>,
226}
227
228/// Deprecation milestone
229#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
230#[derive(Debug, Clone)]
231pub struct DeprecationMilestone {
232    /// Milestone date
233    pub date: chrono::DateTime<chrono::Utc>,
234    /// Milestone description
235    pub description: String,
236    /// Actions to be taken
237    pub actions: Vec<String>,
238}
239
240/// Communication channels for deprecation announcements
241#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
242#[derive(Debug, Clone)]
243pub enum CommunicationChannel {
244    /// Email notification
245    Email,
246    /// Website announcement
247    Website,
248    /// API response headers
249    ApiHeaders,
250    /// Documentation update
251    Documentation,
252    /// Blog post
253    BlogPost,
254    /// Developer newsletter
255    Newsletter,
256    /// Social media
257    SocialMedia,
258    /// Direct notification
259    DirectNotification,
260}
261
262/// Helper function to create a deprecation timeline
263fn deprecation_timeline(
264    version: &Version,
265    _replacement_version: Option<&Version>,
266) -> DeprecationTimeline {
267    let now = chrono::Utc::now();
268    let deprecated_date = now + chrono::Duration::days(30);
269    let end_of_life = now + chrono::Duration::days(180);
270    let removal_date = now + chrono::Duration::days(365);
271
272    let milestones = vec![
273        DeprecationMilestone {
274            date: deprecated_date,
275            description: format!("Version {version} will be deprecated"),
276            actions: vec!["Update to newer version".to_string()],
277        },
278        DeprecationMilestone {
279            date: end_of_life,
280            description: format!("Version {version} reaches end of life"),
281            actions: vec!["Support will be discontinued".to_string()],
282        },
283    ];
284
285    DeprecationTimeline {
286        announced: now,
287        deprecated_date,
288        end_of_life,
289        removal_date: Some(removal_date),
290        milestones,
291    }
292}
293
294/// Deprecation manager implementation
295pub struct DeprecationManager {
296    /// Deprecation policy
297    policy: DeprecationPolicy,
298    /// Deprecation statuses by version
299    deprecations: HashMap<Version, DeprecationStatus>,
300    /// Deprecation announcements
301    announcements: Vec<DeprecationAnnouncement>,
302}
303
304impl DeprecationManager {
305    /// Create a new deprecation manager
306    pub fn new() -> Self {
307        Self {
308            policy: DeprecationPolicy::default(),
309            deprecations: HashMap::new(),
310            announcements: Vec::new(),
311        }
312    }
313
314    /// Create with custom policy
315    pub fn with_policy(policy: DeprecationPolicy) -> Self {
316        Self {
317            policy,
318            deprecations: HashMap::new(),
319            announcements: Vec::new(),
320        }
321    }
322
323    /// Register a version for deprecation management
324    pub fn register_version(&mut self, apiversion: &super::ApiVersion) -> Result<(), CoreError> {
325        // Create active status for new version
326        let status = DeprecationStatus {
327            version: apiversion.version.clone(),
328            phase: DeprecationPhase::Active,
329            announced_date: apiversion.release_date,
330            end_of_life_date: apiversion.end_of_life.unwrap_or_else(|| {
331                apiversion.release_date
332                    + chrono::Duration::days(self.policy.default_deprecation_period as i64)
333            }),
334            actual_end_of_life: None,
335            reason: DeprecationReason::BusinessDecision("Not deprecated".to_string()),
336            replacement_version: None,
337            migration_guide: None,
338            support_level: SupportLevel::Full,
339            usage_metrics: None,
340        };
341
342        self.deprecations.insert(apiversion.version.clone(), status);
343        Ok(())
344    }
345
346    /// Announce deprecation of a version
347    pub fn announce_deprecation(
348        &mut self,
349        version: &Version,
350        reason: DeprecationReason,
351        replacement_version: Option<Version>,
352    ) -> Result<DeprecationAnnouncement, CoreError> {
353        let status = self.deprecations.get_mut(version).ok_or_else(|| {
354            CoreError::ComputationError(crate::error::ErrorContext::new(format!(
355                "Version {version} not registered"
356            )))
357        })?;
358
359        let now = chrono::Utc::now();
360        let deprecated_date = now + chrono::Duration::days(self.policy.notice_period as i64);
361        let end_of_life =
362            deprecated_date + chrono::Duration::days(self.policy.default_deprecation_period as i64);
363
364        // Update status
365        status.phase = DeprecationPhase::Announced;
366        status.announced_date = now;
367        status.end_of_life_date = end_of_life;
368        status.reason = reason.clone();
369        status.replacement_version = replacement_version.clone();
370        status.support_level = self.policy.deprecation_support_level;
371
372        // Create timeline
373        let timeline = DeprecationTimeline {
374            announced: now,
375            deprecated_date,
376            end_of_life,
377            removal_date: Some(
378                end_of_life + chrono::Duration::days(self.policy.grace_period as i64),
379            ),
380            milestones: vec![
381                DeprecationMilestone {
382                    date: deprecated_date,
383                    description: "Version enters deprecated phase".to_string(),
384                    actions: vec!["Migration recommended".to_string()],
385                },
386                DeprecationMilestone {
387                    date: end_of_life - chrono::Duration::days(30),
388                    description: "Final warning - 30 days to end of life".to_string(),
389                    actions: vec!["Complete migration immediately".to_string()],
390                },
391            ],
392        };
393
394        // Create announcement
395        let announcement = DeprecationAnnouncement {
396            version: version.clone(),
397            announcement_date: now,
398            timeline,
399            message: self.generate_deprecation_message(
400                version,
401                &reason,
402                replacement_version.as_ref(),
403            ),
404            migration_instructions: self
405                .generate_migration_instructions(version, replacement_version.as_ref()),
406            support_contact: Some("support@scirs.dev".to_string()),
407            communication_channels: vec![
408                CommunicationChannel::Email,
409                CommunicationChannel::Website,
410                CommunicationChannel::ApiHeaders,
411                CommunicationChannel::Documentation,
412            ],
413        };
414
415        self.announcements.push(announcement.clone());
416        Ok(announcement)
417    }
418
419    /// Get deprecation status for a version
420    pub fn get_deprecation_status(&self, version: &Version) -> Option<DeprecationStatus> {
421        self.deprecations.get(version).cloned()
422    }
423
424    /// Update deprecation status
425    pub fn update_status(
426        &mut self,
427        version: &Version,
428        new_status: DeprecationStatus,
429    ) -> Result<(), CoreError> {
430        if !self.deprecations.contains_key(version) {
431            return Err(CoreError::ComputationError(
432                crate::error::ErrorContext::new(format!("Version {version} not registered")),
433            ));
434        }
435
436        self.deprecations.insert(version.clone(), new_status);
437        Ok(())
438    }
439
440    /// Perform maintenance tasks
441    pub fn perform_maintenance(&mut self) -> Result<Vec<MaintenanceAction>, CoreError> {
442        let mut actions = Vec::new();
443        let now = chrono::Utc::now();
444
445        // Check for automatic deprecations
446        for rule in &self.policy.auto_deprecation_rules.clone() {
447            actions.extend(self.apply_auto_deprecation_rule(rule, now)?);
448        }
449
450        // Update phases based on timeline
451        for (version, status) in &mut self.deprecations {
452            let old_phase = status.phase;
453
454            if status.phase == DeprecationPhase::Announced
455                && now
456                    >= status.end_of_life_date
457                        - chrono::Duration::days(self.policy.default_deprecation_period as i64)
458            {
459                status.phase = DeprecationPhase::Deprecated;
460                actions.push(MaintenanceAction::PhaseTransition {
461                    version: version.clone(),
462                    from_phase: old_phase,
463                    to_phase: status.phase,
464                });
465            }
466
467            if status.phase == DeprecationPhase::Deprecated && now >= status.end_of_life_date {
468                status.phase = DeprecationPhase::EndOfLife;
469                status.actual_end_of_life = Some(now);
470                actions.push(MaintenanceAction::PhaseTransition {
471                    version: version.clone(),
472                    from_phase: old_phase,
473                    to_phase: status.phase,
474                });
475            }
476        }
477
478        Ok(actions)
479    }
480
481    /// Apply automatic deprecation rule
482    fn apply_auto_deprecation_rule(
483        &mut self,
484        rule: &AutoDeprecationRule,
485        now: chrono::DateTime<chrono::Utc>,
486    ) -> Result<Vec<MaintenanceAction>, CoreError> {
487        let mut actions = Vec::new();
488
489        match rule {
490            AutoDeprecationRule::MajorVersionSuperseded { versions_to_keep } => {
491                actions.extend(self.apply_major_version_rule(*versions_to_keep)?);
492            }
493            AutoDeprecationRule::AgeBasedDeprecation { maxage_days } => {
494                actions.extend(self.apply_agebased_rule(*maxage_days, now)?);
495            }
496            AutoDeprecationRule::UsageBasedDeprecation { min_usage_percent } => {
497                actions.extend(self.apply_usagebased_rule(*min_usage_percent)?);
498            }
499            AutoDeprecationRule::StableVersionReleased => {
500                actions.extend(self.apply_stable_release_rule()?);
501            }
502        }
503
504        Ok(actions)
505    }
506
507    /// Apply major version superseded rule
508    fn apply_major_version_rule(
509        &self,
510        versions_to_keep: u32,
511    ) -> Result<Vec<MaintenanceAction>, CoreError> {
512        let mut actions = Vec::new();
513
514        // Group versions by major version and collect them to avoid borrowing issues
515        let mut majorversions: std::collections::BTreeMap<u64, Vec<Version>> =
516            std::collections::BTreeMap::new();
517        for version in self.deprecations.keys() {
518            majorversions
519                .entry(version.major())
520                .or_default()
521                .push(version.clone());
522        }
523
524        // Find majors to deprecate
525        let major_keys: Vec<u64> = majorversions.keys().cloned().collect();
526        if major_keys.len() > versions_to_keep as usize {
527            let to_deprecate = &major_keys[..major_keys.len() - versions_to_keep as usize];
528
529            for &major in to_deprecate {
530                if let Some(versions) = majorversions.get(&major) {
531                    for version in versions {
532                        if let Some(status) = self.deprecations.get(version) {
533                            if status.phase == DeprecationPhase::Active {
534                                let latest_major = major_keys.last().unwrap();
535                                let replacement = Version::new(*latest_major, 0, 0);
536
537                                let _announcement = DeprecationAnnouncement {
538                                    version: version.clone(),
539                                    announcement_date: chrono::Utc::now(),
540                                    timeline: deprecation_timeline(version, Some(&replacement)),
541                                    message: format!(
542                                        "Version {version} is deprecated in favor of {replacement}"
543                                    ),
544                                    migration_instructions: Some(format!(
545                                        "Please migrate to version {replacement}"
546                                    )),
547                                    support_contact: None,
548                                    communication_channels: vec![],
549                                };
550
551                                actions.push(MaintenanceAction::AutoDeprecation {
552                                    version: version.clone(),
553                                    rule: format!(
554                                        "Major version superseded (keep {versions_to_keep})"
555                                    ),
556                                });
557                            }
558                        }
559                    }
560                }
561            }
562        }
563
564        Ok(actions)
565    }
566
567    /// Apply age-based deprecation rule
568    fn apply_agebased_rule(
569        &self,
570        maxage_days: u32,
571        now: chrono::DateTime<chrono::Utc>,
572    ) -> Result<Vec<MaintenanceAction>, CoreError> {
573        let mut actions = Vec::new();
574        let maxage = chrono::Duration::days(maxage_days as i64);
575
576        for (version, status) in &self.deprecations.clone() {
577            if status.phase == DeprecationPhase::Active {
578                let age = now.signed_duration_since(status.announced_date);
579                if age > maxage {
580                    let _announcement = DeprecationAnnouncement {
581                        version: version.clone(),
582                        timeline: deprecation_timeline(version, None),
583                        announcement_date: now,
584                        message: format!("Version {version} deprecated due to maintenance burden"),
585                        migration_instructions: Some("Please upgrade to newer version".to_string()),
586                        support_contact: Some("support@scirs.org".to_string()),
587                        communication_channels: vec![
588                            CommunicationChannel::Documentation,
589                            CommunicationChannel::Email,
590                        ],
591                    };
592
593                    actions.push(MaintenanceAction::AutoDeprecation {
594                        version: version.clone(),
595                        rule: format!("Age-based deprecation (max {maxage_days} days)"),
596                    });
597                }
598            }
599        }
600
601        Ok(actions)
602    }
603
604    /// Apply usage-based deprecation rule
605    fn apply_usagebased_rule(
606        &self,
607        _min_usage_percent: f64,
608    ) -> Result<Vec<MaintenanceAction>, CoreError> {
609        // This would require actual usage metrics
610        // For now, return empty actions
611        Ok(Vec::new())
612    }
613
614    /// Apply stable version released rule
615    fn apply_stable_release_rule(&self) -> Result<Vec<MaintenanceAction>, CoreError> {
616        // This would deprecate pre-release versions when stable is available
617        // For now, return empty actions
618        Ok(Vec::new())
619    }
620
621    /// Generate deprecation message
622    fn generate_deprecation_message(
623        &self,
624        version: &Version,
625        reason: &DeprecationReason,
626        replacement: Option<&Version>,
627    ) -> String {
628        let reason_str = match reason {
629            DeprecationReason::SupersededBy(v) => format!("{v}"),
630            DeprecationReason::SecurityConcerns => "security concerns".to_string(),
631            DeprecationReason::PerformanceIssues => "performance issues".to_string(),
632            DeprecationReason::MaintenanceBurden => "maintenance burden".to_string(),
633            DeprecationReason::LowUsage => "low usage".to_string(),
634            DeprecationReason::TechnologyObsolescence => "technology obsolescence".to_string(),
635            DeprecationReason::BusinessDecision(msg) => msg.clone(),
636            DeprecationReason::VendorEndOfSupport => "vendor end of support".to_string(),
637        };
638
639        let mut message = format!("Version {version} has been deprecated due to {reason_str}. ");
640
641        if let Some(replacement) = replacement {
642            message.push_str(&format!(
643                "Please migrate to version {replacement} as soon as possible. "
644            ));
645        }
646
647        message.push_str(&format!(
648            "Support will end on {}. ",
649            self.deprecations
650                .get(version)
651                .map(|s| s.end_of_life_date.format("%Y-%m-%d").to_string())
652                .unwrap_or_else(|| "TBD".to_string())
653        ));
654
655        message
656    }
657
658    /// Generate migration instructions
659    fn generate_migration_instructions(
660        &self,
661        _current_version: &Version,
662        replacement: Option<&Version>,
663    ) -> Option<String> {
664        replacement.map(|replacement| {
665            format!(
666                "To migrate to version {replacement}:\n\
667                1. Update your dependency to version {replacement}\n\
668                2. Review the changelog for breaking changes\n\
669                3. Update your code as necessary\n\
670                4. Test thoroughly before deploying\n\
671                5. Contact support if you need assistance"
672            )
673        })
674    }
675
676    /// Get all deprecated versions
677    pub fn get_deprecatedversions(&self) -> Vec<&DeprecationStatus> {
678        self.deprecations
679            .values()
680            .filter(|status| status.phase != DeprecationPhase::Active)
681            .collect()
682    }
683
684    /// Get versions in specific phase
685    pub fn getversions_in_phase(&self, phase: DeprecationPhase) -> Vec<&DeprecationStatus> {
686        self.deprecations
687            .values()
688            .filter(|status| status.phase == phase)
689            .collect()
690    }
691}
692
693impl Default for DeprecationManager {
694    fn default() -> Self {
695        Self::new()
696    }
697}
698
699/// Maintenance actions performed by the deprecation manager
700#[derive(Debug, Clone)]
701pub enum MaintenanceAction {
702    /// Phase transition
703    PhaseTransition {
704        version: Version,
705        from_phase: DeprecationPhase,
706        to_phase: DeprecationPhase,
707    },
708    /// Automatic deprecation applied
709    AutoDeprecation { version: Version, rule: String },
710    /// Usage metrics updated
711    UsageMetricsUpdated { version: Version },
712}
713
714#[cfg(test)]
715mod tests {
716    use super::*;
717
718    #[test]
719    fn test_deprecation_manager_creation() {
720        let manager = DeprecationManager::new();
721        assert!(manager.deprecations.is_empty());
722        assert!(manager.announcements.is_empty());
723    }
724
725    #[test]
726    fn test_deprecation_phases() {
727        assert!(DeprecationPhase::Active < DeprecationPhase::Deprecated);
728        assert!(DeprecationPhase::Deprecated < DeprecationPhase::EndOfLife);
729        assert!(DeprecationPhase::Active.is_supported());
730        assert!(!DeprecationPhase::EndOfLife.is_supported());
731    }
732
733    #[test]
734    fn test_deprecation_announcement() {
735        let mut manager = DeprecationManager::new();
736        let version = Version::new(1, 0, 0);
737
738        // Register version first
739        let apiversion = super::super::ApiVersionBuilder::new(version.clone())
740            .build()
741            .unwrap();
742        manager.register_version(&apiversion).unwrap();
743
744        // Announce deprecation
745        let replacement = Version::new(2, 0, 0);
746        let announcement = manager
747            .announce_deprecation(
748                &version,
749                DeprecationReason::SupersededBy(replacement.clone()),
750                Some(replacement),
751            )
752            .unwrap();
753
754        assert_eq!(announcement.version, version);
755        assert!(!announcement.message.is_empty());
756        assert!(announcement.migration_instructions.is_some());
757
758        // Check status was updated
759        let status = manager.get_deprecation_status(&version).unwrap();
760        assert_eq!(status.phase, DeprecationPhase::Announced);
761    }
762
763    #[test]
764    fn test_deprecation_policy() {
765        let policy = DeprecationPolicy::default();
766        assert_eq!(policy.default_deprecation_period, 365);
767        assert_eq!(policy.grace_period, 90);
768        assert_eq!(policy.notice_period, 180);
769        assert!(policy.migration_assistance);
770    }
771
772    #[test]
773    fn test_auto_deprecation_rules() {
774        let mut manager = DeprecationManager::new();
775
776        // Register multiple major versions
777        for major in 1..=5 {
778            let version = Version::new(major, 0, 0);
779            let apiversion = super::super::ApiVersionBuilder::new(version)
780                .build()
781                .unwrap();
782            manager.register_version(&apiversion).unwrap();
783        }
784
785        // Apply major version rule
786        let rule = AutoDeprecationRule::MajorVersionSuperseded {
787            versions_to_keep: 2,
788        };
789        let actions = manager
790            .apply_auto_deprecation_rule(&rule, chrono::Utc::now())
791            .unwrap();
792
793        // Should have deprecated older versions
794        assert!(!actions.is_empty());
795    }
796
797    #[test]
798    fn test_usage_trends() {
799        let metrics = UsageMetrics {
800            active_users: 100,
801            usage_percentage: 5.0,
802            download_count: 1000,
803            last_usage: chrono::Utc::now(),
804            trend: UsageTrend::Decreasing,
805        };
806
807        assert_eq!(metrics.trend, UsageTrend::Decreasing);
808        assert_eq!(metrics.usage_percentage, 5.0);
809    }
810}