Archived
feat: port el-ui vessels — rename crates→vessels, add El source + manifests
This commit is contained in:
@@ -0,0 +1,563 @@
|
||||
//! Comprehensive tests for el-identity.
|
||||
//!
|
||||
//! All tests use `MockEngramClient` — no running Engram instance needed.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
context::IdentityContext,
|
||||
engram::{EngramClient, MockEngramClient},
|
||||
error::IdentityError,
|
||||
guard::AuthGuard,
|
||||
nodes::{
|
||||
OAuthToken, Role, Scope, Session, User,
|
||||
EDGE_GRANTS, EDGE_HAS_ROLE, EDGE_HAS_SESSION,
|
||||
NODE_USER, NODE_ROLE, NODE_SCOPE,
|
||||
},
|
||||
oauth::{PkceChallenge, hash_token},
|
||||
provider::{GoogleOAuth, AppleOAuth, GithubOAuth, OAuthProvider},
|
||||
session::SessionManager,
|
||||
};
|
||||
|
||||
// ── Test helpers ──────────────────────────────────────────────────────────
|
||||
|
||||
fn make_client() -> Arc<MockEngramClient> {
|
||||
Arc::new(MockEngramClient::new())
|
||||
}
|
||||
|
||||
fn make_session_manager(client: Arc<MockEngramClient>) -> Arc<SessionManager> {
|
||||
Arc::new(SessionManager::new(client as Arc<dyn crate::EngramClient>))
|
||||
}
|
||||
|
||||
fn setup_auth_guard() -> (Arc<MockEngramClient>, Arc<SessionManager>, AuthGuard) {
|
||||
let client = make_client();
|
||||
let sm = make_session_manager(client.clone());
|
||||
let guard = AuthGuard::new(
|
||||
client.clone() as Arc<dyn crate::EngramClient>,
|
||||
sm.clone(),
|
||||
);
|
||||
(client, sm, guard)
|
||||
}
|
||||
|
||||
fn make_user() -> User {
|
||||
User::new("alice@example.com", "Alice")
|
||||
}
|
||||
|
||||
fn make_role_admin() -> Role {
|
||||
Role::new("admin")
|
||||
.with_permissions(vec!["users:read", "users:write", "orders:delete"])
|
||||
}
|
||||
|
||||
fn make_role_viewer() -> Role {
|
||||
Role::new("viewer")
|
||||
.with_permissions(vec!["users:read", "orders:read"])
|
||||
}
|
||||
|
||||
fn make_scope_email() -> Scope {
|
||||
Scope::new("email", "Access email address")
|
||||
}
|
||||
|
||||
fn make_scope_profile() -> Scope {
|
||||
Scope::new("profile", "Access profile information")
|
||||
}
|
||||
|
||||
// ── Node tests ────────────────────────────────────────────────────────────
|
||||
|
||||
// Test 1: User node creation and serialization round-trip
|
||||
#[test]
|
||||
fn test_user_node_round_trip() {
|
||||
let user = User::new("bob@example.com", "Bob");
|
||||
let value = user.to_value();
|
||||
let decoded = User::from_value(&value).expect("should decode User");
|
||||
assert_eq!(decoded.email, "bob@example.com");
|
||||
assert_eq!(decoded.display_name, "Bob");
|
||||
assert_eq!(decoded.id, user.id);
|
||||
}
|
||||
|
||||
// Test 2: Role node with permissions serializes correctly
|
||||
#[test]
|
||||
fn test_role_node_permissions() {
|
||||
let role = make_role_admin();
|
||||
assert!(role.has_permission("users:write"));
|
||||
assert!(role.has_permission("orders:delete"));
|
||||
assert!(!role.has_permission("superadmin"));
|
||||
|
||||
let value = role.to_value();
|
||||
let decoded = Role::from_value(&value).expect("should decode Role");
|
||||
assert_eq!(decoded.name, "admin");
|
||||
assert_eq!(decoded.permissions.len(), 3);
|
||||
}
|
||||
|
||||
// Test 3: Scope node round-trip
|
||||
#[test]
|
||||
fn test_scope_node_round_trip() {
|
||||
let scope = make_scope_email();
|
||||
let value = scope.to_value();
|
||||
let decoded = Scope::from_value(&value).expect("should decode Scope");
|
||||
assert_eq!(decoded.name, "email");
|
||||
assert_eq!(decoded.description, "Access email address");
|
||||
}
|
||||
|
||||
// Test 4: Session expiry detection
|
||||
#[test]
|
||||
fn test_session_expiry() {
|
||||
let user_id = Uuid::new_v4();
|
||||
// TTL = -1 → already expired
|
||||
let session = Session::new(user_id, -1, None);
|
||||
assert!(session.is_expired(), "session with negative TTL should be expired");
|
||||
|
||||
let fresh = Session::new(user_id, 3600, None);
|
||||
assert!(!fresh.is_expired(), "fresh session should not be expired");
|
||||
}
|
||||
|
||||
// Test 5: OAuthToken expiry detection
|
||||
#[test]
|
||||
fn test_oauth_token_expiry() {
|
||||
use chrono::{Duration, Utc};
|
||||
let past = Utc::now() - Duration::seconds(1);
|
||||
let token = OAuthToken::new("google", "hash123", None, past, vec![]);
|
||||
assert!(token.is_expired());
|
||||
|
||||
let future = Utc::now() + Duration::seconds(3600);
|
||||
let fresh = OAuthToken::new("google", "hash456", None, future, vec!["email".into()]);
|
||||
assert!(!fresh.is_expired());
|
||||
assert!(fresh.has_scope("email"));
|
||||
assert!(!fresh.has_scope("openid"));
|
||||
}
|
||||
|
||||
// ── MockEngramClient tests ─────────────────────────────────────────────────
|
||||
|
||||
// Test 6: MockEngramClient create and retrieve node
|
||||
#[test]
|
||||
fn test_mock_client_create_and_get() {
|
||||
let client = make_client();
|
||||
let user = make_user();
|
||||
let id = client.create_node(NODE_USER, user.to_value()).unwrap();
|
||||
assert_eq!(id, user.id.to_string());
|
||||
|
||||
let retrieved = client.get_node(&id).unwrap().expect("should find node");
|
||||
let decoded = User::from_value(&retrieved).expect("should decode");
|
||||
assert_eq!(decoded.email, user.email);
|
||||
}
|
||||
|
||||
// Test 7: MockEngramClient get_node returns None for unknown ID
|
||||
#[test]
|
||||
fn test_mock_client_get_unknown_returns_none() {
|
||||
let client = make_client();
|
||||
let result = client.get_node("nonexistent-id").unwrap();
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
// Test 8: MockEngramClient find_nodes with query
|
||||
#[test]
|
||||
fn test_mock_client_find_nodes() {
|
||||
let client = make_client();
|
||||
let u1 = User::new("alice@example.com", "Alice");
|
||||
let u2 = User::new("bob@example.com", "Bob");
|
||||
client.create_node(NODE_USER, u1.to_value()).unwrap();
|
||||
client.create_node(NODE_USER, u2.to_value()).unwrap();
|
||||
|
||||
let query = serde_json::json!({"email": "alice@example.com"});
|
||||
let results = client.find_nodes(NODE_USER, query).unwrap();
|
||||
assert_eq!(results.len(), 1);
|
||||
let found = User::from_value(&results[0]).unwrap();
|
||||
assert_eq!(found.display_name, "Alice");
|
||||
}
|
||||
|
||||
// Test 9: MockEngramClient delete_node removes node and edges
|
||||
#[test]
|
||||
fn test_mock_client_delete_node() {
|
||||
let client = make_client();
|
||||
let user = make_user();
|
||||
let role = make_role_admin();
|
||||
client.create_node(NODE_USER, user.to_value()).unwrap();
|
||||
client.create_node(NODE_ROLE, role.to_value()).unwrap();
|
||||
client.create_edge(&user.id.to_string(), &role.id.to_string(), EDGE_HAS_ROLE).unwrap();
|
||||
assert_eq!(client.count_edges(EDGE_HAS_ROLE), 1);
|
||||
|
||||
client.delete_node(&user.id.to_string()).unwrap();
|
||||
assert!(client.get_node(&user.id.to_string()).unwrap().is_none());
|
||||
// Edge should also be gone
|
||||
assert_eq!(client.count_edges(EDGE_HAS_ROLE), 0);
|
||||
}
|
||||
|
||||
// Test 10: MockEngramClient find_connected traverses edges
|
||||
#[test]
|
||||
fn test_mock_client_find_connected() {
|
||||
let client = make_client();
|
||||
let user = make_user();
|
||||
let role1 = make_role_admin();
|
||||
let role2 = make_role_viewer();
|
||||
client.create_node(NODE_USER, user.to_value()).unwrap();
|
||||
client.create_node(NODE_ROLE, role1.to_value()).unwrap();
|
||||
client.create_node(NODE_ROLE, role2.to_value()).unwrap();
|
||||
client.create_edge(&user.id.to_string(), &role1.id.to_string(), EDGE_HAS_ROLE).unwrap();
|
||||
client.create_edge(&user.id.to_string(), &role2.id.to_string(), EDGE_HAS_ROLE).unwrap();
|
||||
|
||||
let roles = client.find_connected(&user.id.to_string(), EDGE_HAS_ROLE).unwrap();
|
||||
assert_eq!(roles.len(), 2);
|
||||
}
|
||||
|
||||
// ── SessionManager tests ───────────────────────────────────────────────────
|
||||
|
||||
// Test 11: SessionManager creates session and connects to user
|
||||
#[test]
|
||||
fn test_session_manager_create() {
|
||||
let client = make_client();
|
||||
let user = make_user();
|
||||
client.create_node(NODE_USER, user.to_value()).unwrap();
|
||||
|
||||
let sm = make_session_manager(client.clone());
|
||||
let session = sm.create(user.id, Some("127.0.0.1".into())).unwrap();
|
||||
assert_eq!(session.user_id, user.id);
|
||||
assert_eq!(session.ip_address, Some("127.0.0.1".to_string()));
|
||||
|
||||
// Should have created a has_session edge
|
||||
assert_eq!(client.count_edges(EDGE_HAS_SESSION), 1);
|
||||
}
|
||||
|
||||
// Test 12: SessionManager validate succeeds for fresh session
|
||||
#[test]
|
||||
fn test_session_manager_validate_fresh() {
|
||||
let client = make_client();
|
||||
let user = make_user();
|
||||
client.create_node(NODE_USER, user.to_value()).unwrap();
|
||||
let sm = make_session_manager(client.clone());
|
||||
let session = sm.create(user.id, None).unwrap();
|
||||
|
||||
let validated = sm.validate(&session.id.to_string()).unwrap();
|
||||
assert_eq!(validated.id, session.id);
|
||||
}
|
||||
|
||||
// Test 13: SessionManager validate fails for expired session
|
||||
#[test]
|
||||
fn test_session_manager_validate_expired() {
|
||||
let client = make_client();
|
||||
let sm = Arc::new(SessionManager::new(client.clone() as Arc<dyn crate::EngramClient>).with_ttl(-1));
|
||||
let user = make_user();
|
||||
client.create_node(NODE_USER, user.to_value()).unwrap();
|
||||
|
||||
let session = sm.create(user.id, None).unwrap();
|
||||
let result = sm.validate(&session.id.to_string());
|
||||
assert!(matches!(result, Err(IdentityError::SessionExpired)));
|
||||
}
|
||||
|
||||
// Test 14: SessionManager validate fails for missing session
|
||||
#[test]
|
||||
fn test_session_manager_validate_missing() {
|
||||
let client = make_client();
|
||||
let sm = make_session_manager(client.clone());
|
||||
let result = sm.validate("nonexistent-session-id");
|
||||
assert!(matches!(result, Err(IdentityError::SessionNotFound)));
|
||||
}
|
||||
|
||||
// Test 15: SessionManager invalidate removes session
|
||||
#[test]
|
||||
fn test_session_manager_invalidate() {
|
||||
let client = make_client();
|
||||
let user = make_user();
|
||||
client.create_node(NODE_USER, user.to_value()).unwrap();
|
||||
let sm = make_session_manager(client.clone());
|
||||
let session = sm.create(user.id, None).unwrap();
|
||||
let session_id = session.id.to_string();
|
||||
|
||||
sm.invalidate(&session_id).unwrap();
|
||||
assert!(matches!(sm.validate(&session_id), Err(IdentityError::SessionNotFound)));
|
||||
}
|
||||
|
||||
// Test 16: SessionManager list_for_user returns active sessions
|
||||
#[test]
|
||||
fn test_session_manager_list_for_user() {
|
||||
let client = make_client();
|
||||
let user = make_user();
|
||||
client.create_node(NODE_USER, user.to_value()).unwrap();
|
||||
let sm = make_session_manager(client.clone());
|
||||
|
||||
sm.create(user.id, Some("1.1.1.1".into())).unwrap();
|
||||
sm.create(user.id, Some("2.2.2.2".into())).unwrap();
|
||||
|
||||
let sessions = sm.list_for_user(&user.id.to_string()).unwrap();
|
||||
assert_eq!(sessions.len(), 2);
|
||||
}
|
||||
|
||||
// ── AuthGuard tests ───────────────────────────────────────────────────────
|
||||
|
||||
// Test 17: AuthGuard authenticates user with role and scope
|
||||
#[test]
|
||||
fn test_auth_guard_full_resolution() {
|
||||
let (client, sm, guard) = setup_auth_guard();
|
||||
|
||||
let user = make_user();
|
||||
let role = make_role_admin();
|
||||
let scope = make_scope_email();
|
||||
|
||||
// Register user, role, scope, and wire edges
|
||||
client.create_node(NODE_USER, user.to_value()).unwrap();
|
||||
client.create_node(NODE_ROLE, role.to_value()).unwrap();
|
||||
client.create_node(NODE_SCOPE, scope.to_value()).unwrap();
|
||||
client.create_edge(&user.id.to_string(), &role.id.to_string(), EDGE_HAS_ROLE).unwrap();
|
||||
client.create_edge(&role.id.to_string(), &scope.id.to_string(), EDGE_GRANTS).unwrap();
|
||||
|
||||
// Create session
|
||||
let session = sm.create(user.id, None).unwrap();
|
||||
|
||||
// Authenticate
|
||||
let ctx = guard.authenticate(&session.id.to_string()).unwrap();
|
||||
assert_eq!(ctx.user.email, "alice@example.com");
|
||||
assert_eq!(ctx.roles.len(), 1);
|
||||
assert!(ctx.has_role("admin"));
|
||||
assert!(ctx.has_scope("email"));
|
||||
assert!(ctx.has_permission("users:write"));
|
||||
}
|
||||
|
||||
// Test 18: AuthGuard rejects unknown session
|
||||
#[test]
|
||||
fn test_auth_guard_rejects_unknown_session() {
|
||||
let (_, _, guard) = setup_auth_guard();
|
||||
let result = guard.authenticate("no-such-session");
|
||||
assert!(matches!(result, Err(IdentityError::SessionNotFound)));
|
||||
}
|
||||
|
||||
// Test 19: AuthGuard require_role succeeds for correct role
|
||||
#[test]
|
||||
fn test_auth_guard_require_role_success() {
|
||||
let (client, sm, guard) = setup_auth_guard();
|
||||
let user = make_user();
|
||||
let role = make_role_admin();
|
||||
client.create_node(NODE_USER, user.to_value()).unwrap();
|
||||
client.create_node(NODE_ROLE, role.to_value()).unwrap();
|
||||
client.create_edge(&user.id.to_string(), &role.id.to_string(), EDGE_HAS_ROLE).unwrap();
|
||||
let session = sm.create(user.id, None).unwrap();
|
||||
let ctx = guard.authenticate(&session.id.to_string()).unwrap();
|
||||
|
||||
assert!(guard.require_role(&ctx, "admin").is_ok());
|
||||
}
|
||||
|
||||
// Test 20: AuthGuard require_role fails for missing role
|
||||
#[test]
|
||||
fn test_auth_guard_require_role_forbidden() {
|
||||
let (client, sm, guard) = setup_auth_guard();
|
||||
let user = make_user();
|
||||
client.create_node(NODE_USER, user.to_value()).unwrap();
|
||||
let session = sm.create(user.id, None).unwrap();
|
||||
let ctx = guard.authenticate(&session.id.to_string()).unwrap();
|
||||
|
||||
let result = guard.require_role(&ctx, "superadmin");
|
||||
assert!(matches!(result, Err(IdentityError::Forbidden(_))));
|
||||
}
|
||||
|
||||
// Test 21: AuthGuard require_scope fails for missing scope
|
||||
#[test]
|
||||
fn test_auth_guard_require_scope_forbidden() {
|
||||
let (client, sm, guard) = setup_auth_guard();
|
||||
let user = make_user();
|
||||
client.create_node(NODE_USER, user.to_value()).unwrap();
|
||||
let session = sm.create(user.id, None).unwrap();
|
||||
let ctx = guard.authenticate(&session.id.to_string()).unwrap();
|
||||
|
||||
let result = guard.require_scope(&ctx, "admin:write");
|
||||
assert!(matches!(result, Err(IdentityError::ScopeForbidden(_))));
|
||||
}
|
||||
|
||||
// Test 22: AuthGuard register_user and assign_role
|
||||
#[test]
|
||||
fn test_auth_guard_register_and_assign_role() {
|
||||
let (_client, sm, guard) = setup_auth_guard();
|
||||
let user = make_user();
|
||||
let role = make_role_viewer();
|
||||
|
||||
guard.register_user(&user).unwrap();
|
||||
guard.assign_role(&user.id.to_string(), &role).unwrap();
|
||||
|
||||
let session = sm.create(user.id, None).unwrap();
|
||||
let ctx = guard.authenticate(&session.id.to_string()).unwrap();
|
||||
assert!(ctx.has_role("viewer"));
|
||||
assert!(ctx.has_permission("users:read"));
|
||||
assert!(!ctx.has_permission("orders:delete"));
|
||||
}
|
||||
|
||||
// ── PKCE tests ────────────────────────────────────────────────────────────
|
||||
|
||||
// Test 23: PkceChallenge generates valid verifier/challenge pair
|
||||
#[test]
|
||||
fn test_pkce_challenge_generates_valid_pair() {
|
||||
let pkce = PkceChallenge::generate();
|
||||
assert!(!pkce.verifier.is_empty(), "verifier should not be empty");
|
||||
assert!(!pkce.challenge.is_empty(), "challenge should not be empty");
|
||||
assert_ne!(pkce.verifier, pkce.challenge, "verifier and challenge must differ");
|
||||
assert_eq!(pkce.method, "S256");
|
||||
}
|
||||
|
||||
// Test 24: PkceChallenge verify matches correct verifier
|
||||
#[test]
|
||||
fn test_pkce_challenge_verify_correct() {
|
||||
let pkce = PkceChallenge::generate();
|
||||
assert!(pkce.verify(&pkce.verifier.clone()), "should verify correct verifier");
|
||||
}
|
||||
|
||||
// Test 25: PkceChallenge verify rejects wrong verifier
|
||||
#[test]
|
||||
fn test_pkce_challenge_verify_rejects_wrong() {
|
||||
let pkce = PkceChallenge::generate();
|
||||
assert!(!pkce.verify("wrong-verifier-value"), "should reject wrong verifier");
|
||||
}
|
||||
|
||||
// Test 26: hash_token is deterministic
|
||||
#[test]
|
||||
fn test_hash_token_deterministic() {
|
||||
let token = "my-secret-access-token";
|
||||
let h1 = hash_token(token);
|
||||
let h2 = hash_token(token);
|
||||
assert_eq!(h1, h2, "hash must be deterministic");
|
||||
assert_ne!(h1, token, "hash must differ from input");
|
||||
assert_eq!(h1.len(), 64, "SHA-256 hex output is 64 chars");
|
||||
}
|
||||
|
||||
// ── Provider tests ────────────────────────────────────────────────────────
|
||||
|
||||
// Test 27: GoogleOAuth builds correct authorization URL
|
||||
#[test]
|
||||
fn test_google_oauth_authorization_url() {
|
||||
let provider = GoogleOAuth::new("client-id-123", "secret");
|
||||
let url = provider.authorization_url(
|
||||
"https://myapp.com/callback",
|
||||
"pkce-challenge-abc",
|
||||
"state-xyz",
|
||||
&[],
|
||||
);
|
||||
assert!(url.starts_with("https://accounts.google.com/o/oauth2/v2/auth"));
|
||||
assert!(url.contains("client_id=client-id-123"));
|
||||
assert!(url.contains("code_challenge=pkce-challenge-abc"));
|
||||
assert!(url.contains("code_challenge_method=S256"));
|
||||
assert!(url.contains("state=state-xyz"));
|
||||
assert!(url.contains("access_type=offline"), "Google needs offline for refresh tokens");
|
||||
}
|
||||
|
||||
// Test 28: GoogleOAuth default scopes include openid, email, profile
|
||||
#[test]
|
||||
fn test_google_oauth_default_scopes() {
|
||||
let provider = GoogleOAuth::new("id", "secret");
|
||||
let scopes = provider.default_scopes();
|
||||
assert!(scopes.contains(&"openid"));
|
||||
assert!(scopes.contains(&"email"));
|
||||
assert!(scopes.contains(&"profile"));
|
||||
}
|
||||
|
||||
// Test 29: AppleOAuth builds correct authorization URL with form_post
|
||||
#[test]
|
||||
fn test_apple_oauth_authorization_url() {
|
||||
let provider = AppleOAuth::new("com.example.app", "jwt-secret", "TEAMID");
|
||||
let url = provider.authorization_url(
|
||||
"https://myapp.com/callback",
|
||||
"challenge",
|
||||
"state",
|
||||
&[],
|
||||
);
|
||||
assert!(url.starts_with("https://appleid.apple.com/auth/authorize"));
|
||||
assert!(url.contains("response_mode=form_post"), "Apple requires form_post");
|
||||
}
|
||||
|
||||
// Test 30: GithubOAuth ignores PKCE in URL (GitHub doesn't support it)
|
||||
#[test]
|
||||
fn test_github_oauth_no_pkce_in_url() {
|
||||
let provider = GithubOAuth::new("gh-client-id", "gh-secret");
|
||||
let url = provider.authorization_url(
|
||||
"https://myapp.com/callback",
|
||||
"pkce-challenge-ignored",
|
||||
"state",
|
||||
&[],
|
||||
);
|
||||
assert!(url.starts_with("https://github.com/login/oauth/authorize"));
|
||||
assert!(!url.contains("code_challenge"), "GitHub doesn't support PKCE");
|
||||
assert!(url.contains("scope=user%3Aemail+read%3Auser") || url.contains("scope="));
|
||||
}
|
||||
|
||||
// Test 31: GithubOAuth refresh_token returns error
|
||||
#[test]
|
||||
fn test_github_oauth_refresh_returns_error() {
|
||||
let provider = GithubOAuth::new("id", "secret");
|
||||
let result = provider.refresh_token("some-refresh-token");
|
||||
assert!(matches!(result, Err(IdentityError::TokenRefreshFailed(_))));
|
||||
}
|
||||
|
||||
// ── IdentityContext tests ─────────────────────────────────────────────────
|
||||
|
||||
// Test 32: IdentityContext role_names and scope_names
|
||||
#[test]
|
||||
fn test_identity_context_names() {
|
||||
let user = make_user();
|
||||
let session = Session::new(user.id, 3600, None);
|
||||
let roles = vec![make_role_admin(), make_role_viewer()];
|
||||
let scopes = vec![make_scope_email(), make_scope_profile()];
|
||||
|
||||
let ctx = IdentityContext::new(user.clone(), session, roles, scopes);
|
||||
let mut role_names = ctx.role_names();
|
||||
role_names.sort();
|
||||
assert_eq!(role_names, vec!["admin", "viewer"]);
|
||||
|
||||
let mut scope_names = ctx.scope_names();
|
||||
scope_names.sort();
|
||||
assert_eq!(scope_names, vec!["email", "profile"]);
|
||||
}
|
||||
|
||||
// Test 33: IdentityContext has_permission across multiple roles
|
||||
#[test]
|
||||
fn test_identity_context_permission_across_roles() {
|
||||
let user = make_user();
|
||||
let session = Session::new(user.id, 3600, None);
|
||||
let roles = vec![make_role_viewer()]; // viewer has users:read and orders:read
|
||||
let ctx = IdentityContext::new(user, session, roles, vec![]);
|
||||
|
||||
assert!(ctx.has_permission("users:read"));
|
||||
assert!(ctx.has_permission("orders:read"));
|
||||
assert!(!ctx.has_permission("users:write"));
|
||||
assert!(!ctx.has_permission("orders:delete"));
|
||||
}
|
||||
|
||||
// Test 34: AuthGuard assign_scope_to_role wires scope into context
|
||||
#[test]
|
||||
fn test_auth_guard_assign_scope_to_role() {
|
||||
let (_client, sm, guard) = setup_auth_guard();
|
||||
let user = make_user();
|
||||
let role = make_role_admin();
|
||||
let scope = make_scope_profile();
|
||||
|
||||
guard.register_user(&user).unwrap();
|
||||
guard.assign_role(&user.id.to_string(), &role).unwrap();
|
||||
guard.assign_scope_to_role(&role, &scope).unwrap();
|
||||
|
||||
let session = sm.create(user.id, None).unwrap();
|
||||
let ctx = guard.authenticate(&session.id.to_string()).unwrap();
|
||||
assert!(ctx.has_scope("profile"));
|
||||
}
|
||||
|
||||
// Test 35: Two different users have independent sessions
|
||||
#[test]
|
||||
fn test_independent_user_sessions() {
|
||||
let (client, sm, guard) = setup_auth_guard();
|
||||
|
||||
let alice = User::new("alice@example.com", "Alice");
|
||||
let bob = User::new("bob@example.com", "Bob");
|
||||
let admin_role = make_role_admin();
|
||||
|
||||
client.create_node(NODE_USER, alice.to_value()).unwrap();
|
||||
client.create_node(NODE_USER, bob.to_value()).unwrap();
|
||||
client.create_node(NODE_ROLE, admin_role.to_value()).unwrap();
|
||||
// Only Alice gets the admin role
|
||||
client.create_edge(&alice.id.to_string(), &admin_role.id.to_string(), EDGE_HAS_ROLE).unwrap();
|
||||
|
||||
let alice_session = sm.create(alice.id, None).unwrap();
|
||||
let bob_session = sm.create(bob.id, None).unwrap();
|
||||
|
||||
let alice_ctx = guard.authenticate(&alice_session.id.to_string()).unwrap();
|
||||
let bob_ctx = guard.authenticate(&bob_session.id.to_string()).unwrap();
|
||||
|
||||
assert!(alice_ctx.has_role("admin"), "Alice should have admin");
|
||||
assert!(!bob_ctx.has_role("admin"), "Bob should not have admin");
|
||||
assert_eq!(alice_ctx.email(), "alice@example.com");
|
||||
assert_eq!(bob_ctx.email(), "bob@example.com");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user