#!/usr/bin/env python3 from __future__ import print_function import json import os import subprocess import sys import threading import unittest from http.server import BaseHTTPRequestHandler, HTTPServer from pathlib import Path from urllib.parse import parse_qs, urlparse VIN = "LB9A32A21R0LS1464" DENIED_VIN = "LA9935J21F0YWT578" class Handler(BaseHTTPRequestHandler): def log_message(self, _format, *_args): pass def envelope(self, data, status=200): body = json.dumps({"data": data, "traceId": "test", "timestamp": 1}).encode("utf-8") self.send_response(status) self.send_header("Content-Type", "application/json") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) def do_POST(self): if self.path == "/api/v2/auth/login": self.envelope({"accessToken": "customer-token", "expiresAt": "2026-07-17T00:00:00+08:00", "session": {}}) return if self.path == "/api/v2/auth/logout": self.envelope({"loggedOut": True}) return self.envelope({}, 404) def do_GET(self): parsed = urlparse(self.path) values = parse_qs(parsed.query) if parsed.path == "/api/v2/session": self.envelope({"name": "客户验收", "role": "customer", "authMode": "enforce", "menuKeys": ["monitor", "vehicles", "tracks", "statistics"], "vehicleCount": 1}) elif parsed.path == "/api/realtime/vehicles": self.envelope({"items": [{"vin": VIN, "plate": "粤AGR6863", "sourceCount": 2, "online": True, "locationAvailable": True}], "total": 1, "limit": 100, "offset": 0}) elif parsed.path == "/api/vehicle-service" and values.get("keyword") == [VIN]: self.envelope({"lookupResolved": True, "vin": VIN}) elif parsed.path == "/api/vehicle-service" and values.get("keyword") == [DENIED_VIN]: self.envelope({"lookupResolved": False, "vin": ""}) elif parsed.path == "/api/v2/tracks": self.envelope({"vin": VIN, "points": [{"longitude": 113.1, "latitude": 23.1}, {"longitude": 113.2, "latitude": 23.2}]}) elif parsed.path == "/api/v2/statistics/mileage": self.envelope({"vehicleCount": 1, "recordCount": 1}) elif parsed.path in ("/monitor", "/vehicles/" + VIN, "/tracks", "/statistics"): body = "羚牛车辆数据中台".encode("utf-8") self.send_response(200) self.send_header("Content-Type", "text/html; charset=utf-8") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) else: self.envelope({}, 404) class CustomerDemoGateTest(unittest.TestCase): @classmethod def setUpClass(cls): cls.server = HTTPServer(("127.0.0.1", 0), Handler) cls.thread = threading.Thread(target=cls.server.serve_forever) cls.thread.daemon = True cls.thread.start() @classmethod def tearDownClass(cls): cls.server.shutdown() cls.thread.join() def test_complete_customer_demo_gate(self): script = str(Path(__file__).with_name("verify-customer-demo.py")) environment = dict(os.environ) environment["DEMO_PASSWORD"] = "not-persisted" result = subprocess.run([ sys.executable, script, "--base-url", "http://127.0.0.1:{0}".format(self.server.server_port), "--username", "customer-demo", "--expected-vins", VIN, "--denied-vin", DENIED_VIN, "--multi-source-vin", VIN, "--track-vin", VIN, "--track-from", "2026-07-16T17:00:00+08:00", "--track-to", "2026-07-16T18:00:00+08:00", "--mileage-from", "2026-07-17", "--mileage-to", "2026-07-17" ], env=environment, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) self.assertEqual(result.returncode, 0, result.stderr) self.assertIn("customer_demo_gate=ok", result.stdout) self.assertIn("vehicles=1", result.stdout) self.assertIn("samples=1 track=1 mileage=1 denied=1", result.stdout) if __name__ == "__main__": unittest.main()