# AuditLogContract

> Tracon documentation. Tracon is a .NET package family that adds a control
> plane on Microsoft Agent Framework, published as NuGet packages that run
> inside your own ASP.NET Core application.
>
> Page: https://tracon.dev/api/tracon.testing.contracts.storage.auditlogcontract/
> Summary: AuditLogContract: Behavior tests for the IAuditLog contract.

<div class="api-provenance">Namespace Tracon.Testing.Contracts.Storage · Assembly <code>Tracon.Testing.Contracts.Xunit.dll</code></div>

Behavior tests for the [IAuditLog](/api/tracon.iauditlog/) contract.

```csharp
public abstract class AuditLogContract : TenantIsolationContract<IAuditLog>, IAsyncLifetime, IAsyncDisposable
```

<dl class="api-relations"><dt>Inheritance</dt><dd><a href="https://learn.microsoft.com/dotnet/api/system.object">object</a> ← <a href="/api/tracon.testing.contracts.storage.storecancellationcontract-1/">StoreCancellationContract&lt;IAuditLog&gt;</a> ← <a href="/api/tracon.testing.contracts.storage.tenantisolationcontract-1/">TenantIsolationContract&lt;IAuditLog&gt;</a> ← <a href="/api/tracon.testing.contracts.storage.auditlogcontract/">AuditLogContract</a></dd><dt>Implements</dt><dd>IAsyncLifetime, <a href="https://learn.microsoft.com/dotnet/api/system.iasyncdisposable">IAsyncDisposable</a></dd><dt>Inherited Members</dt><dd><a href="/api/tracon.testing.contracts.storage.tenantisolationcontract-1/#Tracon_Testing_Contracts_Storage_TenantIsolationContract_1_TenantA">TenantIsolationContract&lt;IAuditLog&gt;.TenantA</a>, <a href="/api/tracon.testing.contracts.storage.tenantisolationcontract-1/#Tracon_Testing_Contracts_Storage_TenantIsolationContract_1_TenantB">TenantIsolationContract&lt;IAuditLog&gt;.TenantB</a>, <a href="/api/tracon.testing.contracts.storage.tenantisolationcontract-1/#Tracon_Testing_Contracts_Storage_TenantIsolationContract_1_UseAmbientTenant_Tracon_Testing_Contracts_Storage_MutableTenantContext_">TenantIsolationContract&lt;IAuditLog&gt;.UseAmbientTenant(MutableTenantContext)</a>, <a href="/api/tracon.testing.contracts.storage.tenantisolationcontract-1/#Tracon_Testing_Contracts_Storage_TenantIsolationContract_1_SeedAsync_System_String_System_String_System_Threading_CancellationToken_">TenantIsolationContract&lt;IAuditLog&gt;.SeedAsync(string, string, CancellationToken)</a>, <a href="/api/tracon.testing.contracts.storage.tenantisolationcontract-1/#Tracon_Testing_Contracts_Storage_TenantIsolationContract_1_ExistsAsync_System_String_System_Object_">TenantIsolationContract&lt;IAuditLog&gt;.ExistsAsync(string, object)</a>, <a href="/api/tracon.testing.contracts.storage.tenantisolationcontract-1/#Tracon_Testing_Contracts_Storage_TenantIsolationContract_1_CountAsync_System_String_System_Threading_CancellationToken_">TenantIsolationContract&lt;IAuditLog&gt;.CountAsync(string, CancellationToken)</a>, <a href="/api/tracon.testing.contracts.storage.tenantisolationcontract-1/#Tracon_Testing_Contracts_Storage_TenantIsolationContract_1_TryDeleteAsync_System_String_System_Object_">TenantIsolationContract&lt;IAuditLog&gt;.TryDeleteAsync(string, object)</a>, <a href="/api/tracon.testing.contracts.storage.tenantisolationcontract-1/#Tracon_Testing_Contracts_Storage_TenantIsolationContract_1_TryOverwriteAsync_System_String_System_String_">TenantIsolationContract&lt;IAuditLog&gt;.TryOverwriteAsync(string, string)</a>, <a href="/api/tracon.testing.contracts.storage.tenantisolationcontract-1/#Tracon_Testing_Contracts_Storage_TenantIsolationContract_1_CancellableReadAsync_System_Threading_CancellationToken_">TenantIsolationContract&lt;IAuditLog&gt;.CancellableReadAsync(CancellationToken)</a>, <a href="/api/tracon.testing.contracts.storage.tenantisolationcontract-1/#Tracon_Testing_Contracts_Storage_TenantIsolationContract_1_CancellableWriteAsync_System_Threading_CancellationToken_">TenantIsolationContract&lt;IAuditLog&gt;.CancellableWriteAsync(CancellationToken)</a>, <a href="/api/tracon.testing.contracts.storage.tenantisolationcontract-1/#Tracon_Testing_Contracts_Storage_TenantIsolationContract_1_WroteAnythingAsync">TenantIsolationContract&lt;IAuditLog&gt;.WroteAnythingAsync()</a>, <a href="/api/tracon.testing.contracts.storage.tenantisolationcontract-1/#Tracon_Testing_Contracts_Storage_TenantIsolationContract_1_Tenant_cannot_read_another_tenants_record">TenantIsolationContract&lt;IAuditLog&gt;.Tenant_cannot_read_another_tenants_record()</a>, <a href="/api/tracon.testing.contracts.storage.tenantisolationcontract-1/#Tracon_Testing_Contracts_Storage_TenantIsolationContract_1_Tenant_reads_its_own_record">TenantIsolationContract&lt;IAuditLog&gt;.Tenant_reads_its_own_record()</a>, <a href="/api/tracon.testing.contracts.storage.tenantisolationcontract-1/#Tracon_Testing_Contracts_Storage_TenantIsolationContract_1_Tenant_does_not_see_another_tenants_record_in_the_list">TenantIsolationContract&lt;IAuditLog&gt;.Tenant_does_not_see_another_tenants_record_in_the_list()</a>, <a href="/api/tracon.testing.contracts.storage.tenantisolationcontract-1/#Tracon_Testing_Contracts_Storage_TenantIsolationContract_1_Tenant_cannot_delete_another_tenants_record">TenantIsolationContract&lt;IAuditLog&gt;.Tenant_cannot_delete_another_tenants_record()</a>, <a href="/api/tracon.testing.contracts.storage.tenantisolationcontract-1/#Tracon_Testing_Contracts_Storage_TenantIsolationContract_1_Same_name_lives_independently_across_two_tenants">TenantIsolationContract&lt;IAuditLog&gt;.Same_name_lives_independently_across_two_tenants()</a>, <a href="/api/tracon.testing.contracts.storage.tenantisolationcontract-1/#Tracon_Testing_Contracts_Storage_TenantIsolationContract_1_AmbientTenant">TenantIsolationContract&lt;IAuditLog&gt;.AmbientTenant</a>, <a href="/api/tracon.testing.contracts.storage.storecancellationcontract-1/#Tracon_Testing_Contracts_Storage_StoreCancellationContract_1_CreateStoreAsync">StoreCancellationContract&lt;IAuditLog&gt;.CreateStoreAsync()</a>, <a href="/api/tracon.testing.contracts.storage.storecancellationcontract-1/#Tracon_Testing_Contracts_Storage_StoreCancellationContract_1_InitializeAsync">StoreCancellationContract&lt;IAuditLog&gt;.InitializeAsync()</a>, <a href="/api/tracon.testing.contracts.storage.storecancellationcontract-1/#Tracon_Testing_Contracts_Storage_StoreCancellationContract_1_DisposeAsync">StoreCancellationContract&lt;IAuditLog&gt;.DisposeAsync()</a>, <a href="/api/tracon.testing.contracts.storage.storecancellationcontract-1/#Tracon_Testing_Contracts_Storage_StoreCancellationContract_1_OnDisposeAsync">StoreCancellationContract&lt;IAuditLog&gt;.OnDisposeAsync()</a>, <a href="/api/tracon.testing.contracts.storage.storecancellationcontract-1/#Tracon_Testing_Contracts_Storage_StoreCancellationContract_1_CancellableReadAsync_System_Threading_CancellationToken_">StoreCancellationContract&lt;IAuditLog&gt;.CancellableReadAsync(CancellationToken)</a>, <a href="/api/tracon.testing.contracts.storage.storecancellationcontract-1/#Tracon_Testing_Contracts_Storage_StoreCancellationContract_1_CancellableWriteAsync_System_Threading_CancellationToken_">StoreCancellationContract&lt;IAuditLog&gt;.CancellableWriteAsync(CancellationToken)</a>, <a href="/api/tracon.testing.contracts.storage.storecancellationcontract-1/#Tracon_Testing_Contracts_Storage_StoreCancellationContract_1_WroteAnythingAsync">StoreCancellationContract&lt;IAuditLog&gt;.WroteAnythingAsync()</a>, <a href="/api/tracon.testing.contracts.storage.storecancellationcontract-1/#Tracon_Testing_Contracts_Storage_StoreCancellationContract_1_Canceled_token_throws_on_read">StoreCancellationContract&lt;IAuditLog&gt;.Canceled_token_throws_on_read()</a>, <a href="/api/tracon.testing.contracts.storage.storecancellationcontract-1/#Tracon_Testing_Contracts_Storage_StoreCancellationContract_1_The_write_this_contract_checks_can_be_read_back">StoreCancellationContract&lt;IAuditLog&gt;.The_write_this_contract_checks_can_be_read_back()</a>, <a href="/api/tracon.testing.contracts.storage.storecancellationcontract-1/#Tracon_Testing_Contracts_Storage_StoreCancellationContract_1_Canceled_token_throws_on_write_and_leaves_no_trace">StoreCancellationContract&lt;IAuditLog&gt;.Canceled_token_throws_on_write_and_leaves_no_trace()</a>, <a href="/api/tracon.testing.contracts.storage.storecancellationcontract-1/#Tracon_Testing_Contracts_Storage_StoreCancellationContract_1_Store">StoreCancellationContract&lt;IAuditLog&gt;.Store</a>, <a href="https://learn.microsoft.com/dotnet/api/system.object.gettype">object.GetType()</a>, <a href="https://learn.microsoft.com/dotnet/api/system.object.memberwiseclone">object.MemberwiseClone()</a>, <a href="https://learn.microsoft.com/dotnet/api/system.object.tostring">object.ToString()</a>, <a href="https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object)">object.Equals(object?)</a>, <a href="https://learn.microsoft.com/dotnet/api/system.object.equals#system-object-equals(system-object-system-object)">object.Equals(object?, object?)</a>, <a href="https://learn.microsoft.com/dotnet/api/system.object.referenceequals">object.ReferenceEquals(object?, object?)</a>, <a href="https://learn.microsoft.com/dotnet/api/system.object.gethashcode">object.GetHashCode()</a></dd></dl>

## Remarks

Every implementation must pass the same scenarios.

## Constructors

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract__ctor"></a> AuditLogContract\(\)

```csharp
protected AuditLogContract()
```

## Properties

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_Log"></a> Log

The log under test.

```csharp
protected IAuditLog Log { get; }
```

#### Property Value

 [IAuditLog](/api/tracon.iauditlog/)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_QualifiedAuditLogTable"></a> QualifiedAuditLogTable

The fully qualified `audit_log` table reference this provider's raw SQL should use.

```csharp
protected virtual string QualifiedAuditLogTable { get; }
```

#### Property Value

 [string](https://learn.microsoft.com/dotnet/api/system.string)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_SupportsRawTamper"></a> SupportsRawTamper

Whether this store's backing state can be tampered with directly (SQL providers only).

```csharp
protected virtual bool SupportsRawTamper { get; }
```

#### Property Value

 [bool](https://learn.microsoft.com/dotnet/api/system.boolean)

## Methods

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_Action_and_entity_filter_works"></a> Action\_and\_entity\_filter\_works\(\)

```csharp
[Fact]
public Task Action_and_entity_filter_works()
```

#### Returns

 [Task](https://learn.microsoft.com/dotnet/api/system.threading.tasks.task)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_Concurrent_writes_for_one_tenant_produce_a_single_valid_chain"></a> Concurrent\_writes\_for\_one\_tenant\_produce\_a\_single\_valid\_chain\(\)

```csharp
[Fact]
public Task Concurrent_writes_for_one_tenant_produce_a_single_valid_chain()
```

#### Returns

 [Task](https://learn.microsoft.com/dotnet/api/system.threading.tasks.task)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_CountAsync_System_String_System_Threading_CancellationToken_"></a> CountAsync\(string, CancellationToken\)

Reports how many records the given tenant sees through the listing endpoint.

```csharp
protected override ValueTask<int> CountAsync(string tenantId, CancellationToken cancellationToken)
```

#### Parameters

`tenantId` [string](https://learn.microsoft.com/dotnet/api/system.string)

The reading tenant.

`cancellationToken` [CancellationToken](https://learn.microsoft.com/dotnet/api/system.threading.cancellationtoken)

The token every store call inside the hook must be handed; see
[TenantIsolationContract.SeedAsync](/api/tracon.testing.contracts.storage/).

#### Returns

 [ValueTask](https://learn.microsoft.com/dotnet/api/system.threading.tasks.valuetask\-1)<[int](https://learn.microsoft.com/dotnet/api/system.int32)\>

The number of records seen.

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_Deleting_a_row_directly_is_detected_as_a_gap"></a> Deleting\_a\_row\_directly\_is\_detected\_as\_a\_gap\(\)

```csharp
[Fact]
public Task Deleting_a_row_directly_is_detected_as_a_gap()
```

#### Returns

 [Task](https://learn.microsoft.com/dotnet/api/system.threading.tasks.task)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_Entries_are_returned_newest_to_oldest"></a> Entries\_are\_returned\_newest\_to\_oldest\(\)

```csharp
[Fact]
public Task Entries_are_returned_newest_to_oldest()
```

#### Returns

 [Task](https://learn.microsoft.com/dotnet/api/system.threading.tasks.task)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_Entry_with_unknown_actor_carries_null"></a> Entry\_with\_unknown\_actor\_carries\_null\(\)

```csharp
[Fact]
public Task Entry_with_unknown_actor_carries_null()
```

#### Returns

 [Task](https://learn.microsoft.com/dotnet/api/system.threading.tasks.task)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_ExecuteRawAsync_System_String_"></a> ExecuteRawAsync\(string\)

Runs raw SQL against the backing store. Only called when [AuditLogContract.SupportsRawTamper](/api/tracon.testing.contracts.storage.auditlogcontract/#Tracon_Testing_Contracts_Storage_AuditLogContract_SupportsRawTamper) is [true](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/bool).

```csharp
protected virtual ValueTask ExecuteRawAsync(string sql)
```

#### Parameters

`sql` [string](https://learn.microsoft.com/dotnet/api/system.string)

#### Returns

 [ValueTask](https://learn.microsoft.com/dotnet/api/system.threading.tasks.valuetask)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_ExistsAsync_System_String_System_Object_"></a> ExistsAsync\(string, object\)

Reports whether the record is visible from the given tenant's viewpoint.

```csharp
protected override ValueTask<bool> ExistsAsync(string tenantId, object key)
```

#### Parameters

`tenantId` [string](https://learn.microsoft.com/dotnet/api/system.string)

The reading tenant.

`key` [object](https://learn.microsoft.com/dotnet/api/system.object)

The key returned by [TenantIsolationContract.SeedAsync](/api/tracon.testing.contracts.storage/).

#### Returns

 [ValueTask](https://learn.microsoft.com/dotnet/api/system.threading.tasks.valuetask\-1)<[bool](https://learn.microsoft.com/dotnet/api/system.boolean)\>

[true](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/bool) if the record is visible.

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_First_entry_of_a_tenant_carries_no_previous_hash"></a> First\_entry\_of\_a\_tenant\_carries\_no\_previous\_hash\(\)

```csharp
[Fact]
public Task First_entry_of_a_tenant_carries_no_previous_hash()
```

#### Returns

 [Task](https://learn.microsoft.com/dotnet/api/system.threading.tasks.task)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_FormatIdForRawSql_System_Guid_"></a> FormatIdForRawSql\(Guid\)

Formats an id for embedding in raw SQL. A provider that stores a
[Guid](https://learn.microsoft.com/dotnet/api/system.guid) as text and compares case-sensitively (SQLite, in
uppercase) must override the default (lowercase
[Guid.ToString](https://learn.microsoft.com/dotnet/api/system.guid)).

```csharp
protected virtual string FormatIdForRawSql(Guid id)
```

#### Parameters

`id` [Guid](https://learn.microsoft.com/dotnet/api/system.guid)

#### Returns

 [string](https://learn.microsoft.com/dotnet/api/system.string)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_Limit_constrains_the_result"></a> Limit\_constrains\_the\_result\(\)

```csharp
[Fact]
public Task Limit_constrains_the_result()
```

#### Returns

 [Task](https://learn.microsoft.com/dotnet/api/system.threading.tasks.task)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_No_leakage_between_tenants"></a> No\_leakage\_between\_tenants\(\)

```csharp
[Fact]
public Task No_leakage_between_tenants()
```

#### Returns

 [Task](https://learn.microsoft.com/dotnet/api/system.threading.tasks.task)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_Null_tenant_follows_the_ambient_tenant_when_it_changes"></a> Null\_tenant\_follows\_the\_ambient\_tenant\_when\_it\_changes\(\)

```csharp
[Fact]
public Task Null_tenant_follows_the_ambient_tenant_when_it_changes()
```

#### Returns

 [Task](https://learn.microsoft.com/dotnet/api/system.threading.tasks.task)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_Null_tenant_resolves_to_the_ambient_tenant_and_does_not_leak_others"></a> Null\_tenant\_resolves\_to\_the\_ambient\_tenant\_and\_does\_not\_leak\_others\(\)

```csharp
[Fact]
public Task Null_tenant_resolves_to_the_ambient_tenant_and_does_not_leak_others()
```

#### Returns

 [Task](https://learn.microsoft.com/dotnet/api/system.threading.tasks.task)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_Second_entry_links_to_the_first"></a> Second\_entry\_links\_to\_the\_first\(\)

```csharp
[Fact]
public Task Second_entry_links_to_the_first()
```

#### Returns

 [Task](https://learn.microsoft.com/dotnet/api/system.threading.tasks.task)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_SeedAsync_System_String_System_String_System_Threading_CancellationToken_"></a> SeedAsync\(string, string, CancellationToken\)

Writes a sample record for the given tenant.

```csharp
protected override ValueTask<object> SeedAsync(string tenantId, string name, CancellationToken cancellationToken)
```

#### Parameters

`tenantId` [string](https://learn.microsoft.com/dotnet/api/system.string)

The tenant that owns the record.

`name` [string](https://learn.microsoft.com/dotnet/api/system.string)

The name distinguishing the record. The same name may be used by two tenants.

`cancellationToken` [CancellationToken](https://learn.microsoft.com/dotnet/api/system.threading.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

 [ValueTask](https://learn.microsoft.com/dotnet/api/system.threading.tasks.valuetask\-1)<[object](https://learn.microsoft.com/dotnet/api/system.object)\>

The key to use for reading the record back.

#### Remarks

The audit trail entry carries its own `TenantId` field and the
query filters by [AuditQuery.TenantId](/api/tracon.auditquery/#Tracon_AuditQuery_TenantId); it is not read
from the tenant context.

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_Tampering_a_row_directly_is_detected_as_broken"></a> Tampering\_a\_row\_directly\_is\_detected\_as\_broken\(\)

```csharp
[Fact]
public Task Tampering_a_row_directly_is_detected_as_broken()
```

#### Returns

 [Task](https://learn.microsoft.com/dotnet/api/system.threading.tasks.task)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_Two_tenants_chains_verify_independently"></a> Two\_tenants\_chains\_verify\_independently\(\)

```csharp
[Fact]
public Task Two_tenants_chains_verify_independently()
```

#### Returns

 [Task](https://learn.microsoft.com/dotnet/api/system.threading.tasks.task)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_VerifyChainAsync_reports_valid_for_a_normally_written_chain"></a> VerifyChainAsync\_reports\_valid\_for\_a\_normally\_written\_chain\(\)

```csharp
[Fact]
public Task VerifyChainAsync_reports_valid_for_a_normally_written_chain()
```

#### Returns

 [Task](https://learn.microsoft.com/dotnet/api/system.threading.tasks.task)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_VerifyChainAsync_with_null_tenant_checks_the_ambient_tenants_own_chain"></a> VerifyChainAsync\_with\_null\_tenant\_checks\_the\_ambient\_tenants\_own\_chain\(\)

```csharp
[Fact]
public Task VerifyChainAsync_with_null_tenant_checks_the_ambient_tenants_own_chain()
```

#### Returns

 [Task](https://learn.microsoft.com/dotnet/api/system.threading.tasks.task)

### <a id="Tracon_Testing_Contracts_Storage_AuditLogContract_Written_entry_round_trips"></a> Written\_entry\_round\_trips\(\)

```csharp
[Fact]
public Task Written_entry_round_trips()
```

#### Returns

 [Task](https://learn.microsoft.com/dotnet/api/system.threading.tasks.task)
