Skip to content
Tracon

RunScoreStoreContract

Namespace Tracon.Testing.Contracts.Storage · Assembly Tracon.Testing.Contracts.Xunit.dll

Behavior tests for the IRunScoreStore contract.

public abstract class RunScoreStoreContract : TenantIsolationContract<IRunScoreStore>, IAsyncLifetime, IAsyncDisposable

objectTenantIsolationContract<IRunScoreStore>RunScoreStoreContract

IAsyncLifetime, IAsyncDisposable

TenantIsolationContract<IRunScoreStore>.TenantA, TenantIsolationContract<IRunScoreStore>.TenantB, TenantIsolationContract<IRunScoreStore>.CreateStoreAsync(), TenantIsolationContract<IRunScoreStore>.UseAmbientTenant(MutableTenantContext), TenantIsolationContract<IRunScoreStore>.InitializeAsync(), TenantIsolationContract<IRunScoreStore>.DisposeAsync(), TenantIsolationContract<IRunScoreStore>.OnDisposeAsync(), TenantIsolationContract<IRunScoreStore>.SeedAsync(string, string), TenantIsolationContract<IRunScoreStore>.ExistsAsync(string, object), TenantIsolationContract<IRunScoreStore>.CountAsync(string), TenantIsolationContract<IRunScoreStore>.TryDeleteAsync(string, object), TenantIsolationContract<IRunScoreStore>.TryOverwriteAsync(string, string), TenantIsolationContract<IRunScoreStore>.Tenant_cannot_read_another_tenants_record(), TenantIsolationContract<IRunScoreStore>.Tenant_reads_its_own_record(), TenantIsolationContract<IRunScoreStore>.Tenant_does_not_see_another_tenants_record_in_the_list(), TenantIsolationContract<IRunScoreStore>.Tenant_cannot_delete_another_tenants_record(), TenantIsolationContract<IRunScoreStore>.Same_name_lives_independently_across_two_tenants(), TenantIsolationContract<IRunScoreStore>.AmbientTenant, TenantIsolationContract<IRunScoreStore>.Store, object.GetType(), object.MemberwiseClone(), object.ToString(), object.Equals(object?), object.Equals(object?, object?), object.ReferenceEquals(object?, object?), object.GetHashCode()

The in-memory store and the three SQL providers must pass the same scenarios. Critical rule: when the same author writes the same RunScore.Name onto the same target (a run or a message) a second time, the row is updated, not duplicated – a different name opens a new row, and neither rule applies when the author is empty (an anonymous setup).

protected RunScoreStoreContract()

A_categorical_score_carrying_a_numeric_value_is_REJECTED()

Section titled “ A_categorical_score_carrying_a_numeric_value_is_REJECTED()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 186)]
public Task A_categorical_score_carrying_a_numeric_value_is_REJECTED()

Task

[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 166)]
public Task A_categorical_score_is_stored_as_text()

Task

[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 128)]
public Task A_decimal_value_is_NOT_rounded()

Task

A_null_value_is_counted_but_does_not_enter_the_average()

Section titled “ A_null_value_is_counted_but_does_not_enter_the_average()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 406)]
public Task A_null_value_is_counted_but_does_not_enter_the_average()

Task

A_null_value_means_NO_MEASUREMENT_not_zero()

Section titled “ A_null_value_means_NO_MEASUREMENT_not_zero()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 156)]
public Task A_null_value_means_NO_MEASUREMENT_not_zero()

Task

A_numeric_score_carrying_a_text_value_is_REJECTED()

Section titled “ A_numeric_score_carrying_a_text_value_is_REJECTED()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 195)]
public Task A_numeric_score_carrying_a_text_value_is_REJECTED()

Task

A_whole_number_value_is_read_back_as_a_number_not_an_integer()

Section titled “ A_whole_number_value_is_read_back_as_a_number_not_an_integer()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 144)]
public Task A_whole_number_value_is_read_back_as_a_number_not_an_integer()

Task

An_empty_match_returns_empty_lists_not_null()

Section titled “ An_empty_match_returns_empty_lists_not_null()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 579)]
public Task An_empty_match_returns_empty_lists_not_null()

Task

An_empty_string_Author_is_excluded_from_the_author_breakdown_like_no_author()

