#![cfg(any(
feature = "rustls-ring",
feature = "rustls-aws",
feature = "native-tls"
))]
use std::{
env,
time::{SystemTime, UNIX_EPOCH},
};
use io_gmail::v1::{
client::{GmailClientStd, GmailClientStdConnectOptions},
rest::{
labels::GmailLabel,
messages::{GmailMessage, GmailMessageFormat, encode_raw, list::GmailMessagesListParams},
},
};
use pimalaya_stream::tls::Tls;
#[test]
#[ignore = "requires GMAIL_ACCESS_TOKEN env var and --include-ignored"]
fn gmail() {
env_logger::try_init().ok();
let token = env::var("GMAIL_ACCESS_TOKEN").expect("GMAIL_ACCESS_TOKEN not set");
let user_id = env::var("GMAIL_USER_ID").unwrap_or_else(|_| "me".to_owned());
let options = GmailClientStdConnectOptions {
tls: Tls::default(),
user_id: user_id.clone(),
};
let mut client = GmailClientStd::connect(token.clone(), options).expect("connect");
let ts = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
let label_name = format!("io-gmail-test-{ts}");
let label_name_renamed = format!("{label_name}-renamed");
let profile = client.profile_get().expect("profile get").response;
let email = profile.email_address.clone();
assert!(!email.is_empty(), "profile should expose an email address");
let labels = client.labels_list().expect("labels list").response;
assert!(
labels.labels.iter().any(|label| label.id == "INBOX"),
"labels list should contain the INBOX system label"
);
let new_label = GmailLabel {
name: label_name.clone(),
..Default::default()
};
let label = client
.label_create(&new_label)
.expect("label create")
.response;
let label_id = label.id.clone();
assert_eq!(label.name, label_name, "created label name mismatch");
let fetched = client.label_get(&label_id).expect("label get").response;
assert_eq!(fetched.id, label_id, "label get id mismatch");
let renamed_label = GmailLabel {
id: label_id.clone(),
name: label_name_renamed.clone(),
..Default::default()
};
let renamed = client
.label_update(&renamed_label)
.expect("label update")
.response;
assert_eq!(
renamed.name, label_name_renamed,
"label rename not reflected"
);
let eml = build_eml(&email).into_bytes();
let message = GmailMessage {
raw: Some(encode_raw(&eml)),
..Default::default()
};
let sent = client
.message_send(&message)
.expect("message send")
.response;
let message_id = sent.id.clone();
let message = client
.message_get(&message_id, GmailMessageFormat::Full, &[])
.expect("message get")
.response;
assert_eq!(message.id, message_id, "message get id mismatch");
let labelled = client
.message_modify(&message_id, std::slice::from_ref(&label_id), &[])
.expect("message modify add")
.response;
assert!(
labelled.label_ids.contains(&label_id),
"message should carry the test label after modify"
);
let unlabelled = client
.message_modify(&message_id, &[], std::slice::from_ref(&label_id))
.expect("message modify remove")
.response;
assert!(
!unlabelled.label_ids.contains(&label_id),
"message should not carry the test label after removal"
);
let listed = client
.messages_list(&GmailMessagesListParams {
q: Some("subject:io-gmail"),
max_results: Some(10),
include_spam_trash: true,
..Default::default()
})
.expect("messages list")
.response;
assert!(
listed.messages.iter().any(|m| m.id == message_id),
"messages list should surface the sent message"
);
let trashed = client
.message_trash(&message_id)
.expect("message trash")
.response;
assert!(
trashed.label_ids.iter().any(|id| id == "TRASH"),
"trashed message should carry the TRASH label"
);
let untrashed = client
.message_untrash(&message_id)
.expect("message untrash")
.response;
assert!(
!untrashed.label_ids.iter().any(|id| id == "TRASH"),
"untrashed message should no longer carry the TRASH label"
);
client.message_delete(&message_id).expect("message delete");
client.label_delete(&label_id).expect("label delete");
}
fn build_eml(email: &str) -> String {
[
&format!("From: io-gmail test <{email}>"),
&format!("To: io-gmail test <{email}>"),
"Subject: io-gmail integration test",
"Date: Thu, 01 Jan 2026 00:00:00 +0000",
"MIME-Version: 1.0",
"Content-Type: text/plain; charset=utf-8",
"",
"This is an automated test email from io-gmail integration tests.",
]
.join("\r\n")
}