From 1538197cf02e6cb56922619d771fa10bcf92dadd Mon Sep 17 00:00:00 2001 From: simzzz Date: Mon, 20 Sep 2021 17:30:23 +0300 Subject: [PATCH 1/8] initial commit --- sentry/src/lib.rs | 16 +++++++++++++++- sentry/src/routes/channel.rs | 5 +++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/sentry/src/lib.rs b/sentry/src/lib.rs index aba3fee95..a99a6fc5c 100644 --- a/sentry/src/lib.rs +++ b/sentry/src/lib.rs @@ -27,7 +27,7 @@ use routes::campaign::{create_campaign, update_campaign}; use routes::cfg::config; use routes::channel::{ channel_list, channel_validate, create_channel, create_validator_messages, - get_all_spender_limits, get_spender_limits, last_approved, + get_all_spender_limits, get_spender_limits, last_approved, get_accounting_for_channel }; use slog::Logger; use std::collections::HashMap; @@ -80,6 +80,9 @@ static CHANNEL_ALL_SPENDER_LIMITS: Lazy = Lazy::new(|| { Regex::new(r"^/v5/channel/0x([a-zA-Z0-9]{64})/spender/all/?$") .expect("The regex should be valid") }); +static CHANNEL_ACCOUNTING: Lazy = Lazy::new(|| { + Regex::new(r"^/v5/channel/0x([a-zA-Z0-9]{64})/accounting/?$").expect("The regex should be valid") +}); #[derive(Debug, Clone)] pub struct RouteParams(pub Vec); @@ -411,6 +414,17 @@ async fn channels_router( .await?; get_all_spender_limits(req, app).await + } else if let (Some(caps), &Method::GET) = (CHANNEL_ACCOUNTING.captures(&path), method) { + let param = RouteParams(vec![caps.get(1).map_or("".to_string(), |m| m.as_str().to_string())]); + req.extensions_mut().insert(param); + + req = Chain::new() + .chain(AuthRequired) + .chain(ChannelLoad) + .apply(req, app) + .await?; + + get_accounting_for_channel(req, app).await } else { Err(ResponseError::NotFound) } diff --git a/sentry/src/routes/channel.rs b/sentry/src/routes/channel.rs index 7a91d2aa7..76dfd55b5 100644 --- a/sentry/src/routes/channel.rs +++ b/sentry/src/routes/channel.rs @@ -419,6 +419,11 @@ async fn get_corresponding_new_state( new_state } +pub async fn get_accounting_for_channel(req: Request, app: &Application) -> Result, ResponseError> { + + Ok(Default::default()) // TODO +} + #[cfg(test)] mod test { use super::*; From add0a42a950f9bea2fd51ab796d0efe80d977915 Mon Sep 17 00:00:00 2001 From: simzzz Date: Mon, 27 Sep 2021 17:06:55 +0300 Subject: [PATCH 2/8] comitting progress --- sentry/src/db/accounting.rs | 15 +++++++++++++++ sentry/src/routes/channel.rs | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/sentry/src/db/accounting.rs b/sentry/src/db/accounting.rs index 63bc6a0f0..71101810e 100644 --- a/sentry/src/db/accounting.rs +++ b/sentry/src/db/accounting.rs @@ -83,6 +83,21 @@ pub async fn get_accounting( Ok(row.as_ref().map(Accounting::from)) } +pub async fn get_all_accountings_for_channel( + pool: DbPool, + channel_id: ChannelId, + side: Side, +) -> Result, PoolError> { + let client = pool.get().await?; + let statement = client.prepare("SELECT channel_id, side, address, amount, updated, created FROM accounting WHERE channel_id = $1 AND side = $2").await?; + + let rows = client.query(&statement, &[&channel_id, &side]).await?; + + let accountings = rows.iter().map(Accounting::from).collect(); + + Ok(accountings) +} + /// Will update current Spender/Earner amount or insert a new Accounting record /// /// See `UPDATE_ACCOUNTING_STATEMENT` static for full query. diff --git a/sentry/src/routes/channel.rs b/sentry/src/routes/channel.rs index a014efc52..56e73dd2a 100644 --- a/sentry/src/routes/channel.rs +++ b/sentry/src/routes/channel.rs @@ -1,5 +1,6 @@ use crate::db::{ event_aggregate::{latest_approve_state_v5, latest_heartbeats, latest_new_state_v5}, + accounting::{get_all_accountings_for_channel, Side}, insert_channel, insert_validator_messages, list_channels, spendable::{fetch_spendable, get_all_spendables_for_channel, update_spendable}, DbPool, PoolError, @@ -9,17 +10,17 @@ use futures::future::try_join_all; use hyper::{Body, Request, Response}; use primitives::{ adapter::Adapter, - balances::{CheckedState, UncheckedState}, + balances::{CheckedState, UncheckedState, Balances}, channel::Channel as ChannelOld, channel_v5::Channel as ChannelV5, config::TokenInfo, sentry::{ channel_list::ChannelListQuery, AllSpendersResponse, LastApproved, LastApprovedQuery, - LastApprovedResponse, Pagination, SpenderResponse, SuccessResponse, + LastApprovedResponse, Pagination, SpenderResponse, SuccessResponse, AccountingResponse }, spender::{Spendable, Spender, SpenderLeaf}, validator::{MessageTypes, NewState}, - Address, Channel, Deposit, UnifiedNum, + Address, Channel, Deposit, UnifiedNum, UnifiedMap }; use slog::{error, Logger}; use std::{collections::HashMap, str::FromStr}; @@ -414,8 +415,33 @@ async fn get_corresponding_new_state( } pub async fn get_accounting_for_channel(req: Request, app: &Application) -> Result, ResponseError> { + let channel = req + .extensions() + .get::() + .expect("Request should have Channel") + .to_owned(); + + // TODO: Pagination + let earner_accountings = get_all_accountings_for_channel(app.pool.clone(), channel.id(), Side::Earner).await?; + let spender_accountings = get_all_accountings_for_channel(app.pool.clone(), channel.id(), Side::Spender).await?; + + let mut balances = Balances::::new(); + for accounting in earner_accountings { + balances.earners.insert(accounting.address, accounting.amount); + } + + for accounting in spender_accountings { + balances.spenders.insert(accounting.address, accounting.amount); + } + - Ok(Default::default()) // TODO + // TODO: check sums + let balances = balances.check().ok_or(panic!("Fatal error!")); + + let res = AccountingResponse:: { + balances, + }; + Ok(success_response(serde_json::to_string(&res)?)) } #[cfg(test)] From dc2c859d4ec11bb0ca90009e718baf427094b0f6 Mon Sep 17 00:00:00 2001 From: simzzz Date: Tue, 28 Sep 2021 13:13:46 +0300 Subject: [PATCH 3/8] initial route complete --- sentry/src/lib.rs | 11 +++++---- sentry/src/routes/channel.rs | 43 +++++++++++++++++++++--------------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/sentry/src/lib.rs b/sentry/src/lib.rs index 59c87ef74..9bb5561ae 100644 --- a/sentry/src/lib.rs +++ b/sentry/src/lib.rs @@ -25,8 +25,8 @@ use { campaign::{create_campaign, update_campaign}, cfg::config, channel::{ - channel_list, create_validator_messages, get_all_spender_limits, get_spender_limits, - last_approved, get_accounting_for_channel + channel_list, create_validator_messages, get_accounting_for_channel, + get_all_spender_limits, get_spender_limits, last_approved, }, event_aggregate::list_channel_event_aggregates, validator_message::{extract_params, list_validator_messages}, @@ -82,7 +82,8 @@ static CHANNEL_ALL_SPENDER_LIMITS: Lazy = Lazy::new(|| { .expect("The regex should be valid") }); static CHANNEL_ACCOUNTING: Lazy = Lazy::new(|| { - Regex::new(r"^/v5/channel/0x([a-zA-Z0-9]{64})/accounting/?$").expect("The regex should be valid") + Regex::new(r"^/v5/channel/0x([a-zA-Z0-9]{64})/accounting/?$") + .expect("The regex should be valid") }); #[derive(Debug, Clone)] @@ -394,7 +395,9 @@ async fn channels_router( get_all_spender_limits(req, app).await } else if let (Some(caps), &Method::GET) = (CHANNEL_ACCOUNTING.captures(&path), method) { - let param = RouteParams(vec![caps.get(1).map_or("".to_string(), |m| m.as_str().to_string())]); + let param = RouteParams(vec![caps + .get(1) + .map_or("".to_string(), |m| m.as_str().to_string())]); req.extensions_mut().insert(param); req = Chain::new() diff --git a/sentry/src/routes/channel.rs b/sentry/src/routes/channel.rs index e8fdc4cf6..683ce5ca8 100644 --- a/sentry/src/routes/channel.rs +++ b/sentry/src/routes/channel.rs @@ -1,6 +1,6 @@ use crate::db::{ - event_aggregate::{latest_approve_state_v5, latest_heartbeats, latest_new_state_v5}, accounting::{get_all_accountings_for_channel, Side}, + event_aggregate::{latest_approve_state_v5, latest_heartbeats, latest_new_state_v5}, insert_channel, insert_validator_messages, list_channels, spendable::{fetch_spendable, get_all_spendables_for_channel, update_spendable}, DbPool, @@ -10,15 +10,15 @@ use futures::future::try_join_all; use hyper::{Body, Request, Response}; use primitives::{ adapter::Adapter, - balances::{CheckedState, UncheckedState}, + balances::{Balances, CheckedState, UncheckedState}, config::TokenInfo, sentry::{ - channel_list::ChannelListQuery, AllSpendersResponse, LastApproved, LastApprovedQuery, - LastApprovedResponse, Pagination, SpenderResponse, SuccessResponse, AccountingResponse + channel_list::ChannelListQuery, AccountingResponse, AllSpendersResponse, LastApproved, + LastApprovedQuery, LastApprovedResponse, Pagination, SpenderResponse, SuccessResponse, }, spender::{Spendable, Spender, SpenderLeaf}, validator::{MessageTypes, NewState}, - Address, Channel, Deposit, UnifiedNum, UnifiedMap + Address, Channel, Deposit, UnifiedNum, }; use slog::{error, Logger}; use std::{collections::HashMap, str::FromStr}; @@ -342,33 +342,40 @@ async fn get_corresponding_new_state( new_state } -pub async fn get_accounting_for_channel(req: Request, app: &Application) -> Result, ResponseError> { +pub async fn get_accounting_for_channel( + req: Request, + app: &Application, +) -> Result, ResponseError> { let channel = req .extensions() .get::() .expect("Request should have Channel") .to_owned(); - // TODO: Pagination - let earner_accountings = get_all_accountings_for_channel(app.pool.clone(), channel.id(), Side::Earner).await?; - let spender_accountings = get_all_accountings_for_channel(app.pool.clone(), channel.id(), Side::Spender).await?; + let earner_accountings = + get_all_accountings_for_channel(app.pool.clone(), channel.id(), Side::Earner).await?; + let spender_accountings = + get_all_accountings_for_channel(app.pool.clone(), channel.id(), Side::Spender).await?; - let mut balances = Balances::::new(); + let mut unchecked_balances: Balances = Balances::default(); for accounting in earner_accountings { - balances.earners.insert(accounting.address, accounting.amount); + unchecked_balances + .earners + .insert(accounting.address, accounting.amount); } for accounting in spender_accountings { - balances.spenders.insert(accounting.address, accounting.amount); + unchecked_balances + .spenders + .insert(accounting.address, accounting.amount); } - - // TODO: check sums - let balances = balances.check().ok_or(panic!("Fatal error!")); - - let res = AccountingResponse:: { - balances, + let balances = match unchecked_balances.check() { + Ok(balances) => balances, + Err(_) => panic!("Fatal error!"), }; + + let res = AccountingResponse:: { balances }; Ok(success_response(serde_json::to_string(&res)?)) } From 9d880761fc3cb9ffaff5186cd519fff7cb0d0873 Mon Sep 17 00:00:00 2001 From: simzzz Date: Tue, 28 Sep 2021 13:26:56 +0300 Subject: [PATCH 4/8] removed panic --- sentry/src/routes/channel.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sentry/src/routes/channel.rs b/sentry/src/routes/channel.rs index 683ce5ca8..5e79ad0fc 100644 --- a/sentry/src/routes/channel.rs +++ b/sentry/src/routes/channel.rs @@ -372,7 +372,10 @@ pub async fn get_accounting_for_channel( let balances = match unchecked_balances.check() { Ok(balances) => balances, - Err(_) => panic!("Fatal error!"), + Err(error) => { + error!(&app.logger, "{}", &error; "module" => "channel_accounting"); + return Err(ResponseError::FailedValidation("Earners sum is not equal to spenders sum for channel".to_string())) + }, }; let res = AccountingResponse:: { balances }; From 895aa905c42fc71823359a85690cc5099d36d17b Mon Sep 17 00:00:00 2001 From: simzzz Date: Tue, 28 Sep 2021 13:31:50 +0300 Subject: [PATCH 5/8] changed it to be all in the same request --- sentry/src/db/accounting.rs | 5 ++--- sentry/src/routes/channel.rs | 29 ++++++++++++++--------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/sentry/src/db/accounting.rs b/sentry/src/db/accounting.rs index 71101810e..595e7a8d1 100644 --- a/sentry/src/db/accounting.rs +++ b/sentry/src/db/accounting.rs @@ -86,12 +86,11 @@ pub async fn get_accounting( pub async fn get_all_accountings_for_channel( pool: DbPool, channel_id: ChannelId, - side: Side, ) -> Result, PoolError> { let client = pool.get().await?; - let statement = client.prepare("SELECT channel_id, side, address, amount, updated, created FROM accounting WHERE channel_id = $1 AND side = $2").await?; + let statement = client.prepare("SELECT channel_id, side, address, amount, updated, created FROM accounting WHERE channel_id = $1").await?; - let rows = client.query(&statement, &[&channel_id, &side]).await?; + let rows = client.query(&statement, &[&channel_id]).await?; let accountings = rows.iter().map(Accounting::from).collect(); diff --git a/sentry/src/routes/channel.rs b/sentry/src/routes/channel.rs index 5e79ad0fc..6adefe2d9 100644 --- a/sentry/src/routes/channel.rs +++ b/sentry/src/routes/channel.rs @@ -352,30 +352,29 @@ pub async fn get_accounting_for_channel( .expect("Request should have Channel") .to_owned(); - let earner_accountings = - get_all_accountings_for_channel(app.pool.clone(), channel.id(), Side::Earner).await?; - let spender_accountings = - get_all_accountings_for_channel(app.pool.clone(), channel.id(), Side::Spender).await?; + let accountings = get_all_accountings_for_channel(app.pool.clone(), channel.id()).await?; let mut unchecked_balances: Balances = Balances::default(); - for accounting in earner_accountings { - unchecked_balances - .earners - .insert(accounting.address, accounting.amount); - } - for accounting in spender_accountings { - unchecked_balances - .spenders - .insert(accounting.address, accounting.amount); + for accounting in accountings { + match accounting.side { + Side::Earner => unchecked_balances + .earners + .insert(accounting.address, accounting.amount), + Side::Spender => unchecked_balances + .spenders + .insert(accounting.address, accounting.amount), + }; } let balances = match unchecked_balances.check() { Ok(balances) => balances, Err(error) => { error!(&app.logger, "{}", &error; "module" => "channel_accounting"); - return Err(ResponseError::FailedValidation("Earners sum is not equal to spenders sum for channel".to_string())) - }, + return Err(ResponseError::FailedValidation( + "Earners sum is not equal to spenders sum for channel".to_string(), + )); + } }; let res = AccountingResponse:: { balances }; From 9e7ff25bf52846bc9f0c06fa3cd3e02020d13860 Mon Sep 17 00:00:00 2001 From: simzzz Date: Tue, 28 Sep 2021 20:11:41 +0300 Subject: [PATCH 6/8] added an integration test --- sentry/src/routes/channel.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/sentry/src/routes/channel.rs b/sentry/src/routes/channel.rs index 6adefe2d9..5d0482d8a 100644 --- a/sentry/src/routes/channel.rs +++ b/sentry/src/routes/channel.rs @@ -385,11 +385,13 @@ pub async fn get_accounting_for_channel( mod test { use super::*; use crate::test_util::setup_dummy_app; + use crate::db::{accounting::update_accounting, insert_channel}; use primitives::{ adapter::Deposit, util::tests::prep_db::{ADDRESSES, DUMMY_CAMPAIGN}, BigNum, }; + use hyper::StatusCode; #[tokio::test] async fn create_and_fetch_spendable() { @@ -473,4 +475,35 @@ mod test { ); assert_eq!(updated_spendable.spender, ADDRESSES["creator"]); } + + #[tokio::test] + async fn get_accountings_for_channel() { + let app = setup_dummy_app().await; + let channel = DUMMY_CAMPAIGN.channel.clone(); + insert_channel(&app.pool, channel).await.expect("should insert channel"); + let req = Request::builder().extension(channel).body(Body::empty()).expect("Should build Request"); + + update_accounting(app.pool.clone(), channel.id(), ADDRESSES["publisher"], Side::Earner, UnifiedNum::from_u64(200)).await.expect("should insert accounting"); + update_accounting(app.pool.clone(), channel.id(), ADDRESSES["publisher2"], Side::Earner, UnifiedNum::from_u64(100)).await.expect("should insert accounting"); + update_accounting(app.pool.clone(), channel.id(), ADDRESSES["creator"], Side::Spender, UnifiedNum::from_u64(200)).await.expect("should insert accounting"); + update_accounting(app.pool.clone(), channel.id(), ADDRESSES["tester"], Side::Spender, UnifiedNum::from_u64(100)).await.expect("should insert accounting"); + + let accounting_response = get_accounting_for_channel(req, &app).await.expect("should get response"); + assert_eq!(StatusCode::OK, accounting_response.status()); + let json = hyper::body::to_bytes(accounting_response.into_body()) + .await + .expect("Should get json"); + + let accounting_response: AccountingResponse = + serde_json::from_slice(&json).expect("Should get AccouuntingResponse"); + let sum = accounting_response.balances.sum().expect("shouldn't be None"); + assert_eq!(sum.0, UnifiedNum::from_u64(300)); + assert_eq!(sum.1, UnifiedNum::from_u64(300)); + + update_accounting(app.pool.clone(), channel.id(), ADDRESSES["tester"], Side::Spender, UnifiedNum::from_u64(200)).await.expect("should insert accounting"); + + let new_req = Request::builder().extension(channel).body(Body::empty()).expect("Should build Request"); + let bad_accounting_response = get_accounting_for_channel(new_req, &app).await; + assert!(bad_accounting_response.is_err()) + } } From ae29cbbd9d9b179200dea36b63c5df9febc84063 Mon Sep 17 00:00:00 2001 From: simzzz Date: Wed, 29 Sep 2021 18:44:33 +0300 Subject: [PATCH 7/8] Changes to the test structure --- sentry/src/routes/channel.rs | 94 ++++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 21 deletions(-) diff --git a/sentry/src/routes/channel.rs b/sentry/src/routes/channel.rs index 5d0482d8a..f07823aba 100644 --- a/sentry/src/routes/channel.rs +++ b/sentry/src/routes/channel.rs @@ -385,10 +385,10 @@ pub async fn get_accounting_for_channel( mod test { use super::*; use crate::test_util::setup_dummy_app; - use crate::db::{accounting::update_accounting, insert_channel}; + use crate::db::{accounting::spend_amount, insert_channel}; use primitives::{ adapter::Deposit, - util::tests::prep_db::{ADDRESSES, DUMMY_CAMPAIGN}, + util::tests::prep_db::{ADDRESSES, DUMMY_CAMPAIGN, IDS}, BigNum, }; use hyper::StatusCode; @@ -476,34 +476,86 @@ mod test { assert_eq!(updated_spendable.spender, ADDRESSES["creator"]); } + async fn res_to_accounting_response(res: Response) -> AccountingResponse { + let json = hyper::body::to_bytes(res.into_body()) + .await + .expect("Should get json"); + + let accounting_response: AccountingResponse = + serde_json::from_slice(&json).expect("Should get AccouuntingResponse"); + accounting_response + } + #[tokio::test] async fn get_accountings_for_channel() { let app = setup_dummy_app().await; let channel = DUMMY_CAMPAIGN.channel.clone(); insert_channel(&app.pool, channel).await.expect("should insert channel"); - let req = Request::builder().extension(channel).body(Body::empty()).expect("Should build Request"); + let build_request = |channel: Channel| { + Request::builder() + .extension(channel) + .body(Body::empty()) + .expect("Should build Request") + }; + // Testing for no accounting yet + { + let res = get_accounting_for_channel(build_request(channel.clone()), &app).await.expect("should get response"); + assert_eq!(StatusCode::OK, res.status()); + + let accounting_response = res_to_accounting_response(res).await; + assert_eq!(accounting_response.balances.earners.len(), 0); + assert_eq!(accounting_response.balances.spenders.len(), 0); + } - update_accounting(app.pool.clone(), channel.id(), ADDRESSES["publisher"], Side::Earner, UnifiedNum::from_u64(200)).await.expect("should insert accounting"); - update_accounting(app.pool.clone(), channel.id(), ADDRESSES["publisher2"], Side::Earner, UnifiedNum::from_u64(100)).await.expect("should insert accounting"); - update_accounting(app.pool.clone(), channel.id(), ADDRESSES["creator"], Side::Spender, UnifiedNum::from_u64(200)).await.expect("should insert accounting"); - update_accounting(app.pool.clone(), channel.id(), ADDRESSES["tester"], Side::Spender, UnifiedNum::from_u64(100)).await.expect("should insert accounting"); + // Testing for 2 accountings - first channel + let mut balances = Balances::::new(); + balances.earners.insert(ADDRESSES["publisher"], UnifiedNum::from_u64(200)); + balances.earners.insert(ADDRESSES["publisher2"], UnifiedNum::from_u64(100)); + balances.spenders.insert(ADDRESSES["creator"], UnifiedNum::from_u64(200)); + balances.spenders.insert(ADDRESSES["tester"], UnifiedNum::from_u64(100)); + spend_amount(app.pool.clone(), channel.id(), balances.clone()).await.expect("should spend"); + { + let res = get_accounting_for_channel(build_request(channel.clone()), &app).await.expect("should get response"); + assert_eq!(StatusCode::OK, res.status()); + + let accounting_response = res_to_accounting_response(res).await; + + let sum = accounting_response.balances.sum().expect("shouldn't overflow"); + assert_eq!(sum.0, UnifiedNum::from_u64(300)); + assert_eq!(sum.1, UnifiedNum::from_u64(300)); + } - let accounting_response = get_accounting_for_channel(req, &app).await.expect("should get response"); - assert_eq!(StatusCode::OK, accounting_response.status()); - let json = hyper::body::to_bytes(accounting_response.into_body()) - .await - .expect("Should get json"); + // Testing for 2 accountings - second channel (same address is both an earner and a spender) + let mut second_channel = DUMMY_CAMPAIGN.channel.clone(); + second_channel.leader = IDS["user"]; // channel.id() will be different now + insert_channel(&app.pool, second_channel).await.expect("should insert channel"); - let accounting_response: AccountingResponse = - serde_json::from_slice(&json).expect("Should get AccouuntingResponse"); - let sum = accounting_response.balances.sum().expect("shouldn't be None"); - assert_eq!(sum.0, UnifiedNum::from_u64(300)); - assert_eq!(sum.1, UnifiedNum::from_u64(300)); + let mut balances = Balances::::new(); + balances.earners.insert(ADDRESSES["publisher"], UnifiedNum::from_u64(300)); + balances.spenders.insert(ADDRESSES["publisher"], UnifiedNum::from_u64(300)); + spend_amount(app.pool.clone(), second_channel.id(), balances).await.expect("should spend"); - update_accounting(app.pool.clone(), channel.id(), ADDRESSES["tester"], Side::Spender, UnifiedNum::from_u64(200)).await.expect("should insert accounting"); + insert_channel(&app.pool, second_channel).await.expect("should insert channel"); + { + let res = get_accounting_for_channel(build_request(second_channel.clone()), &app).await.expect("should get response"); + assert_eq!(StatusCode::OK, res.status()); - let new_req = Request::builder().extension(channel).body(Body::empty()).expect("Should build Request"); - let bad_accounting_response = get_accounting_for_channel(new_req, &app).await; - assert!(bad_accounting_response.is_err()) + let accounting_response = res_to_accounting_response(res).await; + + let sum = accounting_response.balances.sum().expect("shouldn't overflow"); + assert_eq!(sum.0, UnifiedNum::from_u64(300)); + assert_eq!(sum.1, UnifiedNum::from_u64(300)); + } + // Testing for when sums dont match - Error case + let mut balances = Balances::::new(); + balances.earners.insert(ADDRESSES["publisher"], UnifiedNum::from_u64(100)); + balances.spenders.insert(ADDRESSES["creator"], UnifiedNum::from_u64(200)); + spend_amount(app.pool.clone(), channel.id(), balances).await.expect("should spend"); + { + let res = get_accounting_for_channel(build_request(channel.clone()), &app).await; + assert!(res.is_err()); + assert!(matches!(res, Err(ResponseError::FailedValidation(_)))); + + } } } From 63e1a548c90d7121519ca28ffe5933b55bb90cd4 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Thu, 30 Sep 2021 10:06:05 +0300 Subject: [PATCH 8/8] sentry - routes - channel - clean up tests --- sentry/src/routes/channel.rs | 107 +++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 36 deletions(-) diff --git a/sentry/src/routes/channel.rs b/sentry/src/routes/channel.rs index f07823aba..c163d2a5d 100644 --- a/sentry/src/routes/channel.rs +++ b/sentry/src/routes/channel.rs @@ -384,14 +384,14 @@ pub async fn get_accounting_for_channel( #[cfg(test)] mod test { use super::*; - use crate::test_util::setup_dummy_app; use crate::db::{accounting::spend_amount, insert_channel}; + use crate::test_util::setup_dummy_app; + use hyper::StatusCode; use primitives::{ adapter::Deposit, util::tests::prep_db::{ADDRESSES, DUMMY_CAMPAIGN, IDS}, BigNum, }; - use hyper::StatusCode; #[tokio::test] async fn create_and_fetch_spendable() { @@ -490,7 +490,9 @@ mod test { async fn get_accountings_for_channel() { let app = setup_dummy_app().await; let channel = DUMMY_CAMPAIGN.channel.clone(); - insert_channel(&app.pool, channel).await.expect("should insert channel"); + insert_channel(&app.pool, channel) + .await + .expect("should insert channel"); let build_request = |channel: Channel| { Request::builder() .extension(channel) @@ -499,7 +501,9 @@ mod test { }; // Testing for no accounting yet { - let res = get_accounting_for_channel(build_request(channel.clone()), &app).await.expect("should get response"); + let res = get_accounting_for_channel(build_request(channel.clone()), &app) + .await + .expect("should get response"); assert_eq!(StatusCode::OK, res.status()); let accounting_response = res_to_accounting_response(res).await; @@ -508,54 +512,85 @@ mod test { } // Testing for 2 accountings - first channel - let mut balances = Balances::::new(); - balances.earners.insert(ADDRESSES["publisher"], UnifiedNum::from_u64(200)); - balances.earners.insert(ADDRESSES["publisher2"], UnifiedNum::from_u64(100)); - balances.spenders.insert(ADDRESSES["creator"], UnifiedNum::from_u64(200)); - balances.spenders.insert(ADDRESSES["tester"], UnifiedNum::from_u64(100)); - spend_amount(app.pool.clone(), channel.id(), balances.clone()).await.expect("should spend"); { - let res = get_accounting_for_channel(build_request(channel.clone()), &app).await.expect("should get response"); + let mut balances = Balances::::new(); + balances + .spend( + ADDRESSES["creator"], + ADDRESSES["publisher"], + UnifiedNum::from_u64(200), + ) + .expect("should not overflow"); + balances + .spend( + ADDRESSES["tester"], + ADDRESSES["publisher2"], + UnifiedNum::from_u64(100), + ) + .expect("Should not overflow"); + spend_amount(app.pool.clone(), channel.id(), balances.clone()) + .await + .expect("should spend"); + + let res = get_accounting_for_channel(build_request(channel.clone()), &app) + .await + .expect("should get response"); assert_eq!(StatusCode::OK, res.status()); let accounting_response = res_to_accounting_response(res).await; - let sum = accounting_response.balances.sum().expect("shouldn't overflow"); - assert_eq!(sum.0, UnifiedNum::from_u64(300)); - assert_eq!(sum.1, UnifiedNum::from_u64(300)); + assert_eq!(balances, accounting_response.balances); } // Testing for 2 accountings - second channel (same address is both an earner and a spender) - let mut second_channel = DUMMY_CAMPAIGN.channel.clone(); - second_channel.leader = IDS["user"]; // channel.id() will be different now - insert_channel(&app.pool, second_channel).await.expect("should insert channel"); - - let mut balances = Balances::::new(); - balances.earners.insert(ADDRESSES["publisher"], UnifiedNum::from_u64(300)); - balances.spenders.insert(ADDRESSES["publisher"], UnifiedNum::from_u64(300)); - spend_amount(app.pool.clone(), second_channel.id(), balances).await.expect("should spend"); - - insert_channel(&app.pool, second_channel).await.expect("should insert channel"); { - let res = get_accounting_for_channel(build_request(second_channel.clone()), &app).await.expect("should get response"); + let mut second_channel = DUMMY_CAMPAIGN.channel.clone(); + second_channel.leader = IDS["user"]; // channel.id() will be different now + insert_channel(&app.pool, second_channel) + .await + .expect("should insert channel"); + + let mut balances = Balances::::new(); + balances + .spend(ADDRESSES["tester"], ADDRESSES["publisher"], 300.into()) + .expect("Should not overflow"); + + balances + .spend(ADDRESSES["publisher"], ADDRESSES["user"], 300.into()) + .expect("Should not overflow"); + + spend_amount(app.pool.clone(), second_channel.id(), balances.clone()) + .await + .expect("should spend"); + + let res = get_accounting_for_channel(build_request(second_channel.clone()), &app) + .await + .expect("should get response"); assert_eq!(StatusCode::OK, res.status()); let accounting_response = res_to_accounting_response(res).await; - let sum = accounting_response.balances.sum().expect("shouldn't overflow"); - assert_eq!(sum.0, UnifiedNum::from_u64(300)); - assert_eq!(sum.1, UnifiedNum::from_u64(300)); + assert_eq!(balances, accounting_response.balances) } - // Testing for when sums dont match - Error case - let mut balances = Balances::::new(); - balances.earners.insert(ADDRESSES["publisher"], UnifiedNum::from_u64(100)); - balances.spenders.insert(ADDRESSES["creator"], UnifiedNum::from_u64(200)); - spend_amount(app.pool.clone(), channel.id(), balances).await.expect("should spend"); + + // Testing for when sums don't match on first channel - Error case { - let res = get_accounting_for_channel(build_request(channel.clone()), &app).await; - assert!(res.is_err()); - assert!(matches!(res, Err(ResponseError::FailedValidation(_)))); + let mut balances = Balances::::new(); + balances + .earners + .insert(ADDRESSES["publisher"], UnifiedNum::from_u64(100)); + balances + .spenders + .insert(ADDRESSES["creator"], UnifiedNum::from_u64(200)); + spend_amount(app.pool.clone(), channel.id(), balances) + .await + .expect("should spend"); + let res = get_accounting_for_channel(build_request(channel.clone()), &app).await; + let expected = ResponseError::FailedValidation( + "Earners sum is not equal to spenders sum for channel".to_string(), + ); + assert_eq!(expected, res.expect_err("Should return an error")); } } }