179 lines
9.5 KiB
Bash
179 lines
9.5 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
SCRIPT="$ROOT_DIR/scripts/demo-check.sh"
|
|
|
|
assert_contains() {
|
|
local text="$1"
|
|
grep -Fq "$text" "$SCRIPT" || {
|
|
echo "FAIL: demo-check.sh missing: $text" >&2
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
assert_not_contains() {
|
|
local text="$1"
|
|
if grep -Fq "$text" "$SCRIPT"; then
|
|
echo "FAIL: demo-check.sh still contains unsafe match: $text" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
assert_contains 'AIHR_PILOT_START_DATE'
|
|
assert_contains 'AIHR_PILOT_END_DATE'
|
|
assert_contains 'AIHR_PILOT_TENANT_ID'
|
|
assert_contains 'AIHR_DEMO_CHECK_LIBRARY_ONLY'
|
|
assert_contains 'safe_org AS'
|
|
assert_contains 'HAVING SUM(s.session_count) >= 10'
|
|
assert_contains 'HAVING COUNT(DISTINCT ext_party_id) = 1'
|
|
assert_contains 'finished_time >='
|
|
assert_contains 'reviewed_time >='
|
|
assert_not_contains 'p.identity_key = o.ext_party_id OR p.identity_key = o.person_phone'
|
|
assert_not_contains 's.ext_party_id = o.ext_party_id OR s.ext_party_id = o.person_phone'
|
|
|
|
if ! command -v docker >/dev/null || ! docker ps --format '{{.Names}}' | grep -qx 'wygj-mysql'; then
|
|
echo "SKIP: behavioral pilot gate test requires wygj-mysql"
|
|
exit 0
|
|
fi
|
|
|
|
export AIHR_DEMO_CHECK_LIBRARY_ONLY=true
|
|
# shellcheck source=../demo-check.sh
|
|
source "$SCRIPT"
|
|
|
|
unset AIHR_PILOT_DB
|
|
[[ "$(pilot_sql_scalar 'SELECT 1')" == "1" ]] || {
|
|
echo "FAIL: default ry-vue pilot database is unavailable" >&2
|
|
exit 1
|
|
}
|
|
|
|
TEST_DB="wygj_demo_check_test_$$"
|
|
cleanup() {
|
|
docker exec wygj-mysql mysql -uroot -proot -e "DROP DATABASE IF EXISTS \`$TEST_DB\`" >/dev/null 2>&1 || true
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
docker exec -i wygj-mysql mysql -uroot -proot --default-character-set=utf8mb4 <<SQL >/dev/null
|
|
CREATE DATABASE \`$TEST_DB\` CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
|
|
USE \`$TEST_DB\`;
|
|
CREATE TABLE aihr_practice_scenario (tenant_id varchar(20), position varchar(80), enabled tinyint);
|
|
CREATE TABLE aihr_knowledge_attach (tenant_id varchar(20), type varchar(50), status tinyint);
|
|
CREATE TABLE aihr_case_record (
|
|
tenant_id varchar(20), status varchar(30), title varchar(120), transcript text, summary varchar(1000)
|
|
);
|
|
CREATE TABLE aihr_practice_session (tenant_id varchar(20), session_id varchar(100), ext_party_id varchar(100), mode varchar(30), finished_time datetime, satisfaction_score tinyint);
|
|
CREATE TABLE aihr_practice_assignment (tenant_id varchar(20), ext_party_id varchar(100), create_time datetime);
|
|
CREATE TABLE aihr_org_snapshot (
|
|
tenant_id varchar(20), employment_status varchar(20), ext_party_id varchar(100),
|
|
project_code varchar(50), person_phone varchar(30)
|
|
);
|
|
CREATE TABLE aihr_practice_calibration (
|
|
id bigint auto_increment primary key, tenant_id varchar(20), session_id varchar(100),
|
|
original_score int, corrected_score int, create_time datetime
|
|
);
|
|
CREATE TABLE aihr_sop_answer_review (
|
|
tenant_id varchar(20), category varchar(100), status varchar(30), usable tinyint,
|
|
reviewed_time datetime, create_time datetime
|
|
);
|
|
|
|
INSERT INTO aihr_practice_scenario
|
|
SELECT '000000','生活顾问',1 FROM (
|
|
SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
|
|
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
|
|
) n;
|
|
INSERT INTO aihr_knowledge_attach
|
|
SELECT '000000','sop',2 FROM (
|
|
SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5
|
|
) n;
|
|
INSERT INTO aihr_knowledge_attach VALUES
|
|
('000000','training',2),
|
|
('other','sop',2),
|
|
('000000','sop',1);
|
|
INSERT INTO aihr_case_record
|
|
SELECT '000000','已入库', CONCAT('case-', n), '已按流程回访,手机号1390****1111', '已完成' FROM (
|
|
SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5
|
|
UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10
|
|
UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15
|
|
UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18 UNION ALL SELECT 19 UNION ALL SELECT 20
|
|
) n;
|
|
INSERT INTO aihr_case_record VALUES
|
|
('000000','已转写','pending','',''),
|
|
('other','已入库','other','','');
|
|
INSERT INTO aihr_org_snapshot VALUES
|
|
('000000','active','A','P1','13900000001'),
|
|
('000000','active','B','P1','13900000001'),
|
|
('000000','active','C','P2','D'),
|
|
('000000','active','D','P2','13900000004'),
|
|
('000000','active','E','P3','13900000005'),
|
|
('000000','active','F','P3','13900000006');
|
|
INSERT INTO aihr_practice_assignment VALUES
|
|
('000000','D','2026-07-08 08:00:00'),
|
|
('000000','13900000001','2026-07-08 08:00:00'),
|
|
('000000','E','2026-07-08 08:00:00'),
|
|
('000000','13900000005','2026-07-08 08:00:00'),
|
|
('000000','F','2026-07-08 08:00:00');
|
|
INSERT INTO aihr_practice_session (tenant_id, session_id, ext_party_id, mode, finished_time)
|
|
SELECT '000000', CONCAT('e-', n), CASE WHEN n <= 5 THEN 'E' ELSE '13900000005' END, 'mobile', '2026-07-08 09:00:00'
|
|
FROM (
|
|
SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5
|
|
UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
|
|
) n;
|
|
INSERT INTO aihr_practice_session (tenant_id, session_id, ext_party_id, mode, finished_time)
|
|
SELECT '000000', CONCAT('f-', n), 'F','mobile','2026-07-08 10:00:00' FROM (
|
|
SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5
|
|
UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10
|
|
) n;
|
|
INSERT INTO aihr_practice_session (tenant_id, session_id, ext_party_id, mode, finished_time)
|
|
VALUES
|
|
('000000','e-out','E','mobile','2026-07-06 10:00:00'),
|
|
('000000','unsafe-inside','D','mobile','2026-07-08 10:00:00');
|
|
UPDATE aihr_practice_session SET satisfaction_score = 5 WHERE finished_time >= '2026-07-07 00:00:00';
|
|
INSERT INTO aihr_practice_calibration (tenant_id,session_id,original_score,corrected_score,create_time) VALUES
|
|
('000000','f-1',80,82,'2026-07-08 11:00:00'),
|
|
('000000','unsafe-inside',80,40,'2026-07-08 11:00:00'),
|
|
('000000','e-out',80,40,'2026-07-06 11:00:00');
|
|
INSERT INTO aihr_sop_answer_review VALUES
|
|
('000000','sop','已评审',1,'2026-07-08 12:00:00','2026-07-08 11:00:00'),
|
|
('000000','sop','已评审',0,'2026-07-06 12:00:00','2026-07-06 11:00:00'),
|
|
('000000','sop','待评审',NULL,NULL,'2026-07-08 13:00:00'),
|
|
('000000','training','已评审',0,'2026-07-08 12:30:00','2026-07-08 11:30:00'),
|
|
('000000','training','待评审',NULL,NULL,'2026-07-08 13:00:00'),
|
|
('000000','training','待评审',NULL,NULL,'2026-07-06 13:00:00');
|
|
SQL
|
|
|
|
export AIHR_PILOT_DB="$TEST_DB"
|
|
pilot_output="$(AIHR_PILOT_START_DATE=2026-07-07 AIHR_PILOT_END_DATE=2026-07-10 check_pilot_samples)"
|
|
[[ "$pilot_output" == *'ten_sessions=1/5'* ]] || { echo "FAIL: 9/10 boundary or raw window count: $pilot_output" >&2; exit 1; }
|
|
[[ "$pilot_output" == *'org_identity=2/6'* ]] || { echo "FAIL: unsafe org identities were not excluded: $pilot_output" >&2; exit 1; }
|
|
[[ "$pilot_output" == *'org_pilot=2/20'* ]] || { echo "FAIL: collision or dual-key identity was miscounted: $pilot_output" >&2; exit 1; }
|
|
[[ "$pilot_output" == *'projects=1/1-2'* ]] || { echo "FAIL: safe pilot project count: $pilot_output" >&2; exit 1; }
|
|
[[ "$pilot_output" == *'org_ten_sessions=1/2'* ]] || { echo "FAIL: formal 9/10 boundary: $pilot_output" >&2; exit 1; }
|
|
[[ "$pilot_output" == *'calibration=1/20 consistency=1/1'* ]] || { echo "FAIL: calibration window: $pilot_output" >&2; exit 1; }
|
|
[[ "$pilot_output" == *'sop_docs=5/5 stored_cases=20/20'* ]] || { echo "FAIL: BRD content quantity gates: $pilot_output" >&2; exit 1; }
|
|
[[ "$pilot_output" == *'case_unmasked=0'* ]] || { echo "FAIL: redacted case content gate: $pilot_output" >&2; exit 1; }
|
|
[[ "$pilot_output" == *'sop_usable=1/1 sop_pending=1'* ]] || { echo "FAIL: SOP window: $pilot_output" >&2; exit 1; }
|
|
[[ "$pilot_output" == *'satisfaction=5.0/5 responses=19'* ]] || { echo "FAIL: safe satisfaction identity scope: $pilot_output" >&2; exit 1; }
|
|
|
|
override_output="$(AIHR_PILOT_TENANT_ID=other AIHR_PILOT_START_DATE=2026-07-07 AIHR_PILOT_END_DATE=2026-07-10 check_pilot_samples)"
|
|
[[ "$override_output" == *'scenarios=0/12'* ]] || { echo "FAIL: pilot tenant override was ignored: $override_output" >&2; exit 1; }
|
|
|
|
pilot_sql_scalar "INSERT INTO aihr_practice_session (tenant_id, session_id, ext_party_id, mode, finished_time, satisfaction_score) VALUES ('000000','e-10','13900000005','mobile','2026-07-09 09:00:00',5)" >/dev/null
|
|
pilot_output="$(AIHR_PILOT_START_DATE=2026-07-07 AIHR_PILOT_END_DATE=2026-07-10 check_pilot_samples)"
|
|
[[ "$pilot_output" == *'org_ten_sessions=2/2'* ]] || { echo "FAIL: formal 10/10 boundary: $pilot_output" >&2; exit 1; }
|
|
[[ "$pilot_output" == *'satisfaction=5.0/5 responses=20'* ]] || { echo "FAIL: formal satisfaction scope after window update: $pilot_output" >&2; exit 1; }
|
|
|
|
pilot_sql_scalar "UPDATE aihr_case_record SET transcript = '手机号13900000000' WHERE tenant_id = '000000' AND status = '已入库' LIMIT 1" >/dev/null
|
|
pilot_output="$(AIHR_PILOT_START_DATE=2026-07-07 AIHR_PILOT_END_DATE=2026-07-10 check_pilot_samples)"
|
|
[[ "$pilot_output" == *'case_unmasked=1'* ]] || { echo "FAIL: unmasked case PII was not detected: $pilot_output" >&2; exit 1; }
|
|
|
|
if (AIHR_PILOT_STRICT=true AIHR_PILOT_START_DATE= AIHR_PILOT_END_DATE= check_pilot_samples) >/tmp/wygj-pilot-strict-test.log 2>&1; then
|
|
echo "FAIL: strict mode accepted an implicit date window" >&2
|
|
exit 1
|
|
fi
|
|
grep -Fq 'strict pilot check requires AIHR_PILOT_START_DATE and AIHR_PILOT_END_DATE' /tmp/wygj-pilot-strict-test.log || {
|
|
echo "FAIL: strict mode missing-date error is unclear" >&2
|
|
exit 1
|
|
}
|
|
|
|
echo "PASS: demo pilot gates use a bounded window and unique formal identities"
|