use std::collections::HashMap;
use std::sync::Arc;
use sqlx::PgPool;
use uuid::Uuid;
use super::super::mock_dispatch::{MockJobDispatch, MockWorkflowDispatch};
use crate::function::{AuthContext, RequestMetadata};
use crate::Result;
pub struct TestMutationContext {
pub auth: AuthContext,
pub request: RequestMetadata,
pool: Option<PgPool>,
job_dispatch: Arc<MockJobDispatch>,
workflow_dispatch: Arc<MockWorkflowDispatch>,
}
impl TestMutationContext {
pub fn builder() -> TestMutationContextBuilder {
TestMutationContextBuilder::default()
}
pub fn minimal() -> Self {
Self::builder().build()
}
pub fn authenticated(user_id: Uuid) -> Self {
Self::builder().as_user(user_id).build()
}
pub fn db(&self) -> Option<&PgPool> {
self.pool.as_ref()
}
pub fn job_dispatch(&self) -> &MockJobDispatch {
&self.job_dispatch
}
pub fn workflow_dispatch(&self) -> &MockWorkflowDispatch {
&self.workflow_dispatch
}
pub fn require_user_id(&self) -> Result<Uuid> {
self.auth.require_user_id()
}
pub async fn dispatch_job<T: serde::Serialize>(&self, job_type: &str, args: T) -> Result<Uuid> {
self.job_dispatch.dispatch(job_type, args).await
}
pub async fn start_workflow<T: serde::Serialize>(&self, name: &str, input: T) -> Result<Uuid> {
self.workflow_dispatch.start(name, input).await
}
}
pub struct TestMutationContextBuilder {
user_id: Option<Uuid>,
roles: Vec<String>,
claims: HashMap<String, serde_json::Value>,
pool: Option<PgPool>,
job_dispatch: Arc<MockJobDispatch>,
workflow_dispatch: Arc<MockWorkflowDispatch>,
}
impl Default for TestMutationContextBuilder {
fn default() -> Self {
Self {
user_id: None,
roles: Vec::new(),
claims: HashMap::new(),
pool: None,
job_dispatch: Arc::new(MockJobDispatch::new()),
workflow_dispatch: Arc::new(MockWorkflowDispatch::new()),
}
}
}
impl TestMutationContextBuilder {
pub fn as_user(mut self, id: Uuid) -> Self {
self.user_id = Some(id);
self
}
pub fn with_role(mut self, role: impl Into<String>) -> Self {
self.roles.push(role.into());
self
}
pub fn with_roles(mut self, roles: Vec<String>) -> Self {
self.roles.extend(roles);
self
}
pub fn with_claim(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
self.claims.insert(key.into(), value);
self
}
pub fn with_pool(mut self, pool: PgPool) -> Self {
self.pool = Some(pool);
self
}
pub fn with_job_dispatch(mut self, dispatch: Arc<MockJobDispatch>) -> Self {
self.job_dispatch = dispatch;
self
}
pub fn with_workflow_dispatch(mut self, dispatch: Arc<MockWorkflowDispatch>) -> Self {
self.workflow_dispatch = dispatch;
self
}
pub fn build(self) -> TestMutationContext {
let auth = if let Some(user_id) = self.user_id {
AuthContext::authenticated(user_id, self.roles, self.claims)
} else {
AuthContext::unauthenticated()
};
TestMutationContext {
auth,
request: RequestMetadata::default(),
pool: self.pool,
job_dispatch: self.job_dispatch,
workflow_dispatch: self.workflow_dispatch,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_dispatch_job() {
let ctx = TestMutationContext::authenticated(Uuid::new_v4());
let job_id = ctx
.dispatch_job("send_email", serde_json::json!({"to": "test@example.com"}))
.await
.unwrap();
assert!(!job_id.is_nil());
ctx.job_dispatch().assert_dispatched("send_email");
}
#[tokio::test]
async fn test_start_workflow() {
let ctx = TestMutationContext::authenticated(Uuid::new_v4());
let run_id = ctx
.start_workflow("onboarding", serde_json::json!({"user_id": "123"}))
.await
.unwrap();
assert!(!run_id.is_nil());
ctx.workflow_dispatch().assert_started("onboarding");
}
#[tokio::test]
async fn test_job_not_dispatched() {
let ctx = TestMutationContext::authenticated(Uuid::new_v4());
ctx.job_dispatch().assert_not_dispatched("send_email");
}
}