up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-12-13 00:20:26 +02:00
parent e1f1bef4c1
commit 564df71bfb
2376 changed files with 334389 additions and 328032 deletions

View File

@@ -1,113 +1,113 @@
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"));
}
}
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"));
}
}