.NET API
TenantIsolationContract<TStore\>
Tracon.Testing.Contracts.Xunit.dllThe shared base that checks a store’s tenant isolation both ways.
public abstract class TenantIsolationContract<TStore> : IAsyncLifetime, IAsyncDisposableType Parameters
Section titled “Type Parameters”TStore
The store type under test.
Inheritance
Section titled “Inheritance”object ← TenantIsolationContract<TStore>
Implements
Section titled “Implements”IAsyncLifetime, IAsyncDisposable
Inherited Members
Section titled “Inherited Members”object.GetType(), object.MemberwiseClone(), object.ToString(), object.Equals(object?), object.Equals(object?, object?), object.ReferenceEquals(object?, object?), object.GetHashCode()
Remarks
Section titled “Remarks”This base also carries the lifecycle plumbing shared by every store contract; derived contracts write only their own scenarios and the four isolation hooks.
The two-way check cannot be skipped. If only “B must not see it” is checked, a broken query that returns nothing at all would also pass the test. Every scenario therefore also asks “does A see its own data”, proving the filter is both sufficient and not too narrow.
Constructors
Section titled “Constructors”TenantIsolationContract()
Section titled “ TenantIsolationContract()”protected TenantIsolationContract()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 that must not see the data.
protected const string TenantB = "tenant-b"Field Value
Section titled “Field Value”Properties
Section titled “Properties”AmbientTenant
Section titled “ AmbientTenant”The tenant stores read from ITenantContext. For stores that do not take the tenant as a parameter, the contract sets this property to the relevant tenant on the first line of its hooks.
protected MutableTenantContext AmbientTenant { get; }Property Value
Section titled “Property Value”The store under test.
protected TStore Store { get; }Property Value
Section titled “Property Value”TStore
Methods
Section titled “Methods”CountAsync(string)
Section titled “ CountAsync(string)”Reports how many records the given tenant sees through the listing endpoint.
protected abstract ValueTask<int> CountAsync(string tenantId)Parameters
Section titled “Parameters”tenantId string
The reading tenant.
Returns
Section titled “Returns”The number of records seen.
CreateStoreAsync()
Section titled “ CreateStoreAsync()”Produces an empty store for the test.
protected abstract ValueTask<TStore> CreateStoreAsync()Returns
Section titled “Returns”ValueTask<TStore>
A store ready for use.
DisposeAsync()
Section titled “ DisposeAsync()”public ValueTask DisposeAsync()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 abstract 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.
InitializeAsync()
Section titled “ InitializeAsync()”Called immediately after the class has been created, before it is used.
public ValueTask InitializeAsync()Returns
Section titled “Returns”OnDisposeAsync()
Section titled “ OnDisposeAsync()”Hook for a derived class to release its own resources.
protected virtual ValueTask OnDisposeAsync()Returns
Section titled “Returns”A completed task.
Same_name_lives_independently_across_two_tenants()
Section titled “ Same_name_lives_independently_across_two_tenants()”[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/TenantIsolationContract.cs", 201)]public Task Same_name_lives_independently_across_two_tenants()Returns
Section titled “Returns”SeedAsync(string, string)
Section titled “ SeedAsync(string, string)”Writes a sample record for the given tenant.
protected abstract ValueTask<object> SeedAsync(string tenantId, string name)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.
Returns
Section titled “Returns”The key to use for reading the record back.
Tenant_cannot_delete_another_tenants_record()
Section titled “ Tenant_cannot_delete_another_tenants_record()”[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/TenantIsolationContract.cs", 180)]public Task Tenant_cannot_delete_another_tenants_record()Returns
Section titled “Returns”Tenant_cannot_read_another_tenants_record()
Section titled “ Tenant_cannot_read_another_tenants_record()”[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/TenantIsolationContract.cs", 151)]public Task Tenant_cannot_read_another_tenants_record()Returns
Section titled “Returns”Tenant_does_not_see_another_tenants_record_in_the_list()
Section titled “ Tenant_does_not_see_another_tenants_record_in_the_list()”[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/TenantIsolationContract.cs", 171)]public Task Tenant_does_not_see_another_tenants_record_in_the_list()Returns
Section titled “Returns”Tenant_reads_its_own_record()
Section titled “ Tenant_reads_its_own_record()”[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/TenantIsolationContract.cs", 160)]public Task Tenant_reads_its_own_record()Returns
Section titled “Returns”TryDeleteAsync(string, object)
Section titled “ TryDeleteAsync(string, object)”Attempts to delete the record on behalf of the given tenant.
protected virtual 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.
TryOverwriteAsync(string, string)
Section titled “ TryOverwriteAsync(string, string)”Attempts to change the record’s content on behalf of the given tenant.
protected virtual ValueTask<bool> TryOverwriteAsync(string tenantId, string name)Parameters
Section titled “Parameters”tenantId string
The tenant attempting the update.
name string
The record’s name.
Returns
Section titled “Returns”true if the update was attempted; false if the store does not support this scenario.
Remarks
Section titled “Remarks”The default implementation calls TenantIsolationContract.SeedAsync a second time: writing a record with the same name verifies that two tenants can carry the same name independently.
UseAmbientTenant(MutableTenantContext)
Section titled “ UseAmbientTenant(MutableTenantContext)”Attaches to a tenant context shared per class (used by multiple tests) and resets the tenant back to TenantIsolationContract.TenantA.
protected void UseAmbientTenant(MutableTenantContext tenant)Parameters
Section titled “Parameters”tenant MutableTenantContext
The shared context supplied by the schema/test CLASS fixture.
Remarks
Section titled “Remarks”When store instances are set up once per class (see the schema fixtures), they all capture the SAME ITenantContext object; each test must therefore call this method inside TenantIsolationContract.CreateStoreAsync to attach to that object and reset any state a previous test may have left on TenantIsolationContract.TenantB.