@@ -2,12 +2,13 @@ use crate::{
2
2
add_team_to_crate,
3
3
builders:: { CrateBuilder , PublishBuilder } ,
4
4
new_team,
5
- util:: { MockCookieUser , MockTokenUser , RequestHelper } ,
5
+ util:: { MockAnonymousUser , MockCookieUser , MockTokenUser , RequestHelper } ,
6
6
TestApp ,
7
7
} ;
8
8
use cargo_registry:: {
9
9
models:: Crate ,
10
10
views:: { EncodableCrateOwnerInvitation , EncodableOwner , InvitationResponse } ,
11
+ Emails ,
11
12
} ;
12
13
13
14
use conduit:: StatusCode ;
@@ -84,6 +85,19 @@ impl MockCookieUser {
84
85
}
85
86
}
86
87
88
+ impl MockAnonymousUser {
89
+ fn accept_ownership_invitation_by_token ( & self , token : & str ) {
90
+ #[ derive( Deserialize ) ]
91
+ struct Response {
92
+ crate_owner_invitation : InvitationResponse ,
93
+ }
94
+
95
+ let url = format ! ( "/api/v1/me/crate_owner_invitations/accept/{}" , token) ;
96
+ let response: Response = self . put ( & url, & [ ] ) . good ( ) ;
97
+ assert ! ( response. crate_owner_invitation. accepted) ;
98
+ }
99
+ }
100
+
87
101
#[ test]
88
102
fn new_crate_owner ( ) {
89
103
let ( app, _, _, token) = TestApp :: full ( ) . with_token ( ) ;
@@ -381,6 +395,31 @@ fn test_decline_invitation() {
381
395
assert_eq ! ( json. users. len( ) , 1 ) ;
382
396
}
383
397
398
+ #[ test]
399
+ fn test_accept_invitation_by_mail ( ) {
400
+ let ( app, anon, owner, owner_token) = TestApp :: init ( ) . with_token ( ) ;
401
+ let owner = owner. as_model ( ) ;
402
+ let invited_user = app. db_new_user ( "user_bar" ) ;
403
+ let _krate = app. db ( |conn| CrateBuilder :: new ( "accept_invitation" , owner. id ) . expect_build ( conn) ) ;
404
+
405
+ // Invite a new owner
406
+ owner_token. add_user_owner ( "accept_invitation" , "user_bar" ) ;
407
+
408
+ // Retrieve the ownership invitation
409
+ let invite_token = extract_token_from_invite_email ( & app. as_inner ( ) . emails ) ;
410
+
411
+ // Accept the invitation anonymously with a token
412
+ anon. accept_ownership_invitation_by_token ( & invite_token) ;
413
+
414
+ // New owner's invitation list should now be empty
415
+ let json = invited_user. list_invitations ( ) ;
416
+ assert_eq ! ( json. crate_owner_invitations. len( ) , 0 ) ;
417
+
418
+ // New owner is now listed as an owner, so the crate has two owners
419
+ let json = anon. show_crate_owners ( "accept_invitation" ) ;
420
+ assert_eq ! ( json. users. len( ) , 2 ) ;
421
+ }
422
+
384
423
#[ test]
385
424
fn inactive_users_dont_get_invitations ( ) {
386
425
use cargo_registry:: models:: NewUser ;
@@ -437,3 +476,20 @@ fn highest_gh_id_is_most_recent_account_we_know_of() {
437
476
let json = invited_user. list_invitations ( ) ;
438
477
assert_eq ! ( json. crate_owner_invitations. len( ) , 1 ) ;
439
478
}
479
+
480
+ fn extract_token_from_invite_email ( emails : & Emails ) -> String {
481
+ let message = emails
482
+ . mails_in_memory ( )
483
+ . unwrap ( )
484
+ . into_iter ( )
485
+ . find ( |m| m. subject . contains ( "invitation" ) )
486
+ . expect ( "missing email" ) ;
487
+
488
+ // Simple (but kinda fragile) parser to extract the token.
489
+ let before_token = "/accept-invite/" ;
490
+ let after_token = " " ;
491
+ let body = message. body . as_str ( ) ;
492
+ let before_pos = body. find ( before_token) . unwrap ( ) + before_token. len ( ) ;
493
+ let after_pos = before_pos + ( & body[ before_pos..] ) . find ( after_token) . unwrap ( ) ;
494
+ body[ before_pos..after_pos] . to_string ( )
495
+ }
0 commit comments