Closeout phiên go-live 26/06 (4 commit prod-verified đã push: guide-fix159b69d· wipeba42c59· seed-gate55494ad· user-change-passworde81e87f+ backup local). - STATUS/HANDOFF +S91 entry, state re-ground: Mig 59 · 88 bang · 431 test (45D+386I) · gotcha 76 · menu 54 · bundle admin DgQyuG7f/user BmbQon23 · prod 17 user/7 NCC/0 PE-HD - gotchas #75 (SeedDemoMasterData ungated per-code -> resurrect NCC demo moi restart) + #76 (deploy render appsettings.Production.json tu .example, KHONG file gitignored) - session log 2026-06-26-S91 + feedback_wipe_durability_check_reseed (user memory) - harvest cicd-monitor + test-specialist diary (self-written, verified) - .gitignore +backups/ +*.bak Docs/memory-only -> CI skip (paths-ignore docs/** + **/*.md + .gitignore). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
7.4 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: 419 tests = 419 PASS (45 Domain + 374 Infra) as of D2
- Count history + verbose per-session test-design notes (S54→D2): archive/baseline-history.md (L2, on-demand).
- Latest (D2): +6 PE Mig 58 multi-winner —
CreateContractFromEvaluationMultiWinnerTests.cs(5, 1→N HĐ per IsWinner, GiaTri per-winner=quote-total / single=legacy-budget-sum) +PeListWinnerNamesProjectionTests.cs(1, EF collection-projection translation-smoke). No prod bug.
Pattern S67: private-static seed/init → invoke qua REFLECTION (
GetMethod(name, NonPublic|Static)+Invoke(null, [db, roleManager, NullLogger.Instance])); seed MenuItem rows TRƯỚC Permission (FK MenuKey→MenuItem.Key Cascade, SQLite Error 19 nếu thiếu). Cycle-guard test: SqliteDbFixture đủ (no User); rollup-count test cần IdentityFixture. Run:dotnet test SolutionErp.slnx --nologo --verbosity minimal -p:BuildInParallel=false -maxcpucount:1(MSBuild OOM → serialize build)
⚠️ Pattern: deduction hook FK → seed LeaveType cho terminal test (S43)
LeaveBalance→LeaveType Restrict FK. ApproveLeaveRequest terminal (DaDuyet) insert LeaveBalance → test tới DaDuyet PHẢI seed 1 LeaveType + LeaveRequest.LeaveTypeId=type.Id (random Guid→FK fail SQLite 19). Non-terminal (advance/reject/return/OtRequest) KHÔNG cần. BuildLeave optional leaveTypeId default random. Year=StartDate.Year. Negative OK (no quota guard). 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
- gotcha #57 (S51+S52 REPRODUCED): HRM
HrmConfigFilteredUniqueTests2 RED (LeaveType+Shift+OtPolicy) → em main Mig 45.HasFilterGREEN. S52 EXT MasterMasterCatalogFilteredUniqueTests3 RED (Department cfg:18 / Project:19 / Supplier:24 bare.IsUnique()) → pending em main fix migration. Vehicle+Driver (Mig 44) ĐÃ filtered. Pattern: seedIsDeleted=trueslot + Create cùng Code → assert active==1 +IgnoreQueryFiltersall==2.
📅 Recent activity (one-line FIFO — verbose in archive/baseline-history.md)
- 2026-06-26 go-live ChangePassword (test-before-merge SECURITY) — BLOCKED by prod compile bug, REPORTED em main:
ChangePasswordCommandTests.cs10 test WRITTEN, KHÔNG chạy được. ProdChangePasswordCommand.cs:49 throw new ValidationException(failures)= CS0104 AMBIGUOUS (using FluentValidation;+using ...Common.Exceptions;cùng cóValidationException); file untracked, slnx+tests KHÔNG compile. Fix em main = fully-qualify custom exception. Spec rerun: customValidationException.Errors=Dictionary<string,string[]>key=PropertyName (KHÔNG list); IdentityFixture RequiredLength=4 → 12-char là VALIDATOR enforce; tạo user quaUserManager.CreateAsync(user,"<pass>")trực tiếp;FakeCurrentUser{UserId init;IsAuthenticated init}. Kỳ vọng 419→429 khi build xanh. - S87 +7 PE Mig 59 CCM-budget-period setter authz (
PeCcmBudgetPeriodSetterAuthzTests.cs, role-gate Admin|CostControl, null=clear gotcha #73). → 402. - S86 +3 PE drafter-notify-exclusion REGRESSION (bug-fix test-alongside;
PeApproverNotifyExcludesDrafterTests.cs, RED-proven-via-git-stash, CapturingNotificationService spy). - S85b +3 PE UpdateDraft WF-preserve ROOT-FIX (null-safe guard, ApprovalWorkflowId FK Restrict seed-real). S85 +5 PE urgent DRAFTER-self-unset (isDrafter clear-both).
- S77c urgent-toggle SYMMETRIC→ASYMMETRIC correction (SET=role-đủ / UNSET=role AND DeptManager). S77 +7 suggested-price NOTE (rides price-gate, no-arity-fix). S76 +5 PRO-column-split (ProInitial/ProAdjust, ProAdjust ÂM-OK). S72 +28 Mig 54 (CCM-finalize OPT-IN + ApprovedPrice private-static-reflection + isSystem-unreachable OBSERVATION).
- S69b +14 PE CCM-threshold-finalize (strict-
<boundary) + urgent-toggle authz (test-before-merge). S69 +6 Office permission-seed regression (reflection harness, 16-key Read+Create). - S60 +14 PE submit Section-3 guard + drafter-bypass V2-only (GUARD-FIRST; opinion-ownSlot invariant;
PeSubmitGuardAndBypassTests.cs). - S54 +13 ItTicket reassign authz (2-dept matrix, Forbidden red-able BY-CONTRAST, empty-staff 0-leak). S51-S52 (L2
archive/activity-s51-s52.md) filtered-unique RED→Mig45/47 + WorkflowApps codegen + Serializable-on-SQLite NON-ISSUE.
⚠️ 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.