Files
ClaudeDo/tests/ClaudeDo.Worker.Tests/External/NotesMcpToolsTests.cs
T
CubeGameLPandClaude Opus 5 ae0c7d3771 feat(notes): add_note MCP tool with a per-day agent cap
Agents can write into the user's daily notes, but only within limits:
max 10 agent notes a day, max 500 characters, add only — no edit, no
delete. A new from_agent column separates their lines from the user's
so the cap counts the right ones and the UI can mark them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 21:02:37 +02:00

78 lines
2.7 KiB
C#

using ClaudeDo.Data;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Worker.External;
using ClaudeDo.Worker.Hub;
using ClaudeDo.Worker.Tests.Infrastructure;
namespace ClaudeDo.Worker.Tests.External;
public sealed class NotesMcpToolsTests : IDisposable
{
private readonly DbFixture _db = new();
private readonly ClaudeDoDbContext _ctx;
private readonly DailyNoteRepository _notes;
private readonly NotesMcpTools _sut;
private static readonly DateOnly Today = DateOnly.FromDateTime(DateTime.Today);
public NotesMcpToolsTests()
{
_ctx = _db.CreateContext();
_notes = new DailyNoteRepository(_ctx);
_sut = new NotesMcpTools(_notes, new HubBroadcaster(new CapturingHubContext()));
}
public void Dispose() { _ctx.Dispose(); _db.Dispose(); }
[Fact]
public async Task AddNote_stores_it_on_today_flagged_as_agent_written()
{
var result = await _sut.AddNote(" Mika decided to keep the daily grouping. ");
Assert.True(result.Saved);
var row = Assert.Single(await _notes.ListByDayAsync(Today));
Assert.Equal("Mika decided to keep the daily grouping.", row.Text);
Assert.True(row.FromAgent);
Assert.Equal(NotesMcpTools.DailyAgentCap - 1, result.RemainingToday);
}
[Fact]
public async Task AddNote_refuses_once_the_daily_cap_is_used_up_and_saves_nothing()
{
for (var i = 0; i < NotesMcpTools.DailyAgentCap; i++)
await _sut.AddNote($"note {i}");
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => _sut.AddNote("one too many"));
Assert.Contains("cap", ex.Message, StringComparison.OrdinalIgnoreCase);
Assert.Equal(NotesMcpTools.DailyAgentCap, (await _notes.ListByDayAsync(Today)).Count);
}
[Fact]
public async Task Notes_the_user_wrote_do_not_count_against_the_agent_cap()
{
for (var i = 0; i < NotesMcpTools.DailyAgentCap; i++)
await _notes.AddAsync(Today, $"mine {i}");
var result = await _sut.AddNote("still allowed");
Assert.Equal(1, result.AgentNotesToday);
}
[Theory]
[InlineData("")]
[InlineData(" ")]
public async Task AddNote_rejects_empty_text(string text) =>
await Assert.ThrowsAsync<InvalidOperationException>(() => _sut.AddNote(text));
[Fact]
public async Task AddNote_rejects_text_past_the_length_limit()
{
var tooLong = new string('x', NotesMcpTools.MaxLength + 1);
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => _sut.AddNote(tooLong));
Assert.Contains(NotesMcpTools.MaxLength.ToString(), ex.Message);
Assert.Empty(await _notes.ListByDayAsync(Today));
}
}