From f56aa0f644b7f970633e1fd0e687315777052a53 Mon Sep 17 00:00:00 2001 From: Edoardo Morandi Date: Thu, 30 Apr 2026 16:00:14 +0200 Subject: [PATCH 1/2] fix: correctly represent the `None` variant `serde` triggers an error if none of the fields in the other variants are found, it does not like the _stateless variant form_. --- watermelon/src/client/builder.rs | 2 +- watermelon/src/client/from_env.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/watermelon/src/client/builder.rs b/watermelon/src/client/builder.rs index 1cae804..81143dd 100644 --- a/watermelon/src/client/builder.rs +++ b/watermelon/src/client/builder.rs @@ -94,7 +94,7 @@ impl ClientBuilder { password, })); } - from_env::AuthenticationMethod::None => { + from_env::AuthenticationMethod::None {} => { this = this.authentication_method(None); } } diff --git a/watermelon/src/client/from_env.rs b/watermelon/src/client/from_env.rs index d67071f..a901d3f 100644 --- a/watermelon/src/client/from_env.rs +++ b/watermelon/src/client/from_env.rs @@ -30,7 +30,7 @@ pub(super) enum AuthenticationMethod { #[serde(rename = "nats_password")] password: String, }, - None, + None {}, } fn deserialize_nkey<'de, D>(deserializer: D) -> Result From 9a4dd7bd2673b97acfcba8e2f66750fc93188346 Mon Sep 17 00:00:00 2001 From: Edoardo Morandi Date: Thu, 30 Apr 2026 16:02:34 +0200 Subject: [PATCH 2/2] test: add tests for FromEnv deserialization To avoid analogous problems in the future. --- watermelon/src/client/from_env.rs | 66 +++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/watermelon/src/client/from_env.rs b/watermelon/src/client/from_env.rs index a901d3f..b9a7eb0 100644 --- a/watermelon/src/client/from_env.rs +++ b/watermelon/src/client/from_env.rs @@ -40,3 +40,69 @@ where let secret = String::deserialize(deserializer)?; KeyPair::from_encoded_seed(&secret).map_err(de::Error::custom) } + +#[cfg(test)] +mod tests { + use claims::{assert_matches, assert_ok}; + + use super::{AuthenticationMethod, FromEnv}; + + const TEST_SEED: &str = "SAAPN4W3EG6KCJGUQTKTJ5GSB5NHK5CHAJL4DBGFUM3HHROI4XUEP4OBK4"; + const TEST_PUBLIC_KEY: &str = "SAAJYMSGSUUUC3GAOKL2IFAAKQDV32K4X45HPCPC4EBM7F7N76HQGR4C2I"; + + fn from_iter(vars: [(&str, &str); N]) -> FromEnv { + let iter = vars.into_iter().map(|(k, v)| (k.to_owned(), v.to_owned())); + assert_ok!(envy::from_iter::<_, FromEnv>(iter)) + } + + #[test] + fn deserialize_creds() { + let env = from_iter([ + ("NATS_JWT", "eyJhbGciOiJlZDI1NTE5In0"), + ("NATS_NKEY", TEST_SEED), + ]); + assert_matches!( + env.auth, + AuthenticationMethod::Creds { jwt, nkey } + if jwt == "eyJhbGciOiJlZDI1NTE5In0" + && nkey.public_key().to_string() == TEST_PUBLIC_KEY + ); + assert!(env.inbox_prefix.is_none()); + } + + #[test] + fn deserialize_creds_file() { + let env = from_iter([("NATS_CREDS_FILE", "/etc/nats/user.creds")]); + assert_matches!( + env.auth, + AuthenticationMethod::CredsFile { creds_file } + if creds_file.as_os_str() == "/etc/nats/user.creds" + ); + assert!(env.inbox_prefix.is_none()); + } + + #[test] + fn deserialize_user_and_password() { + let env = from_iter([("NATS_USERNAME", "alice"), ("NATS_PASSWORD", "hunter2")]); + assert_matches!( + env.auth, + AuthenticationMethod::UserAndPassword { username, password } + if username == "alice" && password == "hunter2" + ); + assert!(env.inbox_prefix.is_none()); + } + + #[test] + fn deserialize_none() { + let env = from_iter([]); + assert_matches!(env.auth, AuthenticationMethod::None {}); + assert!(env.inbox_prefix.is_none()); + } + + #[test] + fn deserialize_none_with_inbox_prefix() { + let env = from_iter([("INBOX_PREFIX", "_CUSTOM_INBOX")]); + assert_matches!(env.auth, AuthenticationMethod::None {}); + assert_eq!(env.inbox_prefix.unwrap().as_str(), "_CUSTOM_INBOX"); + } +}