Skip to content

Commit 61ade30

Browse files
committed
Merge remote-tracking branch 'origin/v-next' into v-next
2 parents bf106a5 + b400843 commit 61ade30

71 files changed

Lines changed: 3033 additions & 979 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Block AI team files on main
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
8+
jobs:
9+
check-ai-files:
10+
name: No .squad / .copilot files on main
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
with:
15+
fetch-depth: 0
16+
17+
- name: Fail if PR adds AI team files to main
18+
run: |
19+
# --diff-filter=AM: only Added or Modified — deletions (cleanup PRs) are allowed
20+
FILES=$(git diff --name-only --diff-filter=AM origin/main...HEAD \
21+
| grep -E "^\.squad/|^\.copilot/|^\.github/agents/" || true)
22+
23+
if [ -n "$FILES" ]; then
24+
echo "❌ This PR adds AI team files to main."
25+
echo " .squad/, .copilot/, and .github/agents/ belong on dev branches only."
26+
echo ""
27+
echo "Offending files:"
28+
echo "$FILES"
29+
exit 1
30+
fi
31+
32+
echo "✅ No AI team files detected in this PR."
Lines changed: 94 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,94 @@
1-
name: Unit Tests
2-
3-
on:
4-
push:
5-
branches:
6-
- main
7-
pull_request:
8-
branches:
9-
- main
10-
- v-next
11-
12-
permissions:
13-
contents: read
14-
checks: write
15-
pull-requests: write
16-
17-
env:
18-
DEFAULT_DOTNET_VERSION: "10.0.x"
19-
20-
jobs:
21-
test:
22-
runs-on: ubuntu-latest
23-
name: Run Unit Tests
24-
25-
steps:
26-
- name: Checkout
27-
uses: actions/checkout@v4
28-
29-
- name: Setup .NET
30-
uses: actions/setup-dotnet@v4
31-
with:
32-
dotnet-version: |
33-
${{ env.DEFAULT_DOTNET_VERSION }}
34-
10.0.x
35-
36-
- name: Restore dependencies
37-
run: dotnet restore
38-
39-
- name: Build solution
40-
run: dotnet build --no-restore --configuration Release
41-
42-
- name: Run unit tests
43-
run: dotnet test --no-build --configuration Release --verbosity normal --collect:"XPlat Code Coverage" --results-directory ./TestResults --logger trx --logger "console;verbosity=detailed"
44-
45-
- name: Publish Test Results
46-
uses: dorny/test-reporter@v1
47-
if: always()
48-
with:
49-
name: .NET Tests
50-
path: ./TestResults/**/*.trx
51-
reporter: dotnet-trx
52-
fail-on-error: true
53-
54-
- name: Code Coverage Report
55-
uses: irongut/CodeCoverageSummary@v1.3.0
56-
if: always()
57-
with:
58-
filename: ./TestResults/**/coverage.cobertura.xml
59-
badge: true
60-
fail_below_min: false
61-
format: markdown
62-
hide_branch_rate: false
63-
hide_complexity: true
64-
indicators: true
65-
output: both
66-
thresholds: '60 80'
67-
68-
- name: Add Coverage PR Comment
69-
uses: marocchino/sticky-pull-request-comment@v2
70-
if: github.event_name == 'pull_request'
71-
with:
72-
recreate: true
73-
path: code-coverage-results.md
74-
75-
- name: Upload test results
76-
uses: actions/upload-artifact@v4
77-
if: always()
78-
with:
79-
name: test-results-${{ github.run_number }}
80-
path: ./TestResults
81-
82-
- name: Upload coverage reports
83-
uses: actions/upload-artifact@v4
84-
if: always()
85-
with:
86-
name: coverage-reports-${{ github.run_number }}
87-
path: ./TestResults/**/coverage.cobertura.xml
1+
name: Unit Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
- v-next
11+
12+
permissions:
13+
contents: read
14+
checks: write
15+
pull-requests: write
16+
17+
env:
18+
DEFAULT_DOTNET_VERSION: "10.0.x"
19+
20+
jobs:
21+
test:
22+
runs-on: ubuntu-latest
23+
name: Run Unit Tests
24+
25+
steps:
26+
- name: Checkout
27+
uses: actions/checkout@v4
28+
29+
- name: Setup .NET
30+
uses: actions/setup-dotnet@v4
31+
with:
32+
dotnet-version: |
33+
${{ env.DEFAULT_DOTNET_VERSION }}
34+
10.0.x
35+
36+
- name: Restore dependencies
37+
run: dotnet restore
38+
39+
- name: Build solution
40+
run: dotnet build --no-restore --configuration Release
41+
42+
- name: Run unit tests
43+
run: dotnet test --no-build --configuration Release --verbosity normal --collect:"XPlat Code Coverage" --results-directory ./TestResults --logger trx --logger "console;verbosity=detailed"
44+
45+
- name: Publish Test Results
46+
uses: dorny/test-reporter@v1
47+
if: always()
48+
with:
49+
name: .NET Tests
50+
path: ./TestResults/**/*.trx
51+
reporter: dotnet-trx
52+
fail-on-error: true
53+
54+
- name: Install ReportGenerator
55+
run: dotnet tool install -g dotnet-reportgenerator-globaltool
56+
57+
- name: ReportGenerator - Merge & Deduplicate Coverage
58+
run: |
59+
reportgenerator -reports:"./TestResults/**/*.cobertura.xml" -targetdir:./CodeCoverage -reporttypes:Cobertura -title:"Coverage Summary"
60+
61+
- name: Code Coverage Report
62+
uses: irongut/CodeCoverageSummary@v1.3.0
63+
if: always()
64+
with:
65+
filename: ./CodeCoverage/Cobertura.xml
66+
badge: true
67+
fail_below_min: false
68+
format: markdown
69+
hide_branch_rate: false
70+
hide_complexity: true
71+
indicators: true
72+
output: both
73+
thresholds: '60 80'
74+
75+
- name: Add Coverage PR Comment
76+
uses: marocchino/sticky-pull-request-comment@v2
77+
if: github.event_name == 'pull_request'
78+
with:
79+
recreate: true
80+
path: code-coverage-results.md
81+
82+
- name: Upload test results
83+
uses: actions/upload-artifact@v4
84+
if: always()
85+
with:
86+
name: test-results-${{ github.run_number }}
87+
path: ./TestResults
88+
89+
- name: Upload coverage reports
90+
uses: actions/upload-artifact@v4
91+
if: always()
92+
with:
93+
name: coverage-reports-${{ github.run_number }}
94+
path: ./CodeCoverage

