@@ -11,7 +11,7 @@ use mas_storage::{
1111 user:: { UserRegistrationTokenFilter , UserRegistrationTokenRepository } ,
1212} ;
1313use rand:: RngCore ;
14- use sea_query:: { Expr , PostgresQueryBuilder , Query , enum_def} ;
14+ use sea_query:: { Condition , Expr , PostgresQueryBuilder , Query , enum_def} ;
1515use sea_query_binder:: SqlxBinder ;
1616use sqlx:: PgConnection ;
1717use ulid:: Ulid ;
@@ -54,6 +54,7 @@ struct UserRegistrationTokenLookup {
5454}
5555
5656impl Filter for UserRegistrationTokenFilter {
57+ #[ expect( clippy:: too_many_lines) ]
5758 fn generate_condition ( & self , _has_joins : bool ) -> impl sea_query:: IntoCondition {
5859 sea_query:: Condition :: all ( )
5960 . add_option ( self . has_been_used ( ) . map ( |has_been_used| {
@@ -86,6 +87,93 @@ impl Filter for UserRegistrationTokenFilter {
8687 . is_null ( )
8788 }
8889 } ) )
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+ } ) )
89177 }
90178}
91179
@@ -462,8 +550,10 @@ impl UserRegistrationTokenRepository for PgUserRegistrationTokenRepository<'_> {
462550
463551#[ cfg( test) ]
464552mod 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+ } ;
467557 use rand:: SeedableRng ;
468558 use rand_chacha:: ChaChaRng ;
469559 use sqlx:: PgPool ;
@@ -498,8 +588,8 @@ mod tests {
498588 . unwrap ( ) ;
499589
500590 // 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
503593 . user_registration_token ( )
504594 . add ( & mut rng, & clock, "token3" . to_owned ( ) , None , Some ( past_time) )
505595 . await
@@ -518,7 +608,7 @@ mod tests {
518608 . unwrap ( ) ;
519609
520610 // Test list with empty filter
521- let empty_filter = UserRegistrationTokenFilter :: new ( ) ;
611+ let empty_filter = UserRegistrationTokenFilter :: new ( clock . now ( ) ) ;
522612 let page = repo
523613 . user_registration_token ( )
524614 . list ( empty_filter, Pagination :: first ( 10 ) )
@@ -535,7 +625,7 @@ mod tests {
535625 assert_eq ! ( count, 4 ) ;
536626
537627 // 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 ) ;
539629 let page = repo
540630 . user_registration_token ( )
541631 . list ( used_filter, Pagination :: first ( 10 ) )
@@ -545,16 +635,34 @@ mod tests {
545635 assert_eq ! ( page. edges[ 0 ] . id, token2. id) ;
546636
547637 // 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 ) ;
549639 let page = repo
550640 . user_registration_token ( )
551641 . list ( unused_filter, Pagination :: first ( 10 ) )
552642 . await
553643 . unwrap ( ) ;
554644 assert_eq ! ( page. edges. len( ) , 3 ) ;
555645
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+
556664 // Test is_revoked filter
557- let revoked_filter = UserRegistrationTokenFilter :: new ( ) . with_revoked ( true ) ;
665+ let revoked_filter = UserRegistrationTokenFilter :: new ( clock . now ( ) ) . with_revoked ( true ) ;
558666 let page = repo
559667 . user_registration_token ( )
560668 . list ( revoked_filter, Pagination :: first ( 10 ) )
@@ -563,8 +671,33 @@ mod tests {
563671 assert_eq ! ( page. edges. len( ) , 1 ) ;
564672 assert_eq ! ( page. edges[ 0 ] . id, token4. id) ;
565673
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+
566699 // Test combined filters
567- let combined_filter = UserRegistrationTokenFilter :: new ( )
700+ let combined_filter = UserRegistrationTokenFilter :: new ( clock . now ( ) )
568701 . with_been_used ( false )
569702 . with_revoked ( true ) ;
570703 let page = repo
0 commit comments