@@ -21,6 +21,7 @@ use primitives::{
21
21
sentry:: {
22
22
campaign:: CampaignListQuery ,
23
23
campaign_create:: { CreateCampaign , ModifyCampaign } ,
24
+ SuccessResponse ,
24
25
} ,
25
26
spender:: Spendable ,
26
27
Address , Campaign , CampaignId , Channel , ChannelId , Deposit , UnifiedNum ,
@@ -292,6 +293,49 @@ pub async fn campaign_list<A: Adapter>(
292
293
Ok ( success_response ( serde_json:: to_string ( & list_response) ?) )
293
294
}
294
295
296
+ /// Can only be called by creator
297
+ /// to close a campaign, just set it's budget to what it's spent so far (so that remaining == 0)
298
+ /// newBudget = totalSpent, i.e. newBudget = oldBudget - remaining
299
+ pub async fn close_campaign < A : Adapter > (
300
+ req : Request < Body > ,
301
+ app : & Application < A > ,
302
+ ) -> Result < Response < Body > , ResponseError > {
303
+ let auth = req
304
+ . extensions ( )
305
+ . get :: < Auth > ( )
306
+ . expect ( "Auth should be present" ) ;
307
+
308
+ let mut campaign = req
309
+ . extensions ( )
310
+ . get :: < Campaign > ( )
311
+ . expect ( "We must have a campaign in extensions" )
312
+ . to_owned ( ) ;
313
+
314
+ if auth. uid . to_address ( ) != campaign. creator {
315
+ Err ( ResponseError :: Forbidden (
316
+ "Request not sent by campaign creator" . to_string ( ) ,
317
+ ) )
318
+ } else {
319
+ let old_remaining = app
320
+ . campaign_remaining
321
+ . getset_remaining_to_zero ( campaign. id )
322
+ . await
323
+ . map_err ( |e| ResponseError :: BadRequest ( e. to_string ( ) ) ) ?;
324
+
325
+ campaign. budget = campaign
326
+ . budget
327
+ . checked_sub ( & UnifiedNum :: from ( old_remaining) )
328
+ . ok_or_else ( || {
329
+ ResponseError :: BadRequest ( "Campaign budget overflows/underflows" . to_string ( ) )
330
+ } ) ?;
331
+ update_campaign ( & app. pool , & campaign) . await ?;
332
+
333
+ Ok ( success_response ( serde_json:: to_string ( & SuccessResponse {
334
+ success : true ,
335
+ } ) ?) )
336
+ }
337
+ }
338
+
295
339
pub mod update_campaign {
296
340
use primitives:: Config ;
297
341
@@ -903,13 +947,15 @@ mod test {
903
947
update_campaign:: { get_delta_budget, modify_campaign} ,
904
948
* ,
905
949
} ;
906
- use crate :: db:: redis_pool:: TESTS_POOL ;
950
+ use crate :: db:: { fetch_campaign , redis_pool:: TESTS_POOL } ;
907
951
use crate :: test_util:: setup_dummy_app;
908
952
use crate :: update_campaign:: DeltaBudget ;
909
953
use adapter:: DummyAdapter ;
910
954
use hyper:: StatusCode ;
911
955
use primitives:: {
912
- adapter:: Deposit , util:: tests:: prep_db:: DUMMY_CAMPAIGN , BigNum , ChannelId , ValidatorId ,
956
+ adapter:: Deposit ,
957
+ util:: tests:: prep_db:: { DUMMY_CAMPAIGN , IDS } ,
958
+ BigNum , ChannelId , ValidatorId ,
913
959
} ;
914
960
915
961
#[ tokio:: test]
@@ -1249,4 +1295,81 @@ mod test {
1249
1295
assert_eq ! ( delta_budget, Some ( DeltaBudget :: Decrease ( decrease_by) ) ) ;
1250
1296
}
1251
1297
}
1298
+
1299
+ #[ tokio:: test]
1300
+ async fn campaign_is_closed_properly ( ) {
1301
+ let campaign = DUMMY_CAMPAIGN . clone ( ) ;
1302
+
1303
+ let app = setup_dummy_app ( ) . await ;
1304
+
1305
+ insert_channel ( & app. pool , campaign. channel )
1306
+ . await
1307
+ . expect ( "Should insert dummy channel" ) ;
1308
+ insert_campaign ( & app. pool , & campaign)
1309
+ . await
1310
+ . expect ( "Should insert dummy campaign" ) ;
1311
+
1312
+ // Test if remaining is set to 0
1313
+ {
1314
+ app. campaign_remaining
1315
+ . set_initial ( campaign. id , campaign. budget )
1316
+ . await
1317
+ . expect ( "should set" ) ;
1318
+
1319
+ let auth = Auth {
1320
+ era : 0 ,
1321
+ uid : ValidatorId :: from ( campaign. creator ) ,
1322
+ } ;
1323
+
1324
+ let req = Request :: builder ( )
1325
+ . extension ( auth)
1326
+ . extension ( campaign. clone ( ) )
1327
+ . body ( Body :: empty ( ) )
1328
+ . expect ( "Should build Request" ) ;
1329
+
1330
+ close_campaign ( req, & app)
1331
+ . await
1332
+ . expect ( "Should close campaign" ) ;
1333
+
1334
+ let closed_campaign = fetch_campaign ( app. pool . clone ( ) , & campaign. id )
1335
+ . await
1336
+ . expect ( "Should fetch campaign" )
1337
+ . expect ( "Campaign should exist" ) ;
1338
+
1339
+ // remaining == campaign_budget therefore old_budget - remaining = 0
1340
+ assert_eq ! ( closed_campaign. budget, UnifiedNum :: from_u64( 0 ) ) ;
1341
+
1342
+ let remaining = app
1343
+ . campaign_remaining
1344
+ . get_remaining_opt ( campaign. id )
1345
+ . await
1346
+ . expect ( "Should get remaining from redis" )
1347
+ . expect ( "There should be value for the Campaign" ) ;
1348
+
1349
+ assert_eq ! ( remaining, 0 ) ;
1350
+ }
1351
+
1352
+ // Test if an error is returned when request is not sent by creator
1353
+ {
1354
+ let auth = Auth {
1355
+ era : 0 ,
1356
+ uid : IDS [ "leader" ] ,
1357
+ } ;
1358
+
1359
+ let req = Request :: builder ( )
1360
+ . extension ( auth)
1361
+ . extension ( campaign. clone ( ) )
1362
+ . body ( Body :: empty ( ) )
1363
+ . expect ( "Should build Request" ) ;
1364
+
1365
+ let res = close_campaign ( req, & app)
1366
+ . await
1367
+ . expect_err ( "Should return error for Bad Campaign" ) ;
1368
+
1369
+ assert_eq ! (
1370
+ ResponseError :: Forbidden ( "Request not sent by campaign creator" . to_string( ) ) ,
1371
+ res
1372
+ ) ;
1373
+ }
1374
+ }
1252
1375
}
0 commit comments