Alpha/Preview Release - This project is in early development. While functional, expect:
- Breaking changes between versions
- Incomplete documentation
- Active development with frequent updates
- Limited production readiness
Suitable for experimentation, learning, and development environments.
redmuffin.Blazor.StaticWeb is a modern full-stack web application built with Blazor WebAssembly (.NET 9) and Azure Functions (.NET 9). The solution provides a performant, maintainable static web application with serverless backend capabilities, featuring OAuth integration and comprehensive testing infrastructure.
- Project Status
- Overview
- Features
- Prerequisites
- Development Environment
- Getting Started
- Development Workflow
- Usage
- Project Structure
- Technology Stack
- Development Tools
- Security Policy
- Build and Deployment
- License
- Acknowledgements
- Blazor WebAssembly (.NET 9) - Client-side execution with modern C# features
- Azure Functions (.NET 9) - Serverless backend with HTTP triggers
- Raindrop.io OAuth Integration - External API integration with secure authentication
- Markdown Content Rendering - Advanced Markdown processing with Markdig
- Modern C# (C# 12/13) features - Primary constructors, collection expressions, ref readonly parameters
- Comprehensive Testing - TUnit framework with hand-rolled fakes (see
rm-guide-testing) - Code Coverage - Automated coverage reports with Coverlet and ReportGenerator
- PowerShell Automation - Scripts for coverage report generation and viewing
- SCSS Styling Only - All styling should be done using SCSS files in the
wwwroot/scss/directory. - CSS Files are Auto-Generated - Direct modifications to CSS files are not allowed; they are automatically generated from SCSS.
- SCSS Partials - All SCSS partial files must start with an underscore (_) and be included in
app.scssfor automatic compilation. - Feature Folder Structure - Organized by feature for better maintainability
- Code Quality & Security - CodeQL analysis, automated builds, Dependabot integration
- Accessibility Compliance - WCAG 2.1 AA standards with semantic HTML and ARIA support
- EditorConfig - Consistent code style and formatting
- Directory.Build.props - Centralized project configuration
- Azure Static Web Apps - Deployment and hosting platform
- Visual Studio 2026 (Community)
- .NET 10 SDK — Builds net9.0 projects for Azure SWA compatibility
- Download: dotnet.microsoft.com
- Installs
wasm-toolsworkload for Blazor WebAssembly - See .NET SDK Guide below for details
- Node.js (Latest LTS)
- Python 3.10+ - Required for code-review-graph MCP server
- PowerShell - Required for running project scripts (install on Linux:
yay -S powershell) - RTK (Rust Token Killer) - Required for OpenCode (reduces token consumption by 60-90%)
-
code-review-graph (Optional - for token-efficient code reviews) Local knowledge graph for AI-assisted code understanding. Provides blast radius analysis, community detection, and semantic search:
pip install code-review-graph code-review-graph install # Configure for OpenCode code-review-graph build # Build the knowledge graph
What it provides:
- 28 MCP tools for code analysis
- Blast radius analysis (what changes break)
- Community detection (architectural boundaries)
- 6.8x-49x token reduction on code reviews
Note: On Windows, OpenCode's MCP stdio layer has known issues (#16449). The tool works via shell execution but may not appear in OpenCode's MCP list. This is a known OpenCode bug, not a config issue.
-
Azure Static Web Apps CLI Required for local development and testing of Azure Static Web Apps:
npm install -g @azure/static-web-apps-cli
-
Prettier (Required for OpenCode formatter) Required for auto-formatting non-.NET files (SCSS, CSS, JSON, Markdown, YAML):
npm install -g prettier
-
commitlint (Required for commit message validation) Validates commit messages against Conventional Commits format:
npm install -g @commitlint/cli @commitlint/config-conventional
-
chrome-devtools-mcp (Required for Chrome DevTools MCP integration) Enables AI-powered browser automation, performance analysis, and debugging. Required for the Chrome DevTools MCP server in opencode.json:
npm install -g chrome-devtools-mcp
Note: This MCP server provides browser control capabilities including navigation, script execution, screenshots, performance tracing, and network inspection. It uses the
--isolatedflag to run Chrome in an incognito-like mode with automatic cleanup. -
cc-safety-net (Required for OpenCode plugin) AI agent safety net that blocks destructive git and filesystem commands before execution. Prevents accidental data loss from AI agent mistakes. MIT licensed, open source:
npm install -g cc-safety-net
Push Protection: The project uses two complementary layers to prevent unauthorized remote pushes and protected history rewrites:
Layer Mechanism Blocks 1. cc-safety-net Semantic command analysis via tool.execute.beforehookgit push --force/-f(destroys history)2. block-push.js Custom OpenCode plugin ( .opencode/plugins/block-push.js)ALL git pushandgit revertBlocked Commands (cc-safety-net Built-in Rules):
Command Pattern Reason git push --force/-fDestroys remote history git reset --hardDestroys all uncommitted changes permanently git checkout -- *Discards uncommitted changes permanently git clean -f*Removes untracked files permanently git branch -D *Force-deletes without merge check rm -rf /orrm -rf ~Targeting root or home directory rm -rf .orrm -rf ../pathOutside current working directory Note: This plugin is registered in
opencode.jsonand intercepts all bash commands via thetool.execute.beforehook. It provides semantic command analysis (not simple pattern matching), shell wrapper detection, and interpreter one-liner detection. Default mode blocks only truly destructive operations while allowing safe git workflows. See Supply Chain Attack Protection for npm security settings.
-
Project-Local Tools (automatically managed) The project includes pre-configured .NET tools in
.config/dotnet-tools.json:# Restore all project-local tools dotnet tool restoreIncluded Tools:
Microsoft.Web.LibraryManager.Cli(LibMan) - Client-side library management
-
Additional Global Tools (installed automatically by scripts) These tools are installed on-demand by the project scripts:
dotnet-reportgenerator-globaltool- Code coverage report generation
Manual Installation (if needed):
# Install ReportGenerator globally dotnet tool install --global dotnet-reportgenerator-globaltool
-
RTK (Rust Token Killer) - CLI proxy that reduces LLM token consumption by 60-90% on common dev commands Required for optimal OpenCode performance by compressing verbose command outputs (git, tests, package managers, etc.).
Installation (Windows):
# Download the latest Windows binary curl -L -o rtk.zip https://github.com/rtk-ai/rtk/releases/latest/download/rtk-x86_64-pc-windows-msvc.zip # Extract to a directory in PATH (e.g., C:\Users\<username>\bin) Expand-Archive -Path rtk.zip -DestinationPath . Move-Item rtk.exe C:\Users\$env:USERNAME\bin\rtk.exe # Verify installation rtk --version
Installation (Linux/Omarchy):
# Quick install script curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/master/install.sh | sh # Or manual installation curl -L -o rtk.tar.gz https://github.com/rtk-ai/rtk/releases/latest/download/rtk-x86_64-unknown-linux-musl.tar.gz tar -xzf rtk.tar.gz chmod +x rtk sudo mv rtk /usr/local/bin/rtk # Verify installation rtk --version
OpenCode Integration:
# Initialize RTK for OpenCode (installs plugin automatically) rtk init -g --opencode # Restart OpenCode for plugin to load
What RTK Does:
- Automatically compresses
git status,cargo test,pnpm list, etc. - Reduces token usage by 60-90% for development commands
- Works transparently - no changes needed to your workflow
- Plugin integrates directly with OpenCode's tool execution
Token Savings Examples:
git status: ~2,000 tokens → ~400 tokenscargo test: ~25,000 tokens → ~2,500 tokenspnpm list: ~8,000 tokens → ~2,400 tokens
Note: RTK requires restarting OpenCode after installation. The plugin handles all command rewriting automatically.
- Automatically compresses
- Azure CLI - For Azure resource management and deployment
- Docker Desktop
- Required for optional MCP server integration (enhances AI assistant capabilities)
- Download from Docker website
This project uses .NET 10 SDK (10.0.104+) to build and test .NET 9 projects.
All .csproj files target net9.0 for compatibility with Azure Static Web Apps
(which does not yet support .NET 10 managed Functions). The SDK version and
target framework are separate concerns — the SDK is just the build tool.
| Benefit | Detail |
|---|---|
| Faster builds | SDK 10 includes MSBuild and Roslyn performance improvements |
| Latest C# 13 features | Available in the QualityGates tools project (targets net10.0) |
| Single SDK | One global.json for the entire repository — no per-directory version switching |
| Future-proof | Ready when Azure SWA adds .NET 10 support |
# Install .NET 10 SDK (Arch Linux)
sudo pacman -S dotnet-sdk-10.0
# Install Blazor WebAssembly build tools
sudo dotnet workload install wasm-toolsdotnet --version # Should show 10.0.xxx
dotnet build # 11 projects, 0 errors
dotnet run --project tests/redmuffin.Blazor.StaticWeb.Tests # 293 tests pass# Build everything (from repo root)
dotnet build
# Run main solution tests
dotnet run --project tests/redmuffin.Blazor.StaticWeb.Tests -c Release
# Run quality gates on tools solution
cd tools && dotnet run -- all
# Run quality gates on main solution
cd tools && dotnet run -- all --solution ../redmuffin.Blazor.StaticWeb.slnx
# Publish Blazor WASM for production
dotnet publish src/redmuffin.Blazor.StaticWeb -c Release \
-p:PublishTrimmed=true -o publish/blazor
# Publish API for production
dotnet publish src/redmuffin.Blazor.StaticWeb.Api -c Release \
-o publish/apiAzure Static Web Apps managed Functions currently support up to .NET 9
(apiRuntime: dotnet-isolated:9.0 in staticwebapp.config.json).
The .NET 10 Functions runtime is not yet available on SWA. When it becomes
available, updating is a single-line change in each .csproj.
The Blazor WebAssembly app also targets net9.0 for consistency, even
though it runs entirely in the browser and has no server-side runtime
constraint.
Quick Start:
# Clone and setup
git clone https://github.com/michaelvolz/redmuffin.Blazor.StaticWeb.git
cd redmuffin.Blazor.StaticWeb
npm install -g @azure/static-web-apps-cli prettier @commitlint/cli @commitlint/config-conventional chrome-devtools-mcp
# Setup git hooks (run once)
.\scripts\Setup-GitHooks.ps1
# Build and run
dotnet restore
dotnet build
dotnet test
# Open in Visual Studio or run:
dotnet run --project src/redmuffin.Blazor.StaticWeb/redmuffin.Blazor.StaticWeb.csprojLocated in scripts/:
| Script | Purpose |
|---|---|
Update-PackageVersions.ps1 |
Updates CPM-managed NuGet package versions |
Generate-CoverageReport.ps1 |
Generates code coverage |
View-CoverageReport.ps1 |
Views coverage report |
Setup-GitHooks.ps1 |
Configures git hooks for commit validation |
For package updates, run scripts/Update-PackageVersions.ps1 and finish with
dotnet clean && dotnet build --verbosity quiet && dotnet test.
Creating an alias (optional):
Add to your PowerShell profile ($PROFILE):
Set-Alias -Name opencode -Value "C:\path\to\scripts\opencode-secure.ps1"-
Clone the repository:
git clone https://github.com/michaelvolz/redmuffin.Blazor.StaticWeb.git cd redmuffin.Blazor.StaticWeb
-
Verify prerequisites:
- Check .NET versions:
dotnet --list-sdks - Ensure you have .NET 9 SDK installed
- Verify Node.js:
node --version
- Check .NET versions:
-
Install global tools:
npm install -g @azure/static-web-apps-cli prettier @commitlint/cli @commitlint/config-conventional chrome-devtools-mcp
-
Setup git hooks:
.\scripts\Setup-GitHooks.ps1 -
Build and run:
dotnet restore dotnet build dotnet test
-
Start development:
# Using Visual Studio # Open redmuffin.Blazor.StaticWeb.sln # Use "Start both" profile # Or using CLI dotnet run --project src/redmuffin.Blazor.StaticWeb/redmuffin.Blazor.StaticWeb.csproj --launch-profile https
This is the normal day-to-day dev server command.
scripts/Start.ps1is reserved for final integration-style testing and broader environment validation. -
Navigate to:
- Full Stack: http://localhost:4280
- Frontend Only: http://localhost:5233
After setup, verify everything is working:
- Build succeeds without errors
- All tests pass
- Application loads at
http://localhost:4280 - API endpoints are accessible (check browser dev tools Network tab)
- Hot reload works (modify a
.razorfile and see changes)
IL2111 Warnings (Expected and Safe to Ignore):
During development and building, you may encounter IL2111 warnings like:
warning IL2111: Method 'Microsoft.AspNetCore.Components.LayoutView.Layout.set' with parameters or return type with `DynamicallyAccessedMembersAttribute` is accessed via reflection. Trimmer can't guarantee availability of the requirements of the method.
These warnings are expected and safe to ignore because:
- They occur in generated Razor files (
App_razor.g.cs) during Blazor WebAssembly compilation - They are related to Blazor's internal layout handling mechanism
- They do not affect application functionality or performance
- They are part of the normal Blazor compilation process and ASP.NET Core Components trimming optimization
- They are in generated code that is not under developer control
Action required: None - these warnings can be safely ignored during development and deployment.
- See Usage for common development tasks
- Check Local Development for development workflow
- Review MCP Server Integration for AI-enhanced development
This project follows Trunk-Based Development - a source-control branching model where developers collaborate on code in a single branch called 'trunk' (or 'main'/'master'), avoiding long-lived feature branches.
- Single Main Branch: All development happens on the
masterbranch - Frequent Integration: Developers commit/push to trunk at least once every 24 hours
- Short-Lived Feature Branches: When used, feature branches are small, short-lived (hours to 1-2 days), and created from a single developer workstation
- Continuous Integration: Every commit triggers automated builds and tests
- No Merge Hell: Avoid the complexity of long-lived branches and large merges
- Continuous Integration Ready: Enables true CI/CD with frequent integration
- Faster Feedback: Issues are discovered and resolved quickly
- Simplified Workflow: No complex branching strategies to manage
- Better Collaboration: All developers work with the latest code
- Reduced Risk: Smaller, more frequent changes are easier to review and safer to deploy
- Daily Commits: Commit to
masterat least once per day - Small Changes: Break work into small, incremental commits
- Pre-Integration Checks: Run full build and tests before pushing
- Feature Flags: Use feature flags for incomplete features rather than branches
- Pull Requests: Use short-lived PRs for code review (merge within 24 hours)
- Keep the Build Green: Never break the build on
master - Test Locally First: Run
dotnet buildanddotnet testbefore pushing - Use Feature Flags: Hide incomplete features behind flags instead of long-lived branches
- Quick Code Reviews: Review and merge PRs promptly to avoid drift
- Rollback Ready: Maintain ability to rollback any commit if needed
- GitHub Actions: Automated CI/CD pipeline on every push
- Automated Testing: TUnit tests run on every commit
- Code Quality Checks: CodeQL security scanning and analysis
- Deployment Pipeline: Automatic deployment to Azure Static Web Apps
- ❌ Long-lived feature branches (more than 1-2 days)
- ❌ Delaying integration until "feature complete"
- ❌ Large batch commits
- ❌ Breaking the build on
master - ❌ Avoiding commits due to "incomplete" work
- Official Trunk-Based Development Site
- Atlassian Guide
- Martin Fowler's Feature Flags
- Continuous Integration
This project embraces Test-Driven Development - a software development methodology that guides software development by writing tests before the actual implementation. TDD was developed by Kent Beck in the late 1990s as part of Extreme Programming.
- Red-Green-Refactor Cycle: Write a failing test (Red), make it pass with minimal code (Green), then refactor while keeping tests green
- Test-First Approach: Write tests before writing the production code they're meant to verify
- Incremental Development: Build software in small, testable increments
- Continuous Testing: Maintain a comprehensive suite of automated tests that run frequently
- Design Through Testing: Use tests to drive and validate software design decisions
- Higher Code Quality: TDD leads to cleaner, more maintainable code with fewer bugs
- Better Design: Writing tests first forces consideration of API design and component interfaces
- Faster Feedback: Immediate feedback on code changes through automated test execution
- Regression Prevention: Comprehensive test suite catches issues when refactoring or adding features
- Documentation: Tests serve as living documentation of how the code should behave
- Confidence: Developers can refactor and change code with confidence knowing tests will catch issues
- Write a Failing Test: Start by writing a test that describes the desired behavior
- Run the Test: Verify the test fails (Red state) - this confirms the test is valid
- Write Minimal Code: Implement just enough code to make the test pass (Green state)
- Refactor: Improve the code quality while keeping all tests green
- Repeat: Continue the cycle for each new piece of functionality
- One Test at a Time: Focus on one failing test before moving to the next
- Small Steps: Make the smallest possible change to pass each test
- Test Names: Use descriptive test names with underscores (e.g.,
Should_Return_User_When_Valid_Id_Provided) - Fast Tests: Keep tests fast-running to enable frequent execution
- Independent Tests: Each test should be able to run independently of others
- Mock External Dependencies: Use hand-rolled fakes for test doubles
(see
rm-guide-testingfor patterns)
- TUnit Framework: Modern, fast testing framework optimized for .NET
- Constructor Injection: Design services with dependency injection for easy testing
- Component Testing: Use
TestContextfor testing Blazor components - API Testing: Test Azure Functions with HTTP triggers and dependency injection
- Test Doubles: Hand-rolled fakes as primary pattern. LightMock.Generator available
as a compile-time source-gen fallback for large interfaces
(see
.opencode/skills/redmuffin-standards/rm-guide-testing/SKILL.md)
- Pure Functions First: Extract complex logic into
public staticmethods. Test them directly with zero mocking. - Fakes Over Mocks: Hand-rolled
[Interface]_Fakeclasses document the contract explicitly and work under WASM/AOT. - One Test, One Concept: Each test asserts one logical behavior. Multiple assertions for the same concept are fine.
- Visual Studio Integration: Run tests directly from IDE with full debugging support
- Continuous Testing: Tests run automatically on every commit via GitHub Actions
- Code Coverage: Comprehensive coverage reports to ensure test effectiveness
- Fast Feedback: TUnit's performance optimizations enable rapid test execution
- ❌ Writing tests after the implementation (Test-Last Development)
- ❌ Testing implementation details instead of behavior
- ❌ Large, complex tests that are hard to understand and maintain
- ❌ Skipping the refactor step in the Red-Green-Refactor cycle
- ❌ Writing tests that are tightly coupled to specific implementations
- Martin Fowler on TDD
- Kent Beck's "Test Driven Development: By Example"
- Microsoft's TDD Walkthrough
- Uncle Bob's Clean Code TDD
- .NET Testing Best Practices
This project follows authoritative testing best practices that emphasize testing behavior through public interfaces rather than internal implementation details. This approach produces more maintainable, refactor-safe tests that provide long-term value.
- Test Public Contracts: Focus on testing public methods, parameters, and return values
- Avoid Internal Dependencies: Do not test private methods, internal data structures, or implementation details
- Stable Test Foundation: Test what remains stable over time (the interface) rather than what changes frequently (internal logic)
- Refactor-Safe Design: Write tests that survive refactoring when public behavior remains unchanged
- Design for Testability: Encourage refactoring internal code to make it more testable through public interfaces
- Maintainable Tests: Tests remain valid as long as public behavior is preserved
- Flexible Implementation: Internal code can be refactored without breaking tests
- True Regression Protection: Tests verify actual user-facing behavior, not implementation artifacts
- Reduced Test Brittleness: Fewer tests break during legitimate refactoring activities
- Better API Design: Writing tests first against public interfaces leads to cleaner, more intuitive APIs
- Focus on Public APIs: Test methods, properties, and behaviors that are accessible to consumers
- Avoid Private Method Testing: If a private method needs testing, consider making it public or refactoring
- Test Outcomes, Not Steps: Verify what the code produces, not how it produces it
- Use Mocking Judiciously: Mock external dependencies, not internal components
- Design Components for Testing: Structure code so that behavior can be validated through public interfaces
- ❌ Testing private methods directly
- ❌ Asserting on internal data structures or state
- ❌ Testing implementation details that could change during refactoring
- ❌ White-box testing that tightly couples tests to current implementation
- ❌ Testing trivial code (simple getters/setters) that provides no real value
- Martin Fowler on Unit Testing: Emphasizes testing behavior over implementation
- Kent Beck's TDD Philosophy: Focus on what the code should do, not how it does it
- Microsoft .NET Testing Best Practices: Avoid testing internal implementations
- Uncle Bob's Clean Code: Tests should be independent of implementation details
- Google Testing Blog: Advocates for testing behavior through stable interfaces
# Start the full development environment
# Open redmuffin.Blazor.StaticWeb.sln in Visual Studio
# Press F5 or use "Start both" profile
# Application will be available at http://localhost:4280# Build the entire solution
dotnet build
# Run all tests
dotnet test
# Generate code coverage report
.\scripts\Generate-CoverageReport.ps1
# View coverage report
.\scripts\View-CoverageReport.ps1# Create a new feature (example structure)
# Add files under src/redmuffin.Blazor.StaticWeb/Features/YourFeature/
# - YourFeature.razor # Main component
# - YourFeature.razor.cs # Code-behind
# - Components/ # Child components
# Add component styles as SCSS partial:
# - wwwroot/scss/_YourFeature.scss # Component styles (imported in app.scss)# Add new Azure Function
# Add files under src/redmuffin.Blazor.StaticWeb.Api/Functions/
# Functions are automatically discovered by the runtime
# Test API endpoints
# Use browser dev tools or tools like Postman
# Base URL: http://localhost:4280/api/- Frontend: Set breakpoints in
.razor.csfiles - Backend: Set breakpoints in Azure Functions
- Network: Use browser dev tools to inspect API calls
- Create
src/redmuffin.Blazor.StaticWeb/Features/Pages/NewPage.razor - Add
@page "/newpage"directive - Implement component logic in
NewPage.razor.cs - Add styles as SCSS partial in
wwwroot/scss/_NewPage.scssand import inapp.scss - Test locally and add unit tests
- Create
src/redmuffin.Blazor.StaticWeb.Api/Functions/NewFunction.cs - Add
[Function("FunctionName")]attribute - Implement HTTP trigger logic
- Add corresponding tests in test project
- Test with frontend integration
The project follows a feature folder structure to organize code by feature rather than by technical layer. This approach improves maintainability and scalability by grouping related components, services, and assets together.
redmuffin.Blazor.StaticWeb/
├── .github/
│ ├── instructions/ # AI coding guidelines
│ ├── workflows/ # GitHub Actions
│ └── prompts/ # AI prompts
├── src/
│ ├── redmuffin.Blazor.StaticWeb/ # Blazor WebAssembly (.NET 9)
│ │ ├── Features/
│ │ │ ├── Pages/
│ │ │ └── Shared/
│ │ ├── Core/
│ │ ├── wwwroot/
│ │ └── Properties/
│ ├── redmuffin.Blazor.StaticWeb.Api/ # Azure Functions (.NET 9)
│ │ ├── Functions/
│ │ └── Core/
│ ├── redmuffin.Blazor.StaticWeb.Common/ # Shared utilities
│ └── SwaLauncher/ # SWA CLI launcher (.NET 9)
├── tests/
│ ├── redmuffin.Blazor.StaticWeb.Tests/
│ └── redmuffin.Blazor.StaticWeb.Api.Tests/
├── scripts/ # Build & deployment scripts
├── TestResults/ # Test output
-
Blazor WebAssembly Framework for building interactive web UIs using C# instead of JavaScript. Enables client-side execution of .NET code in the browser.
-
C# 12/13 Modern, object-oriented programming language with latest features including primary constructors, collection expressions, and ref readonly parameters.
-
.NET 9 Cross-platform, high-performance framework for building modern applications with the latest features and performance improvements.
-
Azure Functions Serverless compute platform running on .NET 9 with HTTP triggers for RESTful API endpoints.
-
Raindrop.io API External API integration with OAuth 2.0 authentication for bookmark management functionality.
-
Zurb Foundation Responsive front-end framework providing robust grid system, UI components, and accessibility features.
-
SCSS (Sass) CSS preprocessor with variables, nesting, and modularization for modern styling capabilities.
Important: All styling must be done through SCSS files located in
wwwroot/scss/. CSS files inwwwroot/css/are automatically generated and should never be edited directly. Component-specific styles should be created as SCSS partials (starting with underscore) and imported intoapp.scssfor automatic compilation.SCSS Partials: All SCSS partial files must start with an underscore (_) and be imported into
app.scssfor automatic compilation. This ensures proper dependency management and build optimization. -
Blazored.LocalStorage Blazor library for browser local storage access via JavaScript interop.
-
Markdig Fast, extensible Markdown processor for .NET with advanced extensions support.
-
Microsoft.AspNetCore.WebUtilities Utilities for web applications including query string parsing and URL manipulation.
-
TUnit Modern, fast, and flexible .NET testing framework with parallel execution and comprehensive assertion library.
TUnit offers several advantages over xUnit, making it a compelling choice for modern .NET testing:
-
Performance:
- Source Generation: Utilizes source-generated tests to eliminate runtime reflection, significantly improving performance.
- Faster Execution: Tests execute up to 10x faster in TUnit compared to xUnit due to better optimization.
-
Parallel Execution:
- Flexible Parallelism: Provides granular control over test parallelism with custom attributes like
[NotInParallel]and ParallelLimiter. - Intelligent Scheduling: Offers enhanced control over test order and parallel execution.
- Flexible Parallelism: Provides granular control over test parallelism with custom attributes like
-
Modern Architecture:
- Native AOT Support: Full support for Ahead-Of-Time (AOT) compilation and trimming, making it ideal for modern .NET applications.
-
Advanced Test Control:
- Test Dependencies: Supports dependency chains with
[DependsOn], allowing structured integration testing without turning off parallelism. - Retry Logic: Built-in retry mechanisms for specific test scenarios.
- Test Dependencies: Supports dependency chains with
-
Better Setup and Teardown:
- Enhanced Lifecycle Methods: Offers multiple setup and teardown methods with improved management and reduced issues.
-
Compile-Time Safety:
- Type Safe Assertions: Ensures more reliable tests with compile-time assertion checks.
-
Extensibility:
- Customization: Extensively customizable with support for diverse data sources, attributes, and test behavior.
- IDE Integration: Seamless support with major IDEs, improving developer experience and test management.
-
Rich Data Features:
- Fluent Assertions: Utilizes fluent async assertions and provides detailed test metadata for expressive test writing.
-
-
LightMock.Generator Compile-time source-gen mocking library. Used as a fallback when hand-rolled fakes would be too large. For patterns and conventions, see
.opencode/skills/redmuffin-standards/rm-guide-testing/SKILL.md.
-
LibMan (Library Manager) Lightweight client-side library acquisition tool for managing third-party libraries like Foundation.
-
Roslynator Analyzers Provides refactorings, analyzers, and fixes for improving code quality and maintainability.
-
StyleCop Analyzers Enforces a set of style and consistency rules for C# code, ensuring adherence to coding standards.
-
Meziantou Analyzers Offers additional code quality checks focused on performance, security, and best practices.
-
VSThreading Analyzers Ensures threading best practices are followed, especially for asynchronous programming.
-
Coverlet Cross-platform code coverage library for .NET, enabling comprehensive test coverage analysis with multiple output formats.
-
ReportGenerator Powerful tool for generating readable reports from code coverage data, supporting HTML, XML, and various other formats with historical tracking.
-
GitHub Copilot AI-powered code completion tool with MCP server integration.
-
EditorConfig Consistent coding style definitions across different editors and IDEs.
-
Directory.Build.props Centralized MSBuild properties for consistent build configuration across all projects.
The project includes comprehensive development tools and integrations to enhance productivity and code quality.
The Quality Gates toolchain runs Uncle Bob Martin's full
metric suite (CRAP, SCRAP, Architecture, Mutation) as a single dotnet tool.
All four gates must pass before work is considered done. See
tools/README.md for full documentation.
Our code quality standards are grounded in these authors. All code changes
must improve the code per their principles. See rm-guide-cleanup and
rm-gates-cleanup skills for the full rule set.
| Author | Key Contribution |
|---|---|
| Robert C. Martin (Uncle Bob) | Clean Code, SOLID, TDD, metrics-driven development |
| Kent Beck | TDD, Test Desiderata, Extreme Programming, YAGNI |
| Michael Feathers | Working Effectively with Legacy Code, characterization tests |
| Dave Farley | Modern Software Engineering, Continuous Delivery, fast feedback |
| Martin Fowler | Refactoring, patterns, decompose conditional |
| Sandi Metz | Practical OOD, duplication vs wrong abstraction, Rule of Three |
| John Ousterhout | A Philosophy of Software Design, deep modules (use for structure) |
| Steve Freeman & Nat Pryce | Growing OO Software, mock-object TDD |
| Kevlin Henney | Simplicity before generality, use before reuse |
| Mary & Tom Poppendieck | Lean Software Development, eliminate waste |
- Minimum RAM: 8GB (16GB recommended)
- Disk Space: 10GB free space
- OS: Windows 10/11, macOS 10.15+, or Linux (Ubuntu 20.04+)
- CPU: x64 processor with SSE2 instruction set support
MCP servers run as Docker containers for isolation:
- Brave Search — Web search with Brave API
- Fetch — Web content fetching
- Time — Date/time utilities
- Sequential Thinking — AI reasoning assistance
Note: Context7 uses HTTP endpoint, not Docker.
For optimal performance when searching files in large codebases, this project uses Everything Search with its command-line interface es.exe.
Why es.exe?
- Lightning Fast: Leverages NTFS Master File Table for near-instantaneous results (milliseconds vs. seconds/minutes with grep)
- Comprehensive: Searches file names, paths, and content across entire drives
- Developer Optimized: Perfect for .NET projects with many files
Installation:
- Download from voidtools.com
- Install Everything (the GUI app)
- The CLI
es.exewill be available in the installation directory (usuallyC:\Program Files\Everything\es.exe)
Usage Examples:
# Search for files containing "opencode"
es.exe opencode
# Search for C# files with "interface"
es.exe interface *.cs
# Find all .md files
es.exe *.mdTips:
- Results are returned instantly for indexed drives
- Use for both local and external file searches
- If es.exe fails to load, ensure Everything is running and indexing is complete
This tool significantly speeds up development workflows in this project.
Build the entire solution:
dotnet buildBuild specific projects:
dotnet build src/redmuffin.Blazor.StaticWeb/
dotnet build src/redmuffin.Blazor.StaticWeb.Api/Run tests:
dotnet testThe project includes comprehensive code coverage analysis using Coverlet and ReportGenerator:
Generate Coverage Reports:
.\scripts\Generate-CoverageReport.ps1View Coverage Reports:
# View unified coverage report (default)
.\scripts\View-CoverageReport.ps1
# View branded coverage report with history
.\scripts\View-CoverageReport.ps1 -ReportType Branded
# View basic HTML coverage report
.\scripts\View-CoverageReport.ps1 -ReportType HtmlCoverage Features:
- Multiple Output Formats: HTML, XML, JSON, and Cobertura formats
- Unified Reports: Combined coverage from both Blazor and API test projects
- Historical Tracking: Coverage trends over time with the branded report
- Automated Exclusions: Generated files, vendor libraries, and test projects automatically excluded
- Threshold Configuration: Configurable coverage thresholds for quality gates
- Tool Integration: Automatic installation of required tools (ReportGenerator)
Coverage Configuration:
- Coverage settings are configured in test project files (.csproj)
- Global exclusions are defined in Directory.Build.props
- Additional exclusions can be configured in .coverletrc
- Reports are generated in the
coverage/directory
The project is configured for deployment to Azure Static Web Apps:
- Create an Azure Static Web App resource
- Link the GitHub repository to the Azure resource
- Configure build settings:
- App location:
src/redmuffin.Blazor.StaticWeb - API location:
src/redmuffin.Blazor.StaticWeb.Api - Output location:
wwwroot
- App location:
- Push changes to the
masterbranch to trigger automatic deployment
This project is licensed under the Unlicense.
- Markdig for Markdown processing
- Zurb Foundation for the CSS framework
- TUnit for the modern testing framework
- LightMock.Generator for high-performance mocking capabilities
- Blazored.LocalStorage for browser storage integration
- GitHub Copilot for AI code assistance
- Visual Studio for development environment
The API project leverages Azure Functions with .NET 9 Isolated Worker to provide serverless compute capabilities. This integration enables scalable and event-driven backend functionality for the Blazor WebAssembly application.
- Azure Functions Worker SDK (.NET 9): Isolated worker process for better performance and control
- HTTP Triggers: RESTful API endpoints with strong typing and dependency injection
- OAuth Integration: Secure token exchange for external API authentication
- Dependency Injection: Full DI container support with
IHttpClientFactory,ILogger, and custom services
For more details, refer to the Azure Functions Documentation.
The development environment includes several Model Context Protocol (MCP) servers that enhance AI-powered code assistance capabilities. These servers enable AI assistants (like GitHub Copilot) to access external resources, search capabilities, and up-to-date documentation.
- Fetch MCP Server: Retrieves web content from URLs, automatically converting HTML to markdown for easier AI consumption
- Time MCP Server: Provides current time and date information
- Context7 MCP Server: Fetches up-to-date documentation for libraries and frameworks via HTTP endpoint
- Brave Search MCP Server: Provides real-time web search and local business search capabilities (requires API key)
- Sequential Thinking MCP Server: Enables structured, multi-step reasoning and problem-solving through dynamic thought processes
- Chrome DevTools MCP Server: Provides browser automation, performance analysis, network inspection, and debugging capabilities (requires Node.js and Chrome)
- code-review-graph: Local knowledge graph for token-efficient code reviews. Builds structural map of codebase with Tree-sitter, provides blast radius analysis, and 28 MCP tools for code understanding (Python 3.10+ required)
The project uses context-mode for context window optimization when running OpenCode. This tool executes commands in a sandboxed subprocess, keeping raw output out of the context window.
Already Configured:
context-mode is pre-configured in the project's opencode.json plugin list. No additional installation required.
Usage:
# Run commands in sandbox (output stays in subprocess)
ctx_execute --command "dotnet build"
# Analyze files without loading into context
ctx_execute_file --path "src/Program.cs" --language "csharp" --code "console.log(' analyzing...')"Benefits:
- Command output stays in sandbox, only parsed results enter context
- 5x+ token reduction on large outputs
- Works with any CLI command (git, dotnet, npm, etc.)
- File analysis without full content in context
- Real-Time Information: Access current documentation, search results, and web content
- Enhanced Problem Solving: Structured reasoning and comprehensive resource access
- Development Efficiency: Reduce context switching by accessing external resources directly through AI
- Up-to-Date Content: Documentation that's more recent than AI training data cutoffs
- Privacy-Focused: Brave Search integration respects user privacy
MCP servers are pre-configured in the project's .mcp.json file and integrate seamlessly with GitHub Copilot when Docker Desktop is available. Some servers may require API keys (like Brave Search) for full functionality.
Note: The Chrome DevTools MCP server is configured in the project's
opencode.json. Thechrome-devtools-mcppackage is installed globally via npm (npm install -g chrome-devtools-mcp). It requires:
- Node.js v20.19+ (for running
npx)- Google Chrome (the browser to control)
The
--isolatedflag is used to create a temporary user data directory (incognito-like behavior) that is automatically cleaned up after each session.
Once configured, you can ask your AI assistant to:
- "Fetch the latest Blazor WebAssembly documentation"
- "Search for recent .NET 9 performance improvements"
- "Get current Azure Functions examples using Context7"
- "Help me think through this architecture problem step by step"
- Fetch MCP Server can access local/internal IP addresses - ensure proper network security policies in corporate environments
- All servers respect standard web protocols (robots.txt, user-agent settings)
- Configuration is managed through your AI assistant's settings
CRITICAL: This project follows a zero-tolerance policy for secrets in files. The repository MUST NEVER contain a single secret.
The repository MUST NEVER contain any secrets, including:
- API keys or tokens
- Passwords or credentials
- Database connection strings with credentials
- Private keys (SSH, GPG, etc.)
- Session tokens or refresh tokens
- Any sensitive configuration values
Even in private repositories, secrets must never be committed. Automated scanners will find and exploit them within hours.
| Method | Use Case | Syntax |
|---|---|---|
| Environment Variables | MCP configs, scripts | {env:VAR_NAME} or ${env:VAR} |
| GitHub Repository Secrets | CI/CD pipelines | ${{ secrets.SECRET_NAME }} |
| Azure Key Vault | Production deployments | az keyvault secret show |
| User Secrets | Local .NET development | dotnet user-secrets |
All MCP configurations must read secrets from environment variables:
// CORRECT - reads from environment
"env": { "API_KEY": "${env:API_KEY}" }
// WRONG - hardcoded value (NEVER DO THIS)
"env": { "API_KEY": "actual_secret_here" }For CI/CD, use GitHub Repository Secrets:
# Correct - uses repository secrets
env:
Values__RainDropClientId: ${{ secrets.RAINDROP_CLIENT_ID }}
# Wrong - hardcoded value (NEVER DO THIS)
env:
Values__RainDropClientId: "actual_client_id"- IMMEDIATELY rotate the exposed secret (generate new key/token)
- Alert the team about the exposure
- Remove from git history if committed:
git filter-branch --force --index-filter \ 'git rm --cached --ignore-unmatch path/to/file' \ --prune-empty --tag-name-filter cat -- --all git push origin --force --all - Update all dependent systems with the new secret
Before committing, verify:
- No API keys, tokens, or secrets in changed files
- No
password,secret,token,key,credential,authwith visible values - Config files use
${env:VAR}or${input:VAR}syntax only -
.gitignoreincludes sensitive file patterns
The project is configured for seamless development using Visual Studio's multi-project startup feature, which automatically launches all required components.
-
Start the development environment:
- Open
redmuffin.Blazor.StaticWeb.slnin Visual Studio 2026 (Community) - Use the "Start both" profile (or similar multi-project startup configuration)
- Visual Studio will automatically start:
- Blazor WebAssembly frontend
- Azure Functions API backend
- SwaLauncher (which starts the Azure Static Web Apps emulator)
- Open
-
Access the application:
- Open
http://localhost:4280in your browser - All API calls will be routed through the same port as the web app
- OAuth redirects and authentication flows will work correctly
- Open
- Hot Reload: Changes to Blazor components and API functions are automatically reflected
- Unified Routing: Single port for both frontend and API eliminates CORS issues
- Production Simulation: Mimics the exact Azure Static Web Apps runtime environment
- Debugging Support: Full debugging capabilities for both frontend and backend code
For streamlined development and testing, the project now supports a simplified workflow that focuses on frontend development and design validation without requiring the full Azure Functions backend.
-
Start only the main web project:
- In Visual Studio, set
redmuffin.Blazor.StaticWebas the startup project - Press F5 or click "Start"
- The application will launch on
http://localhost:5233
- In Visual Studio, set
-
Automatic mock data integration:
- The application automatically detects when Azure Functions are unavailable
- Mock data services seamlessly replace API calls
- All UI components and user interactions function normally
- Faster Startup: Eliminates the overhead of starting multiple projects and services
- Design-Focused Development: Perfect for UI/UX work, component development, and frontend testing
- Simplified Debugging: Focus on frontend logic without backend complexity
- Developer Friendly: Reduces cognitive load and setup complexity for team members
- Rapid Prototyping: Quickly test design changes and user interactions
| Development Task | Recommended Approach |
|---|---|
| UI/UX Design | Simplified (localhost:5233) |
| Component Development | Simplified (localhost:5233) |
| Frontend Logic Testing | Simplified (localhost:5233) |
| API Integration Testing | Full Stack ("Start both" profile) |
| OAuth Flow Testing | Full Stack ("Start both" profile) |
| End-to-End Testing | Full Stack ("Start both" profile) |
The simplified workflow leverages:
- Conditional service registration based on environment detection
- Mock data providers that simulate realistic API responses
- Seamless fallback mechanisms for external service dependencies
- Consistent data models ensuring compatibility between mock and real data
Developers can easily switch between approaches:
- To Simplified: Stop debugging, set main project as startup, restart
- To Full Stack: Use "Start both" profile or multi-project startup configuration
- No code changes required - the application automatically adapts
- The CLI simulates the Azure Static Web Apps environment, making it ideal for development and testing
- The "Start both" profile in Visual Studio simplifies launching both projects together
- OAuth flows and API integration work seamlessly in this local development setup
- The simplified workflow is particularly beneficial for design-focused tasks and rapid development
This project implements defense-in-depth protections against package manager and dependency chain attacks. All protections were added in April 2025.
Attackers compromise trusted packages to infiltrate downstream applications. Common vectors include:
- Typosquatting (malicious packages with similar names)
- Dependency confusion (internal packages masquerading as public)
- Malicious maintainers publishing compromised updates
- Compromised package maintainer accounts
| Layer | Protection | How It Works |
|---|---|---|
| npm | 7-day release age filter | Blocks packages published within 7 days, preventing fresh supply chain attacks (e.g., Axios incident) |
| npm | Ignore scripts | Blocks postinstall scripts that could execute malicious code during install |
| npm | Exact versions | save-exact=true prevents unexpected version changes from ^ or ~ ranges |
| npm | Strict peer deps | Prevents malformed peer dependency resolution attacks |
| NuGet | Signature validation | signatureValidationMode=accept validates signed packages while allowing unsigned popular ones |
.npmrc # npm supply chain settings
nuget.config # NuGet supply chain settings
Directory.Packages.props # Centralized package versioning
min-release-age=7 # Block recent packages
ignore-scripts=true # No postinstall code
save-exact=true # Exact versions only
strict-peer-deps=true # Strict peer resolution
engine-strict=true # Enforce Node.js version
<config>
<add key="signatureValidationMode" value="accept" />
</config>- 7-day filter: The Axios compromise used a same-day malicious release; this blocks that vector entirely
- Signature validation: NuGet validates package signatures to detect tampering after publication
- The
acceptmode allows unsigned packages while validating signed ones - npm settings are repository-scoped via
.npmrc— applies to all projects in the repo