nigig-org/nimanyatta/crates/kra-etims-sdk/tests/api_tests.rs
andodeki fd8b0632ca
Some checks failed
repo hygiene / hygiene (push) Has been cancelled
Include nimanyatta as normal tree (not embedded git)
2026-09-26 09:29:36 +03:00

158 lines
No EOL
4.9 KiB
Rust

//! Unit tests for the API service and core logic.
use kra_etims_sdk::{
config::{Config, CorsConfig, Environment},
errors::SdkError,
middleware::{auth::extract_bearer_token, cors::is_origin_allowed},
models::auth::TokenInfo,
validation::validate_date_format,
};
// ── Token validity ───────────────────────────────────────────
#[test]
fn token_not_valid_when_none() {
let info: Option<TokenInfo> = None;
assert!(info.as_ref().map(|t| t.is_valid()).unwrap_or(false) == false);
}
#[test]
fn token_not_valid_when_expired() {
let info = TokenInfo {
token: "expired".into(),
expiry: chrono::Utc::now() - chrono::Duration::seconds(60),
};
assert!(!info.is_valid());
}
#[test]
fn token_valid_when_future() {
let info = TokenInfo {
token: "valid".into(),
expiry: chrono::Utc::now() + chrono::Duration::hours(1),
};
assert!(info.is_valid());
}
// ── Auth middleware ──────────────────────────────────────────
#[test]
fn extract_bearer_success() {
let result = extract_bearer_token(Some("Bearer abc123"));
assert!(result.is_ok());
assert_eq!(result.unwrap(), "abc123");
}
#[test]
fn extract_bearer_missing_header() {
let result = extract_bearer_token(None);
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), SdkError::MissingCredentials));
}
#[test]
fn extract_bearer_wrong_scheme() {
let result = extract_bearer_token(Some("Basic abc123"));
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), SdkError::MissingCredentials));
}
#[test]
fn extract_bearer_empty_token() {
let result = extract_bearer_token(Some("Bearer "));
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), SdkError::InvalidToken));
}
// ── CORS ─────────────────────────────────────────────────────
#[test]
fn cors_allows_no_origin() {
let cfg = CorsConfig::default();
assert!(is_origin_allowed(None, &cfg));
}
#[test]
fn cors_allows_whitelisted() {
let cfg = CorsConfig::default();
assert!(is_origin_allowed(Some("http://localhost:3000"), &cfg));
}
#[test]
fn cors_blocks_unknown() {
let cfg = CorsConfig::default();
assert!(!is_origin_allowed(Some("https://evil.com"), &cfg));
}
#[test]
fn cors_wildcard_allows_all() {
let cfg = CorsConfig {
allowed_origins: vec!["*".into()],
..CorsConfig::default()
};
assert!(is_origin_allowed(Some("https://anything.dev"), &cfg));
}
// ── Validation ───────────────────────────────────────────────
#[test]
fn date_format_valid() {
assert!(validate_date_format("20220101010101").is_ok());
}
#[test]
fn date_format_too_short() {
assert!(validate_date_format("202201").is_err());
}
#[test]
fn date_format_non_digits() {
assert!(validate_date_format("2022-01-01T01:").is_err());
}
// ── Config ───────────────────────────────────────────────────
#[test]
fn environment_from_str() {
assert_eq!(Environment::from("production"), Environment::Production);
assert_eq!(Environment::from("prod"), Environment::Production);
assert_eq!(Environment::from("development"), Environment::Development);
assert_eq!(Environment::from("anything"), Environment::Development);
}
#[test]
fn config_postcard_roundtrip() {
let cfg = Config {
api_base_url: "https://example.com".into(),
api_username: "user".into(),
api_password: "pass".into(),
environment: Environment::Development,
port: 5000,
cors: CorsConfig::default(),
endpoints: kra_etims_sdk::config::Endpoints::default(),
};
let bytes = cfg.to_postcard_bytes().expect("serialise");
let restored = Config::from_postcard_bytes(&bytes).expect("deserialise");
assert_eq!(restored.api_base_url, cfg.api_base_url);
assert_eq!(restored.port, 5000);
}
// ── Error set conversions ────────────────────────────────────
#[test]
fn auth_error_converts_to_sdk_error() {
// AuthError variants are a subset of SdkError, so `.into()` works.
let auth_err = SdkError::InvalidToken;
let sdk_err: SdkError = auth_err; // identity, but proves the type system
assert!(matches!(sdk_err, SdkError::InvalidToken));
}
#[test]
fn validation_error_converts_to_sdk_error() {
let val_err = SdkError::FieldError {
message: "bad".into(),
};
let sdk_err: SdkError = val_err;
assert!(matches!(sdk_err, SdkError::FieldError { .. }));
}