Skip to main content

changeset_version/
lib.rs

1use changeset_core::{BumpType, PrereleaseSpec, ZeroVersionBehavior};
2use semver::{Prerelease, Version};
3use thiserror::Error;
4
5#[derive(Debug, Error)]
6pub enum VersionError {
7    #[error("invalid prerelease identifier: {identifier}")]
8    InvalidPrerelease { identifier: String },
9    #[error("cannot graduate from prerelease version '{version}'; release stable 0.x first")]
10    CannotGraduateFromPrerelease { version: String },
11    #[error("can only graduate 0.x versions to 1.0.0; version is {version}")]
12    CanOnlyGraduateZeroVersions { version: String },
13}
14
15#[must_use]
16pub fn max_bump_type(bumps: &[BumpType]) -> Option<BumpType> {
17    bumps.iter().max().copied()
18}
19
20pub fn bump_version(version: &Version, bump_type: BumpType) -> Version {
21    let mut new_version = version.clone();
22
23    match bump_type {
24        BumpType::Major => {
25            new_version.major += 1;
26            new_version.minor = 0;
27            new_version.patch = 0;
28        }
29        BumpType::Minor => {
30            new_version.minor += 1;
31            new_version.patch = 0;
32        }
33        BumpType::Patch => {
34            new_version.patch += 1;
35        }
36    }
37
38    new_version.pre = Prerelease::EMPTY;
39    new_version
40}
41
42fn parse_prerelease(pre: &Prerelease) -> Option<(String, u64)> {
43    let pre_str = pre.as_str();
44    if pre_str.is_empty() {
45        return None;
46    }
47
48    let parts: Vec<&str> = pre_str.split('.').collect();
49    if parts.len() < 2 {
50        return Some((pre_str.to_string(), 1));
51    }
52
53    let last = parts.last()?;
54    if let Ok(num) = last.parse::<u64>() {
55        let tag = parts[..parts.len() - 1].join(".");
56        Some((tag, num))
57    } else {
58        Some((pre_str.to_string(), 1))
59    }
60}
61
62/// Calculates a new version based on bump type and optional prerelease spec.
63///
64/// # Errors
65///
66/// Returns `VersionError::InvalidPrerelease` if the prerelease identifier
67/// produces an invalid semver prerelease string.
68pub fn calculate_new_version(
69    current: &Version,
70    bump_type: Option<BumpType>,
71    prerelease: Option<&PrereleaseSpec>,
72) -> Result<Version, VersionError> {
73    let mut new_version = current.clone();
74
75    match prerelease {
76        Some(spec) => {
77            let tag = spec.identifier();
78
79            if current.pre.is_empty() {
80                let bump = bump_type.unwrap_or(BumpType::Patch);
81                new_version = bump_version(current, bump);
82                new_version.pre = make_prerelease(tag, 1)?;
83            } else if let Some((current_tag, current_num)) = parse_prerelease(&current.pre) {
84                if current_tag == tag {
85                    new_version.pre = make_prerelease(tag, current_num + 1)?;
86                } else {
87                    new_version.pre = make_prerelease(tag, 1)?;
88                }
89            } else {
90                new_version.pre = make_prerelease(tag, 1)?;
91            }
92        }
93        None => {
94            if !current.pre.is_empty() {
95                new_version.pre = Prerelease::EMPTY;
96            } else if let Some(bump) = bump_type {
97                new_version = bump_version(current, bump);
98            }
99        }
100    }
101
102    Ok(new_version)
103}
104
105fn make_prerelease(tag: &str, num: u64) -> Result<Prerelease, VersionError> {
106    let identifier = format!("{tag}.{num}");
107    Prerelease::new(&identifier).map_err(|_| VersionError::InvalidPrerelease { identifier })
108}
109
110#[must_use]
111pub fn is_prerelease(version: &Version) -> bool {
112    !version.pre.is_empty()
113}
114
115#[must_use]
116pub fn extract_prerelease_tag(version: &Version) -> Option<String> {
117    parse_prerelease(&version.pre).map(|(tag, _)| tag)
118}
119
120#[must_use]
121pub fn is_zero_version(version: &Version) -> bool {
122    version.major == 0
123}
124
125/// Calculates a new version with special handling for 0.x versions.
126///
127/// When `graduate` is true, the version will be promoted to 1.0.0 (with optional
128/// prerelease tag). Graduation has specific restrictions:
129/// - Cannot graduate from a prerelease version (must release stable 0.x first)
130/// - Cannot graduate a version that is already >= 1.0.0
131///
132/// For 0.x versions without graduation, behavior depends on `zero_behavior`:
133/// - `EffectiveMinor`: major bumps become minor, minor/patch both become patch
134/// - `AutoPromoteOnMajor`: major bumps promote to 1.0.0, minor/patch are standard
135///
136/// # Errors
137///
138/// Returns `VersionError::CannotGraduateFromPrerelease` if graduation is requested
139/// on a prerelease version.
140///
141/// Returns `VersionError::CanOnlyGraduateZeroVersions` if graduation is requested
142/// on a version >= 1.0.0.
143///
144/// Returns `VersionError::InvalidPrerelease` if the prerelease identifier
145/// produces an invalid semver prerelease string.
146pub fn calculate_new_version_with_zero_behavior(
147    current: &Version,
148    bump_type: Option<BumpType>,
149    prerelease: Option<&PrereleaseSpec>,
150    zero_behavior: ZeroVersionBehavior,
151    graduate: bool,
152) -> Result<Version, VersionError> {
153    if graduate {
154        return calculate_graduation(current, prerelease);
155    }
156
157    if current.major >= 1 {
158        return calculate_new_version(current, bump_type, prerelease);
159    }
160
161    let effective_bump = match zero_behavior {
162        ZeroVersionBehavior::EffectiveMinor => bump_type.map(|bt| match bt {
163            BumpType::Major => BumpType::Minor,
164            BumpType::Minor | BumpType::Patch => BumpType::Patch,
165        }),
166        ZeroVersionBehavior::AutoPromoteOnMajor => {
167            if bump_type == Some(BumpType::Major) {
168                return apply_prerelease_to_version(Version::new(1, 0, 0), prerelease);
169            }
170            bump_type
171        }
172    };
173
174    calculate_new_version(current, effective_bump, prerelease)
175}
176
177fn calculate_graduation(
178    current: &Version,
179    prerelease: Option<&PrereleaseSpec>,
180) -> Result<Version, VersionError> {
181    if is_prerelease(current) {
182        return Err(VersionError::CannotGraduateFromPrerelease {
183            version: current.to_string(),
184        });
185    }
186
187    if current.major >= 1 {
188        return Err(VersionError::CanOnlyGraduateZeroVersions {
189            version: current.to_string(),
190        });
191    }
192
193    apply_prerelease_to_version(Version::new(1, 0, 0), prerelease)
194}
195
196fn apply_prerelease_to_version(
197    base: Version,
198    prerelease: Option<&PrereleaseSpec>,
199) -> Result<Version, VersionError> {
200    match prerelease {
201        Some(spec) => {
202            let mut version = base;
203            version.pre = make_prerelease(spec.identifier(), 1)?;
204            Ok(version)
205        }
206        None => Ok(base),
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213
214    #[test]
215    fn bump_patch() {
216        let version = Version::parse("1.2.3").unwrap();
217        let bumped = bump_version(&version, BumpType::Patch);
218        assert_eq!(bumped, Version::parse("1.2.4").unwrap());
219    }
220
221    #[test]
222    fn bump_minor() {
223        let version = Version::parse("1.2.3").unwrap();
224        let bumped = bump_version(&version, BumpType::Minor);
225        assert_eq!(bumped, Version::parse("1.3.0").unwrap());
226    }
227
228    #[test]
229    fn bump_major() {
230        let version = Version::parse("1.2.3").unwrap();
231        let bumped = bump_version(&version, BumpType::Major);
232        assert_eq!(bumped, Version::parse("2.0.0").unwrap());
233    }
234
235    #[test]
236    fn bump_version_strips_prerelease() {
237        let version = Version::parse("1.2.3-alpha.1").unwrap();
238        let bumped = bump_version(&version, BumpType::Patch);
239        assert_eq!(bumped, Version::parse("1.2.4").unwrap());
240    }
241
242    mod max_bump_type_tests {
243        use super::*;
244
245        #[test]
246        fn returns_none_for_empty_slice() {
247            assert_eq!(max_bump_type(&[]), None);
248        }
249
250        #[test]
251        fn returns_single_element() {
252            assert_eq!(max_bump_type(&[BumpType::Patch]), Some(BumpType::Patch));
253            assert_eq!(max_bump_type(&[BumpType::Minor]), Some(BumpType::Minor));
254            assert_eq!(max_bump_type(&[BumpType::Major]), Some(BumpType::Major));
255        }
256
257        #[test]
258        fn returns_minor_for_patch_and_minor() {
259            assert_eq!(
260                max_bump_type(&[BumpType::Patch, BumpType::Minor]),
261                Some(BumpType::Minor)
262            );
263        }
264
265        #[test]
266        fn returns_major_for_minor_and_major() {
267            assert_eq!(
268                max_bump_type(&[BumpType::Minor, BumpType::Major]),
269                Some(BumpType::Major)
270            );
271        }
272
273        #[test]
274        fn returns_major_for_all_three() {
275            assert_eq!(
276                max_bump_type(&[BumpType::Patch, BumpType::Minor, BumpType::Major]),
277                Some(BumpType::Major)
278            );
279        }
280
281        #[test]
282        fn handles_duplicates() {
283            assert_eq!(
284                max_bump_type(&[BumpType::Patch, BumpType::Patch, BumpType::Minor]),
285                Some(BumpType::Minor)
286            );
287        }
288
289        #[test]
290        fn order_does_not_matter() {
291            assert_eq!(
292                max_bump_type(&[BumpType::Major, BumpType::Patch, BumpType::Minor]),
293                Some(BumpType::Major)
294            );
295        }
296    }
297
298    mod parse_prerelease_tests {
299        use super::*;
300
301        #[test]
302        fn empty_prerelease_returns_none() {
303            let pre = Prerelease::EMPTY;
304            assert!(parse_prerelease(&pre).is_none());
305        }
306
307        #[test]
308        fn parses_standard_format() {
309            let pre = Prerelease::new("alpha.1").unwrap();
310            let (tag, num) = parse_prerelease(&pre).unwrap();
311            assert_eq!(tag, "alpha");
312            assert_eq!(num, 1);
313        }
314
315        #[test]
316        fn parses_higher_numbers() {
317            let pre = Prerelease::new("rc.42").unwrap();
318            let (tag, num) = parse_prerelease(&pre).unwrap();
319            assert_eq!(tag, "rc");
320            assert_eq!(num, 42);
321        }
322
323        #[test]
324        fn handles_tag_without_number() {
325            let pre = Prerelease::new("beta").unwrap();
326            let (tag, num) = parse_prerelease(&pre).unwrap();
327            assert_eq!(tag, "beta");
328            assert_eq!(num, 1);
329        }
330
331        #[test]
332        fn handles_complex_tag_with_dots() {
333            let pre = Prerelease::new("pre.release.3").unwrap();
334            let (tag, num) = parse_prerelease(&pre).unwrap();
335            assert_eq!(tag, "pre.release");
336            assert_eq!(num, 3);
337        }
338    }
339
340    mod calculate_new_version_tests {
341        use super::*;
342
343        #[test]
344        fn stable_to_alpha_with_patch() {
345            let version = Version::parse("1.0.0").unwrap();
346            let result = calculate_new_version(
347                &version,
348                Some(BumpType::Patch),
349                Some(&PrereleaseSpec::Alpha),
350            )
351            .unwrap();
352            assert_eq!(result, Version::parse("1.0.1-alpha.1").unwrap());
353        }
354
355        #[test]
356        fn stable_to_alpha_with_minor() {
357            let version = Version::parse("1.0.0").unwrap();
358            let result = calculate_new_version(
359                &version,
360                Some(BumpType::Minor),
361                Some(&PrereleaseSpec::Alpha),
362            )
363            .unwrap();
364            assert_eq!(result, Version::parse("1.1.0-alpha.1").unwrap());
365        }
366
367        #[test]
368        fn stable_to_alpha_with_major() {
369            let version = Version::parse("1.0.0").unwrap();
370            let result = calculate_new_version(
371                &version,
372                Some(BumpType::Major),
373                Some(&PrereleaseSpec::Alpha),
374            )
375            .unwrap();
376            assert_eq!(result, Version::parse("2.0.0-alpha.1").unwrap());
377        }
378
379        #[test]
380        fn alpha_increment_same_tag() {
381            let version = Version::parse("1.0.1-alpha.1").unwrap();
382            let result =
383                calculate_new_version(&version, None, Some(&PrereleaseSpec::Alpha)).unwrap();
384            assert_eq!(result, Version::parse("1.0.1-alpha.2").unwrap());
385        }
386
387        #[test]
388        fn alpha_to_beta_transition() {
389            let version = Version::parse("1.0.1-alpha.3").unwrap();
390            let result =
391                calculate_new_version(&version, None, Some(&PrereleaseSpec::Beta)).unwrap();
392            assert_eq!(result, Version::parse("1.0.1-beta.1").unwrap());
393        }
394
395        #[test]
396        fn beta_to_rc_transition() {
397            let version = Version::parse("1.0.1-beta.2").unwrap();
398            let result = calculate_new_version(&version, None, Some(&PrereleaseSpec::Rc)).unwrap();
399            assert_eq!(result, Version::parse("1.0.1-rc.1").unwrap());
400        }
401
402        #[test]
403        fn rc_graduate_to_stable() {
404            let version = Version::parse("1.0.1-rc.1").unwrap();
405            let result = calculate_new_version(&version, None, None).unwrap();
406            assert_eq!(result, Version::parse("1.0.1").unwrap());
407        }
408
409        #[test]
410        fn alpha_graduate_to_stable() {
411            let version = Version::parse("1.0.1-alpha.5").unwrap();
412            let result = calculate_new_version(&version, None, None).unwrap();
413            assert_eq!(result, Version::parse("1.0.1").unwrap());
414        }
415
416        #[test]
417        fn custom_prerelease_tag() {
418            let version = Version::parse("1.0.0").unwrap();
419            let spec = PrereleaseSpec::Custom("dev".to_string());
420            let result =
421                calculate_new_version(&version, Some(BumpType::Patch), Some(&spec)).unwrap();
422            assert_eq!(result, Version::parse("1.0.1-dev.1").unwrap());
423        }
424
425        #[test]
426        fn stable_bump_without_prerelease() {
427            let version = Version::parse("1.0.0").unwrap();
428            let result = calculate_new_version(&version, Some(BumpType::Minor), None).unwrap();
429            assert_eq!(result, Version::parse("1.1.0").unwrap());
430        }
431
432        #[test]
433        fn stable_no_change_without_bump_or_prerelease() {
434            let version = Version::parse("1.0.0").unwrap();
435            let result = calculate_new_version(&version, None, None).unwrap();
436            assert_eq!(result, Version::parse("1.0.0").unwrap());
437        }
438
439        #[test]
440        fn prerelease_defaults_to_patch_when_no_bump_specified() {
441            let version = Version::parse("1.0.0").unwrap();
442            let result =
443                calculate_new_version(&version, None, Some(&PrereleaseSpec::Alpha)).unwrap();
444            assert_eq!(result, Version::parse("1.0.1-alpha.1").unwrap());
445        }
446    }
447
448    mod is_prerelease_tests {
449        use super::*;
450
451        #[test]
452        fn stable_version_is_not_prerelease() {
453            let version = Version::parse("1.0.0").unwrap();
454            assert!(!is_prerelease(&version));
455        }
456
457        #[test]
458        fn alpha_version_is_prerelease() {
459            let version = Version::parse("1.0.0-alpha.1").unwrap();
460            assert!(is_prerelease(&version));
461        }
462
463        #[test]
464        fn rc_version_is_prerelease() {
465            let version = Version::parse("1.0.0-rc.1").unwrap();
466            assert!(is_prerelease(&version));
467        }
468    }
469
470    mod extract_prerelease_tag_tests {
471        use super::*;
472
473        #[test]
474        fn stable_version_returns_none() {
475            let version = Version::parse("1.0.0").unwrap();
476            assert!(extract_prerelease_tag(&version).is_none());
477        }
478
479        #[test]
480        fn extracts_alpha_tag() {
481            let version = Version::parse("1.0.0-alpha.1").unwrap();
482            assert_eq!(extract_prerelease_tag(&version), Some("alpha".to_string()));
483        }
484
485        #[test]
486        fn extracts_rc_tag() {
487            let version = Version::parse("1.0.0-rc.3").unwrap();
488            assert_eq!(extract_prerelease_tag(&version), Some("rc".to_string()));
489        }
490
491        #[test]
492        fn extracts_custom_tag() {
493            let version = Version::parse("1.0.0-nightly.5").unwrap();
494            assert_eq!(
495                extract_prerelease_tag(&version),
496                Some("nightly".to_string())
497            );
498        }
499    }
500
501    mod is_zero_version_tests {
502        use super::*;
503
504        #[test]
505        fn zero_major_is_zero_version() {
506            let version = Version::parse("0.1.0").unwrap();
507            assert!(is_zero_version(&version));
508        }
509
510        #[test]
511        fn zero_minor_patch_is_zero_version() {
512            let version = Version::parse("0.0.1").unwrap();
513            assert!(is_zero_version(&version));
514        }
515
516        #[test]
517        fn one_major_is_not_zero_version() {
518            let version = Version::parse("1.0.0").unwrap();
519            assert!(!is_zero_version(&version));
520        }
521
522        #[test]
523        fn two_major_is_not_zero_version() {
524            let version = Version::parse("2.3.4").unwrap();
525            assert!(!is_zero_version(&version));
526        }
527
528        #[test]
529        fn zero_prerelease_is_zero_version() {
530            let version = Version::parse("0.1.0-alpha.1").unwrap();
531            assert!(is_zero_version(&version));
532        }
533    }
534
535    mod calculate_new_version_with_zero_behavior_tests {
536        use super::*;
537
538        mod effective_minor_behavior {
539            use super::*;
540
541            #[test]
542            fn major_becomes_minor() {
543                let version = Version::parse("0.1.2").unwrap();
544                let result = calculate_new_version_with_zero_behavior(
545                    &version,
546                    Some(BumpType::Major),
547                    None,
548                    ZeroVersionBehavior::EffectiveMinor,
549                    false,
550                )
551                .unwrap();
552                assert_eq!(result, Version::parse("0.2.0").unwrap());
553            }
554
555            #[test]
556            fn minor_becomes_patch() {
557                let version = Version::parse("0.1.2").unwrap();
558                let result = calculate_new_version_with_zero_behavior(
559                    &version,
560                    Some(BumpType::Minor),
561                    None,
562                    ZeroVersionBehavior::EffectiveMinor,
563                    false,
564                )
565                .unwrap();
566                assert_eq!(result, Version::parse("0.1.3").unwrap());
567            }
568
569            #[test]
570            fn patch_stays_patch() {
571                let version = Version::parse("0.1.2").unwrap();
572                let result = calculate_new_version_with_zero_behavior(
573                    &version,
574                    Some(BumpType::Patch),
575                    None,
576                    ZeroVersionBehavior::EffectiveMinor,
577                    false,
578                )
579                .unwrap();
580                assert_eq!(result, Version::parse("0.1.3").unwrap());
581            }
582
583            #[test]
584            fn major_with_prerelease() {
585                let version = Version::parse("0.1.2").unwrap();
586                let result = calculate_new_version_with_zero_behavior(
587                    &version,
588                    Some(BumpType::Major),
589                    Some(&PrereleaseSpec::Alpha),
590                    ZeroVersionBehavior::EffectiveMinor,
591                    false,
592                )
593                .unwrap();
594                assert_eq!(result, Version::parse("0.2.0-alpha.1").unwrap());
595            }
596
597            #[test]
598            fn double_zero_version() {
599                let version = Version::parse("0.0.5").unwrap();
600                let result = calculate_new_version_with_zero_behavior(
601                    &version,
602                    Some(BumpType::Major),
603                    None,
604                    ZeroVersionBehavior::EffectiveMinor,
605                    false,
606                )
607                .unwrap();
608                assert_eq!(result, Version::parse("0.1.0").unwrap());
609            }
610        }
611
612        mod auto_promote_behavior {
613            use super::*;
614
615            #[test]
616            fn major_becomes_1_0_0() {
617                let version = Version::parse("0.1.2").unwrap();
618                let result = calculate_new_version_with_zero_behavior(
619                    &version,
620                    Some(BumpType::Major),
621                    None,
622                    ZeroVersionBehavior::AutoPromoteOnMajor,
623                    false,
624                )
625                .unwrap();
626                assert_eq!(result, Version::parse("1.0.0").unwrap());
627            }
628
629            #[test]
630            fn minor_stays_minor() {
631                let version = Version::parse("0.1.2").unwrap();
632                let result = calculate_new_version_with_zero_behavior(
633                    &version,
634                    Some(BumpType::Minor),
635                    None,
636                    ZeroVersionBehavior::AutoPromoteOnMajor,
637                    false,
638                )
639                .unwrap();
640                assert_eq!(result, Version::parse("0.2.0").unwrap());
641            }
642
643            #[test]
644            fn patch_stays_patch() {
645                let version = Version::parse("0.1.2").unwrap();
646                let result = calculate_new_version_with_zero_behavior(
647                    &version,
648                    Some(BumpType::Patch),
649                    None,
650                    ZeroVersionBehavior::AutoPromoteOnMajor,
651                    false,
652                )
653                .unwrap();
654                assert_eq!(result, Version::parse("0.1.3").unwrap());
655            }
656
657            #[test]
658            fn major_with_prerelease() {
659                let version = Version::parse("0.1.2").unwrap();
660                let result = calculate_new_version_with_zero_behavior(
661                    &version,
662                    Some(BumpType::Major),
663                    Some(&PrereleaseSpec::Alpha),
664                    ZeroVersionBehavior::AutoPromoteOnMajor,
665                    false,
666                )
667                .unwrap();
668                assert_eq!(result, Version::parse("1.0.0-alpha.1").unwrap());
669            }
670        }
671
672        mod stable_versions_unaffected {
673            use super::*;
674
675            #[test]
676            fn effective_minor_major_bump() {
677                let version = Version::parse("1.2.3").unwrap();
678                let result = calculate_new_version_with_zero_behavior(
679                    &version,
680                    Some(BumpType::Major),
681                    None,
682                    ZeroVersionBehavior::EffectiveMinor,
683                    false,
684                )
685                .unwrap();
686                assert_eq!(result, Version::parse("2.0.0").unwrap());
687            }
688
689            #[test]
690            fn auto_promote_major_bump() {
691                let version = Version::parse("1.2.3").unwrap();
692                let result = calculate_new_version_with_zero_behavior(
693                    &version,
694                    Some(BumpType::Major),
695                    None,
696                    ZeroVersionBehavior::AutoPromoteOnMajor,
697                    false,
698                )
699                .unwrap();
700                assert_eq!(result, Version::parse("2.0.0").unwrap());
701            }
702        }
703
704        mod graduation {
705            use super::*;
706
707            #[test]
708            fn promotes_zero_to_1_0_0() {
709                let version = Version::parse("0.5.3").unwrap();
710                let result = calculate_new_version_with_zero_behavior(
711                    &version,
712                    None,
713                    None,
714                    ZeroVersionBehavior::EffectiveMinor,
715                    true,
716                )
717                .unwrap();
718                assert_eq!(result, Version::parse("1.0.0").unwrap());
719            }
720
721            #[test]
722            fn with_prerelease() {
723                let version = Version::parse("0.5.3").unwrap();
724                let result = calculate_new_version_with_zero_behavior(
725                    &version,
726                    None,
727                    Some(&PrereleaseSpec::Alpha),
728                    ZeroVersionBehavior::EffectiveMinor,
729                    true,
730                )
731                .unwrap();
732                assert_eq!(result, Version::parse("1.0.0-alpha.1").unwrap());
733            }
734
735            #[test]
736            fn errors_on_prerelease_version() {
737                let version = Version::parse("0.5.3-alpha.1").unwrap();
738                let result = calculate_new_version_with_zero_behavior(
739                    &version,
740                    None,
741                    None,
742                    ZeroVersionBehavior::EffectiveMinor,
743                    true,
744                );
745                assert!(matches!(
746                    result,
747                    Err(VersionError::CannotGraduateFromPrerelease { .. })
748                ));
749            }
750
751            #[test]
752            fn errors_on_stable_version() {
753                let version = Version::parse("1.2.3").unwrap();
754                let result = calculate_new_version_with_zero_behavior(
755                    &version,
756                    None,
757                    None,
758                    ZeroVersionBehavior::EffectiveMinor,
759                    true,
760                );
761                assert!(matches!(
762                    result,
763                    Err(VersionError::CanOnlyGraduateZeroVersions { .. })
764                ));
765            }
766
767            #[test]
768            fn bump_type_ignored_when_graduating() {
769                let version = Version::parse("0.5.3").unwrap();
770                let result = calculate_new_version_with_zero_behavior(
771                    &version,
772                    Some(BumpType::Patch),
773                    None,
774                    ZeroVersionBehavior::EffectiveMinor,
775                    true,
776                )
777                .unwrap();
778                assert_eq!(result, Version::parse("1.0.0").unwrap());
779            }
780
781            #[test]
782            fn behavior_ignored_when_graduating() {
783                let version = Version::parse("0.5.3").unwrap();
784                let result = calculate_new_version_with_zero_behavior(
785                    &version,
786                    None,
787                    None,
788                    ZeroVersionBehavior::AutoPromoteOnMajor,
789                    true,
790                )
791                .unwrap();
792                assert_eq!(result, Version::parse("1.0.0").unwrap());
793            }
794        }
795    }
796}