Skip to content

release: Sprint 6 — Code Quality - #161

Merged
mpaulosky merged 6 commits into
mainfrom
dev
Apr 25, 2026
Merged

release: Sprint 6 — Code Quality#161
mpaulosky merged 6 commits into
mainfrom
dev

Conversation

@mpaulosky

Copy link
Copy Markdown
Owner

Release PR: merge Sprint 6 code quality improvements into main for tagging and release.

mpaulosky and others added 6 commits April 24, 2026 08:45
Blog posts, README, and docs/index.html updated for v1.2.0. Reviewed and approved by Aragorn.
Fixes 3 review blockers identified in PR #126 review:

- docs/index.html: npm run css:watch → npm run tw:watch (matches root package.json)
- README.md + kickoff blog post: Aspire 13.2.2 → 13.2.3 (matches Directory.Packages.props)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## Summary

Adds `workflow_dispatch` trigger to `squad-mark-released.yml` so the
Done → Released project board migration can be run manually.

## Problem

When v1.2.0 was published, `squad-mark-released.yml` did not fire —
likely because the release event and workflow landing on `main` had a
timing race. With no manual trigger, the only recovery path was direct
GraphQL mutations.

## Changes

- Add `workflow_dispatch: {}` trigger
- Update job `if` condition to always run on manual dispatch (skip the
pre-release/draft guard for `workflow_dispatch` events)
- Use `context.payload.release?.tag_name ?? 'manual dispatch'` to
prevent null-ref in the summary notice

## Testing

After merge to `dev` → `main`, the workflow will appear under **Actions
→ Squad Mark Released → Run workflow** for manual recovery use.

---
> ⚠️ This PR should be fast-tracked to `main` so `dev` and `main` stay
in sync.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Boromir <boromir@squad.dev>
Closes #130

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Closes #133

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## Sprint 6: Code Quality

**Goal:** Eliminate analyzer warnings, enforce internal visibility on
Web feature types, expand test coverage, and migrate Unit.Tests into
Web.Tests.

---

## Merged Feature PRs

- Closes #137 — PR #144 — fix(quality): rename `ct` →
`cancellationToken` in all MediatR handlers
- Closes #138 — PR #145 — test: add unit tests for BlogPost, Result, and
ValidationBehavior (42 tests)
- Closes #139 — PR #146 — test: add unit tests for UserManagementHandler
(16 tests)
- Closes #140 — PR #147 — fix(quality): make Web feature types
`internal` (CA1515, ~28 types)
- Closes #141 — PR #148 — test: add unit tests for BlogPostMappings (22
tests)
- Closes #142 — PR #149 — fix(quality): add ConfigureAwait(false) and
specific exception catches
- Closes #143 — PR #150 — fix(quality): address Domain and
ServiceDefaults analyzer warnings
- Closes #151 — PR #152 — feat(tests): migrate Unit.Tests → Web.Tests
and remove Unit.Tests project

---

## Test Summary

- **80+ new tests** added across all PRs
- **105 Web.Tests passing** (0 failures)
- `DynamicProxyGenAssembly2` added to `InternalsVisibleTo` in Web.csproj
for NSubstitute support

## Notable Changes

- `Unit.Tests` project removed; all tests now live in `Web.Tests`
- ~28 Web feature types changed from `public` to `internal` (CA1515
compliance)
- All MediatR handler parameters renamed from `ct` to
`cancellationToken` for clarity
- `ConfigureAwait(false)` applied to all async calls in service layer
- Domain and ServiceDefaults analyzer warnings resolved

---

## Checklist

