using ClaudeDo.Data.Repositories; using ClaudeDo.Worker.Planning; using ClaudeDo.Worker.Runner; using ClaudeDo.Worker.Tests.Infrastructure; using Microsoft.AspNetCore.Http; using Xunit; namespace ClaudeDo.Worker.Tests.Hub; public sealed class TaskRunTokenAuthTests : IDisposable { private readonly DbFixture _db = new(); public void Dispose() => _db.Dispose(); [Fact] public async Task Valid_taskRun_token_populates_TaskRunContext_and_calls_next() { var reg = new TaskRunTokenRegistry(); reg.Register("run-token", "task-1"); bool nextCalled = false; var mw = new PlanningTokenAuthMiddleware(_ => { nextCalled = true; return Task.CompletedTask; }); var ctx = new DefaultHttpContext(); ctx.Request.Path = "/mcp"; ctx.Request.Headers["Authorization"] = "Bearer run-token"; using var db = _db.CreateContext(); await mw.InvokeAsync(ctx, new TaskRepository(db), reg); Assert.True(nextCalled); var resolved = ctx.Items["TaskRunContext"] as TaskRunMcpContext; Assert.NotNull(resolved); Assert.Equal("task-1", resolved!.CallerTaskId); } [Fact] public async Task Unknown_token_returns_401() { var mw = new PlanningTokenAuthMiddleware(_ => Task.CompletedTask); var ctx = new DefaultHttpContext(); ctx.Request.Path = "/mcp"; ctx.Request.Headers["Authorization"] = "Bearer nope"; using var db = _db.CreateContext(); await mw.InvokeAsync(ctx, new TaskRepository(db), new TaskRunTokenRegistry()); Assert.Equal(401, ctx.Response.StatusCode); } }