Files
solution-erp/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-test-k4a.md
2026-08-01 09:45:05 +07:00

11 KiB
Raw Blame History

TEST-K4A: DONE — 7/7 PASS (6 gốc + T7 finding-D) · Infra 569→576 · Domain 45 · tổng 621 · 0 hồi quy · 0 prod-code touched

Lane test K4a — test-after 49 row menu KHKK 8-nhóm (S166, 2026-08-01)

Agent: test-specialist. Ghi-từng-mục TRONG LÚC LÀM (luật sống-còn bài K3 engine-exit). Mục nào chưa chạy = ghi tiêu-đề + "CHƯA ĐO", KHÔNG điền số dự-đoán.

§0 — Trạng thái khởi động

  • §0.1 ĐÃ đọc khuôn. Chốt harness: seeder là private static → REFLECTION.
    • SeedMenuTreeAsync(ApplicationDbContext, ILogger) — 2 tham số.
    • SeedAllRolesReviewReadPermissionsAsync(ApplicationDbContext, RoleManager<Role>, ILogger) — 3 tham số (site-2).
    • Khuôn invoke đã có sẵn ở ContractCatalogTests.cs:459 (InvokePrivateSeedAsync, rm nullable) → dùng lại HÌNH, không chế fixture mới. Fixture = IdentityFixture (cần RoleManager cho site-2).
  • §0.2 ĐÃ đọc BE đã land (git diff, chưa commit):
    • MenuKeys.cs:72-84KhkkGroupNumbers=[2..8] · KhkkLeafActions=[WfView,List,Create,Pending,Approved,Deleted] · KhkkGroupMenuKeys = SelectMany(group → 6 leaf) · spread .. KhkkGroupMenuKeys vào All:244.
    • DbInitializer.cs:2115-2157 — vòng seed 49 row menu (parent KeHoachKyKet, Order nhóm = n, leaf Order 1..6) + :2235 labelBackfill Khkk_G1 + :2442-2446 site-2 KhkkKeys().Concat(KhkkGroupMenuKeys).
    • Đếm BY-SOURCE All = 113 (tự cộng tay từng dòng initializer). Chưa phải số ĐO — sẽ đo runtime ở T1.
  • §0.3 CHỒNG LẤN ĐÃ CÓ (không viết lại): ContractCatalogTests.cs:445 T6 K1 assert MenuKeys.All.Except(menuKeysInDb).Should().BeEmpty() sau SeedMenuTreeAsync ⇒ invariant "49 key mới ĐỀU có menu-row" ĐÃ được test cũ phủ tự động (nó là phép thử của claim by-construction). Lane K4a KHÔNG nhân bản; thay vào đó đo cái test cũ KHÔNG đo: hình bộ sinh · đếm 56 row họ Khkk · idempotent ×2 · labelBackfill · grant site-2.
  • §0.4 Baseline Infra ĐO TƯƠI 2026-08-01 (trước khi thêm file): 569 PASS / 0 FAIL (42 s). Đo trên cây có BE K4a đã land ⇒ test T6-K1 cũ (All.Except(menuKeysInDb)) XANH với 49 key mới = bằng chứng độc lập rằng vòng seed 49 row khớp đúng bộ key trong All.

§1..§4 — File test đã LAND: tests/SolutionErp.Infrastructure.Tests/Application/KhkkGroupMenuSeedTests.cs (6 test)

Harness: reflection private static + IdentityFixture (không fixture mới). Không đụng prod code.

