smoke: read the token via its item-ID reference
Assisted-by: GLM-5.3 via pi
change yxooopxvvxoqnxpsklvxrpouknkquwkw
commit 82654856d45b7790456607266ab3c8a5a42e56fb
author Alpha Chen <alpha@kejadlen.dev>
date
parent yuwkrtqw
diff --git a/tests/smoke.rs b/tests/smoke.rs
index d664d29..f2339ef 100644
--- a/tests/smoke.rs
+++ b/tests/smoke.rs
@@ -7,24 +7,16 @@ use camino::Utf8PathBuf;
 use docket::jmap::{Client, Credential};
 use docket::store::{Clock, Store};
 
-/// Where the smoke test's token lives in 1Password. The item name's
-/// colon is illegal in an `op://` secret reference (alphanumerics,
-/// `-`, `_`, `.`, and spaces only), so the token is fetched by name
-/// with `op item get` rather than `op read`.
-const OP_VAULT: &str = "Private";
-const OP_ITEM: &str = "Fastmail API token: docket smoke test";
-const OP_FIELD: &str = "credential";
+/// The smoke test's token, by item ID: the item's name contains a
+/// colon, which `op://` references don't allow (alphanumerics, `-`,
+/// `_`, `.`, and spaces only), so the ID stands in for the name.
+const TOKEN_REF: &str = "op://Private/dpepkrnjuhnao5h7fd52kvqay4/credential";
 
 /// Reads the token via `op`, which must be installed and signed in.
 fn token() -> Result<String, Box<dyn std::error::Error>> {
     let output = match std::process::Command::new("op")
-        .arg("item")
-        .arg("get")
-        .arg(OP_ITEM)
-        .arg("--vault")
-        .arg(OP_VAULT)
-        .arg("--fields")
-        .arg(OP_FIELD)
+        .arg("read")
+        .arg(TOKEN_REF)
         .output()
     {
         Ok(output) => output,
@@ -35,7 +27,7 @@ fn token() -> Result<String, Box<dyn std::error::Error>> {
     };
     if !output.status.success() {
         return Err(format!(
-            "op item get failed: {}",
+            "op read failed: {}",
             String::from_utf8_lossy(&output.stderr).trim()
         )
         .into());
@@ -46,10 +38,27 @@ fn token() -> Result<String, Box<dyn std::error::Error>> {
 #[test]
 #[ignore = "run via `just smoke`; reads its token from 1Password"]
 fn the_session_open_chain_runs_against_fastmail() -> Result<(), Box<dyn std::error::Error>> {
+    let token = token()?;
+    // The server sees only the bearer header, so a bad token surfaces
+    // as an opaque 401; what 1Password handed over is the other half.
+    let pieces = token.split_whitespace().count();
+    if pieces != 1 {
+        return Err(format!(
+            "the 1Password field must hold exactly the token; got {pieces} \
+             whitespace-separated pieces"
+        )
+        .into());
+    }
+    println!(
+        "token from 1Password: {} chars, {}…{}",
+        token.chars().count(),
+        token.chars().take(4).collect::<String>(),
+        token.chars().rev().take(4).collect::<String>()
+    );
     // Credentials are files (systemd LoadCredential in production), so
     // hand the token to the Client through one.
     let file = tempfile::NamedTempFile::new()?;
-    std::fs::write(&file, token()?)?;
+    std::fs::write(&file, token)?;
     let credential = Credential {
         name: "smoke".into(),
         token_file: Utf8PathBuf::from_path_buf(file.path().to_owned())