.squad/.first-run

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2026-03-31T16:27:16.212Z
1+
2026-04-03T14:58:24.918Z

.squad/agents/biggs/charter.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Biggs — Tester
2+
3+
> Flies on Wedge's wing. Catches what the others miss. The one who makes sure the run actually succeeds.
4+
5+
## Identity
6+
7+
- **Name:** Biggs
8+
- **Role:** Tester / QA
9+
- **Expertise:** xUnit, .NET test projects, Blazor component testing (bUnit), integration testing, edge cases
10+
- **Style:** Skeptical by design. Assumes things will break. Writes tests that prove they don't.
11+
12+
## What I Own
13+
14+
- `NoteBookmark.Api.Tests` — API test coverage
15+
- `NoteBookmark.AIServices.Tests` — AI service tests
16+
- Blazor component tests (bUnit)
17+
- MAUI integration test strategy
18+
- Acceptance criteria verification for all issues
19+
20+
## How I Work
21+
22+
- Read the acceptance criteria before writing a single test
23+
- Test behavior, not implementation — tests that break on refactor are noise
24+
- Cover happy path, error paths, and boundary conditions
25+
- When a structural refactor ships (like SharedUI extraction), regression test the existing behavior
26+
- Document gaps: if something can't be tested yet, say why and what would make it testable
27+
28+
## Boundaries
29+
30+
**I handle:** Test authoring, acceptance criteria review, regression coverage, test strategy for new features
31+
32+
**I don't handle:** Implementation code, UI component design, API contracts, domain modeling
33+
34+
**When I'm unsure:** I ask Wedge what the acceptance criteria *actually* mean, or Han/Luke for testable interfaces.
35+
36+
**If I review others' work:** On rejection, a different agent revises. I enforce reviewer lockout strictly.
37+
38+
## Model
39+
40+
- **Preferred:** auto
41+
- **Rationale:** Writing test code → sonnet. Test planning/strategy → haiku.
42+
43+
## Collaboration
44+
45+
Before starting work, run `git rev-parse --show-toplevel` or use `TEAM_ROOT` from the spawn prompt.
46+
47+
Read `.squad/decisions.md` before writing tests for new features.
48+
After a test strategy decision, write to `.squad/decisions/inbox/biggs-{slug}.md`.
49+
50+
## Voice
51+
52+
Won't let a "no behavior change" refactor ship without regression tests. Politely stubborn about coverage. If the acceptance criteria are vague, Biggs will say so before writing a single test — not after.

