el-ui v2: universal platform, service bindings, AOP, auth, publish pipeline
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
//! Tests for el-auth.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
context::{AuthContext, AuthUser},
|
||||
jwt::{JwtClaims, JwtProvider},
|
||||
middleware::AuthMiddleware,
|
||||
roles::{Permission, Role, RoleRegistry},
|
||||
session::SessionProvider,
|
||||
AuthError, AuthProvider,
|
||||
};
|
||||
|
||||
fn test_user() -> AuthUser {
|
||||
AuthUser::new("user-1", "alice@example.com", "Alice")
|
||||
}
|
||||
|
||||
fn test_provider() -> JwtProvider {
|
||||
JwtProvider::new(b"super-secret-key-for-testing-only".to_vec())
|
||||
.with_ttl(3600)
|
||||
}
|
||||
|
||||
fn registry() -> RoleRegistry {
|
||||
let mut r = RoleRegistry::new();
|
||||
r.register(
|
||||
Role::new("admin")
|
||||
.with_permissions(vec!["read", "write", "delete"]),
|
||||
);
|
||||
r.register(
|
||||
Role::new("user")
|
||||
.with_permissions(vec!["read"]),
|
||||
);
|
||||
r
|
||||
}
|
||||
|
||||
// ── Test 1: JWT round-trip — sign and verify ──────────────────────────────
|
||||
#[test]
|
||||
fn test_jwt_sign_and_verify() {
|
||||
let provider = test_provider();
|
||||
let user = test_user();
|
||||
let claims = JwtClaims::new(&user, vec!["user".into()], 3600);
|
||||
let token = provider.encode(&claims);
|
||||
let decoded = provider.decode(&token).unwrap();
|
||||
assert_eq!(decoded.sub, "user-1");
|
||||
assert_eq!(decoded.email, "alice@example.com");
|
||||
assert_eq!(decoded.roles, vec!["user"]);
|
||||
}
|
||||
|
||||
// ── Test 2: JWT invalid signature is rejected ─────────────────────────────
|
||||
#[test]
|
||||
fn test_jwt_invalid_signature() {
|
||||
let provider = test_provider();
|
||||
let other_provider = JwtProvider::new(b"different-secret".to_vec());
|
||||
let user = test_user();
|
||||
let claims = JwtClaims::new(&user, vec![], 3600);
|
||||
let token = other_provider.encode(&claims);
|
||||
let result = provider.decode(&token);
|
||||
assert!(matches!(result, Err(AuthError::TokenInvalid(_))));
|
||||
}
|
||||
|
||||
// ── Test 3: JWT expired token is rejected ────────────────────────────────
|
||||
#[test]
|
||||
fn test_jwt_expired_token() {
|
||||
let provider = test_provider();
|
||||
let user = test_user();
|
||||
// TTL of 0 — expires immediately
|
||||
let claims = JwtClaims::new(&user, vec![], 0);
|
||||
let token = provider.encode(&claims);
|
||||
// Wait a moment (in tests, just check the claims are_expired)
|
||||
assert!(claims.is_expired() || {
|
||||
std::thread::sleep(std::time::Duration::from_millis(1100));
|
||||
true
|
||||
});
|
||||
let result = provider.decode(&token);
|
||||
assert!(matches!(result, Err(AuthError::TokenExpired) | Err(AuthError::TokenInvalid(_))));
|
||||
}
|
||||
|
||||
// ── Test 4: JWT verify returns correct AuthContext ───────────────────────
|
||||
#[test]
|
||||
fn test_jwt_verify_returns_auth_context() {
|
||||
let provider = test_provider();
|
||||
let user = test_user();
|
||||
let reg = registry();
|
||||
let token = provider.issue(user, ®).unwrap();
|
||||
let ctx = provider.verify(&token).unwrap();
|
||||
assert!(ctx.is_authenticated());
|
||||
assert_eq!(ctx.user_id().unwrap(), "user-1");
|
||||
}
|
||||
|
||||
// ── Test 5: AuthContext::has_role works ──────────────────────────────────
|
||||
#[test]
|
||||
fn test_auth_context_has_role() {
|
||||
let ctx = AuthContext::authenticated(
|
||||
test_user(),
|
||||
vec!["admin".into(), "user".into()],
|
||||
"tok",
|
||||
);
|
||||
assert!(ctx.has_role("admin"));
|
||||
assert!(ctx.has_role("user"));
|
||||
assert!(!ctx.has_role("superadmin"));
|
||||
}
|
||||
|
||||
// ── Test 6: AuthContext::has_permission works ─────────────────────────────
|
||||
#[test]
|
||||
fn test_auth_context_has_permission() {
|
||||
let ctx = AuthContext::authenticated(test_user(), vec!["admin".into()], "tok")
|
||||
.with_permissions(vec!["read".into(), "write".into(), "delete".into()]);
|
||||
assert!(ctx.has_permission("read"));
|
||||
assert!(ctx.has_permission("delete"));
|
||||
assert!(!ctx.has_permission("sudo"));
|
||||
}
|
||||
|
||||
// ── Test 7: AuthContext::anonymous is not authenticated ───────────────────
|
||||
#[test]
|
||||
fn test_anonymous_context() {
|
||||
let ctx = AuthContext::anonymous();
|
||||
assert!(!ctx.is_authenticated());
|
||||
assert!(ctx.user_id().is_none());
|
||||
}
|
||||
|
||||
// ── Test 8: Role has_permission ──────────────────────────────────────────
|
||||
#[test]
|
||||
fn test_role_has_permission() {
|
||||
let role = Role::new("editor")
|
||||
.with_permissions(vec!["read", "write"]);
|
||||
assert!(role.has_permission("read"));
|
||||
assert!(role.has_permission("write"));
|
||||
assert!(!role.has_permission("delete"));
|
||||
}
|
||||
|
||||
// ── Test 9: RoleRegistry::has_permission checks across roles ─────────────
|
||||
#[test]
|
||||
fn test_role_registry_permission_check() {
|
||||
let reg = registry();
|
||||
let roles = vec!["user".to_string()];
|
||||
assert!(reg.has_permission(&roles, "read"));
|
||||
assert!(!reg.has_permission(&roles, "delete"));
|
||||
|
||||
let admin_roles = vec!["admin".to_string()];
|
||||
assert!(reg.has_permission(&admin_roles, "delete"));
|
||||
}
|
||||
|
||||
// ── Test 10: SessionProvider issue and verify ─────────────────────────────
|
||||
#[test]
|
||||
fn test_session_issue_and_verify() {
|
||||
let provider = SessionProvider::new();
|
||||
let reg = registry();
|
||||
let session_id = provider.issue(test_user(), ®).unwrap();
|
||||
let ctx = provider.verify(&session_id).unwrap();
|
||||
assert!(ctx.is_authenticated());
|
||||
assert_eq!(ctx.user_id().unwrap(), "user-1");
|
||||
}
|
||||
|
||||
// ── Test 11: SessionProvider revoke removes session ───────────────────────
|
||||
#[test]
|
||||
fn test_session_revoke() {
|
||||
let provider = SessionProvider::new();
|
||||
let reg = registry();
|
||||
let session_id = provider.issue(test_user(), ®).unwrap();
|
||||
provider.revoke(&session_id).unwrap();
|
||||
let result = provider.verify(&session_id);
|
||||
assert!(matches!(result, Err(AuthError::SessionNotFound)));
|
||||
}
|
||||
|
||||
// ── Test 12: SessionProvider unknown session returns error ────────────────
|
||||
#[test]
|
||||
fn test_session_unknown() {
|
||||
let provider = SessionProvider::new();
|
||||
let result = provider.verify("nonexistent-session-id");
|
||||
assert!(matches!(result, Err(AuthError::SessionNotFound)));
|
||||
}
|
||||
|
||||
// ── Test 13: AuthMiddleware extracts Bearer token ─────────────────────────
|
||||
#[test]
|
||||
fn test_middleware_extracts_bearer_token() {
|
||||
let provider = Arc::new(test_provider());
|
||||
let user = test_user();
|
||||
let claims = JwtClaims::new(&user, vec!["user".into()], 3600);
|
||||
let token = provider.encode(&claims);
|
||||
let middleware = AuthMiddleware::new(provider);
|
||||
let header = format!("Bearer {}", token);
|
||||
let ctx = middleware.authenticate_from_header(Some(&header)).unwrap();
|
||||
assert!(ctx.is_authenticated());
|
||||
}
|
||||
|
||||
// ── Test 14: AuthMiddleware with no header returns anonymous ──────────────
|
||||
#[test]
|
||||
fn test_middleware_no_header_anonymous() {
|
||||
let provider = Arc::new(test_provider());
|
||||
let middleware = AuthMiddleware::new(provider);
|
||||
let ctx = middleware.authenticate_from_header(None).unwrap();
|
||||
assert!(!ctx.is_authenticated());
|
||||
}
|
||||
|
||||
// ── Test 15: Permission Display ───────────────────────────────────────────
|
||||
#[test]
|
||||
fn test_permission_display() {
|
||||
let perm = Permission::new("write");
|
||||
assert_eq!(perm.to_string(), "write");
|
||||
assert_eq!(perm.as_str(), "write");
|
||||
}
|
||||
|
||||
// ── Test 16: JwtClaims::to_json and from_json round-trip ─────────────────
|
||||
#[test]
|
||||
fn test_jwt_claims_json_round_trip() {
|
||||
let user = test_user();
|
||||
let claims = JwtClaims::new(&user, vec!["admin".into(), "user".into()], 3600);
|
||||
let json = claims.to_json();
|
||||
let decoded = JwtClaims::from_json(&json).unwrap();
|
||||
assert_eq!(decoded.sub, "user-1");
|
||||
assert_eq!(decoded.email, "alice@example.com");
|
||||
assert_eq!(decoded.roles, vec!["admin", "user"]);
|
||||
}
|
||||
|
||||
// ── Test 17: JWT with multiple roles ─────────────────────────────────────
|
||||
#[test]
|
||||
fn test_jwt_multiple_roles() {
|
||||
let provider = test_provider();
|
||||
let user = test_user();
|
||||
let claims = JwtClaims::new(&user, vec!["admin".into(), "user".into()], 3600);
|
||||
let token = provider.encode(&claims);
|
||||
let decoded = provider.decode(&token).unwrap();
|
||||
assert_eq!(decoded.roles.len(), 2);
|
||||
assert!(decoded.roles.contains(&"admin".to_string()));
|
||||
}
|
||||
|
||||
// ── Test 18: RoleRegistry::role_names lists all roles ────────────────────
|
||||
#[test]
|
||||
fn test_role_registry_names() {
|
||||
let reg = registry();
|
||||
let mut names = reg.role_names();
|
||||
names.sort();
|
||||
assert_eq!(names, vec!["admin", "user"]);
|
||||
}
|
||||
|
||||
// ── Test 19: SessionProvider TTL configuration ────────────────────────────
|
||||
#[test]
|
||||
fn test_session_ttl_config() {
|
||||
let provider = SessionProvider::new().with_ttl(7200);
|
||||
assert_eq!(provider.ttl.as_secs(), 7200);
|
||||
}
|
||||
|
||||
// ── Test 20: AuthMiddleware provider_name returns correct name ────────────
|
||||
#[test]
|
||||
fn test_middleware_provider_name() {
|
||||
let jwt_provider = Arc::new(test_provider());
|
||||
let middleware = AuthMiddleware::new(jwt_provider);
|
||||
assert_eq!(middleware.provider_name(), "jwt");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user