# Test Đo cái gì Chống vacuous bằng
T1 KhkkGroupMenuKeys_Is49Keys_GroupImmediatelyFollowedBySixLeaves 49 key · Equal với mảng 49 chuỗi VIẾT TAY (không gọi lại factory ⇒ không tautology) · liền-kề Khkk_G{n} ngay trước Khkk_G{n}_WfView · pin 6 action kỳ vọng là literal, sai 1 ký tự là đỏ
T2 MenuKeys_All_ContainsThe49_NoDuplicate_AndKeepsGroup1Outside All ⊇ 49 · OnlyHaveUniqueItems · HaveCount(113) = số ĐO runtime · KhkkGroupNumbers == [2..8] · 7 key nhóm 1 KHÔNG nằm trong All vế cuối = guard thật: thêm số 1 vào bộ sinh ⇒ tree.Add("Khkk_G1") lần 2 ⇒ trùng PK lúc boot
T3 SeedMenuTree_RunTwice_Has56KhkkRows_NoDuplication_AndCorrectTreeShape seed ×2: 56 row họ Khkk (7 nhóm-1 + 49) · lần 2 danh sách key Y NGUYÊN · tổng bảng MenuItems bất động · 8 group parent=KeHoachKyKet Order=n · mỗi group đúng 6 con · 8 nhãn nhóm phân biệt đếm bằng Ordinal in-memory (không phụ thuộc collation LIKE); đã kiểm KHÔNG có HasData cho MenuItems ⇒ 56 row đến TỪ seeder, không phải EF seed
T4 SeedMenuTree_NewGroupLeaves_MirrorGroup1_LabelIconOrder 42 leaf mới khớp nhãn + icon + Order của leaf nhóm 1 tương ứng (đo DB↔DB, không bảng hardcode) chốt trước "6 nhãn nhóm 1 PHÂN BIỆT" — nếu chúng trùng nhau, phép so mirror đúng ở mọi thế giới
T5 SeedMenuTree_LabelBackfill_HealsGroup1Label_WithoutAddingRows_ControlKeyStaysDirty MA TRẬN cùng-ruột-khác-1-biến: bôi hỏng nhãn CẢ Khkk_G1 (ca thật: về chuỗi skeleton S159) LẪN Khkk_G2 trong cùng 1 lần → seed lại → G1 tự lành, G2 giữ chữ hỏng; tổng row bất động 2 kết quả TRÁI NGƯỢC trong 1 lần chạy; khác biệt duy nhất = có/không entry labelBackfill ⇒ chứng đúng cơ chế backfill, không phải upsert "tiện tay" ghi Label (gotcha #11)
T6 SeedAllRolesReviewRead_GrantsReadOnNew49KhkkKeys_ReadOnly_WithPeContrast site-2 phát CanRead cho cả 49 key (nêu tên Khkk_G2_List) · C/U/D=false · 7 key nhóm 1 GIỮ quyền (.Concat mở rộng chứ không thay thế) ĐỐI CHỨNG CÙNG LƯỢT: cùng role đó Pe_DuyetNcc_List có CanCreate=true ⇒ chuỗi "false" trên Khkk không phải vì seeder no-op. Chạy SeedMenuTreeAsync trước = smoke FK cho TOÀN grant-set

Đường vào duy nhất của 49 key ở site-2 = KhkkKeys().Concat(MenuKeys.KhkkGroupMenuKeys) (K4a) — All.Where(StartsWith "Pe_") không bắt Khkk_* ⇒ gỡ .Concat là T6 ĐỎ.

§5 — Chạy filtered — ĐÃ ĐO

dotnet test tests/SolutionErp.Infrastructure.Tests --filter "FullyQualifiedName~KhkkGroupMenuSeed" --nologoFailed: 0, Passed: 6, Total: 6 (3 s). Ghi chú số: MenuKeys.All runtime = 113 ĐÚNG như đếm by-source của lane BE (assert xanh) ⇒ Policies DERIVED = 113 × 4 = 452.

§6 — Full suite — ĐÃ ĐO

  • Infra: Failed 0, Passed 575, Total 575 (30 s) — baseline 569 + 6 = 575 ⇒ 0 hồi quy.
  • Domain: Failed 0, Passed 45 (311 ms) — đo tươi, không lấy từ trí nhớ.
  • Tổng suite = 45 + 575 = 620.

Caveat trung thực: giữa lượt làm có commit 7a903cf (lane F-1, FE + ContractSigningPlanFeatures.cs) land vào repo. Đã kiểm: git diff --stat -- src/ vẫn đúng 42/79 dòng của riêng K4a như lúc mở lượt ⇒ byte nguồn K4a KHÔNG đổi trong lúc đo; file lane F-1 đã nằm sẵn trên đĩa TRƯỚC lần chạy full (nó là M từ đầu, commit chỉ chuyển trạng thái index) ⇒ con số 575 đo đúng trạng thái đĩa hiện tại.

§7 — Rào đã giữ

  • KHÔNG chạm production code: git status cho thấy dưới tests/ chỉ có 1 file MỚI KhkkGroupMenuSeedTests.cs; 2 file src/ đang M là diff của lane BE K4a (có từ trước lượt này).
  • KHÔNG đụng ContractSigningPlan* / KhkkWorkflowPanel / khkk.ts.
  • KHÔNG commit, KHÔNG push.
  • KHÔNG sửa SolutionErp.slnx (project SDK-style tự glob .cs; test đã build + chạy).

§8 — Quan sát gửi lead (KHÔNG phải bug, không tự sửa)

  1. Chồng lấn có lợi: test T6 lane K1 (ContractCatalogTests.cs:445) nay là phép thử tự động cho claim "49 key ∈ All đều có menu-row" — nó XANH ở baseline 569 ⇒ claim by-construction của K4a đã có chứng nhân độc lập, lane K4a không cần lặp lại.
  2. Nhãn G2..G8 KHÔNG tự lành (đo ở T5, đối chứng): nếu owner đổi chữ 7 nhãn nhóm sau khi prod đã seed thì sửa trong khkkGroupLabels là VÔ TÁC DỤNG — phải thêm entry labelBackfill. Comment BE :2233-2234 đã khai đúng điều này; T5 nay khoá nó thành bất-biến có răng.
  3. Số canonical cần đổi kèm K4a: MenuKeys.All ĐO runtime = 113 ⇒ docs/STATUS.md row Menu keys = 113 · Policies = 452. (T2 sẽ ĐỎ nếu 2 số này drift khỏi mã.)

§D — RESUME gate K4a, finding D (MAJOR): bất-biến "revoker KHÔNG đụng Khkk_*"

Lệnh lead: +1 Fact Revoke_DoesNotTouch_KhkkGroups. Lý do: ca ĐÃ VỠ THẬT trên prod (Run #423 — 447/494 row rơi, 11/13 role về 0; gotcha #84) mà hiện 0 test ⇒ thêm 1 chữ vào bộ lọc revoke thì suite vẫn xanh.

  • §D.1 Đọc PREDICATE THẬT từ mã (DbInitializer.cs:2554-2568), không theo prompt: RoleId != adminRoleId && (MenuKey.StartsWith("Hrm") || MenuKey.StartsWith("Off") || MenuKey == Personal) && (bất kỳ cờ nào đang true) → set cả 4 cờ = false. 🔴 Bẫy vacuous-pass phát hiện khi đọc mã: :2546-2551Admin role chưa seed ⇒ revoker RETURN SỚM, no-op. Test không seed Admin sẽ XANH vì revoker CHƯA TỪNG CHẠY, chứ không phải vì Khkk_* được tha. Bắt buộc seed Admin + có control dương.
  • §D.2 Chọn chỗ đặt: KhkkGroupMenuSeedTests.cs (file của lane K4a) — đã có sẵn mảng Expected49 + 7 key nhóm 1 + harness seeder; đặt ở AdminOnlyModulesRevokeTests.cs sẽ phải chép lại danh sách 49 key (nguồn thứ 2 = nguy cơ lệch). Vẫn MIRROR khuôn 2-vế của file đó.
  • §D.3 ĐÃ viết T7 Revoke_DoesNotTouch_KhkkGroups (cuối KhkkGroupMenuSeedTests.cs). Kịch bản 1 lượt, 3 chiều phân biệt:
    1. seed menu THẬT + site-2 grant THẬT cho 3 role (Admin + Drafter + DeptManager);
    2. cấp tay 2 key trong tập ẩn (Off_DanhBa, Personal) cho cả 3 role = CONTROL DƯƠNG;
    3. tick thêm CanCreate cho Khkk_G5_Create (mô phỏng admin bật cờ ghi trên ma trận);
    4. chạy RevokeTemporarilyHiddenModulesAsync;
    5. Vế 1 (control dương): 2 key kia của 2 role non-Admin về false cả 4 cờ ⇒ chứng revoker CÓ CHẠY (nếu đỏ = phép đo hỏng, không phải Khkk hỏng); Admin giữ true ⇒ chứng vế RoleId != adminRoleId còn sống; Vế 2: 56 key Khkk × 2 role = 112 row sống sót, CanRead true, row KHÔNG bị xoá, cờ ghi đã tick vẫn true. Đếm bằng ĐƠN VỊ của sự cố thật (row sống sót) để thông điệp lỗi cùng ngôn ngữ Run #423.
  • §D.4 CHỨNG T7 CÓ RĂNG bằng PROBE VI-PHÂN (không được chạm prod ⇒ không fault-inject vào DbInitializer): file tạm ZzProbeTeethTemp.cs dựng lại ĐÚNG hình câu truy vấn revoke (:2554-2568) + thêm đúng 1 vế || MenuKey.StartsWith("Khkk") rồi đo trên DB thật. Kết quả ĐO: trước = 112 row Khkk CanRead=true · sau bộ-lọc-bị-nới = 0 ⇒ đúng tập row mà T7 assert sẽ rơi hết ở thế giới hỏng ⇒ T7 phân biệt được 2 thế giới, KHÔNG phải assert đúng ở mọi thế giới. Probe chạy 1/1 PASS rồi XOÁ; verify 2 chiều: git status -- tests/ chỉ còn 1 file ?? (KhkkGroupMenuSeedTests.cs) và suite = 569+7 (không phải +8) ⇒ 0 dư lượng.

§9 — SỐ CHỐT (đo tươi, lần cuối, sau khi đã xoá probe)

  • Filtered ~KhkkGroupMenuSeed: 7/7 PASS.
  • Infra: 576 PASS / 0 FAIL (baseline 569 + 7) · Domain: 45 PASS / 0 FAIL.
  • Tổng suite = 621.

END sub-test-k4a — VERDICT=7/7 PASS (+7 test, suite 621 = 45 Domain + 576 Infra, 0 hồi quy, 0 prod-code touched)