- Added Program.cs to set up the web application with Serilog for logging, health check endpoints, and a placeholder admission endpoint. - Configured Kestrel server to use TLS 1.3 and handle client certificates appropriately. - Created StellaOps.Zastava.Webhook.csproj with necessary dependencies including Serilog and Polly. - Documented tasks in TASKS.md for the Zastava Webhook project, outlining current work and exit criteria for each task.
114 lines
4.4 KiB
C#
114 lines
4.4 KiB
C#
using System.Text.Json;
|
|
using StellaOps.Scheduler.Models;
|
|
|
|
namespace StellaOps.Scheduler.Models.Tests;
|
|
|
|
public sealed class ScheduleSerializationTests
|
|
{
|
|
[Fact]
|
|
public void ScheduleSerialization_IsDeterministicRegardlessOfInputOrdering()
|
|
{
|
|
var selectionA = new Selector(
|
|
SelectorScope.ByNamespace,
|
|
tenantId: "tenant-alpha",
|
|
namespaces: new[] { "team-b", "team-a" },
|
|
repositories: new[] { "app/service-api", "app/service-web" },
|
|
digests: new[] { "sha256:bb", "sha256:aa" },
|
|
includeTags: new[] { "prod", "canary" },
|
|
labels: new[]
|
|
{
|
|
new LabelSelector("env", new[] { "prod", "staging" }),
|
|
new LabelSelector("app", new[] { "web", "api" }),
|
|
},
|
|
resolvesTags: true);
|
|
|
|
var selectionB = new Selector(
|
|
scope: SelectorScope.ByNamespace,
|
|
tenantId: "tenant-alpha",
|
|
namespaces: new[] { "team-a", "team-b" },
|
|
repositories: new[] { "app/service-web", "app/service-api" },
|
|
digests: new[] { "sha256:aa", "sha256:bb" },
|
|
includeTags: new[] { "canary", "prod" },
|
|
labels: new[]
|
|
{
|
|
new LabelSelector("app", new[] { "api", "web" }),
|
|
new LabelSelector("env", new[] { "staging", "prod" }),
|
|
},
|
|
resolvesTags: true);
|
|
|
|
var scheduleA = new Schedule(
|
|
id: "sch_001",
|
|
tenantId: "tenant-alpha",
|
|
name: "Nightly Prod",
|
|
enabled: true,
|
|
cronExpression: "0 2 * * *",
|
|
timezone: "UTC",
|
|
mode: ScheduleMode.AnalysisOnly,
|
|
selection: selectionA,
|
|
onlyIf: new ScheduleOnlyIf(lastReportOlderThanDays: 7, policyRevision: "policy@42"),
|
|
notify: new ScheduleNotify(onNewFindings: true, SeverityRank.High, includeKev: true),
|
|
limits: new ScheduleLimits(maxJobs: 1000, ratePerSecond: 25, parallelism: 4),
|
|
createdAt: DateTimeOffset.Parse("2025-10-18T23:00:00Z"),
|
|
createdBy: "svc_scheduler",
|
|
updatedAt: DateTimeOffset.Parse("2025-10-18T23:00:00Z"),
|
|
updatedBy: "svc_scheduler");
|
|
|
|
var scheduleB = new Schedule(
|
|
id: scheduleA.Id,
|
|
tenantId: scheduleA.TenantId,
|
|
name: scheduleA.Name,
|
|
enabled: scheduleA.Enabled,
|
|
cronExpression: scheduleA.CronExpression,
|
|
timezone: scheduleA.Timezone,
|
|
mode: scheduleA.Mode,
|
|
selection: selectionB,
|
|
onlyIf: scheduleA.OnlyIf,
|
|
notify: scheduleA.Notify,
|
|
limits: scheduleA.Limits,
|
|
createdAt: scheduleA.CreatedAt,
|
|
createdBy: scheduleA.CreatedBy,
|
|
updatedAt: scheduleA.UpdatedAt,
|
|
updatedBy: scheduleA.UpdatedBy,
|
|
subscribers: scheduleA.Subscribers);
|
|
|
|
var jsonA = CanonicalJsonSerializer.Serialize(scheduleA);
|
|
var jsonB = CanonicalJsonSerializer.Serialize(scheduleB);
|
|
|
|
Assert.Equal(jsonA, jsonB);
|
|
|
|
using var doc = JsonDocument.Parse(jsonA);
|
|
var root = doc.RootElement;
|
|
Assert.Equal(SchedulerSchemaVersions.Schedule, root.GetProperty("schemaVersion").GetString());
|
|
Assert.Equal("analysis-only", root.GetProperty("mode").GetString());
|
|
Assert.Equal("tenant-alpha", root.GetProperty("tenantId").GetString());
|
|
|
|
var namespaces = root.GetProperty("selection").GetProperty("namespaces").EnumerateArray().Select(e => e.GetString()).ToArray();
|
|
Assert.Equal(new[] { "team-a", "team-b" }, namespaces);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("")]
|
|
[InlineData("not-a-timezone")]
|
|
public void Schedule_ThrowsWhenTimezoneInvalid(string timezone)
|
|
{
|
|
var selection = new Selector(SelectorScope.AllImages, tenantId: "tenant-alpha");
|
|
|
|
Assert.ThrowsAny<Exception>(() => new Schedule(
|
|
id: "sch_002",
|
|
tenantId: "tenant-alpha",
|
|
name: "Invalid timezone",
|
|
enabled: true,
|
|
cronExpression: "0 3 * * *",
|
|
timezone: timezone,
|
|
mode: ScheduleMode.AnalysisOnly,
|
|
selection: selection,
|
|
onlyIf: null,
|
|
notify: null,
|
|
limits: null,
|
|
createdAt: DateTimeOffset.UtcNow,
|
|
createdBy: "svc",
|
|
updatedAt: DateTimeOffset.UtcNow,
|
|
updatedBy: "svc"));
|
|
}
|
|
}
|