Skip to content

Commit 03a95aa

Browse files
nanotaboadaclaude
andcommitted
test(repositories): add integration tests for Repository<T> and PlayerRepository (#461)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 7de7b87 commit 03a95aa

2 files changed

Lines changed: 168 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ This project uses famous football stadiums (A-Z) that hosted FIFA World Cup matc
4444

4545
### Added
4646

47+
- Add `test/.../Integration/PlayerRepositoryTests.cs` with 9 integration tests covering `Repository<T>` (`GetAllAsync`, `FindByIdAsync`, `RemoveAsync`) and `PlayerRepository` (`FindBySquadNumberAsync`, `SquadNumberExistsAsync`); all tests use `DatabaseFakes.MigrateAsync()` on in-memory SQLite and are tagged `[Trait("Category", "Integration")]` (#461)
4748
- Add `ValidateAsync_SquadNumberNegative_ReturnsValidationError` test to exercise the `GreaterThan(0)` rule with a negative value, which passes `NotEmpty()` but fails the greater-than rule (#427)
4849
- Add `ValidateAsync_FirstNameEmptyInUpdateRuleSet_ReturnsValidationError` test to verify the `"Update"` rule set enforces structural field validation (#427)
4950
- Add `adr/` directory with 12 Architecture Decision Records documenting architectural choices, technology decisions, and design trade-offs (#372)
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
using System.Data.Common;
2+
using Dotnet.Samples.AspNetCore.WebApi.Data;
3+
using Dotnet.Samples.AspNetCore.WebApi.Models;
4+
using Dotnet.Samples.AspNetCore.WebApi.Repositories;
5+
using Dotnet.Samples.AspNetCore.WebApi.Tests.Utilities;
6+
using FluentAssertions;
7+
8+
namespace Dotnet.Samples.AspNetCore.WebApi.Tests.Integration;
9+
10+
/// <summary>
11+
/// Integration tests for <see cref="Repository{T}"/> and <see cref="PlayerRepository"/>.
12+
/// Each test runs against an in-memory SQLite database with the full EF Core migration
13+
/// chain applied via <see cref="DatabaseFakes.MigrateAsync"/>, which also validates
14+
/// that the migration chain itself is healthy as a side effect.
15+
/// </summary>
16+
public class PlayerRepositoryTests : IAsyncLifetime
17+
{
18+
private DbConnection _connection = default!;
19+
private PlayerDbContext _dbContext = default!;
20+
private PlayerRepository _repository = default!;
21+
22+
public async Task InitializeAsync()
23+
{
24+
var (connection, options) = DatabaseFakes.CreateSqliteConnection();
25+
_connection = connection;
26+
_dbContext = DatabaseFakes.CreateDbContext(options);
27+
await _dbContext.MigrateAsync();
28+
_repository = new PlayerRepository(_dbContext);
29+
}
30+
31+
public async Task DisposeAsync()
32+
{
33+
await _dbContext.DisposeAsync();
34+
await _connection.DisposeAsync();
35+
}
36+
37+
/* -------------------------------------------------------------------------
38+
* GetAllAsync
39+
* ---------------------------------------------------------------------- */
40+
41+
[Fact]
42+
[Trait("Category", "Integration")]
43+
public async Task GetAllAsync_WhenCalled_ReturnsAllSeededPlayers()
44+
{
45+
// Act
46+
var players = await _repository.GetAllAsync();
47+
48+
// Assert
49+
players.Should().HaveCount(26);
50+
_dbContext.ChangeTracker.Entries<Player>().Should().BeEmpty();
51+
}
52+
53+
/* -------------------------------------------------------------------------
54+
* FindByIdAsync
55+
* ---------------------------------------------------------------------- */
56+
57+
[Fact]
58+
[Trait("Category", "Integration")]
59+
public async Task FindByIdAsync_ExistingId_ReturnsPlayer()
60+
{
61+
// Arrange — resolve a real ID from the seeded database
62+
var seeded = await _repository.GetAllAsync();
63+
var existingId = seeded[0].Id;
64+
65+
// Act
66+
var player = await _repository.FindByIdAsync(existingId);
67+
68+
// Assert
69+
player.Should().NotBeNull();
70+
player!.Id.Should().Be(existingId);
71+
}
72+
73+
[Fact]
74+
[Trait("Category", "Integration")]
75+
public async Task FindByIdAsync_UnknownId_ReturnsNull()
76+
{
77+
// Act
78+
var player = await _repository.FindByIdAsync(Guid.NewGuid());
79+
80+
// Assert
81+
player.Should().BeNull();
82+
}
83+
84+
/* -------------------------------------------------------------------------
85+
* RemoveAsync
86+
* ---------------------------------------------------------------------- */
87+
88+
[Fact]
89+
[Trait("Category", "Integration")]
90+
public async Task RemoveAsync_ExistingEntity_RemovesFromDatabase()
91+
{
92+
// Arrange
93+
var seeded = await _repository.GetAllAsync();
94+
var existingId = seeded[0].Id;
95+
96+
// Act
97+
await _repository.RemoveAsync(existingId);
98+
99+
// Assert
100+
var player = await _repository.FindByIdAsync(existingId);
101+
player.Should().BeNull();
102+
}
103+
104+
[Fact]
105+
[Trait("Category", "Integration")]
106+
public async Task RemoveAsync_UnknownId_NoExceptionThrown()
107+
{
108+
// Act
109+
var act = async () => await _repository.RemoveAsync(Guid.NewGuid());
110+
111+
// Assert
112+
await act.Should().NotThrowAsync();
113+
}
114+
115+
/* -------------------------------------------------------------------------
116+
* FindBySquadNumberAsync
117+
* ---------------------------------------------------------------------- */
118+
119+
[Fact]
120+
[Trait("Category", "Integration")]
121+
public async Task FindBySquadNumberAsync_ExistingSquadNumber_ReturnsPlayer()
122+
{
123+
// Act
124+
var player = await _repository.FindBySquadNumberAsync(23);
125+
126+
// Assert
127+
player.Should().NotBeNull();
128+
player!.SquadNumber.Should().Be(23);
129+
}
130+
131+
[Fact]
132+
[Trait("Category", "Integration")]
133+
public async Task FindBySquadNumberAsync_UnknownSquadNumber_ReturnsNull()
134+
{
135+
// Act
136+
var player = await _repository.FindBySquadNumberAsync(999);
137+
138+
// Assert
139+
player.Should().BeNull();
140+
}
141+
142+
/* -------------------------------------------------------------------------
143+
* SquadNumberExistsAsync
144+
* ---------------------------------------------------------------------- */
145+
146+
[Fact]
147+
[Trait("Category", "Integration")]
148+
public async Task SquadNumberExistsAsync_ExistingSquadNumber_ReturnsTrue()
149+
{
150+
// Act
151+
var exists = await _repository.SquadNumberExistsAsync(23);
152+
153+
// Assert
154+
exists.Should().BeTrue();
155+
}
156+
157+
[Fact]
158+
[Trait("Category", "Integration")]
159+
public async Task SquadNumberExistsAsync_UnknownSquadNumber_ReturnsFalse()
160+
{
161+
// Act
162+
var exists = await _repository.SquadNumberExistsAsync(999);
163+
164+
// Assert
165+
exists.Should().BeFalse();
166+
}
167+
}

0 commit comments

Comments
 (0)