From 32ca86e5242ec52d940311706c6b7e3d4e33e6b1 Mon Sep 17 00:00:00 2001 From: Martin Schmidt Date: Thu, 1 Aug 2024 09:08:02 +0000 Subject: [PATCH] more tests --- .../DatabaseScripts/1.sql | 5 +- .../Exceptions/RegistryNotKnownException.cs | 12 + .../RegistryService.cs | 7 +- .../UriOptionsLoaderService.cs | 2 +- .../ChroniclerRepositoryTests.cs | 582 +++++++++--------- 5 files changed, 329 insertions(+), 279 deletions(-) create mode 100644 src/ProjectOrigin.Chronicler.Server/Exceptions/RegistryNotKnownException.cs diff --git a/src/ProjectOrigin.Chronicler.Server/DatabaseScripts/1.sql b/src/ProjectOrigin.Chronicler.Server/DatabaseScripts/1.sql index 5b1e564..0fd3899 100644 --- a/src/ProjectOrigin.Chronicler.Server/DatabaseScripts/1.sql +++ b/src/ProjectOrigin.Chronicler.Server/DatabaseScripts/1.sql @@ -16,10 +16,13 @@ CREATE TABLE read_blocks ( CREATE OR REPLACE FUNCTION insert_read_block() RETURNS TRIGGER AS $$ BEGIN - IF NOT EXISTS (SELECT 1 FROM read_blocks WHERE registry_name = NEW.registry_name) THEN + PERFORM 1 FROM read_blocks WHERE registry_name = NEW.registry_name; + + IF NOT FOUND THEN INSERT INTO read_blocks (registry_name, block_height, read_at) VALUES (NEW.registry_name, -1, NOW()); END IF; + RETURN NEW; END; $$ LANGUAGE plpgsql; diff --git a/src/ProjectOrigin.Chronicler.Server/Exceptions/RegistryNotKnownException.cs b/src/ProjectOrigin.Chronicler.Server/Exceptions/RegistryNotKnownException.cs new file mode 100644 index 0000000..e220e30 --- /dev/null +++ b/src/ProjectOrigin.Chronicler.Server/Exceptions/RegistryNotKnownException.cs @@ -0,0 +1,12 @@ +using System; + +namespace ProjectOrigin.Chronicler.Server.Exceptions +{ + public class RegistryNotKnownException : Exception + { + public RegistryNotKnownException(string registryName) + : base($"Registry {registryName} is not known") + { + } + } +} diff --git a/src/ProjectOrigin.Chronicler.Server/RegistryService.cs b/src/ProjectOrigin.Chronicler.Server/RegistryService.cs index 6fa3e2b..9689d5a 100644 --- a/src/ProjectOrigin.Chronicler.Server/RegistryService.cs +++ b/src/ProjectOrigin.Chronicler.Server/RegistryService.cs @@ -1,10 +1,12 @@ using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Grpc.Net.Client; using Microsoft.Extensions.Options; +using ProjectOrigin.Chronicler.Server.Exceptions; using ProjectOrigin.Chronicler.Server.Options; using static ProjectOrigin.Registry.V1.RegistryService; @@ -12,7 +14,7 @@ namespace ProjectOrigin.Chronicler.Server; public class RegistryService : IRegistryService { - ConcurrentDictionary _registries = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _registries = new ConcurrentDictionary(); private readonly IOptionsMonitor _optionsMonitor; public RegistryService(IOptionsMonitor optionsMonitor) @@ -38,7 +40,6 @@ public RegistryService(IOptionsMonitor optionsMonitor) } } }); - } private RegistryServiceClient CreateClient(string registryName) @@ -48,7 +49,7 @@ private RegistryServiceClient CreateClient(string registryName) (name) => { if (!_optionsMonitor.CurrentValue.RegistryUrls.TryGetValue(name, out var address)) - throw new System.Exception($"Registry {name} not found in configuration"); + throw new RegistryNotKnownException($"Registry {name} not found in configuration"); return GrpcChannel.ForAddress(address, new GrpcChannelOptions { diff --git a/src/ProjectOrigin.Chronicler.Server/UriOptionsLoader/UriOptionsLoaderService.cs b/src/ProjectOrigin.Chronicler.Server/UriOptionsLoader/UriOptionsLoaderService.cs index 14a99b6..8280486 100644 --- a/src/ProjectOrigin.Chronicler.Server/UriOptionsLoader/UriOptionsLoaderService.cs +++ b/src/ProjectOrigin.Chronicler.Server/UriOptionsLoader/UriOptionsLoaderService.cs @@ -112,7 +112,7 @@ private async Task LoadFromHttp(CancellationToken stoppingToken) var response = await httpClient.GetAsync(_originOptions.ConfigurationUri, stoppingToken); response.EnsureSuccessStatusCode(); - return await response.Content.ReadFromJsonAsync() + return await response.Content.ReadFromJsonAsync(stoppingToken) ?? throw new JsonException("Failed to read options from response."); } diff --git a/src/ProjectOrigin.Chronicler.Test/ChroniclerRepositoryTests.cs b/src/ProjectOrigin.Chronicler.Test/ChroniclerRepositoryTests.cs index 5b30892..9dd10de 100644 --- a/src/ProjectOrigin.Chronicler.Test/ChroniclerRepositoryTests.cs +++ b/src/ProjectOrigin.Chronicler.Test/ChroniclerRepositoryTests.cs @@ -1,4 +1,3 @@ - using System; using System.Data; using System.Threading.Tasks; @@ -13,327 +12,362 @@ using ProjectOrigin.TestCommon.Fixtures; using Xunit; -namespace ProjectOrigin.Chronicler.Test +namespace ProjectOrigin.Chronicler.Test; + +public sealed class ChroniclerRepositoryTests : IClassFixture>, IDisposable { - public class ChroniclerRepositoryTests : IClassFixture>, IDisposable + private readonly PostgresDatabaseFixture _databaseFixture; + private readonly Fixture _fixture = new(); + private readonly IDbConnection _con; + private readonly ChroniclerRepository _repository; + + public ChroniclerRepositoryTests(PostgresDatabaseFixture databaseFixture) { - private readonly PostgresDatabaseFixture _databaseFixture; - private readonly Fixture _fixture = new(); - private readonly IDbConnection _con; - private readonly ChroniclerRepository _repository; + _databaseFixture = databaseFixture; + _databaseFixture.ResetDatabase(); + _fixture.Customizations.Add(new MicrosecondDateTimeOffsetGenerator()); - public ChroniclerRepositoryTests(PostgresDatabaseFixture databaseFixture) - { - _databaseFixture = databaseFixture; - _databaseFixture.ResetDatabase(); - _fixture.Customizations.Add(new MicrosecondDateTimeOffsetGenerator()); + _con = new NpgsqlConnection(_databaseFixture.HostConnectionString); + _con.Open(); + _repository = new ChroniclerRepository(_con.BeginTransaction()); + } - _con = new NpgsqlConnection(_databaseFixture.HostConnectionString); - _con.Open(); - _repository = new ChroniclerRepository(_con.BeginTransaction()); - } + public void Dispose() + { + _con.Dispose(); + } - public void Dispose() - { - _con.Dispose(); - } + [Fact] + public async Task InsertClaimIntent_InsertsClaimIntent() + { + // Arrange + var intent = _fixture.Create(); - [Fact] - public async Task InsertClaimIntent_InsertsClaimIntent() - { - // Arrange - var intent = _fixture.Create(); + // Act + await _repository.InsertClaimIntent(intent); + + // Assert + _con.QuerySingle("SELECT * FROM claim_intents").Should().BeEquivalentTo(intent); + } + + [Fact] + public async Task InsertClaimIntent_InsertsClaimIntentAndLastReadBlock() + { + // Arrange + var intent = _fixture.Create(); - // Act - await _repository.InsertClaimIntent(intent); + // Act + await _repository.InsertClaimIntent(intent); - // Assert - _con.QuerySingle("SELECT * FROM claim_intents").Should().BeEquivalentTo(intent); - } + // Assert + _con.QuerySingle("SELECT * FROM claim_intents").Should().BeEquivalentTo(intent); + var block = _con.QuerySingle("SELECT * FROM read_blocks"); + block.RegistryName.Should().Be(intent.RegistryName); + block.BlockHeight.Should().Be(-1); + } - [Fact] - public async Task GetClaimIntent_ReturnsClaimIntent() + [Fact] + public async Task InsertClaimIntent_InsertsClaimIntentAndNotChangeLastReadBlock() + { + // Arrange + var intent = _fixture.Create(); + var block = new LastReadBlock { - // Arrange - var intent = _fixture.Create(); - await _repository.InsertClaimIntent(intent); + RegistryName = intent.RegistryName, + BlockHeight = 5, + ReadAt = DateTimeOffset.UtcNow + }; + await _repository.UpsertReadBlock(block); + + // Act + await _repository.InsertClaimIntent(intent); + + // Assert + _con.QuerySingle("SELECT * FROM claim_intents").Should().BeEquivalentTo(intent); + _con.QuerySingle("SELECT * FROM read_blocks").Should().BeEquivalentTo(block); + } - // Act - var result = await _repository.GetClaimIntent(intent.Id); + [Fact] + public async Task GetClaimIntent_ReturnsClaimIntent() + { + // Arrange + var intent = _fixture.Create(); + await _repository.InsertClaimIntent(intent); - // Assert - result.Should().BeEquivalentTo(intent); - } + // Act + var result = await _repository.GetClaimIntent(intent.Id); + // Assert + result.Should().BeEquivalentTo(intent); + } - [Fact] - public async Task GetClaimIntent_ThrowsExceptionWhenNotFound() - { - // Act - await Assert.ThrowsAsync(() => _repository.GetClaimIntent(new FederatedCertificateId - { - RegistryName = _fixture.Create(), - StreamId = Guid.NewGuid() - }, new byte[32])); - } - - [Fact] - public async Task GetClaimIntentFromFid_ReturnsClaimIntent() - { - // Arrange - var intent = _fixture.Create(); - await _repository.InsertClaimIntent(intent); - - // Act - var result = await _repository.GetClaimIntent(new FederatedCertificateId - { - RegistryName = intent.RegistryName, - StreamId = intent.CertificateId - }, intent.CommitmentHash); - - // Assert - result.Should().BeEquivalentTo(intent); - } - - [Fact] - public async Task GetClaimIntentFromFid_ThrowsExceptionWhenNotFound() - { - // Act - await Assert.ThrowsAsync(() => _repository.GetClaimIntent(new FederatedCertificateId - { - RegistryName = _fixture.Create(), - StreamId = Guid.NewGuid() - }, new byte[32])); - } - - [Fact] - public async Task HasClaimIntent_ReturnsFalse_WhenClaimIntentDoesNotExist() + [Fact] + public async Task GetClaimIntent_ThrowsExceptionWhenNotFound() + { + // Act + await Assert.ThrowsAsync(() => _repository.GetClaimIntent(new FederatedCertificateId { - // Act - var result = await _repository.HasClaimIntent(new FederatedCertificateId - { - RegistryName = _fixture.Create(), - StreamId = Guid.NewGuid() - }, new byte[32]); - - // Assert - Assert.False(result); - } - - [Fact] - public async Task HasClaimIntent_ReturnsTrue_WhenClaimIntentExists() + RegistryName = _fixture.Create(), + StreamId = Guid.NewGuid() + }, new byte[32])); + } + + [Fact] + public async Task GetClaimIntentFromFid_ReturnsClaimIntent() + { + // Arrange + var intent = _fixture.Create(); + await _repository.InsertClaimIntent(intent); + + // Act + var result = await _repository.GetClaimIntent(new FederatedCertificateId { - // Arrange - var intent = _fixture.Create(); - await _repository.InsertClaimIntent(intent); - - // Act - var result = await _repository.HasClaimIntent(new FederatedCertificateId - { - RegistryName = intent.RegistryName, - StreamId = intent.CertificateId - }, intent.CommitmentHash); - - // Assert - Assert.True(result); - } - - [Fact] - public async Task HasClaimIntent_ReturnsFalse_WhenOtherClaimIntentExists() + RegistryName = intent.RegistryName, + StreamId = intent.CertificateId + }, intent.CommitmentHash); + + // Assert + result.Should().BeEquivalentTo(intent); + } + + [Fact] + public async Task GetClaimIntentFromFid_ThrowsExceptionWhenNotFound() + { + // Act + await Assert.ThrowsAsync(() => _repository.GetClaimIntent(new FederatedCertificateId { - // Arrange - var intent1 = _fixture.Create(); - var intent2 = _fixture.Create(); - await _repository.InsertClaimIntent(intent1); - - // Act - var result = await _repository.HasClaimIntent(new FederatedCertificateId - { - RegistryName = intent2.RegistryName, - StreamId = intent2.CertificateId - }, intent2.CommitmentHash); - - // Assert - Assert.False(result); - } - - [Fact] - public async Task DeleteClaimIntent_DeletesClaimIntent() + RegistryName = _fixture.Create(), + StreamId = Guid.NewGuid() + }, new byte[32])); + } + + [Fact] + public async Task HasClaimIntent_ReturnsFalse_WhenClaimIntentDoesNotExist() + { + // Act + var result = await _repository.HasClaimIntent(new FederatedCertificateId { - // Arrange - var intent = _fixture.Create(); - await _repository.InsertClaimIntent(intent); + RegistryName = _fixture.Create(), + StreamId = Guid.NewGuid() + }, new byte[32]); - // Act - await _repository.DeleteClaimIntent(intent.Id); + // Assert + Assert.False(result); + } - // Assert - Assert.Empty(_con.Query("SELECT * FROM claim_intents")); - } + [Fact] + public async Task HasClaimIntent_ReturnsTrue_WhenClaimIntentExists() + { + // Arrange + var intent = _fixture.Create(); + await _repository.InsertClaimIntent(intent); - [Fact] - public async Task DeleteClaimIntent_DoesNotThrowExceptionWhenClaimIntentDoesNotExist() + // Act + var result = await _repository.HasClaimIntent(new FederatedCertificateId { - // Act - await _repository.DeleteClaimIntent(Guid.NewGuid()); - } + RegistryName = intent.RegistryName, + StreamId = intent.CertificateId + }, intent.CommitmentHash); - [Fact] - public async Task DeleteClaimIntent_DoesNotDeleteOtherClaimIntents() + // Assert + Assert.True(result); + } + + [Fact] + public async Task HasClaimIntent_ReturnsFalse_WhenOtherClaimIntentExists() + { + // Arrange + var intent1 = _fixture.Create(); + var intent2 = _fixture.Create(); + await _repository.InsertClaimIntent(intent1); + + // Act + var result = await _repository.HasClaimIntent(new FederatedCertificateId { - // Arrange - var intent1 = _fixture.Create(); - var intent2 = _fixture.Create(); - await _repository.InsertClaimIntent(intent1); - await _repository.InsertClaimIntent(intent2); + RegistryName = intent2.RegistryName, + StreamId = intent2.CertificateId + }, intent2.CommitmentHash); - // Act - await _repository.DeleteClaimIntent(intent1.Id); + // Assert + Assert.False(result); + } - // Assert - Assert.Single(_con.Query("SELECT * FROM claim_intents")); - } + [Fact] + public async Task DeleteClaimIntent_DeletesClaimIntent() + { + // Arrange + var intent = _fixture.Create(); + await _repository.InsertClaimIntent(intent); - [Fact] - public async Task UpsertReadBlock_InsertsBlock() - { - // Arrange - var registry = _fixture.Create(); + // Act + await _repository.DeleteClaimIntent(intent.Id); - // Act - await _repository.UpsertReadBlock(registry); + // Assert + Assert.Empty(_con.Query("SELECT * FROM claim_intents")); + } - // Assert - _con.QuerySingle("SELECT * FROM read_blocks").Should().BeEquivalentTo(registry); - } + [Fact] + public async Task DeleteClaimIntent_DoesNotThrowExceptionWhenClaimIntentDoesNotExist() + { + // Act + await _repository.DeleteClaimIntent(Guid.NewGuid()); + } - [Fact] - public async Task UpsertReadBlock_UpdatesBlock() - { - // Arrange - var registry = _fixture.Create(); - await _repository.UpsertReadBlock(registry); - - var updatedRegistry = new LastReadBlock - { - RegistryName = registry.RegistryName, - BlockHeight = registry.BlockHeight + 1, - ReadAt = registry.ReadAt - }; - - // Act - await _repository.UpsertReadBlock(updatedRegistry); - - // Assert - _con.QuerySingle("SELECT * FROM read_blocks").Should().BeEquivalentTo(updatedRegistry); - } - - [Fact] - public async Task GetRegistriesToCrawl_ReturnsRegistries() - { - // Arrange - var registry1 = _fixture.Create(); - var registry2 = _fixture.Create(); - await _repository.UpsertReadBlock(registry1); - await _repository.UpsertReadBlock(registry2); + [Fact] + public async Task DeleteClaimIntent_DoesNotDeleteOtherClaimIntents() + { + // Arrange + var intent1 = _fixture.Create(); + var intent2 = _fixture.Create(); + await _repository.InsertClaimIntent(intent1); + await _repository.InsertClaimIntent(intent2); - // Act - var result = await _repository.GetRegistriesToCrawl(); + // Act + await _repository.DeleteClaimIntent(intent1.Id); - // Assert - result.Should().BeEquivalentTo(new[] { registry1, registry2 }); - } + // Assert + Assert.Single(_con.Query("SELECT * FROM claim_intents")); + } - [Fact] - public async Task GetRegistriesToCrawl_ReturnsEmptyListWhenNoRegistries() - { - // Act - var result = await _repository.GetRegistriesToCrawl(); + [Fact] + public async Task UpsertReadBlock_InsertsBlock() + { + // Arrange + var registry = _fixture.Create(); + + // Act + await _repository.UpsertReadBlock(registry); + + // Assert + _con.QuerySingle("SELECT * FROM read_blocks").Should().BeEquivalentTo(registry); + } - // Assert - Assert.Empty(result); - } + [Fact] + public async Task UpsertReadBlock_UpdatesBlock() + { + // Arrange + var registry = _fixture.Create(); + await _repository.UpsertReadBlock(registry); - [Fact] - public async Task InsertCertificateInfo_InsertsCertificateInfo() + var updatedRegistry = new LastReadBlock { - // Arrange - var certificateInfo = _fixture.Create(); + RegistryName = registry.RegistryName, + BlockHeight = registry.BlockHeight + 1, + ReadAt = registry.ReadAt + }; - // Act - await _repository.InsertCertificateInfo(certificateInfo); + // Act + await _repository.UpsertReadBlock(updatedRegistry); - // Assert - _con.QuerySingle("SELECT * FROM certificate_infos").Should().BeEquivalentTo(certificateInfo); - } + // Assert + _con.QuerySingle("SELECT * FROM read_blocks").Should().BeEquivalentTo(updatedRegistry); + } - [Fact] - public async Task InsertClaimAllocation_InsertsClaimAllocation() - { - // Arrange - var allocation = _fixture.Create(); + [Fact] + public async Task GetRegistriesToCrawl_ReturnsRegistries() + { + // Arrange + var registry1 = _fixture.Create(); + var registry2 = _fixture.Create(); + await _repository.UpsertReadBlock(registry1); + await _repository.UpsertReadBlock(registry2); - // Act - await _repository.InsertClaimAllocation(allocation); + // Act + var result = await _repository.GetRegistriesToCrawl(); - // Assert - _con.QuerySingle("SELECT * FROM claim_allocations").Should().BeEquivalentTo(allocation); - } + // Assert + result.Should().BeEquivalentTo(new[] { registry1, registry2 }); + } - [Fact] - public async Task GetClaimAllocation_ReturnsClaimAllocation() - { - // Arrange - var allocation = _fixture.Create(); - await _repository.InsertClaimAllocation(allocation); - - // Act - var result = await _repository.GetClaimAllocation(new FederatedCertificateId - { - RegistryName = allocation.RegistryName, - StreamId = allocation.CertificateId - }, allocation.AllocationId); - - // Assert - result.Should().BeEquivalentTo(allocation); - } - - [Fact] - public async Task GetClaimAllocation_ThrowsExceptionWhenNotFound() + [Fact] + public async Task GetRegistriesToCrawl_ReturnsEmptyListWhenNoRegistries() + { + // Act + var result = await _repository.GetRegistriesToCrawl(); + + // Assert + Assert.Empty(result); + } + + [Fact] + public async Task InsertCertificateInfo_InsertsCertificateInfo() + { + // Arrange + var certificateInfo = _fixture.Create(); + + // Act + await _repository.InsertCertificateInfo(certificateInfo); + + // Assert + _con.QuerySingle("SELECT * FROM certificate_infos").Should().BeEquivalentTo(certificateInfo); + } + + [Fact] + public async Task InsertClaimAllocation_InsertsClaimAllocation() + { + // Arrange + var allocation = _fixture.Create(); + + // Act + await _repository.InsertClaimAllocation(allocation); + + // Assert + _con.QuerySingle("SELECT * FROM claim_allocations").Should().BeEquivalentTo(allocation); + } + + [Fact] + public async Task GetClaimAllocation_ReturnsClaimAllocation() + { + // Arrange + var allocation = _fixture.Create(); + await _repository.InsertClaimAllocation(allocation); + + // Act + var result = await _repository.GetClaimAllocation(new FederatedCertificateId { - // Act - await Assert.ThrowsAsync(() => _repository.GetClaimAllocation(new FederatedCertificateId - { - RegistryName = _fixture.Create(), - StreamId = Guid.NewGuid() - }, Guid.NewGuid())); - } - - [Fact] - public async Task DeleteClaimAllocation_DeletesClaimAllocation() + RegistryName = allocation.RegistryName, + StreamId = allocation.CertificateId + }, allocation.AllocationId); + + // Assert + result.Should().BeEquivalentTo(allocation); + } + + [Fact] + public async Task GetClaimAllocation_ThrowsExceptionWhenNotFound() + { + // Act + await Assert.ThrowsAsync(() => _repository.GetClaimAllocation(new FederatedCertificateId { - // Arrange - var allocation = _fixture.Create(); - await _repository.InsertClaimAllocation(allocation); + RegistryName = _fixture.Create(), + StreamId = Guid.NewGuid() + }, Guid.NewGuid())); + } - // Act - await _repository.DeleteClaimAllocation(allocation.Id); + [Fact] + public async Task DeleteClaimAllocation_DeletesClaimAllocation() + { + // Arrange + var allocation = _fixture.Create(); + await _repository.InsertClaimAllocation(allocation); - // Assert - Assert.Empty(_con.Query("SELECT * FROM claim_allocations")); - } + // Act + await _repository.DeleteClaimAllocation(allocation.Id); - [Fact] - public async Task InsertClaimRecord_InsertsClaimRecord() - { - // Arrange - var record = _fixture.Create(); + // Assert + Assert.Empty(_con.Query("SELECT * FROM claim_allocations")); + } + + [Fact] + public async Task InsertClaimRecord_InsertsClaimRecord() + { + // Arrange + var record = _fixture.Create(); - // Act - await _repository.InsertClaimRecord(record); + // Act + await _repository.InsertClaimRecord(record); - // Assert - _con.QuerySingle("SELECT * FROM claim_records").Should().BeEquivalentTo(record); - } + // Assert + _con.QuerySingle("SELECT * FROM claim_records").Should().BeEquivalentTo(record); } }