Rename Feedser to Concelier
This commit is contained in:
		@@ -0,0 +1,208 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using StellaOps.Concelier.Storage.Mongo.Exporting;
 | 
			
		||||
 | 
			
		||||
namespace StellaOps.Concelier.Storage.Mongo.Tests;
 | 
			
		||||
 | 
			
		||||
public sealed class ExportStateManagerTests
 | 
			
		||||
{
 | 
			
		||||
    [Fact]
 | 
			
		||||
    public async Task StoreFullExportInitializesBaseline()
 | 
			
		||||
    {
 | 
			
		||||
        var store = new InMemoryExportStateStore();
 | 
			
		||||
        var timeProvider = new TestTimeProvider(DateTimeOffset.Parse("2024-07-20T12:00:00Z"));
 | 
			
		||||
        var manager = new ExportStateManager(store, timeProvider);
 | 
			
		||||
 | 
			
		||||
        var record = await manager.StoreFullExportAsync(
 | 
			
		||||
            exporterId: "export:json",
 | 
			
		||||
            exportId: "20240720T120000Z",
 | 
			
		||||
            exportDigest: "sha256:abcd",
 | 
			
		||||
            cursor: "cursor-1",
 | 
			
		||||
            targetRepository: "registry.local/json",
 | 
			
		||||
            exporterVersion: "1.0.0",
 | 
			
		||||
            resetBaseline: true,
 | 
			
		||||
            manifest: Array.Empty<ExportFileRecord>(),
 | 
			
		||||
            cancellationToken: CancellationToken.None);
 | 
			
		||||
 | 
			
		||||
        Assert.Equal("export:json", record.Id);
 | 
			
		||||
        Assert.Equal("20240720T120000Z", record.BaseExportId);
 | 
			
		||||
        Assert.Equal("sha256:abcd", record.BaseDigest);
 | 
			
		||||
        Assert.Equal("sha256:abcd", record.LastFullDigest);
 | 
			
		||||
        Assert.Null(record.LastDeltaDigest);
 | 
			
		||||
        Assert.Equal("cursor-1", record.ExportCursor);
 | 
			
		||||
        Assert.Equal("registry.local/json", record.TargetRepository);
 | 
			
		||||
        Assert.Equal("1.0.0", record.ExporterVersion);
 | 
			
		||||
        Assert.Equal(timeProvider.Now, record.UpdatedAt);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Fact]
 | 
			
		||||
    public async Task StoreFullExport_ResetBaselineOverridesExisting()
 | 
			
		||||
    {
 | 
			
		||||
        var store = new InMemoryExportStateStore();
 | 
			
		||||
        var timeProvider = new TestTimeProvider(DateTimeOffset.Parse("2024-07-20T12:00:00Z"));
 | 
			
		||||
        var manager = new ExportStateManager(store, timeProvider);
 | 
			
		||||
 | 
			
		||||
        await manager.StoreFullExportAsync(
 | 
			
		||||
            exporterId: "export:json",
 | 
			
		||||
            exportId: "20240720T120000Z",
 | 
			
		||||
            exportDigest: "sha256:base",
 | 
			
		||||
            cursor: "cursor-base",
 | 
			
		||||
            targetRepository: null,
 | 
			
		||||
            exporterVersion: "1.0.0",
 | 
			
		||||
            resetBaseline: true,
 | 
			
		||||
            manifest: Array.Empty<ExportFileRecord>(),
 | 
			
		||||
            cancellationToken: CancellationToken.None);
 | 
			
		||||
 | 
			
		||||
        timeProvider.Advance(TimeSpan.FromMinutes(5));
 | 
			
		||||
        var withoutReset = await manager.StoreFullExportAsync(
 | 
			
		||||
            exporterId: "export:json",
 | 
			
		||||
            exportId: "20240720T120500Z",
 | 
			
		||||
            exportDigest: "sha256:new",
 | 
			
		||||
            cursor: "cursor-new",
 | 
			
		||||
            targetRepository: null,
 | 
			
		||||
            exporterVersion: "1.0.1",
 | 
			
		||||
            resetBaseline: false,
 | 
			
		||||
            manifest: Array.Empty<ExportFileRecord>(),
 | 
			
		||||
            cancellationToken: CancellationToken.None);
 | 
			
		||||
 | 
			
		||||
        Assert.Equal("20240720T120000Z", withoutReset.BaseExportId);
 | 
			
		||||
        Assert.Equal("sha256:base", withoutReset.BaseDigest);
 | 
			
		||||
        Assert.Equal("sha256:new", withoutReset.LastFullDigest);
 | 
			
		||||
        Assert.Equal("cursor-new", withoutReset.ExportCursor);
 | 
			
		||||
        Assert.Equal(timeProvider.Now, withoutReset.UpdatedAt);
 | 
			
		||||
 | 
			
		||||
        timeProvider.Advance(TimeSpan.FromMinutes(5));
 | 
			
		||||
        var reset = await manager.StoreFullExportAsync(
 | 
			
		||||
            exporterId: "export:json",
 | 
			
		||||
            exportId: "20240720T121000Z",
 | 
			
		||||
            exportDigest: "sha256:final",
 | 
			
		||||
            cursor: "cursor-final",
 | 
			
		||||
            targetRepository: null,
 | 
			
		||||
            exporterVersion: "1.0.2",
 | 
			
		||||
            resetBaseline: true,
 | 
			
		||||
            manifest: Array.Empty<ExportFileRecord>(),
 | 
			
		||||
            cancellationToken: CancellationToken.None);
 | 
			
		||||
 | 
			
		||||
        Assert.Equal("20240720T121000Z", reset.BaseExportId);
 | 
			
		||||
        Assert.Equal("sha256:final", reset.BaseDigest);
 | 
			
		||||
        Assert.Equal("sha256:final", reset.LastFullDigest);
 | 
			
		||||
        Assert.Null(reset.LastDeltaDigest);
 | 
			
		||||
        Assert.Equal("cursor-final", reset.ExportCursor);
 | 
			
		||||
        Assert.Equal(timeProvider.Now, reset.UpdatedAt);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Fact]
 | 
			
		||||
    public async Task StoreFullExport_ResetsBaselineWhenRepositoryChanges()
 | 
			
		||||
    {
 | 
			
		||||
        var store = new InMemoryExportStateStore();
 | 
			
		||||
        var timeProvider = new TestTimeProvider(DateTimeOffset.Parse("2024-07-21T08:00:00Z"));
 | 
			
		||||
        var manager = new ExportStateManager(store, timeProvider);
 | 
			
		||||
 | 
			
		||||
        await manager.StoreFullExportAsync(
 | 
			
		||||
            exporterId: "export:json",
 | 
			
		||||
            exportId: "20240721T080000Z",
 | 
			
		||||
            exportDigest: "sha256:base",
 | 
			
		||||
            cursor: "cursor-base",
 | 
			
		||||
            targetRepository: "registry/v1/json",
 | 
			
		||||
            exporterVersion: "1.0.0",
 | 
			
		||||
            resetBaseline: true,
 | 
			
		||||
            manifest: Array.Empty<ExportFileRecord>(),
 | 
			
		||||
            cancellationToken: CancellationToken.None);
 | 
			
		||||
 | 
			
		||||
        timeProvider.Advance(TimeSpan.FromMinutes(10));
 | 
			
		||||
        var updated = await manager.StoreFullExportAsync(
 | 
			
		||||
            exporterId: "export:json",
 | 
			
		||||
            exportId: "20240721T081000Z",
 | 
			
		||||
            exportDigest: "sha256:new",
 | 
			
		||||
            cursor: "cursor-new",
 | 
			
		||||
            targetRepository: "registry/v2/json",
 | 
			
		||||
            exporterVersion: "1.1.0",
 | 
			
		||||
            resetBaseline: false,
 | 
			
		||||
            manifest: Array.Empty<ExportFileRecord>(),
 | 
			
		||||
            cancellationToken: CancellationToken.None);
 | 
			
		||||
 | 
			
		||||
        Assert.Equal("20240721T081000Z", updated.BaseExportId);
 | 
			
		||||
        Assert.Equal("sha256:new", updated.BaseDigest);
 | 
			
		||||
        Assert.Equal("sha256:new", updated.LastFullDigest);
 | 
			
		||||
        Assert.Equal("registry/v2/json", updated.TargetRepository);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Fact]
 | 
			
		||||
    public async Task StoreDeltaExportRequiresBaseline()
 | 
			
		||||
    {
 | 
			
		||||
        var store = new InMemoryExportStateStore();
 | 
			
		||||
        var manager = new ExportStateManager(store);
 | 
			
		||||
 | 
			
		||||
        await Assert.ThrowsAsync<InvalidOperationException>(() => manager.StoreDeltaExportAsync(
 | 
			
		||||
            exporterId: "export:json",
 | 
			
		||||
            deltaDigest: "sha256:def",
 | 
			
		||||
            cursor: null,
 | 
			
		||||
            exporterVersion: "1.0.1",
 | 
			
		||||
            manifest: Array.Empty<ExportFileRecord>(),
 | 
			
		||||
            cancellationToken: CancellationToken.None));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Fact]
 | 
			
		||||
    public async Task StoreDeltaExportUpdatesExistingState()
 | 
			
		||||
    {
 | 
			
		||||
        var store = new InMemoryExportStateStore();
 | 
			
		||||
        var timeProvider = new TestTimeProvider(DateTimeOffset.Parse("2024-07-20T12:00:00Z"));
 | 
			
		||||
        var manager = new ExportStateManager(store, timeProvider);
 | 
			
		||||
 | 
			
		||||
        await manager.StoreFullExportAsync(
 | 
			
		||||
            exporterId: "export:json",
 | 
			
		||||
            exportId: "20240720T120000Z",
 | 
			
		||||
            exportDigest: "sha256:abcd",
 | 
			
		||||
            cursor: "cursor-1",
 | 
			
		||||
            targetRepository: null,
 | 
			
		||||
            exporterVersion: "1.0.0",
 | 
			
		||||
            resetBaseline: true,
 | 
			
		||||
            manifest: Array.Empty<ExportFileRecord>(),
 | 
			
		||||
            cancellationToken: CancellationToken.None);
 | 
			
		||||
 | 
			
		||||
        timeProvider.Advance(TimeSpan.FromMinutes(10));
 | 
			
		||||
        var delta = await manager.StoreDeltaExportAsync(
 | 
			
		||||
            exporterId: "export:json",
 | 
			
		||||
            deltaDigest: "sha256:ef01",
 | 
			
		||||
            cursor: "cursor-2",
 | 
			
		||||
            exporterVersion: "1.0.1",
 | 
			
		||||
            manifest: Array.Empty<ExportFileRecord>(),
 | 
			
		||||
            cancellationToken: CancellationToken.None);
 | 
			
		||||
 | 
			
		||||
        Assert.Equal("sha256:ef01", delta.LastDeltaDigest);
 | 
			
		||||
        Assert.Equal("cursor-2", delta.ExportCursor);
 | 
			
		||||
        Assert.Equal("1.0.1", delta.ExporterVersion);
 | 
			
		||||
        Assert.Equal(timeProvider.Now, delta.UpdatedAt);
 | 
			
		||||
        Assert.Equal("sha256:abcd", delta.LastFullDigest);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private sealed class InMemoryExportStateStore : IExportStateStore
 | 
			
		||||
    {
 | 
			
		||||
        private readonly Dictionary<string, ExportStateRecord> _records = new(StringComparer.Ordinal);
 | 
			
		||||
 | 
			
		||||
        public Task<ExportStateRecord?> FindAsync(string id, CancellationToken cancellationToken)
 | 
			
		||||
        {
 | 
			
		||||
            _records.TryGetValue(id, out var record);
 | 
			
		||||
            return Task.FromResult<ExportStateRecord?>(record);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Task<ExportStateRecord> UpsertAsync(ExportStateRecord record, CancellationToken cancellationToken)
 | 
			
		||||
        {
 | 
			
		||||
            _records[record.Id] = record;
 | 
			
		||||
            return Task.FromResult(record);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private sealed class TestTimeProvider : TimeProvider
 | 
			
		||||
    {
 | 
			
		||||
        public TestTimeProvider(DateTimeOffset start) => Now = start;
 | 
			
		||||
 | 
			
		||||
        public DateTimeOffset Now { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public void Advance(TimeSpan delta) => Now = Now.Add(delta);
 | 
			
		||||
 | 
			
		||||
        public override DateTimeOffset GetUtcNow() => Now;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user