using System.Net; using System.Net.Http.Headers; using System.Net.Http.Json; using System.Security.Claims; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Yavsc.Abstract.Resources; using Yavsc.Api.Test.Fixtures; using Yavsc.Server.Helpers; using Yavsc.Tests.Shared; namespace Yavsc.Api.Test; [Collection("Yavsc Api")] public sealed class ResourceUsageApiControllerTests : IClassFixture { private readonly ApiWebServerFixture _fixture; public ResourceUsageApiControllerTests(ApiWebServerFixture fixture) { _fixture = fixture; } private static HttpClient NewClient(ApiWebServerFixture fixture, string subject = "alice", string scope = "api", IEnumerable? extraClaims = null) { var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true }; var http = new HttpClient(handler) { BaseAddress = new Uri(fixture.BaseAddress) }; http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", TestTokenIssuer.Issue(subject, scope, extraClaims: extraClaims)); return http; } [Fact] public async Task GetCurrentUserUsage_persists_snapshot_in_database() { using var http = NewClient(_fixture, "alice"); var response = await http.GetAsync("/api/v1/resource-usage/me", TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.OK, response.StatusCode); var summary = await response.Content.ReadFromJsonAsync(TestContext.Current.CancellationToken); Assert.NotNull(summary); Assert.Equal("alice", summary!.UserId); using var scope = _fixture.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); Assert.Contains(db.ResourceUsageRecords, r => r.UserId == "alice"); } [Fact] public async Task RecordAsync_persists_cpu_and_storage_usage() { using var scope = _fixture.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); await ResourceUsageTracker.RecordAsync( db, "alice", "alice", cpuSeconds: 1.5m, storageBytes: 2 * 1024 * 1024, actorUserId: "alice"); var record = await db.ResourceUsageRecords .SingleAsync(r => r.UserId == "alice"); Assert.True(record.CpuSeconds >= 1.5m); Assert.True(record.StorageMb >= 2m / 1024m / 1024m); } [Fact] public async Task RecordAsync_persists_api_and_bandwidth_usage() { using var scope = _fixture.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); await ResourceUsageTracker.RecordAsync( db, "telemetry-user", "telemetry-user", apiCalls: 2m, bandwidthMb: 3.5m, actorUserId: "telemetry-user"); var record = await db.ResourceUsageRecords .SingleAsync(r => r.UserId == "telemetry-user"); Assert.True(record.ApiCalls >= 2m); Assert.True(record.BandwidthMb >= 3.5m); } [Fact] public async Task GetAdminOverview_returns_latest_usage_snapshot_for_each_user() { using var http = NewClient(_fixture, "alice"); var first = await http.GetAsync("/api/v1/resource-usage/me", TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.OK, first.StatusCode); using var adminHttp = NewClient( _fixture, "admin-user", "api", new[] { new Claim(Yavsc.Constants.RoleClaimType, Yavsc.Constants.AdminGroupName) }); var response = await adminHttp.GetAsync("/api/v1/resource-usage/admin", TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.OK, response.StatusCode); var payload = await response.Content.ReadFromJsonAsync>(TestContext.Current.CancellationToken); Assert.NotNull(payload); Assert.Contains(payload!, item => item.UserId == "alice"); } }