@@ -638,6 +638,71 @@ def test_missing_intervals_start_override_per_model(make_snapshot: t.Callable[..
638638 ]
639639
640640
641+ def test__missing_intervals__representative_snapshot_prod_intervals_returned (snapshot : Snapshot ):
642+ # Arrange
643+ snapshot .add_interval (start = "2020-01-01" , end = "2020-01-01" , is_dev = False )
644+ snapshot .add_interval (start = "2020-01-02" , end = "2020-01-02" , is_dev = False )
645+ snapshot .add_interval (start = "2020-01-01" , end = "2020-01-01" , is_dev = True )
646+
647+ snapshot .categorize_as (SnapshotChangeCategory .BREAKING )
648+ deployability_index = DeployabilityIndex .create ([snapshot ])
649+
650+ # Act
651+ missing_intervals = snapshot .missing_intervals (
652+ start = "2020-01-01" , end = "2020-01-03" , deployability_index = deployability_index
653+ )
654+
655+ # Assert
656+ assert deployability_index .is_representative (snapshot )
657+ assert missing_intervals == [
658+ (to_timestamp ("2020-01-03" ), to_timestamp ("2020-01-04" )),
659+ ]
660+
661+
662+ def test__missing_intervals__non_representative_snapshot_dev_intervals_returned (snapshot : Snapshot ):
663+ # Arrange
664+ snapshot .add_interval (start = "2020-01-01" , end = "2020-01-01" , is_dev = False )
665+ snapshot .add_interval (start = "2020-01-02" , end = "2020-01-02" , is_dev = False )
666+ snapshot .add_interval (start = "2020-01-01" , end = "2020-01-01" , is_dev = True )
667+
668+ snapshot .categorize_as (SnapshotChangeCategory .BREAKING , forward_only = True )
669+ deployability_index = DeployabilityIndex .create ([snapshot ])
670+
671+ # Act
672+ missing_intervals = snapshot .missing_intervals (
673+ start = "2020-01-01" , end = "2020-01-03" , deployability_index = deployability_index
674+ )
675+
676+ # Assert
677+ assert not deployability_index .is_representative (snapshot )
678+ assert missing_intervals == [
679+ (to_timestamp ("2020-01-02" ), to_timestamp ("2020-01-03" )), # Not missing from prod intervals
680+ (to_timestamp ("2020-01-03" ), to_timestamp ("2020-01-04" )),
681+ ]
682+
683+
684+ def test__missing_intervals__representative_but_non_deployable_snapshot_no_intervals_returned (
685+ snapshot : Snapshot ,
686+ ):
687+ # Arrange
688+ snapshot .add_interval (start = "2020-01-01" , end = "2020-01-01" , is_dev = False )
689+ snapshot .add_interval (start = "2020-01-02" , end = "2020-01-02" , is_dev = False )
690+ snapshot .add_interval (start = "2020-01-01" , end = "2020-01-01" , is_dev = True )
691+
692+ snapshot .categorize_as (SnapshotChangeCategory .INDIRECT_NON_BREAKING )
693+ deployability_index = DeployabilityIndex .create ([snapshot ])
694+
695+ # Act
696+ missing_intervals = snapshot .missing_intervals (
697+ start = "2020-01-01" , end = "2020-01-03" , deployability_index = deployability_index
698+ )
699+
700+ # Assert
701+ assert deployability_index .is_representative (snapshot )
702+ assert not deployability_index .is_deployable (snapshot )
703+ assert missing_intervals == []
704+
705+
641706def test_incremental_time_self_reference (make_snapshot ):
642707 snapshot = make_snapshot (
643708 SqlModel (
@@ -2407,63 +2472,101 @@ def test_earliest_start_date(sushi_context: Context):
24072472
24082473
24092474def test_deployability_index (make_snapshot ):
2410- snapshot_a = make_snapshot (SqlModel (name = "a" , query = parse_one ("SELECT 1" )))
2411- snapshot_a .categorize_as (SnapshotChangeCategory .BREAKING )
2475+ # Breaking change - should be both deployable / representative
2476+ snapshot_breaking = make_snapshot (SqlModel (name = "breaking" , query = parse_one ("SELECT 1" )))
2477+ snapshot_breaking .categorize_as (SnapshotChangeCategory .BREAKING )
24122478
2413- snapshot_b = make_snapshot (SqlModel (name = "b" , query = parse_one ("SELECT 1" )))
2414- snapshot_b .categorize_as (SnapshotChangeCategory .BREAKING , forward_only = True )
2415- snapshot_b .parents = (snapshot_a .snapshot_id ,)
2479+ # Forward only breaking change - cannot be deployable / representative due to forward only
2480+ snapshot_breaking_forward_only = make_snapshot (
2481+ SqlModel (name = "forward_only" , query = parse_one ("SELECT 1" ))
2482+ )
2483+ snapshot_breaking_forward_only .categorize_as (SnapshotChangeCategory .BREAKING , forward_only = True )
2484+ snapshot_breaking_forward_only .parents = (snapshot_breaking .snapshot_id ,)
24162485
2417- snapshot_c = make_snapshot (SqlModel (name = "c" , query = parse_one ("SELECT 1" )))
2418- snapshot_c .categorize_as (SnapshotChangeCategory .INDIRECT_BREAKING )
2419- snapshot_c .parents = (snapshot_b .snapshot_id ,)
2486+ # Indirect breaking - cannot be deployable / representative due to forward only non-representative parent
2487+ snapshot_indirect_breaking_non_representative_parent = make_snapshot (
2488+ SqlModel (name = "indirect_breaking_non_representative_parent" , query = parse_one ("SELECT 1" ))
2489+ )
2490+ snapshot_indirect_breaking_non_representative_parent .categorize_as (
2491+ SnapshotChangeCategory .INDIRECT_BREAKING
2492+ )
2493+ snapshot_indirect_breaking_non_representative_parent .parents = (
2494+ snapshot_breaking_forward_only .snapshot_id ,
2495+ )
24202496
2421- snapshot_d = make_snapshot (SqlModel (name = "d" , query = parse_one ("SELECT 1" )))
2422- snapshot_d .categorize_as (SnapshotChangeCategory .INDIRECT_BREAKING )
2423- snapshot_d .parents = (snapshot_b .snapshot_id , snapshot_a .snapshot_id )
2497+ # Indirect breaking - can be deployable / representative due to forward only representative parent
2498+ snapshot_indirect_breaking_representative_parent = make_snapshot (
2499+ SqlModel (name = "indirect_breaking_representative_parent" , query = parse_one ("SELECT 1" ))
2500+ )
2501+ snapshot_indirect_breaking_representative_parent .categorize_as (
2502+ SnapshotChangeCategory .INDIRECT_BREAKING
2503+ )
2504+ snapshot_indirect_breaking_representative_parent .parents = (snapshot_breaking .snapshot_id ,)
24242505
2425- snapshot_e = make_snapshot (SqlModel (name = "e" , query = parse_one ("SELECT 1" )))
2426- snapshot_e .categorize_as (SnapshotChangeCategory .NON_BREAKING )
2506+ # Non breaking - deployable / representative due to no breaking changes
2507+ snapshot_non_breaking = make_snapshot (
2508+ SqlModel (name = "non_breaking" , query = parse_one ("SELECT 1" ))
2509+ )
2510+ snapshot_non_breaking .categorize_as (SnapshotChangeCategory .NON_BREAKING )
24272511
2428- snapshot_f = make_snapshot (SqlModel (name = "f" , query = parse_one ("SELECT 1" )))
2429- snapshot_f .categorize_as (SnapshotChangeCategory .INDIRECT_BREAKING )
2430- snapshot_f .parents = (snapshot_e .snapshot_id , snapshot_a .snapshot_id )
2512+ # Indirect non breaking - can be representative but not deployable
2513+ snapshot_indirect_non_breaking = make_snapshot (
2514+ SqlModel (name = "indirect_non_breaking" , query = parse_one ("SELECT 1" ))
2515+ )
2516+ snapshot_indirect_non_breaking .intervals = [
2517+ (to_timestamp ("2023-01-01" ), to_timestamp ("2023-01-02" ))
2518+ ]
2519+ snapshot_indirect_non_breaking .categorize_as (SnapshotChangeCategory .INDIRECT_NON_BREAKING )
2520+ snapshot_indirect_non_breaking .parents = (snapshot_non_breaking .snapshot_id ,)
24312521
2432- snapshot_g = make_snapshot (SqlModel (name = "g" , query = parse_one ("SELECT 1" )))
2433- snapshot_g .intervals = [(to_timestamp ("2023-01-01" ), to_timestamp ("2023-01-02" ))]
2434- snapshot_g .categorize_as (SnapshotChangeCategory .INDIRECT_NON_BREAKING )
2435- snapshot_g .parents = (snapshot_e .snapshot_id ,)
2522+ # Breaking with non-representative parent - cannot be deployable due to non-representative parent
2523+ snapshot_breaking_non_representative_parent = make_snapshot (
2524+ SqlModel (name = "breaking_non_deployable_parents" , query = parse_one ("SELECT 1" ))
2525+ )
2526+ snapshot_breaking_non_representative_parent .categorize_as (SnapshotChangeCategory .BREAKING )
2527+ snapshot_breaking_non_representative_parent .parents = (
2528+ snapshot_breaking_forward_only .snapshot_id ,
2529+ )
24362530
24372531 snapshots = {
24382532 s .snapshot_id : s
24392533 for s in [
2440- snapshot_a ,
2441- snapshot_b ,
2442- snapshot_c ,
2443- snapshot_d ,
2444- snapshot_e ,
2445- snapshot_f ,
2446- snapshot_g ,
2534+ snapshot_breaking ,
2535+ snapshot_breaking_forward_only ,
2536+ snapshot_indirect_breaking_non_representative_parent ,
2537+ snapshot_indirect_breaking_representative_parent ,
2538+ snapshot_non_breaking ,
2539+ snapshot_indirect_non_breaking ,
2540+ snapshot_breaking_non_representative_parent ,
24472541 ]
24482542 }
24492543
24502544 deployability_index = DeployabilityIndex .create (snapshots )
24512545
2452- assert deployability_index .is_deployable (snapshot_a )
2453- assert deployability_index .is_deployable (snapshot_e )
2454- assert deployability_index .is_deployable (snapshot_f )
2455- assert not deployability_index .is_deployable (snapshot_g )
2456- assert not deployability_index .is_deployable (snapshot_b )
2457- assert not deployability_index .is_deployable (snapshot_c )
2458- assert not deployability_index .is_deployable (snapshot_d )
2546+ assert deployability_index .is_deployable (snapshot_breaking )
2547+ assert deployability_index .is_representative (snapshot_breaking )
24592548
2460- assert deployability_index .is_representative (snapshot_a )
2461- assert deployability_index .is_representative (snapshot_e )
2462- assert deployability_index .is_representative (snapshot_f )
2463- assert deployability_index .is_representative (snapshot_g )
2464- assert not deployability_index .is_representative (snapshot_b )
2465- assert not deployability_index .is_representative (snapshot_c )
2466- assert not deployability_index .is_representative (snapshot_d )
2549+ assert not deployability_index .is_deployable (snapshot_breaking_forward_only )
2550+ assert not deployability_index .is_representative (snapshot_breaking_forward_only )
2551+
2552+ assert deployability_index .is_deployable (snapshot_non_breaking )
2553+ assert deployability_index .is_representative (snapshot_non_breaking )
2554+
2555+ assert not deployability_index .is_deployable (
2556+ snapshot_indirect_breaking_non_representative_parent
2557+ )
2558+ assert not deployability_index .is_representative (
2559+ snapshot_indirect_breaking_non_representative_parent
2560+ )
2561+
2562+ assert deployability_index .is_deployable (snapshot_indirect_breaking_representative_parent )
2563+ assert deployability_index .is_representative (snapshot_indirect_breaking_representative_parent )
2564+
2565+ assert not deployability_index .is_deployable (snapshot_indirect_non_breaking )
2566+ assert deployability_index .is_representative (snapshot_indirect_non_breaking )
2567+
2568+ assert not deployability_index .is_deployable (snapshot_breaking_non_representative_parent )
2569+ assert not deployability_index .is_representative (snapshot_breaking_non_representative_parent )
24672570
24682571 all_deployable_index = deployability_index .all_deployable ()
24692572 assert all (all_deployable_index .is_deployable (s ) for s in snapshots .values ())
0 commit comments