diff --git a/src/models/token.rs b/src/models/token.rs index e091b7d46a5..a3ed633509e 100644 --- a/src/models/token.rs +++ b/src/models/token.rs @@ -110,7 +110,12 @@ impl ApiToken { .filter( api_tokens::expired_at .is_not_null() - .and(api_tokens::expired_at.lt(now.nullable() + days_until_expiry.days())), + .and(api_tokens::expired_at.assume_not_null().gt(now)) // Ignore already expired tokens + .and( + api_tokens::expired_at + .assume_not_null() + .lt(now + days_until_expiry.days()), + ), ) .filter(api_tokens::expiry_notification_at.is_null()) .select(ApiToken::as_select()) diff --git a/src/worker/jobs/expiry_notification.rs b/src/worker/jobs/expiry_notification.rs index a6c4eaa9293..bf79005e45c 100644 --- a/src/worker/jobs/expiry_notification.rs +++ b/src/worker/jobs/expiry_notification.rs @@ -100,6 +100,7 @@ mod tests { models::token::ApiToken, schema::api_tokens, test_util::test_db_connection, typosquat::test_util::Faker, util::token::PlainToken, }; + use diesel::dsl::IntervalDsl; use diesel::{QueryDsl, SelectableHelper}; use lettre::Address; @@ -112,14 +113,13 @@ mod tests { // Set up a user and a token that is about to expire. let user = faker.user(&mut conn, "a", Some("testuser@test.com".to_owned()))?; let token = PlainToken::generate(); - let expired_at = diesel::dsl::now; let token: ApiToken = diesel::insert_into(api_tokens::table) .values(( api_tokens::user_id.eq(user.id), api_tokens::name.eq("test_token"), api_tokens::token.eq(token.hashed()), - api_tokens::expired_at.eq(expired_at), + api_tokens::expired_at.eq(now.nullable() + ( EXPIRY_THRESHOLD-1).day()) )) .returning(ApiToken::as_returning()) .get_result(&mut conn)?; @@ -139,6 +139,25 @@ mod tests { .first::(&mut conn)?; assert!(update_token.expiry_notification_at.is_some()); + // Insert a already expired token. + let token = PlainToken::generate(); + diesel::insert_into(api_tokens::table) + .values(( + api_tokens::user_id.eq(user.id), + api_tokens::name.eq("expired_token"), + api_tokens::token.eq(token.hashed()), + api_tokens::expired_at.eq(diesel::dsl::now.nullable() - 1.day()), + )) + .returning(ApiToken::as_returning()) + .get_result(&mut conn)?; + + // Check that the token is not about to expire. + check(&emails, &mut conn)?; + + // Check that no email was sent. + let sent_mail = emails.mails_in_memory().unwrap(); + assert_eq!(sent_mail.len(), 1); + Ok(()) } }