Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
# Build and test ASP.NET Core projects targeting .NET 8 on Linux.
# https://learn.microsoft.com/en-us/azure/devops/pipelines/ecosystems/dotnet-core

# Trigger pipeline on commits to master and pull requests (equivalent to GitHub Actions 'on: [push, pull_request]')
# Pipeline triggers:
# - Runs on pushes to master (production deployments)
# - Runs on all pull requests to master (validation before merge)
# - Does NOT run on pushes to development branches (cost optimization)
# Development branches (refactor/*, fix/*, feat/*, etc.) are validated only when a PR is opened
trigger:
- master
Comment thread
nanotaboada marked this conversation as resolved.
- feature/*

pr:
- master
Expand Down
239 changes: 55 additions & 184 deletions test/Dotnet.Samples.AspNetCore.WebApi.Tests/Utilities/PlayerFakes.cs
Original file line number Diff line number Diff line change
@@ -1,201 +1,67 @@
using Dotnet.Samples.AspNetCore.WebApi.Enums;
using Dotnet.Samples.AspNetCore.WebApi.Models;
using Dotnet.Samples.AspNetCore.WebApi.Utilities;

namespace Dotnet.Samples.AspNetCore.WebApi.Tests.Utilities;

/// <summary>
/// A Fake is a working implementation that’s simpler than the real one.
/// It usually has some “real” logic but is not suitable for production
/// Test data factory for Player entities.
/// Wraps production data from PlayerData with test-specific modifications (e.g., GUID assignment).
/// A Fake is a working implementation that's simpler than the real one.
/// It usually has some "real" logic but is not suitable for production
/// (e.g., an in‑memory database instead of a full SQL Server). Fakes are
/// useful when you need behavior thats closer to reality but still want
/// useful when you need behavior that's closer to reality but still want
/// to avoid external dependencies.
/// </summary>
public static class PlayerFakes
{
/// <summary>
/// Returns the starting 11 players with generated GUIDs for in-memory testing.
/// Reuses production player data from PlayerData.MakeStarting11().
/// </summary>
public static List<Player> MakeStarting11()
{
return
[
new()
{
Id = Guid.NewGuid(),
FirstName = "Damián",
MiddleName = "Emiliano",
LastName = "Martínez",
DateOfBirth = new DateTime(1992, 9, 1, 0, 0, 0, DateTimeKind.Utc),
SquadNumber = 23,
Position = Position.Goalkeeper.Text,
AbbrPosition = Position.Goalkeeper.Abbr,
Team = "Aston Villa FC",
League = "Premier League",
Starting11 = true,
},
new()
{
Id = Guid.NewGuid(),
FirstName = "Nahuel",
LastName = "Molina",
DateOfBirth = new DateTime(1998, 4, 5, 0, 0, 0, DateTimeKind.Utc),
SquadNumber = 26,
Position = Position.RightBack.Text,
AbbrPosition = Position.RightBack.Abbr,
Team = "Altético Madrid",
League = "La Liga",
Starting11 = true,
},
new()
{
Id = Guid.NewGuid(),
FirstName = "Cristian",
MiddleName = "Gabriel",
LastName = "Romero",
DateOfBirth = new DateTime(1998, 4, 26, 0, 0, 0, DateTimeKind.Utc),
SquadNumber = 13,
Position = Position.CentreBack.Text,
AbbrPosition = Position.CentreBack.Abbr,
Team = "Tottenham Hotspur",
League = "Premier League",
Starting11 = true,
},
new()
{
Id = Guid.NewGuid(),
FirstName = "Nicolás",
MiddleName = "Hernán Gonzalo",
LastName = "Otamendi",
DateOfBirth = new DateTime(1988, 2, 11, 0, 0, 0, DateTimeKind.Utc),
SquadNumber = 19,
Position = Position.CentreBack.Text,
AbbrPosition = Position.CentreBack.Abbr,
Team = "SL Benfica",
League = "Liga Portugal",
Starting11 = true,
},
new()
{
Id = Guid.NewGuid(),
FirstName = "Nicolás",
MiddleName = "Alejandro",
LastName = "Tagliafico",
DateOfBirth = new DateTime(1992, 8, 30, 0, 0, 0, DateTimeKind.Utc),
SquadNumber = 3,
Position = Position.LeftBack.Text,
AbbrPosition = Position.LeftBack.Abbr,
Team = "Olympique Lyon",
League = "Ligue 1",
Starting11 = true,
},
new()
{
Id = Guid.NewGuid(),
FirstName = "Ángel",
MiddleName = "Fabián",
LastName = "Di María",
DateOfBirth = new DateTime(1988, 2, 13, 0, 0, 0, DateTimeKind.Utc),
SquadNumber = 11,
Position = Position.RightWinger.Text,
AbbrPosition = Position.RightWinger.Abbr,
Team = "SL Benfica",
League = "Liga Portugal",
Starting11 = true,
},
new()
{
Id = Guid.NewGuid(),
FirstName = "Rodrigo",
MiddleName = "Javier",
LastName = "de Paul",
DateOfBirth = new DateTime(1994, 5, 23, 0, 0, 0, DateTimeKind.Utc),
SquadNumber = 7,
Position = Position.CentralMidfield.Text,
AbbrPosition = Position.CentralMidfield.Abbr,
Team = "Altético Madrid",
League = "La Liga",
Starting11 = true,
},
new()
{
Id = Guid.NewGuid(),
FirstName = "Enzo",
MiddleName = "Jeremías",
LastName = "Fernández",
DateOfBirth = new DateTime(2001, 1, 16, 0, 0, 0, DateTimeKind.Utc),
SquadNumber = 24,
Position = Position.CentralMidfield.Text,
AbbrPosition = Position.CentralMidfield.Abbr,
Team = "Chelsea FC",
League = "Premier League",
Starting11 = true,
},
new()
{
Id = Guid.NewGuid(),
FirstName = "Alexis",
LastName = "Mac Allister",
DateOfBirth = new DateTime(1998, 12, 23, 0, 0, 0, DateTimeKind.Utc),
SquadNumber = 20,
Position = Position.CentralMidfield.Text,
AbbrPosition = Position.CentralMidfield.Abbr,
Team = "Liverpool FC",
League = "Premier League",
Starting11 = true,
},
new()
{
Id = Guid.NewGuid(),
FirstName = "Lionel",
MiddleName = "Andrés",
LastName = "Messi",
DateOfBirth = new DateTime(1987, 6, 23, 0, 0, 0, DateTimeKind.Utc),
SquadNumber = 10,
Position = Position.RightWinger.Text,
AbbrPosition = Position.RightWinger.Abbr,
Team = "Inter Miami CF",
League = "Major League Soccer",
Starting11 = true,
},
new()
{
Id = Guid.NewGuid(),
FirstName = "Julián",
LastName = "Álvarez",
DateOfBirth = new DateTime(2000, 1, 30, 0, 0, 0, DateTimeKind.Utc),
SquadNumber = 9,
Position = Position.CentreForward.Text,
AbbrPosition = Position.CentreForward.Abbr,
Team = "Manchester City",
League = "Premier League",
Starting11 = true,
}
];
var players = PlayerData.MakeStarting11();

// Assign GUIDs for in-memory database testing
foreach (var player in players)
{
player.Id = Guid.NewGuid();
}

return players;
}

/// <summary>
/// Returns a specific player from the starting 11 by squad number.
/// Reuses production player data from PlayerData.MakeStarting11().
/// </summary>
public static Player MakeFromStarting11(int squadNumber)
{
var player =
MakeStarting11().SingleOrDefault(player => player.SquadNumber == squadNumber)
PlayerData.MakeStarting11().SingleOrDefault(p => p.SquadNumber == squadNumber)
?? throw new ArgumentNullException(
$"Player with Squad Number {squadNumber} not found."
);
Comment on lines 40 to 44
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Inconsistent exception types for "not found" scenarios.

Line 42 throws ArgumentNullException for a lookup failure, while line 59 throws InvalidOperationException for a similar case. ArgumentNullException is semantically incorrect here since the argument isn't null—the lookup simply returned no result. Consider using InvalidOperationException or ArgumentException consistently.

📝 Proposed fix for consistency
         var player =
             PlayerData.MakeStarting11().SingleOrDefault(p => p.SquadNumber == squadNumber)
-            ?? throw new ArgumentNullException(
+            ?? throw new InvalidOperationException(
                 $"Player with Squad Number {squadNumber} not found."
             );

Apply similar changes at lines 126-128 and 152-154.

Also applies to: 56-61

🤖 Prompt for AI Agents
In `@test/Dotnet.Samples.AspNetCore.WebApi.Tests/Utilities/PlayerFakes.cs` around
lines 40 - 44, Replace incorrect ArgumentNullException thrown when a lookup by
squadNumber fails with a semantically appropriate exception (e.g.,
InvalidOperationException or ArgumentException) for consistency; specifically
update the expression using PlayerData.MakeStarting11().SingleOrDefault(p =>
p.SquadNumber == squadNumber) ?? throw new ArgumentNullException(...) to throw
InvalidOperationException (or ArgumentException) including the same descriptive
message, and apply the same replacement to the other similar lookups in
PlayerFakes (the other two occurrences that currently throw
ArgumentNullException).


player.Id = Guid.NewGuid();
return player;
}

/// <summary>
/// Returns a new player (substitute) for testing create operations.
/// Reuses production player data from PlayerData.GetSubstitutes().
/// </summary>
public static Player MakeNew()
{
return new()
{
FirstName = "Leandro",
MiddleName = "Daniel",
LastName = "Paredes",
DateOfBirth = new DateTime(1994, 06, 29, 0, 0, 0, DateTimeKind.Utc),
SquadNumber = 5,
Position = Position.DefensiveMidfield.Text,
AbbrPosition = Position.DefensiveMidfield.Abbr,
Team = "AS Roma",
League = "Serie A",
Starting11 = false
};
// Get Leandro Paredes (squad number 5) from substitutes
var player =
PlayerData.GetSubstitutes().SingleOrDefault(player => player.SquadNumber == 5)
?? throw new InvalidOperationException(
"Substitute player with squad number 5 not found."
);

player.Id = Guid.NewGuid();
return player;
}

/* -------------------------------------------------------------------------
Expand Down Expand Up @@ -243,7 +109,7 @@ public static PlayerResponseModel MakeResponseModelForCreate()
public static PlayerRequestModel MakeRequestModelForRetrieve(int squadNumber)
{
var player =
MakeStarting11().SingleOrDefault(player => player.SquadNumber == squadNumber)
PlayerData.MakeStarting11().SingleOrDefault(p => p.SquadNumber == squadNumber)
?? throw new ArgumentNullException(
$"Player with Squad Number {squadNumber} not found."
);
Expand All @@ -264,7 +130,7 @@ public static PlayerRequestModel MakeRequestModelForRetrieve(int squadNumber)
public static PlayerResponseModel MakeResponseModelForRetrieve(int squadNumber)
{
var player =
MakeStarting11().SingleOrDefault(player => player.SquadNumber == squadNumber)
PlayerData.MakeStarting11().SingleOrDefault(p => p.SquadNumber == squadNumber)
?? throw new ArgumentNullException(
$"Player with Squad Number {squadNumber} not found."
);
Expand All @@ -284,17 +150,22 @@ public static PlayerResponseModel MakeResponseModelForRetrieve(int squadNumber)

public static List<PlayerResponseModel> MakeResponseModelsForRetrieve() =>
[
.. MakeStarting11()
.Select(player => new PlayerResponseModel
.. PlayerData
.MakeStarting11()
.Select(player =>
{
FullName =
$"{player.FirstName} {(string.IsNullOrWhiteSpace(player.MiddleName) ? "" : player.MiddleName + " ")}{player.LastName}".Trim(),
Birth = $"{player.DateOfBirth:MMMM d, yyyy}",
Dorsal = player.SquadNumber,
Position = player.Position,
Club = player.Team,
League = player.League,
Starting11 = player.Starting11 ? "Yes" : "No"
player.Id = Guid.NewGuid();
return new PlayerResponseModel
{
FullName =
$"{player.FirstName} {(string.IsNullOrWhiteSpace(player.MiddleName) ? "" : player.MiddleName + " ")}{player.LastName}".Trim(),
Birth = $"{player.DateOfBirth:MMMM d, yyyy}",
Dorsal = player.SquadNumber,
Position = player.Position,
Club = player.Team,
League = player.League,
Starting11 = player.Starting11 ? "Yes" : "No"
};
})
];

Expand Down