Section titled “ An_empty_string_Author_is_excluded_from_the_author_breakdown_like_no_author()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 565)]
public Task An_empty_string_Author_is_excluded_from_the_author_breakdown_like_no_author()

Task

An_empty_string_MessageId_is_treated_the_same_as_no_message()

Section titled “ An_empty_string_MessageId_is_treated_the_same_as_no_message()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 547)]
public Task An_empty_string_MessageId_is_treated_the_same_as_no_message()

Task

[Theory("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 204)]
[InlineData(new object?[] { "has space" })]
[InlineData(new object?[] { "" })]
[InlineData(new object?[] { "naïve" })]
[InlineData(new object?[] { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" })]
public Task An_illegal_name_is_REJECTED(string name)

name string

Task

Another_tenants_score_cannot_be_deleted()

Section titled “ Another_tenants_score_cannot_be_deleted()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 327)]
public Task Another_tenants_score_cannot_be_deleted()

Task

[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 303)]
public Task Another_tenants_score_is_not_visible()

Task

Another_tenants_score_never_enters_the_summary()

Section titled “ Another_tenants_score_never_enters_the_summary()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 358)]
public Task Another_tenants_score_never_enters_the_summary()

Task

Author_breakdown_excludes_scores_with_no_author()

Section titled “ Author_breakdown_excludes_scores_with_no_author()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 487)]
public Task Author_breakdown_excludes_scores_with_no_author()

Task

[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 682)]
public Task Canceled_token_throws()

Task

Categorical_scores_report_category_counts_not_an_average()

Section titled “ Categorical_scores_report_category_counts_not_an_average()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 422)]
public Task Categorical_scores_report_category_counts_not_an_average()

Task

Category_cardinality_beyond_MaxRows_is_truncated_and_reported()

Section titled “ Category_cardinality_beyond_MaxRows_is_truncated_and_reported()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 463)]
public Task Category_cardinality_beyond_MaxRows_is_truncated_and_reported()

Task

Concurrent_writes_of_the_SAME_name_leave_exactly_one_row()

Section titled “ Concurrent_writes_of_the_SAME_name_leave_exactly_one_row()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 217)]
public Task Concurrent_writes_of_the_SAME_name_leave_exactly_one_row()

Task

A uniqueness key that does not match shows up first as a race: two writers each miss the other’s row and both insert.

Concurrent_writes_with_null_and_empty_MessageId_leave_exactly_one_row()

Section titled “ Concurrent_writes_with_null_and_empty_MessageId_leave_exactly_one_row()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 235)]
public Task Concurrent_writes_with_null_and_empty_MessageId_leave_exactly_one_row()

Task

A NULL RunScore.MessageId and an empty ONE both mean “the whole run” (RunScoreStoreContract.Score_with_no_message_id_belongs_to_the_whole_run), so a concurrent write of one shape racing a write of the OTHER shape must still collapse to a single row – not two, one per shape.

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

protected override ValueTask<int> CountAsync(string tenantId)

tenantId string

The reading tenant.

ValueTask<int>

The number of records seen.

Deleting_a_nonexistent_score_returns_false()

Section titled “ Deleting_a_nonexistent_score_returns_false()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 339)]
public Task Deleting_a_nonexistent_score_returns_false()

Task

Different_authors_score_the_same_target_independently()

Section titled “ Different_authors_score_the_same_target_independently()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 267)]
public Task Different_authors_score_the_same_target_independently()

Task

Different_kinds_of_the_same_name_are_not_averaged_together()

Section titled “ Different_kinds_of_the_same_name_are_not_averaged_together()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 371)]
public Task Different_kinds_of_the_same_name_are_not_averaged_together()

Task

Different_messages_are_scored_independently()

Section titled “ Different_messages_are_scored_independently()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 278)]
public Task Different_messages_are_scored_independently()

Task

Empty_author_makes_EVERY_call_open_a_new_row()

Section titled “ Empty_author_makes_EVERY_call_open_a_new_row()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 290)]
public Task Empty_author_makes_EVERY_call_open_a_new_row()

Task

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

protected override ValueTask<bool> ExistsAsync(string tenantId, object key)

tenantId string

The reading tenant.

key object

The key returned by TenantIsolationContract.SeedAsync.

ValueTask<bool>

true if the record is visible.

Hour_buckets_group_scores_within_the_same_hour()

Section titled “ Hour_buckets_group_scores_within_the_same_hour()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 617)]
public Task Hour_buckets_group_scores_within_the_same_hour()

Task

MessageId_is_never_a_breakdown_dimension()

Section titled “ MessageId_is_never_a_breakdown_dimension()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 501)]
public Task MessageId_is_never_a_breakdown_dimension()

Task

Repeated_writes_of_the_SAME_name_leave_exactly_one_row()

Section titled “ Repeated_writes_of_the_SAME_name_leave_exactly_one_row()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 251)]
public Task Repeated_writes_of_the_SAME_name_leave_exactly_one_row()

Task

Same_author_scoring_the_same_target_twice_UPDATES_the_row()

Section titled “ Same_author_scoring_the_same_target_twice_UPDATES_the_row()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 80)]
public Task Same_author_scoring_the_same_target_twice_UPDATES_the_row()

Task

Same_author_scoring_the_same_target_under_a_DIFFERENT_name_opens_a_new_row()

Section titled “ Same_author_scoring_the_same_target_under_a_DIFFERENT_name_opens_a_new_row()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 95)]
public Task Same_author_scoring_the_same_target_under_a_DIFFERENT_name_opens_a_new_row()

Task

[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 315)]
public Task Score_is_deleted()

Task

Score_with_no_message_id_belongs_to_the_whole_run()

Section titled “ Score_with_no_message_id_belongs_to_the_whole_run()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 70)]
public Task Score_with_no_message_id_belongs_to_the_whole_run()

Task

Scores_are_grouped_into_time_buckets_and_empty_buckets_are_omitted()

Section titled “ Scores_are_grouped_into_time_buckets_and_empty_buckets_are_omitted()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 591)]
public Task Scores_are_grouped_into_time_buckets_and_empty_buckets_are_omitted()

Task

Writes a sample record for the given tenant.

protected override ValueTask<object> SeedAsync(string tenantId, string name)

tenantId string

The tenant that owns the record.

name string

The name distinguishing the record. The same name may be used by two tenants.

ValueTask<object>

The key to use for reading the record back.

A score is attached to a run; the distinguishing key is the run id. Two tenants use the same run id: if isolation leaked, tenant B would see tenant A’s score.

Summary_can_be_read_while_a_score_is_being_written()

Section titled “ Summary_can_be_read_while_a_score_is_being_written()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 692)]
public Task Summary_can_be_read_while_a_score_is_being_written()

Task

Target_Message_excludes_run_level_scores()

Section titled “ Target_Message_excludes_run_level_scores()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 533)]
public Task Target_Message_excludes_run_level_scores()

Task

Target_Run_excludes_message_level_scores()

Section titled “ Target_Run_excludes_message_level_scores()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 519)]
public Task Target_Run_excludes_message_level_scores()

Task

Attempts to delete the record on behalf of the given tenant.

protected override ValueTask<bool?> TryDeleteAsync(string tenantId, object key)

tenantId string

The tenant attempting the delete.

key object

The key returned by TenantIsolationContract.SeedAsync.

ValueTask<bool?>

true if the delete succeeded, false if the record was not found; null if the store does not offer deletion.

Updating_ONE_name_leaves_the_authors_other_names_untouched()

Section titled “ Updating_ONE_name_leaves_the_authors_other_names_untouched()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 112)]
public Task Updating_ONE_name_leaves_the_authors_other_names_untouched()

Task

Week_buckets_start_on_Monday_and_group_the_whole_week()

Section titled “ Week_buckets_start_on_Monday_and_group_the_whole_week()”
[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 650)]
public Task Week_buckets_start_on_Monday_and_group_the_whole_week()

Task

The week bucket is the riskiest of the three: each of the four implementations (three SQL dialects plus the in-memory store) computes “the preceding Monday” with a DIFFERENT formula (see RunScoreBucketing.Truncate’s remarks and each provider’s SelectRunScoreSeries query). This test is what proves they agree.

[Fact("/Users/farukatasoy/Desktop/projects/Tracon/src/Tracon.Testing.Contracts.Xunit/Contracts/RunScoreStoreContract.cs", 48)]
public Task Written_score_is_read_back()

Task