@@ -6,6 +6,9 @@ use soroban_sdk::{
66 token, vec, Address , Env ,
77} ;
88
9+ const DAY : u64 = 24 * 60 * 60 ;
10+ const WEEK : u64 = 7 * DAY ;
11+
912fn create_token_contract < ' a > ( env : & Env , admin : & Address ) -> token:: Client < ' a > {
1013 let sac = env. register_stellar_asset_contract_v2 ( admin. clone ( ) ) ;
1114 token:: Client :: new ( env, & sac. address ( ) )
@@ -27,18 +30,29 @@ fn test_subscribe_and_collect() {
2730 let contract_id = env. register ( SubStreamContract , ( ) ) ;
2831 let client = SubStreamContractClient :: new ( & env, & contract_id) ;
2932
33+ let start = 100u64 ;
34+ env. ledger ( ) . set_timestamp ( start) ;
3035 env. ledger ( ) . set_timestamp ( 100 ) ;
3136 client. subscribe ( & subscriber, & creator, & token. address , & 100 , & 2 ) ;
3237
3338 assert_eq ! ( token. balance( & subscriber) , 900 ) ;
3439 assert_eq ! ( token. balance( & contract_id) , 100 ) ;
3540
41+ // Still inside trial: no charges.
42+ env. ledger ( ) . set_timestamp ( start + 10 ) ;
43+ client. collect ( & subscriber, & creator) ;
44+ assert_eq ! ( token. balance( & creator) , 0 ) ;
45+
46+ // 10 paid seconds after trial.
47+ env. ledger ( ) . set_timestamp ( start + WEEK + 10 ) ;
3648 env. ledger ( ) . set_timestamp ( 110 ) ;
3749 client. collect ( & subscriber, & creator) ;
3850
3951 assert_eq ! ( token. balance( & creator) , 20 ) ;
4052 assert_eq ! ( token. balance( & contract_id) , 80 ) ;
4153
54+ // Additional 50 paid seconds, capped by remaining balance.
55+ env. ledger ( ) . set_timestamp ( start + WEEK + 60 ) ;
4256 env. ledger ( ) . set_timestamp ( 160 ) ;
4357 client. collect ( & subscriber, & creator) ;
4458
@@ -47,6 +61,37 @@ fn test_subscribe_and_collect() {
4761 assert_eq ! ( client. get_total_streamed( & subscriber, & creator) , 100 ) ;
4862}
4963
64+ #[ test]
65+ fn test_free_trial_ignores_claims_within_first_week( ) {
66+ #[ should_panic( expected = "cannot cancel stream: minimum duration not met" ) ]
67+ fn test_cancel_before_minimum_duration ( ) {
68+ let env = Env :: default ( ) ;
69+ env. mock_all_auths ( ) ;
70+
71+ let subscriber = Address :: generate ( & env) ;
72+ let creator = Address :: generate ( & env) ;
73+ let admin = Address :: generate ( & env) ;
74+
75+ let token = create_token_contract ( & env, & admin) ;
76+ let token_admin = token:: StellarAssetClient :: new ( & env, & token. address ) ;
77+ token_admin. mint ( & subscriber, & 1000 ) ;
78+
79+ let contract_id = env. register ( SubStreamContract , ( ) ) ;
80+ let client = SubStreamContractClient :: new ( & env, & contract_id) ;
81+
82+ let start = 100u64 ;
83+ env. ledger ( ) . set_timestamp ( start) ;
84+ client. subscribe ( & subscriber, & creator, & token. address , & 300 , & 3 ) ;
85+
86+ env. ledger ( ) . set_timestamp ( start + WEEK - 1 ) ;
87+ client. collect ( & subscriber, & creator) ;
88+ assert_eq ! ( token. balance( & creator) , 0 ) ;
89+
90+ env. ledger ( ) . set_timestamp ( start + WEEK + 9 ) ;
91+ client. collect ( & subscriber, & creator) ;
92+ assert_eq ! ( token. balance( & creator) , 27 ) ;
93+ }
94+
5095#[ test]
5196#[ should_panic( expected = "cannot cancel stream: minimum duration not met" ) ]
5297fn test_cancel_before_minimum_duration ( ) {
@@ -87,6 +132,16 @@ fn test_cancel_after_minimum_duration() {
87132 let contract_id = env. register ( SubStreamContract , ( ) ) ;
88133 let client = SubStreamContractClient :: new ( & env, & contract_id) ;
89134
135+ let start = 100u64 ;
136+ env. ledger ( ) . set_timestamp ( start) ;
137+ client. subscribe ( & subscriber, & creator, & token. address , & 100 , & 1 ) ;
138+
139+ // Minimum duration has passed, but still inside free trial.
140+ env. ledger ( ) . set_timestamp ( start + DAY + 10 ) ;
141+ client. cancel ( & subscriber, & creator) ;
142+
143+ assert_eq ! ( token. balance( & creator) , 0 ) ;
144+ assert_eq ! ( token. balance( & subscriber) , 1000 ) ;
90145 env. ledger ( ) . set_timestamp ( 100 ) ;
91146 client. subscribe ( & subscriber, & creator, & token. address , & 100 , & 1 ) ;
92147
@@ -118,6 +173,7 @@ fn test_top_up() {
118173 client. subscribe ( & subscriber, & creator, & token. address , & 100 , & 1 ) ;
119174 client. top_up ( & subscriber, & creator, & 50 ) ;
120175
176+ env. ledger ( ) . set_timestamp ( WEEK + 120 ) ;
121177 env. ledger ( ) . set_timestamp ( 120 ) ;
122178 client. collect ( & subscriber, & creator) ;
123179
@@ -156,6 +212,8 @@ fn test_group_subscribe_and_collect_split() {
156212 ] ;
157213 let percentages = vec ! [ & env, 40u32 , 25u32 , 15u32 , 10u32 , 10u32 ] ;
158214
215+ let start = 100u64 ;
216+ env. ledger ( ) . set_timestamp ( start) ;
159217 env. ledger ( ) . set_timestamp ( 100 ) ;
160218 client. subscribe_group (
161219 & subscriber,
@@ -167,6 +225,7 @@ fn test_group_subscribe_and_collect_split() {
167225 & percentages,
168226 ) ;
169227
228+ env. ledger ( ) . set_timestamp ( start + WEEK + 10 ) ;
170229 env. ledger ( ) . set_timestamp ( 110 ) ;
171230 client. collect_group ( & subscriber, & channel_id) ;
172231
@@ -235,6 +294,21 @@ fn test_pause_channel_blocks_charges_and_unpause_resumes() {
235294 let contract_id = env. register ( SubStreamContract , ( ) ) ;
236295 let client = SubStreamContractClient :: new ( & env, & contract_id) ;
237296
297+ let start = 100u64 ;
298+ env. ledger ( ) . set_timestamp ( start) ;
299+ client. subscribe ( & subscriber, & creator, & token. address , & 300 , & 2 ) ;
300+
301+ env. ledger ( ) . set_timestamp ( start + WEEK + 10 ) ;
302+ client. collect ( & subscriber, & creator) ;
303+ assert_eq ! ( token. balance( & creator) , 20 ) ;
304+
305+ env. ledger ( ) . set_timestamp ( start + WEEK + 20 ) ;
306+ client. pause_channel ( & creator) ;
307+ assert ! ( client. is_channel_paused( & creator) ) ;
308+ assert_eq ! ( token. balance( & creator) , 40 ) ;
309+
310+ env. ledger ( ) . set_timestamp ( start + WEEK + 100 ) ;
311+ client. collect ( & subscriber, & creator) ;
238312 env. ledger ( ) . set_timestamp ( 100 ) ;
239313 client. subscribe ( & subscriber, & creator, & token. address , & 300 , & 2 ) ;
240314
@@ -256,6 +330,7 @@ fn test_pause_channel_blocks_charges_and_unpause_resumes() {
256330 client. unpause_channel ( & creator) ;
257331 assert ! ( !client. is_channel_paused( & creator) ) ;
258332
333+ env. ledger ( ) . set_timestamp ( start + WEEK + 110 ) ;
259334 env. ledger ( ) . set_timestamp ( 210 ) ;
260335 client. collect ( & subscriber, & creator) ;
261336 assert_eq ! ( token. balance( & creator) , 60 ) ;
@@ -280,6 +355,21 @@ fn test_pause_channel_applies_to_all_subscribers() {
280355 let contract_id = env. register ( SubStreamContract , ( ) ) ;
281356 let client = SubStreamContractClient :: new ( & env, & contract_id) ;
282357
358+ let start = 100u64 ;
359+ env. ledger ( ) . set_timestamp ( start) ;
360+ client. subscribe ( & subscriber_1, & creator, & token. address , & 200 , & 1 ) ;
361+ client. subscribe ( & subscriber_2, & creator, & token. address , & 200 , & 1 ) ;
362+
363+ env. ledger ( ) . set_timestamp ( start + WEEK + 30 ) ;
364+ client. pause_channel ( & creator) ;
365+ assert_eq ! ( token. balance( & creator) , 60 ) ;
366+
367+ env. ledger ( ) . set_timestamp ( start + WEEK + 130 ) ;
368+ client. unpause_channel ( & creator) ;
369+
370+ env. ledger ( ) . set_timestamp ( start + WEEK + 140 ) ;
371+ let total = client. withdraw_all ( & creator, & 10 ) ;
372+
283373 env. ledger ( ) . set_timestamp ( 100 ) ;
284374 client. subscribe ( & subscriber_1, & creator, & token. address , & 200 , & 1 ) ;
285375 client. subscribe ( & subscriber_2, & creator, & token. address , & 200 , & 1 ) ;
@@ -318,6 +408,12 @@ fn test_cliff_threshold_access() {
318408
319409 client. set_cliff_threshold ( & creator, & 50 ) ;
320410
411+ let start = 100u64 ;
412+ env. ledger ( ) . set_timestamp ( start) ;
413+ client. subscribe ( & subscriber, & creator, & token. address , & 100 , & 1 ) ;
414+
415+ env. ledger ( ) . set_timestamp ( start + WEEK + 30 ) ;
416+
321417 env. ledger ( ) . set_timestamp ( 100 ) ;
322418 client. subscribe ( & subscriber, & creator, & token. address , & 100 , & 1 ) ;
323419
@@ -326,12 +422,38 @@ fn test_cliff_threshold_access() {
326422 assert ! ( !client. has_unlocked_access( & subscriber, & creator) ) ;
327423 assert_eq ! ( client. get_access_tier( & subscriber, & creator) , 0 ) ;
328424
425+ env. ledger ( ) . set_timestamp ( start + WEEK + 50 ) ;
329426 env. ledger ( ) . set_timestamp ( 150 ) ;
330427 client. collect ( & subscriber, & creator) ;
331428 assert ! ( client. has_unlocked_access( & subscriber, & creator) ) ;
332429 assert_eq ! ( client. get_access_tier( & subscriber, & creator) , 1 ) ;
333430}
334431
432+ #[ test]
433+ fn test_migrate_tier_downgrade_prorates_refund( ) {
434+ let env = Env :: default ( ) ;
435+ env. mock_all_auths ( ) ;
436+
437+ let subscriber = Address :: generate ( & env) ;
438+ let creator = Address :: generate ( & env) ;
439+ let admin = Address :: generate ( & env) ;
440+
441+ let token = create_token_contract ( & env, & admin) ;
442+ let token_admin = token:: StellarAssetClient :: new ( & env, & token. address ) ;
443+ token_admin. mint ( & subscriber, & 1000 ) ;
444+
445+ let contract_id = env. register ( SubStreamContract , ( ) ) ;
446+ let client = SubStreamContractClient :: new ( & env, & contract_id) ;
447+
448+ let start = 100u64 ;
449+ env. ledger ( ) . set_timestamp ( start) ;
450+ client. subscribe ( & subscriber, & creator, & token. address , & 100 , & 10 ) ;
451+
452+ env. ledger ( ) . set_timestamp ( start + WEEK + 5 ) ;
453+ client. migrate_tier ( & subscriber, & creator, & 5 , & 0 ) ;
454+
455+ assert_eq ! ( token. balance( & creator) , 50 ) ;
456+
335457#[ test]
336458fn test_migrate_tier_downgrade_prorates_refund ( ) {
337459 let env = Env :: default ( ) ;
0 commit comments