"""Unit tests for `soundrecording_utils.metadata.consolidation_logic`. These are comprehensive tests migrated from the TikTok delivery lambda to verify the `consolidate_rights` function behavior with various rules combinations and edge cases. """ from datetime import datetime, timedelta, timezone from typing import List, Optional import pytest from soundrecording_utils.metadata.consolidation_logic import _parse_date_string, consolidate_rights from soundrecording_utils.metadata.types import ( ExplicitStatus, FingerprintRule, FingerprintRuleBuilder, Genre, HasFingerprintRules, Label, LabelID, LabelType, OrchardSoundRecording, PolicyType, Product, ProductStatus, Profile, Subgenre, Track, TrackConnection, ) def _minimal_profile(): return Profile(id='1234', type='ORCHADMIN') def _minimal_label_w_subaccount( vendor_rules: Optional[List[FingerprintRule]] = None, subaccount_rules: Optional[List[FingerprintRule]] = None): if vendor_rules is None: vendor_rules = [] if subaccount_rules is None: subaccount_rules = [] vendor_rules_wrapped = HasFingerprintRules(rules=vendor_rules) return Label( name='Test Label with Subaccount', type=LabelType.Subaccount, uuid='lbl-subacct-uuid', id=LabelID(vendor=123, subaccount=456), rules=subaccount_rules, vendor=vendor_rules_wrapped ) def _minimal_label_no_subaccount(vendor_rules: Optional[List[FingerprintRule]] = None): if vendor_rules is None: vendor_rules = [] return Label( name='Test Label without Subaccount', type=LabelType.Vendor, uuid='lbl-no-subacct-uuid', id=LabelID(vendor=123, subaccount=0), rules=vendor_rules, vendor=None ) def _minimal_product( with_subaccount: bool = True, vendor_rules: Optional[List[FingerprintRule]] = None, subaccount_rules: Optional[List[FingerprintRule]] = None): if vendor_rules is None: vendor_rules = [] if subaccount_rules is None: subaccount_rules = [] wrapped_label = None if with_subaccount: wrapped_label = _minimal_label_w_subaccount( vendor_rules=vendor_rules, subaccount_rules=subaccount_rules) else: wrapped_label = _minimal_label_no_subaccount(vendor_rules=vendor_rules) return Product( id='12345', display_upc='8877665544', status=ProductStatus.in_content, label=wrapped_label, subgenre=Subgenre(id=1, name='s', genre=Genre(id=1, name='g')), release_date='2025-01-01' ) def _minimal_track( product: Optional[Product] = None, isrc: str = 'ISRC12345678', primary: bool = True, rules: Optional[List[FingerprintRule]] = None): if product is None: product = _minimal_product() if rules is None: rules = [] return Track( tuid='1234', product=product, isrc=isrc, name='Test Track', territories=['US'], participations=[], explicit=ExplicitStatus.N, primary=primary, rules=rules, ) def _fingerprint_rule( policy, territory, start_date: Optional[datetime] = None, end_date: Optional[datetime] = None, service='tiktok'): return FingerprintRule( active=True, policy=policy, service=service, territory=territory, created_date='2025-01-01', last_modified_date='2025-01-01', created_by=_minimal_profile(), last_modified_by=_minimal_profile(), start_date=start_date, end_date=end_date, ) def _dt(year: int, month: int, day: int) -> datetime: """Create a UTC midnight datetime for test convenience.""" return datetime(year, month, day, tzinfo=timezone.utc) rule_builder = FingerprintRuleBuilder() # TEST HELPER FUNCTIONS class TestParseDateString: """Test the _parse_date_string helper function.""" def test_parse_iso_date_only(self): """Test parsing a bare ISO date string defaults to UTC midnight.""" result = _parse_date_string('2024-10-01') assert result == datetime(2024, 10, 1, tzinfo=timezone.utc) def test_parse_datetime_with_time(self): """Test parsing an ISO datetime string without timezone defaults to UTC.""" result = _parse_date_string('2024-10-01T00:00:00') assert result == datetime(2024, 10, 1, 0, 0, 0, tzinfo=timezone.utc) def test_parse_datetime_with_milliseconds_and_zone(self): """Test parsing an ISO datetime string with milliseconds and Z timezone.""" result = _parse_date_string('2024-10-01T00:00:00.000Z') assert result == datetime(2024, 10, 1, 0, 0, 0, tzinfo=timezone.utc) def test_parse_datetime_with_utc_offset(self): """Test parsing an ISO datetime string with UTC offset preserves time.""" result = _parse_date_string('2024-10-01T14:30:00+00:00') assert result == datetime(2024, 10, 1, 14, 30, 0, tzinfo=timezone.utc) def test_parse_datetime_with_non_utc_offset(self): """Test parsing an ISO datetime string with non-UTC offset is preserved.""" from datetime import timedelta ist = timezone(timedelta(hours=5, minutes=30)) result = _parse_date_string('2024-10-01T14:30:00+05:30') assert result == datetime(2024, 10, 1, 14, 30, 0, tzinfo=ist) def test_parse_datetime_with_different_time(self): """Test that time component is preserved in the returned datetime.""" result = _parse_date_string('2024-10-01T23:59:59.999Z') assert result == datetime(2024, 10, 1, 23, 59, 59, 999000, tzinfo=timezone.utc) def test_invalid_string_too_short(self): """Test that a string shorter than 10 characters raises ValueError.""" with pytest.raises(ValueError, match="Invalid isoformat string: '2024-10-0'"): _parse_date_string('2024-10-0') def test_invalid_string_empty(self): """Test that an empty string raises ValueError.""" with pytest.raises(ValueError, match="Invalid isoformat string: ''"): _parse_date_string('') def test_invalid_format(self): """Test that an invalid date format raises ValueError.""" with pytest.raises(ValueError): _parse_date_string('01-10-2024T00:00:00') # TEST SINGLE TRACK SCENARIOS def test_simplest_monetization_scenario(): """Test a single track, no rules on track or subacc level scenario. Monetize US starting 2025-01-01. Should only contain the monetization policy. """ product = _minimal_product( with_subaccount=False, vendor_rules=[ rule_builder .with_policy(PolicyType.monetize) .with_territory('US') .with_start_date(_dt(2025, 1, 1)) .build() ] ) track = _minimal_track(product=product) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 1 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date is None assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_simplest_carveout_scenario(): """Test a single track, no rules on track or subacc level scenario. US carveout only. Should only contain the block_access policy. """ product = _minimal_product( with_subaccount=False, vendor_rules=[ rule_builder .with_policy(PolicyType.carveout) .with_territory('US') .build() ] ) track = _minimal_track(product=product) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_simplest_block_scenario(): """Test a single track, no rules on track or subacc level scenario. US block rule only. Should result in a block access policy. """ product = _minimal_product( with_subaccount=False, vendor_rules=[ rule_builder .with_policy(PolicyType.block_access) .with_territory('US') .with_start_date(_dt(2025, 1, 1)) .build() ] ) track = _minimal_track(product=product) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 1 block_access_policy = result.block_access_policies[0] assert block_access_policy.territories == ['US'] assert block_access_policy.start_date == _dt(2025, 1, 1) assert block_access_policy.end_date is None def test_consolidation_ignores_other_services(): """Test a single track, rules on other services scenario. US carveout on vendor level for youtube, monetize US starting 2025-01-01 for tiktok. Monetization for tiktok should be unaffected. """ product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.carveout.value, territory='US', service='youtube'), _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1)) ] ) track = _minimal_track(product=product) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 1 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date is None assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_carveout_cancels_monetize(): """Test a single track, monetize on vendor, carveout on track scenario. Monetize US on vendor level, carveout US on track level. Should result in no rules at all. """ product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1)) ] ) track = _minimal_track( product=product, rules=[ _fingerprint_rule(policy=PolicyType.carveout.value, territory='US') ]) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_block_overrides_monetize(): """Test a single track, monetize on vendor, block on track scenario. Monetize US on vendor level, block US on track level, same date range. """ product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1)) ] ) track = _minimal_track( product=product, rules=[ _fingerprint_rule(policy=PolicyType.block_access.value, territory='US', start_date=_dt(2025, 1, 1)) ]) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 1 block_access_policy = result.block_access_policies[0] assert block_access_policy.territories == ['US'] assert block_access_policy.start_date == _dt(2025, 1, 1) assert block_access_policy.end_date is None def test_sixtyseconds_overrides_monetize(): """Test that sixtyseconds policies win over monetize policies.""" product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule( policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1), service='youtube', ), _fingerprint_rule( policy=PolicyType.sixtyseconds.value, territory='US', start_date=_dt(2025, 1, 1), service='youtube', ), ], ) track = _minimal_track(product=product) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='youtube') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'sixtyseconds_policies') assert len(result.sixtyseconds_policies) == 1 sixtyseconds_policy = result.sixtyseconds_policies[0] assert sixtyseconds_policy.territories == ['US'] assert sixtyseconds_policy.start_date == _dt(2025, 1, 1) assert sixtyseconds_policy.end_date is None assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_block_overrides_sixtyseconds(): """Test that block policies win over sixtyseconds policies.""" product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule( policy=PolicyType.sixtyseconds.value, territory='US', start_date=_dt(2025, 1, 1), service='youtube', ), _fingerprint_rule( policy=PolicyType.block_access.value, territory='US', start_date=_dt(2025, 1, 1), service='youtube', ), ], ) track = _minimal_track(product=product) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='youtube') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'sixtyseconds_policies') assert len(result.sixtyseconds_policies) == 0 assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 1 block_access_policy = result.block_access_policies[0] assert block_access_policy.territories == ['US'] assert block_access_policy.start_date == _dt(2025, 1, 1) assert block_access_policy.end_date is None def test_block_voided_by_carveout(): """Test a single track, block on vendor, carveout on track scenario. Block on vendor level should be voided by carveout on track level. """ product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.block_access.value, territory='US', start_date=_dt(2025, 1, 1)) ] ) track = _minimal_track( product=product, rules=[ _fingerprint_rule(policy=PolicyType.carveout.value, territory='US') ]) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_overlapping_date_ranges(): """Test a single track, overlapping date ranges scenario. One monetize US on vendor level rule, starting 2025-01-01, ending 2025-02-01. Another monetize rule on vendor level, starting 2025-02-01, ending 2025-03-01. Should result in one monetize policy from 2025-01-01 to 2025-03-01. """ product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 2, 1)), _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 2, 1), end_date=_dt(2025, 3, 1)) ] ) track = _minimal_track(product=product) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 1 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date == _dt(2025, 3, 1) assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_non_overlapping_date_ranges(): """Test a single track, non overlapping date ranges scenario. One monetize US on vendor level rule, starting 2025-01-01, ending 2025-02-01. Another monetize rule on vendor level, starting 2025-03-01, ending 2025-04-01. Should result in both monetize policies getting returned individually. """ product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 2, 1)), _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 3, 1), end_date=_dt(2025, 4, 1)) ] ) track = _minimal_track(product=product) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 2 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date == _dt(2025, 2, 1) monetization_policy = result.monetization_policies[1] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 3, 1) assert monetization_policy.end_date == _dt(2025, 4, 1) assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_overlapping_open_end_date_ranges(): """Test a single track, overlapping date ranges scenario. One monetize US on vendor level rule, starting 2025-01-01, no end date. Another monetize rule on vendor level, starting 2025-03-01, ending 2025-04-01. Should result in one monetize policy from 2025-01-01, no end date. """ product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1)), _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 3, 1), end_date=_dt(2025, 4, 1)) ] ) track = _minimal_track(product=product) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 1 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date is None assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_partially_overlapping_mixed_date_ranges(): """Test a single track, partially overlapping date ranges scenario. One monetize US on vendor level rule, starting 2025-01-01, ending 2025-02-01. Another monetize rule on vendor level, starting 2025-03-01, ending 2025-04-01. Another monetize rule on vendor level, starting 2025-03-15, no end date. Another monetize rule on vendor level, starting 2025-06-01, ending 2025-07-01. Should result in two one monetize policies, one from 2025-01-01 to 2025-02-01 and another from 2025-03-01 with no end date. """ product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 2, 1)), _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 3, 1), end_date=_dt(2025, 4, 1)), _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 3, 15)), _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 6, 1), end_date=_dt(2025, 7, 1)) ] ) track = _minimal_track(product=product) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 2 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date == _dt(2025, 2, 1) monetization_policy = result.monetization_policies[1] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 3, 1) assert monetization_policy.end_date is None assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_mixed_date_ranges_mixed_territories(): """Test a single track, partially overlapping date ranges and varying territories scenario. One monetize US on vendor level rule, starting 2025-01-01, ending 2025-02-01. Another monetize FR rule, with the same date range. Another monetize US rule on vendor level, starting 2025-03-01, ending 2025-04-01. Another monetize US rule on vendor level, starting 2025-03-15, no end date. Another DE monetize rule on vendor level, starting 2025-06-01, ending 2025-07-01. Should result in three monetize policies, one from 2025-01-01 to 2025-02-01 for both US and FR, one for US from 2025-03-01 with no end date, one for DE from 2025-06-01 to 2025-07-01. """ product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 2, 1)), _fingerprint_rule(policy=PolicyType.monetize.value, territory='FR', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 2, 1)), _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 3, 1), end_date=_dt(2025, 4, 1)), _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 3, 15)), _fingerprint_rule(policy=PolicyType.monetize.value, territory='DE', start_date=_dt(2025, 6, 1), end_date=_dt(2025, 7, 1)) ] ) track = _minimal_track(product=product) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 3 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['FR', 'US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date == _dt(2025, 2, 1) monetization_policy = result.monetization_policies[1] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 3, 1) assert monetization_policy.end_date is None monetization_policy = result.monetization_policies[2] assert monetization_policy.territories == ['DE'] assert monetization_policy.start_date == _dt(2025, 6, 1) assert monetization_policy.end_date == _dt(2025, 7, 1) assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_block_splits_monetize(): """Test a single track, where a block rule splits monetize policies. One monetize rule on vendor level rule, starting 2025-01-01, ending 2025-03-01. Another block rule on vendor level, starting 2025-02-01, ending 2025-02-15. Should result in two monetize policies and one block policy with shared midnight boundary timestamps (DDEX consecutive-deal convention): 1) monetize from 2025-01-01T00:00:00 to 2025-02-01T00:00:00 2) block from 2025-02-01T00:00:00 to 2025-02-15T00:00:00 3) monetize from 2025-02-15T00:00:00 to 2025-03-01T00:00:00 """ product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 3, 1)), _fingerprint_rule(policy=PolicyType.block_access.value, territory='US', start_date=_dt(2025, 2, 1), end_date=_dt(2025, 2, 15)), ] ) track = _minimal_track(product=product) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 2 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date == _dt(2025, 2, 1) monetization_policy = result.monetization_policies[1] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 2, 15) assert monetization_policy.end_date == _dt(2025, 3, 1) assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 1 block_access_policy = result.block_access_policies[0] assert block_access_policy.territories == ['US'] assert block_access_policy.start_date == _dt(2025, 2, 1) assert block_access_policy.end_date == _dt(2025, 2, 15) # TEST CROSS TRACK CONSOLIDATION SCENARIOS def test_no_rights_consolidation(): """Test a two track, no rules on track or subacc level scenario. No rules for track 1. No rules for track 2. Should contain no policies. """ product1 = _minimal_product(with_subaccount=False, vendor_rules=[]) product2 = _minimal_product(with_subaccount=False, vendor_rules=[]) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_simplest_monetization_consolidation(): """Test a two track, no rules on track or subacc level scenario. Monetize US starting 2025-01-01 for track 1. No rules for track 2. Should contain the monetize policy. """ product1 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1)) ] ) product2 = _minimal_product( with_subaccount=False, vendor_rules=[] ) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 1 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date is None assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_simplest_carveout_consolidation(): """Test a two track, no rules on track or subacc level scenario. Carveout US for track 1. No rules for track 2. Should contain no policies. """ product1 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.carveout.value, territory='US') ] ) product2 = _minimal_product(with_subaccount=False, vendor_rules=[]) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_simplest_block_consolidation(): """Test a two track, no rules on track or subacc level scenario. block_access US for track 1. No rules for track 2. Should contain the block policy """ product1 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.block_access.value, territory='US', start_date=_dt(2025, 1, 1)) ] ) product2 = _minimal_product(with_subaccount=False, vendor_rules=[]) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 1 block_access_policy = result.block_access_policies[0] assert block_access_policy.territories == ['US'] assert block_access_policy.start_date == _dt(2025, 1, 1) assert block_access_policy.end_date is None def test_carveout_doesnt_cancel_monetize_across_tracks(): """Test a two track, monetize on track 1, carveout on track 2 scenario. Monetize US on track 1, carveout US on track 2. Should result in monetize policy. """ product1 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1)) ] ) product2 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.carveout.value, territory='US') ] ) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 1 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date is None assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_block_overrides_monetize_across_tracks(): """Test a two track, monetize on track 1, block on track 2 scenario. Monetize US on track 1, block US on track 2. Should result in block policy. """ product1 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1)) ] ) product2 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.block_access.value, territory='US', start_date=_dt(2025, 1, 1)) ] ) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 1 block_access_policy = result.block_access_policies[0] assert block_access_policy.territories == ['US'] assert block_access_policy.start_date == _dt(2025, 1, 1) assert block_access_policy.end_date is None def test_block_ignores_carveout_across_tracks(): """Test a two track, block on track 1, carveout on track 2 scenario. Block US on track 1, carveout US on track 2. Should result in block policy. """ product1 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.block_access.value, territory='US', start_date=_dt(2025, 1, 1)) ] ) product2 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.carveout.value, territory='US') ] ) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 1 block_access_policy = result.block_access_policies[0] assert block_access_policy.territories == ['US'] assert block_access_policy.start_date == _dt(2025, 1, 1) assert block_access_policy.end_date is None def test_block_overrides_all_across_tracks(): """Test a two track, monetize on track 1, carveout on track 2, block on track 3 scenario. Block on track 3 should be the only remaining policy. """ product1 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1)) ] ) product2 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.carveout.value, territory='US') ] ) product3 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.block_access.value, territory='US', start_date=_dt(2025, 1, 1)), ] ) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) track3 = _minimal_track(product=product3) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2, track3]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 1 block_access_policy = result.block_access_policies[0] assert block_access_policy.territories == ['US'] assert block_access_policy.start_date == _dt(2025, 1, 1) assert block_access_policy.end_date is None def test_overlapping_date_ranges_across_tracks(): """Test a two track, overlapping date ranges scenario. One monetize US on track 1, starting 2025-01-01, no end date. Another monetize rule on track 2, starting 2025-01-01, ending 2025-02-01. Should result in one monetize policy from 2025-01-01, no end date. """ product1 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1)) ] ) product2 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 2, 1)) ] ) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 1 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date is None assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_joining_overlapping_date_range_open_ended_across_tracks(): """Test a two track, overlapping date ranges scenario. One monetize US on track 1, starting 2025-01-01, ending 2025-02-01. Another monetize rule on track 2, starting 2025-02-01, no end date. Should result in one monetize policy from 2025-01-01, no end date. """ product1 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 2, 1)) ] ) product2 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 2, 1)) ] ) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 1 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date is None assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_joining_overlapping_date_range_across_tracks(): """Test a two track, overlapping date ranges scenario. One monetize US on track 1, starting 2025-01-01, ending 2025-02-01. Another monetize rule on track 2, starting 2025-02-01, ending 2025-03-01. Should result in one monetize policy from 2025-01-01 to 2025-03-01. """ product1 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 2, 1)) ] ) product2 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 2, 1), end_date=_dt(2025, 3, 1)) ] ) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 1 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date == _dt(2025, 3, 1) assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_not_joining_non_overlapping_date_range_across_tracks(): """Test a two track, non overlapping date ranges scenario. One monetize US on track 1, starting 2025-01-01, ending 2025-02-01. Another monetize rule on track 2, starting 2025-03-01, ending 2025-04-01. Should result in two monetize policies getting returned individually. """ product1 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 2, 1)) ] ) product2 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 3, 1), end_date=_dt(2025, 4, 1)) ] ) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 2 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date == _dt(2025, 2, 1) monetization_policy = result.monetization_policies[1] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 3, 1) assert monetization_policy.end_date == _dt(2025, 4, 1) assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_block_overrides_monetize_across_tracks_within_open_ends(): """Test a two track, monetize on track 1, with an interrupting block on track 2 within date range scenario. Monetize US on track 1, start block US on track 2 one month later. Should result in a monetize policy ending at the exact midnight the block starts. """ product1 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1)) ] ) product2 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.block_access.value, territory='US', start_date=_dt(2025, 2, 1)) ] ) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 1 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date == _dt(2025, 2, 1) assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 1 block_access_policy = result.block_access_policies[0] assert block_access_policy.territories == ['US'] assert block_access_policy.start_date == _dt(2025, 2, 1) assert block_access_policy.end_date is None def test_block_interrupts_monetize_across_tracks(): """Test a two track, monetize on track 1, block on track 2 within date range scenario. Monetize US on track 1, block US on track 2 one month later for 1 month. Should result in two monetize policies and one block policy with shared midnight boundary timestamps (DDEX consecutive-deal convention): 1) monetize from 2025-01-01 to 2025-02-01 2) block from 2025-02-01 to 2025-03-01 3) monetize from 2025-03-01 with no end date """ product1 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1)) ] ) product2 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.block_access.value, territory='US', start_date=_dt(2025, 2, 1), end_date=_dt(2025, 3, 1)), ] ) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 2 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date == _dt(2025, 2, 1) monetization_policy = result.monetization_policies[1] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 3, 1) assert monetization_policy.end_date is None assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 1 block_access_policy = result.block_access_policies[0] assert block_access_policy.territories == ['US'] assert block_access_policy.start_date == _dt(2025, 2, 1) assert block_access_policy.end_date == _dt(2025, 3, 1) def test_blocks_interrupt_monetize_across_tracks(): """Test a two track, monetize on track 1, block on track 2 within date range scenario. Monetize US on track 1, block US on track 2 twice for one day each. Should result in three monetize policies and two block policies with shared midnight boundary timestamps (DDEX consecutive-deal convention): 1) monetize from 2025-01-01 to 2025-02-01 2) block from 2025-02-01 to 2025-02-02 3) monetize from 2025-02-02 to 2025-03-01 4) block from 2025-03-01 to 2025-03-02 5) monetize from 2025-03-02 with no end date """ product1 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1)) ] ) product2 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.block_access.value, territory='US', start_date=_dt(2025, 2, 1), end_date=_dt(2025, 2, 2)), _fingerprint_rule(policy=PolicyType.block_access.value, territory='US', start_date=_dt(2025, 3, 1), end_date=_dt(2025, 3, 2)), ] ) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 3 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date == _dt(2025, 2, 1) monetization_policy = result.monetization_policies[1] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 2, 2) assert monetization_policy.end_date == _dt(2025, 3, 1) monetization_policy = result.monetization_policies[2] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 3, 2) assert monetization_policy.end_date is None assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 2 block_access_policy = result.block_access_policies[0] assert block_access_policy.territories == ['US'] assert block_access_policy.start_date == _dt(2025, 2, 1) assert block_access_policy.end_date == _dt(2025, 2, 2) block_access_policy = result.block_access_policies[1] assert block_access_policy.territories == ['US'] assert block_access_policy.start_date == _dt(2025, 3, 1) assert block_access_policy.end_date == _dt(2025, 3, 2) def test_partially_overlapping_monetize_with_block_across_tracks(): """Test a two track, partially overlapping monetize date ranges with an interrupting block scenario. One monetize US on track 1, starting 2025-01-01, ending 2025-02-01. Another monetize rule on track 2, starting 2025-02-01, ending 2025-03-01. A block rule on track 3, starting 2025-02-01, ending 2025-03-01. Should result in one monetize and one block policy with shared midnight boundary: 1) monetize from 2025-01-01 to 2025-02-01 2) block from 2025-02-01 to 2025-03-01 """ product1 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 2, 1)) ] ) product2 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 2, 1), end_date=_dt(2025, 3, 1)) ] ) product3 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.block_access.value, territory='US', start_date=_dt(2025, 2, 1), end_date=_dt(2025, 3, 1)), ] ) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) track3 = _minimal_track(product=product3) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2, track3]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 1 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date == _dt(2025, 2, 1) assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 1 block_access_policy = result.block_access_policies[0] assert block_access_policy.territories == ['US'] assert block_access_policy.start_date == _dt(2025, 2, 1) assert block_access_policy.end_date == _dt(2025, 3, 1) def test_monetize_does_not_interrupt_block_across_tracks(): """Test a two track, block on track 1, monetize on track 2 within date range scenario. Block US on track 1, monetize US on track 2 one month later for 1 month. Should result in one block policy only. """ product1 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.block_access.value, territory='US', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 3, 1)), ] ) product2 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 2, 1), end_date=_dt(2025, 2, 15)) ] ) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 1 block_access_policy = result.block_access_policies[0] assert block_access_policy.territories == ['US'] assert block_access_policy.start_date == _dt(2025, 1, 1) assert block_access_policy.end_date == _dt(2025, 3, 1) def test_services_dont_interfere(): """Test a scenario where multiple services have different rules. One block rule for youtube, one monetize rule for tiktok in the same range. The youtube rule should be completely discarded, as we are consolidating for tiktok. Should result in one monetize policy. """ product1 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.block_access.value, territory='US', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 2, 1), service='youtube'), ] ) product2 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 2, 1)) ] ) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 1 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date == _dt(2025, 2, 1) assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_territories_dont_interfere(): """Test a scenario where tracks have different territories. One block rule for DE, one monetize rule for US in the same range. The DE rule should not cancel the US rule. Should result in one monetize and one block policy. """ product1 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.block_access.value, territory='DE', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 2, 1)), ] ) product2 = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 2, 1)) ] ) track1 = _minimal_track(product=product1) track2 = _minimal_track(product=product2) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 1 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date == _dt(2025, 2, 1) assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 1 block_access_policy = result.block_access_policies[0] assert block_access_policy.territories == ['DE'] assert block_access_policy.start_date == _dt(2025, 1, 1) assert block_access_policy.end_date == _dt(2025, 2, 1) def test_date_ranges_getting_grouped(): """Test a scenario where multiple date ranges get grouped together. Three different tracks making up the same date ranges for several territories. Should result in one policy. """ product1 = _minimal_product( with_subaccount=False ) track1 = _minimal_track(product=product1, rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 2, 1)), _fingerprint_rule(policy=PolicyType.monetize.value, territory='DE', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 3, 1)), ]) track2 = _minimal_track(product=product1, rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 2, 1), end_date=_dt(2025, 3, 1)), ]) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 1 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['DE', 'US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date == _dt(2025, 3, 1) assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_carveout_overrides_block_intra_track(): """Test a scenario where carveout overrides block on the same track. Two vendors, one monetizing as per ususal. The other one is blocking globally on account level, but the track has a global carveout, voiding the block. Should result in one monetize policy. """ monetize_vendor_product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1)), ] ) block_vendor_product = _minimal_product( with_subaccount=True, vendor_rules=[ _fingerprint_rule(policy=PolicyType.block_access.value, territory='US', start_date=_dt(2025, 1, 1)), ] ) track1 = _minimal_track(product=monetize_vendor_product) track2 = _minimal_track(product=block_vendor_product, rules=[ _fingerprint_rule(policy=PolicyType.carveout.value, territory='US'), ]) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1, track2]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 1 monetization_policy = result.monetization_policies[0] assert monetization_policy.territories == ['US'] assert monetization_policy.start_date == _dt(2025, 1, 1) assert monetization_policy.end_date is None assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_subaccount_rules_override_vendor_rules(): """Test a scenario where subaccount rules override vendor rules. One vendor is blocking US, carves out AU, subaccount carves out US, blocks AU. There are no track level rules. Should result in no rules being generated (carveout trumps block). """ product = _minimal_product( with_subaccount=True, vendor_rules=[ rule_builder .with_policy(PolicyType.block_access) .with_territory('US') .with_start_date(_dt(2025, 1, 1)) .build(), rule_builder .with_policy(PolicyType.carveout) .with_territory('AU') .build() ], subaccount_rules=[ rule_builder .with_policy(PolicyType.carveout) .with_territory('US') .build(), rule_builder .with_policy(PolicyType.block_access) .with_territory('AU') .build() ] ) track1 = _minimal_track(product=product, rules=[]) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 0 def test_subaccount_adds_block_for_new_territory(): """Test a scenario where subaccount rules override vendor rules. One vendor is blocking US, the subaccount adds a block for DE starting later. There are no track level rules. Should result in the US & DE block policy being generated. """ product = _minimal_product( with_subaccount=True, vendor_rules=[ rule_builder .with_policy(PolicyType.block_access) .with_territory('US') .with_start_date(_dt(2025, 1, 1)) .build() ], subaccount_rules=[ rule_builder .with_policy(PolicyType.block_access) .with_territory('DE') .with_start_date(_dt(2025, 2, 1)) .build() ] ) track1 = _minimal_track(product=product, rules=[]) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 2 assert result.block_access_policies[0].territories == ['US'] assert result.block_access_policies[1].territories == ['DE'] def test_subaccount_rules_override_vendor_monetise(): """Test a scenario where subaccount rules override vendor rules. One vendor is blocking US & monetising DE, the subaccount has a carveout for DE. There are no track level rules. Should result in 1 block access rule being generated for US. """ product = _minimal_product( with_subaccount=True, vendor_rules=[ rule_builder .with_policy(PolicyType.block_access) .with_territory('US') .with_start_date(_dt(2025, 1, 1)) .build(), rule_builder .with_policy(PolicyType.monetize) .with_territory('DE') .build() ], subaccount_rules=[ rule_builder .with_policy(PolicyType.carveout) .with_territory('DE') .build() ] ) track1 = _minimal_track(product=product, rules=[]) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 1 assert result.block_access_policies[0].territories == ['US'] def test_track_overrides_subaccount(): """Test a scenario where subaccount rules override vendor rules. One vendor is blocking US, monetize DE, the subaccount has a block for DE. The track has a carveout for DE. Should result in 1 block access for US rule being generated. """ product = _minimal_product( with_subaccount=True, vendor_rules=[ rule_builder .with_policy(PolicyType.block_access) .with_territory('US') .with_start_date(_dt(2025, 1, 1)) .build(), rule_builder .with_policy(PolicyType.monetize) .with_territory('DE') .build() ], subaccount_rules=[ rule_builder .with_policy(PolicyType.block_access) .with_territory('DE') .build() ] ) track1 = _minimal_track(product=product, rules=[ rule_builder .with_policy(PolicyType.carveout) .with_territory('DE') .build() ]) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 1 assert result.block_access_policies[0].territories == ['US'] def test_subaccount_dates_override_vendor(): """Test a scenario where subaccount rules override vendor rules. One vendor is blocking US from 1.1.2025 to 1.2.2025, the subaccount blocks US from 1.2.2025 to 1.3.2025. No track rules. Should result in 1 block access rule being generated ending 1.3.2025 """ block_vendor_product = _minimal_product( with_subaccount=True, vendor_rules=[ rule_builder .with_policy(PolicyType.block_access) .with_territory('US') .with_start_date(_dt(2025, 1, 1)) .with_end_date(_dt(2025, 2, 1)) .build() ], subaccount_rules=[ rule_builder .with_policy(PolicyType.block_access) .with_territory('US') .with_start_date(_dt(2025, 2, 1)) .with_end_date(_dt(2025, 3, 1)) .build() ] ) track1 = _minimal_track(product=block_vendor_product, rules=[]) osr = OrchardSoundRecording(track_connection=TrackConnection( tracks=[track1]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert hasattr(result, 'monetization_policies') assert len(result.monetization_policies) == 0 assert hasattr(result, 'block_access_policies') assert len(result.block_access_policies) == 1 assert result.block_access_policies[0].start_date == _dt(2025, 1, 1) assert result.block_access_policies[0].end_date == _dt(2025, 3, 1) def test_time_portion_preserved_in_output(): """Test that non-midnight times are preserved through consolidation.""" product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=datetime.fromisoformat('2025-01-01T14:30:00+00:00'), end_date=datetime.fromisoformat('2025-03-01T18:00:00+00:00')) ] ) track = _minimal_track(product=product) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert len(result.monetization_policies) == 1 assert result.monetization_policies[0].start_date == datetime.fromisoformat('2025-01-01T14:30:00+00:00') assert result.monetization_policies[0].end_date == datetime.fromisoformat('2025-03-01T18:00:00+00:00') def test_non_utc_timezone_converted_to_utc(): """Test that non-UTC timezone offsets are converted to UTC.""" product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=datetime.fromisoformat('2025-01-01T14:30:00+05:00'), end_date=datetime.fromisoformat('2025-03-01T03:00:00-03:00')) ] ) track = _minimal_track(product=product) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert len(result.monetization_policies) == 1 assert result.monetization_policies[0].start_date is not None assert result.monetization_policies[0].end_date is not None # 14:30 +05:00 → 09:30 UTC (offset preserved, but equivalent instant) assert result.monetization_policies[0].start_date == datetime.fromisoformat('2025-01-01T09:30:00+00:00') assert result.monetization_policies[0].start_date.utcoffset() == timedelta(hours=5) # 03:00 -03:00 → 06:00 UTC (offset preserved, but equivalent instant) assert result.monetization_policies[0].end_date == datetime.fromisoformat('2025-03-01T06:00:00+00:00') assert result.monetization_policies[0].end_date.utcoffset() == timedelta(hours=-3) def test_block_split_produces_shared_boundary_timestamps(): """Test that block-split produces identical boundary timestamps on adjacent policies. Monetize US from 2025-01-01 to 2025-04-01 (open on month boundary). Block US from 2025-02-01 to 2025-03-01. The split must produce exact shared midnight timestamps, not ±1ms offsets, so that the EndDateTime of one period equals the StartDateTime of the next per the DDEX consecutive-deal convention. """ product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 4, 1)), _fingerprint_rule(policy=PolicyType.block_access.value, territory='US', start_date=_dt(2025, 2, 1), end_date=_dt(2025, 3, 1)), ] ) track = _minimal_track(product=product) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert len(result.monetization_policies) == 2 assert result.monetization_policies[0].start_date == _dt(2025, 1, 1) assert result.monetization_policies[0].end_date == _dt(2025, 2, 1) assert result.monetization_policies[1].start_date == _dt(2025, 3, 1) assert result.monetization_policies[1].end_date == _dt(2025, 4, 1) assert len(result.block_access_policies) == 1 assert result.block_access_policies[0].start_date == _dt(2025, 2, 1) assert result.block_access_policies[0].end_date == _dt(2025, 3, 1) # Verify the shared-boundary invariant: adjacent periods share the same timestamp. assert result.monetization_policies[0].end_date == result.block_access_policies[0].start_date assert result.block_access_policies[0].end_date == result.monetization_policies[1].start_date def test_timezone_conversion_crossing_day_boundary(): """Test that timezone conversion that crosses a day boundary works correctly.""" product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule(policy=PolicyType.monetize.value, territory='US', start_date=datetime.fromisoformat('2025-01-02T01:00:00+05:00')) ] ) track = _minimal_track(product=product) osr = OrchardSoundRecording(track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678') result = consolidate_rights(osr, service='tiktok') assert len(result.monetization_policies) == 1 # 2025-01-02T01:00 +05:00 → 2025-01-01T20:00 UTC (one day less due to crossing day boundary) assert result.monetization_policies[0].start_date == datetime.fromisoformat('2025-01-01T20:00:00+00:00') assert result.monetization_policies[0].start_date.utcoffset() == timedelta(hours=5) def test_block_splits_monetize_at_exact_midnight_boundaries(): """Test rolling policy update: block inserted inside monetize uses exact midnight boundaries. Monetize US from 2026-01-01 to 2026-01-10 (end is exclusive midnight). Block US from 2026-01-03 to 2026-01-05. Expected result — three policies with shared midnight boundary timestamps: 1) monetize 2026-01-01T00:00:00 → 2026-01-03T00:00:00 2) block 2026-01-03T00:00:00 → 2026-01-05T00:00:00 3) monetize 2026-01-05T00:00:00 → 2026-01-10T00:00:00 Per the DDEX consecutive-deal rule, the EndDateTime of one period equals the StartDateTime of the next — no 1ms gap, no 23:59:59 end-of-day timestamps. """ product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule( policy=PolicyType.monetize.value, territory='US', start_date=_dt(2026, 1, 1), end_date=_dt(2026, 1, 10), ), _fingerprint_rule( policy=PolicyType.block_access.value, territory='US', start_date=_dt(2026, 1, 3), end_date=_dt(2026, 1, 5), ), ], ) track = _minimal_track(product=product) osr = OrchardSoundRecording( track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678' ) result = consolidate_rights(osr, service='tiktok') assert len(result.monetization_policies) == 2 assert result.monetization_policies[0].territories == ['US'] assert result.monetization_policies[0].start_date == _dt(2026, 1, 1) assert result.monetization_policies[0].end_date == _dt(2026, 1, 3) assert result.monetization_policies[1].territories == ['US'] assert result.monetization_policies[1].start_date == _dt(2026, 1, 5) assert result.monetization_policies[1].end_date == _dt(2026, 1, 10) assert len(result.block_access_policies) == 1 assert result.block_access_policies[0].territories == ['US'] assert result.block_access_policies[0].start_date == _dt(2026, 1, 3) assert result.block_access_policies[0].end_date == _dt(2026, 1, 5) def test_block_splits_monetize_with_non_midnight_times(): """Test that a block with non-midnight times splits a monetize at exact timestamps. When input rules carry non-midnight times (which should not happen in practice but the consolidation logic does not enforce), the split boundaries land at the exact block_start and block_end timestamps — no ±1ms gap is introduced. Monetize US from 2025-01-01T10:00 to 2025-03-01T10:00. Block US from 2025-02-01T10:00 to 2025-02-15T10:00. Expected: 1) monetize 2025-01-01T10:00 → 2025-02-01T10:00 (ends exactly at block_start) 2) block 2025-02-01T10:00 → 2025-02-15T10:00 3) monetize 2025-02-15T10:00 → 2025-03-01T10:00 (starts exactly at block_end) """ product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule( policy=PolicyType.monetize.value, territory='US', start_date=datetime.fromisoformat('2025-01-01T10:00:00+00:00'), end_date=datetime.fromisoformat('2025-03-01T10:00:00+00:00'), ), _fingerprint_rule( policy=PolicyType.block_access.value, territory='US', start_date=datetime.fromisoformat('2025-02-01T10:00:00+00:00'), end_date=datetime.fromisoformat('2025-02-15T10:00:00+00:00'), ), ], ) track = _minimal_track(product=product) osr = OrchardSoundRecording( track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678' ) result = consolidate_rights(osr, service='tiktok') assert len(result.monetization_policies) == 2 assert result.monetization_policies[0].start_date == datetime.fromisoformat('2025-01-01T10:00:00+00:00') assert result.monetization_policies[0].end_date == datetime.fromisoformat('2025-02-01T10:00:00+00:00') assert result.monetization_policies[1].start_date == datetime.fromisoformat('2025-02-15T10:00:00+00:00') assert result.monetization_policies[1].end_date == datetime.fromisoformat('2025-03-01T10:00:00+00:00') assert len(result.block_access_policies) == 1 assert result.block_access_policies[0].start_date == datetime.fromisoformat('2025-02-01T10:00:00+00:00') assert result.block_access_policies[0].end_date == datetime.fromisoformat('2025-02-15T10:00:00+00:00') # Shared-boundary invariant holds regardless of whether times are midnight. assert result.monetization_policies[0].end_date == result.block_access_policies[0].start_date assert result.block_access_policies[0].end_date == result.monetization_policies[1].start_date def test_non_midnight_monetize_adjacent_to_non_midnight_block_is_not_trimmed(): """Test that a monetize ending exactly when a block starts is treated as adjacent. When monetize_end == block_start (at a non-midnight time), the strict overlap check (`block_start < monetize_end`) evaluates to False, so the monetize is left untouched — it is adjacent to the block, not overlapping. Monetize US from 2025-01-01T00:00 to 2025-02-01T10:00. Block US from 2025-02-01T10:00 to 2025-03-01T10:00. Expected: monetize is preserved as-is; block is returned separately. """ product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule( policy=PolicyType.monetize.value, territory='US', start_date=datetime.fromisoformat('2025-01-01T00:00:00+00:00'), end_date=datetime.fromisoformat('2025-02-01T10:00:00+00:00'), ), _fingerprint_rule( policy=PolicyType.block_access.value, territory='US', start_date=datetime.fromisoformat('2025-02-01T10:00:00+00:00'), end_date=datetime.fromisoformat('2025-03-01T10:00:00+00:00'), ), ], ) track = _minimal_track(product=product) osr = OrchardSoundRecording( track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678' ) result = consolidate_rights(osr, service='tiktok') assert len(result.monetization_policies) == 1 assert result.monetization_policies[0].start_date == datetime.fromisoformat('2025-01-01T00:00:00+00:00') assert result.monetization_policies[0].end_date == datetime.fromisoformat('2025-02-01T10:00:00+00:00') assert len(result.block_access_policies) == 1 assert result.block_access_policies[0].start_date == datetime.fromisoformat('2025-02-01T10:00:00+00:00') assert result.block_access_policies[0].end_date == datetime.fromisoformat('2025-03-01T10:00:00+00:00') # KNOWN ISSUE: zero-duration blocks (start_date == end_date) inside a monetize range # trigger Case 4 and split the monetize into two adjacent pieces that together cover # the same continuous period as the original. This is a side-effect of using the block # boundaries directly as split points when block_start == block_end. To be fixed in a # separate ticket if necessary. def test_zero_duration_midnight_block_splits_monetize_into_adjacent_pieces(): """Document current behaviour: a zero-duration midnight block fragments a monetize. Monetize US from 2025-01-01 to 2025-10-01. Block US from 2025-02-01 to 2025-02-01 (zero duration — start == end). The block has no real duration but still triggers a split, producing two adjacent monetize ranges that together cover the original period unchanged: 1) monetize 2025-01-01 → 2025-02-01 2) block 2025-02-01 → 2025-02-01 (zero duration) 3) monetize 2025-02-01 → 2025-10-01 """ product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule( policy=PolicyType.monetize.value, territory='US', start_date=_dt(2025, 1, 1), end_date=_dt(2025, 10, 1), ), _fingerprint_rule( policy=PolicyType.block_access.value, territory='US', start_date=_dt(2025, 2, 1), end_date=_dt(2025, 2, 1), ), ], ) track = _minimal_track(product=product) osr = OrchardSoundRecording( track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678' ) result = consolidate_rights(osr, service='tiktok') # Two adjacent monetize ranges — same continuous coverage as the original single range. assert len(result.monetization_policies) == 2 assert result.monetization_policies[0].start_date == _dt(2025, 1, 1) assert result.monetization_policies[0].end_date == _dt(2025, 2, 1) assert result.monetization_policies[1].start_date == _dt(2025, 2, 1) assert result.monetization_policies[1].end_date == _dt(2025, 10, 1) assert len(result.block_access_policies) == 1 assert result.block_access_policies[0].start_date == _dt(2025, 2, 1) assert result.block_access_policies[0].end_date == _dt(2025, 2, 1) def test_zero_duration_non_midnight_block_splits_monetize_into_adjacent_pieces(): """Document current behaviour: a zero-duration non-midnight block fragments a monetize. Same issue as the midnight case: the split boundaries land at the block's non-midnight timestamp, producing two adjacent monetize ranges. Monetize US from 2025-01-01T00:00 to 2025-10-01T00:00. Block US from 2025-02-01T10:00 to 2025-02-01T10:00 (zero duration at T10:00). Expected (current buggy behaviour): 1) monetize 2025-01-01T00:00 → 2025-02-01T10:00 2) block 2025-02-01T10:00 → 2025-02-01T10:00 (zero duration) 3) monetize 2025-02-01T10:00 → 2025-10-01T00:00 """ product = _minimal_product( with_subaccount=False, vendor_rules=[ _fingerprint_rule( policy=PolicyType.monetize.value, territory='US', start_date=datetime.fromisoformat('2025-01-01T00:00:00+00:00'), end_date=datetime.fromisoformat('2025-10-01T00:00:00+00:00'), ), _fingerprint_rule( policy=PolicyType.block_access.value, territory='US', start_date=datetime.fromisoformat('2025-02-01T10:00:00+00:00'), end_date=datetime.fromisoformat('2025-02-01T10:00:00+00:00'), ), ], ) track = _minimal_track(product=product) osr = OrchardSoundRecording( track_connection=TrackConnection(tracks=[track]), assets=[], isrc='ISRC12345678' ) result = consolidate_rights(osr, service='tiktok') # Two adjacent monetize ranges — same continuous coverage as the original single range. assert len(result.monetization_policies) == 2 assert result.monetization_policies[0].start_date == datetime.fromisoformat('2025-01-01T00:00:00+00:00') assert result.monetization_policies[0].end_date == datetime.fromisoformat('2025-02-01T10:00:00+00:00') assert result.monetization_policies[1].start_date == datetime.fromisoformat('2025-02-01T10:00:00+00:00') assert result.monetization_policies[1].end_date == datetime.fromisoformat('2025-10-01T00:00:00+00:00') assert len(result.block_access_policies) == 1 assert result.block_access_policies[0].start_date == datetime.fromisoformat('2025-02-01T10:00:00+00:00') assert result.block_access_policies[0].end_date == datetime.fromisoformat('2025-02-01T10:00:00+00:00')