1. Bối cảnh: flash-sale và vì sao checkout dễ vỡ
Một sàn thương mại điện tử lớn mở flash-sale lúc 12 giờ trưa: mười nghìn người cùng tranh một trăm suất iPhone giảm giá sâu trong ba giây đầu. Đây là điều kiện tồi tệ nhất cho checkout — đồng thời cực cao, tồn kho khan hiếm, coupon chồng lớp, và người dùng bấm nút liên tục. Nếu hệ thống oversell (bán quá số tồn) hay tính sai coupon, hậu quả là huỷ đơn hàng loạt, khách phẫn nộ và thiệt hại thương hiệu. Vì thế ta kiểm thử bằng bất biến, không bằng 'đặt hàng thành công'.
Đội QA không thể tự tay dựng đủ tổ hợp đua tranh giữa hàng nghìn phiên. Một AI test agent giúp phần này: nó khám phá cây thao tác checkout, tự sinh kịch bản đồng thời và ca biên, chạy ở cả tầng API (nhanh, ổn định làm oracle) lẫn tầng UI (kiểm trải nghiệm). Con người vẫn giữ cổng review cho ca rủi ro như hoàn tiền và coupon phức tạp. Bài này đi từ bất biến, tới cách agent sinh ca, tới các ca lỗi sâu, CI và góc phỏng vấn.
2. Kiến trúc agent & mô hình dữ liệu checkout
Agent gồm ba vai giống Playwright Agents: Planner khám phá luồng chọn hàng → giỏ → áp coupon → thanh toán và viết kế hoạch; Generator biến kế hoạch thành spec chạy trên API và UI; Healer đọc trace khi lỗi để sửa hoặc skip. Mô hình dữ liệu tối giản gồm ba bảng: sản phẩm với tồn kho, đơn hàng với trạng thái, và coupon với luật giảm giá. Tồn kho là tài nguyên khan hiếm được bảo vệ bằng ràng buộc không âm ở tầng cơ sở dữ liệu.
-- Tồn kho: KHÔNG BAO GIỜ được âm (bất biến chống oversell)
CREATE TABLE product (
sku TEXT PRIMARY KEY,
price_minor BIGINT NOT NULL,
stock INT NOT NULL CHECK (stock >= 0) -- ràng buộc ở tầng DB
);
CREATE TABLE order_row (
order_key TEXT PRIMARY KEY, -- idempotency key từ client
sku TEXT REFERENCES product(sku),
qty INT NOT NULL CHECK (qty > 0),
state TEXT NOT NULL, -- CART|RESERVED|PAID|CANCELLED
total_minor BIGINT NOT NULL CHECK (total_minor >= 0)
);
CREATE TABLE coupon (
code TEXT PRIMARY KEY,
kind TEXT NOT NULL, -- PERCENT | FIXED
value INT NOT NULL,
max_off_minor BIGINT, -- trần giảm giá
stackable BOOLEAN NOT NULL DEFAULT false
);Giữ tồn kho không âm bằng ràng buộc CHECK ở tầng cơ sở dữ liệu là tuyến phòng thủ cuối cùng, quan trọng hơn mọi kiểm tra ở tầng ứng dụng. Khi hàng nghìn phiên cùng trừ tồn, chỉ hàng đợi giao dịch của cơ sở dữ liệu mới đảm bảo tính đúng. Agent sẽ tấn công đúng vào ranh giới này: đặt số lượng phiên tranh chấp lớn hơn số tồn còn lại và khẳng định không bao giờ có oversell.
3. Bất biến làm oracle: tồn kho, coupon, trạng thái đơn
Ba nhóm bất biến neo toàn bộ bộ kiểm thử. Một, tồn kho: stock luôn ≥ 0, và tổng số lượng đã bán không bao giờ vượt tồn ban đầu — đây là bất biến chống oversell. Hai, toán coupon: số tiền giảm không vượt trần, tổng thanh toán không âm, và coupon không chồng nếu không được phép. Ba, trạng thái đơn: một đơn có đúng một trạng thái cuối (PAID hoặc CANCELLED), và thanh toán idempotent theo order_key nên retry không tạo đơn kép hay tính tiền hai lần.
// Oracle checkout — bất biến dùng chung cho mọi ca agent sinh
import { expect } from "@playwright/test";
export function assertNoOversell(sku: string, initialStock: number,
orders: OrderRow[]) {
const sold = orders.filter(o => o.sku === sku && o.state === "PAID")
.reduce((s, o) => s + o.qty, 0);
expect(sold, "tổng bán ≤ tồn ban đầu").toBeLessThanOrEqual(initialStock);
}
export function assertStockNonNegative(product: Product) {
expect(product.stock, "tồn kho không âm").toBeGreaterThanOrEqual(0);
}
export function assertCouponMath(order: OrderRow, base: bigint,
discount: bigint, cap: bigint) {
expect(discount).toBeLessThanOrEqual(cap); // không quá trần
expect(order.totalMinor).toBe(base - discount); // toán đúng
expect(order.totalMinor).toBeGreaterThanOrEqual(0n); // không âm
}
export function assertOrderFinalUnique(states: string[]) {
const finals = states.filter(s => ["PAID", "CANCELLED"].includes(s));
expect(finals.length, "đúng 1 trạng thái cuối").toBe(1);
}
💬 Bình luận (0)
Chưa có bình luận nào. Hãy là người đầu tiên!