- [x] All sprint issues closed (#137#143, #151)
- [x] CI green (0 build errors)
- [x] 105 Web.Tests passing
- [x] Milestone at 100%

---------

Co-authored-by: Boromir <boromir@squad.dev>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 25, 2026 00:51
@github-actions github-actions Bot added the squad Squad triage inbox — Lead will assign to a member label Apr 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🏗️ PR Added to Squad Triage Queue

This PR has been labeled with squad and added to the triage queue.

Next steps:

  • The squad Lead will review and assign to an appropriate team member
  • A squad:member label will be added after triage

If you know which squad member should handle this, you can add the appropriate squad:member label yourself.

@mpaulosky
mpaulosky enabled auto-merge (squash) April 25, 2026 00:51
@mpaulosky
mpaulosky merged commit 1e1fb57 into main Apr 25, 2026
30 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Release PR that consolidates Sprint 6 “code quality” work: tightening Web-layer visibility, improving handler error/cancellation behavior, reorganizing/adding tests, and expanding release/docs automation + GitHub Pages content.

Changes:

  • Refined Web CQRS handlers (cancellation + exception handling, ConfigureAwait(false)), and reduced surface area by making many Web types internal with updated InternalsVisibleTo.
  • Added/adjusted test coverage across Web/Domain test projects and updated release workflows to run the new test projects.
  • Added/updated documentation (README + GitHub Pages index + dev blog posts) and improved release/project automation workflows.

Reviewed changes

Copilot reviewed 64 out of 67 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
tests/Web.Tests/Handlers/GetBlogPostsHandlerTests.cs Adds cache-exception test for GetBlogPosts handler.
tests/Web.Tests/Handlers/EditBlogPostHandlerTests.cs Adds cache-exception test for GetBlogPostById path.
tests/Web.Tests/Handlers/CreateBlogPostHandlerTests.cs Adds test asserting Create invalidates “all” but not “by id”.
tests/Web.Tests.Integration/Infrastructure/RedisFixture.cs Narrows helper visibility (CreateCacheServiceinternal).
tests/Unit.Tests/Unit.Tests.csproj Removes the Unit.Tests project file.
tests/Unit.Tests/Handlers/UserManagementHandlerTests.cs Adds unit tests for UserManagement handler (currently under removed project).
tests/Unit.Tests/Handlers/GetBlogPostsHandlerTests.cs Deletes prior Unit.Tests handler tests.
tests/Unit.Tests/Handlers/GetBlogPostByIdHandlerTests.cs Deletes prior Unit.Tests handler tests.
tests/Unit.Tests/Handlers/EditBlogPostHandlerTests.cs Deletes prior Unit.Tests handler tests.
tests/Unit.Tests/Handlers/DeleteBlogPostHandlerTests.cs Deletes prior Unit.Tests handler tests.
tests/Unit.Tests/Handlers/CreateBlogPostHandlerTests.cs Deletes prior Unit.Tests handler tests.
tests/Unit.Tests/GlobalUsings.cs Deletes Unit.Tests global usings.
tests/Unit.Tests/Data/BlogPostMappingsTests.cs Adds mapping tests (currently under removed project).
tests/Domain.Tests/Properties/AssemblyInfo.cs Adds CLS compliance attribute for Domain.Tests.
tests/Domain.Tests/GlobalUsings.cs Expands global usings (FluentValidation, MediatR, NSubstitute, etc.).
tests/Domain.Tests/Entities/BlogPostTests.cs Adds BlogPost entity tests to Domain.Tests.
tests/Domain.Tests/Domain.Tests.csproj Adds packages needed for new Domain.Tests coverage.
tests/Domain.Tests/Behaviors/ValidationBehaviorTests.cs Adds ValidationBehavior pipeline behavior tests.
tests/Domain.Tests/Abstractions/ResultTests.cs Adds Result/Result behavior tests.
src/Web/Security/RoleClaimsHelper.cs Reduces visibility to internal.
src/Web/Properties/AssemblyInfo.cs Adds CLS + expands InternalsVisibleTo list (incl. proxy gen).
src/Web/Infrastructure/Caching/IBlogPostCacheService.cs Reduces interface visibility to internal.
src/Web/Infrastructure/Caching/CachingServiceExtensions.cs Reduces extensions visibility to internal.
src/Web/Infrastructure/Caching/BlogPostCacheKeys.cs Reduces cache keys visibility to internal.
src/Web/Features/UserManagement/UserManagementHandler.cs Makes handler internal; improves cancellation/exception handling + ConfigureAwait(false).
src/Web/Features/UserManagement/RemoveRoleCommand.cs Reduces visibility to internal.
src/Web/Features/UserManagement/GetUsersWithRolesQuery.cs Reduces visibility to internal (query + DTO).
src/Web/Features/UserManagement/GetAvailableRolesQuery.cs Reduces visibility to internal (query + DTO).
src/Web/Features/UserManagement/AssignRoleCommand.cs Reduces visibility to internal.
src/Web/Features/BlogPosts/List/GetBlogPostsQuery.cs Reduces visibility to internal.
src/Web/Features/BlogPosts/List/GetBlogPostsHandler.cs Makes handler internal; improves cancellation/exception handling + ConfigureAwait(false).
src/Web/Features/BlogPosts/Edit/GetBlogPostByIdQuery.cs Reduces visibility to internal.
src/Web/Features/BlogPosts/Edit/EditBlogPostHandler.cs Makes handler internal; improves cancellation/exception handling + ConfigureAwait(false).
src/Web/Features/BlogPosts/Edit/EditBlogPostCommandValidator.cs Reduces visibility to internal.
src/Web/Features/BlogPosts/Edit/EditBlogPostCommand.cs Reduces visibility to internal.
src/Web/Features/BlogPosts/Delete/DeleteBlogPostHandler.cs Makes handler internal; improves cancellation/exception handling + ConfigureAwait(false).
src/Web/Features/BlogPosts/Delete/DeleteBlogPostCommandValidator.cs Reduces visibility to internal.
src/Web/Features/BlogPosts/Delete/DeleteBlogPostCommand.cs Reduces visibility to internal.
src/Web/Features/BlogPosts/Create/CreateBlogPostHandler.cs Makes handler internal; improves cancellation/exception handling + ConfigureAwait(false).
src/Web/Features/BlogPosts/Create/CreateBlogPostCommandValidator.cs Reduces visibility to internal.
src/Web/Features/BlogPosts/Create/CreateBlogPostCommand.cs Reduces visibility to internal.
src/Web/Data/MongoDbBlogPostRepository.cs Reduces repository visibility to internal.
src/Web/Data/BlogPostDto.cs Reduces DTO visibility to internal.
src/ServiceDefaults/Properties/AssemblyInfo.cs Adds CLS compliance attribute for ServiceDefaults.
src/ServiceDefaults/Extensions.cs Makes Path filtering case-insensitive in OpenTelemetry instrumentation.
src/Domain/Properties/AssemblyInfo.cs Adds CLS compliance + InternalsVisibleTo for Domain.Tests.
src/Domain/Behaviors/ValidationBehavior.cs Adds null-check for next delegate.
src/Domain/Abstractions/Result.cs Adds ToValue() and Result<T>.FromValue() helper.
docs/index.html Updates GitHub Pages landing page content to reflect current stack and releases.
docs/blog/index.md Adds dev blog index (table-driven).
docs/blog/2026-04-24-release-v1-2-0.md Adds release blog post for v1.2.0.
docs/blog/2026-04-24-release-v1-1-0.md Adds release blog post for v1.1.0.
docs/blog/2026-04-20-sprint-3-e2e-tests-ci-hardening.md Adds Sprint 3 blog post.
docs/blog/2026-04-20-sprint-2-cqrs-mediatr.md Adds Sprint 2 blog post.
docs/blog/2026-04-20-release-v1-0-0.md Adds v1.0.0 release blog post.
docs/blog/2026-04-18-myblog-project-kickoff.md Adds Sprint 1 kickoff post.
docs/SQUAD-COMMANDS.md Adds Squad commands documentation.
README.md Updates README with badges, stack, structure, blog + release history sections.
MyBlog.slnx Removes Unit.Tests project from the solution.
.squad/agents/gimli/history.md Adds a review note about ConfigureAwait(false) behavior.
.squad/agents/aragorn/charter.md Updates PR merge sequence to include Codecov check.
.github/workflows/squad-release.yml Updates release workflow test execution to new project layout.
.github/workflows/squad-mark-released.yml Adds manual dispatch + improves conditions/message.
.github/workflows/squad-insider-release.yml Updates insider release workflow test execution to new project layout.
.github/workflows/release-blog.yml Adjusts generated blog brief body formatting.
.github/workflows/milestone-release-decision.yml Fixes indentation/formatting for posted issue comments/bodies.
.github/workflows/milestone-blog.yml Fixes indentation/formatting for posted issue bodies.

Comment thread docs/blog/index.md
Comment on lines +7 to +14
| Date | Title | Tags |
|------|-------|------|
| 2026-04-24 | [Release: v1.2.0 — Redis Caching and L1/L2 Cache Strategy](./2026-04-24-release-v1-2-0.md) | release, v1.2.0, redis, caching, aspire, sprint-5 |
| 2026-04-24 | [Release: v1.1.0 — Blazor Theme System with TailwindCSS v4](./2026-04-24-release-v1-1-0.md) | release, v1.1.0, blazor, tailwind, theme, testing, sprint-4 |
| 2026-04-20 | [Release: v1.0.0 — Semantic Versioning and Production Readiness](./2026-04-20-release-v1-0-0.md) | release, semver, ci, devops |
| 2026-04-20 | [Sprint 3: E2E Testing and CI Hardening](./2026-04-20-sprint-3-e2e-tests-ci-hardening.md) | e2e, aspire, ci, testing, sprint-3 |
| 2026-04-20 | [Sprint 2: CQRS and MediatR Deep Dive](./2026-04-20-sprint-2-cqrs-mediatr.md) | cqrs, mediatr, testing, domain, sprint-2 |
| 2026-04-18 | [MyBlog Project Kickoff: Building with .NET 10 and Clean Architecture](./2026-04-18-myblog-project-kickoff.md) | aspire, blazor, clean-architecture, sprint-1 |

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

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

The blog index table won’t be picked up by blog-readme-sync.yml because the header/separator/rows use || ... (double leading pipes). The sync workflow regex matches the exact header | Date | Title | Tags | followed by the separator line and rows starting/ending with a single |. Update the table to use single-pipe markdown rows so README sync works.

Copilot uses AI. Check for mistakes.
Comment thread docs/blog/index.md
Comment on lines +9 to +39
| 2026-04-24 | [Release: v1.2.0 — Redis Caching and L1/L2 Cache Strategy](./2026-04-24-release-v1-2-0.md) | release, v1.2.0, redis, caching, aspire, sprint-5 |
| 2026-04-24 | [Release: v1.1.0 — Blazor Theme System with TailwindCSS v4](./2026-04-24-release-v1-1-0.md) | release, v1.1.0, blazor, tailwind, theme, testing, sprint-4 |
| 2026-04-20 | [Release: v1.0.0 — Semantic Versioning and Production Readiness](./2026-04-20-release-v1-0-0.md) | release, semver, ci, devops |
| 2026-04-20 | [Sprint 3: E2E Testing and CI Hardening](./2026-04-20-sprint-3-e2e-tests-ci-hardening.md) | e2e, aspire, ci, testing, sprint-3 |
| 2026-04-20 | [Sprint 2: CQRS and MediatR Deep Dive](./2026-04-20-sprint-2-cqrs-mediatr.md) | cqrs, mediatr, testing, domain, sprint-2 |
| 2026-04-18 | [MyBlog Project Kickoff: Building with .NET 10 and Clean Architecture](./2026-04-18-myblog-project-kickoff.md) | aspire, blazor, clean-architecture, sprint-1 |

## By Sprint

### Sprint 1: Foundation
- [MyBlog Project Kickoff: Building with .NET 10 and Clean Architecture](./2026-04-18-myblog-project-kickoff.md)

### Sprint 2: CQRS & MediatR
- [Sprint 2: CQRS and MediatR Deep Dive](./2026-04-20-sprint-2-cqrs-mediatr.md)

### Sprint 3: Testing & CI
- [Sprint 3: E2E Testing and CI Hardening](./2026-04-20-sprint-3-e2e-tests-ci-hardening.md)

### Sprint 4: Theme System
- [Release: v1.1.0 — Blazor Theme System with TailwindCSS v4](./2026-04-24-release-v1-1-0.md)

### Sprint 5: Caching
- [Release: v1.2.0 — Redis Caching and L1/L2 Cache Strategy](./2026-04-24-release-v1-2-0.md)

## Release Timeline

| Release | Date | Focus |
|---------|------|-------|
| [v1.0.0](./2026-04-20-release-v1-0-0.md) | 2026-04-20 | Semantic versioning, production readiness |
| [v1.1.0](./2026-04-24-release-v1-1-0.md) | 2026-04-24 | Blazor theme system, test reorganization |
| [v1.2.0](./2026-04-24-release-v1-2-0.md) | 2026-04-24 | Redis caching, distributed cache strategy |

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

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

The links in the blog index use ./... relative paths. blog-readme-sync.yml rewrites relative links by prefixing docs/blog/ but does not strip a leading ./, producing docs/blog/./... in README. Use links without the ./ prefix (e.g., (2026-04-24-release-v1-2-0.md)) to keep README links clean and consistent.

Suggested change
| 2026-04-24 | [Release: v1.2.0 — Redis Caching and L1/L2 Cache Strategy](./2026-04-24-release-v1-2-0.md) | release, v1.2.0, redis, caching, aspire, sprint-5 |
| 2026-04-24 | [Release: v1.1.0 — Blazor Theme System with TailwindCSS v4](./2026-04-24-release-v1-1-0.md) | release, v1.1.0, blazor, tailwind, theme, testing, sprint-4 |
| 2026-04-20 | [Release: v1.0.0 — Semantic Versioning and Production Readiness](./2026-04-20-release-v1-0-0.md) | release, semver, ci, devops |
| 2026-04-20 | [Sprint 3: E2E Testing and CI Hardening](./2026-04-20-sprint-3-e2e-tests-ci-hardening.md) | e2e, aspire, ci, testing, sprint-3 |
| 2026-04-20 | [Sprint 2: CQRS and MediatR Deep Dive](./2026-04-20-sprint-2-cqrs-mediatr.md) | cqrs, mediatr, testing, domain, sprint-2 |
| 2026-04-18 | [MyBlog Project Kickoff: Building with .NET 10 and Clean Architecture](./2026-04-18-myblog-project-kickoff.md) | aspire, blazor, clean-architecture, sprint-1 |
## By Sprint
### Sprint 1: Foundation
- [MyBlog Project Kickoff: Building with .NET 10 and Clean Architecture](./2026-04-18-myblog-project-kickoff.md)
### Sprint 2: CQRS & MediatR
- [Sprint 2: CQRS and MediatR Deep Dive](./2026-04-20-sprint-2-cqrs-mediatr.md)
### Sprint 3: Testing & CI
- [Sprint 3: E2E Testing and CI Hardening](./2026-04-20-sprint-3-e2e-tests-ci-hardening.md)
### Sprint 4: Theme System
- [Release: v1.1.0 — Blazor Theme System with TailwindCSS v4](./2026-04-24-release-v1-1-0.md)
### Sprint 5: Caching
- [Release: v1.2.0 — Redis Caching and L1/L2 Cache Strategy](./2026-04-24-release-v1-2-0.md)
## Release Timeline
| Release | Date | Focus |
|---------|------|-------|
| [v1.0.0](./2026-04-20-release-v1-0-0.md) | 2026-04-20 | Semantic versioning, production readiness |
| [v1.1.0](./2026-04-24-release-v1-1-0.md) | 2026-04-24 | Blazor theme system, test reorganization |
| [v1.2.0](./2026-04-24-release-v1-2-0.md) | 2026-04-24 | Redis caching, distributed cache strategy |
| 2026-04-24 | [Release: v1.2.0 — Redis Caching and L1/L2 Cache Strategy](2026-04-24-release-v1-2-0.md) | release, v1.2.0, redis, caching, aspire, sprint-5 |
| 2026-04-24 | [Release: v1.1.0 — Blazor Theme System with TailwindCSS v4](2026-04-24-release-v1-1-0.md) | release, v1.1.0, blazor, tailwind, theme, testing, sprint-4 |
| 2026-04-20 | [Release: v1.0.0 — Semantic Versioning and Production Readiness](2026-04-20-release-v1-0-0.md) | release, semver, ci, devops |
| 2026-04-20 | [Sprint 3: E2E Testing and CI Hardening](2026-04-20-sprint-3-e2e-tests-ci-hardening.md) | e2e, aspire, ci, testing, sprint-3 |
| 2026-04-20 | [Sprint 2: CQRS and MediatR Deep Dive](2026-04-20-sprint-2-cqrs-mediatr.md) | cqrs, mediatr, testing, domain, sprint-2 |
| 2026-04-18 | [MyBlog Project Kickoff: Building with .NET 10 and Clean Architecture](2026-04-18-myblog-project-kickoff.md) | aspire, blazor, clean-architecture, sprint-1 |
## By Sprint
### Sprint 1: Foundation
- [MyBlog Project Kickoff: Building with .NET 10 and Clean Architecture](2026-04-18-myblog-project-kickoff.md)
### Sprint 2: CQRS & MediatR
- [Sprint 2: CQRS and MediatR Deep Dive](2026-04-20-sprint-2-cqrs-mediatr.md)
### Sprint 3: Testing & CI
- [Sprint 3: E2E Testing and CI Hardening](2026-04-20-sprint-3-e2e-tests-ci-hardening.md)
### Sprint 4: Theme System
- [Release: v1.1.0 — Blazor Theme System with TailwindCSS v4](2026-04-24-release-v1-1-0.md)
### Sprint 5: Caching
- [Release: v1.2.0 — Redis Caching and L1/L2 Cache Strategy](2026-04-24-release-v1-2-0.md)
## Release Timeline
| Release | Date | Focus |
|---------|------|-------|
| [v1.0.0](2026-04-20-release-v1-0-0.md) | 2026-04-20 | Semantic versioning, production readiness |
| [v1.1.0](2026-04-24-release-v1-1-0.md) | 2026-04-24 | Blazor theme system, test reorganization |
| [v1.2.0](2026-04-24-release-v1-2-0.md) | 2026-04-24 | Redis caching, distributed cache strategy |

Copilot uses AI. Check for mistakes.
Comment thread README.md
Comment on lines +168 to +176
<!-- BLOG_START -->
| Date | Title | Tags |
|------|-------|------|
| 2026-04-24 | [Release: v1.2.0 — Redis Caching and L1/L2 Cache Strategy](docs/blog/2026-04-24-release-v1-2-0.md) | release, v1.2.0, redis, caching, aspire, sprint-5 |
| 2026-04-24 | [Release: v1.1.0 — Blazor Theme System with TailwindCSS v4](docs/blog/2026-04-24-release-v1-1-0.md) | release, v1.1.0, blazor, tailwind, theme, testing, sprint-4 |
| 2026-04-20 | [Release: v1.0.0 — Semantic Versioning and Production Readiness](docs/blog/2026-04-20-release-v1-0-0.md) | release, semver, ci, devops |
| 2026-04-20 | [Sprint 3: E2E Testing and CI Hardening](docs/blog/2026-04-20-sprint-3-e2e-tests-ci-hardening.md) | e2e, aspire, ci, testing, sprint-3 |
| 2026-04-20 | [Sprint 2: CQRS and MediatR Deep Dive](docs/blog/2026-04-20-sprint-2-cqrs-mediatr.md) | cqrs, mediatr, testing, domain, sprint-2 |
<!-- BLOG_END -->

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

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

The Dev Blog table header here uses || Date | Title | Tags | (double leading pipes). If docs/blog/index.md is fixed to the single-pipe format expected by blog-readme-sync.yml, this section will be overwritten on the next sync anyway. Consider matching the expected single-pipe header/rows now to avoid a broken-looking table until the sync runs.

Copilot uses AI. Check for mistakes.
public static Result<T> FromValue(T? value)
{
if (value is null)
return Fail("Value cannot be null.");

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

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

Result.FromValue<T>(...) fails with the message "Provided value is null.", but the newly added Result<T>.FromValue(...) fails with a different message ("Value cannot be null."). Having two FromValue APIs with inconsistent null-error messages makes failures harder to reason about and can lead to brittle tests. Consider reusing the same message (or delegating one implementation to the other) so null handling is consistent.

Suggested change
return Fail("Value cannot be null.");
return Fail("Provided value is null.");

Copilot uses AI. Check for mistakes.
dotnet test tests/Domain.Tests --configuration Release --no-build --no-restore \
--logger "trx;LogFileName=domain-results.trx"
dotnet test tests/Web.Tests --configuration Release --no-build --no-restore \
--logger "trx;LogFileName=web-results.trx"

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

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

The release workflow now runs Architecture/Domain/Web/Web.Integration tests, but it still skips tests/Web.Tests.Bunit (which is part of the repo’s main squad-test.yml suite). Consider adding the bUnit project here as well so releases can’t ship with failing component tests.

Suggested change
--logger "trx;LogFileName=web-results.trx"
--logger "trx;LogFileName=web-results.trx"
dotnet test tests/Web.Tests.Bunit --configuration Release --no-build --no-restore \
--logger "trx;LogFileName=web-bunit-results.trx"

Copilot uses AI. Check for mistakes.
Comment thread MyBlog.slnx
@@ -9,7 +9,6 @@
<Project Path="tests/AppHost.Tests/AppHost.Tests.csproj" />
<Project Path="tests/Architecture.Tests/Architecture.Tests.csproj" />
<Project Path="tests/Domain.Tests/Domain.Tests.csproj" />

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

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

Unit.Tests is removed from the solution, but this PR still adds/keeps source files under tests/Unit.Tests/. Without a Unit.Tests.csproj referenced by the solution/CI, those tests won’t compile or run. Either reintroduce the project reference or move these tests into the active test projects (e.g., Domain.Tests/Web.Tests) and delete the orphaned folder.

Suggested change
<Project Path="tests/Domain.Tests/Domain.Tests.csproj" />
<Project Path="tests/Domain.Tests/Domain.Tests.csproj" />
<Project Path="tests/Unit.Tests/Unit.Tests.csproj" />

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +28
public class UserManagementHandlerTests
{
private readonly IConfiguration _config = Substitute.For<IConfiguration>();
private readonly IHttpClientFactory _httpFactory = Substitute.For<IHttpClientFactory>();
private readonly UserManagementHandler _handler;

public UserManagementHandlerTests()
{
_config["Auth0:ManagementApiDomain"].Returns((string?)null);
_handler = new UserManagementHandler(_config, _httpFactory);
}

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

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

This test file lives under tests/Unit.Tests/, but tests/Unit.Tests/Unit.Tests.csproj was removed in this PR and the solution no longer references Unit.Tests. As-is, these tests won’t be built or executed by CI. Move them into an active test project (e.g., tests/Web.Tests or tests/Domain.Tests) or restore the Unit.Tests project.

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +20
namespace Unit.Data;

public class BlogPostMappingsTests
{
[Fact]
public void ToDto_MapsAllFields_Correctly()
{
// Arrange
var post = BlogPost.Create("Test Title", "Test Content", "Test Author");

// Act

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

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

This test file is under tests/Unit.Tests/, but the Unit.Tests project file was removed and the solution no longer includes it. These tests will be orphaned (not compiled/run) unless they’re moved into an existing test project (e.g., tests/Web.Tests for Web mappings) or Unit.Tests is restored.

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +101
### IBlogPostCacheService
```csharp
public interface IBlogPostCacheService
{
Task<IEnumerable<BlogPostResponse>> GetAllBlogPostsAsync(CancellationToken cancellationToken);
Task<BlogPostResponse?> GetBlogPostByIdAsync(int id, CancellationToken cancellationToken);
Task InvalidateAsync(int? blogPostId = null);
}
```

### Two-Tier Implementation
```csharp
public class BlogPostCacheService : IBlogPostCacheService
{
private readonly IMemoryCache _l1Cache;
private readonly IDistributedCache _l2Cache;
private readonly IBlogPostRepository _repository;

public async Task<IEnumerable<BlogPostResponse>> GetAllBlogPostsAsync(CancellationToken cancellationToken)
{
const string key = "all_blog_posts";

// Check L1
if (_l1Cache.TryGetValue(key, out var cached))
return cached as IEnumerable<BlogPostResponse>;

// Check L2
var json = await _l2Cache.GetStringAsync(key, cancellationToken);
if (json != null)
{
var result = JsonSerializer.Deserialize<IEnumerable<BlogPostResponse>>(json);
_l1Cache.Set(key, result, TimeSpan.FromHours(1));
return result;
}

// Fetch from DB, populate both caches
var posts = await _repository.GetAllAsync(cancellationToken);
var response = posts.Select(p => new BlogPostResponse { /* ... */ });
var json = JsonSerializer.Serialize(response);
await _l2Cache.SetStringAsync(key, json, new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1)
}, cancellationToken);
_l1Cache.Set(key, response, TimeSpan.FromHours(1));

return response;
}
}
```

### Query Handler Integration
```csharp
public class GetAllBlogPostsQueryHandler : IRequestHandler<GetAllBlogPostsQuery, IEnumerable<BlogPostResponse>>
{
private readonly IBlogPostCacheService _cacheService;

public async Task<IEnumerable<BlogPostResponse>> Handle(GetAllBlogPostsQuery request, CancellationToken cancellationToken)
{
return await _cacheService.GetAllBlogPostsAsync(cancellationToken);
}
}
```

### Invalidation on Mutation
```csharp
public class CreateBlogPostCommandHandler : IRequestHandler<CreateBlogPostCommand, BlogPostResponse>
{
private readonly IBlogPostRepository _repository;
private readonly IBlogPostCacheService _cacheService;

public async Task<BlogPostResponse> Handle(CreateBlogPostCommand request, CancellationToken cancellationToken)
{
var entity = await _repository.CreateAsync(/* ... */, cancellationToken);
await _cacheService.InvalidateAsync(); // Clear all blog post caches
return MapToResponse(entity);
}
}
```

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

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

The release blog post’s IBlogPostCacheService / handler integration code samples don’t match the actual code in this PR (e.g., different method names/signatures like GetAllBlogPostsAsync, InvalidateAsync, and handler names like GetAllBlogPostsQueryHandler). If these snippets are meant to document the current implementation, they should be updated to reflect the real GetOrFetchAllAsync/GetOrFetchByIdAsync + InvalidateAllAsync/InvalidateByIdAsync API to avoid confusing readers.

Copilot uses AI. Check for mistakes.
dotnet test tests/Domain.Tests --configuration Release --no-build --no-restore \
--logger "trx;LogFileName=domain-results.trx"
dotnet test tests/Web.Tests --configuration Release --no-build --no-restore \
--logger "trx;LogFileName=web-results.trx"

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

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

The insider release workflow runs Architecture/Domain/Web/Web.Integration tests but skips tests/Web.Tests.Bunit (part of the repo’s standard test suite). Consider adding the bUnit project so pre-releases also validate component tests.

Suggested change
--logger "trx;LogFileName=web-results.trx"
--logger "trx;LogFileName=web-results.trx"
dotnet test tests/Web.Tests.Bunit --configuration Release --no-build --no-restore \
--logger "trx;LogFileName=web-bunit-results.trx"

Copilot uses AI. Check for mistakes.
@codecov

codecov Bot commented Apr 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 32.98969% with 65 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.12%. Comparing base (e522b3f) to head (e7035b1).
⚠️ Report is 30 commits behind head on main.

Files with missing lines Patch % Lines
...b/Features/UserManagement/UserManagementHandler.cs 0.00% 40 Missing ⚠️
...Web/Features/BlogPosts/Edit/EditBlogPostHandler.cs 47.05% 9 Missing ⚠️
src/Domain/Abstractions/Result.cs 0.00% 3 Missing and 1 partial ⚠️
...Features/BlogPosts/Create/CreateBlogPostHandler.cs 50.00% 4 Missing ⚠️
...Features/BlogPosts/Delete/DeleteBlogPostHandler.cs 55.55% 4 Missing ⚠️
...Web/Features/BlogPosts/List/GetBlogPostsHandler.cs 50.00% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #161      +/-   ##
==========================================
- Coverage   76.93%   72.12%   -4.82%     
==========================================
  Files          43       43              
  Lines         672      721      +49     
  Branches      111      112       +1     
==========================================
+ Hits          517      520       +3     
- Misses        105      150      +45     
- Partials       50       51       +1     
Files with missing lines Coverage Δ
src/Domain/Behaviors/ValidationBehavior.cs 100.00% <100.00%> (ø)
src/Web/Data/BlogPostDto.cs 100.00% <100.00%> (ø)
src/Web/Data/MongoDbBlogPostRepository.cs 100.00% <100.00%> (ø)
...Features/BlogPosts/Create/CreateBlogPostCommand.cs 100.00% <100.00%> (ø)
...BlogPosts/Create/CreateBlogPostCommandValidator.cs 100.00% <ø> (ø)
...Features/BlogPosts/Delete/DeleteBlogPostCommand.cs 100.00% <100.00%> (ø)
...BlogPosts/Delete/DeleteBlogPostCommandValidator.cs 100.00% <ø> (ø)
...Web/Features/BlogPosts/Edit/EditBlogPostCommand.cs 100.00% <100.00%> (ø)
...res/BlogPosts/Edit/EditBlogPostCommandValidator.cs 100.00% <ø> (ø)
...eb/Features/BlogPosts/Edit/GetBlogPostByIdQuery.cs 100.00% <100.00%> (ø)
... and 13 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

Labels

squad Squad triage inbox — Lead will assign to a member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants