.NET API
RetentionStoreContract
Tracon.Testing.Contracts.Xunit.dllBehavior and tenant isolation tests for the IRetentionStore data plane.
public abstract class RetentionStoreContract : StoreCancellationContract<IRetentionStore>, IAsyncLifetime, IAsyncDisposable- Inheritance
- object ← StoreCancellationContract<IRetentionStore> ← RetentionStoreContract
- Implements
- IAsyncLifetime, IAsyncDisposable
- Inherited Members
- StoreCancellationContract<IRetentionStore>.CreateStoreAsync(), StoreCancellationContract<IRetentionStore>.InitializeAsync(), StoreCancellationContract<IRetentionStore>.DisposeAsync(), StoreCancellationContract<IRetentionStore>.OnDisposeAsync(), StoreCancellationContract<IRetentionStore>.CancellableReadAsync(CancellationToken), StoreCancellationContract<IRetentionStore>.CancellableWriteAsync(CancellationToken), StoreCancellationContract<IRetentionStore>.WroteAnythingAsync(), StoreCancellationContract<IRetentionStore>.Canceled_token_throws_on_read(), StoreCancellationContract<IRetentionStore>.The_write_this_contract_checks_can_be_read_back(), StoreCancellationContract<IRetentionStore>.Canceled_token_throws_on_write_and_leaves_no_trace(), StoreCancellationContract<IRetentionStore>.Store, object.GetType(), object.MemberwiseClone(), object.ToString(), object.Equals(object?), object.Equals(object?, object?), object.ReferenceEquals(object?, object?), object.GetHashCode()
Remarks
Section titled “Remarks”This contract guards against a security defect: a data plane with no tenant filter, where one tenant’s retention policy could delete all tenants’ rows.
The contract does not derive from Storage.TenantIsolationContract: the data plane has no concept of a “record” (there is no write side), seeding goes through the target table’s own store. Isolation is tested directly here, on top of Storage.StoreCancellationContract.
Constructors
Section titled “Constructors”RetentionStoreContract()
Section titled “ RetentionStoreContract()”protected RetentionStoreContract()Fields
Section titled “Fields”TenantA
Section titled “ TenantA”The tenant that writes the data.
protected const string TenantA = "tenant-a"Field Value
Section titled “Field Value”TenantB
Section titled “ TenantB”The tenant whose data must be preserved.
protected const string TenantB = "tenant-b"Field Value
Section titled “Field Value”Methods
Section titled “Methods”A_failed_archive_write_deletes_nothing()
Section titled “ A_failed_archive_write_deletes_nothing()”[Fact]public Task A_failed_archive_write_deletes_nothing()Returns
Section titled “Returns”Archive_batches_delete_only_the_rows_they_archived()
Section titled “ Archive_batches_delete_only_the_rows_they_archived()”[Fact]public Task Archive_batches_delete_only_the_rows_they_archived()Returns
Section titled “Returns”Archive_read_returns_only_the_given_tenant()
Section titled “ Archive_read_returns_only_the_given_tenant()”[Fact]public Task Archive_read_returns_only_the_given_tenant()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.
CancellableWriteAsync(CancellationToken)
Section titled “ CancellableWriteAsync(CancellationToken)”Calls one write method of the store with the given token.
protected override ValueTask CancellableWriteAsync(CancellationToken cancellationToken)Parameters
Section titled “Parameters”cancellationToken CancellationToken
The token the store must observe.
Returns
Section titled “Returns”The completion task.
Remarks
Section titled “Remarks”The write must be one StoreCancellationContract.WroteAnythingAsync can see. A write this contract cannot read back proves nothing about side effects, which is why StoreCancellationContract.The_write_this_contract_checks_can_be_read_back runs the same pair with a live token and requires the read to find it.
Count_covers_only_the_given_tenant()
Section titled “ Count_covers_only_the_given_tenant()”[Fact]public Task Count_covers_only_the_given_tenant()Returns
Section titled “Returns”Delete_does_NOT_touch_the_others_data()
Section titled “ Delete_does_NOT_touch_the_others_data()”[Fact]public Task Delete_does_NOT_touch_the_others_data()Returns
Section titled “Returns”Row_limit_threshold_counts_only_the_given_tenant()
Section titled “ Row_limit_threshold_counts_only_the_given_tenant()”[Fact]public Task Row_limit_threshold_counts_only_the_given_tenant()Returns
Section titled “Returns”SeedOldRowAsync(string)
Section titled “ SeedOldRowAsync(string)”Writes a row for the given tenant, older than the cutoff date, that falls under the RetentionTargets.VoiceSessions target.
protected abstract ValueTask SeedOldRowAsync(string tenantId)Parameters
Section titled “Parameters”tenantId string
The tenant that owns the row.
Returns
Section titled “Returns”The completion task.
SeedOldRowAsync(string, DateTimeOffset)
Section titled “ SeedOldRowAsync(string, DateTimeOffset)”Writes a row like RetentionStoreContract.SeedOldRowAsync that started at
startedAt.
protected virtual ValueTask SeedOldRowAsync(string tenantId, DateTimeOffset startedAt)Parameters
Section titled “Parameters”tenantId string
The tenant that owns the row.
startedAt DateTimeOffset
When the row started; older than the cutoff date.
Returns
Section titled “Returns”The completion task.
Remarks
Section titled “Remarks”The default ignores startedAt and calls
RetentionStoreContract.SeedOldRowAsync. Override it: the archive case
writes rows whose time order is the reverse of their write order, and
that is the order that shows a delete which does not follow the archive.
Unknown_target_throws()
Section titled “ Unknown_target_throws()”[Fact]public Task Unknown_target_throws()Returns
Section titled “Returns”WroteAnythingAsync()
Section titled “ WroteAnythingAsync()”Reports whether StoreCancellationContract.CancellableWriteAsync left anything behind.
protected override ValueTask<bool> WroteAnythingAsync()Returns
Section titled “Returns”true if the store holds any record the write would have created.
Remarks
Section titled “Remarks”The seeded row must still be there. A sweep that threw but deleted anyway is exactly the defect this case exists for.