Restore live platform compatibility contracts

This commit is contained in:
master
2026-03-10 01:37:24 +02:00
parent 6b7168ca3c
commit afb9711e61
15 changed files with 1790 additions and 27 deletions

View File

@@ -0,0 +1,113 @@
using System.Net.Http.Json;
using System.Text.Json;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Platform.WebService.Tests;
public sealed class CompatibilityEndpointsTests : IClassFixture<PlatformWebApplicationFactory>
{
private readonly PlatformWebApplicationFactory _factory;
public CompatibilityEndpointsTests(PlatformWebApplicationFactory factory)
{
_factory = factory;
}
[Trait("Category", TestCategories.Integration)]
[Fact]
public async Task ConsoleStatus_ReturnsDeterministicPayload()
{
using var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "demo-prod");
var response = await client.GetAsync("/api/console/status", TestContext.Current.CancellationToken);
response.EnsureSuccessStatusCode();
var payload = await response.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
Assert.True(payload.TryGetProperty("healthy", out var healthy));
Assert.True(healthy.GetBoolean());
Assert.True(payload.GetProperty("backlog").GetInt32() > 0);
Assert.False(string.IsNullOrWhiteSpace(payload.GetProperty("lastCompletedRunId").GetString()));
}
[Trait("Category", TestCategories.Integration)]
[Fact]
public async Task ConsoleRunFirstSignal_ReturnsCompatibilitySnapshot_AndHonorsConditionalRequests()
{
using var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "demo-prod");
var response = await client.GetAsync("/api/console/runs/run::demo-prod::20260309/first-signal", TestContext.Current.CancellationToken);
response.EnsureSuccessStatusCode();
var payload = await response.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
Assert.Equal("run::demo-prod::20260309", payload.GetProperty("runId").GetString());
Assert.Equal("completed", payload.GetProperty("firstSignal").GetProperty("type").GetString());
Assert.Equal("snapshot", payload.GetProperty("firstSignal").GetProperty("step").GetString());
Assert.Equal("run", payload.GetProperty("firstSignal").GetProperty("artifact").GetProperty("kind").GetString());
var etag = response.Headers.ETag?.Tag;
Assert.False(string.IsNullOrWhiteSpace(etag));
Assert.Contains("compatibility", string.Join(", ", response.Headers.GetValues("Cache-Status")));
using var conditionalRequest = new HttpRequestMessage(HttpMethod.Get, "/api/console/runs/run::demo-prod::20260309/first-signal");
conditionalRequest.Headers.Add("X-StellaOps-Tenant", "demo-prod");
conditionalRequest.Headers.TryAddWithoutValidation("If-None-Match", etag);
var notModified = await client.SendAsync(conditionalRequest, TestContext.Current.CancellationToken);
Assert.Equal(System.Net.HttpStatusCode.NotModified, notModified.StatusCode);
}
[Trait("Category", TestCategories.Integration)]
[Fact]
public async Task AocMetrics_RespectRequestedWindow()
{
using var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "demo-prod");
var response = await client.GetAsync("/api/v1/aoc/metrics?windowMinutes=60", TestContext.Current.CancellationToken);
response.EnsureSuccessStatusCode();
var payload = await response.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
Assert.Equal(12870, payload.GetProperty("totalCount").GetInt32());
Assert.Equal(60, payload.GetProperty("timeWindow").GetProperty("durationMinutes").GetInt32());
Assert.True(payload.GetProperty("recentViolations").GetArrayLength() > 0);
}
[Trait("Category", TestCategories.Integration)]
[Fact]
public async Task AocMetrics_DefaultWindowWhenCallerOmitsWindowMinutes()
{
using var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "demo-prod");
var response = await client.GetAsync("/api/v1/aoc/metrics", TestContext.Current.CancellationToken);
response.EnsureSuccessStatusCode();
var payload = await response.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
Assert.Equal(1440, payload.GetProperty("timeWindow").GetProperty("durationMinutes").GetInt32());
}
[Trait("Category", TestCategories.Integration)]
[Fact]
public async Task AocComplianceEndpoints_ReturnDashboardAndRetryableViolations()
{
using var client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("X-StellaOps-Tenant", "demo-prod");
var dashboardResponse = await client.GetAsync("/api/v1/aoc/compliance/dashboard", TestContext.Current.CancellationToken);
dashboardResponse.EnsureSuccessStatusCode();
var dashboard = await dashboardResponse.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
Assert.True(dashboard.TryGetProperty("metrics", out _));
Assert.True(dashboard.GetProperty("recentViolations").GetArrayLength() > 0);
var retryResponse = await client.PostAsJsonAsync(
"/api/v1/aoc/compliance/violations/viol-001/retry",
new { },
TestContext.Current.CancellationToken);
retryResponse.EnsureSuccessStatusCode();
var retryPayload = await retryResponse.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
Assert.True(retryPayload.GetProperty("success").GetBoolean());
}
}

View File

@@ -2,6 +2,10 @@ using System;
using System.Linq;
using System.Net.Http.Json;
using System.Text.Json.Nodes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
using StellaOps.Auth.Abstractions;
using StellaOps.Auth.ServerIntegration;
using StellaOps.Platform.WebService.Contracts;
using Xunit;
@@ -124,4 +128,29 @@ public sealed class QuotaEndpointsTests : IClassFixture<PlatformWebApplicationFa
Assert.NotNull(body);
Assert.Equal("tenant_forbidden", body!["error"]?.GetValue<string>());
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task QuotaPolicies_AcceptLegacyOrchQuotaScope()
{
var policyProvider = factory.Services.GetRequiredService<IAuthorizationPolicyProvider>();
var readPolicy = await policyProvider.GetPolicyAsync("platform.quota.read");
var adminPolicy = await policyProvider.GetPolicyAsync("platform.quota.admin");
Assert.NotNull(readPolicy);
Assert.NotNull(adminPolicy);
var readScopes = Assert.Single(readPolicy!.Requirements.OfType<StellaOpsScopeRequirement>()).RequiredScopes;
var readRequirement = Assert.Single(readPolicy!.Requirements.OfType<StellaOpsScopeRequirement>());
var adminRequirement = Assert.Single(adminPolicy!.Requirements.OfType<StellaOpsScopeRequirement>());
var adminScopes = adminRequirement.RequiredScopes;
Assert.Contains(StellaOpsScopes.OrchQuota, readScopes);
Assert.Contains("quota.read", readScopes);
Assert.Contains(StellaOpsScopes.OrchQuota, adminScopes);
Assert.Contains("quota.admin", adminScopes);
Assert.False(readRequirement.RequireAllScopes);
Assert.False(adminRequirement.RequireAllScopes);
}
}