Files
ClaudeDo/tests/ClaudeDo.Data.Tests/PowerUseTests.cs
T
Mika Kuns 3cf9628f46 feat(data): PowerUse-Flags und konfigurierbares Handoff-Budget
app_settings bekommt power_use_enabled (Master-Schalter, wird im Usage-Monitor
gesetzt) und list_handler_max_handoffs (Default 4 = zwei Merge-Runden).
list_config bekommt power_use_allowed als Opt-in pro Liste.

Ein Handler-Run laeuft nur unbeaufsichtigt, wenn BEIDE Schalter gesetzt sind.
Damit kann der globale Schalter ueber Nacht an bleiben, ohne dass der Handler
einer nicht zugestimmten Liste sein Verhalten aendert.

UpdateListConfigDto.PowerUseAllowed ist bewusst bool? (tri-state, null =
gespeicherten Wert behalten), analog zu TicketProjectId: SetConfigAsync kopiert
verbatim, ein non-nullable bool haette jedem fremden Writer erlaubt, das Opt-in
per Auslassung zu widerrufen. Der Test dazu nagelt genau das fest.

Migration ist drei reine ALTER TABLE ADD COLUMN, kein Table-Rebuild.
2026-09-02 15:43:32 +02:00

99 lines
3.6 KiB
C#

using ClaudeDo.Data;
using ClaudeDo.Data.Models;
using ClaudeDo.Data.Repositories;
using Microsoft.EntityFrameworkCore;
namespace ClaudeDo.Data.Tests;
public sealed class PowerUseTests : IDisposable
{
private readonly string _dbPath;
private readonly ClaudeDoDbContext _ctx;
private const string ListId = "l1";
public PowerUseTests()
{
_dbPath = Path.Combine(Path.GetTempPath(), $"claudedo_poweruse_{Guid.NewGuid():N}.db");
var options = new DbContextOptionsBuilder<ClaudeDoDbContext>()
.UseSqlite($"Data Source={_dbPath}")
.Options;
_ctx = new ClaudeDoDbContext(options);
_ctx.Database.EnsureCreated();
_ctx.Lists.Add(new ListEntity { Id = ListId, Name = "Test", CreatedAt = DateTime.UtcNow });
_ctx.SaveChanges();
}
public void Dispose()
{
_ctx.Dispose();
foreach (var suffix in new[] { "", "-wal", "-shm" })
try { File.Delete(_dbPath + suffix); } catch { }
}
private ClaudeDoDbContext Reload() => new(
new DbContextOptionsBuilder<ClaudeDoDbContext>()
.UseSqlite(_ctx.Database.GetConnectionString()).Options);
[Fact]
public async Task ListConfig_PowerUseAllowed_DefaultsToFalse()
{
await new ListRepository(_ctx).SetConfigAsync(new ListConfigEntity { ListId = ListId, Model = "sonnet" });
using var reload = Reload();
var cfg = await new ListRepository(reload).GetConfigAsync(ListId);
Assert.False(cfg!.PowerUseAllowed);
}
[Fact]
public async Task ListConfig_PowerUseAllowed_RoundtripsTrue()
{
await new ListRepository(_ctx).SetConfigAsync(new ListConfigEntity { ListId = ListId, PowerUseAllowed = true });
using var reload = Reload();
var cfg = await new ListRepository(reload).GetConfigAsync(ListId);
Assert.True(cfg!.PowerUseAllowed);
}
/// SetConfigAsync copies the incoming entity field by field onto the existing row. A field
/// forgotten in that copy block reads as "silently reset on the next unrelated save", which is
/// exactly how the ticket-project link broke before. Pin it for PowerUseAllowed.
[Fact]
public async Task ListConfig_UpdatingAnotherField_DoesNotResetPowerUseAllowed()
{
var repo = new ListRepository(_ctx);
await repo.SetConfigAsync(new ListConfigEntity { ListId = ListId, PowerUseAllowed = true });
await repo.SetConfigAsync(new ListConfigEntity { ListId = ListId, PowerUseAllowed = true, VerifyCommand = "dotnet build" });
using var reload = Reload();
var cfg = await new ListRepository(reload).GetConfigAsync(ListId);
Assert.True(cfg!.PowerUseAllowed);
Assert.Equal("dotnet build", cfg.VerifyCommand);
}
[Fact]
public async Task AppSettings_PowerUseDefaults_AreOffAndFourHandoffs()
{
var settings = await new AppSettingsRepository(_ctx).GetAsync();
Assert.False(settings.PowerUseEnabled);
Assert.Equal(4, settings.ListHandlerMaxHandoffs);
}
[Fact]
public async Task AppSettings_PowerUseFields_Roundtrip()
{
await new AppSettingsRepository(_ctx).GetAsync();
var row = await _ctx.AppSettings.FirstAsync(s => s.Id == AppSettingsEntity.SingletonId);
row.PowerUseEnabled = true;
row.ListHandlerMaxHandoffs = 40;
await _ctx.SaveChangesAsync();
using var reload = Reload();
var settings = await new AppSettingsRepository(reload).GetAsync();
Assert.True(settings.PowerUseEnabled);
Assert.Equal(40, settings.ListHandlerMaxHandoffs);
}
}