.NET API
WorkflowResponseStoreContract
Tracon.Testing.Contracts.Xunit.dllBehavior tests for the IWorkflowResponseStore contract.
public abstract class WorkflowResponseStoreContract : TenantIsolationContract<IWorkflowResponseStore>, IAsyncLifetime, IAsyncDisposable- Inheritance
- object ← StoreCancellationContract<IWorkflowResponseStore> ← TenantIsolationContract<IWorkflowResponseStore> ← WorkflowResponseStoreContract
- Implements
- IAsyncLifetime, IAsyncDisposable
- Inherited Members
- TenantIsolationContract<IWorkflowResponseStore>.TenantA, TenantIsolationContract<IWorkflowResponseStore>.TenantB, TenantIsolationContract<IWorkflowResponseStore>.UseAmbientTenant(MutableTenantContext), TenantIsolationContract<IWorkflowResponseStore>.SeedAsync(string, string, CancellationToken), TenantIsolationContract<IWorkflowResponseStore>.ExistsAsync(string, object), TenantIsolationContract<IWorkflowResponseStore>.CountAsync(string, CancellationToken), TenantIsolationContract<IWorkflowResponseStore>.TryDeleteAsync(string, object), TenantIsolationContract<IWorkflowResponseStore>.TryOverwriteAsync(string, string), TenantIsolationContract<IWorkflowResponseStore>.CancellableReadAsync(CancellationToken), TenantIsolationContract<IWorkflowResponseStore>.CancellableWriteAsync(CancellationToken), TenantIsolationContract<IWorkflowResponseStore>.WroteAnythingAsync(), TenantIsolationContract<IWorkflowResponseStore>.Tenant_cannot_read_another_tenants_record(), TenantIsolationContract<IWorkflowResponseStore>.Tenant_reads_its_own_record(), TenantIsolationContract<IWorkflowResponseStore>.Tenant_does_not_see_another_tenants_record_in_the_list(), TenantIsolationContract<IWorkflowResponseStore>.Tenant_cannot_delete_another_tenants_record(), TenantIsolationContract<IWorkflowResponseStore>.Same_name_lives_independently_across_two_tenants(), TenantIsolationContract<IWorkflowResponseStore>.AmbientTenant, StoreCancellationContract<IWorkflowResponseStore>.CreateStoreAsync(), StoreCancellationContract<IWorkflowResponseStore>.InitializeAsync(), StoreCancellationContract<IWorkflowResponseStore>.DisposeAsync(), StoreCancellationContract<IWorkflowResponseStore>.OnDisposeAsync(), StoreCancellationContract<IWorkflowResponseStore>.CancellableReadAsync(CancellationToken), StoreCancellationContract<IWorkflowResponseStore>.CancellableWriteAsync(CancellationToken), StoreCancellationContract<IWorkflowResponseStore>.WroteAnythingAsync(), StoreCancellationContract<IWorkflowResponseStore>.Canceled_token_throws_on_read(), StoreCancellationContract<IWorkflowResponseStore>.The_write_this_contract_checks_can_be_read_back(), StoreCancellationContract<IWorkflowResponseStore>.Canceled_token_throws_on_write_and_leaves_no_trace(), StoreCancellationContract<IWorkflowResponseStore>.Store, object.GetType(), object.MemberwiseClone(), object.ToString(), object.Equals(object?), object.Equals(object?, object?), object.ReferenceEquals(object?, object?), object.GetHashCode()
Remarks
Section titled “Remarks”The store is the authority of a single-answer workflow: two concurrent answers to one request must leave exactly one claim, and two concurrent re-drives of a stopped answer must leave exactly one replacement. A unit test of the endpoint cannot prove either; only the store can, which is why the races are cases here.
The TenantIsolationContract.TryDeleteAsync hook is wired to IWorkflowResponseStore.ReplaceAsync, the one mutation of an existing record a caller asks for: another tenant must not be able to abandon a record.
Constructors
Section titled “Constructors”WorkflowResponseStoreContract()
Section titled “ WorkflowResponseStoreContract()”protected WorkflowResponseStoreContract()Methods
Section titled “Methods”A_claim_is_found_by_its_scope_and_request()
Section titled “ A_claim_is_found_by_its_scope_and_request()”[Fact]public Task A_claim_is_found_by_its_scope_and_request()Returns
Section titled “Returns”A_delivered_record_can_be_replaced_too()
Section titled “ A_delivered_record_can_be_replaced_too()”[Fact]public Task A_delivered_record_can_be_replaced_too()Returns
Section titled “Returns”A_recorded_answer_is_read_back_with_every_field()
Section titled “ A_recorded_answer_is_read_back_with_every_field()”[Fact]public Task A_recorded_answer_is_read_back_with_every_field()Returns
Section titled “Returns”A_second_answer_to_a_claimed_request_is_not_written_and_names_the_first()
Section titled “ A_second_answer_to_a_claimed_request_is_not_written_and_names_the_first()”[Fact]public Task A_second_answer_to_a_claimed_request_is_not_written_and_names_the_first()Returns
Section titled “Returns”An_abandoned_record_cannot_be_replaced_again_and_nothing_is_written()
Section titled “ An_abandoned_record_cannot_be_replaced_again_and_nothing_is_written()”[Fact]public Task An_abandoned_record_cannot_be_replaced_again_and_nothing_is_written()Returns
Section titled “Returns”Another_tenant_cannot_mark_a_record_delivered()
Section titled “ Another_tenant_cannot_mark_a_record_delivered()”[Fact]public Task Another_tenant_cannot_mark_a_record_delivered()Returns
Section titled “Returns”Answers_without_a_claim_are_all_written()
Section titled “ Answers_without_a_claim_are_all_written()”[Fact]public Task Answers_without_a_claim_are_all_written()Returns
Section titled “Returns”CancellableReadAsync(CancellationToken)
Section titled “ CancellableReadAsync(CancellationToken)”Calls one read method of the store with the given token.
protected override ValueTask CancellableReadAsync(CancellationToken cancellationToken)Parameters
Section titled “Parameters”cancellationToken CancellationToken
The token the store must observe.
Returns
Section titled “Returns”The completion task; any result is discarded.
Remarks
Section titled “Remarks”The listing hook is the read: it needs no key, so it can be called on an untouched store. A contract whose store has no listing endpoint – and whose TenantIsolationContract.CountAsync therefore makes no store call at all on an empty store – overrides this with its own read.
Concurrent_answers_to_one_request_leave_exactly_one_claim()
Section titled “ Concurrent_answers_to_one_request_leave_exactly_one_claim()”[Fact]public Task Concurrent_answers_to_one_request_leave_exactly_one_claim()Returns
Section titled “Returns”Concurrent_replacements_of_one_record_leave_exactly_one_claim()
Section titled “ Concurrent_replacements_of_one_record_leave_exactly_one_claim()”[Fact]public Task Concurrent_replacements_of_one_record_leave_exactly_one_claim()Returns
Section titled “Returns”CountAsync(string, CancellationToken)
Section titled “ CountAsync(string, CancellationToken)”Reports how many records the given tenant sees through the listing endpoint.
protected override ValueTask<int> CountAsync(string tenantId, CancellationToken cancellationToken)Parameters
Section titled “Parameters”tenantId string
The reading tenant.
cancellationToken CancellationToken
The token every store call inside the hook must be handed; see TenantIsolationContract.SeedAsync.
Returns
Section titled “Returns”The number of records seen.
EraseRunAsync(Guid, string)
Section titled “ EraseRunAsync(Guid, string)”Erases a run the way a data subject erasure does.
protected virtual ValueTask<bool> EraseRunAsync(Guid runId, string tenantId)Parameters
Section titled “Parameters”runId Guid
The run to erase.
tenantId string
The run’s tenant.
Returns
Section titled “Returns”true when the run was erased; false when the store under test has no run table to erase from, and the case does not apply.
Erasing_the_source_run_removes_its_answers()
Section titled “ Erasing_the_source_run_removes_its_answers()”[Fact]public Task Erasing_the_source_run_removes_its_answers()Returns
Section titled “Returns”ExistsAsync(string, object)
Section titled “ ExistsAsync(string, object)”Reports whether the record is visible from the given tenant’s viewpoint.
protected override ValueTask<bool> ExistsAsync(string tenantId, object key)Parameters
Section titled “Parameters”tenantId string
The reading tenant.
key object
The key returned by TenantIsolationContract.SeedAsync.
Returns
Section titled “Returns”true if the record is visible.
Marking_delivered_moves_only_an_accepted_record()
Section titled “ Marking_delivered_moves_only_an_accepted_record()”[Fact]public Task Marking_delivered_moves_only_an_accepted_record()Returns
Section titled “Returns”PrepareRunAsync(Guid, string, string)
Section titled “ PrepareRunAsync(Guid, string, string)”Opens the run row an answer’s source run id points at.
protected virtual ValueTask PrepareRunAsync(Guid runId, string tenantId, string sessionId)Parameters
Section titled “Parameters”runId Guid
The run id.
tenantId string
The tenant id.
sessionId string
The run’s execution session.
Returns
Section titled “Returns”The completion task.
Remarks
Section titled “Remarks”In the SQL implementations workflow_responses.source_run_id is a foreign key into
runs; the in-memory implementation has no such link, and the hook does nothing.
Replacing_abandons_the_record_and_hands_the_claim_to_the_replacement()
Section titled “ Replacing_abandons_the_record_and_hands_the_claim_to_the_replacement()”[Fact]public Task Replacing_abandons_the_record_and_hands_the_claim_to_the_replacement()Returns
Section titled “Returns”Replacing_an_unknown_record_writes_nothing()
Section titled “ Replacing_an_unknown_record_writes_nothing()”[Fact]public Task Replacing_an_unknown_record_writes_nothing()Returns
Section titled “Returns”SeedAsync(string, string, CancellationToken)
Section titled “ SeedAsync(string, string, CancellationToken)”Writes a sample record for the given tenant.
protected override ValueTask<object> SeedAsync(string tenantId, string name, CancellationToken cancellationToken)Parameters
Section titled “Parameters”tenantId string
The tenant that owns the record.
name string
The name distinguishing the record. The same name may be used by two tenants.
cancellationToken CancellationToken
The token every store call inside the hook must be handed. The cancellation contract calls this hook with a token that is already cancelled, so a hook that drops the token reports a store as sound when it is not.
Returns
Section titled “Returns”The key to use for reading the record back.
The_answers_of_a_source_are_listed_oldest_first()
Section titled “ The_answers_of_a_source_are_listed_oldest_first()”[Fact]public Task The_answers_of_a_source_are_listed_oldest_first()Returns
Section titled “Returns”TryDeleteAsync(string, object)
Section titled “ TryDeleteAsync(string, object)”Attempts to delete the record on behalf of the given tenant.
protected override ValueTask<bool?> TryDeleteAsync(string tenantId, object key)Parameters
Section titled “Parameters”tenantId string
The tenant attempting the delete.
key object
The key returned by TenantIsolationContract.SeedAsync.
Returns
Section titled “Returns”true if the delete succeeded, false if the record was not found; null if the store does not offer deletion.
Two_tenants_claim_the_same_scope_and_request_without_seeing_each_other()
Section titled “ Two_tenants_claim_the_same_scope_and_request_without_seeing_each_other()”[Fact]public Task Two_tenants_claim_the_same_scope_and_request_without_seeing_each_other()