#![cfg(feature = "gmail")]
use std::time::Duration;
use grr_cli::core::{GoogleAuth, GrrConfig, GrrError, TokenStorage};
use grr_cli::gmail::{GmailClient, GmailClientBuilder};
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
#[tokio::test]
async fn unauthorized_401_fails_fast_without_retrying_stale_token() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/users/me/messages/m-401"))
.respond_with(ResponseTemplate::new(401).set_body_json(serde_json::json!({
"error": { "code": 401, "message": "Request had invalid authentication credentials.", "status": "UNAUTHENTICATED" }
})))
.mount(&server)
.await;
let client = test_client(&server.uri()).await;
let result = tokio::time::timeout(Duration::from_secs(5), client.get_message("m-401", None))
.await
.expect("401 must fail fast, not burn retry backoff cycles");
let err = result.expect_err("a 401 must yield an error");
assert!(
matches!(err, GrrError::Auth(_)),
"expected Auth error, got: {err:?}"
);
let message = err.to_string();
assert!(
message.contains("grr auth login"),
"remediation guidance missing: {message}"
);
let received = server.received_requests().await.unwrap();
assert_eq!(
received.len(),
1,
"401 must not be retried with the same stale token"
);
}
async fn test_client(base: &str) -> GmailClient {
let config = GrrConfig::default();
let storage = TokenStorage {
access_token: "test-token".into(),
refresh_token: None,
expires_at: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs()
+ 3600,
token_type: "Bearer".into(),
scope: "test-scope".into(),
};
let dir = tempfile::tempdir().unwrap();
let auth = GoogleAuth::with_token(config.oauth.clone(), storage)
.await
.unwrap()
.with_token_path(dir.path().join("token.json"));
GmailClientBuilder::new()
.auth(auth)
.base_url(base.parse().unwrap())
.upload_base_url(format!("{}/", base).parse().unwrap())
.build()
.await
.unwrap()
}