#!/usr/bin/env python3 """Multi-role scenario API test runner for HMS health management platform.""" import json import time import urllib.request import urllib.error import sys from datetime import datetime BASE = "http://localhost:3000/api/v1" RESULTS = [] def api(method, path, token=None, body=None): """Make API call and return (status_code, response_dict).""" url = f"{BASE}{path}" headers = {"Content-Type": "application/json"} if token: headers["Authorization"] = f"Bearer {token}" data = json.dumps(body).encode() if body else None req = urllib.request.Request(url, data=data, headers=headers, method=method) try: with urllib.request.urlopen(req, timeout=15) as resp: raw = resp.read().decode() return resp.status, json.loads(raw) if raw else {} except urllib.error.HTTPError as e: raw = e.read().decode() if e.fp else "" try: return e.code, json.loads(raw) except: return e.code, {"error": raw} except Exception as ex: return 0, {"error": str(ex)} def login(username, password="Admin@2026"): status, resp = api("POST", "/auth/login", body={"username": username, "password": password}) if resp.get("success"): return resp["data"]["access_token"] print(f" LOGIN FAILED for {username}: {resp}") return None def record(role, chain, test_id, description, passed, detail=""): RESULTS.append({ "role": role, "chain": chain, "test_id": test_id, "description": description, "passed": passed, "detail": detail }) status = "PASS" if passed else "FAIL" print(f" [{status}] {role}-{chain}.{test_id}: {description}" + (f" -- {detail}" if detail and not passed else "")) def check_api(role, chain, test_id, desc, method, path, token, expected_success=True, body=None): status, resp = api(method, path, token, body) # Handle non-dict responses (e.g. raw lists) if not isinstance(resp, dict): ok = status == 200 record(role, chain, test_id, desc, ok, f"status={status}" if not ok else "") return resp if ok else None ok = resp.get("success", False) == expected_success if isinstance(expected_success, bool) else True detail = "" if not ok: detail = f"status={status}, msg={resp.get('message', resp.get('error', ''))}" record(role, chain, test_id, desc, ok, detail) return resp if ok else None def check_api_status(role, chain, test_id, desc, method, path, token, expected_status=200, body=None): status, resp = api(method, path, token, body) ok = status == expected_status detail = "" if not ok: detail = f"expected={expected_status}, got={status}, msg={resp.get('message', resp.get('error', ''))}" record(role, chain, test_id, desc, ok, detail) return resp if ok else None def main(): print(f"\n{'='*60}") print(f" HMS Multi-Role Scenario API Testing") print(f" Started: {datetime.now().isoformat()}") print(f"{'='*60}\n") # Login all roles print("Logging in all 5 roles...") time.sleep(1) tokens = {} for name, user in [("admin", "admin"), ("doctor", "doctor_test"), ("nurse", "nurse_test"), ("operator", "operator_test"), ("hm", "health_manager_test")]: tokens[name] = login(user) time.sleep(1) if not tokens[name]: print(f"FATAL: Cannot login as {user}") sys.exit(1) print("All 5 roles logged in successfully.\n") AT = tokens["admin"] DT = tokens["doctor"] NT = tokens["nurse"] OT = tokens["operator"] HT = tokens["hm"] # ======================================== # R01 - ADMIN (9 chains) # ======================================== print("=" * 60) print("R01 - ADMIN BUSINESS CHAINS") print("=" * 60) # Chain A: Patient creation full chain print("\n--- Chain A: Patient Creation ---") resp = check_api("R01", "A", "1", "Create patient", "POST", "/health/patients", AT, body={"name": "MultiRoleTestPatient", "gender": "male", "phone": "13900001111", "birth_date": "1990-01-01"}) patient_id = resp["data"]["id"] if resp and resp.get("success") else "" time.sleep(0.5) check_api("R01", "A", "2", "Patient detail", "GET", f"/health/patients/{patient_id}", AT) time.sleep(0.3) resp = check_api("R01", "A", "3", "Create tag", "POST", "/health/patient-tags", AT, body={"name": "HighBP-Risk-Test", "color": "#FF0000"}) tag_id = resp["data"]["id"] if resp and resp.get("success") else "" time.sleep(0.3) check_api("R01", "A", "3b", "Assign tag to patient", "POST", f"/health/patients/{patient_id}/tags", AT, body={"tag_ids": [tag_id]} if tag_id else None) time.sleep(0.3) check_api("R01", "A", "4", "Devices list", "GET", "/health/devices?page=1&page_size=10", AT) time.sleep(0.3) check_api("R01", "A", "5", "Consents list (via patient)", "GET", f"/health/patients/{patient_id}/consents?page=1&page_size=10", AT) time.sleep(0.3) check_api("R01", "A", "6", "Search patient", "GET", "/health/patients?search=MultiRoleTest", AT) time.sleep(0.5) # Chain B: Follow-up closed loop print("\n--- Chain B: Follow-up Closed Loop ---") resp = check_api("R01", "B", "1", "Create follow-up task", "POST", "/health/follow-up-tasks", AT, body={"patient_id": patient_id, "follow_up_type": "phone", "planned_date": "2026-05-20", "notes": "Admin test FU"} if patient_id else None) fu_id = resp["data"]["id"] if resp and resp.get("success") else "" time.sleep(0.3) check_api("R01", "B", "2", "Follow-up list (pending)", "GET", "/health/follow-up-tasks?status=pending&page=1&page_size=10", AT) time.sleep(0.3) check_api("R01", "B", "3", "Follow-up templates", "GET", "/health/follow-up-templates?page=1&page_size=10", AT) time.sleep(0.3) check_api("R01", "B", "4", "Action inbox", "GET", "/health/action-inbox?page=1&page_size=10", AT) time.sleep(0.5) # Chain C: Consultation flow print("\n--- Chain C: Consultation Flow ---") check_api("R01", "C", "1", "Consultation sessions list", "GET", "/health/consultation-sessions?page=1&page_size=10", AT) time.sleep(0.3) check_api("R01", "C", "2", "Doctor dashboard", "GET", "/health/doctor/dashboard", AT) time.sleep(0.5) # Chain D: Alert handling chain print("\n--- Chain D: Alert Handling ---") check_api("R01", "D", "1", "Critical value thresholds", "GET", "/health/critical-value-thresholds?page=1&page_size=10", AT) time.sleep(0.3) check_api("R01", "D", "2", "Alerts list", "GET", "/health/alerts?page=1&page_size=10", AT) time.sleep(0.3) check_api("R01", "D", "3", "Alert rules", "GET", "/health/alert-rules?page=1&page_size=10", AT) time.sleep(0.3) check_api("R01", "D", "4", "Critical alerts", "GET", "/health/critical-alerts?page=1&page_size=10", AT) time.sleep(0.3) check_api("R01", "D", "5", "BLE gateways", "GET", "/health/ble-gateways?page=1&page_size=10", AT) time.sleep(0.5) # Chain E: AI analysis chain print("\n--- Chain E: AI Analysis ---") check_api("R01", "E", "1", "AI prompts", "GET", "/ai/prompts?page=1&page_size=10", AT) time.sleep(0.3) check_api("R01", "E", "2", "AI suggestions", "GET", "/ai/suggestions?page=1&page_size=10", AT) time.sleep(0.3) check_api("R01", "E", "3", "AI chat sessions", "GET", "/ai/chat/sessions?page=1&page_size=10", AT) time.sleep(0.5) # Chain F: Content publishing chain print("\n--- Chain F: Content Publishing ---") resp = check_api("R01", "F", "1", "Create article", "POST", "/health/articles", AT, body={"title": "MultiRole Test Article", "content": "Test content", "status": "draft"}) art_id = resp["data"]["id"] if resp and resp.get("success") else "" time.sleep(0.3) check_api("R01", "F", "2", "Edit article", "PUT", f"/health/articles/{art_id}", AT, body={"title": "MultiRole Test Article Updated", "content": "Updated content"} if art_id else None) time.sleep(0.3) check_api("R01", "F", "3a", "Submit article", "POST", f"/health/articles/{art_id}/submit", AT) time.sleep(0.3) check_api("R01", "F", "3b", "Approve article", "POST", f"/health/articles/{art_id}/approve", AT) time.sleep(0.3) check_api("R01", "F", "4", "Unpublish article", "POST", f"/health/articles/{art_id}/unpublish", AT) time.sleep(0.5) # Chain G: Points mall chain print("\n--- Chain G: Points Mall ---") check_api("R01", "G", "1", "Points rules", "GET", "/health/admin/points/rules?page=1&page_size=10", AT) time.sleep(0.3) check_api("R01", "G", "2", "Points products", "GET", "/health/admin/points/products?page=1&page_size=10", AT) time.sleep(0.3) check_api("R01", "G", "3", "Points orders", "GET", "/health/admin/points/orders?page=1&page_size=10", AT) time.sleep(0.5) # Chain H: Offline events chain print("\n--- Chain H: Offline Events ---") resp = check_api("R01", "H", "1", "Create offline event", "POST", "/health/admin/offline-events", AT, body={"title": "MultiRole Test Event", "description": "Test event", "event_date": "2026-06-01", "location": "Test Location", "max_participants": 50}) event_id = resp["data"]["id"] if resp and resp.get("success") else "" time.sleep(0.3) check_api("R01", "H", "2", "List offline events", "GET", "/health/offline-events?page=1&page_size=10", AT) time.sleep(0.5) # Chain I: System management chain print("\n--- Chain I: System Management ---") check_api("R01", "I", "1", "Users list", "GET", "/users?page=1&page_size=10", AT) time.sleep(0.3) check_api("R01", "I", "2", "Roles list", "GET", "/roles?page=1&page_size=10", AT) time.sleep(0.3) check_api("R01", "I", "3", "Organizations", "GET", "/organizations", AT) time.sleep(0.3) check_api("R01", "I", "4", "Statistics dashboard", "GET", "/health/admin/statistics/dashboard", AT) time.sleep(0.3) check_api("R01", "I", "5", "Workflow definitions", "GET", "/workflow/definitions?page=1&page_size=10", AT) time.sleep(0.3) check_api("R01", "I", "6", "Messages", "GET", "/messages?page=1&page_size=10", AT) time.sleep(0.3) check_api("R01", "I", "7", "Settings", "GET", "/config/settings/general", AT) time.sleep(0.3) check_api("R01", "I", "8", "Plugins", "GET", "/admin/plugins", AT) time.sleep(0.3) check_api("R01", "I", "9", "OAuth clients", "GET", "/health/oauth/clients?page=1&page_size=10", AT) time.sleep(1) # ======================================== # R02 - DOCTOR (5 chains + permissions) # ======================================== print("\n" + "=" * 60) print("R02 - DOCTOR BUSINESS CHAINS") print("=" * 60) # Chain A: Patient management and clinical workflow print("\n--- Chain A: Patient & Clinical ---") check_api("R02", "A", "1", "Patient list", "GET", "/health/patients?page=1&page_size=10", DT) time.sleep(0.3) check_api("R02", "A", "2", "Patient detail", "GET", f"/health/patients/{patient_id}", DT) time.sleep(0.3) resp = check_api("R02", "A", "3", "Create patient", "POST", "/health/patients", DT, body={"name": "DoctorTestPatient", "gender": "female", "phone": "13900002222", "birth_date": "1985-03-15"}) time.sleep(0.3) check_api("R02", "A", "4", "Doctor list", "GET", "/health/doctors?page=1&page_size=10", DT) time.sleep(0.3) check_api("R02", "A", "5", "Patient diagnoses", "GET", f"/health/patients/{patient_id}/diagnoses?page=1&page_size=10", DT) time.sleep(0.3) check_api("R02", "A", "6", "Consents list (via patient)", "GET", f"/health/patients/{patient_id}/consents?page=1&page_size=10", DT) time.sleep(0.5) # Chain B: Follow-up closed loop (doctor side) print("\n--- Chain B: Follow-up (Doctor) ---") resp = check_api("R02", "B", "1", "Create follow-up task", "POST", "/health/follow-up-tasks", DT, body={"patient_id": patient_id, "follow_up_type": "visit", "planned_date": "2026-05-21", "notes": "Doctor created FU"} if patient_id else None) dr_fu_id = resp["data"]["id"] if resp and resp.get("success") else "" time.sleep(0.3) check_api("R02", "B", "2", "Follow-up list", "GET", "/health/follow-up-tasks?page=1&page_size=10", DT) time.sleep(0.3) check_api("R02", "B", "3", "Follow-up templates", "GET", "/health/follow-up-templates?page=1&page_size=10", DT) time.sleep(0.3) check_api("R02", "B", "4", "Action inbox", "GET", "/health/action-inbox?page=1&page_size=10", DT) time.sleep(0.5) # Chain C: Consultation intake closed loop print("\n--- Chain C: Consultation Intake ---") check_api("R02", "C", "1", "Consultation sessions list", "GET", "/health/consultation-sessions?page=1&page_size=10", DT) time.sleep(0.3) check_api("R02", "C", "2", "Consultation messages", "GET", "/health/consultation-messages?page=1&page_size=10", DT) time.sleep(0.5) # Chain D: Alert handling print("\n--- Chain D: Alerts ---") check_api("R02", "D", "1", "Alerts list", "GET", "/health/alerts?page=1&page_size=10", DT) time.sleep(0.3) check_api("R02", "D", "2", "Alert rules", "GET", "/health/alert-rules?page=1&page_size=10", DT) time.sleep(0.3) check_api("R02", "D", "3", "Critical alerts", "GET", "/health/critical-alerts?page=1&page_size=10", DT) time.sleep(0.5) # Chain E: AI analysis print("\n--- Chain E: AI Analysis ---") check_api("R02", "E", "1", "AI prompts", "GET", "/ai/prompts?page=1&page_size=10", DT) time.sleep(0.3) check_api("R02", "E", "2", "AI suggestions", "GET", "/ai/suggestions?page=1&page_size=10", DT) time.sleep(0.3) check_api("R02", "E", "3", "Messages", "GET", "/messages?page=1&page_size=10", DT) time.sleep(0.5) # Doctor permission boundary checks print("\n--- Doctor Permission Boundaries ---") check_api_status("R02", "P", "1", "No user management", "GET", "/users?page=1&page_size=1", DT, expected_status=403) time.sleep(0.3) check_api_status("R02", "P", "2", "No role management", "GET", "/roles?page=1&page_size=1", DT, expected_status=403) time.sleep(0.3) check_api_status("R02", "P", "3", "No points rules", "GET", "/health/admin/points/rules?page=1&page_size=1", DT, expected_status=403) time.sleep(0.3) check_api_status("R02", "P", "4", "No article management", "GET", "/health/articles?page=1&page_size=1", DT, expected_status=403) time.sleep(0.3) check_api_status("R02", "P", "5", "No system settings", "GET", "/settings", DT, expected_status=403) time.sleep(0.3) check_api_status("R02", "P", "6", "No BLE gateways", "GET", "/health/ble-gateways?page=1&page_size=1", DT, expected_status=403) time.sleep(0.3) check_api_status("R02", "P", "7", "No tag management", "GET", "/health/patient-tags?page=1&page_size=1", DT, expected_status=403) time.sleep(1) # ======================================== # R03 - NURSE (6 chains + permissions) # ======================================== print("\n" + "=" * 60) print("R03 - NURSE BUSINESS CHAINS") print("=" * 60) # Chain A: Patient management print("\n--- Chain A: Patient Management ---") check_api("R03", "A", "1", "Patient list", "GET", "/health/patients?page=1&page_size=10", NT) time.sleep(0.3) check_api("R03", "A", "2", "Patient detail", "GET", f"/health/patients/{patient_id}", NT) time.sleep(0.3) resp = check_api("R03", "A", "3", "Create patient", "POST", "/health/patients", NT, body={"name": "NurseTestPatient", "gender": "male", "phone": "13900003333", "birth_date": "1995-07-20"}) time.sleep(0.3) check_api("R03", "A", "4", "Daily monitoring", "GET", "/health/daily-monitoring?page=1&page_size=10", NT) time.sleep(0.5) # Chain B: Follow-up execution print("\n--- Chain B: Follow-up Execution ---") check_api("R03", "B", "1", "Follow-up tasks list", "GET", "/health/follow-up-tasks?page=1&page_size=10", NT) time.sleep(0.3) resp = check_api("R03", "B", "2", "Create follow-up task", "POST", "/health/follow-up-tasks", NT, body={"patient_id": patient_id, "follow_up_type": "phone", "planned_date": "2026-05-22", "notes": "Nurse FU"} if patient_id else None) time.sleep(0.3) # Try to update a follow-up task status if fu_id: check_api("R03", "B", "3", "Update follow-up task", "PUT", f"/health/follow-up-tasks/{fu_id}", NT, body={"status": "in_progress"}) else: record("R03", "B", "3", "Update follow-up task", False, "No follow-up ID available") time.sleep(0.3) check_api("R03", "B", "4", "Follow-up records", "GET", "/health/follow-up-records?page=1&page_size=10", NT) time.sleep(0.5) # Chain C: Consultation viewing (read-only) print("\n--- Chain C: Consultation Viewing ---") check_api("R03", "C", "1", "Consultation sessions (read)", "GET", "/health/consultation-sessions?page=1&page_size=10", NT) time.sleep(0.3) check_api("R03", "C", "2", "Consultation messages (read)", "GET", "/health/consultation-sessions?page=1&page_size=10", NT) time.sleep(0.5) # Chain D: Alert handling print("\n--- Chain D: Alerts ---") check_api("R03", "D", "1", "Alerts list", "GET", "/health/alerts?page=1&page_size=10", NT) time.sleep(0.3) check_api("R03", "D", "2", "Alert detail (if any)", "GET", "/health/alerts?page=1&page_size=1", NT) time.sleep(0.5) # Chain E: Diagnosis and informed consent print("\n--- Chain E: Diagnosis & Consent ---") check_api("R03", "E", "1", "Consents list (via patient)", "GET", f"/health/patients/{patient_id}/consents?page=1&page_size=10", NT) time.sleep(0.3) check_api("R03", "E", "2", "Create consent", "POST", "/health/consents", NT, body={"patient_id": patient_id, "consent_type": "general", "consent_scope": "data_collection", "status": "signed"} if patient_id else None) time.sleep(0.5) # Chain F: Action inbox print("\n--- Chain F: Action Inbox ---") check_api("R03", "F", "1", "Action inbox list", "GET", "/health/action-inbox?page=1&page_size=10", NT) time.sleep(0.3) check_api("R03", "F", "2", "Action inbox stats", "GET", "/health/action-inbox/stats", NT) time.sleep(0.3) check_api("R03", "8.1", "", "Messages", "GET", "/messages?page=1&page_size=10", NT) time.sleep(0.5) # Nurse permission boundary checks print("\n--- Nurse Permission Boundaries ---") check_api_status("R03", "P", "1", "No doctor management", "GET", "/health/doctors?page=1&page_size=1", NT, expected_status=403) time.sleep(0.3) check_api_status("R03", "P", "2", "No tag management", "GET", "/health/patient-tags?page=1&page_size=1", NT, expected_status=403) time.sleep(0.3) check_api_status("R03", "P", "3", "No points rules", "GET", "/health/admin/points/rules?page=1&page_size=1", NT, expected_status=403) time.sleep(0.3) check_api_status("R03", "P", "4", "No article management", "GET", "/health/articles?page=1&page_size=1", NT, expected_status=403) time.sleep(0.3) check_api_status("R03", "P", "5", "No AI analysis", "GET", "/ai/prompts?page=1&page_size=1", NT, expected_status=403) time.sleep(0.3) check_api_status("R03", "P", "6", "No follow-up templates", "GET", "/health/follow-up-templates?page=1&page_size=1", NT, expected_status=403) time.sleep(0.3) check_api_status("R03", "P", "7", "No user management", "GET", "/users?page=1&page_size=1", NT, expected_status=403) time.sleep(1) # ======================================== # R04 - HEALTH MANAGER (6 chains + permissions) # ======================================== print("\n" + "=" * 60) print("R04 - HEALTH MANAGER BUSINESS CHAINS") print("=" * 60) # Chain A: Patient and tag management print("\n--- Chain A: Patient & Tag ---") resp = check_api("R04", "A", "1", "Create tag", "POST", "/health/patient-tags", HT, body={"name": "Chronic-Disease-Mgmt", "color": "#00FF00"}) hm_tag_id = resp["data"]["id"] if resp and resp.get("success") else "" time.sleep(0.3) check_api("R04", "A", "2", "Patient list", "GET", "/health/patients?page=1&page_size=10", HT) time.sleep(0.3) resp = check_api("R04", "A", "3", "Create patient", "POST", "/health/patients", HT, body={"name": "HMTestPatient", "gender": "female", "phone": "13900004444", "birth_date": "1988-11-10"}) time.sleep(0.3) check_api("R04", "A", "4", "Doctor list (read)", "GET", "/health/doctors?page=1&page_size=10", HT) time.sleep(0.5) # Chain B: Follow-up management print("\n--- Chain B: Follow-up Management ---") resp = check_api("R04", "B", "1", "Create follow-up task", "POST", "/health/follow-up-tasks", HT, body={"patient_id": patient_id, "follow_up_type": "visit", "planned_date": "2026-05-23", "notes": "HM FU task"} if patient_id else None) time.sleep(0.3) check_api("R04", "B", "2", "Follow-up list", "GET", "/health/follow-up-tasks?page=1&page_size=10", HT) time.sleep(0.3) check_api("R04", "B", "3", "Follow-up templates", "GET", "/health/follow-up-templates?page=1&page_size=10", HT) time.sleep(0.3) check_api("R04", "B", "4", "Action inbox", "GET", "/health/action-inbox?page=1&page_size=10", HT) time.sleep(0.3) check_api("R04", "B", "5", "Team action inbox", "GET", "/health/action-inbox/team?page=1&page_size=10", HT) time.sleep(0.5) # Chain C: Consultation management print("\n--- Chain C: Consultation Management ---") check_api("R04", "C", "1", "Consultation sessions list", "GET", "/health/consultation-sessions?page=1&page_size=10", HT) time.sleep(0.5) # Chain D: Alerts and monitoring print("\n--- Chain D: Alerts & Monitoring ---") check_api("R04", "D", "1", "Alerts list", "GET", "/health/alerts?page=1&page_size=10", HT) time.sleep(0.3) check_api("R04", "D", "2", "Alert rules", "GET", "/health/alert-rules?page=1&page_size=10", HT) time.sleep(0.3) check_api("R04", "D", "3", "Critical value thresholds", "GET", "/health/critical-value-thresholds?page=1&page_size=10", HT) time.sleep(0.3) check_api("R04", "D", "4", "Devices (read)", "GET", "/health/devices?page=1&page_size=10", HT) time.sleep(0.3) check_api("R04", "D", "5", "Daily monitoring", "GET", "/health/daily-monitoring?page=1&page_size=10", HT) time.sleep(0.5) # Chain E: AI analysis print("\n--- Chain E: AI Analysis ---") check_api("R04", "E", "1", "AI prompts (read)", "GET", "/ai/prompts?page=1&page_size=10", HT) time.sleep(0.3) check_api("R04", "E", "2", "AI suggestions", "GET", "/ai/suggestions?page=1&page_size=10", HT) time.sleep(0.5) # Chain F: Diagnosis and consent print("\n--- Chain F: Diagnosis & Consent ---") check_api("R04", "F", "1", "Consents list (via patient)", "GET", f"/health/patients/{patient_id}/consents?page=1&page_size=10", HT) time.sleep(0.3) check_api("R04", "F", "2", "Patient diagnoses", "GET", f"/health/patients/{patient_id}/diagnoses?page=1&page_size=10", HT) time.sleep(0.3) check_api("R04", "8.1", "", "Messages", "GET", "/messages?page=1&page_size=10", HT) time.sleep(0.3) check_api("R04", "9.1", "", "Workflow definitions", "GET", "/workflow/definitions?page=1&page_size=10", HT) time.sleep(0.5) # Health Manager permission boundary checks print("\n--- Health Manager Permission Boundaries ---") check_api_status("R04", "P", "1", "No user management", "GET", "/users?page=1&page_size=1", HT, expected_status=403) time.sleep(0.3) check_api_status("R04", "P", "2", "No points management", "GET", "/health/admin/points/rules?page=1&page_size=1", HT, expected_status=403) time.sleep(0.3) check_api_status("R04", "P", "3", "No article management", "GET", "/health/articles?page=1&page_size=1", HT, expected_status=403) time.sleep(0.3) check_api_status("R04", "P", "4", "No system settings", "GET", "/settings", HT, expected_status=403) time.sleep(0.3) check_api_status("R04", "P", "5", "No plugin management", "GET", "/admin/plugins", HT, expected_status=403) time.sleep(0.3) check_api_status("R04", "P", "6", "No BLE gateways", "GET", "/health/ble-gateways?page=1&page_size=1", HT, expected_status=403) time.sleep(1) # ======================================== # R05 - OPERATOR (6 chains + permissions) # ======================================== print("\n" + "=" * 60) print("R05 - OPERATOR BUSINESS CHAINS") print("=" * 60) # Chain A: Patient and tag management print("\n--- Chain A: Patient & Tag ---") resp = check_api("R05", "A", "1", "Create tag", "POST", "/health/patient-tags", OT, body={"name": "PostSurgery-Rehab", "color": "#0000FF"}) time.sleep(0.3) check_api("R05", "A", "2", "Patient list (read)", "GET", "/health/patients?page=1&page_size=10", OT) time.sleep(0.3) check_api("R05", "A", "3", "Search patient", "GET", "/health/patients?search=MultiRoleTest", OT) time.sleep(0.5) # Chain B: Content publishing print("\n--- Chain B: Content Publishing ---") resp = check_api("R05", "B", "1", "Create article", "POST", "/health/articles", OT, body={"title": "Operator Test Article", "content": "Operator content", "status": "draft"}) op_art_id = resp["data"]["id"] if resp and resp.get("success") else "" time.sleep(0.3) check_api("R05", "B", "2", "Edit article", "PUT", f"/health/articles/{op_art_id}", OT, body={"title": "Operator Test Article Updated", "content": "Updated"} if op_art_id else None) time.sleep(0.3) check_api("R05", "B", "3", "Submit article", "POST", f"/health/articles/{op_art_id}/submit", OT) time.sleep(0.3) check_api("R05", "B", "4", "Approve article", "POST", f"/health/articles/{op_art_id}/approve", OT) time.sleep(0.5) # Chain C: Points mall print("\n--- Chain C: Points Mall ---") check_api("R05", "C", "1", "Points rules", "GET", "/health/admin/points/rules?page=1&page_size=10", OT) time.sleep(0.3) resp = check_api("R05", "C", "2", "Create points product", "POST", "/health/admin/points/products", OT, body={"name": "Test Product", "points_required": 100, "stock": 50, "description": "Test product"}) time.sleep(0.3) check_api("R05", "C", "3", "Points orders", "GET", "/health/admin/points/orders?page=1&page_size=10", OT) time.sleep(0.5) # Chain D: Offline events print("\n--- Chain D: Offline Events ---") resp = check_api("R05", "D", "1", "Create event", "POST", "/health/admin/offline-events", OT, body={"title": "Operator Test Event", "description": "Test event by operator", "event_date": "2026-06-15", "location": "Hall B", "max_participants": 30}) time.sleep(0.3) check_api("R05", "D", "2", "List events", "GET", "/health/offline-events?page=1&page_size=10", OT) time.sleep(0.5) # Chain E: Device and alert viewing print("\n--- Chain E: Device & Alert Viewing ---") check_api("R05", "E", "1", "Devices (read)", "GET", "/health/devices?page=1&page_size=10", OT) time.sleep(0.3) check_api("R05", "E", "2", "Alerts (read)", "GET", "/health/alerts?page=1&page_size=10", OT) time.sleep(0.5) # Chain F: AI usage print("\n--- Chain F: AI Usage ---") check_api("R05", "F", "1", "AI suggestions (read)", "GET", "/ai/suggestions?page=1&page_size=10", OT) time.sleep(0.3) check_api("R05", "8.1", "", "Messages", "GET", "/messages?page=1&page_size=10", OT) time.sleep(0.5) # Operator permission boundary checks print("\n--- Operator Permission Boundaries ---") check_api_status("R05", "P", "1", "No user management", "GET", "/users?page=1&page_size=1", OT, expected_status=403) time.sleep(0.3) check_api_status("R05", "P", "2", "No doctor management", "GET", "/health/doctors?page=1&page_size=1", OT, expected_status=403) time.sleep(0.3) check_api_status("R05", "P", "3", "No follow-up management", "GET", "/health/follow-up-tasks?page=1&page_size=1", OT, expected_status=403) time.sleep(0.3) check_api_status("R05", "P", "4", "No consultation management", "GET", "/health/consultation-sessions?page=1&page_size=1", OT, expected_status=403) time.sleep(0.3) check_api_status("R05", "P", "5", "No diagnoses", "GET", f"/health/patients/{patient_id}/diagnoses?page=1&page_size=1", OT, expected_status=403) time.sleep(0.3) check_api_status("R05", "P", "6", "No action inbox", "GET", "/health/action-inbox?page=1&page_size=1", OT, expected_status=403) time.sleep(0.3) check_api_status("R05", "P", "7", "No consents", "GET", "/health/consents?page=1&page_size=1", OT, expected_status=403) time.sleep(0.3) check_api_status("R05", "P", "8", "No AI analysis", "GET", "/ai/prompts?page=1&page_size=1", OT, expected_status=403) time.sleep(0.3) check_api_status("R05", "P", "9", "No system settings", "GET", "/settings", OT, expected_status=403) time.sleep(1) # ======================================== # CROSS-ROLE COLLABORATION CHECKS # ======================================== print("\n" + "=" * 60) print("CROSS-ROLE COLLABORATION CHECKS") print("=" * 60) print("\n--- Cross-role: Admin patient visible to all ---") # Doctor sees admin-created patient _, dr_resp = api("GET", f"/health/patients/{patient_id}", DT) dr_sees = dr_resp.get("success", False) record("XROLE", "X", "1", "Doctor sees admin-created patient", dr_sees, "" if dr_sees else f"msg={dr_resp.get('message', '')}") time.sleep(0.3) # Nurse sees admin-created patient _, nr_resp = api("GET", f"/health/patients/{patient_id}", NT) nr_sees = nr_resp.get("success", False) record("XROLE", "X", "2", "Nurse sees admin-created patient", nr_sees, "" if nr_sees else f"msg={nr_resp.get('message', '')}") time.sleep(0.3) # HM sees admin-created patient _, hm_resp = api("GET", f"/health/patients/{patient_id}", HT) hm_sees = hm_resp.get("success", False) record("XROLE", "X", "3", "HM sees admin-created patient", hm_sees, "" if hm_sees else f"msg={hm_resp.get('message', '')}") time.sleep(0.3) # Operator sees admin-created patient _, op_resp = api("GET", f"/health/patients/{patient_id}", OT) op_sees = op_resp.get("success", False) record("XROLE", "X", "4", "Operator sees admin-created patient", op_sees, "" if op_sees else f"msg={op_resp.get('message', '')}") time.sleep(0.5) print("\n--- Cross-role: Doctor follow-up visible to nurse ---") _, nurse_fu = api("GET", "/health/follow-up-tasks?status=pending&page=1&page_size=20", NT) nurse_fu_ok = nurse_fu.get("success", False) total_fu = nurse_fu.get("data", {}).get("total", 0) if nurse_fu_ok else 0 record("XROLE", "X", "5", "Nurse sees follow-up tasks (incl. doctor-created)", nurse_fu_ok and total_fu > 0, f"total={total_fu}" if nurse_fu_ok else f"msg={nurse_fu.get('message', '')}") time.sleep(0.5) print("\n--- Cross-role: Admin tag visible to other roles ---") _, dr_tags = api("GET", "/health/patient-tags?page=1&page_size=20", DT) dr_tag_ok = dr_tags.get("success", False) record("XROLE", "X", "6", "Doctor can list tags", dr_tag_ok, "" if dr_tag_ok else f"status=403 - doctor has no tag.list permission (expected per R02.P.7)") time.sleep(0.3) _, hm_tags = api("GET", "/health/patient-tags?page=1&page_size=20", HT) hm_tag_ok = hm_tags.get("success", False) record("XROLE", "X", "7", "HM can list tags", hm_tag_ok, "" if hm_tag_ok else f"msg={hm_tags.get('message', '')}") time.sleep(0.3) _, op_tags = api("GET", "/health/patient-tags?page=1&page_size=20", OT) op_tag_ok = op_tags.get("success", False) record("XROLE", "X", "8", "Operator can list tags", op_tag_ok, "" if op_tag_ok else f"msg={op_tags.get('message', '')}") time.sleep(0.5) print("\n--- Cross-role: Alert visibility ---") _, dr_alerts = api("GET", "/health/alerts?page=1&page_size=5", DT) _, nt_alerts = api("GET", "/health/alerts?page=1&page_size=5", NT) _, hm_alerts = api("GET", "/health/alerts?page=1&page_size=5", HT) record("XROLE", "X", "9", "Doctor can view alerts", dr_alerts.get("success", False)) record("XROLE", "X", "10", "Nurse can view alerts", nt_alerts.get("success", False)) record("XROLE", "X", "11", "HM can view alerts", hm_alerts.get("success", False)) time.sleep(0.5) print("\n--- Cross-role: Article visibility ---") _, op_articles = api("GET", "/health/articles?page=1&page_size=5", OT) op_art_ok = op_articles.get("success", False) record("XROLE", "X", "12", "Operator can manage articles", op_art_ok) time.sleep(0.3) # Admin should also be able to see articles _, admin_articles = api("GET", "/health/articles?page=1&page_size=5", AT) record("XROLE", "X", "13", "Admin can see operator articles", admin_articles.get("success", False)) time.sleep(0.5) print("\n--- Cross-role: Offline events visibility ---") _, op_events = api("GET", "/health/offline-events?page=1&page_size=5", OT) _, admin_events = api("GET", "/health/offline-events?page=1&page_size=5", AT) record("XROLE", "X", "14", "Operator sees offline events", op_events.get("success", False)) record("XROLE", "X", "15", "Admin sees offline events", admin_events.get("success", False)) # ======================================== # Summary # ======================================== print("\n" + "=" * 60) print("TEST SUMMARY") print("=" * 60) total = len(RESULTS) passed = sum(1 for r in RESULTS if r["passed"]) failed = total - passed # By role for role in ["R01", "R02", "R03", "R04", "R05", "XROLE"]: role_results = [r for r in RESULTS if r["role"] == role] role_pass = sum(1 for r in role_results if r["passed"]) role_total = len(role_results) print(f" {role}: {role_pass}/{role_total} passed ({100*role_pass//role_total if role_total else 0}%)") print(f"\n TOTAL: {passed}/{total} passed ({100*passed//total if total else 0}%)") if failed > 0: print(f"\n FAILED TESTS ({failed}):") for r in RESULTS: if not r["passed"]: print(f" [{r['role']}-{r['chain']}.{r['test_id']}] {r['description']}: {r['detail']}") # Save results as JSON for report generation with open(r"G:\hms\docs\qa\role-test-results\test_results.json", "w", encoding="utf-8") as f: json.dump({"timestamp": datetime.now().isoformat(), "total": total, "passed": passed, "failed": failed, "results": RESULTS}, f, ensure_ascii=False, indent=2) print(f"\n Results saved to docs/qa/role-test-results/test_results.json") if __name__ == "__main__": main()