-- ============================================================
-- HDH-BOS Test Cases Database Schema v3 (Dynamic Modules)
-- Modules: quản lý động qua bảng tc_modules
-- Run in: Supabase SQL Editor
-- ============================================================
-- ── 1. ENUM types (status & priority) ──────────────────────
DO $$ BEGIN
CREATE TYPE tc_status AS ENUM ('todo','pass','fail','skip');
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
CREATE TYPE tc_priority AS ENUM ('Cao','Trung bình','Thấp');
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
-- NOTE: tc_module ENUM đã bỏ — module nay là TEXT tham chiếu tc_modules.key
-- ── 2. Bảng tc_modules (quản lý module động) ───────────────
CREATE TABLE IF NOT EXISTS tc_modules (
key TEXT PRIMARY KEY,
display_name TEXT NOT NULL,
sort_order INT NOT NULL DEFAULT 0
);
-- ── 3. Bảng test_cases ─────────────────────────────────────
CREATE TABLE IF NOT EXISTS test_cases (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
tc_id TEXT NOT NULL UNIQUE,
module TEXT NOT NULL REFERENCES tc_modules(key) ON UPDATE CASCADE,
category TEXT NOT NULL,
name TEXT NOT NULL,
description TEXT,
precondition TEXT,
steps TEXT,
expected TEXT,
role TEXT NOT NULL DEFAULT 'All',
priority TEXT NOT NULL DEFAULT 'Trung bình',
status TEXT NOT NULL DEFAULT 'todo',
error_note TEXT,
tester_name TEXT,
tested_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT chk_status CHECK (status IN ('todo','pass','fail','skip')),
CONSTRAINT chk_priority CHECK (priority IN ('Cao','Trung bình','Thấp'))
-- Không có chk_module — module được validate qua FOREIGN KEY → tc_modules
);
-- ── 4. Bảng test_case_images (ảnh đính kèm lỗi) ───────────
CREATE TABLE IF NOT EXISTS test_case_images (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
tc_id TEXT NOT NULL
REFERENCES test_cases(tc_id) ON DELETE CASCADE,
file_name TEXT NOT NULL,
file_data TEXT NOT NULL, -- base64 data URL (data:image/png;base64,…)
file_size INT, -- bytes
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- ── 5. Indexes ─────────────────────────────────────────────
CREATE INDEX IF NOT EXISTS idx_tc_module ON test_cases(module);
CREATE INDEX IF NOT EXISTS idx_tc_status ON test_cases(status);
CREATE INDEX IF NOT EXISTS idx_tc_priority ON test_cases(priority);
CREATE INDEX IF NOT EXISTS idx_tc_role ON test_cases(role);
CREATE INDEX IF NOT EXISTS idx_tc_updated ON test_cases(updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_img_tcid ON test_case_images(tc_id);
-- ── 6. Trigger: auto-update updated_at ────────────────────
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
BEGIN
NEW.updated_at = NOW();
IF NEW.status <> OLD.status THEN
NEW.tested_at = NOW();
END IF;
RETURN NEW;
END;
$$;
DROP TRIGGER IF EXISTS trg_tc_updated ON test_cases;
CREATE TRIGGER trg_tc_updated
BEFORE UPDATE ON test_cases
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
-- ── 7. RLS (bật khi cần bảo mật theo user) ────────────────
-- ALTER TABLE test_cases ENABLE ROW LEVEL SECURITY;
-- ALTER TABLE test_case_images ENABLE ROW LEVEL SECURITY;
-- CREATE POLICY "anon_all" ON test_cases FOR ALL USING (true) WITH CHECK (true);
-- CREATE POLICY "anon_all" ON test_case_images FOR ALL USING (true) WITH CHECK (true);
-- ── 8. View: thống kê theo module ─────────────────────────
CREATE OR REPLACE VIEW v_test_summary AS
SELECT
module,
COUNT(*) AS total,
COUNT(*) FILTER (WHERE status = 'pass') AS passed,
COUNT(*) FILTER (WHERE status = 'fail') AS failed,
COUNT(*) FILTER (WHERE status = 'skip') AS skipped,
COUNT(*) FILTER (WHERE status = 'todo') AS todo,
ROUND(
COUNT(*) FILTER (WHERE status = 'pass') * 100.0
/ NULLIF(COUNT(*), 0), 1
) AS pass_rate,
MAX(updated_at) AS last_updated
FROM test_cases
GROUP BY module
ORDER BY module;
-- ── 9. View: danh sách TC đang lỗi ────────────────────────
CREATE OR REPLACE VIEW v_failed_tests AS
SELECT tc_id, module, category, name, role, priority,
error_note, tester_name, tested_at
FROM test_cases
WHERE status = 'fail'
ORDER BY tested_at DESC NULLS LAST;
-- ── 10. Hàm tiện ích: reset trạng thái theo module ────────
CREATE OR REPLACE FUNCTION reset_module_status(p_module TEXT)
RETURNS INT LANGUAGE plpgsql AS $$
DECLARE updated_count INT;
BEGIN
UPDATE test_cases
SET status = 'todo', error_note = NULL, tested_at = NULL
WHERE module = p_module;
GET DIAGNOSTICS updated_count = ROW_COUNT;
RETURN updated_count;
END;
$$;
-- Dùng: SELECT reset_module_status('project');
-- ── 11. Seed tc_modules ──────────────────────────────────────
-- Điền dữ liệu module mặc định (có thể sửa trước khi chạy)
INSERT INTO tc_modules (key, display_name, sort_order) VALUES
('units', 'Danh sách đơn vị', 1),
('positions', 'Danh sách vị trí', 2),
('staffs', 'Danh sách nhân sự', 3),
('orgchart', 'Sơ đồ tổ chức', 4),
('details', 'Hồ sơ nhân sự', 5),
('task_mgmt', 'Quản lý công việc', 6),
('project', 'Danh mục dự án', 7)
ON CONFLICT (key) DO UPDATE
SET display_name = EXCLUDED.display_name,
sort_order = EXCLUDED.sort_order;
-- ── 12. Seed test_cases ──────────────────────────────────────
-- Dùng nút [📥 Import từ Excel] hoặc [📋 Copy SQL đầy đủ (kèm dữ liệu)]