using System.Net.Http.Json;
using FamilyNido.Domain.HouseholdTasks;
using FluentAssertions;
namespace FamilyNido.Tests.Integration;
///
/// Coverage for the family scoreboard slices: leaderboard aggregation and
/// per-member totals (this week / this month / all time). Seeds tasks +
/// completions directly through EF so we don't need to drive the UI flow
/// for every fixture variation.
///
public sealed class ScoresTests : IntegrationTestBase
{
public ScoresTests(IntegrationFixture fixture) : base(fixture) { }
[Fact]
public async Task Leaderboard_sums_points_per_member_for_the_range()
{
var handle = await WithDbAsync(db => TestSeed.SeedFamilyAsync(db, Fixture.Factory.Services));
await TestSeed.LoginAsync(Client, "dan@example.com");
var alice = await WithDbAsync(db => TestSeed.SeedMemberAsync(db, handle.Family.Id, "Alice"));
// Dan: 5 points (one task), Alice: 12 points (two tasks).
var today = DateOnly.FromDateTime(DateTime.UtcNow);
await WithDbAsync(async db =>
{
var t1 = new HouseholdTask
{
FamilyId = handle.Family.Id,
Title = "Tarea 5",
StartDate = today,
DueDate = today,
CreatedByMemberId = handle.Member.Id,
Points = 5,
};
var t2 = new HouseholdTask
{
FamilyId = handle.Family.Id,
Title = "Tarea 7",
StartDate = today,
DueDate = today,
CreatedByMemberId = handle.Member.Id,
Points = 7,
};
var t3 = new HouseholdTask
{
FamilyId = handle.Family.Id,
Title = "Tarea 5b",
StartDate = today,
DueDate = today,
CreatedByMemberId = handle.Member.Id,
Points = 5,
};
db.HouseholdTasks.AddRange(t1, t2, t3);
await db.SaveChangesAsync();
db.TaskCompletions.AddRange(
new TaskCompletion
{
TaskId = t1.Id, OccurrenceDate = today,
CompletedByMemberId = handle.Member.Id,
CompletedAt = DateTimeOffset.UtcNow,
},
new TaskCompletion
{
TaskId = t2.Id, OccurrenceDate = today,
CompletedByMemberId = alice.Id,
CompletedAt = DateTimeOffset.UtcNow,
},
new TaskCompletion
{
TaskId = t3.Id, OccurrenceDate = today,
CompletedByMemberId = alice.Id,
CompletedAt = DateTimeOffset.UtcNow,
});
await db.SaveChangesAsync();
});
var resp = await Client.GetAsync($"/api/scores/leaderboard?from={today:yyyy-MM-dd}&to={today:yyyy-MM-dd}");
var board = await ReadAsync(resp);
board!.Entries.Should().HaveCount(2);
board.Entries[0].MemberId.Should().Be(alice.Id);
board.Entries[0].Points.Should().Be(12);
board.Entries[0].CompletionCount.Should().Be(2);
board.Entries[1].MemberId.Should().Be(handle.Member.Id);
board.Entries[1].Points.Should().Be(5);
}
[Fact]
public async Task Member_score_returns_zeros_when_no_completions()
{
var handle = await WithDbAsync(db => TestSeed.SeedFamilyAsync(db, Fixture.Factory.Services));
await TestSeed.LoginAsync(Client, "dan@example.com");
var resp = await Client.GetAsync($"/api/scores/members/{handle.Member.Id}");
var score = await ReadAsync(resp);
score!.MemberId.Should().Be(handle.Member.Id);
score.ThisWeek.Should().Be(0);
score.ThisMonth.Should().Be(0);
score.AllTime.Should().Be(0);
}
private sealed record LeaderboardDto(string From, string To, List Entries);
private sealed record ScoreboardEntry(Guid MemberId, int Points, int CompletionCount);
private sealed record MemberScoreDto(Guid MemberId, int ThisWeek, int ThisMonth, int AllTime);
}