Files
solution-erp/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-test-k7.md
2026-08-01 12:40:53 +07:00

9.2 KiB

TEST-K7: IN-PROGRESS

sub-test-k7 — Lane TEST K7 (bridge KHKK→HĐ, chuỗi SP-002 B4, S167 · 2026-08-01)

Rào: chỉ tests/** · KHÔNG production code · KHÔNG commit. Ghi-TỪNG-MỤC chống-#53: xong mục nào ghi ngay mục đó; mục CHƯA CHẠY chỉ được tiêu-đề + "CHƯA ĐO".

§0 — Tiền đề ĐO TƯƠI (không tin số đề bài)

  • git status @spawn: src/Backend/** dirty 3 file + 1 file MỚI (CreateContractFromSigningPlanFeatures.cs) = lane BE K7 đã LAND, uncommitted — khớp sub-implbe-k7.md. tests/** = 0 file dirty (baseline sạch).
  • Baseline suite ĐO TƯƠI (trước khi tôi gõ 1 chữ): SolutionErp.Infrastructure.Tests = 577 total / 576 passed / 1 FAILED. FAIL đúng test đã bàn giao: AuthorizePolicyRegressionTests.ContractSigningPlansController_EveryWriteEndpoint_HasAuthorizePolicy @:398Expected parts[0] ... they differ: "Contracts" vs "KeHoachKyKet"RED tái hiện đúng nguyên nhân implbe-k7 khai, không phải flaky.
  • Đếm action controller từ ĐĨA: grep -c "^ \[Http" = 17 (implbe-k7 ước "16"; số đo là 17). Neo chống-vacuous >= 15 không vỡ, giữ nguyên nghĩa.

🔎 Chồng-lấn TÌM TRƯỚC KHI VIẾT (bài S166) — T8 ĐÃ CÓ NGƯỜI ĐO MỘT NỬA

Application/ContractV2PipelineTests.cs (W6-S161) đã phủ đúng mệnh đề T8 của bảng invest (sub-invest-fable-b2-cum3.md:82): approver-V2-không-role-legacy thấy List/Detail + Inbox, cấp-2 KHÔNG thấy khi đang cấp-1 — Inbox_PendingLevelApprover_SeesChoDuyetContract · Inbox_OtherLevel_DoesNotSeeContract · View_V2Approver_NotDrafter_CanSeeChoDuyetContract. ⇒ KHÔNG nhân bản. Cái các test đó KHÔNG đo: chúng seed thẳng Contract{Phase=ChoDuyet, ApprovalWorkflowId, stepIndex} — tức giả định sẵn thứ mà cầu K7 phải TỰ sinh. Vế còn hở = mối nối cầu→đường-ống. T8 của tôi đo đúng chỗ hở đó (bridge → trình THẬT qua ContractWorkflowService → inbox), 0 chồng lấn.

§1 — [BƯỚC 1 XONG] Fix bàn-giao BE: ngoại lệ CreateContract

File: tests/SolutionErp.Infrastructure.Tests/Api/AuthorizePolicyRegressionTests.cs

  • Thêm bản đồ kỳ-vọng-CHÍNH-XÁC KhkkCrossModuleGates = { CreateContract → "Contracts.Create" } (KHÔNG phải danh sách "bỏ qua"): action trong bản đồ vẫn bị đo, chỉ đổi key kỳ vọng ⇒ gate sai thành KeHoachKyKet.Create vẫn ĐỎ. Comment nêu LUẬT (tài nguyên SINH RA quyết định khoá ghi) + trỏ gotcha #85 + MenuPermissionHandler.cs:40 (so khớp CHÍNH XÁC 1 key, không kế-thừa). 0 số-đếm trong comment.
  • Vế (b) tách 2 nhánh: có trong bản đồ → authz.Policy.Should().Be("Contracts.Create"); không → giữ nguyên luật cũ parts[0] == MenuKeys.KeHoachKyKet cho 16 action còn lại.
  • Thêm (e) chốt-bằng-TÊN: byName[CreateContract] == "Contracts.Create" + KhkkCrossModuleGates.Keys.Should().BeSubsetOf(byName.Keys) — đổi tên/xoá action mà quên dọn bản đồ = cấu-hình-chết im lặng (vắng-mặt trông giống ổn) ⇒ bắt luôn.
  • Neo chống-vacuous :375 GIỮ NGUYÊN >= 15 (không hạ, không đổi nghĩa; thực đo 17).
  • Vế (a)/(c)/(d) không đụng: CreateContract là POST nên vẫn phải qua luật "verb ghi không được gate Read" + "phải Create/Update/Delete".

ĐO: --filter AuthorizePolicyRegressionTestsPassed! Failed: 0, Passed: 22 (trước: 1 FAIL). File này +0 test mới (sửa test sẵn có, +2 vế assert).

§2 — [BƯỚC 2 XONG] File MỚI 11 test bridge — --filter CreateContractFromSigningPlanTestsPassed! Failed: 0, Passed: 11

File: tests/SolutionErp.Infrastructure.Tests/Application/CreateContractFromSigningPlanTests.cs (mirror khuôn bridge PE cũ CreateContractFromEvaluationMultiWinnerTestsIdentityFixture + deps THẬT ContractCodeGenerator/ContractWorkflowService, KHÔNG stub).

# Test Đo cái gì (vế mạnh nhất)
T1 T1_MotDong_TaoDungMotHopDong_VaGhiNguocContractId trọn field-map + mã HĐ so RUỘT {Project.Code}/HĐTP/SOL&{Supplier.Code}/01 (bằng chứng cầu ĐÃ nạp 2 bảng — vá-6) + SLA≈+7d + Line.ContractId + pe.ContractId + 2 changelog ĐÚNG 2 BẢNG. 3 số 900/999/750 cố ý khác nhau ⇒ chỉ Σ ApprovedAmount đúng
T2 T2_HaiDongCungNcc_GopVaoMotHopDong_GiaTriLaTong gộp N (vá-5, chỉ tồn tại nhờ Mig 71 bộ-3 unique) — 1 HĐ, CẢ 2 dòng cùng ContractId
T3 T3_DongChuaCoGiaChot_ThiChan_KhongTaoHd_VaKhongDotMa message NÊU TÊN hạng mục + atomic (dòng LÀNH cũng không bị ghi) + ContractCodeSequences rỗng = rào đứng TRƯỚC codegen
T4 T4_PhieuChuaDuyetXong_ThiChan dòng CÓ đủ giá ⇒ chỉ đổi ĐÚNG 1 biến (phase)
T5 T5_DongDaCoHopDong_ThiChan double-bridge PER-DÒNG + HĐ cũ không bị ghi đè
T6 T6_HaiDongKhacNcc_ThiChan + ĐỐI CHỨNG DƯƠNG cùng lượt: tách ra bấm từng dòng thì QUA (2 HĐ)
T7 T7_QuyTrinhPinSai_Chan4Nhanh_VaQuyTrinhDungThiQua ma-trận cùng-ruột đổi ĐÚNG 1 biến: NotFound · sai-loại · [vá-3] !IsActive · [vá-3] !IsUserSelectable4 message PHÂN BIỆT + đối-chứng-dương quy-trình-đúng QUA
T8 T8_HdSinhTuCau_TrinhDuyet_ThiVaoDungHopThuCapDangCho mối nối cầu→đường-ống (chỗ ContractV2PipelineTests KHÔNG đo): trình qua máy THẬT → levelOrder==1 (quên pin V2 = null) → Cấp-1 thấy hộp thư, Cấp-2 chưa
T9 T9_DongCuaPhieuKhac_ThiChan trộn dòng phiếu hàng xóm + id rác → chặn, dòng hàng xóm không bị đụng
T10 T10_BacCauLanHai_TaoHdThuHai_NhungPeGiuHdDau CHIỀU của ??= (vá-4a): viết = thay ??= thì mọi assert khác vẫn xanh, chỉ vế này ĐỎ. + cardinality DoD vá-15 + seq mã 01→02
T11 T11_BamHaiLanCungTapDong_ThiLanHaiBiChan_KhongTaoHdThua double-click — khoá HÀNH VI ĐANG CÓ (409 "đã có Hợp đồng", KHÔNG chế "no-op") + LastSeq==1 (không cháy mã oan)

Ánh xạ bảng invest T1-T8 → 11 test: T1→T1 · T2→T2 · T3→T3 · T4→T4 · T5→T5 · T6→T6 · T7→T7(nhánh b, +3 nhánh vá-3/NotFound) · T8→T8. +3 test NGOÀI bảng (T9 rào tập LineIds · T10 chiều ??= · T11 double-click) — 2 cái sau là yêu cầu §4/§5 của đề bài.

🔴 LỆCH SPEC ĐÃ ĐO, test theo CODE + khai trong header file: bảng invest T1 (:75) ghi WorkflowDefinitionId null; vá-6 (spec-cum3:16) ĐẢO lại "pin như 2 tiền lệ" và spec :4 chốt "16 vá THẮNG". Mã theo vá-6 ⇒ T1 assert pin ĐÚNG bản active của loại HĐ đó (seed sẵn 1 bản NHIỄU loại khác ⇒ != null suông sẽ không đủ).

2 lần ĐỎ trên đường đi (đều là seed, KHÔNG phải prod): T9 gọi SeedAsync 2 lần ⇒ SQLite 19 UNIQUE ApprovalWorkflows(Code,Version) rồi PurchaseEvaluations.MaPhieu → vá bằng hậu-tố ngẫu nhiên cho MỌI mã (Projects.Code/Suppliers.Code/2 bảng workflow/MaPhieu); T1+T10 chuyển sang so mã HĐ với s.Project.Code/s.Suppliers[0].Code đọc từ seed. 0 dòng production bị sửa.

§3a — [ XONG] Authz 2 CHIỀU — --filter KhkkBridgeAuthzTestsPassed! Failed: 0, Passed: 5

File MỚI: tests/SolutionErp.Infrastructure.Tests/Api/KhkkBridgeAuthzTests.cs. Chọn khuôn RẺ NHẤT MÀ VẪN THẬT: chạy thẳng MenuPermissionHandler + UserManager/RoleManager thật trên IdentityFixtureKHÔNG WebApplicationFactory. Khác AuthorizePolicyRegressionTests (đọc ATTRIBUTE = "cửa có khoá tên gì"), file này đo QUYẾT ĐỊNH = "khoá đó mở cho ai".

🔴 Điều kiện làm phép đo có răng: 2 tên policy KHÔNG hardcode — đọc TỪ CHÍNH controller (class-level + action-level của CreateContract) rồi mới đem đi hỏi handler ⇒ ai sửa attribute thì test đo theo cái MỚI mà vẫn hỏi đúng câu "hai vế có phải HỘI không". Kèm vế actionPolicy != classPolicy ("hai vế TRÙNG NHAU thì hội-2-khoá chỉ còn 1 khoá — cửa tự nới mà nhìn vẫn như cũ").

# Test Vế
1 CauKhkkSangHd_VaiDuCaHaiVe_ThiQua ĐỐI CHỨNG DƯƠNG — thiếu nó thì 2 test 403 vẫn xanh trong thế giới "chặn tất cả"
2 CauKhkkSangHd_VaiChiCoQuyenKeHoach_ThiBiChan cấp RỘNG TAY cả 4 cờ trên KeHoachKyKet vẫn 403 — chốt trước vế-1-PHẢI-qua để không xanh vì lý do sai
3 CauKhkkSangHd_VaiChiCoQuyenTaoHopDong_ThiBiChan chiều ngược lại, cũng chốt trước vế-2-PHẢI-qua
4 CauKhkkSangHd_Admin_KhongCanDongQuyenNao_VanQua khoá nhánh bypass (0 dòng Permissions) — chống "siết cho nhất quán" rồi khoá luôn Admin
5 CauKhkkSangHd_UserBiVoHieuHoa_ThiChan_DuCoDuQuyen cùng bộ quyền, đổi ĐÚNG 1 biến IsActive ⇒ kết quả ngược (nghỉ việc mà token còn hạn)

§3b — Vá-7 B6 neo mới

CHƯA ĐO.

§4 — Suite FULL

CHƯA ĐO.

END test-k7 — VERDICT=IN-PROGRESS