pub trait ExecutorFactory: Send + Sync {
type Workflow: WorkflowDefinition;
type Executor: WorkflowExecutor<Self::Workflow> + Send + Sync;
// Required methods
fn seed<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<SeedOf<Self>, String>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait;
fn interrupt_after<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
seeded: &'life1 SeedOf<Self>,
batch: &'life2 BatchOf<Self>,
applied: usize,
) -> Pin<Box<dyn Future<Output = Result<(), String>> + Send + 'async_trait>>
where Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait;
}Expand description
Builds a fresh executor with one case in it, once per check.
§The seed must be deterministic
seed is called more than once inside a single check —
check_interrupted_batch_resumes_to_the_same_state compares an
interrupted run against an uninterrupted one — and the two runs are only
comparable if both start from the same state at the same revision, with the
same account and case identifier. A factory that mints a random case id per
call, or reuses one executor across calls, makes that check meaningless
rather than failing it.
The seeded revision may be anything, including
CaseRevision::ZERO for a case
that does not exist yet, as long as case_ref.expected_revision is the
revision load reports.
Required Associated Types§
Sourcetype Workflow: WorkflowDefinition
type Workflow: WorkflowDefinition
The workflow whose executor is under test.
Required Methods§
Sourcefn seed<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<SeedOf<Self>, String>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn seed<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<SeedOf<Self>, String>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Builds a fresh executor and seeds one case in it.
§Errors
- A description of what went wrong while building or seeding. The suite
turns it into a
ConformanceFailurefor the check that asked.
Sourcefn interrupt_after<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
seeded: &'life1 SeedOf<Self>,
batch: &'life2 BatchOf<Self>,
applied: usize,
) -> Pin<Box<dyn Future<Output = Result<(), String>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn interrupt_after<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
seeded: &'life1 SeedOf<Self>,
batch: &'life2 BatchOf<Self>,
applied: usize,
) -> Pin<Box<dyn Future<Output = Result<(), String>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Commits only the first applied envelopes of batch, as a process that
died mid-batch would have left them.
This is the one thing the suite cannot do through WorkflowExecutor,
and the check that matters most needs it. Implement it against your own
tables, with the same code path execute uses, stopping after applied
envelopes have committed.
If a partial batch is impossible in your executor — every envelope
commits inside one database transaction, so a crash rolls all of them
back — then implement this by executing the whole batch and returning
Ok(()). That is the honest translation: the state a crash can leave
behind is either “none of it”, which needs no resume, or “all of it”,
which the resume must recognise as a replay. The checks then prove that
stronger property instead of a weaker one.
§Errors
- A description of why the prefix could not be committed.
appliedis always at least one and never larger than the batch, so a bounds complaint is a bug in the suite, not in the implementation.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".