108 lines
4.1 KiB
C#
108 lines
4.1 KiB
C#
using ClaudeDo.Data;
|
|
using ClaudeDo.Data.Models;
|
|
using ClaudeDo.Data.Repositories;
|
|
using ClaudeDo.Worker.External;
|
|
using ClaudeDo.Worker.Hub;
|
|
using ClaudeDo.Worker.Tests.Hub;
|
|
using ClaudeDo.Worker.Tests.Infrastructure;
|
|
using Xunit;
|
|
|
|
namespace ClaudeDo.Worker.Tests.Tickets;
|
|
|
|
/// TicketProjectId shares the exact SetConfigAsync-copies-verbatim trap that SerializeOnFileOverlap
|
|
/// already hit once: any writer that doesn't carry the field forward silently clears the
|
|
/// list<->ticket-project link. UpdateListConfigDto's TicketProjectId is tri-state for the same
|
|
/// reason SerializeOnFileOverlap is: null = keep stored, <=0 = clear, >0 = set. The MCP
|
|
/// set_list_config tool doesn't expose the field at all (UI-only), so it must preserve whatever is
|
|
/// already stored.
|
|
public sealed class ListConfigTicketProjectTests : IDisposable
|
|
{
|
|
private readonly DbFixture _db = new();
|
|
|
|
public void Dispose() => _db.Dispose();
|
|
|
|
private WorkerHub CreateHub()
|
|
{
|
|
var factory = _db.CreateFactory();
|
|
var broadcaster = new HubBroadcaster(new CapturingHubContext());
|
|
var hub = new WorkerHub(
|
|
null!, null!, null!, null!, broadcaster, factory,
|
|
null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!,
|
|
null!, new ClaudeDo.Worker.Online.OnlineInboxConfig(), new ClaudeDo.Worker.OnlineRefreshTokenStore(new ClaudeDo.Worker.DpapiTokenStore("unused.token")),
|
|
new ClaudeDo.Worker.Runner.PendingQuestionRegistry(), null!);
|
|
hub.Clients = new FakeHubCallerClients(new RecordingClientProxy());
|
|
hub.Context = new FakeHubCallerContext();
|
|
return hub;
|
|
}
|
|
|
|
private async Task<string> SeedListAsync()
|
|
{
|
|
var listId = Guid.NewGuid().ToString();
|
|
await using var ctx = _db.CreateContext();
|
|
await new ListRepository(ctx).AddAsync(new ListEntity { Id = listId, Name = "L", CreatedAt = DateTime.UtcNow });
|
|
return listId;
|
|
}
|
|
|
|
private async Task SeedConfigAsync(string listId, int? ticketProjectId = null, string? model = null)
|
|
{
|
|
await using var ctx = _db.CreateContext();
|
|
await new ListRepository(ctx).SetConfigAsync(new ListConfigEntity
|
|
{
|
|
ListId = listId, Model = model, TicketProjectId = ticketProjectId,
|
|
});
|
|
}
|
|
|
|
private async Task<ListConfigEntity?> ReadConfigAsync(string listId)
|
|
{
|
|
await using var ctx = _db.CreateContext();
|
|
return await new ListRepository(ctx).GetConfigAsync(listId);
|
|
}
|
|
|
|
private async Task CallSetListConfigAsync(string listId, string model)
|
|
{
|
|
var factory = _db.CreateFactory();
|
|
await using var ctx = factory.CreateDbContext();
|
|
var tool = new ConfigMcpTools(
|
|
new ListRepository(ctx), new TaskRepository(ctx),
|
|
new HubBroadcaster(new CapturingHubContext()), factory);
|
|
await tool.SetListConfig(listId, model: model, cancellationToken: CancellationToken.None);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task set_list_config_preserves_TicketProjectId()
|
|
{
|
|
var listId = await SeedListAsync();
|
|
await SeedConfigAsync(listId, ticketProjectId: 393);
|
|
|
|
await CallSetListConfigAsync(listId, model: "opus");
|
|
|
|
var config = await ReadConfigAsync(listId);
|
|
Assert.Equal(393, config!.TicketProjectId);
|
|
Assert.Equal("opus", config.Model);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateListConfig_with_null_TicketProjectId_keeps_the_stored_value()
|
|
{
|
|
var hub = CreateHub();
|
|
var listId = await SeedListAsync();
|
|
await SeedConfigAsync(listId, ticketProjectId: 393);
|
|
|
|
await hub.UpdateListConfig(new UpdateListConfigDto(listId, "opus", null, null));
|
|
|
|
Assert.Equal(393, (await ReadConfigAsync(listId))!.TicketProjectId);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateListConfig_can_clear_the_link_explicitly()
|
|
{
|
|
var hub = CreateHub();
|
|
var listId = await SeedListAsync();
|
|
await SeedConfigAsync(listId, ticketProjectId: 393);
|
|
|
|
await hub.UpdateListConfig(new UpdateListConfigDto(listId, "opus", null, null, TicketProjectId: 0));
|
|
|
|
Assert.Null((await ReadConfigAsync(listId))!.TicketProjectId);
|
|
}
|
|
}
|