1
1
use crate :: db:: {
2
+ accounting:: { get_all_accountings_for_channel, Side } ,
2
3
event_aggregate:: { latest_approve_state_v5, latest_heartbeats, latest_new_state_v5} ,
3
4
insert_channel, insert_validator_messages, list_channels,
4
5
spendable:: { fetch_spendable, get_all_spendables_for_channel, update_spendable} ,
@@ -9,11 +10,11 @@ use futures::future::try_join_all;
9
10
use hyper:: { Body , Request , Response } ;
10
11
use primitives:: {
11
12
adapter:: Adapter ,
12
- balances:: { CheckedState , UncheckedState } ,
13
+ balances:: { Balances , CheckedState , UncheckedState } ,
13
14
config:: TokenInfo ,
14
15
sentry:: {
15
- channel_list:: ChannelListQuery , AllSpendersResponse , LastApproved , LastApprovedQuery ,
16
- LastApprovedResponse , Pagination , SpenderResponse , SuccessResponse ,
16
+ channel_list:: ChannelListQuery , AccountingResponse , AllSpendersResponse , LastApproved ,
17
+ LastApprovedQuery , LastApprovedResponse , Pagination , SpenderResponse , SuccessResponse ,
17
18
} ,
18
19
spender:: { Spendable , Spender , SpenderLeaf } ,
19
20
validator:: { MessageTypes , NewState } ,
@@ -341,13 +342,54 @@ async fn get_corresponding_new_state(
341
342
new_state
342
343
}
343
344
345
+ pub async fn get_accounting_for_channel < A : Adapter + ' static > (
346
+ req : Request < Body > ,
347
+ app : & Application < A > ,
348
+ ) -> Result < Response < Body > , ResponseError > {
349
+ let channel = req
350
+ . extensions ( )
351
+ . get :: < Channel > ( )
352
+ . expect ( "Request should have Channel" )
353
+ . to_owned ( ) ;
354
+
355
+ let accountings = get_all_accountings_for_channel ( app. pool . clone ( ) , channel. id ( ) ) . await ?;
356
+
357
+ let mut unchecked_balances: Balances < UncheckedState > = Balances :: default ( ) ;
358
+
359
+ for accounting in accountings {
360
+ match accounting. side {
361
+ Side :: Earner => unchecked_balances
362
+ . earners
363
+ . insert ( accounting. address , accounting. amount ) ,
364
+ Side :: Spender => unchecked_balances
365
+ . spenders
366
+ . insert ( accounting. address , accounting. amount ) ,
367
+ } ;
368
+ }
369
+
370
+ let balances = match unchecked_balances. check ( ) {
371
+ Ok ( balances) => balances,
372
+ Err ( error) => {
373
+ error ! ( & app. logger, "{}" , & error; "module" => "channel_accounting" ) ;
374
+ return Err ( ResponseError :: FailedValidation (
375
+ "Earners sum is not equal to spenders sum for channel" . to_string ( ) ,
376
+ ) ) ;
377
+ }
378
+ } ;
379
+
380
+ let res = AccountingResponse :: < CheckedState > { balances } ;
381
+ Ok ( success_response ( serde_json:: to_string ( & res) ?) )
382
+ }
383
+
344
384
#[ cfg( test) ]
345
385
mod test {
346
386
use super :: * ;
387
+ use crate :: db:: { accounting:: spend_amount, insert_channel} ;
347
388
use crate :: test_util:: setup_dummy_app;
389
+ use hyper:: StatusCode ;
348
390
use primitives:: {
349
391
adapter:: Deposit ,
350
- util:: tests:: prep_db:: { ADDRESSES , DUMMY_CAMPAIGN } ,
392
+ util:: tests:: prep_db:: { ADDRESSES , DUMMY_CAMPAIGN , IDS } ,
351
393
BigNum ,
352
394
} ;
353
395
@@ -433,4 +475,122 @@ mod test {
433
475
) ;
434
476
assert_eq ! ( updated_spendable. spender, ADDRESSES [ "creator" ] ) ;
435
477
}
478
+
479
+ async fn res_to_accounting_response ( res : Response < Body > ) -> AccountingResponse < CheckedState > {
480
+ let json = hyper:: body:: to_bytes ( res. into_body ( ) )
481
+ . await
482
+ . expect ( "Should get json" ) ;
483
+
484
+ let accounting_response: AccountingResponse < CheckedState > =
485
+ serde_json:: from_slice ( & json) . expect ( "Should get AccouuntingResponse" ) ;
486
+ accounting_response
487
+ }
488
+
489
+ #[ tokio:: test]
490
+ async fn get_accountings_for_channel ( ) {
491
+ let app = setup_dummy_app ( ) . await ;
492
+ let channel = DUMMY_CAMPAIGN . channel . clone ( ) ;
493
+ insert_channel ( & app. pool , channel)
494
+ . await
495
+ . expect ( "should insert channel" ) ;
496
+ let build_request = |channel : Channel | {
497
+ Request :: builder ( )
498
+ . extension ( channel)
499
+ . body ( Body :: empty ( ) )
500
+ . expect ( "Should build Request" )
501
+ } ;
502
+ // Testing for no accounting yet
503
+ {
504
+ let res = get_accounting_for_channel ( build_request ( channel. clone ( ) ) , & app)
505
+ . await
506
+ . expect ( "should get response" ) ;
507
+ assert_eq ! ( StatusCode :: OK , res. status( ) ) ;
508
+
509
+ let accounting_response = res_to_accounting_response ( res) . await ;
510
+ assert_eq ! ( accounting_response. balances. earners. len( ) , 0 ) ;
511
+ assert_eq ! ( accounting_response. balances. spenders. len( ) , 0 ) ;
512
+ }
513
+
514
+ // Testing for 2 accountings - first channel
515
+ {
516
+ let mut balances = Balances :: < CheckedState > :: new ( ) ;
517
+ balances
518
+ . spend (
519
+ ADDRESSES [ "creator" ] ,
520
+ ADDRESSES [ "publisher" ] ,
521
+ UnifiedNum :: from_u64 ( 200 ) ,
522
+ )
523
+ . expect ( "should not overflow" ) ;
524
+ balances
525
+ . spend (
526
+ ADDRESSES [ "tester" ] ,
527
+ ADDRESSES [ "publisher2" ] ,
528
+ UnifiedNum :: from_u64 ( 100 ) ,
529
+ )
530
+ . expect ( "Should not overflow" ) ;
531
+ spend_amount ( app. pool . clone ( ) , channel. id ( ) , balances. clone ( ) )
532
+ . await
533
+ . expect ( "should spend" ) ;
534
+
535
+ let res = get_accounting_for_channel ( build_request ( channel. clone ( ) ) , & app)
536
+ . await
537
+ . expect ( "should get response" ) ;
538
+ assert_eq ! ( StatusCode :: OK , res. status( ) ) ;
539
+
540
+ let accounting_response = res_to_accounting_response ( res) . await ;
541
+
542
+ assert_eq ! ( balances, accounting_response. balances) ;
543
+ }
544
+
545
+ // Testing for 2 accountings - second channel (same address is both an earner and a spender)
546
+ {
547
+ let mut second_channel = DUMMY_CAMPAIGN . channel . clone ( ) ;
548
+ second_channel. leader = IDS [ "user" ] ; // channel.id() will be different now
549
+ insert_channel ( & app. pool , second_channel)
550
+ . await
551
+ . expect ( "should insert channel" ) ;
552
+
553
+ let mut balances = Balances :: < CheckedState > :: new ( ) ;
554
+ balances
555
+ . spend ( ADDRESSES [ "tester" ] , ADDRESSES [ "publisher" ] , 300 . into ( ) )
556
+ . expect ( "Should not overflow" ) ;
557
+
558
+ balances
559
+ . spend ( ADDRESSES [ "publisher" ] , ADDRESSES [ "user" ] , 300 . into ( ) )
560
+ . expect ( "Should not overflow" ) ;
561
+
562
+ spend_amount ( app. pool . clone ( ) , second_channel. id ( ) , balances. clone ( ) )
563
+ . await
564
+ . expect ( "should spend" ) ;
565
+
566
+ let res = get_accounting_for_channel ( build_request ( second_channel. clone ( ) ) , & app)
567
+ . await
568
+ . expect ( "should get response" ) ;
569
+ assert_eq ! ( StatusCode :: OK , res. status( ) ) ;
570
+
571
+ let accounting_response = res_to_accounting_response ( res) . await ;
572
+
573
+ assert_eq ! ( balances, accounting_response. balances)
574
+ }
575
+
576
+ // Testing for when sums don't match on first channel - Error case
577
+ {
578
+ let mut balances = Balances :: < CheckedState > :: new ( ) ;
579
+ balances
580
+ . earners
581
+ . insert ( ADDRESSES [ "publisher" ] , UnifiedNum :: from_u64 ( 100 ) ) ;
582
+ balances
583
+ . spenders
584
+ . insert ( ADDRESSES [ "creator" ] , UnifiedNum :: from_u64 ( 200 ) ) ;
585
+ spend_amount ( app. pool . clone ( ) , channel. id ( ) , balances)
586
+ . await
587
+ . expect ( "should spend" ) ;
588
+
589
+ let res = get_accounting_for_channel ( build_request ( channel. clone ( ) ) , & app) . await ;
590
+ let expected = ResponseError :: FailedValidation (
591
+ "Earners sum is not equal to spenders sum for channel" . to_string ( ) ,
592
+ ) ;
593
+ assert_eq ! ( expected, res. expect_err( "Should return an error" ) ) ;
594
+ }
595
+ }
436
596
}
0 commit comments