From db929525c6af5d2017d9bfe5710331bcf0fbc203 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:20:20 +0000 Subject: [PATCH 1/3] Bump the all-actions group with 30 updates Bumps Aspire.Hosting.Redis from 13.4.2 to 13.4.6 Bumps Aspire.Hosting.Testing from 13.4.2 to 13.4.6 Bumps Aspire.MongoDB.Driver from 13.4.2 to 13.4.6 Bumps Aspire.StackExchange.Redis from 13.4.2 to 13.4.6 Bumps Auth0.AspNetCore.Authentication from 1.7.0 to 1.8.0 Bumps Auth0.ManagementApi from 8.4.0 to 8.6.0 Bumps gitversion.tool from 6.7.0 to 6.8.1 Bumps MediatR from 14.1.0 to 14.2.0 Bumps Microsoft.AspNetCore.Mvc.Testing from 10.0.8 to 10.0.9 Bumps Microsoft.AspNetCore.OpenApi from 10.0.8 to 10.0.9 Bumps Microsoft.AspNetCore.SignalR.Client from 10.0.8 to 10.0.9 Bumps Microsoft.Extensions.Caching.StackExchangeRedis from 10.0.8 to 10.0.9 Bumps Microsoft.Extensions.Configuration from 10.0.8 to 10.0.9 Bumps Microsoft.Extensions.Configuration.Abstractions from 10.0.8 to 10.0.9 Bumps Microsoft.Extensions.Configuration.Binder from 10.0.8 to 10.0.9 Bumps Microsoft.Extensions.DependencyInjection from 10.0.8 to 10.0.9 Bumps Microsoft.Extensions.DependencyInjection.Abstractions from 10.0.8 to 10.0.9 Bumps Microsoft.Extensions.Http.Resilience from 10.6.0 to 10.7.0 Bumps Microsoft.Extensions.Logging.Abstractions from 10.0.8 to 10.0.9 Bumps Microsoft.Extensions.Options from 10.0.8 to 10.0.9 Bumps Microsoft.Extensions.Options.ConfigurationExtensions from 10.0.8 to 10.0.9 Bumps Microsoft.Extensions.ServiceDiscovery from 10.6.0 to 10.7.0 Bumps Microsoft.NET.Test.Sdk from 18.6.0 to 18.7.0 Bumps Microsoft.Playwright from 1.60.0 to 1.61.0 Bumps OpenTelemetry.Exporter.OpenTelemetryProtocol from 1.15.3 to 1.16.0 Bumps OpenTelemetry.Extensions.Hosting from 1.15.3 to 1.16.0 Bumps OpenTelemetry.Instrumentation.AspNetCore from 1.15.2 to 1.16.0 Bumps OpenTelemetry.Instrumentation.Http from 1.15.1 to 1.16.0 Bumps SixLabors.ImageSharp from 3.1.12 to 4.0.0 Bumps Testcontainers.MongoDb from 4.12.0 to 4.13.0 --- updated-dependencies: - dependency-name: Aspire.Hosting.Redis dependency-version: 13.4.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: Aspire.Hosting.Testing dependency-version: 13.4.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: Aspire.MongoDB.Driver dependency-version: 13.4.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: Aspire.StackExchange.Redis dependency-version: 13.4.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: Auth0.AspNetCore.Authentication dependency-version: 1.8.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions - dependency-name: Auth0.ManagementApi dependency-version: 8.6.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions - dependency-name: gitversion.tool dependency-version: 6.8.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions - dependency-name: MediatR dependency-version: 14.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions - dependency-name: Microsoft.AspNetCore.Mvc.Testing dependency-version: 10.0.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: Microsoft.AspNetCore.OpenApi dependency-version: 10.0.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: Microsoft.AspNetCore.SignalR.Client dependency-version: 10.0.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: Microsoft.Extensions.Caching.StackExchangeRedis dependency-version: 10.0.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: Microsoft.Extensions.Configuration dependency-version: 10.0.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: Microsoft.Extensions.Configuration.Abstractions dependency-version: 10.0.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: Microsoft.Extensions.Configuration.Binder dependency-version: 10.0.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: Microsoft.Extensions.DependencyInjection dependency-version: 10.0.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: Microsoft.Extensions.DependencyInjection.Abstractions dependency-version: 10.0.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: Microsoft.Extensions.Http.Resilience dependency-version: 10.7.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions - dependency-name: Microsoft.Extensions.Logging.Abstractions dependency-version: 10.0.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: Microsoft.Extensions.Options dependency-version: 10.0.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: Microsoft.Extensions.Options.ConfigurationExtensions dependency-version: 10.0.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-actions - dependency-name: Microsoft.Extensions.ServiceDiscovery dependency-version: 10.7.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions - dependency-name: Microsoft.NET.Test.Sdk dependency-version: 18.7.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions - dependency-name: Microsoft.Playwright dependency-version: 1.61.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions - dependency-name: OpenTelemetry.Exporter.OpenTelemetryProtocol dependency-version: 1.16.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions - dependency-name: OpenTelemetry.Extensions.Hosting dependency-version: 1.16.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions - dependency-name: OpenTelemetry.Instrumentation.AspNetCore dependency-version: 1.16.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions - dependency-name: OpenTelemetry.Instrumentation.Http dependency-version: 1.16.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions - dependency-name: SixLabors.ImageSharp dependency-version: 4.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: all-actions - dependency-name: Testcontainers.MongoDb dependency-version: 4.13.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-actions ... Signed-off-by: dependabot[bot] --- .config/dotnet-tools.json | 4 +- Directory.Packages.props | 56 +++++++++---------- src/AppHost/AppHost.csproj | 2 +- .../Persistence.MongoDb.csproj | 1 + 4 files changed, 32 insertions(+), 31 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 6e79b60..bd70323 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,10 +3,10 @@ "isRoot": true, "tools": { "gitversion.tool": { - "version": "6.7.0", + "version": "6.8.1", "commands": [ "dotnet-gitversion" ] } } -} +} \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props index 7484bea..7aab217 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,42 +4,42 @@ - - - + + + - - - + + + - + - + - - + + - - - - - - - - + + + + + + + + - + @@ -47,17 +47,17 @@ - - + + - + - - - - - - + + + + + + diff --git a/src/AppHost/AppHost.csproj b/src/AppHost/AppHost.csproj index d44ec40..f5a4b51 100644 --- a/src/AppHost/AppHost.csproj +++ b/src/AppHost/AppHost.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/Persistence.MongoDb/Persistence.MongoDb.csproj b/src/Persistence.MongoDb/Persistence.MongoDb.csproj index bf06ab0..20109e3 100644 --- a/src/Persistence.MongoDb/Persistence.MongoDb.csproj +++ b/src/Persistence.MongoDb/Persistence.MongoDb.csproj @@ -5,6 +5,7 @@ + From b96f1b85bbb1afc258c495c6eeb7dc0a330afad7 Mon Sep 17 00:00:00 2001 From: mpaulosky <60372079+mpaulosky@users.noreply.github.com> Date: Sun, 5 Jul 2026 13:57:19 -0700 Subject: [PATCH 2/3] fix: pin SixLabors.ImageSharp to 3.1.12 and fix Auth0.ManagementApi 8.6.0 breaking change - SixLabors.ImageSharp 4.0.0 requires a paid commercial license; pin to last free 3.1.12 release to unblock Build Solution / build (ubuntu-latest) / Analyze (csharp) checks. - Auth0.ManagementApi 8.6.0 (bumped alongside the actions group update) removed the UserDateSchema wrapper type; LastLogin is now DateTime? directly on GetUserResponseContent/UserResponseSchema. Update ParseLastLogin in UserManagementService accordingly. lint-markdown, lint-yaml, and markdownlint failures on this PR are pre-existing on dev (confirmed via recent dev push runs) and unrelated to this branch's diff, so left untouched per scope. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .config/dotnet-tools.json | 2 +- Directory.Packages.props | 5 +++-- .../Features/Admin/Users/UserManagementService.cs | 12 ++++-------- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index bd70323..3e7fbed 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -9,4 +9,4 @@ ] } } -} \ No newline at end of file +} diff --git a/Directory.Packages.props b/Directory.Packages.props index 7aab217..1ef6c90 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -17,7 +17,8 @@ - + + @@ -64,4 +65,4 @@ - + \ No newline at end of file diff --git a/src/Web/Features/Admin/Users/UserManagementService.cs b/src/Web/Features/Admin/Users/UserManagementService.cs index d9dcf1e..eef79ed 100644 --- a/src/Web/Features/Admin/Users/UserManagementService.cs +++ b/src/Web/Features/Admin/Users/UserManagementService.cs @@ -489,13 +489,9 @@ private async Task> GetRoleMapAsync(CancellationToken }; /// - /// Safely converts a last-login field to a nullable - /// , returning if the value is absent - /// or unparseable. + /// Converts a nullable last-login to a nullable + /// , returning if the value is absent. /// - private static DateTimeOffset? ParseLastLogin(UserDateSchema? lastLogin) - { - if (lastLogin is null) return null; - return lastLogin.TryGetString(out var s) && DateTimeOffset.TryParse(s, out var dto) ? dto : null; - } + private static DateTimeOffset? ParseLastLogin(DateTime? lastLogin) => + lastLogin is null ? null : new DateTimeOffset(DateTime.SpecifyKind(lastLogin.Value, DateTimeKind.Utc)); } From b459366d08245fecbfa4e93dba20c24f31e62a13 Mon Sep 17 00:00:00 2001 From: mpaulosky <60372079+mpaulosky@users.noreply.github.com> Date: Sun, 5 Jul 2026 14:15:25 -0700 Subject: [PATCH 3/3] fix: resolve yamllint and markdownlint violations (#301) - Strip trailing whitespace and extra blank lines in workflow YAML files, and add missing trailing newline to code-metrics.yml so yamllint (matching lint-yaml.yml CI config) passes with 0 errors. - Auto-fix markdownlint-cli2 violations (blank lines around lists/ headings/fences) across docs, prompts, and README files. - Add missing language hints to fenced code blocks (MD040), fix a heading-level skip (MD001), a missing blank line between adjacent lists (MD032), and a missing opening fence in xunit-integration.prompt.md (MD046). - Soft-wrap long single-line paragraphs exceeding the 400-character limit (MD013) in blog posts and research docs without changing their rendered content. markdownlint-cli2 and yamllint both now report 0 errors locally, mirroring lint-markdown.yml and lint-yaml.yml. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/dependabot.yml | 2 +- .github/instructions/copilot-instructions.md | 2 + .../instructions/git-commit-instructions.md | 10 +-- .github/instructions/markdown.instructions.md | 2 +- .../instructions/mongo-dba.instructions.md | 4 ++ .github/prompts/build-repair.prompt.md | 70 ++++++++++--------- .../prompts/copyright-header-update-prompt.md | 1 - .github/prompts/create-readme.prompt.md | 18 ++--- .github/prompts/editorconfig.prompt.md | 7 +- .../structured-autonomy-generate.prompt.md | 11 +++ .../structured-autonomy-plan.prompt.md | 7 +- .github/prompts/structured-autonomy.md | 11 ++- .github/prompts/xunit-integration.prompt.md | 4 ++ .github/prompts/xunit.prompt.md | 26 +++---- .github/pull_request_template.md | 5 ++ .github/workflows/code-metrics.yml | 2 +- .../workflows/squad-dependabot-auto-merge.yml | 1 - .github/workflows/squad-label-enforce.yml | 8 +-- .../workflows/squad-main-from-dev-guard.yml | 1 - .github/workflows/squad-pr-auto-label.yml | 2 +- .../squad-standard-lint-markdown.yml | 1 - .../workflows/squad-standard-lint-yaml.yml | 1 - AGENTS.md | 8 ++- CONTRIBUTING.md | 17 +++-- README.md | 2 +- docs/ARCHITECTURE.md | 12 ++-- docs/CODE_OF_CONDUCT.md | 20 +++--- docs/FEATURES.md | 8 +-- docs/New Work process.md | 12 ++-- docs/TESTING.md | 5 +- docs/THEMING.md | 4 +- ...-27-apphost-aspire-playwright-e2e-tests.md | 4 ++ docs/blog/2026-03-30-release-v0-2-0.md | 8 ++- docs/blog/2026-04-01-release-v0-3-0.md | 3 +- docs/blog/2026-04-01-release-v0-4-0.md | 3 +- docs/blog/2026-04-02-release-v0-5-0.md | 6 +- docs/blog/2026-04-02-release-v0-6-0.md | 7 +- docs/blog/2026-04-04-release-v0-7-0.md | 3 +- docs/features/admin-user-management.md | 9 +++ docs/research/github-github-sdk.md | 9 +-- docs/research/tailwindcss-com.md | 4 +- docs/session-4-summary.md | 26 +++++++ src/Web/Auth/README.md | 3 + src/Web/Hubs/README.md | 25 +++++++ .../Helpers/README.md | 19 +++++ tests/Web.Tests.Bunit/Issues/README.md | 26 +++++++ tests/Web.Tests.Bunit/Issues/TESTING_GUIDE.md | 24 ++++++- .../Web.Tests.Bunit/Issues/TEST_CASE_LIST.md | 22 ++++++ tests/Web.Tests.Bunit/Issues/TEST_SUMMARY.md | 23 ++++++ tests/Web.Tests.Bunit/Pages/Admin/README.md | 37 ++++++++-- tests/Web.Tests.Bunit/Pages/TEST_SUMMARY.md | 38 +++++++--- .../Shared/HOW_TO_ADD_TESTS.md | 21 ++++++ .../Shared/SHARED_COMPONENT_TESTS_SUMMARY.md | 29 ++++++++ .../Shared/TEST_QUICK_REFERENCE.md | 29 ++++++++ tests/Web.Tests.Bunit/Theme/INDEX.md | 26 +++++++ .../Web.Tests.Bunit/Theme/QUICK_REFERENCE.md | 19 ++++- .../Theme/TEST_COVERAGE_MAP.md | 44 ++++++++++-- tests/Web.Tests.Bunit/Theme/TEST_SUMMARY.md | 30 +++++++- tests/Web.Tests/Services/README.md | 7 ++ 59 files changed, 628 insertions(+), 160 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4edfb9e..fbd6f77 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,7 +6,7 @@ version: 2 updates: - + # Maintain dependencies for GitHub Actions - package-ecosystem: "github-actions" directory: "/" diff --git a/.github/instructions/copilot-instructions.md b/.github/instructions/copilot-instructions.md index ba1b8e0..90fac1d 100644 --- a/.github/instructions/copilot-instructions.md +++ b/.github/instructions/copilot-instructions.md @@ -36,9 +36,11 @@ These instructions define the required coding, architecture, and project rules f - **Use .editorconfig:** `true` - **Preferred Modifier Order:** `public`, `private`, `protected`, `internal`, `static`, `readonly`, `const` - _Example:_ + ```csharp public static readonly int MY_CONST = 42; ``` + - **Use Explicit Type:** `true` (except where `var` improves clarity) - **Use Var:** `true` (when the type is obvious) - **Prefer Null Check:** diff --git a/.github/instructions/git-commit-instructions.md b/.github/instructions/git-commit-instructions.md index a87d2d3..fe04d91 100644 --- a/.github/instructions/git-commit-instructions.md +++ b/.github/instructions/git-commit-instructions.md @@ -6,7 +6,7 @@ This document defines the required conventions and best practices for writing Gi All commit messages **must** follow this structure: -``` +```text (): @@ -46,7 +46,7 @@ The scope should be the name of the affected project, folder, or feature (e.g., ## Examples -``` +```text feat(Web): add user authentication with Auth0 Implements login and logout functionality using Auth0. @@ -54,13 +54,13 @@ Updates navigation bar to show user info when authenticated. Fixes #42 ``` -``` +```text fix(Mongo): handle null reference in BlogRepository Adds null checks to prevent exceptions when querying missing documents. ``` -``` +```text docs(CONTRIBUTING): update testing section for Playwright Adds Playwright usage instructions and links to documentation. @@ -71,4 +71,4 @@ Adds Playwright usage instructions and links to documentation. - Group related changes in a single commit. - Separate unrelated changes into different commits. - Use English for all commit messages. -- Reference issues and pull requests when relevant. \ No newline at end of file +- Reference issues and pull requests when relevant. diff --git a/.github/instructions/markdown.instructions.md b/.github/instructions/markdown.instructions.md index 724815d..7f866fe 100644 --- a/.github/instructions/markdown.instructions.md +++ b/.github/instructions/markdown.instructions.md @@ -23,7 +23,7 @@ Follow these guidelines for formatting and structuring your markdown content: - **Headings**: Use `##` for H2 and `###` for H3. Ensure that headings are used in a hierarchical manner. Recommend restructuring if content includes H4, and more strongly recommend for H5. - **Lists**: Use `-` for bullet points and `1.` for numbered lists. Indent nested lists with two spaces. -- **Code Blocks**: Use triple backticks (`) to create fenced code blocks. Specify the language after the opening backticks for syntax highlighting (e.g., `csharp). +- **Code Blocks**: Use triple backticks (`) to create fenced code blocks. Specify the language after the opening backticks for syntax highlighting (e.g.,`csharp). - **Links**: Use `[link text](URL)` for links. Ensure that the link text is descriptive and the URL is valid. - **Images**: Use `![alt text](image URL)` for images. Include a brief description of the image in the alt text. - **Tables**: Use `|` to create tables. Ensure that columns are properly aligned and headers are included. diff --git a/.github/instructions/mongo-dba.instructions.md b/.github/instructions/mongo-dba.instructions.md index 02705a9..7125310 100644 --- a/.github/instructions/mongo-dba.instructions.md +++ b/.github/instructions/mongo-dba.instructions.md @@ -6,9 +6,11 @@ description: 'Instructions for customizing GitHub Copilot behavior for MONGODB D # MongoDB DBA Chat Mode Instructions ## Purpose + These instructions guide GitHub Copilot to provide expert assistance for MongoDB Database Administrator (DBA) tasks when the mongodb-dba.agent.md chat mode is active. ## Guidelines + - Always recommend installing and enabling the MongoDB for VS Code extension for full database management capabilities. - Focus on database administration tasks: Cluster and Replica Set Management, Database and Collection Creation, Backup/Restore (mongodump/mongorestore), Performance Tuning (indexes, profiling), Security (authentication, roles, TLS), Upgrades and Compatibility with MongoDB 7.x+ - Use official MongoDB documentation links for reference and troubleshooting. @@ -17,9 +19,11 @@ These instructions guide GitHub Copilot to provide expert assistance for MongoDB - Encourage secure, auditable, and performance-oriented solutions (e.g., enable auditing, use SCRAM-SHA authentication). ## Example Behaviors + - When asked about connecting to a MongoDB cluster, provide steps using the recommended VS Code extension or MongoDB Compass. - For performance or security questions, reference official MongoDB best practices (e.g., index strategies, role-based access control). - If a feature is deprecated in MongoDB 7.x+, warn the user and suggest alternatives (e.g., ensureIndex → createIndexes). ## Testing + - Test this chat mode with Copilot to ensure responses align with these instructions and provide actionable, accurate MongoDB DBA guidance. diff --git a/.github/prompts/build-repair.prompt.md b/.github/prompts/build-repair.prompt.md index e1ee185..eb0914a 100644 --- a/.github/prompts/build-repair.prompt.md +++ b/.github/prompts/build-repair.prompt.md @@ -10,39 +10,45 @@ agent: agent ## Instructions 1. **Locate Solution File** - - Check for `*.slnx` file in current directory - - If found, continue to next step - - If not found, run: `cd ..` and check again - - Repeat until `*.slnx` file is found - -2. **Restore Dependencies** - - Run: `dotnet restore` - -3. **Build Solution** - - Run: `dotnet build --no-restore` - - Capture all build output, including errors and warnings. - -4. **Error & Warning Resolution** - - For each error or warning in the build output: - - Identify the affected file and line number. - - Research the error/warning code and message. - - Apply the recommended fix to the codebase. - - Rebuild the solution to verify the fix. - - Repeat until the build completes with zero errors and warnings. - -5. **Verification** - - Ensure the final build output shows “Build succeeded” and no warnings. - - Document any changes made to resolve issues. - -6. **Testing** - - Run unit tests to ensure the solution works as expected. - - If tests fail, identify and fix the issues in the codebase. - - Rebuild and retest until all tests pass. - -7. **Documentation** - - Create a `build-log.txt` file in the solution directory. - - Log all build outputs, error resolutions, and changes made. +- Check for `*.slnx` file in current directory +- If found, continue to next step +- If not found, run: `cd ..` and check again +- Repeat until `*.slnx` file is found + +1. **Restore Dependencies** + +- Run: `dotnet restore` + +1. **Build Solution** + +- Run: `dotnet build --no-restore` +- Capture all build output, including errors and warnings. + +1. **Error & Warning Resolution** + +- For each error or warning in the build output: + - Identify the affected file and line number. + - Research the error/warning code and message. + - Apply the recommended fix to the codebase. + - Rebuild the solution to verify the fix. + - Repeat until the build completes with zero errors and warnings. + +1. **Verification** + +- Ensure the final build output shows “Build succeeded” and no warnings. +- Document any changes made to resolve issues. + +1. **Testing** + +- Run unit tests to ensure the solution works as expected. +- If tests fail, identify and fix the issues in the codebase. +- Rebuild and retest until all tests pass. + +1. **Documentation** + +- Create a `build-log.txt` file in the solution directory. +- Log all build outputs, error resolutions, and changes made. ## Notes diff --git a/.github/prompts/copyright-header-update-prompt.md b/.github/prompts/copyright-header-update-prompt.md index 91b81a4..3c969e2 100644 --- a/.github/prompts/copyright-header-update-prompt.md +++ b/.github/prompts/copyright-header-update-prompt.md @@ -28,7 +28,6 @@ This plan details how to review and update copyright headers in C# files, suppor ``` - **Steps:** 1. Identify all target `.cs` files, excluding those in `bin/` and `obj/` folders. diff --git a/.github/prompts/create-readme.prompt.md b/.github/prompts/create-readme.prompt.md index e285756..466a7ea 100644 --- a/.github/prompts/create-readme.prompt.md +++ b/.github/prompts/create-readme.prompt.md @@ -13,13 +13,15 @@ README files you write are appealing, informative, and easy to read. 1. Take a deep breath, and review the entire project and workspace, then create a comprehensive and well-structured README.md file for the project. 2. Take inspiration from these readme files for the structure, tone and content: - - https://raw.githubusercontent.com/Azure-Samples/serverless-chat-langchainjs/refs/heads/main/README.md - - https://raw.githubusercontent.com/Azure-Samples/serverless-recipes-javascript/refs/heads/main/README.md - - https://raw.githubusercontent.com/sinedied/run-on-output/refs/heads/main/README.md - - https://raw.githubusercontent.com/sinedied/smoke/refs/heads/main/README.md -3. Do not overuse emojis, and keep the readme concise and to the point. -4. Do not include sections like "LICENSE", "CONTRIBUTING", "CHANGELOG", etc. There are dedicated files for those + +- https://raw.githubusercontent.com/Azure-Samples/serverless-chat-langchainjs/refs/heads/main/README.md +- https://raw.githubusercontent.com/Azure-Samples/serverless-recipes-javascript/refs/heads/main/README.md +- https://raw.githubusercontent.com/sinedied/run-on-output/refs/heads/main/README.md +- https://raw.githubusercontent.com/sinedied/smoke/refs/heads/main/README.md + +1. Do not overuse emojis, and keep the readme concise and to the point. +2. Do not include sections like "LICENSE", "CONTRIBUTING", "CHANGELOG", etc. There are dedicated files for those sections. -5. Use GFM (GitHub Flavored Markdown) for formatting, and GitHub admonition +3. Use GFM (GitHub Flavored Markdown) for formatting, and GitHub admonition syntax (https://github.com/orgs/community/discussions/16925) where appropriate. -6. If you find a logo or icon for the project, use it in the readme's header. +4. If you find a logo or icon for the project, use it in the readme's header. diff --git a/.github/prompts/editorconfig.prompt.md b/.github/prompts/editorconfig.prompt.md index 58b3a91..8848c57 100644 --- a/.github/prompts/editorconfig.prompt.md +++ b/.github/prompts/editorconfig.prompt.md @@ -6,7 +6,8 @@ mode: 'agent' ## 📜 MISSION -You are an **EditorConfig Expert**. Your mission is to create a robust, comprehensive, and best-practice-oriented `.editorconfig` file. You will analyze the user's project structure and explicit requirements to generate a configuration that ensures consistent coding styles across different editors and IDEs. You must operate with absolute precision and provide clear, rule-by-rule explanations for your configuration choices. +You are an **EditorConfig Expert**. Your mission is to create a robust, comprehensive, and best-practice-oriented `.editorconfig` file. You will analyze the user's project structure and explicit requirements to generate a configuration that ensures consistent coding styles across different editors and IDEs. You must operate with absolute precision and provide clear, rule-by-rule explanations for +your configuration choices. ## 📝 DIRECTIVES @@ -28,7 +29,7 @@ You are an **EditorConfig Expert**. Your mission is to create a robust, comprehe Begin by acknowledging the user's preferences. Then, proceed directly to generating the `.editorconfig` file and the detailed explanation as per the specified output format. -### Example Output Structure: +### Example Output Structure Here is the `.editorconfig` file tailored to your project: @@ -61,4 +62,4 @@ trim_trailing_whitespace = false - `trim_trailing_whitespace = true`: This automatically removes any whitespace characters at the end of lines, which keeps the code clean and avoids unnecessary diffs in version control. - `insert_final_newline = true`: This ensures that every file ends with a single newline character, a POSIX standard that prevents certain scripting and concatenation issues. - `[*.md]`: This glob pattern applies specific rules only to Markdown files. -- `trim_trailing_whitespace = false`: This overrides the universal setting for Markdown files. It's disabled because trailing whitespace can be significant in Markdown (e.g., for creating hard line breaks). \ No newline at end of file +- `trim_trailing_whitespace = false`: This overrides the universal setting for Markdown files. It's disabled because trailing whitespace can be significant in Markdown (e.g., for creating hard line breaks). diff --git a/.github/prompts/structured-autonomy-generate.prompt.md b/.github/prompts/structured-autonomy-generate.prompt.md index e77616d..2d57024 100644 --- a/.github/prompts/structured-autonomy-generate.prompt.md +++ b/.github/prompts/structured-autonomy-generate.prompt.md @@ -8,6 +8,7 @@ agent: agent You are a PR implementation plan generator that creates complete, copy-paste ready implementation documentation. Your SOLE responsibility is to: + 1. Accept a complete PR plan (plan.md in plans/{feature-name}/) 2. Extract all implementation steps from the plan 3. Generate comprehensive step documentation with complete code @@ -31,6 +32,7 @@ Follow the below to generate and save implementation files for each s Output the plan as a COMPLETE markdown document using the , ready to be saved as a `.md` file. The plan MUST include: + - Complete, copy-paste ready code blocks with ZERO modifications needed - Exact file paths appropriate to the project structure - Markdown checkboxes for EVERY action item @@ -77,18 +79,22 @@ Return a comprehensive research package covering the entire project context. + # {FEATURE_NAME} ## Goal + {One sentence describing exactly what this implementation accomplishes} ## Prerequisites + Make sure that the use is currently on the `{feature-name}` branch before beginning implementation. If not, move them to the correct branch. If the branch does not exist, create it from main. ### Step-by-Step Instructions #### Step 1: {Action} + - [ ] {Specific instruction 1} - [ ] Copy and paste code below into `{file}`: @@ -104,13 +110,16 @@ If not, move them to the correct branch. If the branch does not exist, create it ``` ##### Step 1 Verification Checklist + - [ ] No build errors - [ ] Specific instructions for UI verification (if applicable) #### Step 1 STOP & COMMIT + **STOP & COMMIT:** Agent must stop here and wait for the user to test, stage, and commit the change. #### Step 2: {Action} + - [ ] {Specific Instruction 1} - [ ] Copy and paste code below into `{file}`: @@ -119,9 +128,11 @@ If not, move them to the correct branch. If the branch does not exist, create it ``` ##### Step 2 Verification Checklist + - [ ] No build errors - [ ] Specific instructions for UI verification (if applicable) #### Step 2 STOP & COMMIT + **STOP & COMMIT:** Agent must stop here and wait for the user to test, stage, and commit the change. diff --git a/.github/prompts/structured-autonomy-plan.prompt.md b/.github/prompts/structured-autonomy-plan.prompt.md index 9f41535..e77180a 100644 --- a/.github/prompts/structured-autonomy-plan.prompt.md +++ b/.github/prompts/structured-autonomy-plan.prompt.md @@ -32,9 +32,9 @@ Analyze the user's request and break it down into commits: 1. Generate draft plan using with `[NEEDS CLARIFICATION]` markers where the user's input is needed. 2. Save the plan to "plans/{feature-name}/plan.md" -4. Ask clarifying questions for any `[NEEDS CLARIFICATION]` sections -5. MANDATORY: Pause for feedback -6. If feedback received, revise plan and go back to Step 1 for any research needed +3. Ask clarifying questions for any `[NEEDS CLARIFICATION]` sections +4. MANDATORY: Pause for feedback +5. If feedback received, revise plan and go back to Step 1 for any research needed @@ -65,6 +65,7 @@ Analyze the user's request and break it down into commits: ### Step 3: {Step Name} ... ``` + diff --git a/.github/prompts/structured-autonomy.md b/.github/prompts/structured-autonomy.md index d1b3f42..a015ec4 100644 --- a/.github/prompts/structured-autonomy.md +++ b/.github/prompts/structured-autonomy.md @@ -20,7 +20,7 @@ Structured Autonomy is a three-phase workflow designed to maximize the value you ### The Workflow -``` +```text ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ sa-plan │ ──▶ │ sa-generate │ ──▶ │ sa-implement │ │ (1 request) │ │ (1 request) │ │ (many requests)│ @@ -30,20 +30,19 @@ Structured Autonomy is a three-phase workflow designed to maximize the value you **Phase 1: Plan** — You describe what you want to build. The planning agent researches your codebase, reads documentation, and breaks your feature into logical commits. Output: `plans/{feature-name}/plan.md` - -``` +```text /plan Add a user profile page to this application that allows the user to view and edit their profile information. ``` **Phase 2: Generate** — The generator takes your plan and produces complete, copy-paste ready implementation instructions with full code blocks. No placeholders, no TODOs—just working code based on your actual codebase patterns. Output: `plans/{feature-name}/implementation.md` -``` +```text /generate #plan.md ``` **Phase 3: Implement** — The implementation agent follows the generated instructions step-by-step, checking off items as it goes and stopping at defined commit boundaries for you to verify and commit. -``` +```text /implement #implementation.md ``` @@ -66,5 +65,3 @@ Each workflow operates on a single feature branch targeting a single pull reques **Developer Engagement** — The step-by-step implementation with manual commit points keeps you in the loop. You're reviewing and testing real changes at each step, not staring at a massive diff at the end wondering what happened. **Familiar Workflow** — This isn't a new way of working—it's your existing git workflow with AI assistance. Feature branches, incremental commits, PR reviews. The AI handles the tedious parts while you stay in control of the process. - - diff --git a/.github/prompts/xunit-integration.prompt.md b/.github/prompts/xunit-integration.prompt.md index bba3679..bef256d 100644 --- a/.github/prompts/xunit-integration.prompt.md +++ b/.github/prompts/xunit-integration.prompt.md @@ -28,6 +28,7 @@ idiomatic patterns, and reusable fixtures. - Use the .NET SDK test project format and target the same framework as the main app (e.g., `net7.0`). - Recommended package references: + ```xml @@ -305,12 +306,14 @@ Project setup (packages & templates) - Use DotNet.Testcontainers to run a real Postgres (or other) container per suite and a unique database name per factory/test to isolate state. +``````yaml name: Integration Tests on: workflow_dispatch: push: paths: + - 'tests/Web.Tests.Integration/**' - '.github/workflows/integration-tests.yml' @@ -320,6 +323,7 @@ runs-on: ubuntu-latest env: DOTNET_CLI_TELEMETRY_OPTOUT: '1' steps: + - name: Checkout repo uses: actions/checkout@v4 diff --git a/.github/prompts/xunit.prompt.md b/.github/prompts/xunit.prompt.md index ab00e77..fce4982 100644 --- a/.github/prompts/xunit.prompt.md +++ b/.github/prompts/xunit.prompt.md @@ -56,18 +56,18 @@ public class ArticleTests // Arrange & Act article = new Article(); - // Assert - article.Title.Should().Be(""); - article.Introduction.Should().Be(""); - article.Content.Should().Be(""); - article.CoverImageUrl.Should().Be(""); - article.UrlSlug.Should().Be(""); - article.Author.Should().Be(ApplicationUserDto.Empty); - article.Category.Should().Be(CategoryDto.Empty); - article.IsPublished.Should().BeFalse(); - article.PublishedOn.Should().BeNull(); - article.IsArchived.Should().BeFalse(); - article.Tags.Should().BeEmpty(); + // Assert + article.Title.Should().Be(""); + article.Introduction.Should().Be(""); + article.Content.Should().Be(""); + article.CoverImageUrl.Should().Be(""); + article.UrlSlug.Should().Be(""); + article.Author.Should().Be(ApplicationUserDto.Empty); + article.Category.Should().Be(CategoryDto.Empty); + article.IsPublished.Should().BeFalse(); + article.PublishedOn.Should().BeNull(); + article.IsArchived.Should().BeFalse(); + article.Tags.Should().BeEmpty(); } ``` @@ -121,7 +121,7 @@ bUnit is the recommended library for unit testing Blazor components and pages. I - Use FluentAssertions for expressive assertions. -### Example: Advanced bUnit Mocking +## Example: Advanced bUnit Mocking ```csharp diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index c5b7a29..84f6040 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -35,12 +35,14 @@ Closes # ### Code Quality + - [ ] I ran `dotnet build IssueTrackerApp.slnx --configuration Release` — 0 errors, 0 warnings - [ ] I ran `dotnet test IssueTrackerApp.slnx --configuration Release --no-build` — all pass - [ ] No TODO/FIXME left unless tracked in a follow-up issue (link it) - [ ] No secrets, API keys, or credentials committed ### Architecture + - [ ] New handlers follow the `Command`/`Query`/`Handler`/`Validator` naming conventions - [ ] New handlers are `sealed` - [ ] Domain layer has no references to `Web` or `Persistence.*` projects @@ -49,16 +51,19 @@ Closes # - [ ] No DTO types embedded in Model classes ### Tests + - [ ] New code has corresponding unit tests - [ ] Integration tests use domain-specific collections (`[Collection("XxxIntegration")]`) - [ ] No test compares two `IssueDto.Empty` / `CommentDto.Empty` instances directly ### Security (check if security-relevant) + - [ ] New endpoints have appropriate `RequireAuthorization` / policy applied - [ ] No `MarkupString` used with user-supplied content - [ ] No user input reflected in MongoDB queries without sanitization ### Merge Readiness + - [ ] Branch is up to date with `main` (no merge conflicts) - [ ] CI checks are green (do not request review while checks are pending/failing) - [ ] PR description is complete — reviewers should not have to ask what this does diff --git a/.github/workflows/code-metrics.yml b/.github/workflows/code-metrics.yml index 8087724..5e3f0a3 100644 --- a/.github/workflows/code-metrics.yml +++ b/.github/workflows/code-metrics.yml @@ -50,4 +50,4 @@ jobs: with: title: '${{ steps.dotnet-code-metrics.outputs.summary-title }}' body: '${{ steps.dotnet-code-metrics.outputs.summary-details }}' - commit-message: '.NET code metrics, automated pull request.' \ No newline at end of file + commit-message: '.NET code metrics, automated pull request.' diff --git a/.github/workflows/squad-dependabot-auto-merge.yml b/.github/workflows/squad-dependabot-auto-merge.yml index 6fa9a35..a67d235 100644 --- a/.github/workflows/squad-dependabot-auto-merge.yml +++ b/.github/workflows/squad-dependabot-auto-merge.yml @@ -42,4 +42,3 @@ jobs: } throw error; } - diff --git a/.github/workflows/squad-label-enforce.yml b/.github/workflows/squad-label-enforce.yml index a1dad6d..e3d0a83 100644 --- a/.github/workflows/squad-label-enforce.yml +++ b/.github/workflows/squad-label-enforce.yml @@ -34,7 +34,7 @@ jobs: // Handle go: namespace (mutual exclusivity) if (appliedLabel.startsWith('go:')) { - const otherGoLabels = allLabels.filter(l => + const otherGoLabels = allLabels.filter(l => l.startsWith('go:') && l !== appliedLabel ); @@ -100,7 +100,7 @@ jobs: // Handle release: namespace (mutual exclusivity) if (appliedLabel.startsWith('release:')) { - const otherReleaseLabels = allLabels.filter(l => + const otherReleaseLabels = allLabels.filter(l => l.startsWith('release:') && l !== appliedLabel ); @@ -128,7 +128,7 @@ jobs: // Handle type: namespace (mutual exclusivity) if (appliedLabel.startsWith('type:')) { - const otherTypeLabels = allLabels.filter(l => + const otherTypeLabels = allLabels.filter(l => l.startsWith('type:') && l !== appliedLabel ); @@ -154,7 +154,7 @@ jobs: // Handle priority: namespace (mutual exclusivity) if (appliedLabel.startsWith('priority:')) { - const otherPriorityLabels = allLabels.filter(l => + const otherPriorityLabels = allLabels.filter(l => l.startsWith('priority:') && l !== appliedLabel ); diff --git a/.github/workflows/squad-main-from-dev-guard.yml b/.github/workflows/squad-main-from-dev-guard.yml index c73a3e4..798af8c 100644 --- a/.github/workflows/squad-main-from-dev-guard.yml +++ b/.github/workflows/squad-main-from-dev-guard.yml @@ -20,4 +20,3 @@ jobs: if (source !== "dev") { core.setFailed(`PRs to main must come from dev. Current source: ${source}`); } - diff --git a/.github/workflows/squad-pr-auto-label.yml b/.github/workflows/squad-pr-auto-label.yml index c0ff06f..c918e54 100644 --- a/.github/workflows/squad-pr-auto-label.yml +++ b/.github/workflows/squad-pr-auto-label.yml @@ -30,7 +30,7 @@ jobs: const labelNames = currentLabels.map(l => l.name); // Check if already has squad labels - const hasSquadLabel = labelNames.some(name => + const hasSquadLabel = labelNames.some(name => name === 'squad' || name.startsWith('squad:') ); diff --git a/.github/workflows/squad-standard-lint-markdown.yml b/.github/workflows/squad-standard-lint-markdown.yml index 30c494b..83f6933 100644 --- a/.github/workflows/squad-standard-lint-markdown.yml +++ b/.github/workflows/squad-standard-lint-markdown.yml @@ -21,4 +21,3 @@ jobs: exit 0 fi npx --yes markdownlint-cli $files - diff --git a/.github/workflows/squad-standard-lint-yaml.yml b/.github/workflows/squad-standard-lint-yaml.yml index 6961ae6..b8049c0 100644 --- a/.github/workflows/squad-standard-lint-yaml.yml +++ b/.github/workflows/squad-standard-lint-yaml.yml @@ -22,4 +22,3 @@ jobs: exit 0 fi yamllint -s $files - diff --git a/AGENTS.md b/AGENTS.md index 6d348b6..db44c2c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,6 +3,7 @@ - Follow `.github/copilot-instructions.md`, `.squad/team.md`, and `.squad/routing.md` before autonomous work. Squad branches use `squad/{issue-number}-{slug}`; do not commit `.squad/` files on `feature/*` branches. ## Architecture map + - `src/AppHost/AppHost.cs` is the local-dev entry point: it wires `web` to `ConnectionStrings:mongodb`, provisions `redis` (with RedisCommander in Development), waits for Redis, and health-checks `/health`. - `src/Web/Program.cs` is the real composition root: DI, Auth0, MediatR, minimal APIs, SignalR, background services, and startup seeding all start here. - `src/ServiceDefaults/Extensions.cs` centralizes OpenTelemetry, service discovery, resilience, and the `/health` + `/alive` endpoints. @@ -14,9 +15,11 @@ - `src/Web/Components/Theme/` holds `ThemeProvider.razor` and `ThemeSelector.razor` for system-aware dark mode with 4 colour schemes. - `src/Web/Components/Charts/` holds `BarChart.razor`, `LineChart.razor`, and `PieChart.razor` Blazor components used in the Analytics admin page. - `src/Web/Helpers/` holds `ObjectIdJsonConverter` (registered globally in `Program.cs` for MongoDB `ObjectId` ↔ JSON string) and `CsvExportHelper` (reflection-based CSV export used by bulk-export). -- Cache strategy: `DistributedCacheHelper` (`src/Web/Services/DistributedCacheHelper.cs`) wraps `IDistributedCache` (Redis in prod, `DistributedMemoryCache` in test) with typed JSON get/set/remove and version-token helpers. All services inject it for cache-aside reads (miss → DB → cache) and CRUD invalidation. TTLs: reference data (Category/Status) 60 min, lookups 30 min, issues/comments/dashboard 5–10 min, Auth0 users 5–10 min. See Sprint issues #218–#240 for full invalidation matrix. +- Cache strategy: `DistributedCacheHelper` (`src/Web/Services/DistributedCacheHelper.cs`) wraps `IDistributedCache` (Redis in prod, `DistributedMemoryCache` in test) with typed JSON get/set/remove and version-token helpers. All services inject it for cache-aside reads (miss → DB → cache) and CRUD invalidation. TTLs: reference data (Category/Status) 60 min, lookups 30 min, issues/comments/dashboard +5–10 min, Auth0 users 5–10 min. See Sprint issues #218–#240 for full invalidation matrix. ## Request and data flow + - Typical flow is `Web` page/endpoint -> `src/Web/Services/*` facade -> MediatR command/query in `src/Domain/Features/*` -> `IRepository` -> MongoDB. - Example: `src/Web/Services/IssueService.cs` sends `CreateIssueCommand`; `src/Domain/Features/Issues/Commands/CreateIssueCommand.cs` maps DTOs to models and persists through repositories. - Minimal API endpoints live in **two** directories: `src/Web/Endpoints/` (`CategoryEndpoints.cs`, `StatusEndpoints.cs`) and `src/Web/Features/` (`CommentEndpoints.cs`, `AttachmentEndpoints.cs`). Both follow the same route-group + `AdminPolicy` / `RequireAuthorization` pattern shown in `CategoryEndpoints.cs`. @@ -29,6 +32,7 @@ - Email pipeline: domain events (`src/Domain/Events/`) are published with MediatR `IPublisher`; `Domain/Features/Notifications/` handlers react and dispatch `QueueEmailCommand`; `EmailQueueBackgroundService` polls the queue every 10 s and sends via `IEmailService` (SendGrid if `SendGrid:ApiKey` is set, SMTP otherwise). ## Conventions enforced by tests + - `tests/Architecture.Tests/LayerDependencyTests.cs` enforces boundaries: `Domain` cannot depend on `Web` or `Persistence.*`; `Persistence.*` cannot depend on `Web`. - `tests/Architecture.Tests/NamingConventionTests.cs`, `AdvancedArchitectureTests.cs`, and `CodeStructureTests.cs` enforce the repo's shape: commands end with `Command`, queries with `Query`, validators with `Validator`, handlers with `Handler` and should be `sealed`. - CQRS types belong under `Domain.Features.*`; validators live in `Validators` or next to commands; DTOs in `Domain.DTOs` should be records. @@ -36,12 +40,14 @@ - Commands may reside in either a `Commands` or a `Notifications` namespace; `AdvancedArchitectureTests.cs` explicitly allows both locations. ## Integrations to respect + - Auth0 is configured in `src/Web/Program.cs`; role claim remapping happens in `src/Web/Auth/Auth0ClaimsTransformation.cs`, and policy names/constants live in `src/Web/Auth/*`. - `src/Persistence.MongoDb/ServiceCollectionExtensions.cs` falls back from `MongoDB:ConnectionString` to `ConnectionStrings:mongodb`, which matters under Aspire and in tests. - Redis is provisioned in `AppHost` and health-check helpers exist in `ServiceDefaults`, but current app code leans on `IMemoryCache` more than distributed cache. - SignalR updates flow through `src/Web/Hubs/IssueHub` and notification-aware services such as `IssueService`. ## Developer workflow + - **Branching model:** `dev` is the active development branch (feature PRs target `dev` via squash merge); `main` is releases only (promoted from `dev` via merge commit, then tagged). Direct pushes to both `dev` and `main` are blocked. - From repo root: `dotnet restore`, `dotnet build`, `dotnet test IssueTrackerApp.slnx`. - Run locally through Aspire: `dotnet run --project src/AppHost/AppHost.csproj`. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8dabef3..9bf36a0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -102,13 +102,13 @@ All work **must** happen on a dedicated branch. Direct pushes to `main` and `dev Use the squad branch convention: -``` +```text squad/{issue-number}-{kebab-case-slug} ``` Examples: -``` +```text squad/42-fix-login-validation squad/112-contributing-pre-push-docs squad/87-add-comment-pagination @@ -147,7 +147,7 @@ When you run `git push`, a Bash hook fires and runs four sequential gates before The hook lives at `.git/hooks/pre-push` (installed from `.github/hooks/pre-push` via `scripts/install-hooks.sh`). -``` +```text ┌─────────────────────────────────────────────────────────────────────┐ │ git push → pre-push hook │ │ │ @@ -342,6 +342,7 @@ dotnet tool run reportgenerator \ Then open `coverage-report/index.html` in your browser. > If `dotnet tool run reportgenerator` fails, install it globally first: +> > ```bash > dotnet tool install -g dotnet-reportgenerator-globaltool > ``` @@ -366,13 +367,13 @@ Test projects themselves, generated code, and `obj/` directories are excluded au The `Coverage Analysis` CI job reads `Summary.json` produced by ReportGenerator and compares `linecoverage` against `80`. If the value is below 80, the job exits with an error and the PR cannot be merged: -``` +```text ::error::Code coverage is below 80% threshold: 74.3% (required: 80%) ``` When coverage passes the gate you will see: -``` +```text ::notice::Coverage gate passed: 83.1% >= 80% ``` @@ -448,13 +449,13 @@ All `public` types and members require a `` XML doc comment: 2. **Open a PR** targeting `dev`. Reference the issue in the body: - ``` + ```text Closes #{issue-number} ``` 3. **If you worked as a squad member**, note it in the PR description: - ``` + ```text Working as Frodo (Tech Writer) ``` @@ -501,9 +502,11 @@ AppHost.Tests boots the full Aspire stack. If the app does not become healthy wi Playwright E2E tests run against a live Aspire application and can exhibit timing-related failures when the full suite runs in parallel. A test that fails on one run may pass on the next. If you see a single test failure in AppHost.Tests: 1. Re-run the test project in isolation to confirm it passes: + ```bash dotnet test tests/AppHost.Tests/AppHost.Tests.csproj --configuration Release --no-build ``` + 2. If the same test fails consistently in isolation (not just in the full suite), investigate the failure — it likely points to a real regression. 3. If the test only fails in the full suite run, it is a known flakiness issue. Re-run AppHost.Tests once more. If it passes, proceed with your push. 4. If you cannot get AppHost.Tests to pass after three full-suite runs, open a draft PR, tag it `squad:pippin`, and ask for help — do not push a broken suite. diff --git a/README.md b/README.md index 5b48f14..e12111c 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ IssueTrackerApp is a full-stack web application for managing issues and tracking ## Project Structure -``` +```text IssueTrackerApp/ ├── src/ │ ├── AppHost/ # .NET Aspire orchestration diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 956a5aa..7ff8724 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -4,7 +4,7 @@ This document describes the solution architecture of IssueTrackerApp, including ## Solution Structure -``` +```text IssueTrackerApp/ ├── src/ │ ├── AppHost/ # .NET Aspire Orchestration @@ -130,7 +130,7 @@ IssueTrackerApp/ ## Data Flow Diagram -``` +```text ┌─────────────────────────────────────────────────────────────────────────────┐ │ AppHost │ │ (.NET Aspire Orchestration) │ @@ -273,7 +273,7 @@ public class Comment ### Authentication Flow -``` +```text ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Browser │───►│ /login │───►│ Auth0 │───►│/callback│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ @@ -307,7 +307,7 @@ public class Comment ### OpenTelemetry Integration -``` +```text ┌─────────────────────────────────────────────────────────────────┐ │ ServiceDefaults │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ @@ -392,7 +392,7 @@ The application uses MediatR to implement the Command Query Responsibility Segre ### Feature Structure -``` +```text Domain/Features/ ├── Issues/ │ ├── Commands/ @@ -488,6 +488,7 @@ public record IssueSearchRequest ### Filter Pipeline The search handler applies filters in sequence: + 1. Archive filter (exclude archived by default) 2. Text search (title and description, case-insensitive) 3. Status filter (exact match) @@ -499,6 +500,7 @@ The search handler applies filters in sequence: ### URL Parameter Persistence Search state is synchronized with URL query parameters, enabling: + - Bookmarkable search results - Browser back/forward navigation - Shareable filtered views diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md index e477ba3..fcf27bc 100644 --- a/docs/CODE_OF_CONDUCT.md +++ b/docs/CODE_OF_CONDUCT.md @@ -17,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or +- The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities diff --git a/docs/FEATURES.md b/docs/FEATURES.md index 95d697d..a31161a 100644 --- a/docs/FEATURES.md +++ b/docs/FEATURES.md @@ -47,7 +47,7 @@ Full CRUD (Create, Read, Update, Delete) functionality for tracking issues. ### CQRS Commands & Queries -``` +```text Domain/Features/Issues/ ├── Commands/ │ ├── CreateIssueCommand.cs # Create new issue @@ -215,7 +215,7 @@ Comments are displayed inline on the issue details page: ### CQRS Commands & Queries -``` +```text Domain/Features/Comments/ ├── Commands/ │ ├── AddCommentCommand.cs # Add new comment to issue @@ -275,7 +275,7 @@ Admin functionality for managing issue categories. ### CQRS Commands & Queries -``` +```text Domain/Features/Categories/ ├── Commands/ │ ├── CreateCategoryCommand.cs @@ -330,7 +330,7 @@ Admin functionality for managing issue statuses and workflows. ### CQRS Commands & Queries -``` +```text Domain/Features/Statuses/ ├── Commands/ │ ├── CreateStatusCommand.cs diff --git a/docs/New Work process.md b/docs/New Work process.md index e0e105c..cb7438e 100644 --- a/docs/New Work process.md +++ b/docs/New Work process.md @@ -80,11 +80,11 @@ Ralph checks all gates before spawning reviewers: discretionary 2. **Ralph spawns parallel domain reviewers** — Aragorn always reviews; relevant specialists (Legolas, Gimli, etc.) review their domain -2. Reviewers post verdicts via GitHub PR review (`--approve` or +3. Reviewers post verdicts via GitHub PR review (`--approve` or `--request-changes`) -3. If **CHANGES_REQUESTED**: PR author is locked out; a *different* squad member +4. If **CHANGES_REQUESTED**: PR author is locked out; a *different* squad member fixes and pushes to the same branch — then re-review begins -4. **Unanimous approval + CI green** → Ralph squash-merges into `dev` and deletes the branch: +5. **Unanimous approval + CI green** → Ralph squash-merges into `dev` and deletes the branch: ```bash gh pr merge {N} --squash --delete-branch @@ -103,7 +103,7 @@ Ralph checks all gates before spawning reviewers: docs: rewrite New Work process to reflect squad ceremonies (Closes #215) ``` -5. Ralph runs **Post-Merge Orphan Branch Cleanup** ceremony automatically — +6. Ralph runs **Post-Merge Orphan Branch Cleanup** ceremony automatically — removes stale local and remote `squad/*` refs and the sprint worktree --- @@ -138,7 +138,7 @@ blog-only └─ squad:bilbo brief issue created directly ``` -5. **Bilbo** picks up the brief, writes the blog post, and adds a row to +1. **Bilbo** picks up the brief, writes the blog post, and adds a row to `docs/blog/index.md` -6. `blog-readme-sync.yml` detects the index change and auto-updates the +2. `blog-readme-sync.yml` detects the index change and auto-updates the **README Dev Blog section** — no manual README edit needed diff --git a/docs/TESTING.md b/docs/TESTING.md index f28fcf0..6957507 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -40,11 +40,12 @@ Coverage reports are generated in `TestResults//coverage.cobertura.xml`. ## Test Naming Convention -``` +```text {MethodUnderTest}_{Scenario}_{ExpectedBehavior} ``` Examples: + - `CreateIssue_WithValidData_ReturnsSuccessResult` - `GetIssueById_WhenNotFound_ReturnsFailure` - `ValidateIssue_WithEmptyTitle_HasValidationError` @@ -100,11 +101,13 @@ Coverage is enforced in CI via the `squad-test.yml` workflow. ## CI/CD Integration Tests run automatically on: + - Push to `main` branch - Pull requests to any branch - Manual workflow dispatch The `squad-test.yml` workflow: + 1. Builds the solution 2. Runs all test projects in parallel 3. Collects and merges coverage reports diff --git a/docs/THEMING.md b/docs/THEMING.md index ae62a79..81f5f3c 100644 --- a/docs/THEMING.md +++ b/docs/THEMING.md @@ -27,7 +27,7 @@ Select from four built-in color schemes: ### Architecture Overview -``` +```text ┌─────────────────────────────────────────────────────────────┐ │ ThemeProvider.razor │ │ ┌─────────────────────────────────────────────────────────┐ │ @@ -267,7 +267,7 @@ npm run css:build IssueTrackerApp uses [OKLCH](https://oklch.com/) color format for perceptually uniform color scales: -``` +```text oklch(L% C H) ``` diff --git a/docs/blog/2026-03-27-apphost-aspire-playwright-e2e-tests.md b/docs/blog/2026-03-27-apphost-aspire-playwright-e2e-tests.md index 5e62b39..73e8cea 100644 --- a/docs/blog/2026-03-27-apphost-aspire-playwright-e2e-tests.md +++ b/docs/blog/2026-03-27-apphost-aspire-playwright-e2e-tests.md @@ -71,6 +71,7 @@ It gracefully skips when credentials (`PLAYWRIGHT_TEST_EMAIL`, `PLAYWRIGHT_TEST_ ## Test Coverage ### Aspire Integration Tests (3 tests) + - **AppHostIntegrationTests.cs**: Health check (`/health`) endpoint responds - **EnvVarTests.cs**: MongoDB and Redis connection strings are properly injected - **IntegrationTests.cs**: HTTP health endpoint + home page load @@ -78,15 +79,18 @@ It gracefully skips when credentials (`PLAYWRIGHT_TEST_EMAIL`, `PLAYWRIGHT_TEST_ ### Playwright E2E Tests (29 tests across 8 files) #### Layout & Navigation (10 tests) + - **LayoutAnonymousTests.cs** — 6 tests: brand visible, login link present, nav hidden, footer, theme selector, color scheme selector - **LayoutAuthenticatedTests.cs** — 4 tests: nav links visible, footer, login link hidden from authenticated users #### Pages (9 tests) + - **HomePageTests.cs** — 4 tests: guest heading, authenticated welcome-back message - **DashboardPageTests.cs** — 3 tests: no redirect loop, heading, stat cards render - **NotFoundPageTests.cs** — 2 tests: heading and helpful message #### Issues & Features (6 tests) + - **IssueIndexPageTests.cs** — 2 tests: authenticated users see no redirect, page title - **ThemeToggleTests.cs** — 4 tests: toggle visible, dropdown renders, dark class applied to body - **ColorSchemeTests.cs** — 4 tests: color swatches visible, selection works, CSS class applied diff --git a/docs/blog/2026-03-30-release-v0-2-0.md b/docs/blog/2026-03-30-release-v0-2-0.md index c910941..76938ce 100644 --- a/docs/blog/2026-03-30-release-v0-2-0.md +++ b/docs/blog/2026-03-30-release-v0-2-0.md @@ -8,7 +8,8 @@ summary: "v0.2.0 ships 30 improvements: UI refresh with neutral colors, GitHub P ## Summary -**v0.2.0 is now live.** This release represents a major quality and documentation push: a neutral color migration for the UI, complete GitHub Pages deployment automation, all skipped tests audited and re-enabled, formal peer review gates, and a new blog where the squad documents every release. Under the hood, we've strengthened E2E test coverage, unified developer tooling, and formalized the path to production. +**v0.2.0 is now live.** This release represents a major quality and documentation push: a neutral color migration for the UI, complete GitHub Pages deployment automation, all skipped tests audited and re-enabled, formal peer review gates, and a new blog where the squad documents every release. Under the hood, we've strengthened E2E test coverage, unified developer tooling, and formalized the path +to production. ## What's New @@ -68,9 +69,10 @@ The squad codified squad roles, blog process, and project decisions: ## Architecture Highlights ### Vertical Slice with MediatR + Commands and Queries live in `src/Domain/Features/*`. Every feature is a self-contained unit: -``` +```text Domain/ Features/ Issues/ @@ -81,9 +83,11 @@ Domain/ ``` ### GitHub Pages + Plain Markdown + No Jekyll config needed. Blog lives at `docs/blog/`; GitHub auto-renders Markdown. Every release gets a post. ### Aspire Orchestration + Local dev runs via `dotnet run --project src/AppHost/AppHost.csproj`. AppHost wires up web, MongoDB, Redis, and runs health checks before accepting requests. --- diff --git a/docs/blog/2026-04-01-release-v0-3-0.md b/docs/blog/2026-04-01-release-v0-3-0.md index a27228a..385bc20 100644 --- a/docs/blog/2026-04-01-release-v0-3-0.md +++ b/docs/blog/2026-04-01-release-v0-3-0.md @@ -8,7 +8,8 @@ summary: "v0.3.0 completes the Polish Sprint with dashboard UI fixes, Redis cach ## Summary -**v0.3.0 is now live.** This release focuses on polish and developer experience: the Dashboard Quick Actions layout has been corrected to stack vertically for better mobile responsiveness, Redis caching infrastructure is now integrated for performance optimization, issues can now be restored after deletion via a new `RestoreIssueCommand`, and the Issue model gains full assignee field support. Together, these changes solidify the v0.3 Polish Sprint and prepare the foundation for richer issue workflows in future releases. +**v0.3.0 is now live.** This release focuses on polish and developer experience: the Dashboard Quick Actions layout has been corrected to stack vertically for better mobile responsiveness, Redis caching infrastructure is now integrated for performance optimization, issues can now be restored after deletion via a new `RestoreIssueCommand`, and the Issue model gains full assignee field support. +Together, these changes solidify the v0.3 Polish Sprint and prepare the foundation for richer issue workflows in future releases. ## What's New diff --git a/docs/blog/2026-04-01-release-v0-4-0.md b/docs/blog/2026-04-01-release-v0-4-0.md index dcb5176..8043b94 100644 --- a/docs/blog/2026-04-01-release-v0-4-0.md +++ b/docs/blog/2026-04-01-release-v0-4-0.md @@ -8,7 +8,8 @@ summary: "v0.4.0 introduces a complete Issue Voting system with CQRS commands, d ## Summary -**v0.4.0 is now live.** This release delivers a **complete Issue Voting system** that enables team-driven prioritization. Each issue now has a vote count badge, users can upvote/unvote issues (with idempotent per-user voting), and the issues list supports filtering by "Top Voted" to surface high-priority work. Under the hood, voting is implemented as a full CQRS pattern with `VoteIssueCommand` and `UnvoteIssueCommand`, domain events trigger when vote thresholds are crossed, and SignalR broadcasts real-time updates across all connected clients. Comprehensive bUnit and integration test coverage ensures the voting system is production-ready. +**v0.4.0 is now live.** This release delivers a **complete Issue Voting system** that enables team-driven prioritization. Each issue now has a vote count badge, users can upvote/unvote issues (with idempotent per-user voting), and the issues list supports filtering by "Top Voted" to surface high-priority work. Under the hood, voting is implemented as a full CQRS pattern with `VoteIssueCommand` and +`UnvoteIssueCommand`, domain events trigger when vote thresholds are crossed, and SignalR broadcasts real-time updates across all connected clients. Comprehensive bUnit and integration test coverage ensures the voting system is production-ready. ## What's New diff --git a/docs/blog/2026-04-02-release-v0-5-0.md b/docs/blog/2026-04-02-release-v0-5-0.md index 4c710fb..6fd987b 100644 --- a/docs/blog/2026-04-02-release-v0-5-0.md +++ b/docs/blog/2026-04-02-release-v0-5-0.md @@ -8,7 +8,8 @@ summary: "v0.5.0 ships the Admin User Management feature: Auth0 Management API i ## Summary -**v0.5.0 is now live.** This release delivers **complete Admin User Management capabilities**, enabling system administrators to manage Auth0 users, assign roles, and audit user actions from a dedicated admin interface. The new `/admin/users` page provides a searchable, paginated table of all users with in-place role management, audit history, and role badges. Under the hood, we've integrated the Auth0 Management API, implemented a token-caching strategy to respect rate limits, and built a full CQRS command/query layer with domain models and comprehensive test coverage. +**v0.5.0 is now live.** This release delivers **complete Admin User Management capabilities**, enabling system administrators to manage Auth0 users, assign roles, and audit user actions from a dedicated admin interface. The new `/admin/users` page provides a searchable, paginated table of all users with in-place role management, audit history, and role badges. Under the hood, we've integrated the +Auth0 Management API, implemented a token-caching strategy to respect rate limits, and built a full CQRS command/query layer with domain models and comprehensive test coverage. ## What's New @@ -180,4 +181,5 @@ With v0.5.0, the admin tier is ready for production. Upcoming work includes: **Release v0.5.0:** [Full Changelog](https://github.com/mpaulosky/IssueTrackerApp/compare/v0.4.0...v0.5.0) -**Key Issues Closed:** [#130](https://github.com/mpaulosky/IssueTrackerApp/issues/130), [#131](https://github.com/mpaulosky/IssueTrackerApp/issues/131), [#132](https://github.com/mpaulosky/IssueTrackerApp/issues/132), [#134](https://github.com/mpaulosky/IssueTrackerApp/issues/134), [#135](https://github.com/mpaulosky/IssueTrackerApp/issues/135), [#136](https://github.com/mpaulosky/IssueTrackerApp/issues/136), [#137](https://github.com/mpaulosky/IssueTrackerApp/issues/137), [#138](https://github.com/mpaulosky/IssueTrackerApp/issues/138), [#139](https://github.com/mpaulosky/IssueTrackerApp/issues/139), [#140](https://github.com/mpaulosky/IssueTrackerApp/issues/140) +**Key Issues Closed:** [#130](https://github.com/mpaulosky/IssueTrackerApp/issues/130), [#131](https://github.com/mpaulosky/IssueTrackerApp/issues/131), [#132](https://github.com/mpaulosky/IssueTrackerApp/issues/132), [#134](https://github.com/mpaulosky/IssueTrackerApp/issues/134), [#135](https://github.com/mpaulosky/IssueTrackerApp/issues/135), +[#136](https://github.com/mpaulosky/IssueTrackerApp/issues/136), [#137](https://github.com/mpaulosky/IssueTrackerApp/issues/137), [#138](https://github.com/mpaulosky/IssueTrackerApp/issues/138), [#139](https://github.com/mpaulosky/IssueTrackerApp/issues/139), [#140](https://github.com/mpaulosky/IssueTrackerApp/issues/140) diff --git a/docs/blog/2026-04-02-release-v0-6-0.md b/docs/blog/2026-04-02-release-v0-6-0.md index ba14150..af732b3 100644 --- a/docs/blog/2026-04-02-release-v0-6-0.md +++ b/docs/blog/2026-04-02-release-v0-6-0.md @@ -8,7 +8,9 @@ summary: "v0.6.0 ships the Labels feature: multi-value tag input with autocomple ## Summary -**v0.6.0 is now live.** This release introduces a **flexible Labels feature** that enables team members to tag issues with custom labels, filter by label, and organize work across multiple dimensions. Each issue now supports multiple labels stored as `List` on the Issue model. The new `LabelInput.razor` component provides a polished multi-value tag input with debounced autocomplete, making it seamless to add and remove labels. The Issues list supports URL-based label filtering with quick-filter chips above the table. Under the hood, `AddLabelCommand` and `RemoveLabelCommand` implement the CQRS pattern, and `ILabelService` aggregates distinct labels from existing issues for autocomplete suggestions. This release brings total test coverage to **1,167 tests** (up from 1,050 before Sprint 6). +**v0.6.0 is now live.** This release introduces a **flexible Labels feature** that enables team members to tag issues with custom labels, filter by label, and organize work across multiple dimensions. Each issue now supports multiple labels stored as `List` on the Issue model. The new `LabelInput.razor` component provides a polished multi-value tag input with debounced autocomplete, making +it seamless to add and remove labels. The Issues list supports URL-based label filtering with quick-filter chips above the table. Under the hood, `AddLabelCommand` and `RemoveLabelCommand` implement the CQRS pattern, and `ILabelService` aggregates distinct labels from existing issues for autocomplete suggestions. This release brings total test coverage to **1,167 tests** (up from 1,050 before +Sprint 6). ## What's New @@ -373,4 +375,5 @@ With v0.6.0, the core issue management features are mature. Upcoming work includ **Release v0.6.0:** [Full Changelog](https://github.com/mpaulosky/IssueTrackerApp/compare/v0.5.0...v0.6.0) -**Key Issues Closed:** [#148](https://github.com/mpaulosky/IssueTrackerApp/issues/148), [#149](https://github.com/mpaulosky/IssueTrackerApp/issues/149), [#150](https://github.com/mpaulosky/IssueTrackerApp/issues/150), [#151](https://github.com/mpaulosky/IssueTrackerApp/issues/151), [#152](https://github.com/mpaulosky/IssueTrackerApp/issues/152), [#153](https://github.com/mpaulosky/IssueTrackerApp/issues/153), [#154](https://github.com/mpaulosky/IssueTrackerApp/issues/154), [#155](https://github.com/mpaulosky/IssueTrackerApp/issues/155), [#156](https://github.com/mpaulosky/IssueTrackerApp/issues/156) +**Key Issues Closed:** [#148](https://github.com/mpaulosky/IssueTrackerApp/issues/148), [#149](https://github.com/mpaulosky/IssueTrackerApp/issues/149), [#150](https://github.com/mpaulosky/IssueTrackerApp/issues/150), [#151](https://github.com/mpaulosky/IssueTrackerApp/issues/151), [#152](https://github.com/mpaulosky/IssueTrackerApp/issues/152), +[#153](https://github.com/mpaulosky/IssueTrackerApp/issues/153), [#154](https://github.com/mpaulosky/IssueTrackerApp/issues/154), [#155](https://github.com/mpaulosky/IssueTrackerApp/issues/155), [#156](https://github.com/mpaulosky/IssueTrackerApp/issues/156) diff --git a/docs/blog/2026-04-04-release-v0-7-0.md b/docs/blog/2026-04-04-release-v0-7-0.md index 951e45a..68aa2b0 100644 --- a/docs/blog/2026-04-04-release-v0-7-0.md +++ b/docs/blog/2026-04-04-release-v0-7-0.md @@ -8,7 +8,8 @@ summary: "v0.7.0 delivers an 82.5% code coverage baseline with an enforced CI ga ## Summary -**v0.7.0 is now live.** This release is an engineering quality milestone — no new user-facing features, but the codebase is now measurably safer and more maintainable. A three-sprint code coverage initiative brings line coverage to **82.5%** with a CI gate that fails any PR dropping below 80%. Alongside that, a full UI polish pass standardizes Tailwind colour usage, button classes, and table styles across the entire application. A real production race condition in `InMemoryBulkOperationQueue` was discovered and fixed in the process. +**v0.7.0 is now live.** This release is an engineering quality milestone — no new user-facing features, but the codebase is now measurably safer and more maintainable. A three-sprint code coverage initiative brings line coverage to **82.5%** with a CI gate that fails any PR dropping below 80%. Alongside that, a full UI polish pass standardizes Tailwind colour usage, button classes, and table +styles across the entire application. A real production race condition in `InMemoryBulkOperationQueue` was discovered and fixed in the process. ## What's New diff --git a/docs/features/admin-user-management.md b/docs/features/admin-user-management.md index 8ff6774..d36ba7e 100644 --- a/docs/features/admin-user-management.md +++ b/docs/features/admin-user-management.md @@ -72,6 +72,7 @@ Replace the placeholder values with the actual credentials from your Auth0 M2M a When the application starts, it will bind these secrets to the `Auth0Management` configuration section. If any required secret is missing, the application may fail during startup or when attempting to access the User Management page. To verify the configuration is correct: + 1. Run the application: `dotnet run --project src/AppHost` 2. Log in with an Admin user 3. Navigate to `/admin/users` — you should see the list of users from your Auth0 tenant @@ -83,6 +84,7 @@ To verify the configuration is correct: The main Admin User Management page (`/admin/users`) displays all users from your Auth0 tenant in a paginated table. **Fields displayed:** + - User ID (Auth0 identifier, e.g., `auth0|abc123`) - Email address - Display name @@ -90,6 +92,7 @@ The main Admin User Management page (`/admin/users`) displays all users from you - Assigned roles (comma-separated list) **Pagination:** + - Navigate between pages of users (default: 10 users per page) - Maximum page size: 100 users per page @@ -98,12 +101,14 @@ The main Admin User Management page (`/admin/users`) displays all users from you From the user detail page or inline actions, administrators can assign one or more Auth0 roles to a user. **Steps:** + 1. Click on a user in the list or select "Assign Role" from the user row 2. Select one or more roles from the "Available Roles" dropdown 3. Click **Assign** 4. The assignment is logged to the audit log with the timestamp and acting administrator **Result:** + - The role is immediately applied in Auth0 - The user's session is refreshed on their next login to reflect the new role - A `RoleChangeAuditEntry` is created in the audit log @@ -113,12 +118,14 @@ From the user detail page or inline actions, administrators can assign one or mo Administrators can revoke assigned roles from users. **Steps:** + 1. Click on a user in the list 2. Click **Remove** next to the role to be revoked 3. Confirm the action 4. The revocation is logged to the audit log **Result:** + - The role is immediately removed in Auth0 - The user's session is updated on their next login - A `RoleChangeAuditEntry` is created in the audit log @@ -308,6 +315,7 @@ Auth0 Management API enforces rate limits (HTTP 429). Implement a Polly retry po **Cause:** Missing M2M configuration secrets. **Resolution:** Verify all four secrets are set: + ```bash dotnet user-secrets list --project src/Web ``` @@ -317,6 +325,7 @@ dotnet user-secrets list --project src/Web **Cause:** Incorrect M2M credentials or M2M application lacks required scopes. **Resolution:** + 1. Verify M2M credentials in User Secrets match Auth0 Dashboard 2. Verify the M2M application has the required scopes (read:users, update:users, read:roles, create:role_members, delete:role_members) 3. Check Auth0 logs for API errors diff --git a/docs/research/github-github-sdk.md b/docs/research/github-github-sdk.md index db5911c..81193a1 100644 --- a/docs/research/github-github-sdk.md +++ b/docs/research/github-github-sdk.md @@ -9,7 +9,8 @@ There is no single repository at `github/github-sdk`. Instead, GitHub maintains **two distinct SDK ecosystems** for different purposes: -1. **Octokit** — The official GitHub REST/GraphQL API client libraries, maintained under the [`octokit`](https://github.com/octokit) organization. These are the traditional SDKs for interacting with GitHub's platform APIs (repos, issues, PRs, users, etc.). Available in JavaScript/TypeScript, Ruby, .NET, Go, and Objective-C. The newer Go and .NET SDKs are **auto-generated** from GitHub's OpenAPI spec using Microsoft's Kiota tool[^1]. +1. **Octokit** — The official GitHub REST/GraphQL API client libraries, maintained under the [`octokit`](https://github.com/octokit) organization. These are the traditional SDKs for interacting with GitHub's platform APIs (repos, issues, PRs, users, etc.). Available in JavaScript/TypeScript, Ruby, .NET, Go, and Objective-C. The newer Go and .NET SDKs are **auto-generated** from GitHub's OpenAPI +spec using Microsoft's Kiota tool[^1]. 2. **Copilot SDK** — A newer, fast-growing SDK at [`github/copilot-sdk`](https://github.com/github/copilot-sdk) (8k stars) for embedding GitHub Copilot's agentic AI workflows into applications. Available in TypeScript, Python, Go, .NET, and Java. Currently in **Technical Preview**[^2]. @@ -269,7 +270,7 @@ The foundation for generated SDKs is [`github/rest-api-description`](https://git The [`octokit/source-generator`](https://github.com/octokit/source-generator) repository contains the code generation pipeline[^13]: -``` +```text github/rest-api-description (OpenAPI spec) │ ▼ @@ -296,7 +297,7 @@ The [**GitHub Copilot SDK**](https://github.com/github/copilot-sdk) (8k ⭐, 1k **Status:** Technical Preview (not yet production-ready) -``` +```text ┌─────────────────────────┐ │ Your Application │ │ (Python/TS/Go/.NET) │ @@ -499,7 +500,7 @@ const result = await generateText({ ## Choosing the Right SDK -``` +```text What do you need? │ ├─► Interact with GitHub API (repos, issues, PRs, users)? diff --git a/docs/research/tailwindcss-com.md b/docs/research/tailwindcss-com.md index f798fcd..d63260e 100644 --- a/docs/research/tailwindcss-com.md +++ b/docs/research/tailwindcss-com.md @@ -2,7 +2,9 @@ ## Executive Summary -Tailwind CSS is a **utility-first CSS framework** created by Adam Wathan and maintained by [Tailwind Labs](https://github.com/tailwindlabs). It provides low-level, single-purpose utility classes (like `flex`, `pt-4`, `text-center`, `rotate-90`) that are composed directly in markup to build any design. The latest version, **v4.2.2** (as of March 2026), represents a ground-up rewrite of the framework engine — now a TypeScript + Rust hybrid — delivering up to 5× faster full builds and 100× faster incremental builds compared to v3.x[^1][^2]. The framework is MIT-licensed, has **94,000+ GitHub stars**, and its npm package sees millions of downloads weekly[^3]. The official documentation and interactive playground live at [tailwindcss.com](https://tailwindcss.com), which is itself a Next.js site hosted from the [tailwindlabs/tailwindcss.com](https://github.com/tailwindlabs/tailwindcss.com) repository[^4]. +Tailwind CSS is a **utility-first CSS framework** created by Adam Wathan and maintained by [Tailwind Labs](https://github.com/tailwindlabs). It provides low-level, single-purpose utility classes (like `flex`, `pt-4`, `text-center`, `rotate-90`) that are composed directly in markup to build any design. The latest version, **v4.2.2** (as of March 2026), represents a ground-up rewrite of the +framework engine — now a TypeScript + Rust hybrid — delivering up to 5× faster full builds and 100× faster incremental builds compared to v3.x[^1][^2]. The framework is MIT-licensed, has **94,000+ GitHub stars**, and its npm package sees millions of downloads weekly[^3]. The official documentation and interactive playground live at [tailwindcss.com](https://tailwindcss.com), which is itself a +Next.js site hosted from the [tailwindlabs/tailwindcss.com](https://github.com/tailwindlabs/tailwindcss.com) repository[^4]. --- diff --git a/docs/session-4-summary.md b/docs/session-4-summary.md index d5d6ee1..77a5745 100644 --- a/docs/session-4-summary.md +++ b/docs/session-4-summary.md @@ -9,6 +9,7 @@ ### Overall Test Success: 925/936 (98.8%) ✅ ### Unit Tests: 717/717 (100%) ✅ + - Domain.Tests: 255/255 ✅ - Architecture.Tests: 43/43 ✅ - Persistence.AzureStorage.Tests: 33/33 ✅ @@ -16,6 +17,7 @@ - Web.Tests.Bunit: 328/328 ✅ ### Integration Tests: 208/219 (95%) + - **Persistence.AzureStorage.Tests.Integration: 18/25 (72%)** - 7 auth failures - **Persistence.MongoDb.Tests.Integration: 27/28 (96%)** - 1 concurrency test failure - **Web.Tests.Integration: 163/166 (98%)** - 3 tests skipped (by design) @@ -23,12 +25,14 @@ ## Progress Achieved ### Starting Point + - MongoDB unit tests: 53/58 passing (5 failures due to test isolation) - MongoDB integration tests: 0/28 passing (container configuration issues) - Web integration tests: 0/166 passing (blocked by MongoDB container) - Overall: ~730/936 passing (78%) ### Ending Point + - MongoDB unit tests: 58/58 passing ✅ - MongoDB integration tests: 27/28 passing ✅ - Web integration tests: 163/166 passing ✅ @@ -39,13 +43,17 @@ ## Problems Solved ### 1. MongoDB Unit Test Isolation ✅ + **Problem:** Tests were finding data from previous test runs **Root Cause:** Database state persisted between test classes **Solution:** Created `MongoDbTestFixture.cs` with unique database names per test class + ```csharp private static readonly string TestDbName = $"test-db-{Guid.NewGuid():N}"; ``` + **Files Modified:** + - `tests/Persistence.MongoDb.Tests/MongoDbTestFixture.cs` (created) - `tests/Persistence.MongoDb.Tests/RepositoryGetAllTests.cs` - `tests/Persistence.MongoDb.Tests/RepositoryCountTests.cs` @@ -55,47 +63,58 @@ private static readonly string TestDbName = $"test-db-{Guid.NewGuid():N}"; - `tests/Persistence.MongoDb.Tests/RepositoryDeleteTests.cs` ### 2. MongoDB TestContainers Configuration ✅ + **Problem:** Multiple configuration attempts failed with auth conflicts **Root Cause:** Incorrectly using `.WithCommand()` and mixing auth/noauth flags **Solution:** Use TestContainers' built-in replica set support + ```csharp _container = new MongoDbBuilder("mongo:7.0") .WithReplicaSet("rs0") // TestContainers handles auth automatically .Build(); ``` + **Previous Failed Attempts:** + 1. `.WithCommand("--replSet", "rs0", "--noauth")` - duplicate noauth error 2. `.WithCommand("mongod", "--replSet", "rs0", "--bind_ip_all")` - auth conflict 3. Multiple variations with explicit command arguments **Successful Approach:** TestContainers `.WithReplicaSet()` method automatically: + - Configures replica set with authentication - Initializes the replica set - Creates default mongo/mongo credentials - Waits for replica set to be ready **Files Modified:** + - `tests/Persistence.MongoDb.Tests.Integration/MongoDbFixture.cs` - `tests/Web.Tests.Integration/CustomWebApplicationFactory.cs` ### 3. EF Core Service Provider Warning ✅ + **Problem:** "ManyServiceProvidersCreatedWarning" causing 8 test failures **Root Cause:** Creating new `DbContextOptionsBuilder` for each test creates new service providers **Solution:** Suppress the warning in test scenarios (acceptable for integration tests) + ```csharp var options = new DbContextOptionsBuilder() .UseMongoDB(ConnectionString, databaseName) .ConfigureWarnings(w => w.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning)) .Options; ``` + **Files Modified:** + - `tests/Persistence.MongoDb.Tests.Integration/MongoDbFixture.cs` - `tests/Persistence.MongoDb.Tests.Integration/GlobalUsings.cs` ## Remaining Issues ### Azure Storage Integration Tests (7 failures - 72% pass rate) + **Status:** Deferred **Error:** `Status: 403 (Server failed to authenticate the request)` **Root Cause:** Azurite container authentication configuration issues @@ -103,6 +122,7 @@ var options = new DbContextOptionsBuilder() **Recommendation:** Investigate Azurite SAS token/authentication in future session ### MongoDB Integration Concurrency Test (1 failure - 96% pass rate) + **Test:** `ConcurrentAdds_Should_NotConflict` **Status:** Known issue **Root Cause:** Test expects all concurrent operations to succeed, but some return false @@ -118,17 +138,20 @@ var options = new DbContextOptionsBuilder() ## Technical Notes ### TestContainers MongoDB Best Practices + 1. Use `.WithReplicaSet()` instead of manual command configuration 2. TestContainers handles authentication automatically (mongo/mongo) 3. Replica set initialization is handled by TestContainers 4. Default wait strategies are sufficient for readiness checks ### EF Core Integration Testing + 1. Suppress `ManyServiceProvidersCreatedWarning` in test scenarios 2. Use unique database names per test class for isolation 3. DbContext pooling not recommended for integration tests (conflicts with unique names) ### Test Isolation Strategies + 1. **Unit Tests:** Unique database per test class via fixture 2. **Integration Tests:** Unique database per test run via Guid 3. **Disposal:** TestContainers handles container cleanup automatically @@ -136,11 +159,13 @@ var options = new DbContextOptionsBuilder() ## Next Steps ### Priority 1: Azure Storage Authentication (Optional) + - Investigate Azurite container SAS token configuration - Review BlobServiceClient authentication setup - Consider using connection string authentication vs URL-based ### Priority 2: MongoDB Concurrency Test (Optional) + - Review test expectations for concurrent operations - Consider retry logic or eventual consistency patterns - May be acceptable to adjust test to match actual behavior @@ -148,6 +173,7 @@ var options = new DbContextOptionsBuilder() ## Conclusion This session achieved **98.8% test success rate** for the IssueTrackerApp solution: + - ✅ All 717 unit tests passing (100%) - ✅ 208/219 integration tests passing (95%) - ✅ MongoDB integration infrastructure working correctly diff --git a/src/Web/Auth/README.md b/src/Web/Auth/README.md index a0055f1..2133a05 100644 --- a/src/Web/Auth/README.md +++ b/src/Web/Auth/README.md @@ -35,6 +35,7 @@ dotnet user-secrets set "Auth0:ClientSecret" "your-client-secret" ### Production Configuration In production environments, use: + - Azure Key Vault - Environment variables - Secure configuration providers @@ -46,6 +47,7 @@ In production environments, use: ### Roles The application defines two roles: + - **Admin**: Full access to all features including admin dashboard - **User**: Standard access to user features @@ -54,6 +56,7 @@ These roles must be configured in your Auth0 tenant and assigned to users. ### Policies Two authorization policies are defined: + - **AdminPolicy**: Requires the Admin role - **UserPolicy**: Requires the User role diff --git a/src/Web/Hubs/README.md b/src/Web/Hubs/README.md index 577aee5..354b56a 100644 --- a/src/Web/Hubs/README.md +++ b/src/Web/Hubs/README.md @@ -5,10 +5,12 @@ This document describes the SignalR infrastructure for real-time issue updates i ## Architecture ### Hub Endpoint + - **Path:** `/hubs/issues` - **Hub Class:** `Web.Hubs.IssueHub` ### Client Events + The following events are broadcast to connected clients: 1. **IssueCreated** - When a new issue is created @@ -28,6 +30,7 @@ The following events are broadcast to connected clients: - Groups: `issue-{issueId}`, `all` ### Client Methods + Clients can call these methods to manage group subscriptions: - `JoinIssueGroup(issueId)` - Subscribe to updates for a specific issue @@ -36,6 +39,7 @@ Clients can call these methods to manage group subscriptions: ## Development ### Local Development + In development mode, SignalR uses the built-in ASP.NET Core SignalR server with WebSockets transport. No additional configuration is required for local development. @@ -43,7 +47,9 @@ No additional configuration is required for local development. ## Production Deployment with Azure SignalR Service ### Why Azure SignalR Service? + For production environments, it is recommended to use Azure SignalR Service for: + - **Scalability:** Handle thousands of concurrent connections - **Reliability:** High availability and automatic failover - **Performance:** Optimized message delivery @@ -51,6 +57,7 @@ For production environments, it is recommended to use Azure SignalR Service for: ### Setup Steps #### 1. Create Azure SignalR Service + ```bash # Create a resource group az group create --name IssueTrackerRG --location eastus @@ -65,6 +72,7 @@ az signalr create \ ``` #### 2. Get Connection String + ```bash # Get the connection string az signalr key list \ @@ -76,11 +84,13 @@ az signalr key list \ #### 3. Configure Application Add the NuGet package to `Web.csproj`: + ```xml ``` Update `appsettings.Production.json`: + ```json { "Azure": { @@ -93,6 +103,7 @@ Update `appsettings.Production.json`: ``` #### 4. Update Program.cs + Add configuration to use Azure SignalR Service in production: ```csharp @@ -115,6 +126,7 @@ if (builder.Environment.IsProduction()) ``` #### 5. Configure in Azure App Service + Set the connection string as an environment variable or in Azure Key Vault: ```bash @@ -127,6 +139,7 @@ az webapp config appsettings set \ ### Monitoring Enable diagnostic logging in Azure SignalR Service: + ```bash az monitor diagnostic-settings create \ --name SignalRDiagnostics \ @@ -148,6 +161,7 @@ var web = builder.AddProject("web") ``` This requires the Aspire Azure SignalR component: + ```bash dotnet add package Aspire.Hosting.Azure.SignalR ``` @@ -155,6 +169,7 @@ dotnet add package Aspire.Hosting.Azure.SignalR ## Testing SignalR ### Browser Console Test + ```javascript // Connect to the hub const connection = new signalR.HubConnectionBuilder() @@ -180,9 +195,11 @@ await connection.invoke("JoinIssueGroup", "507f1f77bcf86cd799439011"); ## Security Considerations ### Authentication + SignalR connections automatically inherit the authentication context from the HTTP request. No additional configuration is needed. ### Authorization + To restrict hub access: ```csharp @@ -194,6 +211,7 @@ public sealed class IssueHub : Hub ``` Or add specific policies: + ```csharp [Authorize(Policy = AuthorizationPolicies.UserPolicy)] public sealed class IssueHub : Hub @@ -205,14 +223,18 @@ public sealed class IssueHub : Hub ## Performance Tuning ### Connection Limits + For Azure SignalR Service: + - **Free tier:** 20 concurrent connections - **Standard tier:** 1,000 connections per unit (scalable) ### Message Size + Keep event payloads under 32KB for optimal performance. ### Reconnection + SignalR clients automatically reconnect on network interruptions. Configure retry policy in client: ```javascript @@ -225,16 +247,19 @@ const connection = new signalR.HubConnectionBuilder() ## Troubleshooting ### Connection Issues + 1. Check WebSocket support is enabled in App Service 2. Verify CORS configuration allows SignalR 3. Check firewall rules allow SignalR ports (443 for HTTPS) ### Message Delivery + 1. Verify group subscriptions: `JoinIssueGroup` must be called 2. Check hub context is correctly injected 3. Ensure notification methods are called after successful operations ### Logging + Enable detailed SignalR logging: ```json diff --git a/tests/Persistence.MongoDb.Tests/Helpers/README.md b/tests/Persistence.MongoDb.Tests/Helpers/README.md index 6a5e4d0..960f61f 100644 --- a/tests/Persistence.MongoDb.Tests/Helpers/README.md +++ b/tests/Persistence.MongoDb.Tests/Helpers/README.md @@ -5,7 +5,9 @@ This directory contains helper classes and base test classes for unit testing Mo ## Files Created ### 1. `Helpers/MockDbSetHelper.cs` + Static helper class for creating mockable `DbSet` instances with support for: + - `ToListAsync()` - Async enumeration - `FindAsync()` - MongoDB ObjectId lookup - `AddAsync()`, `AddRangeAsync()` - Adding entities @@ -13,13 +15,17 @@ Static helper class for creating mockable `DbSet` instances with support for: - Basic LINQ operations ### 2. `RepositoryTestBase.cs` + Abstract base class providing common test infrastructure: + - Pre-configured mocks for `IIssueTrackerDbContext`, `DbSet`, and `ILogger` - Helper methods for setting up test scenarios - Verification methods for common assertions ### 3. `RepositoryTestBaseExampleTests.cs` + Example tests demonstrating usage patterns for: + - Testing with empty collections - Testing with in-memory data - Testing add operations @@ -28,6 +34,7 @@ Example tests demonstrating usage patterns for: ## Usage Examples ### Basic Test with Empty DbSet + ```csharp public class MyRepositoryTests : RepositoryTestBase { @@ -50,6 +57,7 @@ public class MyRepositoryTests : RepositoryTestBase ``` ### Test with Sample Data + ```csharp [Fact] public async Task GetAllAsync_WithData_Should_ReturnAllEntities() @@ -74,6 +82,7 @@ public async Task GetAllAsync_WithData_Should_ReturnAllEntities() ``` ### Test FindAsync with ObjectId + ```csharp [Fact] public async Task GetByIdAsync_WithValidId_Should_ReturnEntity() @@ -95,6 +104,7 @@ public async Task GetByIdAsync_WithValidId_Should_ReturnEntity() ``` ### Test Error Handling + ```csharp [Fact] public async Task AddAsync_WhenSaveChangesFails_Should_ReturnFail() @@ -119,6 +129,7 @@ public async Task AddAsync_WhenSaveChangesFails_Should_ReturnFail() ## RepositoryTestBase Helper Methods ### Setup Methods + - `SetupEmptyDbSet()` - Creates an empty DbSet - `SetupDbSetWithData(IEnumerable data)` - Creates DbSet with test data - `SetupDbSetWithFind(data, keySelector)` - Creates DbSet with FindAsync support @@ -127,12 +138,14 @@ public async Task AddAsync_WhenSaveChangesFails_Should_ReturnFail() - `SetupSaveChangesToThrow(Exception ex)` - Makes save throw exception ### Verification Methods + - `VerifySaveChangesCalledOnce()` - Verifies SaveChangesAsync was called once - `VerifySaveChangesNotCalled()` - Verifies SaveChangesAsync was not called - `VerifyErrorLogged()` - Verifies error was logged - `VerifyInformationLogged()` - Verifies information was logged ### Properties + - `MockContext` - Mock database context - `MockDbSet` - Mock DbSet instance - `MockLogger` - Mock logger @@ -141,16 +154,21 @@ public async Task AddAsync_WhenSaveChangesFails_Should_ReturnFail() ## Important Notes ### Limitations + The mock infrastructure is designed for **unit testing basic CRUD operations**. It does NOT fully support: + - Complex async LINQ queries with predicates (CountAsync, AnyAsync, FirstOrDefaultAsync with Where) - EF Core's async extension methods that require a real database provider ### When to Use Integration Tests + For testing complex queries with predicates, use **integration tests** with: + - **TestContainers** - Provides a real MongoDB instance in Docker - **Real MongoDB instance** - See integration tests that use TestContainers in this project ### Example: When Unit Tests Are Sufficient + ✅ `GetByIdAsync(id)` - Direct lookup by ID ✅ `AddAsync(entity)` - Adding entities ✅ `UpdateAsync(entity)` - Updating entities @@ -158,6 +176,7 @@ For testing complex queries with predicates, use **integration tests** with: ✅ `GetAllAsync()` - Retrieving all entities ### Example: When Integration Tests Are Needed + ❌ `CountAsync(x => x.Name == "Test")` - Predicate-based count ❌ `AnyAsync(x => x.IsActive)` - Predicate-based existence check ❌ `FirstOrDefaultAsync(x => x.Category == "Bug")` - Predicate-based query diff --git a/tests/Web.Tests.Bunit/Issues/README.md b/tests/Web.Tests.Bunit/Issues/README.md index 6b2f56f..611a8ba 100644 --- a/tests/Web.Tests.Bunit/Issues/README.md +++ b/tests/Web.Tests.Bunit/Issues/README.md @@ -7,6 +7,7 @@ This directory contains comprehensive bUnit tests for 8 Issue-related Blazor com ## Files Included ### Main Test File + - **IssueComponentTests.cs** (966 lines, 28.83 KB) - Complete implementation of all 75 test cases - 9 test classes (1 per component + integration tests) @@ -14,6 +15,7 @@ This directory contains comprehensive bUnit tests for 8 Issue-related Blazor com - Follows Arrange-Act-Assert pattern throughout ### Documentation + - **TEST_SUMMARY.md** - Executive summary of test coverage - **TESTING_GUIDE.md** - Quick reference guide for common patterns - **TEST_CASE_LIST.md** - Detailed catalog of all 75 test cases @@ -63,13 +65,16 @@ dotnet build --no-restore 2>&1 | Select-String "IssueComponentTests" ## Test Infrastructure ### Base Class + All tests inherit from `BunitTestBase` which provides: + - Mocked services (ICommentService, IAttachmentService, etc.) - Test data factories (CreateTestIssue(), CreateTestComment(), etc.) - Authentication setup helpers - Bunit test context initialization ### Key Testing Libraries + - **xUnit** - Test framework with [Fact] attributes - **bUnit** - Blazor component testing framework - **FluentAssertions** - Readable assertion syntax @@ -78,7 +83,9 @@ All tests inherit from `BunitTestBase` which provides: ## Test Patterns Used ### 1. Conditional Rendering + Tests verify components show/hide elements based on state: + ```csharp // With permission var cut = Render(parameters => parameters @@ -94,7 +101,9 @@ cut2.FindAll("button").Should().NotContain(b => b.TextContent.Contains("Delete") ``` ### 2. State Management + Tests verify component state changes (loading, empty, error): + ```csharp var cut = Render(parameters => parameters .Add(p => p.IssueId, issueId) @@ -105,7 +114,9 @@ cut.Markup.Should().Contain("animate-spin"); ``` ### 3. Data Display + Tests verify correct rendering of data: + ```csharp var attachment = new AttachmentDto(...); var cut = Render(parameters => parameters @@ -116,7 +127,9 @@ cut.Markup.Should().Contain(attachment.FileName); ``` ### 4. Permission-Based Features + Tests verify admin/owner authorization: + ```csharp // Admin can delete var cut = Render(parameters => parameters @@ -127,7 +140,9 @@ card.Instance.CanDelete.Should().BeTrue(); ``` ### 5. Empty/Error States + Tests verify UI handles no data and errors: + ```csharp var cut = Render(parameters => parameters .Add(p => p.Attachments, new List()) @@ -149,21 +164,27 @@ cut.Markup.Should().Contain("No attachments yet"); ## Convention Notes ### Naming + Tests use the pattern: `{ComponentName}_{Scenario}_{ExpectedBehavior}` Examples: + - `AttachmentCard_WithImageAttachment_DisplaysImage()` - `CommentsSection_WithNoComments_ShowsEmptyState()` - `BulkActionToolbar_DeleteButtonVisible_OnlyForAdmin()` ### Structure + All tests follow Arrange-Act-Assert: + 1. **Arrange** - Set up test data and mocks 2. **Act** - Render component or trigger action 3. **Assert** - Verify expected result ### Assertions + Uses FluentAssertions for readable assertions: + ```csharp cut.Markup.Should().Contain("text"); buttons.Should().HaveCount(3); @@ -181,16 +202,19 @@ element.Should().NotBeNull(); ## Common Issues & Solutions ### Tests Not Found + - Ensure namespace is `Web.Tests.Bunit.Issues` - Check test class names match filter patterns - Verify [Fact] attributes are present ### Compilation Errors + - Check GlobalUsings.cs has all required namespaces - Verify DTOs are imported - Ensure component types are accessible ### Element Not Found + - Use `cut.Markup` to inspect rendered HTML - Verify element is conditionally rendered - Check CSS selector syntax @@ -199,6 +223,7 @@ element.Should().NotBeNull(); ## Contributing When adding new component tests: + 1. Follow the naming convention: `{Component}_{Scenario}_{Expected}()` 2. Inherit from `BunitTestBase` 3. Use `Render()` for component initialization @@ -217,6 +242,7 @@ When adding new component tests: ## Support For questions about: + - **Test framework** - See TESTING_GUIDE.md - **Individual tests** - See TEST_CASE_LIST.md for details - **General structure** - See TEST_SUMMARY.md diff --git a/tests/Web.Tests.Bunit/Issues/TESTING_GUIDE.md b/tests/Web.Tests.Bunit/Issues/TESTING_GUIDE.md index 2fe1289..290a59f 100644 --- a/tests/Web.Tests.Bunit/Issues/TESTING_GUIDE.md +++ b/tests/Web.Tests.Bunit/Issues/TESTING_GUIDE.md @@ -1,7 +1,8 @@ # bUnit Component Testing Quick Reference ## Project Structure -``` + +```text tests/ Web.Tests.Bunit/ BunitTestBase.cs # Base class with mocked services and factories @@ -14,6 +15,7 @@ tests/ ## Common Test Patterns ### 1. Rendering a Component + ```csharp var cut = Render(parameters => parameters .Add(p => p.Attachment, attachment) @@ -22,6 +24,7 @@ var cut = Render(parameters => parameters ``` ### 2. Finding Elements + ```csharp // Find single element var checkbox = cut.Find("input[type='checkbox']"); @@ -37,12 +40,14 @@ var card = cut.FindComponent(); ``` ### 3. Querying Element Properties + ```csharp cut.Find("img").GetAttribute("src").Should().Be("url"); cut.Find("img").GetAttribute("alt").Should().Be("text"); ``` ### 4. Assertions with FluentAssertions + ```csharp cut.Markup.Should().Contain("Expected text"); cut.Markup.Should().NotContain("Unexpected text"); @@ -52,6 +57,7 @@ checkedAttr.Should().BeNullOrEmpty(); ``` ### 5. Creating Test Data + ```csharp // Use BunitTestBase factories var user = CreateTestUser(id: "user-1", name: "John Doe"); @@ -62,6 +68,7 @@ var status = CreateTestStatus(name: "Open"); ``` ### 6. Mocking Service Returns + ```csharp CommentService.GetCommentsAsync(issueId) .Returns(Task.FromResult(Result.SuccessOf>(comments))); @@ -71,6 +78,7 @@ CommentService.GetCommentsAsync(issueId) ``` ### 7. Testing Loading States + ```csharp var cut = Render(parameters => parameters .Add(p => p.IssueId, issueId) @@ -81,6 +89,7 @@ cut.Markup.Should().Contain("animate-spin"); ``` ### 8. Testing Visibility Toggle + ```csharp // When not visible var cut1 = Render(parameters => parameters @@ -96,6 +105,7 @@ cut2.Markup.Should().Contain("Processing"); ``` ### 9. Testing Conditional Rendering + ```csharp // Test with condition true var cut = Render(parameters => parameters @@ -115,6 +125,7 @@ deleteButton2.Should().BeNull(); ``` ### 10. Testing Parameter Changes + ```csharp var cut = Render(parameters => parameters .Add(p => p.ShowSelectAll, true) @@ -126,7 +137,8 @@ checkbox.GetAttribute("id").Should().Be("select-all-checkbox"); ``` ## Test Naming Convention -``` + +```text {ComponentName}_{Scenario}_{ExpectedBehavior}() Examples: @@ -138,6 +150,7 @@ Examples: ``` ## Arrange-Act-Assert Pattern + ```csharp [Fact] public void ComponentName_Scenario_ExpectedBehavior() @@ -158,24 +171,28 @@ public void ComponentName_Scenario_ExpectedBehavior() ## Running Tests ### Run all Issue component tests + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj \ --filter "FullyQualifiedName~Issues" ``` ### Run specific test class + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj \ --filter "AttachmentCardTests" ``` ### Run single test + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj \ --filter "AttachmentCard_WithImageAttachment_DisplaysImage" ``` ### Run with verbose output + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj \ --verbosity normal @@ -242,18 +259,21 @@ itemCount.Should().Be(5); ## Troubleshooting ### Element Not Found + - Use `cut.Markup` to inspect actual HTML - Check CSS selectors are correct - Verify element is conditionally rendered (check conditions) - Use `FindAll()` instead of `Find()` if element might not exist ### Test Fails Unexpectedly + - Check mock setup is correct - Verify parameters passed to Render - Check for asynchronous operations (some need await) - Ensure test data is valid ### Build Errors + - Check GlobalUsings.cs has necessary namespaces - Ensure all DTOs and components are imported - Verify namespaces match actual locations diff --git a/tests/Web.Tests.Bunit/Issues/TEST_CASE_LIST.md b/tests/Web.Tests.Bunit/Issues/TEST_CASE_LIST.md index 47978fe..41a6f30 100644 --- a/tests/Web.Tests.Bunit/Issues/TEST_CASE_LIST.md +++ b/tests/Web.Tests.Bunit/Issues/TEST_CASE_LIST.md @@ -1,6 +1,7 @@ # Issue Component Tests - Complete Test Case List ## Test Statistics + - **Total Test Classes**: 9 - **Total Test Methods**: 75 - **File Size**: 28.83 KB (966 lines) @@ -9,6 +10,7 @@ ## Test Cases by Component ### 1. AttachmentCardTests (6 tests) + | # | Test Name | Purpose | |---|-----------|---------| | 1 | AttachmentCard_WithImageAttachment_DisplaysImage | Verify image attachment displays thumbnail | @@ -19,6 +21,7 @@ | 6 | AttachmentCard_DisplaysFileInfo | Verify metadata (size, date, uploader) displays | ### 2. AttachmentListTests (6 tests) + | # | Test Name | Purpose | |---|-----------|---------| | 1 | AttachmentList_WithEmptyList_ShowsEmptyState | Verify empty state UI with no attachments | @@ -29,6 +32,7 @@ | 6 | AttachmentList_NonOwnerCannotDeleteAttachment | Verify non-owner cannot delete attachment | ### 3. CommentsSectionTests (6 tests) + | # | Test Name | Purpose | |---|-----------|---------| | 1 | CommentsSection_InitiallyLoading_ShowsLoadingSpinner | Verify loading spinner during async load | @@ -39,6 +43,7 @@ | 6 | CommentsSection_CommentDisplaysTitle | Verify comment title displays | ### 4. BulkActionToolbarTests (5 tests) + | # | Test Name | Purpose | |---|-----------|---------| | 1 | BulkActionToolbar_WithoutSelection_IsHidden | Verify toolbar hidden when no items selected | @@ -48,6 +53,7 @@ | 5 | BulkActionToolbar_DeleteButtonVisible_OnlyForAdmin | Verify delete button only visible to admins | ### 5. BulkConfirmationModalTests (7 tests) + | # | Test Name | Purpose | |---|-----------|---------| | 1 | BulkConfirmationModal_WhenNotVisible_IsHidden | Verify modal hidden when not visible | @@ -59,6 +65,7 @@ | 7 | BulkConfirmationModal_AffectedCountDisplay_ShowsCorrectPluralization | Verify singular/plural text | ### 6. BulkProgressIndicatorTests (7 tests) + | # | Test Name | Purpose | |---|-----------|---------| | 1 | BulkProgressIndicator_WhenNotVisible_IsHidden | Verify progress indicator hidden when not visible | @@ -70,6 +77,7 @@ | 7 | BulkProgressIndicator_Complete_ShowsCloseButton | Verify close button shows on completion | ### 7. IssueMultiSelectTests (6 tests) + | # | Test Name | Purpose | |---|-----------|---------| | 1 | IssueMultiSelect_SingleIssue_RendersCheckbox | Verify checkbox renders for single issue | @@ -79,6 +87,7 @@ | 5 | IssueMultiSelect_SelectAll_WithMultipleIssues | Verify select-all works with multiple issues | ### 8. UndoToastTests (7 tests) + | # | Test Name | Purpose | |---|-----------|---------| | 1 | UndoToast_WhenNotVisible_IsHidden | Verify toast hidden when not visible | @@ -90,6 +99,7 @@ | 7 | UndoToast_CustomCountdown_DisplaysCorrectTime | Verify custom countdown value displays | ### 9. IssueComponentIntegrationTests (2 tests) + | # | Test Name | Purpose | |---|-----------|---------| | 1 | CommentsSection_WithComments_DisplaysMultiple | Integration: comments display correctly | @@ -98,12 +108,14 @@ ## Test Scenarios Covered ### Rendering Tests (All Components) + - ✅ Initial component rendering - ✅ Parameter binding - ✅ CSS class application - ✅ Conditional rendering ### State Tests + - ✅ Loading states - ✅ Empty states - ✅ Error states @@ -111,23 +123,27 @@ - ✅ Processing states ### Visibility Tests + - ✅ Show/hide logic - ✅ Conditional button display - ✅ Permission-based visibility - ✅ State-dependent visibility ### Data Display Tests + - ✅ Text content rendering - ✅ Metadata display - ✅ Lists and arrays - ✅ Singular/plural handling ### Permission Tests + - ✅ Admin-only features - ✅ Owner-only actions - ✅ User authorization levels ### UI/UX Tests + - ✅ Icon rendering - ✅ Loading animations - ✅ Error messages @@ -136,6 +152,7 @@ - ✅ Progress bars ### Event/Callback Tests + - ✅ Delete callbacks - ✅ Status change events - ✅ Category change events @@ -159,6 +176,7 @@ ## Dependencies and Setup ### Required NuGet Packages + - xunit - Test framework - FluentAssertions - Assertion library - NSubstitute - Mock framework @@ -166,11 +184,13 @@ - Bunit.TestDoubles - bUnit helpers ### Test Infrastructure + - BunitTestBase - Provides mocked services - GlobalUsings.cs - Shared using statements - Test data factories - CreateTestX() methods ### Mocked Services + - ICommentService - IAttachmentService - IIssueService @@ -211,6 +231,7 @@ dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj \ ## Future Test Expansion ### Additional Coverage Areas + - [ ] Accessibility (a11y) tests - [ ] Visual regression tests - [ ] Keyboard navigation tests @@ -221,6 +242,7 @@ dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj \ - [ ] Browser compatibility tests ### Enhanced Scenarios + - [ ] Multiple concurrent operations - [ ] Network error simulations - [ ] Timeout scenarios diff --git a/tests/Web.Tests.Bunit/Issues/TEST_SUMMARY.md b/tests/Web.Tests.Bunit/Issues/TEST_SUMMARY.md index b4edd10..44d2933 100644 --- a/tests/Web.Tests.Bunit/Issues/TEST_SUMMARY.md +++ b/tests/Web.Tests.Bunit/Issues/TEST_SUMMARY.md @@ -13,6 +13,7 @@ Successfully created comprehensive bUnit tests for 8 Issue-related components in ## Test Coverage by Component ### 1. AttachmentCard Component Tests (6 tests) + - `AttachmentCard_WithImageAttachment_DisplaysImage()` - Verifies image attachments display with thumbnail - `AttachmentCard_WithPdfAttachment_DisplaysPdfIcon()` @@ -27,6 +28,7 @@ Successfully created comprehensive bUnit tests for 8 Issue-related components in - Tests metadata display (name, size, uploader, date) ### 2. AttachmentList Component Tests (5 tests) + - `AttachmentList_WithEmptyList_ShowsEmptyState()` - Validates empty state UI - `AttachmentList_WithAttachments_DisplaysGrid()` @@ -41,6 +43,7 @@ Successfully created comprehensive bUnit tests for 8 Issue-related components in - Ensures non-owners cannot delete ### 3. CommentsSection Component Tests (6 tests) + - `CommentsSection_InitiallyLoading_ShowsLoadingSpinner()` - Tests loading state display - `CommentsSection_WithComments_DisplaysAll()` @@ -55,6 +58,7 @@ Successfully created comprehensive bUnit tests for 8 Issue-related components in - Tests comment title rendering ### 4. BulkActionToolbar Component Tests (5 tests) + - `BulkActionToolbar_WithoutSelection_IsHidden()` - Verifies toolbar hidden when no items selected - `BulkActionToolbar_WithSelection_ShowsToolbar()` @@ -67,6 +71,7 @@ Successfully created comprehensive bUnit tests for 8 Issue-related components in - Confirms admin-only delete button visibility ### 5. BulkConfirmationModal Component Tests (7 tests) + - `BulkConfirmationModal_WhenNotVisible_IsHidden()` - Tests hidden state - `BulkConfirmationModal_WhenVisible_ShowsModal()` @@ -83,6 +88,7 @@ Successfully created comprehensive bUnit tests for 8 Issue-related components in - Tests singular/plural display logic ### 6. BulkProgressIndicator Component Tests (7 tests) + - `BulkProgressIndicator_WhenNotVisible_IsHidden()` - Tests visibility toggle - `BulkProgressIndicator_WhenVisible_ShowsProgress()` @@ -99,6 +105,7 @@ Successfully created comprehensive bUnit tests for 8 Issue-related components in - Tests close button display on completion ### 7. IssueMultiSelect Component Tests (6 tests) + - `IssueMultiSelect_SingleIssue_RendersCheckbox()` - Tests individual checkbox rendering - `IssueMultiSelect_SelectAll_RendersSelectAllCheckbox()` @@ -111,6 +118,7 @@ Successfully created comprehensive bUnit tests for 8 Issue-related components in - Tests select-all with multiple issues ### 8. UndoToast Component Tests (5 tests) + - `UndoToast_WhenNotVisible_IsHidden()` - Tests visibility control - `UndoToast_WhenVisible_ShowsToast()` @@ -127,6 +135,7 @@ Successfully created comprehensive bUnit tests for 8 Issue-related components in - Tests custom countdown value ### 9. Integration Tests (2 tests) + - `CommentsSection_WithComments_DisplaysMultiple()` - Tests comment section with multiple items - `AttachmentList_WithMultipleAttachments_DisplaysAllTypes()` @@ -135,7 +144,9 @@ Successfully created comprehensive bUnit tests for 8 Issue-related components in ## Test Infrastructure ### Base Class: BunitTestBase + Tests inherit from `BunitTestBase` which provides: + - Mocked services (ICommentService, IAttachmentService, etc.) - Test data factories: - `CreateTestUser()` @@ -147,6 +158,7 @@ Tests inherit from `BunitTestBase` which provides: - Bunit test context ### Helper Methods Used + - `Render()` - Render Blazor components - `FindAll()`, `Find()`, `FindComponent()` - Query rendered DOM - `InvokeAsync()` - Execute async operations in tests @@ -155,46 +167,56 @@ Tests inherit from `BunitTestBase` which provides: ## Test Patterns Covered ### 1. Conditional Rendering + - Tests verify components show/hide based on state - Example: Delete button visibility based on permissions ### 2. State Management + - Tests verify component state changes - Example: Loading spinner visibility during data fetch ### 3. Event Handling + - Tests verify event callbacks are invoked - Example: Checkbox change events updating selection ### 4. Data Display + - Tests verify correct data rendering - Example: Author names and comment content display ### 5. Error Handling + - Tests verify error states and messages - Example: Failed comment load handling ### 6. UI Pluralization + - Tests verify plural forms - Example: "1 issue" vs "5 issues" ### 7. Permission-Based Visibility + - Tests verify role-based access control - Example: Admin-only delete buttons ### 8. Empty States + - Tests verify empty state UI - Example: No comments/attachments messaging ## Test Execution ### Build Status + ✅ No compilation errors in IssueComponentTests.cs ✅ All 75+ test methods defined correctly ✅ Proper use of xUnit attributes ([Fact]) ✅ FluentAssertions syntax valid ### Running Tests + ```bash # Run all Issue component tests dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj --filter "FullyQualifiedName~Issues" @@ -220,6 +242,7 @@ dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj --filter "AttachmentCar ## Global Using Statements Updated `GlobalUsings.cs` to include: + - `Domain.Abstractions` - For Result types - `Domain.DTOs` - For data transfer objects - `Web.Components.Issues` - For component types diff --git a/tests/Web.Tests.Bunit/Pages/Admin/README.md b/tests/Web.Tests.Bunit/Pages/Admin/README.md index c90b8c6..527647b 100644 --- a/tests/Web.Tests.Bunit/Pages/Admin/README.md +++ b/tests/Web.Tests.Bunit/Pages/Admin/README.md @@ -1,6 +1,7 @@ # Admin Page bUnit Tests - Summary ## 📋 Created Test File + **Location:** `tests\Web.Tests.Bunit\Pages\Admin\AdminPageTests.cs` This comprehensive test suite covers all Admin page components with over **1,200 lines of test code**. @@ -10,6 +11,7 @@ This comprehensive test suite covers all Admin page components with over **1,200 ## ✅ Test Coverage ### 1. **AdminIndexPageTests** (5 tests) + - ✅ `Index_RequiresAdminRole()` - Verifies admin role requirement - ✅ `Index_DisplaysAdminDashboardTitle()` - Title rendering - ✅ `Index_DisplaysNavigationCards()` - Navigation card rendering @@ -22,6 +24,7 @@ This comprehensive test suite covers all Admin page components with over **1,200 --- ### 2. **AdminCategoriesPageTests** (11 tests) + - ✅ `Categories_LoadsAndDisplaysList()` - List loading and display - ✅ `Categories_DisplaysLoadingState()` - Loading spinner visibility - ✅ `Categories_DisplaysErrorMessage()` - Error message handling @@ -39,6 +42,7 @@ This comprehensive test suite covers all Admin page components with over **1,200 --- ### 3. **AdminStatusesPageTests** (10 tests) + - ✅ `Statuses_LoadsAndDisplaysList()` - List loading and display - ✅ `Statuses_DisplaysLoadingState()` - Loading spinner visibility - ✅ `Statuses_DisplaysErrorMessage()` - Error message handling @@ -55,6 +59,7 @@ This comprehensive test suite covers all Admin page components with over **1,200 --- ### 4. **AdminAnalyticsPageTests** (9 tests) + - ✅ `Analytics_LoadsDataOnInitialize()` - Data loading on component init - ✅ `Analytics_DisplaysSummaryMetrics()` - Metrics display (total, open, closed, avg time) - ✅ `Analytics_DisplaysLoadingState()` - Loading spinner visibility @@ -70,6 +75,7 @@ This comprehensive test suite covers all Admin page components with over **1,200 --- ### 5. **AdminPageLayoutTests** (8 tests) + - ✅ `AdminPageLayout_RendersTitle()` - Title parameter rendering - ✅ `AdminPageLayout_RendersDescription()` - Description parameter rendering - ✅ `AdminPageLayout_RendersChildContent()` - Child content rendering @@ -87,6 +93,7 @@ This comprehensive test suite covers all Admin page components with over **1,200 --- ### 6. **AdminAuthenticationTests** (4 tests) + - ✅ `AdminPages_RequireAdminRole()` - Admin role enforcement - ✅ `AdminPages_RequireAuthentication()` - Authentication requirement - ✅ `AdminUser_CanAccessAllAdminPages()` - Admin access verification @@ -99,14 +106,18 @@ This comprehensive test suite covers all Admin page components with over **1,200 ## 🔧 Test Infrastructure Updates ### Updated **BunitTestBase.cs** + Added service mocks for admin functionality: + ```csharp protected ICategoryService CategoryService { get; } protected IStatusService StatusService { get; } ``` ### Updated **GlobalUsings.cs** + Added necessary global using statements: + ```csharp global using Domain.DTOs.Analytics; global using Web.Components.Pages.Admin; @@ -129,28 +140,33 @@ global using Web.Components.Pages.Admin; ## 🎯 Test Scenarios Covered ### Loading States + - ✅ Initial data loading - ✅ Loading spinners during async operations - ✅ Default data ranges for analytics ### CRUD Operations + - ✅ Create (Categories, Statuses, Analytics data) - ✅ Read (List retrieval and display) - ✅ Update (Category and Status editing) - ✅ Archive/Restore (Soft delete operations) ### Validation + - ✅ Required field validation - ✅ Field length constraints (if applicable) - ✅ Error message display - ✅ Success message feedback ### Authorization + - ✅ Admin role requirement - ✅ Authentication check - ✅ User context in operations ### UI Features + - ✅ Modal dialogs (create/edit) - ✅ Table rendering - ✅ Navigation links @@ -160,6 +176,7 @@ global using Web.Components.Pages.Admin; - ✅ Data export ### Error Handling + - ✅ Service failure scenarios - ✅ Error message display - ✅ Network error handling @@ -190,6 +207,7 @@ IJSRuntime ## 📝 Key Test Patterns Used ### Arrange-Act-Assert (AAA) + ```csharp [Fact] public async Task Categories_CanCreateNewCategory() @@ -208,12 +226,14 @@ public async Task Categories_CanCreateNewCategory() ``` ### Mock Configuration + ```csharp _categoryService.GetCategoriesAsync(Arg.Any(), Arg.Any()) .Returns(Result>.Success(categories)); ``` ### Component Rendering + ```csharp var cut = Render(); var rows = cut.FindAll("tbody tr"); @@ -238,22 +258,26 @@ CreateTestIssues(count: 5) ## 🚀 Running the Tests -### Run all admin tests: +### Run all admin tests + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj --filter "AdminPageTests" ``` -### Run specific test class: +### Run specific test class + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj --filter "AdminCategoriesPageTests" ``` -### Run specific test: +### Run specific test + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj --filter "Categories_CanCreateNewCategory" ``` -### Run with verbose output: +### Run with verbose output + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj -v detailed ``` @@ -277,7 +301,7 @@ dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj -v detailed ## 📖 Test Organization -``` +```text tests/Web.Tests.Bunit/ ├── Pages/ │ └── Admin/ @@ -303,6 +327,7 @@ tests/Web.Tests.Bunit/ ## 🔮 Future Enhancements Potential additions for even more comprehensive testing: + - [ ] Integration tests with real database - [ ] Visual regression tests for UI components - [ ] Performance benchmarks for data loading @@ -312,6 +337,6 @@ Potential additions for even more comprehensive testing: --- -## ✅ All Tests Ready to Run! +## ✅ All Tests Ready to Run The test suite is complete and ready for execution. No external dependencies are required beyond what's already in the project. diff --git a/tests/Web.Tests.Bunit/Pages/TEST_SUMMARY.md b/tests/Web.Tests.Bunit/Pages/TEST_SUMMARY.md index 08febfc..6beb25c 100644 --- a/tests/Web.Tests.Bunit/Pages/TEST_SUMMARY.md +++ b/tests/Web.Tests.Bunit/Pages/TEST_SUMMARY.md @@ -1,9 +1,11 @@ # bUnit Page Component Tests Summary ## File Created + **Path:** `tests/Web.Tests.Bunit/Pages/GeneralPageTests.cs` ## Overview + Comprehensive bUnit test suite for all four page components in the IssueTrackerApp. Tests cover authentication states, content rendering, error handling, and accessibility for both authenticated and anonymous users. --- @@ -11,9 +13,11 @@ Comprehensive bUnit test suite for all four page components in the IssueTrackerA ## Test Classes & Coverage ### 1. **DashboardPageTests** (8 tests) + **Component:** `Dashboard.razor` - User-specific dashboard page -#### Test Cases: +#### Test Cases + - ✅ `Dashboard_RequiresAuthentication()` - Verifies dashboard access restrictions - ✅ `Dashboard_WhenAuthenticated_InitializesWithUserContext()` - Tests user context initialization - ✅ `Dashboard_DisplaysStatistics()` - Validates stat cards (Total, Open, Resolved, ThisWeek) @@ -22,7 +26,8 @@ Comprehensive bUnit test suite for all four page components in the IssueTrackerA - ✅ `Dashboard_HandlesDashboardServiceError()` - Tests error handling - ✅ `Dashboard_DisplaysEmptyStateWhenNoRecentIssues()` - Tests empty state UI -#### Test Approach: +#### Test Approach + - Sets up authenticated user with custom userId and userName - Mocks `DashboardService.GetUserDashboardAsync()` - Creates test `UserDashboardDto` with configurable statistics @@ -32,15 +37,18 @@ Comprehensive bUnit test suite for all four page components in the IssueTrackerA --- ### 2. **HomePageTests** (4 tests) + **Component:** `Home.razor` - Public landing page -#### Test Cases: +#### Test Cases + - ✅ `Home_RendersForAnonymousUser()` - Tests rendering without authentication - ✅ `Home_RendersForAuthenticatedUser()` - Tests rendering with authentication - ✅ `Home_DisplaysWelcomeMessage()` - Validates welcome content - ✅ `Home_IsPublicAndDoesNotRequireAuthentication()` - Confirms public accessibility -#### Test Approach: +#### Test Approach + - Tests both `SetupAnonymousUser()` and `SetupAuthenticatedUser()` paths - Validates "Hello, world!" and "Welcome" messages - Ensures component renders identically for both user types @@ -49,9 +57,11 @@ Comprehensive bUnit test suite for all four page components in the IssueTrackerA --- ### 3. **ErrorPageTests** (6 tests) + **Component:** `Error.razor` - Error boundary display page -#### Test Cases: +#### Test Cases + - ✅ `Error_RendersErrorPage()` - Tests basic error page rendering - ✅ `Error_DisplaysErrorHeading()` - Validates "Error" heading presence - ✅ `Error_RendersWithoutRequiringServices()` - Tests with minimal setup @@ -59,7 +69,8 @@ Comprehensive bUnit test suite for all four page components in the IssueTrackerA - ✅ `Error_WorksForAuthenticatedAndAnonymousUsers()` - Tests both user types - ✅ `Error_RendersErrorLayout()` - Validates page structure and node count -#### Test Approach: +#### Test Approach + - Tests error page accessibility for both authenticated and anonymous users - Validates HTML structure with `FindAll()` to check for headings - Verifies `cut.Nodes.Count > 0` for proper markup rendering @@ -68,9 +79,11 @@ Comprehensive bUnit test suite for all four page components in the IssueTrackerA --- ### 4. **NotFoundPageTests** (8 tests) + **Component:** `NotFound.razor` - 404 Not Found page -#### Test Cases: +#### Test Cases + - ✅ `NotFound_Renders404Page()` - Tests basic 404 page rendering - ✅ `NotFound_DisplaysNotFoundMessage()` - Validates "Not Found" text - ✅ `NotFound_DisplaysHelpfulText()` - Tests error message ("does not exist") @@ -80,7 +93,8 @@ Comprehensive bUnit test suite for all four page components in the IssueTrackerA - ✅ `NotFound_ContainsProperHeading()` - Validates heading elements (h1-h4) - ✅ `NotFound_HasCorrectPageStructure()` - Validates DOM structure -#### Test Approach: +#### Test Approach + - Tests accessibility for both user types - Validates specific text content in markup - Uses `FindAll()` to verify heading elements exist @@ -91,6 +105,7 @@ Comprehensive bUnit test suite for all four page components in the IssueTrackerA ## Testing Techniques Used ### Authentication Setup + ```csharp // Anonymous user (public pages) SetupAnonymousUser(); @@ -100,6 +115,7 @@ SetupAuthenticatedUser(userId: "user123", userName: "John Doe"); ``` ### Service Mocking + ```csharp DashboardService.GetUserDashboardAsync(userId) .Returns(Result.Ok(dashboard)); @@ -109,6 +125,7 @@ DashboardService.Received(1).GetUserDashboardAsync(userId); ``` ### Component Rendering + ```csharp // Render component var cut = Render(); @@ -123,6 +140,7 @@ cut.Nodes.Count.Should().BeGreaterThan(0); ``` ### Assertion Patterns + ```csharp // Content assertions markup.Should().Contain("text"); @@ -139,6 +157,7 @@ cut.Markup.Should().NotBeNull(); --- ## Test Statistics + | Component | Test Count | Coverage | |-----------|-----------|----------| | Dashboard | 8 | Auth, Stats, Data, Errors, Empty State | @@ -150,6 +169,7 @@ cut.Markup.Should().NotBeNull(); --- ## Dependencies + - **xUnit** - Test framework - **bUnit** - Blazor component testing library - **FluentAssertions** - Fluent assertion syntax @@ -159,6 +179,7 @@ cut.Markup.Should().NotBeNull(); --- ## Test Execution + ```bash # Run all page component tests dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj --filter "Name~GeneralPageTests" @@ -186,6 +207,7 @@ dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj --filter "FullyQualifie --- ## Next Steps + 1. Run tests: `dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj --filter "*GeneralPageTests*"` 2. Fix any failing tests by adjusting assertions to match actual component output 3. Consider adding integration tests for cross-page navigation scenarios diff --git a/tests/Web.Tests.Bunit/Shared/HOW_TO_ADD_TESTS.md b/tests/Web.Tests.Bunit/Shared/HOW_TO_ADD_TESTS.md index 285e1fb..2bdbc62 100644 --- a/tests/Web.Tests.Bunit/Shared/HOW_TO_ADD_TESTS.md +++ b/tests/Web.Tests.Bunit/Shared/HOW_TO_ADD_TESTS.md @@ -1,6 +1,7 @@ # How to Add More Tests for Shared Components ## Overview + This guide shows how to add additional tests to the SharedComponentTests.cs file following the established patterns. --- @@ -8,9 +9,11 @@ This guide shows how to add additional tests to the SharedComponentTests.cs file ## Adding Tests to an Existing Component ### Step 1: Locate the Test Class + Find the appropriate test class in SharedComponentTests.cs, e.g., `PaginationTests`. ### Step 2: Add New Test Method + Follow the naming convention: `ComponentName_Scenario_ExpectedResult` ```csharp @@ -35,6 +38,7 @@ public void Pagination_WithLargePageCount_ShowsEllipsis() ### Step 3: Common Test Patterns #### Testing Async Actions + ```csharp [Fact] public async Task Component_AsyncAction_InvokesCallback() @@ -53,6 +57,7 @@ public async Task Component_AsyncAction_InvokesCallback() ``` #### Testing with Multiple Clicks + ```csharp [Fact] public async Task Component_MultipleClicks_UpdatesCounter() @@ -74,6 +79,7 @@ public async Task Component_MultipleClicks_UpdatesCounter() ``` #### Testing Element Collections + ```csharp [Fact] public void Component_WithMultipleItems_RendersAll() @@ -89,6 +95,7 @@ public void Component_WithMultipleItems_RendersAll() ``` #### Testing Component Parameter Updates + ```csharp [Fact] public async Task Component_ParameterUpdate_ReRendersCorrectly() @@ -111,6 +118,7 @@ public async Task Component_ParameterUpdate_ReRendersCorrectly() ## Adding Tests for a New Component ### Step 1: Create Test Class + Add a new test class at the end of SharedComponentTests.cs: ```csharp @@ -132,6 +140,7 @@ public class NewComponentTests : BunitTestBase ``` ### Step 2: Add Core Tests + Start with basic rendering tests, then add parameter and callback tests: ```csharp @@ -171,6 +180,7 @@ public async Task NewComponent_ClickButton_InvokesCallback() ``` ### Step 3: Add Edge Case Tests + Test null values, empty collections, boundary conditions: ```csharp @@ -198,6 +208,7 @@ public void NewComponent_WithEmptyList_ShowsMessage() ## Using Test Data Helpers ### From BunitTestBase + ```csharp // Create test data var issue = CreateTestIssue(title: "Custom Title"); @@ -214,6 +225,7 @@ var cut = Render(p => p ``` ### Create Custom Helpers if Needed + ```csharp private static List CreateTestStatuses() { @@ -236,6 +248,7 @@ var cut = Render(p => p ## Advanced Testing Scenarios ### Testing JavaScript Interop + ```csharp [Fact] public async Task Component_WithJSInterop_CallsJS() @@ -255,6 +268,7 @@ public async Task Component_WithJSInterop_CallsJS() ``` ### Testing with Service Calls + ```csharp [Fact] public async Task Component_LoadsData_DisplaysResults() @@ -275,6 +289,7 @@ public async Task Component_LoadsData_DisplaysResults() ``` ### Testing Authorization + ```csharp [Fact] public void Component_WithoutAuth_HidesContent() @@ -302,6 +317,7 @@ public void Component_WithAdminAuth_ShowsAdminOptions() ## Best Practices for New Tests ### ✅ Do + - Use descriptive test names - Follow AAA pattern - Test one thing per test @@ -311,6 +327,7 @@ public void Component_WithAdminAuth_ShowsAdminOptions() - Use FluentAssertions ### ❌ Don't + - Test multiple scenarios in one test - Create test dependencies - Use magic numbers/strings @@ -376,11 +393,13 @@ public class [ComponentName]Tests : BunitTestBase ## Debugging Tests ### Print Markup + ```csharp System.Diagnostics.Debug.WriteLine(cut.Markup); ``` ### Print Element Info + ```csharp var element = cut.Find("selector"); System.Diagnostics.Debug.WriteLine($"Class: {element.GetAttribute("class")}"); @@ -388,11 +407,13 @@ System.Diagnostics.Debug.WriteLine($"Content: {element.TextContent}"); ``` ### Breakpoints + - Set breakpoints in test method - Use Debug mode to inspect state - Check variable values ### Inspect Rendered HTML + ```csharp // Save markup to file for inspection File.WriteAllText("output.html", cut.Markup); diff --git a/tests/Web.Tests.Bunit/Shared/SHARED_COMPONENT_TESTS_SUMMARY.md b/tests/Web.Tests.Bunit/Shared/SHARED_COMPONENT_TESTS_SUMMARY.md index 0841af9..28007fc 100644 --- a/tests/Web.Tests.Bunit/Shared/SHARED_COMPONENT_TESTS_SUMMARY.md +++ b/tests/Web.Tests.Bunit/Shared/SHARED_COMPONENT_TESTS_SUMMARY.md @@ -10,10 +10,12 @@ # Shared Component Tests Summary ## Overview + Comprehensive bUnit test suite created for 11 Shared components in IssueTrackerApp. All tests inherit from `BunitTestBase` and use mocked services provided by the base class. ## Test File Location + `tests/Web.Tests.Bunit/Shared/SharedComponentTests.cs` --- @@ -21,6 +23,7 @@ All tests inherit from `BunitTestBase` and use mocked services provided by the b ## Components Tested (11 Total) ### 1. **Pagination Component** - `PaginationTests` + - ✅ Single page: Does not render navigation - ✅ Multiple pages: Renders navigation correctly - ✅ First page: Previous button not rendered @@ -35,6 +38,7 @@ All tests inherit from `BunitTestBase` and use mocked services provided by the b --- ### 2. **FilterPanel Component** - `FilterPanelTests` + - ✅ Renders SearchInput component - ✅ Without filters: Clear button not visible - ✅ With filters: Displays active filter count @@ -49,6 +53,7 @@ All tests inherit from `BunitTestBase` and use mocked services provided by the b --- ### 3. **StatusBadge Component** - `StatusBadgeTests` + - ✅ With status: Renders status name - ✅ Null status: Renders "Unknown" - ✅ Open status: Blue color class applied @@ -63,6 +68,7 @@ All tests inherit from `BunitTestBase` and use mocked services provided by the b --- ### 4. **CategoryBadge Component** - `CategoryBadgeTests` + - ✅ With category: Renders category name - ✅ Null category: Renders "Unknown" - ✅ Bug category: Red color class applied @@ -77,6 +83,7 @@ All tests inherit from `BunitTestBase` and use mocked services provided by the b --- ### 5. **SearchInput Component** - `SearchInputTests` + - ✅ Correct ID attribute rendered - ✅ Placeholder text displayed - ✅ Value displayed in input @@ -91,6 +98,7 @@ All tests inherit from `BunitTestBase` and use mocked services provided by the b --- ### 6. **SummaryCard Component** - `SummaryCardTests` + - ✅ Renders title - ✅ Renders value - ✅ With subtitle: Rendered @@ -107,6 +115,7 @@ All tests inherit from `BunitTestBase` and use mocked services provided by the b --- ### 7. **ToastContainer Component** - `ToastContainerTests` + - ✅ Component renders - ✅ No toasts: No alert messages - ✅ Info toast: Displayed with blue styles @@ -121,6 +130,7 @@ All tests inherit from `BunitTestBase` and use mocked services provided by the b --- ### 8. **SignalRConnection Component** - `SignalRConnectionTests` + - ✅ Component renders - ✅ Disconnected state: Shows appropriate status - ✅ Status indicator: Has title attribute @@ -132,6 +142,7 @@ All tests inherit from `BunitTestBase` and use mocked services provided by the b --- ### 9. **FileUpload Component** - `FileUploadTests` + - ✅ Upload zone rendered - ✅ Correct accept types displayed - ✅ Without error: Error message not shown @@ -145,6 +156,7 @@ All tests inherit from `BunitTestBase` and use mocked services provided by the b --- ### 10. **DeleteConfirmationModal Component** - `DeleteConfirmationModalTests` + - ✅ Hidden: Dialog not rendered - ✅ Visible: Dialog rendered - ✅ Renders title @@ -163,6 +175,7 @@ All tests inherit from `BunitTestBase` and use mocked services provided by the b --- ### 11. **DateRangePicker Component** - `DateRangePickerTests` + - ✅ Renders preset buttons (7, 30, 90 days, All time) - ✅ Renders date inputs - ✅ With start date: Start date rendered @@ -200,7 +213,9 @@ All tests inherit from `BunitTestBase` and use mocked services provided by the b ## Test Patterns Used ### 1. **Arrange-Act-Assert (AAA)** + All tests follow the AAA pattern: + ```csharp // Arrange - Set up component parameters var cut = Render(parameters => ...); @@ -213,32 +228,42 @@ cut.Markup.Should().Contain("expected"); ``` ### 2. **Parameter Binding** + Tests verify parameter binding by passing parameters and checking rendered output: + ```csharp .Add(p => p.CurrentPage, 1) .Add(p => p.TotalPages, 5) ``` ### 3. **Event Callbacks** + Tests verify callbacks are invoked with correct values: + ```csharp .Add(p => p.OnPageChange, EventCallback.Factory.Create(this, page => { })) ``` ### 4. **Conditional Rendering** + Tests verify elements are shown/hidden based on state: + ```csharp cut.Markup.Should().NotContain("Clear All"); ``` ### 5. **CSS Class Verification** + Tests verify correct styling applied based on state: + ```csharp cut.Markup.Should().Contain("bg-blue-100"); ``` ### 6. **Helper Methods** + Tests use BunitTestBase helper methods: + - `CreateTestStatus(name)` - Creates test StatusDto - `CreateTestCategory(name)` - Creates test CategoryDto - `CreateTestUser()` - Creates test UserDto @@ -263,21 +288,25 @@ Tests use BunitTestBase helper methods: ## Running the Tests ### Run all tests + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj ``` ### Run specific test class + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj --filter "FullyQualifiedName~PaginationTests" ``` ### Run with verbose output + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj --verbosity normal ``` ### Run with code coverage + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj /p:CollectCoverage=true ``` diff --git a/tests/Web.Tests.Bunit/Shared/TEST_QUICK_REFERENCE.md b/tests/Web.Tests.Bunit/Shared/TEST_QUICK_REFERENCE.md index 1cdf4ac..7e364db 100644 --- a/tests/Web.Tests.Bunit/Shared/TEST_QUICK_REFERENCE.md +++ b/tests/Web.Tests.Bunit/Shared/TEST_QUICK_REFERENCE.md @@ -10,6 +10,7 @@ # Shared Component Tests - Quick Reference Guide ## Test File Location + `tests/Web.Tests.Bunit/Shared/SharedComponentTests.cs` ## Components & Test Counts @@ -52,12 +53,14 @@ public void ComponentName_Scenario_ExpectedResult() ## Testing Patterns ### 1. Parameter Binding + ```csharp .Add(p => p.CurrentPage, 1) .Add(p => p.TotalPages, 5) ``` ### 2. Event Callbacks + ```csharp .Add(p => p.OnPageChange, EventCallback.Factory.Create(this, page => { @@ -68,6 +71,7 @@ public void ComponentName_Scenario_ExpectedResult() ``` ### 3. Clicking Elements + ```csharp await cut.Find("button").Click(); // or @@ -75,6 +79,7 @@ await cut.FindAll("button").FirstOrDefault(b => b.TextContent.Contains("Next"))? ``` ### 4. Checking Rendered Output + ```csharp cut.Markup.Should().Contain("text"); cut.Markup.Should().NotContain("hidden text"); @@ -82,11 +87,13 @@ cut.Find("nav").Should().NotBeNull(); ``` ### 5. Checking CSS Classes + ```csharp cut.Find("span").GetAttribute("class").Should().Contain("bg-blue-100"); ``` ### 6. Checking Attributes + ```csharp cut.Find("input").GetAttribute("id").Should().Be("test-search"); cut.Find("input").GetAttribute("placeholder").Should().Be("Search..."); @@ -125,24 +132,28 @@ protected IJSRuntime JsRuntime { get; } ## Commonly Used Assertions ### Text Content + ```csharp cut.Markup.Should().Contain("text"); cut.Find("selector").TextContent.Should().Be("expected"); ``` ### Visibility + ```csharp cut.Markup.Should().NotContain("().Should().NotBeNull(); ``` ### Attributes + ```csharp cut.Find("input").GetAttribute("class").Should().Contain("primary"); cut.Find("button").GetAttribute("disabled").Should().Be("disabled"); @@ -170,50 +181,60 @@ dotnet test --verbosity=detailed ## Component-Specific Notes ### Pagination + - Tests navigation button visibility based on page position - Verifies page change callbacks - Checks primary color styling on current page ### FilterPanel + - Tests SearchInput component integration - Verifies filter counts with multiple active filters - Tests expandable filter options ### Status/CategoryBadge + - Tests color mapping for different values - Verifies "Unknown" for null values - Tests additional CSS classes ### SearchInput + - Tests debounce behavior implicitly through callback - Verifies clear button conditional rendering - Tests accessibility attributes ### SummaryCard + - Tests trend visualization (up/down arrows) - Verifies icon rendering with custom backgrounds - Tests subtitle conditional rendering ### ToastContainer + - Requires ToastService injection - Tests different toast types and styles - Verifies toast dismissal buttons ### SignalRConnection + - Tests connection state visualization - Requires SignalRClientService ### FileUpload + - Tests file type acceptance - Verifies accessibility labels - Tests InputFile component usage ### DeleteConfirmationModal + - Tests modal visibility toggling - Verifies loading spinner during deletion - Tests button disabling during operation ### DateRangePicker + - Tests preset button selection - Verifies date range callback - Tests manual date input @@ -221,27 +242,32 @@ dotnet test --verbosity=detailed ## Tips & Tricks 1. **Use FirstOrDefault with Contains** when finding specific buttons: + ```csharp var nextButton = cut.FindAll("button").FirstOrDefault(b => b.TextContent.Contains("Next")); ``` 2. **Check TextContent for rendered text**: + ```csharp cut.Find("span").TextContent.Should().Contain("Open"); ``` 3. **Use Find for single element, FindAll for multiple**: + ```csharp cut.Find("nav"); // Throws if not found cut.FindAll("button"); // Returns list ``` 4. **Don't forget Task.CompletedTask in callbacks**: + ```csharp EventCallback.Factory.Create(this, _ => Task.CompletedTask) ``` 5. **Use @Key in Razor components for duplicate elements**: + ```csharp @key="toast.Id" // In component markup ``` @@ -249,16 +275,19 @@ dotnet test --verbosity=detailed ## Debugging Tests ### Enable Console Output + ```csharp System.Diagnostics.Debug.WriteLine(cut.Markup); ``` ### Take Snapshot of Component + ```csharp var snapshot = cut.GetChangesSinceFirstRender(); ``` ### Find Element Details + ```csharp var element = cut.Find("selector"); Console.WriteLine(element.GetAttribute("class")); diff --git a/tests/Web.Tests.Bunit/Theme/INDEX.md b/tests/Web.Tests.Bunit/Theme/INDEX.md index d200b43..44c559d 100644 --- a/tests/Web.Tests.Bunit/Theme/INDEX.md +++ b/tests/Web.Tests.Bunit/Theme/INDEX.md @@ -38,15 +38,19 @@ This directory contains comprehensive bUnit tests for the Theme components in Is ## 🎯 Quick Navigation ### For Running Tests + → See **QUICK_REFERENCE.md** section "Running Tests" ### For Understanding Tests + → See **TEST_SUMMARY.md** or **TEST_COVERAGE_MAP.md** ### For Test Patterns + → See **QUICK_REFERENCE.md** section "Common Test Patterns" ### For Troubleshooting + → See **QUICK_REFERENCE.md** section "Common Issues & Solutions" --- @@ -66,23 +70,27 @@ This directory contains comprehensive bUnit tests for the Theme components in Is ## 🧪 Test Classes ### 1. ThemeProviderTests (4 tests) + - Component rendering and initialization - Cascading parameter functionality - Error handling ### 2. ThemeToggleTests (9 tests) + - Theme mode switching (light/dark/system) - Dropdown menu behavior - UI icon rendering - State management ### 3. ColorSchemeSelectorTests (9 tests) + - Color scheme selection (4 colors) - Dropdown functionality - Color swatches display - State management ### 4. ThemeIntegrationTests (8 tests) + - Component interaction - State sharing between components - Event handling @@ -93,11 +101,13 @@ This directory contains comprehensive bUnit tests for the Theme components in Is ## 🧠 Key Concepts ### Components Tested + 1. **ThemeProvider.razor** - State management and cascading 2. **ThemeToggle.razor** - Theme mode selection UI 3. **ColorSchemeSelector.razor** - Color scheme selection UI ### Testing Patterns + - Component rendering with `Render()` - JavaScript interop mocking with `JSInterop.Setup()` - DOM querying with `Find()`, `FindAll()` @@ -105,6 +115,7 @@ This directory contains comprehensive bUnit tests for the Theme components in Is - State assertions with `Should().Be()` ### Coverage Areas + - Component lifecycle - User interactions - State management @@ -119,21 +130,25 @@ This directory contains comprehensive bUnit tests for the Theme components in Is ## 🚀 Getting Started ### Build + ```bash dotnet build tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj ``` ### Run All Tests + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj ``` ### Run Specific Test + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj --filter "ThemeToggleTests" ``` ### Run with Coverage + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj /p:CollectCoverage=true ``` @@ -143,21 +158,25 @@ dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj /p:CollectCoverage=true ## 📖 Reading Guide ### First Time? + 1. Start with **QUICK_REFERENCE.md** 2. Then read **TEST_SUMMARY.md** 3. Examine **ThemeComponentTests.cs** ### Want to Add Tests? + 1. Review **QUICK_REFERENCE.md** - "Testing Checklist" 2. Look at examples in **ThemeComponentTests.cs** 3. Follow naming conventions from **QUICK_REFERENCE.md** ### Troubleshooting? + 1. Check **QUICK_REFERENCE.md** - "Common Issues & Solutions" 2. Review test examples for similar scenario 3. Check test setup patterns ### Need Details? + 1. Check **TEST_COVERAGE_MAP.md** for organization 2. Check **TEST_SUMMARY.md** for descriptions 3. Review source code comments in test file @@ -167,29 +186,34 @@ dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj /p:CollectCoverage=true ## 💡 Key Testing Patterns ### Setup JavaScript Interop + ```csharp JSInterop.Setup("method").SetResult("value"); JSInterop.SetupVoid("method"); ``` ### Render Component with Provider + ```csharp var component = Render(parameters => parameters.AddChildContent()); ``` ### Query DOM + ```csharp var button = component.Find("button"); var items = component.FindAll("button[role='menuitem']"); ``` ### Simulate User Action + ```csharp await button.ClickAsync(new()); ``` ### Assert State + ```csharp component.Instance.ThemeMode.Should().Be("dark"); ``` @@ -216,6 +240,7 @@ component.Instance.ThemeMode.Should().Be("dark"); **Format**: `[Component]_[Scenario]_[Expected Result]` **Examples**: + - `ThemeProvider_RendersSuccessfully` - `ThemeToggle_DropdownOpens_OnButtonClick` - `ColorSchemeSelector_HighlightsCurrentScheme` @@ -244,6 +269,7 @@ component.Instance.ThemeMode.Should().Be("dark"); ## ✨ Summary This test suite provides **comprehensive coverage** of the Theme components with: + - ✅ Clear organization - ✅ Well-documented tests - ✅ Reusable patterns diff --git a/tests/Web.Tests.Bunit/Theme/QUICK_REFERENCE.md b/tests/Web.Tests.Bunit/Theme/QUICK_REFERENCE.md index 4519c31..d86f2a7 100644 --- a/tests/Web.Tests.Bunit/Theme/QUICK_REFERENCE.md +++ b/tests/Web.Tests.Bunit/Theme/QUICK_REFERENCE.md @@ -2,7 +2,7 @@ ## Test File Structure -``` +```text tests/Web.Tests.Bunit/Theme/ ├── ThemeComponentTests.cs # Main test file (29 tests) └── TEST_SUMMARY.md # This summary document @@ -13,20 +13,25 @@ tests/Web.Tests.Bunit/Theme/ The tests are organized into 4 main test classes: ### 1. **ThemeProviderTests** (4 tests) + Tests the core theme management component that handles state and cascading. ### 2. **ThemeToggleTests** (9 tests) + Tests the theme mode toggle component (light/dark/system). ### 3. **ColorSchemeSelectorTests** (9 tests) + Tests the color scheme selector component (blue/red/green/yellow). ### 4. **ThemeIntegrationTests** (8 tests) + Tests how theme components work together and state persistence. ## Common Test Patterns ### Setup JavaScript Interop (All Tests) + ```csharp // String returns JSInterop.Setup("themeManager.getThemeMode").SetResult("light"); @@ -42,12 +47,14 @@ JSInterop.Setup("method").SetException(new JSException("error")); ``` ### Render Component with Theme Provider + ```csharp var themeProvider = Render(parameters => parameters.AddChildContent()); ``` ### Test User Interactions + ```csharp // Click a button var button = component.Find("button"); @@ -58,6 +65,7 @@ var menuItems = component.FindAll("button[role='menuitem']"); ``` ### Verify JavaScript Calls + ```csharp JSInterop.VerifyInvoke("themeManager.setThemeMode", calledTimes: 1); ``` @@ -77,6 +85,7 @@ JSInterop.VerifyInvoke("themeManager.setThemeMode", calledTimes: 1); `[Component]_[Scenario]_[ExpectedResult]` Examples: + - `ThemeToggle_DropdownOpens_OnButtonClick` - `ColorSchemeSelector_HighlightsCurrentScheme` - `Theme_StateIsShared_BetweenComponents` @@ -84,16 +93,19 @@ Examples: ## Components Tested ### ThemeProvider.razor + - **Location**: src/Web/Components/Theme/ThemeProvider.razor.cs - **Role**: Manages theme state and cascading values - **Key Methods**: SetThemeModeAsync, SetColorSchemeAsync, OnSystemPreferenceChanged ### ThemeToggle.razor + - **Location**: src/Web/Components/Theme/ThemeToggle.razor - **Role**: UI for switching between light/dark/system modes - **Key Features**: Dropdown menu, icon changes, accessibility attributes ### ColorSchemeSelector.razor + - **Location**: src/Web/Components/Theme/ColorSchemeSelector.razor - **Role**: UI for selecting accent color scheme - **Key Features**: Color swatches, dropdown menu, selection highlighting @@ -128,6 +140,7 @@ using Web.Components.Theme; ## Test Base Class All tests inherit from `BunitTestBase` which provides: + - `JSInterop` - Mock JavaScript interop setup - `Render()` - Render Blazor components - `FindAll()`, `Find()`, `FindComponent()` - Query rendered components @@ -144,15 +157,19 @@ All tests inherit from `BunitTestBase` which provides: ## Common Issues & Solutions **Issue**: "No matching JSInterop setup" + - **Solution**: Add `JSInterop.Setup()` or `JSInterop.SetupVoid()` for the method **Issue**: "CascadingParameter is null" + - **Solution**: Wrap child component in ThemeProvider when rendering **Issue**: "Elements not found" + - **Solution**: Ensure components have rendered; use `Render()` if needed **Issue**: "Async context error" + - **Solution**: Use `await component.InvokeAsync()` for state changes ## Testing Checklist for New Theme Tests diff --git a/tests/Web.Tests.Bunit/Theme/TEST_COVERAGE_MAP.md b/tests/Web.Tests.Bunit/Theme/TEST_COVERAGE_MAP.md index fc5eced..786c70d 100644 --- a/tests/Web.Tests.Bunit/Theme/TEST_COVERAGE_MAP.md +++ b/tests/Web.Tests.Bunit/Theme/TEST_COVERAGE_MAP.md @@ -13,7 +13,8 @@ ## 🎯 Test Breakdown by Component ### ThemeProvider Component (4 tests) -``` + +```text ✓ Renders successfully with child content ✓ Initializes theme on first render ✓ Cascades value to child components @@ -21,6 +22,7 @@ ``` **Coverage:** + - Component lifecycle - State initialization - Cascading parameter functionality @@ -29,7 +31,8 @@ --- ### ThemeToggle Component (9 tests) -``` + +```text ✓ Renders button with correct attributes ✓ Shows sun icon in light mode ✓ Shows moon icon in dark mode @@ -43,6 +46,7 @@ ``` **Coverage:** + - UI rendering and icons - Dropdown functionality - Theme mode selection @@ -52,7 +56,8 @@ --- ### ColorSchemeSelector Component (9 tests) -``` + +```text ✓ Renders button with correct attributes ✓ Dropdown opens on button click ✓ Dropdown contains all four color schemes @@ -65,6 +70,7 @@ ``` **Coverage:** + - UI rendering with swatches - Dropdown functionality - Color scheme selection @@ -74,7 +80,8 @@ --- ### Integration Tests (8 tests) -``` + +```text ✓ Both components render together ✓ Theme state is shared between components ✓ OnThemeChanged event is triggered @@ -85,6 +92,7 @@ ``` **Coverage:** + - Component interaction - State sharing via cascading - Event triggering @@ -96,30 +104,35 @@ ## 🔍 Testing Areas Covered ### ✅ Component Lifecycle + - Component rendering - Initialization - Parameter updates - Disposal and cleanup ### ✅ User Interactions + - Button clicks - Dropdown toggle - Menu item selection - State updates ### ✅ State Management + - Theme mode changes (light/dark/system) - Color scheme changes - State persistence - Event propagation ### ✅ JavaScript Interop + - localStorage operations - System preference detection - Error handling - Exception scenarios ### ✅ UI Rendering + - Icon rendering based on state - Menu rendering - Color swatches @@ -127,6 +140,7 @@ - Accessibility attributes ### ✅ Cascading Parameters + - Provider cascading - Child component access - State sharing @@ -136,6 +150,7 @@ ## 📋 Test Details ### Theme Mode Coverage + | Mode | Toggle Test | Integration Test | Status | |------|------------|------------------|--------| | Light | ✅ | ✅ | Complete | @@ -143,6 +158,7 @@ | System | ✅ | ✅ | Complete | ### Color Scheme Coverage + | Scheme | Selector Test | Visual Test | Status | |--------|--------------|-------------|--------| | Blue | ✅ | ✅ | Complete | @@ -155,30 +171,35 @@ ## 🧪 Test Patterns Used ### 1. Component Rendering + ```csharp var component = Render(parameters => parameters.AddChildContent()); ``` ### 2. User Interaction + ```csharp var button = component.Find("button"); await button.ClickAsync(new()); ``` ### 3. State Verification + ```csharp component.Instance.ThemeMode.Should().Be("dark"); component.Instance.IsDarkMode.Should().BeTrue(); ``` ### 4. UI Verification + ```csharp var menuItems = component.FindAll("button[role='menuitem']"); menuItems.Should().HaveCount(3); ``` ### 5. JavaScript Interop + ```csharp JSInterop.Setup("themeManager.getThemeMode").SetResult("light"); JSInterop.SetupVoid("themeManager.setThemeMode"); @@ -188,7 +209,7 @@ JSInterop.SetupVoid("themeManager.setThemeMode"); ## 📁 File Structure -``` +```text tests/Web.Tests.Bunit/Theme/ ├── ThemeComponentTests.cs (Main test file) │ ├── ThemeProviderTests @@ -206,28 +227,33 @@ tests/Web.Tests.Bunit/Theme/ ## 🎓 Key Features ### ✅ Comprehensive Mocking + - JavaScript interop mocking for all themeManager methods - Service dependencies mocked via BunitTestBase - Exception scenarios covered ### ✅ Accessibility Testing + - ARIA attributes verified (aria-label, role) - Semantic HTML structure validated - Dropdown menu accessibility ### ✅ State Management Testing + - Component state changes - Event triggering and handling - Event cleanup on disposal - State persistence across components ### ✅ UI/UX Testing + - Icon rendering and changes - Dropdown behavior - Selection highlighting - Labels and tooltips ### ✅ Error Handling + - JavaScript exception scenarios - Prerendering error handling - Graceful degradation @@ -237,21 +263,25 @@ tests/Web.Tests.Bunit/Theme/ ## 🚀 Running the Tests ### All Tests + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj -v detailed ``` ### Specific Test Class + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj --filter "ThemeToggleTests" ``` ### Specific Test Method + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj --filter "ThemeToggle_DropdownOpens_OnButtonClick" ``` ### With Code Coverage + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj /p:CollectCoverage=true ``` @@ -286,14 +316,17 @@ dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj /p:CollectCoverage=true ## 🔄 Test Dependencies ### Base Class + - `BunitTestBase` - Provides component rendering and service mocks ### Global Usings + - Xunit, FluentAssertions, NSubstitute - Bunit, Bunit.TestDoubles - Microsoft.Extensions.DependencyInjection ### Component Dependencies + - ThemeProvider (cascading component) - ThemeToggle (dependent on ThemeProvider) - ColorSchemeSelector (dependent on ThemeProvider) @@ -305,6 +338,7 @@ dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj /p:CollectCoverage=true **Format**: `[Component]_[Scenario]_[Expected Result]` **Examples**: + - `ThemeToggle_DropdownOpens_OnButtonClick` - `ColorSchemeSelector_HighlightsCurrentScheme` - `Theme_StateIsShared_BetweenComponents` diff --git a/tests/Web.Tests.Bunit/Theme/TEST_SUMMARY.md b/tests/Web.Tests.Bunit/Theme/TEST_SUMMARY.md index c1e1990..866177b 100644 --- a/tests/Web.Tests.Bunit/Theme/TEST_SUMMARY.md +++ b/tests/Web.Tests.Bunit/Theme/TEST_SUMMARY.md @@ -1,26 +1,32 @@ # Theme Components bUnit Tests ## Overview + This test suite provides comprehensive testing for the Theme-related Blazor components in the IssueTrackerApp. The tests cover component rendering, user interactions, state management, and theme persistence. ## Test File Location + `tests/Web.Tests.Bunit/Theme/ThemeComponentTests.cs` ## Components Tested ### 1. ThemeProvider Component + **Purpose:** Manages theme state (dark/light mode, color scheme) and provides cascading values to child components. **Tests (4 tests):** + - `ThemeProvider_RendersSuccessfully` - Verifies component renders with child content - `ThemeProvider_InitializesThemeOnFirstRender` - Tests theme initialization on first render - `ThemeProvider_CascadesValueToChildren` - Validates cascading of provider to child components - `ThemeProvider_HandlesJSException_DuringInitialization` - Tests error handling for prerendering scenario ### 2. ThemeToggle Component + **Purpose:** Provides a dropdown menu for switching between light, dark, and system theme modes. **Tests (8 tests):** + - `ThemeToggle_RendersWithButton` - Confirms button renders with correct attributes - `ThemeToggle_ShowsSunIcon_InLightMode` - Verifies sun icon displays in light mode - `ThemeToggle_ShowsMoonIcon_InDarkMode` - Verifies moon icon displays in dark mode @@ -33,9 +39,11 @@ This test suite provides comprehensive testing for the Theme-related Blazor comp - `ThemeToggle_DisposesEventHandler` - Tests proper cleanup on disposal ### 3. ColorSchemeSelector Component + **Purpose:** Allows users to select accent color scheme (Blue, Red, Green, Yellow). **Tests (9 tests):** + - `ColorSchemeSelector_RendersWithButton` - Confirms button renders with correct attributes - `ColorSchemeSelector_DropdownOpens_OnButtonClick` - Tests dropdown open/close functionality - `ColorSchemeSelector_DropdownContains_AllColorSchemes` - Validates all four color options @@ -47,9 +55,11 @@ This test suite provides comprehensive testing for the Theme-related Blazor comp - `ColorSchemeSelector_DisposesEventHandler` - Tests proper cleanup on disposal ### 4. Integration Tests + **Purpose:** Tests theme components working together and theme state persistence. **Tests (8 tests):** + - `Theme_BothComponents_RenderTogether` - Verifies both toggle and selector render together - `Theme_StateIsShared_BetweenComponents` - Validates theme state is shared across components - `Theme_OnThemeChanged_TriggeredOnThemeChange` - Tests event triggering on theme change @@ -59,6 +69,7 @@ This test suite provides comprehensive testing for the Theme-related Blazor comp - `Theme_SystemPreferenceChanged_IgnoredInNonSystemMode` - Validates proper system preference handling ## Test Statistics + - **Total Test Classes:** 4 - **Total Test Methods:** 29 - **File Size:** 27,605 bytes @@ -66,6 +77,7 @@ This test suite provides comprehensive testing for the Theme-related Blazor comp ## Key Testing Patterns ### JavaScript Interop Mocking + ```csharp // For methods returning values JSInterop.Setup("themeManager.getThemeMode").SetResult("light"); @@ -79,12 +91,14 @@ JSInterop.Setup("method").SetException(new JSException("error")); ``` ### Component Rendering with Cascading Values + ```csharp var themeProvider = Render(parameters => parameters.AddChildContent()); ``` ### Dropdown and Event Testing + ```csharp var button = component.Find("button"); await button.ClickAsync(new()); @@ -94,30 +108,35 @@ var menuItems = component.FindAll("button[role='menuitem']"); ## Coverage Areas ### Component Lifecycle + - ✅ Initial render and initialization - ✅ Parameter changes - ✅ Disposal and cleanup - ✅ Event subscription/unsubscription ### User Interactions + - ✅ Button clicks - ✅ Dropdown toggle - ✅ Menu item selection - ✅ State updates via user actions ### State Management + - ✅ Theme mode changes (light/dark/system) - ✅ Color scheme changes - ✅ State persistence across component boundaries - ✅ Event triggering and handling ### JavaScript Interop + - ✅ localStorage integration - ✅ System preference detection - ✅ Error handling during JS interop - ✅ Exception scenarios (prerendering) ### UI Rendering + - ✅ Icon display based on mode - ✅ Menu items rendering - ✅ Color swatches display @@ -126,17 +145,20 @@ var menuItems = component.FindAll("button[role='menuitem']"); ## Running the Tests -### Run all theme tests: +### Run all theme tests + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj --logger "console" -v normal ``` -### Run specific test class: +### Run specific test class + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj --filter "ClassName" ``` -### Run with code coverage: +### Run with code coverage + ```bash dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj /p:CollectCoverage=true ``` @@ -144,6 +166,7 @@ dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj /p:CollectCoverage=true ## Dependencies ### Using Global + - Xunit - FluentAssertions - NSubstitute @@ -154,6 +177,7 @@ dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj /p:CollectCoverage=true - Microsoft.AspNetCore.Components.Rendering ### Base Class + - `BunitTestBase` - Provides JSInterop mocking and service setup ## Notes diff --git a/tests/Web.Tests/Services/README.md b/tests/Web.Tests/Services/README.md index a816a57..0c6b12a 100644 --- a/tests/Web.Tests/Services/README.md +++ b/tests/Web.Tests/Services/README.md @@ -1,6 +1,7 @@ # Web Service Unit Tests - Status Report ## Overview + This document explains the status of unit tests for Web layer services. ## Services Identified @@ -12,6 +13,7 @@ Three service classes exist in `src/Web/Services/`: 3. **AttachmentService** - File attachment operations facade All three services are facade/adapter patterns that: + - Wrap MediatR calls for command/query dispatch - Handle real-time notifications (SignalR) on successful operations - Follow CQRS patterns established in the Domain layer @@ -54,9 +56,11 @@ Tests were created in `tests/Web.Tests/Services/`: ## Dependencies Updated Added to `tests/Web.Tests/Web.Tests.csproj`: + - NSubstitute package reference Added to `tests/Web.Tests/GlobalUsings.cs`: + - Domain abstractions and DTOs - MediatR types - Logging types @@ -65,6 +69,7 @@ Added to `tests/Web.Tests/GlobalUsings.cs`: ## Build Status **Note**: The Web project currently has pre-existing build errors unrelated to these tests: + - `WithOpenApi` extension method errors in Endpoints - `FirstOrDefaultAsync` extension method errors @@ -73,10 +78,12 @@ These errors prevent the Web.Tests project from building. Once the Web project b ## Test Verification The tests follow established patterns from Domain.Tests and use the same: + - Testing frameworks (xUnit, FluentAssertions, NSubstitute) - Code organization (regions for test groups) - Helper methods for creating test DTOs - Async/await patterns ## Date Created + Created as part of Sprint 5, Task 9 (s5-9-web-services)