@@ -11,7 +11,7 @@ use mas_storage::{
11
11
user:: { UserRegistrationTokenFilter , UserRegistrationTokenRepository } ,
12
12
} ;
13
13
use rand:: RngCore ;
14
- use sea_query:: { Expr , PostgresQueryBuilder , Query , enum_def} ;
14
+ use sea_query:: { Condition , Expr , PostgresQueryBuilder , Query , enum_def} ;
15
15
use sea_query_binder:: SqlxBinder ;
16
16
use sqlx:: PgConnection ;
17
17
use ulid:: Ulid ;
@@ -54,6 +54,7 @@ struct UserRegistrationTokenLookup {
54
54
}
55
55
56
56
impl Filter for UserRegistrationTokenFilter {
57
+ #[ expect( clippy:: too_many_lines) ]
57
58
fn generate_condition ( & self , _has_joins : bool ) -> impl sea_query:: IntoCondition {
58
59
sea_query:: Condition :: all ( )
59
60
. add_option ( self . has_been_used ( ) . map ( |has_been_used| {
@@ -86,6 +87,93 @@ impl Filter for UserRegistrationTokenFilter {
86
87
. is_null ( )
87
88
}
88
89
} ) )
90
+ . add_option ( self . is_expired ( ) . map ( |is_expired| {
91
+ if is_expired {
92
+ Condition :: all ( )
93
+ . add (
94
+ Expr :: col ( (
95
+ UserRegistrationTokens :: Table ,
96
+ UserRegistrationTokens :: ExpiresAt ,
97
+ ) )
98
+ . is_not_null ( ) ,
99
+ )
100
+ . add (
101
+ Expr :: col ( (
102
+ UserRegistrationTokens :: Table ,
103
+ UserRegistrationTokens :: ExpiresAt ,
104
+ ) )
105
+ . lt ( Expr :: val ( self . now ( ) ) ) ,
106
+ )
107
+ } else {
108
+ Condition :: any ( )
109
+ . add (
110
+ Expr :: col ( (
111
+ UserRegistrationTokens :: Table ,
112
+ UserRegistrationTokens :: ExpiresAt ,
113
+ ) )
114
+ . is_null ( ) ,
115
+ )
116
+ . add (
117
+ Expr :: col ( (
118
+ UserRegistrationTokens :: Table ,
119
+ UserRegistrationTokens :: ExpiresAt ,
120
+ ) )
121
+ . gte ( Expr :: val ( self . now ( ) ) ) ,
122
+ )
123
+ }
124
+ } ) )
125
+ . add_option ( self . is_valid ( ) . map ( |is_valid| {
126
+ let valid = Condition :: all ( )
127
+ // Has not reached its usage limit
128
+ . add (
129
+ Condition :: any ( )
130
+ . add (
131
+ Expr :: col ( (
132
+ UserRegistrationTokens :: Table ,
133
+ UserRegistrationTokens :: UsageLimit ,
134
+ ) )
135
+ . is_null ( ) ,
136
+ )
137
+ . add (
138
+ Expr :: col ( (
139
+ UserRegistrationTokens :: Table ,
140
+ UserRegistrationTokens :: TimesUsed ,
141
+ ) )
142
+ . lt ( Expr :: col ( (
143
+ UserRegistrationTokens :: Table ,
144
+ UserRegistrationTokens :: UsageLimit ,
145
+ ) ) ) ,
146
+ ) ,
147
+ )
148
+ // Has not been revoked
149
+ . add (
150
+ Expr :: col ( (
151
+ UserRegistrationTokens :: Table ,
152
+ UserRegistrationTokens :: RevokedAt ,
153
+ ) )
154
+ . is_null ( ) ,
155
+ )
156
+ // Has not expired
157
+ . add (
158
+ Condition :: any ( )
159
+ . add (
160
+ Expr :: col ( (
161
+ UserRegistrationTokens :: Table ,
162
+ UserRegistrationTokens :: ExpiresAt ,
163
+ ) )
164
+ . is_null ( ) ,
165
+ )
166
+ . add (
167
+ Expr :: col ( (
168
+ UserRegistrationTokens :: Table ,
169
+ UserRegistrationTokens :: ExpiresAt ,
170
+ ) )
171
+ . gte ( Expr :: val ( self . now ( ) ) ) ,
172
+ ) ,
173
+ ) ;
174
+
175
+ if is_valid { valid } else { valid. not ( ) }
176
+ } ) )
89
177
}
90
178
}
91
179
@@ -462,8 +550,10 @@ impl UserRegistrationTokenRepository for PgUserRegistrationTokenRepository<'_> {
462
550
463
551
#[ cfg( test) ]
464
552
mod tests {
465
- use chrono:: { DateTime , Utc } ;
466
- use mas_storage:: { Pagination , clock:: MockClock , user:: UserRegistrationTokenFilter } ;
553
+ use chrono:: Duration ;
554
+ use mas_storage:: {
555
+ Clock as _, Pagination , clock:: MockClock , user:: UserRegistrationTokenFilter ,
556
+ } ;
467
557
use rand:: SeedableRng ;
468
558
use rand_chacha:: ChaChaRng ;
469
559
use sqlx:: PgPool ;
@@ -498,8 +588,8 @@ mod tests {
498
588
. unwrap ( ) ;
499
589
500
590
// 3. A token that is expired
501
- let past_time = DateTime :: < Utc > :: from_timestamp ( 0 , 0 ) . unwrap ( ) ;
502
- let _token3 = repo
591
+ let past_time = clock . now ( ) - Duration :: days ( 1 ) ;
592
+ let token3 = repo
503
593
. user_registration_token ( )
504
594
. add ( & mut rng, & clock, "token3" . to_owned ( ) , None , Some ( past_time) )
505
595
. await
@@ -518,7 +608,7 @@ mod tests {
518
608
. unwrap ( ) ;
519
609
520
610
// Test list with empty filter
521
- let empty_filter = UserRegistrationTokenFilter :: new ( ) ;
611
+ let empty_filter = UserRegistrationTokenFilter :: new ( clock . now ( ) ) ;
522
612
let page = repo
523
613
. user_registration_token ( )
524
614
. list ( empty_filter, Pagination :: first ( 10 ) )
@@ -535,7 +625,7 @@ mod tests {
535
625
assert_eq ! ( count, 4 ) ;
536
626
537
627
// Test has_been_used filter
538
- let used_filter = UserRegistrationTokenFilter :: new ( ) . with_been_used ( true ) ;
628
+ let used_filter = UserRegistrationTokenFilter :: new ( clock . now ( ) ) . with_been_used ( true ) ;
539
629
let page = repo
540
630
. user_registration_token ( )
541
631
. list ( used_filter, Pagination :: first ( 10 ) )
@@ -545,16 +635,34 @@ mod tests {
545
635
assert_eq ! ( page. edges[ 0 ] . id, token2. id) ;
546
636
547
637
// Test unused filter
548
- let unused_filter = UserRegistrationTokenFilter :: new ( ) . with_been_used ( false ) ;
638
+ let unused_filter = UserRegistrationTokenFilter :: new ( clock . now ( ) ) . with_been_used ( false ) ;
549
639
let page = repo
550
640
. user_registration_token ( )
551
641
. list ( unused_filter, Pagination :: first ( 10 ) )
552
642
. await
553
643
. unwrap ( ) ;
554
644
assert_eq ! ( page. edges. len( ) , 3 ) ;
555
645
646
+ // Test is_expired filter
647
+ let expired_filter = UserRegistrationTokenFilter :: new ( clock. now ( ) ) . with_expired ( true ) ;
648
+ let page = repo
649
+ . user_registration_token ( )
650
+ . list ( expired_filter, Pagination :: first ( 10 ) )
651
+ . await
652
+ . unwrap ( ) ;
653
+ assert_eq ! ( page. edges. len( ) , 1 ) ;
654
+ assert_eq ! ( page. edges[ 0 ] . id, token3. id) ;
655
+
656
+ let not_expired_filter = UserRegistrationTokenFilter :: new ( clock. now ( ) ) . with_expired ( false ) ;
657
+ let page = repo
658
+ . user_registration_token ( )
659
+ . list ( not_expired_filter, Pagination :: first ( 10 ) )
660
+ . await
661
+ . unwrap ( ) ;
662
+ assert_eq ! ( page. edges. len( ) , 3 ) ;
663
+
556
664
// Test is_revoked filter
557
- let revoked_filter = UserRegistrationTokenFilter :: new ( ) . with_revoked ( true ) ;
665
+ let revoked_filter = UserRegistrationTokenFilter :: new ( clock . now ( ) ) . with_revoked ( true ) ;
558
666
let page = repo
559
667
. user_registration_token ( )
560
668
. list ( revoked_filter, Pagination :: first ( 10 ) )
@@ -563,8 +671,33 @@ mod tests {
563
671
assert_eq ! ( page. edges. len( ) , 1 ) ;
564
672
assert_eq ! ( page. edges[ 0 ] . id, token4. id) ;
565
673
674
+ let not_revoked_filter = UserRegistrationTokenFilter :: new ( clock. now ( ) ) . with_revoked ( false ) ;
675
+ let page = repo
676
+ . user_registration_token ( )
677
+ . list ( not_revoked_filter, Pagination :: first ( 10 ) )
678
+ . await
679
+ . unwrap ( ) ;
680
+ assert_eq ! ( page. edges. len( ) , 3 ) ;
681
+
682
+ // Test is_valid filter
683
+ let valid_filter = UserRegistrationTokenFilter :: new ( clock. now ( ) ) . with_valid ( true ) ;
684
+ let page = repo
685
+ . user_registration_token ( )
686
+ . list ( valid_filter, Pagination :: first ( 10 ) )
687
+ . await
688
+ . unwrap ( ) ;
689
+ assert_eq ! ( page. edges. len( ) , 2 ) ;
690
+
691
+ let invalid_filter = UserRegistrationTokenFilter :: new ( clock. now ( ) ) . with_valid ( false ) ;
692
+ let page = repo
693
+ . user_registration_token ( )
694
+ . list ( invalid_filter, Pagination :: first ( 10 ) )
695
+ . await
696
+ . unwrap ( ) ;
697
+ assert_eq ! ( page. edges. len( ) , 2 ) ;
698
+
566
699
// Test combined filters
567
- let combined_filter = UserRegistrationTokenFilter :: new ( )
700
+ let combined_filter = UserRegistrationTokenFilter :: new ( clock . now ( ) )
568
701
. with_been_used ( false )
569
702
. with_revoked ( true ) ;
570
703
let page = repo
0 commit comments