Files
solution-erp/.claude/agent-memory/test-specialist/MEMORY.md
pqhuy1987 396266e5fd [CLAUDE] Docs: S92 closeout — ẩn 5 nhóm menu chỉ Admin (Run #358 PASS prod-verified) + harvest
- Session log 2026-06-29-S92-admin-only-modules.md + STATUS/HANDOFF prepend S92 (demote S91→Prev).
- cicd Run #358 PASS: deploy shipped (dll-mtime in-window 08:00–08:01Z + w3wp recycle 08:04Z),
  smoke 4×200, prod sqlcmd verify (non-Admin CanRead 0 trên 5 nhóm ẩn / 0 leak Contracts·Master·Catalogs
  / Admin giữ 5 key / Pe_* non-Admin 120 untouched). Permission change LIVE.
- Bundle JS rotate admin xz8FdmLN / user CA8Rla2w (CSS frozen) — sửa nhận-định #69-REFINE
  (deploy.yml rebuild FE vô-điều-kiện + rolldown chunk-id non-deterministic → bundle-hash KHÔNG
  đáng-tin làm ship-signal cho BE-only; tín-hiệu thật = dll-mtime + w3wp-recycle).
- Harvest agent-memory: test-specialist (434) + reviewer (S92 topic file + L1 curate 12.9KB) +
  cicd-monitor (Run #358 + prod schema note: bảng Roles/UserRoles KHÔNG prefix AspNetRoles).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 15:10:43 +07:00

7.7 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 RAG search_memory just-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: 434 tests = 434 PASS (45 Domain + 389 Infra) as of S92

  • 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.

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)

  1. DONE S45 — HrmConfig Holiday composite UNIQUE (Year,Date): 7 test (HrmConfigHolidayTests.cs) + surfaced Mig 43 filtered-index fix
  2. DONE S45 — EmployeeSatellite FK invariant + soft-delete + cascade: 10 test (EmployeeSatelliteTests.cs)
  3. DONE S45 — gotcha #44 authz regression EmployeesController + HrmConfigsController: 10 test (extend AuthorizePolicyRegressionTests.cs)
  4. Phase 10.3 Proposal ApproveV2 (S37) + Workflow Apps skeleton (S38) — test-after khi UAT confirm
  5. gotcha #57 (S51+S52 REPRODUCED): HRM HrmConfigFilteredUniqueTests 2 RED (LeaveType+Shift+OtPolicy) → em main Mig 45 .HasFilter GREEN. S52 EXT Master MasterCatalogFilteredUniqueTests 3 RED (Department cfg:18 / Project:19 / Supplier:24 bare .IsUnique()) → pending em main fix migration. Vehicle+Driver (Mig 44) ĐÃ filtered. Pattern: seed IsDeleted=true slot + Create cùng Code → assert active==1 + IgnoreQueryFilters all==2.

📅 Recent activity (one-line FIFO — verbose in archive/baseline-history.md)

  • S92 (2026-06-29) +3 admin-only-modules revoke (test-before-merge SECURITY) — 431→434 PASS, no red: anh chốt 5 nhóm menu (NHÂN SỰ/VĂN PHÒNG SỐ/CÁ NHÂN/HỢP ĐỒNG/DANH MỤC) CHỈ Admin thấy. Em-main mở rộng RevokeTemporarilyHiddenModulesAsync predicate +Contracts/Ct_/Master/Suppliers/Projects/Departments/Catalog (cộng dồn S58 Hrm*/Off*/Personal) + TẮT 2 re-grant S65/S69 + narrow InReviewScope→Pe_*. AdminOnlyModulesRevokeTests.cs (3): NonAdmin-S92Modules-4cờ-false LOAD-BEARING + Admin-untouched + Pe-module-NOT-revoked invariant (root PurchaseEvaluations + Pe_DuyetNcc không match predicate → giữ user). Reflection harness mirror Office/Hrm seed test. Gotcha: OfficeModulePermissionSeedTests + HrmProfilePermissionSeedTests VẪN PASS dù prod đã TẮT 2 call grant — vì test invoke method-CÔ-LẬP qua reflection (private static giữ cho reversibility), KHÔNG chạy SeedAsync chain; thêm 1 dòng header note mỗi file. (ChangePassword S91 10-test ĐÃ merge → trong baseline 431.)
  • 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)

  1. 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.