work
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using StellaOps.Notifier.Tests.Support;
|
||||
using StellaOps.Notifier.WebService.Contracts;
|
||||
using StellaOps.Notify.Queue;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Notifier.Tests;
|
||||
|
||||
public sealed class AttestationEventEndpointTests : IClassFixture<NotifierApplicationFactory>
|
||||
{
|
||||
private readonly NotifierApplicationFactory _factory;
|
||||
|
||||
public AttestationEventEndpointTests(NotifierApplicationFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Attestation_event_is_published_to_queue()
|
||||
{
|
||||
var recordingQueue = new RecordingNotifyEventQueue();
|
||||
|
||||
var client = _factory.WithWebHostBuilder(builder =>
|
||||
{
|
||||
builder.ConfigureServices(services =>
|
||||
{
|
||||
services.RemoveAll<INotifyEventQueue>();
|
||||
services.AddSingleton<INotifyEventQueue>(recordingQueue);
|
||||
});
|
||||
}).CreateClient();
|
||||
|
||||
var request = new AttestationEventRequest
|
||||
{
|
||||
EventId = Guid.NewGuid(),
|
||||
Kind = "authority.keys.rotated",
|
||||
Actor = "authority",
|
||||
Timestamp = DateTimeOffset.Parse("2025-11-24T00:00:00Z"),
|
||||
Payload = new System.Text.Json.Nodes.JsonObject
|
||||
{
|
||||
["rotation"] = new System.Text.Json.Nodes.JsonObject
|
||||
{
|
||||
["batchId"] = "batch-42",
|
||||
["executedAt"] = "2025-11-24T00:00:00Z"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var message = new HttpRequestMessage(HttpMethod.Post, "/api/v1/notify/attestation-events")
|
||||
{
|
||||
Content = JsonContent.Create(request)
|
||||
};
|
||||
message.Headers.Add("X-StellaOps-Tenant", "tenant-sample");
|
||||
|
||||
var response = await client.SendAsync(message, TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.Equal(HttpStatusCode.Accepted, response.StatusCode);
|
||||
Assert.Single(recordingQueue.Published);
|
||||
|
||||
var published = recordingQueue.Published.Single();
|
||||
Assert.Equal("authority.keys.rotated", published.Event.Kind);
|
||||
Assert.Equal("tenant-sample", published.Event.Tenant);
|
||||
Assert.Equal("notify:events", published.Stream);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using StellaOps.Notifier.Tests.Support;
|
||||
using StellaOps.Notifier.WebService.Setup;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Notifier.Tests;
|
||||
|
||||
public sealed class AttestationTemplateSeederTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task SeedTemplates_and_routing_load_from_offline_bundle()
|
||||
{
|
||||
var templateRepo = new InMemoryTemplateRepository();
|
||||
var channelRepo = new InMemoryChannelRepository();
|
||||
var ruleRepo = new InMemoryRuleRepository();
|
||||
var logger = NullLogger<AttestationTemplateSeeder>.Instance;
|
||||
|
||||
var contentRoot = LocateRepoRoot();
|
||||
|
||||
var seededTemplates = await AttestationTemplateSeeder.SeedTemplatesAsync(
|
||||
templateRepo,
|
||||
contentRoot,
|
||||
logger,
|
||||
TestContext.Current.CancellationToken);
|
||||
|
||||
var seededRouting = await AttestationTemplateSeeder.SeedRoutingAsync(
|
||||
channelRepo,
|
||||
ruleRepo,
|
||||
contentRoot,
|
||||
logger,
|
||||
TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.True(seededTemplates >= 6, "Expected attestation templates to be seeded.");
|
||||
Assert.True(seededRouting >= 3, "Expected attestation routing seed to create channels and rules.");
|
||||
|
||||
var templates = await templateRepo.ListAsync("bootstrap", TestContext.Current.CancellationToken);
|
||||
Assert.Contains(templates, t => t.Key == "tmpl-attest-key-rotation");
|
||||
Assert.Contains(templates, t => t.Key == "tmpl-attest-transparency-anomaly");
|
||||
|
||||
var rules = await ruleRepo.ListAsync("bootstrap", TestContext.Current.CancellationToken);
|
||||
Assert.Contains(rules, r => r.Match.EventKinds.Contains("authority.keys.rotated"));
|
||||
Assert.Contains(rules, r => r.Match.EventKinds.Contains("attestor.transparency.anomaly"));
|
||||
}
|
||||
|
||||
private static string LocateRepoRoot()
|
||||
{
|
||||
var directory = AppContext.BaseDirectory;
|
||||
while (directory != null)
|
||||
{
|
||||
if (File.Exists(Path.Combine(directory, "StellaOps.sln")) ||
|
||||
File.Exists(Path.Combine(directory, "StellaOps.Notifier.sln")))
|
||||
{
|
||||
return directory;
|
||||
}
|
||||
|
||||
directory = Directory.GetParent(directory)?.FullName;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Unable to locate repository root.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using StellaOps.Notifier.Tests.Support;
|
||||
using StellaOps.Notifier.WebService.Contracts;
|
||||
using StellaOps.Notify.Queue;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Notifier.Tests;
|
||||
|
||||
public sealed class RiskEventEndpointTests : IClassFixture<NotifierApplicationFactory>
|
||||
{
|
||||
private readonly NotifierApplicationFactory _factory;
|
||||
|
||||
public RiskEventEndpointTests(NotifierApplicationFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Risk_event_is_published_to_queue()
|
||||
{
|
||||
var recordingQueue = new RecordingNotifyEventQueue();
|
||||
|
||||
var client = _factory.WithWebHostBuilder(builder =>
|
||||
{
|
||||
builder.ConfigureServices(services =>
|
||||
{
|
||||
services.RemoveAll<INotifyEventQueue>();
|
||||
services.AddSingleton<INotifyEventQueue>(recordingQueue);
|
||||
});
|
||||
}).CreateClient();
|
||||
|
||||
var request = new RiskEventRequest
|
||||
{
|
||||
EventId = Guid.NewGuid(),
|
||||
Kind = "risk.profile.severity.changed",
|
||||
Actor = "risk-engine",
|
||||
Timestamp = DateTimeOffset.Parse("2025-11-24T00:00:00Z"),
|
||||
Payload = new System.Text.Json.Nodes.JsonObject
|
||||
{
|
||||
["profile"] = new System.Text.Json.Nodes.JsonObject
|
||||
{
|
||||
["id"] = "stellaops://risk/profile/example@2025.11",
|
||||
["version"] = "2025.11"
|
||||
},
|
||||
["previous"] = new System.Text.Json.Nodes.JsonObject { ["severity"] = "medium" },
|
||||
["current"] = new System.Text.Json.Nodes.JsonObject { ["severity"] = "high" }
|
||||
}
|
||||
};
|
||||
|
||||
var message = new HttpRequestMessage(HttpMethod.Post, "/api/v1/notify/risk-events")
|
||||
{
|
||||
Content = JsonContent.Create(request)
|
||||
};
|
||||
message.Headers.Add("X-StellaOps-Tenant", "tenant-sample");
|
||||
|
||||
var response = await client.SendAsync(message, TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.Equal(HttpStatusCode.Accepted, response.StatusCode);
|
||||
Assert.Single(recordingQueue.Published);
|
||||
|
||||
var published = recordingQueue.Published.Single();
|
||||
Assert.Equal("risk.profile.severity.changed", published.Event.Kind);
|
||||
Assert.Equal("tenant-sample", published.Event.Tenant);
|
||||
Assert.Equal("notify:events", published.Stream);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using StellaOps.Notifier.Tests.Support;
|
||||
using StellaOps.Notifier.WebService.Setup;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Notifier.Tests;
|
||||
|
||||
public sealed class RiskTemplateSeederTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task SeedTemplates_and_routing_load_from_offline_bundle()
|
||||
{
|
||||
var templateRepo = new InMemoryTemplateRepository();
|
||||
var channelRepo = new InMemoryChannelRepository();
|
||||
var ruleRepo = new InMemoryRuleRepository();
|
||||
var logger = NullLogger<RiskTemplateSeeder>.Instance;
|
||||
|
||||
var contentRoot = LocateRepoRoot();
|
||||
|
||||
var seededTemplates = await RiskTemplateSeeder.SeedTemplatesAsync(
|
||||
templateRepo,
|
||||
contentRoot,
|
||||
logger,
|
||||
TestContext.Current.CancellationToken);
|
||||
|
||||
var seededRouting = await RiskTemplateSeeder.SeedRoutingAsync(
|
||||
channelRepo,
|
||||
ruleRepo,
|
||||
contentRoot,
|
||||
logger,
|
||||
TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.True(seededTemplates >= 4, "Expected risk templates to be seeded.");
|
||||
Assert.True(seededRouting >= 4, "Expected risk routing seed to create channels and rules.");
|
||||
|
||||
var templates = await templateRepo.ListAsync("bootstrap", TestContext.Current.CancellationToken);
|
||||
Assert.Contains(templates, t => t.Key == "tmpl-risk-severity-change");
|
||||
Assert.Contains(templates, t => t.Key == "tmpl-risk-profile-state");
|
||||
|
||||
var rules = await ruleRepo.ListAsync("bootstrap", TestContext.Current.CancellationToken);
|
||||
Assert.Contains(rules, r => r.Match.EventKinds.Contains("risk.profile.severity.changed"));
|
||||
Assert.Contains(rules, r => r.Match.EventKinds.Contains("risk.profile.published"));
|
||||
}
|
||||
|
||||
private static string LocateRepoRoot()
|
||||
{
|
||||
var directory = AppContext.BaseDirectory;
|
||||
while (directory != null)
|
||||
{
|
||||
if (File.Exists(Path.Combine(directory, "StellaOps.sln")) ||
|
||||
File.Exists(Path.Combine(directory, "StellaOps.Notifier.sln")))
|
||||
{
|
||||
return directory;
|
||||
}
|
||||
|
||||
directory = Directory.GetParent(directory)?.FullName;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Unable to locate repository root.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using StellaOps.Notify.Queue;
|
||||
|
||||
namespace StellaOps.Notifier.Tests.Support;
|
||||
|
||||
internal sealed class RecordingNotifyEventQueue : INotifyEventQueue
|
||||
{
|
||||
private readonly List<NotifyQueueEventMessage> _messages = new();
|
||||
|
||||
public IReadOnlyList<NotifyQueueEventMessage> Published => _messages;
|
||||
|
||||
public ValueTask<IReadOnlyList<INotifyQueueLease<NotifyQueueEventMessage>>> LeaseAsync(NotifyQueueLeaseRequest request, CancellationToken cancellationToken = default)
|
||||
=> ValueTask.FromResult<IReadOnlyList<INotifyQueueLease<NotifyQueueEventMessage>>>(Array.Empty<INotifyQueueLease<NotifyQueueEventMessage>>());
|
||||
|
||||
public ValueTask PublishAsync(NotifyQueueEventMessage message, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_messages.Add(message);
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
|
||||
}
|
||||
Reference in New Issue
Block a user