feat(postit): migrate editable profile data and admin profile flow

This commit is contained in:
Paul Schneider 2026-10-07 12:20:26 +01:00
commit af1e572f32
Signed by: notazof
GPG key ID: AD4B9B545FBD7D12
38 changed files with 6573 additions and 19 deletions

View file

@ -66,7 +66,14 @@ public sealed class ApiWebServerFixture : WebHostFixture
builder.Services.Configure<GoogleAuthSettings>(_ => { });
builder.Services.AddTransient<IBillingService, BillingService>();
builder.Services.AddTransient<IYavscMessageSender, NoopMessageSender>();
builder.Services.AddAuthorization();
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdministratorOnly", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireClaim(Yavsc.Constants.RoleClaimType, Yavsc.Constants.AdminGroupName);
});
});
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>

View file

@ -0,0 +1,124 @@
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<ApiWebServerFixture>
{
private readonly ApiWebServerFixture _fixture;
public ResourceUsageApiControllerTests(ApiWebServerFixture fixture)
{
_fixture = fixture;
}
private static HttpClient NewClient(ApiWebServerFixture fixture, string subject = "alice", string scope = "api", IEnumerable<Claim>? 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<ResourceUsageSummary>(TestContext.Current.CancellationToken);
Assert.NotNull(summary);
Assert.Equal("alice", summary!.UserId);
using var scope = _fixture.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<Yavsc.Models.ApplicationDbContext>();
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<Yavsc.Models.ApplicationDbContext>();
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<Yavsc.Models.ApplicationDbContext>();
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<List<ResourceUsageSummary>>(TestContext.Current.CancellationToken);
Assert.NotNull(payload);
Assert.Contains(payload!, item => item.UserId == "alice");
}
}