- Verified-runtime all 3: 2 monitor sub (H1/H2 RE-REPORT) + H2 wave-mode B6 isolation (Run wf_b7e4d6ef-787, chunk 2415=2415, 0 leak) + H3 email send-path (handshake self-verified). - H1 caught 3 doc-freshness drifts -> patched: plugin 15->18, skill-index 31->43 mig + 49->57 gotcha. - gotcha #57 exact coords confirmed: LeaveTypeConfiguration.cs:19 + ShiftPatternConfiguration.cs:19. - AS-10/E-006: monitor sub(s) autonomously wrote canonical+agent-memory files; em-main git-diff commit-gate caught + verified ALL accurate (0 mojibake, chunk 2415, 0 src/tests) -> adopted per keep-if-correct. Process gap flagged for monitor tool-grant review. - Test 181 PASS unchanged (0 .cs). CI-skip (all .md). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8.8 KiB
Test-Specialist Agent — Persistent Memory
Persistent diary cross-session. Auto-injected first ~200 lines at spawn (L1 HOT). Update BEFORE every stop. Tiered Memory v1: L1 HOT soft-cap ~30KB · L2
archive/on-demand · L3 RAGsearch_memoryjust-in-time. Keep entry ≤ 1.5K chars (gotcha #53). NEW agent S39 (2026-05-29) — dedicated test layer (tách khỏi implementer Case 3).
🎯 Role baseline
WRITE specialist độc quyền tests/**. xUnit + FluentAssertions 7.2 + EF SQLite TestApplicationDbContext + IdentityFixture. Tools: Read, Edit, Write, Bash, Grep, Glob + 5 RAG. Skills: contract-workflow + permission-matrix.
🚫 Split boundary
- ✅ MINE:
tests/SolutionErp.{Domain,Infrastructure}.Tests/** - ❌ NOT: production code
src/Backend/**+fe-*/**→ test reveal bug → REPORT em main, KHÔNG fix - ❌ NOT: decide WHAT to test (test plan) → em main + reviewer chốt priority
📊 Baseline 181 PASS (58 Domain + 123 Infra) ← S45 +27 HRM coverage (Gap1/2/3) · S43 +8 LeaveBalance
⚠️ Entry S45 dưới = em main proxy (agent final msg truncated mid-MEMORY-update, gotcha #53 recurrence). Run:
dotnet test SolutionErp.slnx --nologo --verbosity minimal
⚠️ Pattern: deduction hook FK → seed LeaveType cho terminal test (S43)
LeaveBalance → LeaveType Restrict FK. ApproveLeaveRequestHandler terminal branch (DaDuyet) insert LeaveBalance. Test đi tới DaDuyet PHẢI seed 1 LeaveType row + LeaveRequest.LeaveTypeId = type.Id (KHÔNG random Guid → FK fail SQLite Error 19). Non-terminal (advance/reject/return/OtRequest) KHÔNG cần (OtRequest no hook). BuildLeave thêm optional leaveTypeId default random (giữ test cũ non-terminal). Year = StartDate.Year. Negative allowed (no quota guard → Remaining<0 OK). Query lazy synth Entitled=DaysPerYear khi 0 row.
⏱️ Timing rules (docs/rules.md §7)
- Feature mới = test-after (UAT ổn → viết, Phase 9 skip per
feedback_uat_skip_verify) - Bug fix = test-before BẮT BUỘC (reproduce → fix)
- Critical algo = test-before merge (codegen/guard/financial/security)
- Skip: DTO mapping, CRUD master, FE snapshot
📋 Patterns proven (apply confidently)
Pattern 10 Reflection authz regression (~50 LOC)
Catch class-level [Authorize(Policy=...)] regression: typeof(Ctrl).GetCustomAttribute<AuthorizeAttribute>().Policy.Should().Be(...). KHÔNG WebApplicationFactory heavy. Cho gotcha #44 silent 403.
Pattern 11 Test infra helper cookie-cutter
SeedWorkflowAsync (1 Step DepartmentId=null skip FK + 2 Levels) + SeedApproversAsync (N user fix.CreateUserAsync). Reusable PE/Contract/Proposal workflow test.
Pattern 12 InternalsVisibleTo
Expose internal helper via <InternalsVisibleTo Include="SolutionErp.Infrastructure.Tests" /> csproj.
Spec drift detection BEFORE write (S34 lesson)
Test theo CODE (single source truth), document mismatch header comment + report. Vd soft-delete UNIQUE: code chặn opt-out → test theo code, flag drift.
gotcha #48 SQLite tie-break
OrderByDescending(CreatedAt).First() pick wrong khi 2+ Add() cùng CreatedAt frozen-clock → discriminator filter .Where(Summary.Contains("Chuyển phase")) BEFORE OrderBy.
🎯 Coverage gap backlog (priority — Reviewer flagged S36)
- ✅ DONE S45 — HrmConfig Holiday composite UNIQUE (Year,Date): 7 test (
HrmConfigHolidayTests.cs) + surfaced Mig 43 filtered-index fix - ✅ DONE S45 — EmployeeSatellite FK invariant + soft-delete + cascade: 10 test (
EmployeeSatelliteTests.cs) - ✅ DONE S45 — gotcha #44 authz regression EmployeesController + HrmConfigsController: 10 test (extend
AuthorizePolicyRegressionTests.cs) - Phase 10.3 Proposal ApproveV2 (S37) + Workflow Apps skeleton (S38) — test-after khi UAT confirm
- NEW (gotcha #57): LeaveType.Code + ShiftPattern.Code UNIQUE chưa filter
[IsDeleted]=0(cùng bug class Holiday đã fix Mig 43) — recreate-on-soft-deleted-slot → 500. Test-before khi fix.
📅 Recent activity (last 10 FIFO)
- 2026-05-29 (S40 baseline audit smoke): CONFIRMED 130 PASS (Domain 58 + Infra 72), 0 fail/skip, ~15s. Runner count authoritative; raw
[Fact]/[Theory]attr = 48+70 (Theory→InlineData expand). Infra spread 15 files. Gap re-verified vs prod: EmployeesController+HrmConfigsController EXIST, authz regression chỉ ApprovalWorkflowsV2Controller (gotcha #44 gap real). Proposal = Domain entity + EF config only, CHƯA có ApproveV2Async service (S37 skeleton, defer đúng). Agent load OK. AUDIT-only, no write. - 2026-05-30 (S42 P11-A Wave4): +11 test
tests/.../Application/WorkflowAppApproveV2Tests.cs→ 141 PASS (Infra 72→83). LeaveRequest 8 case full (Submit happy/guard×2, Approve advance/terminal/UPSERT-invariant/forbidden/empty-comment-placeholder, Reject→TuChoi, Return→TraLai+RejectedFromStatus) + OtRequest smoke (submit→approve single-level→DaDuyet). No prod bug — LeaveOt ApproveV2 wire correct, all PASS first run. NEW Pattern: WorkflowApps handlers = CQRS MediatR (KHÔNG service) → instantiate handler trực tiếpnew ApproveLeaveRequestHandler(db, AsUser(u), clock).Handle(cmd,ct), chỉ 3 dep (IApplicationDbContext + TestCurrentUser + FixedDateTime) — nhẹ hơn 6-dep Contract service. MaDonTu format "DT/LR/2026/001". Gap #4 (Workflow Apps) PARTIAL done — Travel/Vehicle mirror pending. ⚠️ Lesson: CWD drift (fe-user) → ghi MEMORY nhầm path, em main relocate. Verify CWD root trước Write memory. - 2026-05-30 (S43 P11-B Wave3 LeaveBalance): +8 test
tests/.../Application/LeaveBalanceTests.cs→ 152 PASS (Infra 86→94). Deduction hook (ApproveLeaveRequestHandler terminal) full: deduct single-level (create row from DaysPerYear), only-at-terminal multi-level (advance no-deduct + 1× terminal), accumulate UPSERT (5+2=7 no new row), negative allowed (Used20>Entitled12 → Remaining−8 no throw), Reject+Return no-deduct (split 5a/5b), GetMyLeaveBalances lazy synth (2 active type filter inactive), AdjustLeaveBalance upsert. ⚠️ FOUND + FIXED 2 pre-existing RED in S42 template (Approve_LastLevel_TransitionsToDaDuyet+Approve_EmptyComment_StoresPlaceholder): Wave 1 deduction hook (uncommitted, prod) làm terminal insert LeaveBalance FK→LeaveTypes Restrict FAIL vì BuildLeave dùngLeaveTypeId=Guid.NewGuid(). NOT prod bug (prod đơn luôn pin LeaveType thật) — fix tại test: BuildLeave +optional leaveTypeId, seed LeaveType ở 2 test đó. Baseline thật trước S43 = 142-pass/2-RED (KHÔNG phải 144-green). REPORTED em main. - 2026-06-01 (S45 HRM coverage gaps + Holiday drift) [em main proxy]: +27 test → 181 PASS (Infra 96→123). 3 file: HrmConfigHolidayTests (7 — composite UNIQUE Create/Update, ⭐self-update giữ key đổi Name no-false-positive, soft-delete exclusion) + EmployeeSatelliteTests (10 — 5× FK-invariant parent
AnyAsync(!IsDeleted)guard + soft-delete + cascade-non-behavior Case5 + EF modelDeleteBehavior.Cascadeconfig assertion) + AuthorizePolicyRegressionTests extend (10 — HrmConfigs bare-[Authorize]+writesRoles=Admin; Employees class-Policy=Hrm_HoSo.Read+per-action). FOUND drift (test theo CODE = single source): Holiday DB UNIQUE (Year,Date) unfiltered vs handler!IsDeleted→ recreate-on-soft-deleted-slotDbUpdateException(500). REPORTED → em main fixed Mig 43.HasFilter("[IsDeleted]=0")(Case 7 flipped assert SUCCESS). New pattern: EF model-metadata assertiondb.Model.FindEntityType(typeof(X)).GetForeignKeys()...DeleteBehaviorlock schema intent. ⚠️ gotcha #57 backlog: LeaveType.Code + ShiftPattern.Code vẫn unfiltered. - 2026-06-07 (S50 wave
h2-verify— test-structure analysis, write-direct B4) [em main harvest from wave sub-MD]: No new test (plumbing test). CONFIRMED 181 split = 58 Domain (3 files) + 123 Infra (19 test + 4 infra Common); raw attrs 48+121=169 → 181 via[Theory]/[InlineData]expand (note: corrects older "58+72" → now 58+123 post-S45). gotcha #57 exact coords (test-before when fixed): bug OPEN @LeaveTypeConfiguration.cs:19+ShiftPatternConfiguration.cs:19(bare.IsUnique(), no filter) vs fixedHolidayConfiguration.cs:18 .HasFilter("[IsDeleted] = 0"). Template =HrmConfigHolidayTests.cs:180-197(Case 7 filtered-unique proof) — mirror: seed soft-deleted row in slot → Create same slot succeeds → 3 asserts (id NotBeEmpty + CountAsync(active)==1 + CountAsync(all)==2). SQLite honors filtered-unique. Test home =tests/.../Application/. Tag [wave-h2, gotcha-57-coords, plumbing].
⚠️ Anti-patterns (DO NOT)
- ❌ Touch production code → REPORT bug · 2. ❌ Skip MEMORY · 3. ❌ Test không chạy (dotnet test must PASS) · 4. ❌
git add -A· 5. ❌ Push remote · 6. ❌ Assertion trivial
🔄 Curate trigger
Size > ~30KB → archive to L2 (tiered v1). Commit scope (em main commits): Tests.