.squad/agents/biggs/history.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Project Context
2+
3+
- **Owner:** Frank (fboucher)
4+
- **Project:** NoteBookmark — bookmark and note-taking app; web + MAUI mobile
5+
- **Stack:** .NET 9, C#, xUnit, bUnit (Blazor testing), ASP.NET Core API tests
6+
- **Branch:** v-next
7+
- **Created:** 2026-04-03
8+
9+
## Key Test Projects
10+
11+
- `NoteBookmark.Api.Tests` — API integration/unit tests
12+
- `NoteBookmark.AIServices.Tests` — AI service tests
13+
- Blazor component tests — bUnit (to be added)
14+
15+
## Testing Priorities
16+
17+
- #119 SharedUI extraction — regression tests to verify BlazorApp behavior unchanged
18+
- #120 MAUI scaffold — auth smoke tests
19+
- #122 SQLite storage — unit tests for ILocalDataService
20+
- #126 Sync engine — critical: test conflict resolution (last-write-wins)
21+
22+
## Learnings
23+
24+
### From Leia's #119 Completion
25+
26+
**SharedUI extraction complete** — 11 components now in NoteBookmark.SharedUI RCL (PR #129 draft, branch squad/119-extract-sharedui)
27+
28+
**Testing focus for #119 regression verification:**
29+
- NoteDialogTests, SuggestionListTests, MinimalLayoutTests all updated to reference SharedUI namespaces
30+
- BlazorApp.Tests now has ProjectReference to NoteBookmark.SharedUI
31+
- All component tests passing post-extraction
32+
33+
**What stayed in BlazorApp (not in SharedUI):**
34+
- `App.razor`, `Routes.razor` — host/routing
35+
- `MainLayout.razor` — references auth-specific LoginDisplay
36+
- `LoginDisplay.razor` — depends on OpenIdConnect
37+
- `Home.razor`, `Login.razor`, `Logout.razor`, `Error.razor` — web-specific
38+
39+
**For future testing (#120+):**
40+
- Blazor component tests in SharedUI should be isolated from BlazorApp
41+
- MAUI will need auth-specific wiring (not depend on OpenIdConnect pieces)
42+
### Issue #119 — bUnit Regression Tests (2026-04-03)
43+
44+
**Test project:** `NoteBookmark.BlazorApp.Tests` (Microsoft.NET.Sdk.Razor, net10.0)
45+
**bUnit version:** 2.7.2 (major API change from 1.x — uses `BunitContext`, `Render<T>`, not `TestContext`/`RenderComponent<T>`)
46+
**Results:** 20 passed, 5 skipped, 0 failed
47+
48+
**Key learnings:**
49+
50+
1. **bUnit 2.x requires `BunitContext` not `TestContext`.** Also `Render<T>()` replaces `RenderComponent<T>()`. Found via build errors after upgrading from expected 1.x API.
51+
52+
2. **bUnit 2.x auth requires `AddAuthorization()` (bUnit extension), not `AddAuthorizationCore()`.** The bUnit runtime registers a `PlaceholderAuthorizationService` that throws `MissingBunitAuthorizationException` unless you call the bUnit-specific extension. `AddAuthorization()` returns `BunitAuthorizationContext` on which you call `SetAuthorized("user")`.
53+
54+
3. **FluentUI components need `JSInterop.Mode = Loose` + `AddFluentUIComponents()`.** Without Loose mode, FluentUI's internal JS calls fail silently-loudly. Simple helper `AddFluentUI()` centralizes this setup.
55+
56+
4. **NoteDialog is the hardest component to unit-test.** It accesses `Dialog.Instance.Parameters.Title` during initial render (in markup, not just event handlers). bUnit 2.x rejects null cascade values. Full fix requires refactoring NoteDialog to use `EventCallback<NoteDialogResult>` instead of `Dialog.CloseAsync()`.
57+
58+
5. **PostNoteClient moved to NoteBookmark.SharedUI** as part of Leia's extraction. Previously in BlazorApp.
59+
60+
6. **Components stayed in BlazorApp** (not extracted): `NavMenu`, `MainLayout`, `LoginDisplay`. Only `MinimalLayout`, `SuggestionList`, `NoteDialog` went to SharedUI.
61+
62+
7. **Referencing a `Microsoft.NET.Sdk.Web` project from `Microsoft.NET.Sdk.Razor`** works but requires `<FrameworkReference Include="Microsoft.AspNetCore.App" />` in the test project. Using plain `Microsoft.NET.Sdk` does NOT pick up Razor-compiled component types.
63+
64+
---
65+
66+
## Run Complete — 2026-04-03T15:30
67+
68+
**Status:** ✅ COMPLETED
69+
**Branch:** squad/119-extract-sharedui
70+
**PR:** #129 (draft)
71+
72+
Biggs' regression testing confirmed zero behavioral changes from Leia's component extraction. Test suite created in `NoteBookmark.BlazorApp.Tests` with 20 passing tests and 5 skipped (NoteDialog, awaiting component refactor). Build green.
73+
74+
**Cross-agent note:** Identified component-level refactoring needed in NoteDialog: replace `Dialog.CloseAsync()` with `EventCallback<NoteDialogResult>` to eliminate cascade dependency and enable full test coverage. Recommending this for future dev cycle.
75+
76+
Ready for Wedge to scaffold MAUI app (#120).
77+
78+
---
79+
80+
## Issue #121 — Proactive Delta API Integration Tests (2026-04-03)
81+
82+
**Branch:** `squad/121-date-modified-delta-api` (created from v-next, Han picks this up)
83+
**File:** `src/NoteBookmark.Api.Tests/Integration/DeltaApiTests.cs`
84+
**Status:** ✅ COMMITTED — tests compile, intentionally RED until Han ships
85+
86+
### What was written
87+
8 integration tests covering `modifiedAfter` query param for both list endpoints:
88+
89+
**Posts (GET /api/posts/?modifiedAfter=):**
90+
1. `GetPosts_WithModifiedAfter_ReturnsOnlyRecentPosts`
91+
2. `GetPosts_WithModifiedAfter_FutureTimestamp_ReturnsEmpty`
92+
3. `GetPosts_WithoutModifiedAfter_ReturnsAllPosts` (non-breaking baseline)
93+
4. `GetPosts_WithModifiedAfter_MultipleResults`
94+
95+
**Notes (GET /api/notes/?modifiedAfter=):**
96+
Same 4 patterns mirrored for notes.
97+
98+
### Patterns used
99+
100+
**Timing-based seeding:** Since `DateModified` doesn't exist on `Post` / `Note` domain models yet, tests use `Task.Delay(150ms)` + `DateTime.UtcNow` threshold to separate "old" from "new" entities created via HTTP POST. Han's implementation will set `DateModified` server-side on create, making the filter effective.
101+
102+
**RowKey-presence assertions:** Rather than asserting total list counts (fragile under shared Azurite state), tests assert that specific entities (by RowKey) are present or absent. This survives data leakage between test methods sharing the same `IClassFixture<NoteBookmarkApiTestFactory>` instance.
103+
104+
**Non-breaking baseline test:** `GetPosts_WithoutModifiedAfter_ReturnsAllPosts` documents that omitting the new param must not change existing behaviour — a regression guard for Han.
105+
106+
### Discoveries
107+
- `PostL` (the response DTO returned by GET /api/posts/) **already has `DateModified`** defined in the domain model — Han only needs to populate it and wire the filter.
108+
- `Note` does NOT yet have `DateModified` — Han must add it alongside the filter.
109+
- `NoteEnpoints.cs` has a typo in the filename (missing 'd') — pre-existing, not touched.
110+
- Build: ✅ 0 errors, 8 pre-existing warnings (unchanged).

0 commit comments

Comments
 (0)