notify doctors work, audit work, new product advisory sprints
This commit is contained in:
@@ -0,0 +1,313 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using StellaOps.Attestor.Core.Rekor;
|
||||
using StellaOps.Attestor.Infrastructure.Rekor;
|
||||
using StellaOps.TestKit;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Attestor.Infrastructure.Tests;
|
||||
|
||||
public sealed class HttpRekorTileClientTests
|
||||
{
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetCheckpointAsync_ValidCheckpoint_ParsesCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var checkpoint = """
|
||||
rekor.sigstore.dev - 2605736670972794746
|
||||
12345678
|
||||
rMj3G9LfM9C6Xt0qpV3pHbM2q5lPvKjS0mOmV8jXwAk=
|
||||
|
||||
- rekor.sigstore.dev ABC123signature==
|
||||
""";
|
||||
|
||||
var client = CreateClient(new CheckpointHandler(checkpoint));
|
||||
var backend = CreateBackend();
|
||||
|
||||
// Act
|
||||
var result = await client.GetCheckpointAsync(backend, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result!.Origin.Should().Be("rekor.sigstore.dev - 2605736670972794746");
|
||||
result.TreeSize.Should().Be(12345678);
|
||||
result.RootHash.Should().NotBeNullOrEmpty();
|
||||
result.Signatures.Should().HaveCount(1);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetCheckpointAsync_NotFound_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var client = CreateClient(new NotFoundHandler());
|
||||
var backend = CreateBackend();
|
||||
|
||||
// Act
|
||||
var result = await client.GetCheckpointAsync(backend, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().BeNull();
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetTileAsync_ValidTile_ReturnsTileData()
|
||||
{
|
||||
// Arrange - 256 hashes (32 bytes each) = 8192 bytes
|
||||
var tileData = new byte[32 * 4]; // 4 hashes for simplicity
|
||||
Random.Shared.NextBytes(tileData);
|
||||
|
||||
var client = CreateClient(new TileHandler(tileData));
|
||||
var backend = CreateBackend();
|
||||
|
||||
// Act
|
||||
var result = await client.GetTileAsync(backend, level: 0, index: 0, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
result!.Level.Should().Be(0);
|
||||
result.Index.Should().Be(0);
|
||||
result.Width.Should().Be(4);
|
||||
result.Hashes.Should().Equal(tileData);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetTileAsync_NotFound_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var client = CreateClient(new NotFoundHandler());
|
||||
var backend = CreateBackend();
|
||||
|
||||
// Act
|
||||
var result = await client.GetTileAsync(backend, level: 0, index: 999999, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().BeNull();
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void RekorTileData_GetHash_ReturnsCorrectHash()
|
||||
{
|
||||
// Arrange
|
||||
var hash1 = new byte[32];
|
||||
var hash2 = new byte[32];
|
||||
Random.Shared.NextBytes(hash1);
|
||||
Random.Shared.NextBytes(hash2);
|
||||
|
||||
var hashes = new byte[64];
|
||||
Array.Copy(hash1, 0, hashes, 0, 32);
|
||||
Array.Copy(hash2, 0, hashes, 32, 32);
|
||||
|
||||
var tile = new RekorTileData
|
||||
{
|
||||
Level = 0,
|
||||
Index = 0,
|
||||
Width = 2,
|
||||
Hashes = hashes
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
tile.GetHash(0).Should().Equal(hash1);
|
||||
tile.GetHash(1).Should().Equal(hash2);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void RekorTileData_GetHash_OutOfRange_Throws()
|
||||
{
|
||||
// Arrange
|
||||
var tile = new RekorTileData
|
||||
{
|
||||
Level = 0,
|
||||
Index = 0,
|
||||
Width = 2,
|
||||
Hashes = new byte[64]
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
var action = () => tile.GetHash(2);
|
||||
action.Should().Throw<ArgumentOutOfRangeException>();
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void RekorBackend_GetEffectiveTileBaseUrl_WithoutConfig_ReturnsDefault()
|
||||
{
|
||||
// Arrange
|
||||
var backend = new RekorBackend
|
||||
{
|
||||
Name = "test",
|
||||
Url = new Uri("https://rekor.sigstore.dev"),
|
||||
Version = RekorLogVersion.V2
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = backend.GetEffectiveTileBaseUrl();
|
||||
|
||||
// Assert
|
||||
result.Should().Be(new Uri("https://rekor.sigstore.dev/tile/"));
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void RekorBackend_GetEffectiveTileBaseUrl_WithConfig_ReturnsConfigured()
|
||||
{
|
||||
// Arrange
|
||||
var backend = new RekorBackend
|
||||
{
|
||||
Name = "test",
|
||||
Url = new Uri("https://rekor.sigstore.dev"),
|
||||
Version = RekorLogVersion.V2,
|
||||
TileBaseUrl = new Uri("https://tiles.rekor.sigstore.dev/")
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = backend.GetEffectiveTileBaseUrl();
|
||||
|
||||
// Assert
|
||||
result.Should().Be(new Uri("https://tiles.rekor.sigstore.dev/"));
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Theory]
|
||||
[InlineData(RekorLogVersion.V2, false, true)]
|
||||
[InlineData(RekorLogVersion.V1, false, false)]
|
||||
[InlineData(RekorLogVersion.V1, true, false)]
|
||||
[InlineData(RekorLogVersion.Auto, false, false)]
|
||||
[InlineData(RekorLogVersion.Auto, true, true)]
|
||||
public void ShouldUseTileProofs_ReturnsExpected(RekorLogVersion version, bool preferTiles, bool expected)
|
||||
{
|
||||
// Arrange
|
||||
var backend = new RekorBackend
|
||||
{
|
||||
Name = "test",
|
||||
Url = new Uri("https://rekor.sigstore.dev"),
|
||||
Version = version,
|
||||
PreferTileProofs = preferTiles
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = RekorBackendResolver.ShouldUseTileProofs(backend);
|
||||
|
||||
// Assert
|
||||
result.Should().Be(expected);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetEntryAsync_NotFound_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var client = CreateClient(new NotFoundHandler());
|
||||
var backend = CreateBackend();
|
||||
|
||||
// Act
|
||||
var result = await client.GetEntryAsync(backend, logIndex: 12345, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().BeNull();
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task ComputeInclusionProofAsync_InvalidIndex_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var client = CreateClient(new NotFoundHandler());
|
||||
var backend = CreateBackend();
|
||||
|
||||
// Act - index >= treeSize
|
||||
var result = await client.ComputeInclusionProofAsync(backend, logIndex: 100, treeSize: 50, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Should().BeNull();
|
||||
}
|
||||
|
||||
private static HttpRekorTileClient CreateClient(HttpMessageHandler handler)
|
||||
{
|
||||
var httpClient = new HttpClient(handler)
|
||||
{
|
||||
BaseAddress = new Uri("https://rekor.sigstore.dev")
|
||||
};
|
||||
|
||||
return new HttpRekorTileClient(httpClient, NullLogger<HttpRekorTileClient>.Instance);
|
||||
}
|
||||
|
||||
private static RekorBackend CreateBackend()
|
||||
{
|
||||
return new RekorBackend
|
||||
{
|
||||
Name = "primary",
|
||||
Url = new Uri("https://rekor.sigstore.dev"),
|
||||
Version = RekorLogVersion.V2
|
||||
};
|
||||
}
|
||||
|
||||
private sealed class CheckpointHandler : HttpMessageHandler
|
||||
{
|
||||
private readonly string _checkpoint;
|
||||
|
||||
public CheckpointHandler(string checkpoint)
|
||||
{
|
||||
_checkpoint = checkpoint;
|
||||
}
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var path = request.RequestUri?.AbsolutePath ?? string.Empty;
|
||||
|
||||
if (path.Contains("checkpoint", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new StringContent(_checkpoint, Encoding.UTF8, "text/plain")
|
||||
});
|
||||
}
|
||||
|
||||
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound));
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class TileHandler : HttpMessageHandler
|
||||
{
|
||||
private readonly byte[] _tileData;
|
||||
|
||||
public TileHandler(byte[] tileData)
|
||||
{
|
||||
_tileData = tileData;
|
||||
}
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var path = request.RequestUri?.AbsolutePath ?? string.Empty;
|
||||
|
||||
if (path.Contains("tile/", StringComparison.OrdinalIgnoreCase) && !path.Contains("checkpoint", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new ByteArrayContent(_tileData)
|
||||
});
|
||||
}
|
||||
|
||||
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound));
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class NotFoundHandler : HttpMessageHandler
|
||||
{
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using StellaOps.Attestor.Core.Options;
|
||||
using StellaOps.Attestor.Core.Rekor;
|
||||
using StellaOps.Attestor.Infrastructure.Rekor;
|
||||
using StellaOps.TestKit;
|
||||
using Xunit;
|
||||
@@ -35,6 +36,155 @@ public sealed class RekorBackendResolverTests
|
||||
backend.Url.Should().Be(new Uri("https://rekor.primary.example"));
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Theory]
|
||||
[InlineData("Auto", RekorLogVersion.Auto)]
|
||||
[InlineData("auto", RekorLogVersion.Auto)]
|
||||
[InlineData("V1", RekorLogVersion.V1)]
|
||||
[InlineData("v1", RekorLogVersion.V1)]
|
||||
[InlineData("1", RekorLogVersion.V1)]
|
||||
[InlineData("V2", RekorLogVersion.V2)]
|
||||
[InlineData("v2", RekorLogVersion.V2)]
|
||||
[InlineData("2", RekorLogVersion.V2)]
|
||||
[InlineData("", RekorLogVersion.Auto)]
|
||||
[InlineData(null, RekorLogVersion.Auto)]
|
||||
[InlineData("invalid", RekorLogVersion.Auto)]
|
||||
public void ResolveBackend_ParsesVersionCorrectly(string? versionString, RekorLogVersion expected)
|
||||
{
|
||||
var options = new AttestorOptions
|
||||
{
|
||||
Rekor = new AttestorOptions.RekorOptions
|
||||
{
|
||||
Primary = new AttestorOptions.RekorBackendOptions
|
||||
{
|
||||
Url = "https://rekor.sigstore.dev",
|
||||
Version = versionString ?? "Auto"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var backend = RekorBackendResolver.ResolveBackend(options, "primary", allowFallbackToPrimary: false);
|
||||
|
||||
backend.Version.Should().Be(expected);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void ResolveBackend_WithTileBaseUrl_SetsProperty()
|
||||
{
|
||||
var options = new AttestorOptions
|
||||
{
|
||||
Rekor = new AttestorOptions.RekorOptions
|
||||
{
|
||||
Primary = new AttestorOptions.RekorBackendOptions
|
||||
{
|
||||
Url = "https://rekor.sigstore.dev",
|
||||
Version = "V2",
|
||||
TileBaseUrl = "https://rekor.sigstore.dev/tile/"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var backend = RekorBackendResolver.ResolveBackend(options, "primary", allowFallbackToPrimary: false);
|
||||
|
||||
backend.Version.Should().Be(RekorLogVersion.V2);
|
||||
backend.TileBaseUrl.Should().Be(new Uri("https://rekor.sigstore.dev/tile/"));
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void ResolveBackend_WithLogId_SetsProperty()
|
||||
{
|
||||
var options = new AttestorOptions
|
||||
{
|
||||
Rekor = new AttestorOptions.RekorOptions
|
||||
{
|
||||
Primary = new AttestorOptions.RekorBackendOptions
|
||||
{
|
||||
Url = "https://rekor.sigstore.dev",
|
||||
LogId = RekorBackend.SigstoreProductionLogId
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var backend = RekorBackendResolver.ResolveBackend(options, "primary", allowFallbackToPrimary: false);
|
||||
|
||||
backend.LogId.Should().Be(RekorBackend.SigstoreProductionLogId);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void ResolveBackend_WithPreferTileProofs_SetsProperty()
|
||||
{
|
||||
var options = new AttestorOptions
|
||||
{
|
||||
Rekor = new AttestorOptions.RekorOptions
|
||||
{
|
||||
Primary = new AttestorOptions.RekorBackendOptions
|
||||
{
|
||||
Url = "https://rekor.sigstore.dev",
|
||||
PreferTileProofs = true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var backend = RekorBackendResolver.ResolveBackend(options, "primary", allowFallbackToPrimary: false);
|
||||
|
||||
backend.PreferTileProofs.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Theory]
|
||||
[InlineData(RekorLogVersion.V2, false, true)]
|
||||
[InlineData(RekorLogVersion.V1, true, false)]
|
||||
[InlineData(RekorLogVersion.Auto, true, true)]
|
||||
[InlineData(RekorLogVersion.Auto, false, false)]
|
||||
public void ShouldUseTileProofs_ReturnsCorrectValue(RekorLogVersion version, bool preferTileProofs, bool expected)
|
||||
{
|
||||
var backend = new RekorBackend
|
||||
{
|
||||
Name = "test",
|
||||
Url = new Uri("https://rekor.sigstore.dev"),
|
||||
Version = version,
|
||||
PreferTileProofs = preferTileProofs
|
||||
};
|
||||
|
||||
var result = RekorBackendResolver.ShouldUseTileProofs(backend);
|
||||
|
||||
result.Should().Be(expected);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void GetEffectiveTileBaseUrl_WithoutTileBaseUrl_ReturnsDefault()
|
||||
{
|
||||
var backend = new RekorBackend
|
||||
{
|
||||
Name = "test",
|
||||
Url = new Uri("https://rekor.sigstore.dev")
|
||||
};
|
||||
|
||||
var result = backend.GetEffectiveTileBaseUrl();
|
||||
|
||||
result.Should().Be(new Uri("https://rekor.sigstore.dev/tile/"));
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void GetEffectiveTileBaseUrl_WithTileBaseUrl_ReturnsConfigured()
|
||||
{
|
||||
var backend = new RekorBackend
|
||||
{
|
||||
Name = "test",
|
||||
Url = new Uri("https://rekor.sigstore.dev"),
|
||||
TileBaseUrl = new Uri("https://custom.tile.endpoint/v2/tile/")
|
||||
};
|
||||
|
||||
var result = backend.GetEffectiveTileBaseUrl();
|
||||
|
||||
result.Should().Be(new Uri("https://custom.tile.endpoint/v2/tile/"));
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void ResolveBackend_UnknownBackend_ThrowsWhenFallbackDisabled()
|
||||
|
||||
Reference in New Issue
Block a user