use serde::{Deserialize, Serialize};
pub const AUTH_ROTATE_METHOD: &str = "nexo/admin/auth/rotate_token";
pub const MIN_TOKEN_LEN: usize = 16;
pub const REASON_MAX_LEN: usize = 200;
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(default)]
pub struct AuthRotateInput {
#[serde(skip_serializing_if = "Option::is_none")]
pub new_token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AuthRotateResponse {
pub ok: bool,
pub new_hash: String,
pub at_ms: u64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn method_literal_matches_expected_jsonrpc_path() {
assert_eq!(AUTH_ROTATE_METHOD, "nexo/admin/auth/rotate_token");
}
#[test]
fn input_round_trips_with_optional_fields() {
let i = AuthRotateInput::default();
let v = serde_json::to_value(&i).unwrap();
let obj = v.as_object().unwrap();
assert!(!obj.contains_key("new_token"));
assert!(!obj.contains_key("reason"));
let back: AuthRotateInput = serde_json::from_value(v).unwrap();
assert_eq!(i, back);
let i2 = AuthRotateInput {
new_token: Some("super-secret-bearer-32".into()),
reason: Some("scheduled rotation".into()),
};
let v2 = serde_json::to_value(&i2).unwrap();
assert_eq!(v2["new_token"], "super-secret-bearer-32");
assert_eq!(v2["reason"], "scheduled rotation");
let back2: AuthRotateInput = serde_json::from_value(v2).unwrap();
assert_eq!(i2, back2);
}
#[test]
fn response_round_trip_preserves_fields() {
let r = AuthRotateResponse {
ok: true,
new_hash: "deadbeefcafebabe".into(),
at_ms: 1_700_000_000_000,
};
let v = serde_json::to_value(&r).unwrap();
let back: AuthRotateResponse = serde_json::from_value(v).unwrap();
assert_eq!(r, back);
assert_eq!(r.new_hash.len(), 16);
}
#[test]
fn min_token_len_is_16() {
assert_eq!(MIN_TOKEN_LEN, 16);
}
}