Some checks are pending
CI / Build og unit tests (push) Waiting to run
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
Hub: nightly retention of resolved findings, the submission log and silent instances; delete-customer erases a customer's data and blocks its licenses; persistent data protection keys and trusted reverse proxies. Docker image and compose file with PostgreSQL. The agent refuses to send the license to a non-https hub. Documentation for the hub, for data processing (GDPR basis for a data processing agreement) and the README roadmap for phase 3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
81 lines
4.1 KiB
C#
81 lines
4.1 KiB
C#
using System.Net;
|
|
using System.Net.Http.Headers;
|
|
using System.Net.Http.Json;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using SQLInsight.Hub.Data;
|
|
using SQLInsight.Hub.Maintenance;
|
|
|
|
namespace SQLInsight.IntegrationTests;
|
|
|
|
[Trait("Category", "Integration")]
|
|
public class HubMaintenanceTests(HubFixture hub) : IClassFixture<HubFixture>
|
|
{
|
|
private async Task<HttpStatusCode> SubmitAsync(string license, params (string Rule, string Subject, string Severity)[] findings)
|
|
{
|
|
using var request = new HttpRequestMessage(HttpMethod.Post, "/api/v1/findings")
|
|
{
|
|
Content = JsonContent.Create(HubApiTests.Submission("sql01", DateTimeOffset.UtcNow, findings)),
|
|
};
|
|
request.Headers.Authorization = new AuthenticationHeaderValue("SQLInsight-License", license);
|
|
return (await hub.Factory.CreateClient().SendAsync(request)).StatusCode;
|
|
}
|
|
|
|
private async Task<T> WithDbAsync<T>(Func<HubDbContext, HubMaintenance, Task<T>> action)
|
|
{
|
|
await using var scope = hub.Factory.Services.CreateAsyncScope();
|
|
return await action(scope.ServiceProvider.GetRequiredService<HubDbContext>(), scope.ServiceProvider.GetRequiredService<HubMaintenance>());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Deleting_a_customer_removes_its_data_and_blocks_its_licenses()
|
|
{
|
|
var name = $"Slet {Guid.NewGuid():N}"[..20];
|
|
var license = hub.License(name);
|
|
Assert.Equal(HttpStatusCode.OK, await SubmitAsync(license, ("SQLI-DB-007", "database: Sales", "Critical")));
|
|
|
|
var deleted = await WithDbAsync((_, m) => m.DeleteCustomerAsync(name));
|
|
|
|
Assert.True(deleted);
|
|
var (customers, findings, instances, revoked) = await WithDbAsync(async (db, _) => (
|
|
await db.Customers.CountAsync(c => c.Name == name),
|
|
await db.Findings.CountAsync(f => f.Instance!.Customer!.Name == name),
|
|
await db.Instances.CountAsync(i => i.Customer!.Name == name),
|
|
await db.Licenses.Where(l => l.Customer!.Name.StartsWith("Slettet kunde #")).CountAsync(l => l.RevokedAt != null)));
|
|
Assert.Equal(0, customers);
|
|
Assert.Equal(0, findings);
|
|
Assert.Equal(0, instances);
|
|
Assert.True(revoked >= 1);
|
|
Assert.Equal(HttpStatusCode.Forbidden, await SubmitAsync(license, ("SQLI-DB-007", "database: Sales", "Critical")));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Retention_removes_old_resolved_findings_old_submissions_and_silent_instances()
|
|
{
|
|
var active = $"Aktiv {Guid.NewGuid():N}"[..20];
|
|
var silent = $"Tavs {Guid.NewGuid():N}"[..20];
|
|
await SubmitAsync(hub.License(active), ("SQLI-CFG-001", "", "High"), ("SQLI-DB-007", "database: A", "Critical"));
|
|
await SubmitAsync(hub.License(silent), ("SQLI-CFG-001", "", "High"));
|
|
await WithDbAsync(async (db, _) =>
|
|
{
|
|
var old = DateTime.UtcNow.AddDays(-400);
|
|
await db.Findings.Where(f => f.Instance!.Customer!.Name == active && f.RuleId == "SQLI-DB-007")
|
|
.ExecuteUpdateAsync(f => f.SetProperty(x => x.ResolvedAt, old));
|
|
await db.Submissions.Where(s => db.Instances.Any(i => i.Id == s.InstanceId && i.Customer!.Name == active))
|
|
.ExecuteUpdateAsync(s => s.SetProperty(x => x.ReceivedAt, old));
|
|
await db.Instances.Where(i => i.Customer!.Name == silent).ExecuteUpdateAsync(i => i.SetProperty(x => x.LastSubmissionAt, old));
|
|
return 0;
|
|
});
|
|
|
|
var result = await WithDbAsync((_, m) => m.ApplyRetentionAsync());
|
|
|
|
Assert.True(result.Findings >= 1 && result.Submissions >= 1 && result.Instances >= 1);
|
|
var (activeFindings, activeSubmissions, silentInstances) = await WithDbAsync(async (db, _) => (
|
|
await db.Findings.Where(f => f.Instance!.Customer!.Name == active).Select(f => f.RuleId).ToListAsync(),
|
|
await db.Submissions.CountAsync(s => db.Instances.Any(i => i.Id == s.InstanceId && i.Customer!.Name == active)),
|
|
await db.Instances.CountAsync(i => i.Customer!.Name == silent)));
|
|
Assert.Equal(["SQLI-CFG-001"], activeFindings);
|
|
Assert.Equal(0, activeSubmissions);
|
|
Assert.Equal(0, silentInstances);
|
|
}
|
|
}
|