Fix build and code structure improvements. New but essential UI functionality. CI improvements. Documentation improvements. AI module improvements.

This commit is contained in:
StellaOps Bot
2025-12-26 21:54:17 +02:00
parent 335ff7da16
commit c2b9cd8d1f
3717 changed files with 264714 additions and 48202 deletions

View File

@@ -50,7 +50,7 @@ public sealed class DigestGeneratorTests
var query = DigestQuery.LastHours(24, _timeProvider.GetUtcNow());
// Act
var result = await _generator.GenerateAsync("tenant-1", query);
var result = await _generator.GenerateAsync("tenant-1", query, CancellationToken.None);
// Assert
Assert.NotNull(result);
@@ -65,15 +65,14 @@ public sealed class DigestGeneratorTests
public async Task GenerateAsync_WithIncidents_ReturnsSummary()
{
// Arrange
var incident = await _incidentManager.GetOrCreateIncidentAsync(
"tenant-1", "vuln:critical:pkg-foo", "vulnerability.detected", "Critical vulnerability in pkg-foo");
await _incidentManager.RecordEventAsync("tenant-1", incident.IncidentId, "evt-1");
await _incidentManager.RecordEventAsync("tenant-1", incident.IncidentId, "evt-2");
var incident = await _incidentManager.GetOrCreateIncidentAsync("tenant-1", "vuln:critical:pkg-foo", "vulnerability.detected", "Critical vulnerability in pkg-foo", CancellationToken.None);
await _incidentManager.RecordEventAsync("tenant-1", incident.IncidentId, "evt-1", CancellationToken.None);
await _incidentManager.RecordEventAsync("tenant-1", incident.IncidentId, "evt-2", CancellationToken.None);
var query = DigestQuery.LastHours(24, _timeProvider.GetUtcNow());
// Act
var result = await _generator.GenerateAsync("tenant-1", query);
var result = await _generator.GenerateAsync("tenant-1", query, CancellationToken.None);
// Assert
Assert.Single(result.Incidents);
@@ -87,22 +86,19 @@ public sealed class DigestGeneratorTests
public async Task GenerateAsync_MultipleIncidents_GroupsByEventKind()
{
// Arrange
var inc1 = await _incidentManager.GetOrCreateIncidentAsync(
"tenant-1", "key1", "vulnerability.detected", "Vuln 1");
await _incidentManager.RecordEventAsync("tenant-1", inc1.IncidentId, "evt-1");
var inc1 = await _incidentManager.GetOrCreateIncidentAsync("tenant-1", "key1", "vulnerability.detected", "Vuln 1", CancellationToken.None);
await _incidentManager.RecordEventAsync("tenant-1", inc1.IncidentId, "evt-1", CancellationToken.None);
var inc2 = await _incidentManager.GetOrCreateIncidentAsync(
"tenant-1", "key2", "vulnerability.detected", "Vuln 2");
await _incidentManager.RecordEventAsync("tenant-1", inc2.IncidentId, "evt-2");
var inc2 = await _incidentManager.GetOrCreateIncidentAsync("tenant-1", "key2", "vulnerability.detected", "Vuln 2", CancellationToken.None);
await _incidentManager.RecordEventAsync("tenant-1", inc2.IncidentId, "evt-2", CancellationToken.None);
var inc3 = await _incidentManager.GetOrCreateIncidentAsync(
"tenant-1", "key3", "pack.approval.required", "Approval needed");
await _incidentManager.RecordEventAsync("tenant-1", inc3.IncidentId, "evt-3");
var inc3 = await _incidentManager.GetOrCreateIncidentAsync("tenant-1", "key3", "pack.approval.required", "Approval needed", CancellationToken.None);
await _incidentManager.RecordEventAsync("tenant-1", inc3.IncidentId, "evt-3", CancellationToken.None);
var query = DigestQuery.LastHours(24, _timeProvider.GetUtcNow());
// Act
var result = await _generator.GenerateAsync("tenant-1", query);
var result = await _generator.GenerateAsync("tenant-1", query, CancellationToken.None);
// Assert
Assert.Equal(3, result.Incidents.Count);
@@ -117,14 +113,13 @@ public sealed class DigestGeneratorTests
public async Task GenerateAsync_RendersContent()
{
// Arrange
var incident = await _incidentManager.GetOrCreateIncidentAsync(
"tenant-1", "key", "vulnerability.detected", "Critical issue");
await _incidentManager.RecordEventAsync("tenant-1", incident.IncidentId, "evt-1");
var incident = await _incidentManager.GetOrCreateIncidentAsync("tenant-1", "key", "vulnerability.detected", "Critical issue", CancellationToken.None);
await _incidentManager.RecordEventAsync("tenant-1", incident.IncidentId, "evt-1", CancellationToken.None);
var query = DigestQuery.LastHours(24, _timeProvider.GetUtcNow());
// Act
var result = await _generator.GenerateAsync("tenant-1", query);
var result = await _generator.GenerateAsync("tenant-1", query, CancellationToken.None);
// Assert
Assert.NotNull(result.Content);
@@ -145,9 +140,8 @@ public sealed class DigestGeneratorTests
// Arrange
for (var i = 0; i < 10; i++)
{
var inc = await _incidentManager.GetOrCreateIncidentAsync(
"tenant-1", $"key-{i}", "test.event", $"Test incident {i}");
await _incidentManager.RecordEventAsync("tenant-1", inc.IncidentId, $"evt-{i}");
var inc = await _incidentManager.GetOrCreateIncidentAsync("tenant-1", $"key-{i}", "test.event", $"Test incident {i}", CancellationToken.None);
await _incidentManager.RecordEventAsync("tenant-1", inc.IncidentId, $"evt-{i}", CancellationToken.None);
}
var query = new DigestQuery
@@ -158,7 +152,7 @@ public sealed class DigestGeneratorTests
};
// Act
var result = await _generator.GenerateAsync("tenant-1", query);
var result = await _generator.GenerateAsync("tenant-1", query, CancellationToken.None);
// Assert
Assert.Equal(5, result.Incidents.Count);
@@ -170,14 +164,12 @@ public sealed class DigestGeneratorTests
public async Task GenerateAsync_FiltersResolvedIncidents()
{
// Arrange
var openInc = await _incidentManager.GetOrCreateIncidentAsync(
"tenant-1", "key-open", "test.event", "Open incident");
await _incidentManager.RecordEventAsync("tenant-1", openInc.IncidentId, "evt-1");
var openInc = await _incidentManager.GetOrCreateIncidentAsync("tenant-1", "key-open", "test.event", "Open incident", CancellationToken.None);
await _incidentManager.RecordEventAsync("tenant-1", openInc.IncidentId, "evt-1", CancellationToken.None);
var resolvedInc = await _incidentManager.GetOrCreateIncidentAsync(
"tenant-1", "key-resolved", "test.event", "Resolved incident");
await _incidentManager.RecordEventAsync("tenant-1", resolvedInc.IncidentId, "evt-2");
await _incidentManager.ResolveAsync("tenant-1", resolvedInc.IncidentId, "system", "Auto-resolved");
var resolvedInc = await _incidentManager.GetOrCreateIncidentAsync("tenant-1", "key-resolved", "test.event", "Resolved incident", CancellationToken.None);
await _incidentManager.RecordEventAsync("tenant-1", resolvedInc.IncidentId, "evt-2", CancellationToken.None);
await _incidentManager.ResolveAsync("tenant-1", resolvedInc.IncidentId, "system", "Auto-resolved", CancellationToken.None);
var queryExcludeResolved = new DigestQuery
{
@@ -194,8 +186,8 @@ public sealed class DigestGeneratorTests
};
// Act
var resultExclude = await _generator.GenerateAsync("tenant-1", queryExcludeResolved);
var resultInclude = await _generator.GenerateAsync("tenant-1", queryIncludeResolved);
var resultExclude = await _generator.GenerateAsync("tenant-1", queryExcludeResolved, CancellationToken.None);
var resultInclude = await _generator.GenerateAsync("tenant-1", queryIncludeResolved, CancellationToken.None);
// Assert
Assert.Single(resultExclude.Incidents);
@@ -208,13 +200,11 @@ public sealed class DigestGeneratorTests
public async Task GenerateAsync_FiltersEventKinds()
{
// Arrange
var vulnInc = await _incidentManager.GetOrCreateIncidentAsync(
"tenant-1", "key-vuln", "vulnerability.detected", "Vulnerability");
await _incidentManager.RecordEventAsync("tenant-1", vulnInc.IncidentId, "evt-1");
var vulnInc = await _incidentManager.GetOrCreateIncidentAsync("tenant-1", "key-vuln", "vulnerability.detected", "Vulnerability", CancellationToken.None);
await _incidentManager.RecordEventAsync("tenant-1", vulnInc.IncidentId, "evt-1", CancellationToken.None);
var approvalInc = await _incidentManager.GetOrCreateIncidentAsync(
"tenant-1", "key-approval", "pack.approval.required", "Approval");
await _incidentManager.RecordEventAsync("tenant-1", approvalInc.IncidentId, "evt-2");
var approvalInc = await _incidentManager.GetOrCreateIncidentAsync("tenant-1", "key-approval", "pack.approval.required", "Approval", CancellationToken.None);
await _incidentManager.RecordEventAsync("tenant-1", approvalInc.IncidentId, "evt-2", CancellationToken.None);
var query = new DigestQuery
{
@@ -224,7 +214,7 @@ public sealed class DigestGeneratorTests
};
// Act
var result = await _generator.GenerateAsync("tenant-1", query);
var result = await _generator.GenerateAsync("tenant-1", query, CancellationToken.None);
// Assert
Assert.Single(result.Incidents);
@@ -235,14 +225,13 @@ public sealed class DigestGeneratorTests
public async Task PreviewAsync_SetsIsPreviewFlag()
{
// Arrange
var incident = await _incidentManager.GetOrCreateIncidentAsync(
"tenant-1", "key", "test.event", "Test");
await _incidentManager.RecordEventAsync("tenant-1", incident.IncidentId, "evt-1");
var incident = await _incidentManager.GetOrCreateIncidentAsync("tenant-1", "key", "test.event", "Test", CancellationToken.None);
await _incidentManager.RecordEventAsync("tenant-1", incident.IncidentId, "evt-1", CancellationToken.None);
var query = DigestQuery.LastHours(24, _timeProvider.GetUtcNow());
// Act
var result = await _generator.PreviewAsync("tenant-1", query);
var result = await _generator.PreviewAsync("tenant-1", query, CancellationToken.None);
// Assert
Assert.True(result.IsPreview);

View File

@@ -24,7 +24,7 @@ public class InMemoryDigestSchedulerTests
var schedule = CreateTestSchedule("schedule-1");
// Act
var result = await _scheduler.UpsertScheduleAsync(schedule);
var result = await _scheduler.UpsertScheduleAsync(schedule, CancellationToken.None);
// Assert
Assert.NotNull(result);
@@ -37,12 +37,12 @@ public class InMemoryDigestSchedulerTests
{
// Arrange
var schedule = CreateTestSchedule("schedule-1");
await _scheduler.UpsertScheduleAsync(schedule);
await _scheduler.UpsertScheduleAsync(schedule, CancellationToken.None);
var updated = schedule with { Name = "Updated Name" };
// Act
var result = await _scheduler.UpsertScheduleAsync(updated);
var result = await _scheduler.UpsertScheduleAsync(updated, CancellationToken.None);
// Assert
Assert.Equal("Updated Name", result.Name);
@@ -53,10 +53,10 @@ public class InMemoryDigestSchedulerTests
{
// Arrange
var schedule = CreateTestSchedule("schedule-1");
await _scheduler.UpsertScheduleAsync(schedule);
await _scheduler.UpsertScheduleAsync(schedule, CancellationToken.None);
// Act
var result = await _scheduler.GetScheduleAsync("tenant1", "schedule-1");
var result = await _scheduler.GetScheduleAsync("tenant1", "schedule-1", CancellationToken.None);
// Assert
Assert.NotNull(result);
@@ -67,7 +67,7 @@ public class InMemoryDigestSchedulerTests
public async Task GetScheduleAsync_ReturnsNullForUnknown()
{
// Act
var result = await _scheduler.GetScheduleAsync("tenant1", "unknown");
var result = await _scheduler.GetScheduleAsync("tenant1", "unknown", CancellationToken.None);
// Assert
Assert.Null(result);
@@ -77,12 +77,12 @@ public class InMemoryDigestSchedulerTests
public async Task GetSchedulesAsync_ReturnsTenantSchedules()
{
// Arrange
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-1", "tenant1"));
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-2", "tenant1"));
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-3", "tenant2"));
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-1", "tenant1"), CancellationToken.None);
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-2", "tenant1"), CancellationToken.None);
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-3", "tenant2"), CancellationToken.None);
// Act
var result = await _scheduler.GetSchedulesAsync("tenant1");
var result = await _scheduler.GetSchedulesAsync("tenant1", CancellationToken.None);
// Assert
Assert.Equal(2, result.Count);
@@ -93,14 +93,14 @@ public class InMemoryDigestSchedulerTests
public async Task DeleteScheduleAsync_RemovesSchedule()
{
// Arrange
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-1"));
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-1"), CancellationToken.None);
// Act
var deleted = await _scheduler.DeleteScheduleAsync("tenant1", "schedule-1");
var deleted = await _scheduler.DeleteScheduleAsync("tenant1", "schedule-1", CancellationToken.None);
// Assert
Assert.True(deleted);
var result = await _scheduler.GetScheduleAsync("tenant1", "schedule-1");
var result = await _scheduler.GetScheduleAsync("tenant1", "schedule-1", CancellationToken.None);
Assert.Null(result);
}
@@ -108,7 +108,7 @@ public class InMemoryDigestSchedulerTests
public async Task DeleteScheduleAsync_ReturnsFalseForUnknown()
{
// Act
var deleted = await _scheduler.DeleteScheduleAsync("tenant1", "unknown");
var deleted = await _scheduler.DeleteScheduleAsync("tenant1", "unknown", CancellationToken.None);
// Assert
Assert.False(deleted);
@@ -122,13 +122,13 @@ public class InMemoryDigestSchedulerTests
{
CronExpression = "0 * * * * *" // Every minute
};
await _scheduler.UpsertScheduleAsync(schedule);
await _scheduler.UpsertScheduleAsync(schedule, CancellationToken.None);
// Advance time past next run
_timeProvider.Advance(TimeSpan.FromMinutes(2));
// Act
var dueSchedules = await _scheduler.GetDueSchedulesAsync(_timeProvider.GetUtcNow());
var dueSchedules = await _scheduler.GetDueSchedulesAsync(_timeProvider.GetUtcNow(), CancellationToken.None);
// Assert
Assert.Single(dueSchedules);
@@ -144,12 +144,12 @@ public class InMemoryDigestSchedulerTests
Enabled = false,
CronExpression = "0 * * * * *"
};
await _scheduler.UpsertScheduleAsync(schedule);
await _scheduler.UpsertScheduleAsync(schedule, CancellationToken.None);
_timeProvider.Advance(TimeSpan.FromMinutes(2));
// Act
var dueSchedules = await _scheduler.GetDueSchedulesAsync(_timeProvider.GetUtcNow());
var dueSchedules = await _scheduler.GetDueSchedulesAsync(_timeProvider.GetUtcNow(), CancellationToken.None);
// Assert
Assert.Empty(dueSchedules);
@@ -163,15 +163,15 @@ public class InMemoryDigestSchedulerTests
{
CronExpression = "0 0 * * * *" // Every hour
};
await _scheduler.UpsertScheduleAsync(schedule);
await _scheduler.UpsertScheduleAsync(schedule, CancellationToken.None);
var runTime = _timeProvider.GetUtcNow();
// Act
await _scheduler.UpdateLastRunAsync("tenant1", "schedule-1", runTime);
await _scheduler.UpdateLastRunAsync("tenant1", "schedule-1", runTime, CancellationToken.None);
// Assert
var updated = await _scheduler.GetScheduleAsync("tenant1", "schedule-1");
var updated = await _scheduler.GetScheduleAsync("tenant1", "schedule-1", CancellationToken.None);
Assert.NotNull(updated);
Assert.Equal(runTime, updated.LastRunAt);
Assert.NotNull(updated.NextRunAt);
@@ -189,7 +189,7 @@ public class InMemoryDigestSchedulerTests
};
// Act
var result = await _scheduler.UpsertScheduleAsync(schedule);
var result = await _scheduler.UpsertScheduleAsync(schedule, CancellationToken.None);
// Assert
Assert.NotNull(result.NextRunAt);
@@ -205,7 +205,7 @@ public class InMemoryDigestSchedulerTests
};
// Act
var result = await _scheduler.UpsertScheduleAsync(schedule);
var result = await _scheduler.UpsertScheduleAsync(schedule, CancellationToken.None);
// Assert
Assert.Null(result.NextRunAt);
@@ -215,12 +215,12 @@ public class InMemoryDigestSchedulerTests
public async Task GetSchedulesAsync_OrdersByName()
{
// Arrange
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-c") with { Name = "Charlie" });
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-a") with { Name = "Alpha" });
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-b") with { Name = "Bravo" });
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-c") with { Name = "Charlie" }, CancellationToken.None);
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-a") with { Name = "Alpha" }, CancellationToken.None);
await _scheduler.UpsertScheduleAsync(CreateTestSchedule("schedule-b") with { Name = "Bravo" }, CancellationToken.None);
// Act
var result = await _scheduler.GetSchedulesAsync("tenant1");
var result = await _scheduler.GetSchedulesAsync("tenant1", CancellationToken.None);
// Assert
Assert.Equal(3, result.Count);