Restore live platform compatibility contracts
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user