bổ sung đủ 57 trường hợp test
This commit is contained in:
@@ -394,14 +394,154 @@ Dọn dẹp chung: Xóa UserRole và admin user khỏi DB.
|
||||
2. Truy cập "/admin/parameter" trực tiếp.
|
||||
Kết quả: URL path là "/admin" (bị redirect về trang đăng nhập admin).
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
FILE 9: tests/Browser/GameLogicTest.php (3 tests)
|
||||
--------------------------------------------------------------------------------
|
||||
Chuẩn bị chung: Tạo user mới (spined=0) và một prize có prize_number=9999 trong DB.
|
||||
Dọn dẹp chung: Xóa PrizeUser, gameUser và testPrize khỏi DB sau mỗi test.
|
||||
|
||||
[TC-50] test_prevent_double_click_spam_on_spin_button
|
||||
Mô tả : Click liên tiếp nhiều lần lên #spinButton chỉ tạo đúng 1 lần quay
|
||||
nhờ cờ bảo vệ isSpinning trong game.js.
|
||||
Cơ chế : let isSpinning = false; → click đầu tiên set isSpinning = true;
|
||||
các click sau bị block bởi guard if (!isSpinning).
|
||||
Bước : 1. Đăng nhập user chưa quay (spined=0), truy cập "/game".
|
||||
2. Chờ 800ms để wheel canvas render xong.
|
||||
3. Dùng JavaScript gọi spinButton.click() 4 lần liên tiếp.
|
||||
4. Chờ popup kết quả "XIN CHÚC MỪNG" xuất hiện (timeout 30s).
|
||||
5. Chờ thêm 1500ms để DB transaction commit hoàn tất.
|
||||
Kết quả: Browser: popup "XIN CHÚC MỪNG" hiển thị.
|
||||
DB Check: PrizeUser::where('user_id', ...) count = 1
|
||||
(chứng minh 3 click sau bị ngăn — chỉ 1 lần quay được ghi nhận).
|
||||
Lưu ý : Spin thực mất 10–15s; waitForText timeout = 30s.
|
||||
|
||||
[TC-51] test_complete_spin_reward_flow
|
||||
Mô tả : Luồng quay đầy đủ: click → animation → popup kết quả → DB cập nhật.
|
||||
Bước : 1. Đăng nhập user chưa quay, vào "/game".
|
||||
2. Chờ 800ms để wheel render.
|
||||
3. Xác nhận #spinButton có data-check="true".
|
||||
4. Click #spinButton (1 lần).
|
||||
5. Chờ text "XIN CHÚC MỪNG" trong div#result (AJAX response từ
|
||||
/updateprizeuser, render view luckywheel_prize.blade.php).
|
||||
6. Chờ thêm 1500ms để DB write hoàn tất.
|
||||
Kết quả: Browser: div#result hiển thị "XIN CHÚC MỪNG".
|
||||
DB Check: User::find(id)->spined = 1 (đã được cập nhật sau khi quay);
|
||||
PrizeUser::where('user_id', id)->first() != null;
|
||||
PrizeUser.user_id khớp với id của người dùng vừa quay.
|
||||
Lưu ý : Selector tương ứng ".reward-popup-class" trong yêu cầu ban đầu
|
||||
ánh xạ tới div#result + text "XIN CHÚC MỪNG" trong app này.
|
||||
|
||||
[TC-52] test_spin_when_out_of_stock
|
||||
Mô tả : Kiểm tra trạng thái trang game khi TẤT CẢ giải thưởng hết hàng
|
||||
(prize_number = 0 cho mọi prize).
|
||||
Chuẩn bị: Lưu prize_number gốc của tất cả prizes → update prize_number = 0.
|
||||
Bước : 1. Đăng nhập user, vào "/game".
|
||||
2. Chờ 500ms.
|
||||
Kết quả: Source HTML chứa 'game_data = "[]"' (IndexController không thêm
|
||||
segment nào vì count_user >= prize_number với mọi prize_number = 0).
|
||||
Trang vẫn tải bình thường: #wheelCanvas và #spinButton hiển thị.
|
||||
Dọn dẹp: Khôi phục tất cả prize_number về giá trị gốc trong finally block.
|
||||
TODO : Khi implement xử lý "hết giải" vào frontend (assertSee "Đã hết giải
|
||||
thưởng" / assertPresent ".no-prize-message"), bỏ comment assertions
|
||||
tương ứng trong test.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
FILE 10: tests/Browser/SecurityTest.php (2 tests)
|
||||
--------------------------------------------------------------------------------
|
||||
Chuẩn bị chung: Tạo regularUser (không có role) và adminUser (đầy đủ roles).
|
||||
Dọn dẹp chung: Xóa UserRole, adminUser, regularUser khỏi DB sau mỗi test.
|
||||
|
||||
[TC-53] test_regular_user_cannot_access_admin_area
|
||||
Mô tả : Người dùng đã đăng nhập nhưng không có role admin bị redirect
|
||||
khỏi mọi route trong middleware UserRole.
|
||||
Cơ chế : Middleware UserRole → constructMenuLeft() → menu rỗng nếu không
|
||||
có UserRole record → mọi admin route bị chặn → redirect('admin').
|
||||
Bước : 1. Đăng nhập bằng regularUser (loginAs).
|
||||
2. Truy cập "/admin/account".
|
||||
3. Truy cập "/admin/users".
|
||||
4. Truy cập "/admin/prizes".
|
||||
Kết quả: Cả 3 lần truy cập đều redirect về path "/admin"
|
||||
(trang đăng nhập admin, không phải trang quản lý).
|
||||
|
||||
[TC-54] test_xss_injection_on_registration_form
|
||||
Mô tả : Nhập XSS payload vào trường name khi đăng ký — ứng dụng phải
|
||||
escape đúng cách, không thực thi JavaScript.
|
||||
Payload: name = <script>alert('XSS')</script>
|
||||
Bước : 1. Đăng ký tài khoản mới với name = payload XSS.
|
||||
2. Đăng xuất.
|
||||
3. Đăng nhập admin → vào "/admin/users" → chờ #table.
|
||||
4. Dismiss mọi alert dialog (DataTables warnings) nếu có.
|
||||
Kết quả: Source HTML KHÔNG chứa "<script>alert('XSS')</script>" (unescaped);
|
||||
Source HTML KHÔNG chứa "<SCRIPT>ALERT('XSS')</SCRIPT>" (unescaped);
|
||||
Source HTML CÓ CHỨA "<SCRIPT>" (Blade {{ }} đã HTML-escape).
|
||||
Cơ chế : Blade {{ }} tự escape → Controller mb_strtoupper → lưu DB → render
|
||||
escaped. Payload thực tế trong DB: <SCRIPT>ALERT('XSS')</SCRIPT>.
|
||||
Output trong HTML: <SCRIPT>ALERT('XSS')</SCRIPT>.
|
||||
Lưu ý : Dùng helper dismissAnyAlert() trước khi assert để tránh Chrome
|
||||
bị lock bởi DataTables errMode alert dialog còn mở.
|
||||
Sửa đi kèm: public/backend/js/main.js → DataTable.ext.errMode = 'none'
|
||||
(tắt alert dialog của DataTables, dùng silent fail thay thế).
|
||||
Dọn dẹp: Xóa user XSS test khỏi DB trong finally block.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
FILE 11: tests/Browser/AdminEdgeCaseTest.php (3 tests)
|
||||
--------------------------------------------------------------------------------
|
||||
Chuẩn bị chung: Tạo adminUser với đầy đủ roles trong DB.
|
||||
Dọn dẹp chung: Xóa UserRole, adminUser khỏi DB; xóa prize test nếu còn sót.
|
||||
|
||||
[TC-55] test_prize_number_cannot_be_negative
|
||||
Mô tả : Nhập prize_number âm (-5) vào form thêm giải thưởng phải bị
|
||||
server từ chối bằng validation error.
|
||||
Sửa app: PrizeController::updatePrize() thêm 'prize_number' => 'nullable|numeric|min:0'
|
||||
+ message 'Số lượng giải thưởng không được âm'.
|
||||
Bước : 1. Đăng nhập admin, vào "/admin/prize/update?id=0".
|
||||
2. Dùng JavaScript xóa thuộc tính required và min khỏi input
|
||||
(bypass HTML5 client-side validation).
|
||||
3. Nhập prize_name hợp lệ, prize_number=-5, prize_order=10.
|
||||
4. Nhấn "Thêm mới".
|
||||
Kết quả: Phần tử ".text-danger" hoặc ".alert-danger" hiển thị (validation error).
|
||||
DB Check: Prize::where('prize_name', testName)->first() = null
|
||||
(Prize với số âm KHÔNG được tạo trong DB).
|
||||
|
||||
[TC-56] test_invalid_logo_upload_is_rejected
|
||||
Mô tả : Upload file PDF vào trường logo_image phải bị server từ chối.
|
||||
Sửa app: ParameterController::update() thêm $request->validate([
|
||||
'logo_image' => 'nullable|mimes:jpg,png,jpeg,webp,jfif|max:2048']).
|
||||
Fix bảo mật OWASP A05: Unrestricted File Upload.
|
||||
Bước : 1. Tạo file PDF giả tạm thời (sys_get_temp_dir()).
|
||||
2. Đăng nhập admin, vào "/admin/parameter".
|
||||
3. Attach file PDF vào input[name="logo_image"].
|
||||
4. Nhấn "Cập nhật".
|
||||
Kết quả: Phần tử ".text-danger" hoặc ".alert-danger" hiển thị.
|
||||
Trang KHÔNG hiển thị "Cập nhật thành công".
|
||||
Dọn dẹp: Xóa file PDF tạm trong finally block.
|
||||
|
||||
[TC-57] test_prize_winner_export_excel_response
|
||||
Mô tả : Nút export Excel (#excel_export) tồn tại và href khi click trỏ
|
||||
đúng route admin/prizeuser/export; route tồn tại trong hệ thống.
|
||||
Lưu ý : #excel_export là <a> KHÔNG có static href attribute. Href được tạo
|
||||
động trong JS onclick: document.createElement("a"); a.href = exportUrl.
|
||||
→ Không thể dùng attribute('#excel_export', 'href') để kiểm tra.
|
||||
Bước A : 1. Đăng nhập admin, vào "/admin/prizeuser".
|
||||
2. Xác nhận #excel_export tồn tại.
|
||||
3. Dùng JavaScript override document.createElement để capture
|
||||
href được gán cho thẻ <a> khi #excel_export.click().
|
||||
Kết quả A: capturedHref != null;
|
||||
capturedHref chứa chuỗi 'prizeuser/export'.
|
||||
Bước B : Gọi route('admin/prizeuser/export') bằng PHP helper.
|
||||
Kết quả B: URL trả về chứa '/admin/prizeuser/export'
|
||||
(xác nhận route đã được đăng ký trong hệ thống).
|
||||
Lưu ý : Không dùng actingAs()->get(exportRoute) vì excel.blade.php gọi exit()
|
||||
sau khi xuất file — điều này sẽ terminate PHPUnit process.
|
||||
|
||||
================================================================================
|
||||
TỔNG KẾT
|
||||
================================================================================
|
||||
|
||||
Số file test : 8 files
|
||||
Số test cases : 49 test cases
|
||||
Số assertions : 90 assertions
|
||||
Thời gian : ~103 giây
|
||||
Số file test : 11 files
|
||||
Số test cases : 57 test cases
|
||||
Số assertions : 114 assertions
|
||||
Thời gian : ~178 giây
|
||||
Kết quả : ALL PASSED ✓
|
||||
|
||||
Phân bổ theo chức năng:
|
||||
@@ -416,8 +556,11 @@ TỔNG KẾT
|
||||
│ Quản lý giải thưởng (admin) │ 9 │
|
||||
│ Quản lý người dùng (admin) │ 5 │
|
||||
│ Cấu hình hệ thống (admin) │ 5 │
|
||||
│ Logic game nâng cao │ 3 │
|
||||
│ Bảo mật & phân quyền │ 2 │
|
||||
│ Admin edge cases │ 3 │
|
||||
├──────────────────────────────────────────┼────────┤
|
||||
│ TỔNG CỘNG │ 49 │
|
||||
│ TỔNG CỘNG │ 57 │
|
||||
└──────────────────────────────────────────┴────────┘
|
||||
|
||||
Công nghệ kiểm thử:
|
||||
@@ -428,4 +571,14 @@ TỔNG KẾT
|
||||
- Server : php artisan serve --port=8001
|
||||
- DB : MySQL (fsg3kct926qj_quaysoDB)
|
||||
|
||||
Sửa lỗi ứng dụng phát hiện qua quá trình kiểm thử:
|
||||
┌──────────────────────────────────────────────────────────────────────┐
|
||||
│ 1. PrizeController — thiếu min:0 trong prize_number validation │
|
||||
│ → Cho phép tạo giải thưởng với số lượng âm (bug logic) │
|
||||
│ 2. ParameterController — không validate MIME type khi upload ảnh │
|
||||
│ → OWASP A05: Unrestricted File Upload (lỗ hổng bảo mật) │
|
||||
│ 3. DataTables errMode = "alert" trong main.js │
|
||||
│ → Alert dialog blocking Chrome khi render HTML content trong cell│
|
||||
└──────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
================================================================================
|
||||
Reference in New Issue
Block a user