use std::sync::{Arc, Weak};
use aion_core::{RunId, WorkflowId, WorkflowStatus};
use aion_package::ContentHash;
use aion_store::InMemoryStore;
use super::*;
use crate::durability::Recorder;
use crate::engine_seam::{EngineHandle, WorkflowProcessHandle, WorkflowResidency};
use crate::registry::{
CompletionNotifier, HandleResidency, Registry, WorkflowHandle, WorkflowHandleParts,
};
type TestResult = Result<(), Box<dyn std::error::Error>>;
const PREDECESSOR_PID: u64 = 4_001;
const SUCCESSOR_PID: u64 = 4_002;
fn handle_for(
store: &Arc<InMemoryStore>,
workflow_id: &WorkflowId,
run_id: &RunId,
pid: u64,
cached_status: WorkflowStatus,
) -> WorkflowHandle {
WorkflowHandle::new(WorkflowHandleParts {
workflow_id: workflow_id.clone(),
run_id: run_id.clone(),
pid,
workflow_type: "checkout".to_owned(),
namespace: String::from("default"),
loaded_version: ContentHash::from_bytes([9; 32]),
cached_status,
residency: HandleResidency::Resident,
recorder: Recorder::resume_at(workflow_id.clone(), Arc::clone(store) as _, 0),
completion: CompletionNotifier::new(),
})
}
fn seam(registry: &Arc<Registry>) -> QueryMailboxEngine {
QueryMailboxEngine::new(Arc::clone(registry), Weak::new(), Weak::new())
}
#[test]
fn a_continued_workflow_resolves_to_its_current_run() -> TestResult {
let registry = Arc::new(Registry::default());
let store = Arc::new(InMemoryStore::default());
let workflow_id = WorkflowId::new_v4();
let predecessor_run = RunId::new_v4();
let successor_run = RunId::new_v4();
registry.insert(
(workflow_id.clone(), predecessor_run.clone()),
handle_for(
&store,
&workflow_id,
&predecessor_run,
PREDECESSOR_PID,
WorkflowStatus::ContinuedAsNew,
),
)?;
registry.insert(
(workflow_id.clone(), successor_run.clone()),
handle_for(
&store,
&workflow_id,
&successor_run,
SUCCESSOR_PID,
WorkflowStatus::Running,
),
)?;
assert_eq!(
seam(®istry).resolve_workflow(&workflow_id)?,
WorkflowResidency::Resident(WorkflowProcessHandle::new(SUCCESSOR_PID)),
"the seam must name the current run, not whichever handle a HashMap scan yielded"
);
Ok(())
}
#[test]
fn a_stale_predecessor_handle_without_an_index_entry_is_unknown() -> TestResult {
let registry = Arc::new(Registry::default());
let store = Arc::new(InMemoryStore::default());
let workflow_id = WorkflowId::new_v4();
let predecessor_run = RunId::new_v4();
let successor_run = RunId::new_v4();
registry.insert(
(workflow_id.clone(), predecessor_run.clone()),
handle_for(
&store,
&workflow_id,
&predecessor_run,
PREDECESSOR_PID,
WorkflowStatus::ContinuedAsNew,
),
)?;
registry.insert(
(workflow_id.clone(), successor_run.clone()),
handle_for(
&store,
&workflow_id,
&successor_run,
SUCCESSOR_PID,
WorkflowStatus::Running,
),
)?;
registry.remove(&workflow_id, &successor_run)?;
assert!(
registry.get(&workflow_id, &predecessor_run)?.is_some(),
"premise: the predecessor's handle outlives the successor's removal"
);
assert_eq!(
seam(®istry).resolve_workflow(&workflow_id)?,
WorkflowResidency::Unknown,
"no current run means Unknown, not the stale predecessor's Terminal"
);
Ok(())
}
#[test]
fn the_live_run_index_follows_registration_order() -> TestResult {
let registry = Arc::new(Registry::default());
let store = Arc::new(InMemoryStore::default());
let workflow_id = WorkflowId::new_v4();
let predecessor_run = RunId::new_v4();
let successor_run = RunId::new_v4();
registry.insert(
(workflow_id.clone(), successor_run.clone()),
handle_for(
&store,
&workflow_id,
&successor_run,
SUCCESSOR_PID,
WorkflowStatus::Running,
),
)?;
registry.insert(
(workflow_id.clone(), predecessor_run.clone()),
handle_for(
&store,
&workflow_id,
&predecessor_run,
PREDECESSOR_PID,
WorkflowStatus::ContinuedAsNew,
),
)?;
assert_eq!(
registry.live_run_pid(&workflow_id)?,
Some((predecessor_run, PREDECESSOR_PID)),
"the index names the LAST run registered, whatever its age"
);
assert_eq!(
seam(®istry).resolve_workflow(&workflow_id)?,
WorkflowResidency::Terminal,
"so a superseded run registered last would be the run this seam answers for"
);
Ok(())
}