Skip to content

refactor: consolidate player data into single source of truth (#210)#352

Merged
nanotaboada merged 3 commits intomasterfrom
refactor/issue-210-consolidate-player-data-ssot
Jan 18, 2026
Merged

refactor: consolidate player data into single source of truth (#210)#352
nanotaboada merged 3 commits intomasterfrom
refactor/issue-210-consolidate-player-data-ssot

Conversation

@nanotaboada
Copy link
Copy Markdown
Owner

@nanotaboada nanotaboada commented Jan 18, 2026

Eliminate duplication between PlayerData.cs and PlayerFakes.cs by
making PlayerFakes delegate to PlayerData methods.

  • PlayerFakes.MakeStarting11() now calls PlayerData.MakeStarting11()
  • PlayerFakes.MakeNew() now uses PlayerData.GetSubstitutes()
  • All factory methods updated to delegate to PlayerData
  • Enhanced XML documentation to clarify SSOT relationship

Summary by CodeRabbit

  • Chores
    • CI pipeline triggers simplified to run only for production branches and PRs; development branches no longer trigger builds automatically.
  • Tests
    • Test data now uses canonical production data with unique IDs; in-memory SQLite support, DB seeding and context helpers added for reliable integration-style tests.
    • New test helper methods and mocks for controllers, services, URL helpers, and memory cache behavior.
  • Documentation
    • Added XML and inline comments clarifying behavior across several utilities and filters.

✏️ Tip: You can customize this high-level summary in your review settings.

Eliminate duplication between PlayerData.cs and PlayerFakes.cs by
making PlayerFakes delegate to PlayerData methods.

- PlayerFakes.MakeStarting11() now calls PlayerData.MakeStarting11()
- PlayerFakes.MakeNew() now uses PlayerData.GetSubstitutes()
- All factory methods updated to delegate to PlayerData
- Enhanced XML documentation to clarify SSOT relationship
@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented Jan 18, 2026

Coverage summary from Codacy

See diff coverage on Codacy

Coverage variation Diff coverage
+19.87%
Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (a766e4e) 1721 194 11.27%
Head commit (0d34a18) 1721 (+0) 536 (+342) 31.14% (+19.87%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#352) 0 0 ∅ (not applicable)

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

See your quality gate settings    Change summary preferences

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 18, 2026

Walkthrough

Removed feature/* from CI triggers and added comments in the pipeline; test utilities were updated to consume centralized PlayerData (starting XI and substitutes), assign new GUIDs to generated Players, and add in-memory SQLite helpers plus several mock/utility methods and XML docs.

Changes

Cohort / File(s) Summary
Pipeline config
azure-pipelines.yml
Removed feature/* branch trigger and added explanatory comments about production deployments, PR validation, and development-branch behavior.
Player test fakes
test/Dotnet.Samples.AspNetCore.WebApi.Tests/Utilities/PlayerFakes.cs
Switched data sourcing to PlayerData.MakeStarting11() / PlayerData.GetSubstitutes(), assign new GUIDs to created Player instances, adjusted mapping/request/response helper methods, and added XML docs.
Player test mocks & stubs
test/Dotnet.Samples.AspNetCore.WebApi.Tests/Utilities/PlayerMocks.cs, test/Dotnet.Samples.AspNetCore.WebApi.Tests/Utilities/PlayerStubs.cs
Added controller/service mock initializers, IUrlHelper and memory-cache mock helpers, and XML docs; added CreateModelError documentation.
Database test helpers
test/Dotnet.Samples.AspNetCore.WebApi.Tests/Utilities/DatabaseFakes.cs
Added in-memory SQLite support: CreateSqliteConnection() returning open connection and DbContextOptions, CreateDbContext(...), and PlayerDbContext extension methods CreateTable() and Seed().
Production/test data docs
src/Dotnet.Samples.AspNetCore.WebApi/Utilities/PlayerData.cs
Added XML documentation for PlayerData and MakeStarting11() (no logic changes).
Swagger/authorization docs
src/Dotnet.Samples.AspNetCore.WebApi/Configurations/AuthorizeCheckOperationFilter.cs
Added XML documentation to Apply method only (no behavioral changes).

Sequence Diagram(s)

(omitted — changes do not introduce a new multi-component control flow requiring visualization)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title follows Conventional Commits format (refactor:), is under 80 characters (68 chars), and clearly describes the main change: consolidating player data into a single source of truth by eliminating duplication between PlayerData.cs and PlayerFakes.cs.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 75.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/issue-210-consolidate-player-data-ssot

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@azure-pipelines.yml`:
- Around line 6-14: The pipeline trigger list was broadened to include many
branch patterns (refactor/*, fix/*, feat/*, chore/*, docs/*, test/*) which
conflicts with the repo policy—restore the triggers to only "master" and
"feature/*" and ensure PR triggers still target master; update the trigger block
in azure-pipelines.yml (the trigger: section) to remove the extra patterns and
leave only the allowed entries so CI runs match policy.

Comment thread azure-pipelines.yml Outdated
Remove feature/* from pipeline triggers to avoid running CI on every
push to development branches. CI now only runs on:
- Pushes to master (production)
- Pull requests to master (validation before merge)

Development branches using conventional commit naming (refactor/*,
fix/*, feat/*, etc.) will only trigger CI when a PR is opened,
not on every commit. This maintains code quality validation while
reducing Azure DevOps pipeline execution costs.
@nanotaboada nanotaboada force-pushed the refactor/issue-210-consolidate-player-data-ssot branch from a20854a to ac62c9a Compare January 18, 2026 04:15
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@azure-pipelines.yml`:
- Around line 5-11: The pipeline trigger was narrowed to only "master" but
should also include feature branches; update the trigger block (the trigger YAML
that currently lists "master") to add "feature/*" so the pipeline runs on
commits to master and feature/* branches per the coding guidelines, or if the
policy changed update the guideline text instead to reflect the new behavior.

Comment thread azure-pipelines.yml
@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
src/Dotnet.Samples.AspNetCore.WebApi/Utilities/PlayerData.cs (1)

43-44: Typo: "Altético Madrid" should be "Atlético Madrid".

The team name is misspelled as "Altético" (missing the accent on the 'l') in multiple player entries. Note that the JSON data in MakeStarting11FromDeserializedJson() correctly uses "Atlético Madrid" (lines 779, 839).

📝 Proposed fix
-                Team = "Altético Madrid",
+                Team = "Atlético Madrid",

Apply this fix at lines 43, 108, 196, and 266.

Also applies to: 108-109, 196-197, 266-267

test/Dotnet.Samples.AspNetCore.WebApi.Tests/Utilities/DatabaseFakes.cs (1)

51-62: Incomplete table schema and potential inconsistency with EF Core model.

The CreateTable() method contains placeholder SQL (/* ... other columns ... */) that will create an incomplete schema missing most columns. Additionally, this raw SQL approach may drift from the actual entity configuration in PlayerDbContext.OnModelCreating().

Consider using EF Core's built-in schema creation instead:

🔧 Recommended fix using EF Core schema management
         public static void CreateTable(this PlayerDbContext context)
         {
-            using var cmd = context.Database.GetDbConnection().CreateCommand();
-            cmd.CommandText =
-                @"
-                CREATE TABLE players (
-                    id INTEGER PRIMARY KEY,
-                    firstName TEXT NOT NULL,
-                    /* ... other columns ... */
-                )";
-            cmd.ExecuteNonQuery();
+            context.Database.EnsureCreated();
         }

EnsureCreated() generates the schema from the EF Core model, ensuring consistency with PlayerDbContext.OnModelCreating() configuration (including the unique index on SquadNumber).

test/Dotnet.Samples.AspNetCore.WebApi.Tests/Utilities/PlayerMocks.cs (2)

46-52: Incorrect use of It.IsAny<string>() as return value.

It.IsAny<T>() is a Moq argument matcher for setup expressions, not a value producer. Using it in .Returns() will return default(string) (null), not "any string". If the intention is to return a non-null placeholder URL:

🔧 Proposed fix
         public static Mock<IUrlHelper> SetupUrlHelperMock()
         {
             var mock = new Mock<IUrlHelper>();
-            mock.Setup(url => url.Action(It.IsAny<UrlActionContext>())).Returns(It.IsAny<string>());
+            mock.Setup(url => url.Action(It.IsAny<UrlActionContext>())).Returns("http://localhost/api/players");

             return mock;
         }

Alternatively, if tests need dynamic URLs based on input, use a callback with the UrlActionContext parameter.


66-75: Incorrect use of It.IsAny<object>() as a value.

On line 70, when cacheValue is null, It.IsAny<object>() is passed to SetupMemoryCacheMock(). This is incorrect because It.IsAny<T>() is an argument matcher, not a value—it returns default(object) (null) in this context. If the intent is to simulate an empty/null cache value, just pass null directly.

📝 Proposed fix
-            var memoryCache = SetupMemoryCacheMock(cacheValue ?? It.IsAny<object>());
+            var memoryCache = SetupMemoryCacheMock(cacheValue);

The SetupMemoryCacheMock already handles null values correctly by returning null from TryGetValue when appropriate.

🤖 Fix all issues with AI agents
In `@test/Dotnet.Samples.AspNetCore.WebApi.Tests/Utilities/PlayerFakes.cs`:
- Around line 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).
🧹 Nitpick comments (2)
src/Dotnet.Samples.AspNetCore.WebApi/Utilities/PlayerData.cs (1)

165-325: Consider extracting shared player definitions to reduce duplication.

MakeStarting11WithId() and GetSubstitutesWithId() duplicate all player data from their non-ID counterparts, only adding the Id property. This creates a maintenance burden where any player data change must be applied in two places.

Consider a helper approach that assigns IDs to an existing list:

public static List<Player> MakeStarting11WithId()
{
    var players = MakeStarting11();
    var guids = new[] { /* predefined GUIDs */ };
    for (int i = 0; i < players.Count; i++)
        players[i].Id = guids[i];
    return players;
}

This would ensure data consistency while preserving the deterministic GUIDs needed for testing.

Also applies to: 530-746

test/Dotnet.Samples.AspNetCore.WebApi.Tests/Utilities/PlayerFakes.cs (1)

173-192: Side effect in LINQ Select: mutating source objects.

The Select lambda assigns player.Id = Guid.NewGuid() which mutates the Player objects returned by PlayerData.MakeStarting11(). While this works because MakeStarting11() creates new instances on each call, it's unexpected behavior in a projection and could cause subtle bugs if the implementation changes.

Consider creating new instances or assigning the ID before the projection:

♻️ Cleaner approach
     public static List<PlayerResponseModel> MakeResponseModelsForRetrieve() =>
-        [
-            .. PlayerData
-                .MakeStarting11()
-                .Select(player =>
-                {
-                    player.Id = Guid.NewGuid();
-                    return new PlayerResponseModel
+        PlayerData
+            .MakeStarting11()
+            .Select(player => 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"
+            })
+            .ToList();

The Id assignment appears unused for PlayerResponseModel (which doesn't have an Id property based on the relevant snippets), so it can be removed entirely.

Comment on lines 40 to 44
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."
);
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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant