use actix_web::{test, web, App, HttpResponse};
use chrono::Utc;
use corteq::{Claims, JwtService, TenantContext, TenantExtractor};
use uuid::Uuid;
#[actix_web::test]
async fn test_jwt_token_encode_decode() {
let jwt_service = JwtService::new(b"test-secret");
let tenant_id = Uuid::new_v4();
let user_id = Uuid::new_v4();
let claims = Claims {
sub: user_id,
tenant_id,
roles: vec!["admin".to_string()],
exp: (Utc::now() + chrono::Duration::hours(1)).timestamp(),
iat: Utc::now().timestamp(),
};
let token = jwt_service.encode(&claims).expect("Should encode token");
let decoded = jwt_service.decode(&token).expect("Should decode token");
assert_eq!(decoded.sub, user_id);
assert_eq!(decoded.tenant_id, tenant_id);
assert_eq!(decoded.roles, vec!["admin"]);
}
#[actix_web::test]
async fn test_jwt_tenant_validation() {
let jwt_service = JwtService::new(b"test-secret");
let tenant_id = Uuid::new_v4();
let user_id = Uuid::new_v4();
let claims = Claims {
sub: user_id,
tenant_id,
roles: vec!["user".to_string()],
exp: (Utc::now() + chrono::Duration::hours(1)).timestamp(),
iat: Utc::now().timestamp(),
};
let token = jwt_service.encode(&claims).expect("Should encode token");
assert!(jwt_service.validate_tenant(&token, tenant_id).is_ok());
let wrong_tenant = Uuid::new_v4();
assert!(jwt_service.validate_tenant(&token, wrong_tenant).is_err());
}
#[actix_web::test]
async fn test_expired_token_rejected() {
let jwt_service = JwtService::new(b"test-secret");
let claims = Claims {
sub: Uuid::new_v4(),
tenant_id: Uuid::new_v4(),
roles: vec!["user".to_string()],
exp: (Utc::now() - chrono::Duration::hours(1)).timestamp(), iat: (Utc::now() - chrono::Duration::hours(2)).timestamp(),
};
let token = jwt_service.encode(&claims).expect("Should encode token");
assert!(jwt_service.decode(&token).is_err());
}
#[actix_web::test]
async fn test_tenant_extractor_requires_context() {
async fn handler(tenant: TenantExtractor) -> HttpResponse {
HttpResponse::Ok().json(serde_json::json!({
"tenant_id": tenant.0.tenant_id.to_string()
}))
}
let app = test::init_service(App::new().route("/test", web::get().to(handler))).await;
let req = test::TestRequest::get().uri("/test").to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), 400);
}
#[actix_web::test]
async fn test_tenant_extractor_with_context() {
async fn handler(tenant: TenantExtractor) -> HttpResponse {
HttpResponse::Ok().json(serde_json::json!({
"tenant_id": tenant.0.tenant_id.to_string(),
"tenant_slug": tenant.0.tenant_slug
}))
}
let tenant_id = Uuid::new_v4();
let tenant_context = TenantContext::new(
tenant_id,
"test-tenant".to_string(),
"test-key-id".to_string(),
);
let _app = test::init_service(
App::new()
.app_data(web::Data::new(tenant_context.clone()))
.route("/test", web::get().to(handler)),
)
.await;
let _req = test::TestRequest::get()
.uri("/test")
.data(tenant_context.clone())
.to_request();
}
#[tokio::test]
async fn test_tenant_context_creation_sync() {
let tenant_id = Uuid::new_v4();
let ctx = TenantContext::new(
tenant_id,
"acme-corp".to_string(),
"encryption-key-123".to_string(),
);
assert_eq!(ctx.tenant_id, tenant_id);
assert_eq!(ctx.tenant_slug, "acme-corp");
assert_eq!(ctx.encryption_key_id, "encryption-key-123");
}