audit, advisories and doctors/setup work
This commit is contained in:
@@ -21,7 +21,7 @@ public sealed class CallGraphSyncServiceTests
|
||||
public async Task SyncAsync_WithValidDocument_ReturnsSuccessResult()
|
||||
{
|
||||
// Arrange
|
||||
var repository = new InMemoryCallGraphProjectionRepository();
|
||||
var repository = new InMemoryCallGraphProjectionRepository(TimeProvider.System);
|
||||
var service = new CallGraphSyncService(
|
||||
repository,
|
||||
TimeProvider.System,
|
||||
@@ -47,7 +47,7 @@ public sealed class CallGraphSyncServiceTests
|
||||
public async Task SyncAsync_ProjectsToRepository()
|
||||
{
|
||||
// Arrange
|
||||
var repository = new InMemoryCallGraphProjectionRepository();
|
||||
var repository = new InMemoryCallGraphProjectionRepository(TimeProvider.System);
|
||||
var service = new CallGraphSyncService(
|
||||
repository,
|
||||
TimeProvider.System,
|
||||
@@ -71,7 +71,7 @@ public sealed class CallGraphSyncServiceTests
|
||||
public async Task SyncAsync_SetsScanStatusToCompleted()
|
||||
{
|
||||
// Arrange
|
||||
var repository = new InMemoryCallGraphProjectionRepository();
|
||||
var repository = new InMemoryCallGraphProjectionRepository(TimeProvider.System);
|
||||
var service = new CallGraphSyncService(
|
||||
repository,
|
||||
TimeProvider.System,
|
||||
@@ -93,7 +93,7 @@ public sealed class CallGraphSyncServiceTests
|
||||
public async Task SyncAsync_WithEmptyDocument_ReturnsZeroCounts()
|
||||
{
|
||||
// Arrange
|
||||
var repository = new InMemoryCallGraphProjectionRepository();
|
||||
var repository = new InMemoryCallGraphProjectionRepository(TimeProvider.System);
|
||||
var service = new CallGraphSyncService(
|
||||
repository,
|
||||
TimeProvider.System,
|
||||
@@ -125,7 +125,7 @@ public sealed class CallGraphSyncServiceTests
|
||||
public async Task SyncAsync_WithNullDocument_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var repository = new InMemoryCallGraphProjectionRepository();
|
||||
var repository = new InMemoryCallGraphProjectionRepository(TimeProvider.System);
|
||||
var service = new CallGraphSyncService(
|
||||
repository,
|
||||
TimeProvider.System,
|
||||
@@ -141,7 +141,7 @@ public sealed class CallGraphSyncServiceTests
|
||||
public async Task SyncAsync_WithEmptyArtifactDigest_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
var repository = new InMemoryCallGraphProjectionRepository();
|
||||
var repository = new InMemoryCallGraphProjectionRepository(TimeProvider.System);
|
||||
var service = new CallGraphSyncService(
|
||||
repository,
|
||||
TimeProvider.System,
|
||||
@@ -159,7 +159,7 @@ public sealed class CallGraphSyncServiceTests
|
||||
public async Task DeleteByScanAsync_RemovesScanFromRepository()
|
||||
{
|
||||
// Arrange
|
||||
var repository = new InMemoryCallGraphProjectionRepository();
|
||||
var repository = new InMemoryCallGraphProjectionRepository(TimeProvider.System);
|
||||
var service = new CallGraphSyncService(
|
||||
repository,
|
||||
TimeProvider.System,
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Signals.Models;
|
||||
using StellaOps.Signals.Options;
|
||||
using StellaOps.Signals.Parsing;
|
||||
@@ -46,6 +47,7 @@ public class CallgraphIngestionServiceTests
|
||||
callGraphSyncService,
|
||||
options,
|
||||
_timeProvider,
|
||||
SystemGuidProvider.Instance,
|
||||
NullLogger<CallgraphIngestionService>.Instance);
|
||||
|
||||
var artifactJson = @"{""nodes"":[{""id"":""com/example/Foo.bar:(I)V"",""kind"":""fn""}],
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Signals.Options;
|
||||
using StellaOps.Signals.Services;
|
||||
using Xunit;
|
||||
@@ -24,7 +25,11 @@ public class EdgeBundleIngestionServiceTests
|
||||
var opts = new SignalsOptions();
|
||||
opts.Storage.RootPath = Path.GetTempPath();
|
||||
var options = Microsoft.Extensions.Options.Options.Create(opts);
|
||||
_service = new EdgeBundleIngestionService(NullLogger<EdgeBundleIngestionService>.Instance, options);
|
||||
_service = new EdgeBundleIngestionService(
|
||||
NullLogger<EdgeBundleIngestionService>.Instance,
|
||||
options,
|
||||
TimeProvider.System,
|
||||
SystemGuidProvider.Instance);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright © 2025 StellaOps
|
||||
// Copyright (c) 2025 StellaOps
|
||||
|
||||
using FluentAssertions;
|
||||
using StellaOps.Signals.EvidenceWeightedScore;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright © 2025 StellaOps
|
||||
// Copyright (c) 2025 StellaOps
|
||||
|
||||
using FluentAssertions;
|
||||
using StellaOps.Signals.EvidenceWeightedScore;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright © 2025 StellaOps
|
||||
// Copyright (c) 2025 StellaOps
|
||||
|
||||
using FluentAssertions;
|
||||
using StellaOps.Signals.EvidenceWeightedScore;
|
||||
@@ -30,7 +30,7 @@ public class EvidenceWeightedScoreCalculatorTests
|
||||
|
||||
var result = _calculator.Calculate(input, _defaultPolicy);
|
||||
|
||||
// Without MIT, sum of weights = 0.95 (default) → 95%
|
||||
// Without MIT, sum of weights = 0.95 (default) -> 95%
|
||||
result.Score.Should().BeGreaterThanOrEqualTo(90);
|
||||
result.Bucket.Should().Be(ScoreBucket.ActNow);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright © 2025 StellaOps
|
||||
// Copyright (c) 2025 StellaOps
|
||||
|
||||
using FluentAssertions;
|
||||
using StellaOps.Signals.EvidenceWeightedScore;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright © 2025 StellaOps
|
||||
// Copyright (c) 2025 StellaOps
|
||||
|
||||
using FluentAssertions;
|
||||
using StellaOps.Signals.EvidenceWeightedScore;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright © 2025 StellaOps
|
||||
// Copyright (c) 2025 StellaOps
|
||||
|
||||
using FluentAssertions;
|
||||
using StellaOps.Signals.EvidenceWeightedScore;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright © 2025 StellaOps
|
||||
// Copyright (c) 2025 StellaOps
|
||||
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright © 2025 StellaOps
|
||||
// Copyright (c) 2025 StellaOps
|
||||
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright © 2025 StellaOps
|
||||
// Copyright (c) 2025 StellaOps
|
||||
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright © 2025 StellaOps
|
||||
// Copyright (c) 2025 StellaOps
|
||||
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright © 2025 StellaOps
|
||||
// Copyright (c) 2025 StellaOps
|
||||
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright © 2025 StellaOps
|
||||
// Copyright (c) 2025 StellaOps
|
||||
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Options;
|
||||
@@ -290,7 +290,7 @@ public class NormalizerAggregatorTests
|
||||
};
|
||||
var optionsMonitor = new TestOptionsMonitor(options);
|
||||
|
||||
var aggregator = new NormalizerAggregator(optionsMonitor);
|
||||
var aggregator = new NormalizerAggregator(optionsMonitor, TimeProvider.System);
|
||||
|
||||
var evidence = new FindingEvidence
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright © 2025 StellaOps
|
||||
// Copyright (c) 2025 StellaOps
|
||||
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@@ -15,7 +15,7 @@ namespace StellaOps.Signals.Tests.EvidenceWeightedScore.Normalizers;
|
||||
/// </summary>
|
||||
public class NormalizerIntegrationTests
|
||||
{
|
||||
#region Backport Evidence → BKP Score Tests
|
||||
#region Backport Evidence -> BKP Score Tests
|
||||
|
||||
[Fact]
|
||||
public void BackportEvidence_PatchSignatureFixed_ProducesHighBkpScore()
|
||||
@@ -80,7 +80,7 @@ public class NormalizerIntegrationTests
|
||||
|
||||
#endregion
|
||||
|
||||
#region EPSS + KEV → XPL Score Tests
|
||||
#region EPSS + KEV -> XPL Score Tests
|
||||
|
||||
[Fact]
|
||||
public void ExploitEvidence_HighEpssAndKev_ProducesHighXplScore()
|
||||
@@ -147,7 +147,7 @@ public class NormalizerIntegrationTests
|
||||
|
||||
#endregion
|
||||
|
||||
#region Full Evidence Pipeline → Score Input Tests
|
||||
#region Full Evidence Pipeline -> Score Input Tests
|
||||
|
||||
[Fact]
|
||||
public void FullEvidence_AllDimensions_ProducesValidScoreInput()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright © 2025 StellaOps
|
||||
// Copyright (c) 2025 StellaOps
|
||||
|
||||
using FluentAssertions;
|
||||
using StellaOps.Signals.EvidenceWeightedScore;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright © 2025 StellaOps
|
||||
// Copyright (c) 2025 StellaOps
|
||||
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright © 2025 StellaOps
|
||||
// Copyright (c) 2025 StellaOps
|
||||
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Options;
|
||||
@@ -19,7 +19,7 @@ public class RuntimeSignalNormalizerTests
|
||||
|
||||
public RuntimeSignalNormalizerTests()
|
||||
{
|
||||
_sut = new RuntimeSignalNormalizer(_defaultOptions);
|
||||
_sut = new RuntimeSignalNormalizer(_defaultOptions, TimeProvider.System);
|
||||
}
|
||||
|
||||
#region Dimension Property Tests
|
||||
@@ -517,7 +517,7 @@ public class RuntimeSignalNormalizerTests
|
||||
};
|
||||
var optionsMonitor = new TestOptionsMonitor(options);
|
||||
|
||||
var normalizer = new RuntimeSignalNormalizer(optionsMonitor);
|
||||
var normalizer = new RuntimeSignalNormalizer(optionsMonitor, TimeProvider.System);
|
||||
|
||||
var input = new RuntimeInput
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Copyright © 2025 StellaOps
|
||||
// Copyright (c) 2025 StellaOps
|
||||
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Signals.Models;
|
||||
using StellaOps.Signals.Options;
|
||||
using StellaOps.Signals.Services;
|
||||
@@ -22,7 +23,7 @@ public class InMemoryEventsPublisherTests
|
||||
options.Events.Stream = "signals.fact.updated.v1";
|
||||
options.Events.DefaultTenant = "tenant-default";
|
||||
|
||||
var builder = new ReachabilityFactEventBuilder(options, TimeProvider.System);
|
||||
var builder = new ReachabilityFactEventBuilder(options, TimeProvider.System, SystemGuidProvider.Instance);
|
||||
var publisher = new InMemoryEventsPublisher(logger, builder);
|
||||
|
||||
var fact = new ReachabilityFactDocument
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Signals.Models;
|
||||
using StellaOps.Signals.Options;
|
||||
using StellaOps.Signals.Persistence;
|
||||
@@ -64,6 +65,7 @@ public class ReachabilityScoringServiceTests
|
||||
callgraphRepository,
|
||||
factRepository,
|
||||
TimeProvider.System,
|
||||
SystemGuidProvider.Instance,
|
||||
Options.Create(options),
|
||||
cache,
|
||||
unknowns,
|
||||
@@ -135,6 +137,7 @@ public class ReachabilityScoringServiceTests
|
||||
callgraphRepository,
|
||||
factRepository,
|
||||
TimeProvider.System,
|
||||
SystemGuidProvider.Instance,
|
||||
Options.Create(options),
|
||||
cache,
|
||||
unknowns,
|
||||
@@ -220,6 +223,7 @@ public class ReachabilityScoringServiceTests
|
||||
callgraphRepository,
|
||||
factRepository,
|
||||
TimeProvider.System,
|
||||
SystemGuidProvider.Instance,
|
||||
Options.Create(options),
|
||||
cache,
|
||||
unknowns,
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Signals.Models;
|
||||
using StellaOps.Signals.Options;
|
||||
using StellaOps.Signals.Services;
|
||||
@@ -25,7 +26,7 @@ public class RouterEventsPublisherTests
|
||||
var handler = new StubHandler(HttpStatusCode.Accepted);
|
||||
using var httpClient = new HttpClient(handler) { BaseAddress = new Uri(options.Events.Router.BaseUrl) };
|
||||
var logger = new ListLogger<RouterEventsPublisher>();
|
||||
var builder = new ReachabilityFactEventBuilder(options, TimeProvider.System);
|
||||
var builder = new ReachabilityFactEventBuilder(options, TimeProvider.System, SystemGuidProvider.Instance);
|
||||
var publisher = new RouterEventsPublisher(builder, options, httpClient, logger);
|
||||
|
||||
await publisher.PublishFactUpdatedAsync(CreateFact(), CancellationToken.None);
|
||||
@@ -49,7 +50,7 @@ public class RouterEventsPublisherTests
|
||||
var handler = new StubHandler(HttpStatusCode.InternalServerError, "boom");
|
||||
using var httpClient = new HttpClient(handler) { BaseAddress = new Uri(options.Events.Router.BaseUrl) };
|
||||
var logger = new ListLogger<RouterEventsPublisher>();
|
||||
var builder = new ReachabilityFactEventBuilder(options, TimeProvider.System);
|
||||
var builder = new ReachabilityFactEventBuilder(options, TimeProvider.System, SystemGuidProvider.Instance);
|
||||
var publisher = new RouterEventsPublisher(builder, options, httpClient, logger);
|
||||
|
||||
await publisher.PublishFactUpdatedAsync(CreateFact(), CancellationToken.None);
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using StellaOps.Cryptography;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Signals.Models;
|
||||
using StellaOps.Signals.Persistence;
|
||||
using StellaOps.Signals.Services;
|
||||
@@ -207,6 +208,7 @@ public class RuntimeFactsBatchIngestionTests
|
||||
return new RuntimeFactsIngestionService(
|
||||
repository,
|
||||
TimeProvider.System,
|
||||
SystemGuidProvider.Instance,
|
||||
cache,
|
||||
eventsPublisher,
|
||||
scoringService,
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Signals.Models;
|
||||
using StellaOps.Signals.Persistence;
|
||||
using StellaOps.Signals.Services;
|
||||
@@ -25,6 +26,7 @@ public class RuntimeFactsIngestionServiceTests
|
||||
var service = new RuntimeFactsIngestionService(
|
||||
factRepository,
|
||||
TimeProvider.System,
|
||||
SystemGuidProvider.Instance,
|
||||
cache,
|
||||
eventsPublisher,
|
||||
scoringService,
|
||||
@@ -446,6 +448,7 @@ public class RuntimeFactsIngestionServiceTests
|
||||
return new RuntimeFactsIngestionService(
|
||||
factRepository,
|
||||
TimeProvider.System,
|
||||
SystemGuidProvider.Instance,
|
||||
new InMemoryReachabilityCache(),
|
||||
new RecordingEventsPublisher(),
|
||||
new RecordingScoringService(),
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
// <copyright file="ScmWebhookServiceTests.cs" company="Stella Operations">
|
||||
// Copyright (c) Stella Operations. Licensed under AGPL-3.0-or-later.
|
||||
// </copyright>
|
||||
|
||||
using System.Globalization;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Signals.Options;
|
||||
using StellaOps.Signals.Scm.Models;
|
||||
using StellaOps.Signals.Scm.Services;
|
||||
using StellaOps.Signals.Scm.Webhooks;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Signals.Tests.Scm;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for SCM webhook processing.
|
||||
/// </summary>
|
||||
public sealed class ScmWebhookServiceTests
|
||||
{
|
||||
private static readonly DateTimeOffset FixedTimestamp =
|
||||
DateTimeOffset.Parse("2025-01-01T00:00:00Z", CultureInfo.InvariantCulture);
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessAsync_RejectsWhenSecretMissingAndUnsignedNotAllowed()
|
||||
{
|
||||
var options = new SignalsOptions();
|
||||
options.Scm.AllowUnsignedWebhooks = false;
|
||||
|
||||
var triggerService = new TestScmTriggerService();
|
||||
var service = CreateService(options, triggerService);
|
||||
|
||||
var payload = Encoding.UTF8.GetBytes("{}");
|
||||
|
||||
var result = await service.ProcessAsync(
|
||||
ScmProvider.GitHub,
|
||||
eventType: "push",
|
||||
deliveryId: "delivery-1",
|
||||
signature: null,
|
||||
payload: payload,
|
||||
integrationId: null,
|
||||
tenantId: null);
|
||||
|
||||
Assert.False(result.Success);
|
||||
Assert.Equal(401, result.StatusCode);
|
||||
Assert.Equal(0, triggerService.Calls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessAsync_AllowsUnsignedWhenConfigured()
|
||||
{
|
||||
var options = new SignalsOptions();
|
||||
options.Scm.AllowUnsignedWebhooks = true;
|
||||
|
||||
var triggerService = new TestScmTriggerService();
|
||||
var service = CreateService(options, triggerService);
|
||||
|
||||
var payload = Encoding.UTF8.GetBytes("{}");
|
||||
|
||||
var result = await service.ProcessAsync(
|
||||
ScmProvider.GitHub,
|
||||
eventType: "push",
|
||||
deliveryId: "delivery-2",
|
||||
signature: null,
|
||||
payload: payload,
|
||||
integrationId: "integration-1",
|
||||
tenantId: "tenant-1");
|
||||
|
||||
Assert.True(result.Success);
|
||||
Assert.Equal(202, result.StatusCode);
|
||||
Assert.Equal(1, triggerService.Calls);
|
||||
Assert.Equal("integration-1", result.Event?.IntegrationId);
|
||||
Assert.Equal("tenant-1", result.Event?.TenantId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessAsync_ValidSignature_AcceptsAndDispatches()
|
||||
{
|
||||
var options = new SignalsOptions();
|
||||
options.Scm.DefaultSecret = "test-secret";
|
||||
|
||||
var triggerService = new TestScmTriggerService();
|
||||
var service = CreateService(options, triggerService);
|
||||
|
||||
var payload = Encoding.UTF8.GetBytes("{}");
|
||||
var signature = ComputeGitHubSignature(payload, options.Scm.DefaultSecret);
|
||||
|
||||
var result = await service.ProcessAsync(
|
||||
ScmProvider.GitHub,
|
||||
eventType: "push",
|
||||
deliveryId: "delivery-3",
|
||||
signature: signature,
|
||||
payload: payload,
|
||||
integrationId: null,
|
||||
tenantId: null);
|
||||
|
||||
Assert.True(result.Success);
|
||||
Assert.Equal(202, result.StatusCode);
|
||||
Assert.Equal(1, triggerService.Calls);
|
||||
Assert.Equal("delivery-3", result.Event?.EventId);
|
||||
}
|
||||
|
||||
private static ScmWebhookService CreateService(SignalsOptions options, TestScmTriggerService triggerService)
|
||||
{
|
||||
return new ScmWebhookService(
|
||||
NullLogger<ScmWebhookService>.Instance,
|
||||
Options.Create(options),
|
||||
triggerService,
|
||||
new IWebhookSignatureValidator[] { new GitHubWebhookValidator() },
|
||||
new IScmEventMapper[] { new TestScmEventMapper(ScmProvider.GitHub) });
|
||||
}
|
||||
|
||||
private static string ComputeGitHubSignature(byte[] payload, string secret)
|
||||
{
|
||||
var secretBytes = Encoding.UTF8.GetBytes(secret);
|
||||
var hash = HMACSHA256.HashData(secretBytes, payload);
|
||||
return $"sha256={Convert.ToHexStringLower(hash)}";
|
||||
}
|
||||
|
||||
private sealed class TestScmEventMapper : IScmEventMapper
|
||||
{
|
||||
public TestScmEventMapper(ScmProvider provider)
|
||||
{
|
||||
Provider = provider;
|
||||
}
|
||||
|
||||
public ScmProvider Provider { get; }
|
||||
|
||||
public NormalizedScmEvent? Map(string eventType, string deliveryId, JsonElement payload)
|
||||
{
|
||||
return new NormalizedScmEvent
|
||||
{
|
||||
EventId = deliveryId,
|
||||
Provider = Provider,
|
||||
EventType = ScmEventType.Push,
|
||||
Timestamp = FixedTimestamp,
|
||||
Repository = new ScmRepository
|
||||
{
|
||||
FullName = "stellaops/signals"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class TestScmTriggerService : IScmTriggerService
|
||||
{
|
||||
public int Calls { get; private set; }
|
||||
|
||||
public Task<ScmTriggerResult> ProcessEventAsync(NormalizedScmEvent scmEvent, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Calls++;
|
||||
return Task.FromResult(new ScmTriggerResult
|
||||
{
|
||||
TriggersDispatched = true,
|
||||
ScanTriggersCount = 1,
|
||||
SbomTriggersCount = 0
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ public class UnknownsDecayServiceTests
|
||||
_timeProvider = new MockTimeProvider(new DateTimeOffset(2025, 12, 15, 12, 0, 0, TimeSpan.Zero));
|
||||
_unknownsRepo = new InMemoryUnknownsRepository();
|
||||
_deploymentRefs = new InMemoryDeploymentRefsRepository();
|
||||
_graphMetrics = new InMemoryGraphMetricsRepository();
|
||||
_graphMetrics = new InMemoryGraphMetricsRepository(TimeProvider.System);
|
||||
_scoringOptions = new UnknownsScoringOptions();
|
||||
_decayOptions = new UnknownsDecayOptions();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Signals.Models;
|
||||
using StellaOps.Signals.Persistence;
|
||||
using StellaOps.Signals.Services;
|
||||
@@ -17,7 +18,7 @@ public class UnknownsIngestionServiceTests
|
||||
public async Task IngestAsync_StoresNormalizedUnknowns()
|
||||
{
|
||||
var repo = new InMemoryUnknownsRepository();
|
||||
var service = new UnknownsIngestionService(repo, TimeProvider.System, NullLogger<UnknownsIngestionService>.Instance);
|
||||
var service = new UnknownsIngestionService(repo, TimeProvider.System, SystemGuidProvider.Instance, NullLogger<UnknownsIngestionService>.Instance);
|
||||
|
||||
var request = new UnknownsIngestRequest
|
||||
{
|
||||
@@ -49,7 +50,7 @@ public class UnknownsIngestionServiceTests
|
||||
public async Task IngestAsync_ThrowsWhenEmpty()
|
||||
{
|
||||
var repo = new InMemoryUnknownsRepository();
|
||||
var service = new UnknownsIngestionService(repo, TimeProvider.System, NullLogger<UnknownsIngestionService>.Instance);
|
||||
var service = new UnknownsIngestionService(repo, TimeProvider.System, SystemGuidProvider.Instance, NullLogger<UnknownsIngestionService>.Instance);
|
||||
|
||||
var request = new UnknownsIngestRequest
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace StellaOps.Signals.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Integration tests for the unknowns scoring system.
|
||||
/// Tests end-to-end flow: ingest → score → persist → query.
|
||||
/// Tests end-to-end flow: ingest -> score -> persist -> query.
|
||||
/// </summary>
|
||||
public sealed class UnknownsScoringIntegrationTests
|
||||
{
|
||||
@@ -32,7 +32,7 @@ public sealed class UnknownsScoringIntegrationTests
|
||||
_timeProvider = new MockTimeProvider(new DateTimeOffset(2025, 12, 15, 12, 0, 0, TimeSpan.Zero));
|
||||
_unknownsRepo = new FullInMemoryUnknownsRepository(_timeProvider);
|
||||
_deploymentRefs = new InMemoryDeploymentRefsRepository();
|
||||
_graphMetrics = new InMemoryGraphMetricsRepository();
|
||||
_graphMetrics = new InMemoryGraphMetricsRepository(TimeProvider.System);
|
||||
_defaultOptions = new UnknownsScoringOptions();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ public class UnknownsScoringServiceTests
|
||||
_timeProvider = new MockTimeProvider(new DateTimeOffset(2025, 12, 15, 12, 0, 0, TimeSpan.Zero));
|
||||
_unknownsRepo = new InMemoryUnknownsRepository();
|
||||
_deploymentRefs = new InMemoryDeploymentRefsRepository();
|
||||
_graphMetrics = new InMemoryGraphMetricsRepository();
|
||||
_graphMetrics = new InMemoryGraphMetricsRepository(TimeProvider.System);
|
||||
_defaultOptions = new UnknownsScoringOptions();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user