#!/usr/bin/env python3
"""
Tests for ``version_utils.compute_next_version`` -- 'rollover' policy.

This file holds the rollover-policy scenarios. Strictness invariants and
legacy-'patch' backcompat regressions live in sibling files (round 2
codex review split):

  - ``test_compute_next_version_strictness`` -- ``result > floor`` AND
    ``result > current`` invariant guard across all policies.
  - ``test_compute_next_version_patch_backcompat`` -- guards that the
    legacy unbounded 'patch' policy is unchanged for consumers pinning
    the historical default.

Splitting keeps each file under the project's 10-functions-per-file cap.

Rollover semantics: patch+1 with carry. At the resulting patch>9 it
rolls into the next minor (1.0.9 -> 1.1.0); at the resulting minor>9 it
cascades into the next major (1.9.9 -> 2.0.0). Major has no upper limit
(9.9.9 -> 10.0.0). The carry uses ``>9`` (not ``==10``) so projects with
inputs already past the boundary -- e.g. a 1.0.10 left from the legacy
'patch' policy -- still cascade cleanly. Cross-train semantics inherited
from 'patch' are preserved: floor on a higher major.minor train wins
outright, and rollover bumps from there.
"""

from __future__ import annotations

import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))

import version_utils  # noqa: E402


class ComputeNextVersionRolloverTests(unittest.TestCase):
    """Rollover policy: patch bumps with carry into minor / major."""

    def test_compute_next_version_rollover_simple_patch_bump(self):
        # Below the .9 boundary -> simple patch increment.
        result = version_utils.compute_next_version("1.0.5", "1.0.5", "rollover")
        self.assertEqual(result, "1.0.6")

    def test_compute_next_version_rollover_patch_overflow(self):
        # Patch at .9 must roll: 1.0.9 -> 1.1.0.
        result = version_utils.compute_next_version("1.0.9", "1.0.9", "rollover")
        self.assertEqual(result, "1.1.0")

    def test_compute_next_version_rollover_minor_overflow(self):
        # Both patch and minor at .9 must cascade: 1.9.9 -> 2.0.0.
        result = version_utils.compute_next_version("1.9.9", "1.9.9", "rollover")
        self.assertEqual(result, "2.0.0")

    def test_compute_next_version_rollover_floor_higher_patch(self):
        # Floor sits above current on the same train; rollover from floor.
        # 1.0.5 vs floor 1.0.9 -> bump from .9 -> 1.1.0.
        result = version_utils.compute_next_version("1.0.5", "1.0.9", "rollover")
        self.assertEqual(result, "1.1.0")

    def test_compute_next_version_rollover_cross_train(self):
        # Floor on a higher major.minor train wins outright; rollover then
        # bumps from floor's patch. 1.0.5 vs floor 2.0.5 -> 2.0.6.
        result = version_utils.compute_next_version("1.0.5", "2.0.5", "rollover")
        self.assertEqual(result, "2.0.6")

    def test_compute_next_version_rollover_major_overflow_no_cap(self):
        # Major has no rollover ceiling; 9.9.9 -> 10.0.0 (just keeps growing).
        result = version_utils.compute_next_version("9.9.9", "9.9.9", "rollover")
        self.assertEqual(result, "10.0.0")

    def test_compute_next_version_rollover_two_component_equal(self):
        # 2-component MARKETING_VERSION ("1.0") parses .patch=0 -> .1.
        result = version_utils.compute_next_version("1.0", "1.0", "rollover")
        self.assertEqual(result, "1.0.1")

    def test_compute_next_version_rollover_over_nine_patch_input(self):
        # Round-2 codex: a project previously on 'patch' may sit at
        # 1.0.10 before switching to 'rollover'. The carry must use
        # ``new_patch > 9`` (not ``== 10``) so 11 still cascades.
        # 1.0.10 -> +1 -> patch=11 -> >9 -> patch=0, minor+=1 -> 1.1.0.
        result = version_utils.compute_next_version("1.0.10", "1.0.10", "rollover")
        self.assertEqual(result, "1.1.0")

    def test_compute_next_version_rollover_over_nine_with_floor_higher(self):
        # Round-2 codex: floor pulls patch_base above 9 too. trains
        # match -> patch_base=max(15,20)=20 -> +1=21 -> >9 cascade.
        # 1.0.15 vs floor 1.0.20 -> 1.1.0.
        result = version_utils.compute_next_version("1.0.15", "1.0.20", "rollover")
        self.assertEqual(result, "1.1.0")


if __name__ == "__main__":
    unittest.main()
