feat(data): add DailyNoteRepository with tests
This commit is contained in:
59
src/ClaudeDo.Data/Repositories/DailyNoteRepository.cs
Normal file
59
src/ClaudeDo.Data/Repositories/DailyNoteRepository.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using ClaudeDo.Data.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ClaudeDo.Data.Repositories;
|
||||
|
||||
public sealed class DailyNoteRepository
|
||||
{
|
||||
private readonly ClaudeDoDbContext _context;
|
||||
|
||||
public DailyNoteRepository(ClaudeDoDbContext context) => _context = context;
|
||||
|
||||
public async Task<IReadOnlyList<DailyNoteEntity>> ListByDayAsync(DateOnly day, CancellationToken ct = default) =>
|
||||
await _context.DailyNotes.AsNoTracking()
|
||||
.Where(n => n.Date == day)
|
||||
.OrderBy(n => n.SortOrder)
|
||||
.ToListAsync(ct);
|
||||
|
||||
public async Task<IReadOnlyList<DailyNoteEntity>> ListBetweenAsync(
|
||||
DateOnly start, DateOnly end, CancellationToken ct = default) =>
|
||||
await _context.DailyNotes.AsNoTracking()
|
||||
.Where(n => n.Date >= start && n.Date <= end)
|
||||
.OrderBy(n => n.Date).ThenBy(n => n.SortOrder)
|
||||
.ToListAsync(ct);
|
||||
|
||||
public async Task<DailyNoteEntity> AddAsync(DateOnly day, string text, CancellationToken ct = default)
|
||||
{
|
||||
var nextOrder = await _context.DailyNotes
|
||||
.Where(n => n.Date == day)
|
||||
.Select(n => (int?)n.SortOrder)
|
||||
.MaxAsync(ct) ?? -1;
|
||||
|
||||
var note = new DailyNoteEntity
|
||||
{
|
||||
Date = day,
|
||||
Text = text,
|
||||
SortOrder = nextOrder + 1,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
};
|
||||
_context.DailyNotes.Add(note);
|
||||
await _context.SaveChangesAsync(ct);
|
||||
return note;
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(string id, string text, CancellationToken ct = default)
|
||||
{
|
||||
var row = await _context.DailyNotes.FirstOrDefaultAsync(n => n.Id == id, ct);
|
||||
if (row is null) return;
|
||||
row.Text = text;
|
||||
await _context.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(string id, CancellationToken ct = default)
|
||||
{
|
||||
var row = await _context.DailyNotes.FirstOrDefaultAsync(n => n.Id == id, ct);
|
||||
if (row is null) return;
|
||||
_context.DailyNotes.Remove(row);
|
||||
await _context.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using ClaudeDo.Data.Models;
|
||||
using ClaudeDo.Data.Repositories;
|
||||
using ClaudeDo.Worker.Tests.Infrastructure;
|
||||
|
||||
namespace ClaudeDo.Worker.Tests.Repositories;
|
||||
|
||||
public class DailyNoteRepositoryTests : IDisposable
|
||||
{
|
||||
private readonly DbFixture _db = new();
|
||||
public void Dispose() => _db.Dispose();
|
||||
|
||||
[Fact]
|
||||
public async Task Add_AssignsIncrementingSortOrder_WithinDay()
|
||||
{
|
||||
var day = new DateOnly(2026, 6, 1);
|
||||
using (var ctx = _db.CreateContext())
|
||||
{
|
||||
var repo = new DailyNoteRepository(ctx);
|
||||
await repo.AddAsync(day, "first");
|
||||
await repo.AddAsync(day, "second");
|
||||
}
|
||||
using var read = _db.CreateContext();
|
||||
var notes = await new DailyNoteRepository(read).ListByDayAsync(day);
|
||||
Assert.Equal(new[] { "first", "second" }, notes.Select(n => n.Text));
|
||||
Assert.Equal(new[] { 0, 1 }, notes.Select(n => n.SortOrder));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ListBetween_ReturnsOnlyInRange_OrderedByDateThenSort()
|
||||
{
|
||||
using (var ctx = _db.CreateContext())
|
||||
{
|
||||
var repo = new DailyNoteRepository(ctx);
|
||||
await repo.AddAsync(new DateOnly(2026, 5, 31), "before");
|
||||
await repo.AddAsync(new DateOnly(2026, 6, 1), "in-a");
|
||||
await repo.AddAsync(new DateOnly(2026, 6, 2), "in-b");
|
||||
await repo.AddAsync(new DateOnly(2026, 6, 5), "after");
|
||||
}
|
||||
using var read = _db.CreateContext();
|
||||
var notes = await new DailyNoteRepository(read)
|
||||
.ListBetweenAsync(new DateOnly(2026, 6, 1), new DateOnly(2026, 6, 2));
|
||||
Assert.Equal(new[] { "in-a", "in-b" }, notes.Select(n => n.Text));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Update_And_Delete_Work()
|
||||
{
|
||||
var day = new DateOnly(2026, 6, 1);
|
||||
string id;
|
||||
using (var ctx = _db.CreateContext())
|
||||
{
|
||||
var n = await new DailyNoteRepository(ctx).AddAsync(day, "orig");
|
||||
id = n.Id;
|
||||
}
|
||||
using (var ctx = _db.CreateContext())
|
||||
await new DailyNoteRepository(ctx).UpdateAsync(id, "edited");
|
||||
using (var ctx = _db.CreateContext())
|
||||
{
|
||||
var notes = await new DailyNoteRepository(ctx).ListByDayAsync(day);
|
||||
Assert.Equal("edited", notes.Single().Text);
|
||||
}
|
||||
using (var ctx = _db.CreateContext())
|
||||
await new DailyNoteRepository(ctx).DeleteAsync(id);
|
||||
using var read = _db.CreateContext();
|
||||
Assert.Empty(await new DailyNoteRepository(read).ListByDayAsync(day));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user