Files
Academic-Affairs-System/tests/Jiaowu.Api.Tests/PluginRuntimeTests.cs
T
2026-10-01 08:24:32 +08:00

117 lines
4.1 KiB
C#

using System.Reflection;
using Jiaowu.Api.Controllers;
using Jiaowu.Api.Domain.Identity;
using Jiaowu.Api.Infrastructure.Persistence;
using Jiaowu.Api.Infrastructure.Plugins;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Jiaowu.Api.Infrastructure.Middleware;
using Microsoft.Extensions.DependencyInjection;
namespace Jiaowu.Api.Tests;
public sealed class PluginRuntimeTests
{
[Fact]
public void Management_endpoints_require_super_admin()
{
var method = typeof(PluginsController).GetMethod(
nameof(PluginsController.SetState));
var authorize = method?.GetCustomAttribute<AuthorizeAttribute>();
Assert.NotNull(authorize);
Assert.Equal(SystemRoles.SuperAdmin, authorize.Roles);
}
[Fact]
public void Catalog_maps_only_complete_api_path_segments()
{
var catalog = new BuiltInPluginCatalog();
Assert.Equal(
"mingxu.experiments",
catalog.FindByApiPath("/api/experiments/123")?.Id);
Assert.Null(catalog.FindByApiPath("/api/experiments-other"));
Assert.Null(catalog.FindByApiPath("/api/plugins/runtime"));
}
[Fact]
public async Task Plugins_are_enabled_by_default_and_state_is_persisted()
{
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlite("Data Source=:memory:")
.Options;
await using var db = new AppDbContext(options);
await db.Database.OpenConnectionAsync();
await db.Database.EnsureCreatedAsync();
var runtime = new PluginRuntimeService(
db,
new BuiltInPluginCatalog());
Assert.True(await runtime.IsEnabledAsync("mingxu.experiments"));
var updated = await runtime.SetEnabledAsync(
"mingxu.experiments",
false,
"admin-user");
Assert.NotNull(updated);
Assert.False(updated.IsEnabled);
Assert.False(await runtime.IsEnabledAsync("mingxu.experiments"));
var persisted = await db.PluginInstallations.SingleAsync();
Assert.Equal("admin-user", persisted.ChangedByUserId);
Assert.Equal("1.0.0", persisted.InstalledVersion);
}
[Fact]
public async Task Unknown_plugin_cannot_be_enabled()
{
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlite("Data Source=:memory:")
.Options;
await using var db = new AppDbContext(options);
await db.Database.OpenConnectionAsync();
await db.Database.EnsureCreatedAsync();
var runtime = new PluginRuntimeService(
db,
new BuiltInPluginCatalog());
Assert.Null(await runtime.SetEnabledAsync("unknown.plugin", true, null));
Assert.False(await runtime.IsEnabledAsync("unknown.plugin"));
}
[Fact]
public async Task Disabled_plugin_blocks_its_api_without_invoking_the_endpoint()
{
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlite("Data Source=:memory:")
.Options;
await using var db = new AppDbContext(options);
await db.Database.OpenConnectionAsync();
await db.Database.EnsureCreatedAsync();
var catalog = new BuiltInPluginCatalog();
var runtime = new PluginRuntimeService(db, catalog);
await runtime.SetEnabledAsync("mingxu.experiments", false, "admin-user");
var endpointInvoked = false;
var middleware = new PluginAvailabilityMiddleware(_ =>
{
endpointInvoked = true;
return Task.CompletedTask;
});
var context = new DefaultHttpContext();
await using var requestServices = new ServiceCollection()
.AddLogging()
.AddProblemDetails()
.BuildServiceProvider();
context.RequestServices = requestServices;
context.Request.Path = "/api/experiments";
context.Response.Body = new MemoryStream();
await middleware.InvokeAsync(context, catalog, runtime);
Assert.False(endpointInvoked);
Assert.Equal(StatusCodes.Status404NotFound, context.Response.StatusCode);
}
}