Merge remote-tracking branch 'upstream/main' into reinlooptest
This commit is contained in:
+66
-1
@@ -1,6 +1,10 @@
|
||||
"""ReinLoop cloud-server endpoint configuration shared by core modules."""
|
||||
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
|
||||
import requests
|
||||
|
||||
from license_utils import get_verified_license
|
||||
|
||||
@@ -16,6 +20,7 @@ server_api_url = os.environ.get(
|
||||
# Compatibility alias used by existing modules. It points to the ReinLoop
|
||||
# Express server API, not a cloud-function endpoint.
|
||||
data_record_url = server_api_url
|
||||
device_api_url = f"{server_api_url.rstrip('/')}/device"
|
||||
_license = get_verified_license()
|
||||
_license_device_id = (_license or {}).get("device_id", "").strip()
|
||||
_environment_device_id = os.environ.get("REINLOOP_DEVICE_ID", "").strip()
|
||||
@@ -26,4 +31,64 @@ if _license_device_id and _environment_device_id and _license_device_id != _envi
|
||||
the_folder = _license_device_id or _environment_device_id or "local-test-device"
|
||||
|
||||
if not the_folder:
|
||||
raise RuntimeError("设备 ID 不能为空")
|
||||
raise RuntimeError("设备 ID 不能为空")
|
||||
|
||||
_DEVICE_TOKEN = None
|
||||
_DEVICE_TOKEN_EXPIRES_AT_MS = 0
|
||||
_DEVICE_TOKEN_LOCK = threading.Lock()
|
||||
|
||||
|
||||
def _current_license_context():
|
||||
payload = get_verified_license() or {}
|
||||
license_id = str(payload.get("license_id") or "").strip()
|
||||
device_id = str(payload.get("device_id") or _environment_device_id or the_folder).strip()
|
||||
return license_id, device_id
|
||||
|
||||
|
||||
def _ensure_device_token(timeout=10):
|
||||
global _DEVICE_TOKEN, _DEVICE_TOKEN_EXPIRES_AT_MS
|
||||
with _DEVICE_TOKEN_LOCK:
|
||||
now_ms = int(time.time() * 1000)
|
||||
if _DEVICE_TOKEN and _DEVICE_TOKEN_EXPIRES_AT_MS - now_ms > 30_000:
|
||||
return _DEVICE_TOKEN
|
||||
|
||||
license_id, device_id = _current_license_context()
|
||||
if not license_id:
|
||||
raise RuntimeError("许可证未就绪,无法获取 deviceToken")
|
||||
|
||||
response = requests.post(device_api_url, json={
|
||||
"type": "deviceAuth",
|
||||
"licenseId": license_id,
|
||||
"deviceId": device_id,
|
||||
}, timeout=timeout)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
if not result.get("success"):
|
||||
raise RuntimeError(result.get("errMsg") or "设备鉴权失败")
|
||||
|
||||
_DEVICE_TOKEN = str(result.get("deviceToken") or "").strip()
|
||||
_DEVICE_TOKEN_EXPIRES_AT_MS = int(result.get("expiresAtMs") or 0)
|
||||
if not _DEVICE_TOKEN or _DEVICE_TOKEN_EXPIRES_AT_MS <= now_ms:
|
||||
raise RuntimeError("设备鉴权返回了无效 deviceToken")
|
||||
return _DEVICE_TOKEN
|
||||
|
||||
|
||||
def device_post(payload, timeout=10):
|
||||
"""Call the device-scoped API route with an auto-renewed device token."""
|
||||
token = _ensure_device_token(timeout=timeout)
|
||||
request_payload = {**payload, "deviceToken": token}
|
||||
response = requests.post(device_api_url, json=request_payload, timeout=timeout)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
return result
|
||||
if result.get("errCode") in {"DEVICE_TOKEN_EXPIRED", "DEVICE_TOKEN_INVALID"}:
|
||||
with _DEVICE_TOKEN_LOCK:
|
||||
global _DEVICE_TOKEN, _DEVICE_TOKEN_EXPIRES_AT_MS
|
||||
_DEVICE_TOKEN = None
|
||||
_DEVICE_TOKEN_EXPIRES_AT_MS = 0
|
||||
token = _ensure_device_token(timeout=timeout)
|
||||
response = requests.post(device_api_url, json={**payload, "deviceToken": token}, timeout=timeout)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
return result
|
||||
@@ -11,7 +11,7 @@ import datetime
|
||||
import threading
|
||||
import requests
|
||||
|
||||
from api import base_url, data_record_url, the_folder
|
||||
from api import device_post, the_folder
|
||||
|
||||
|
||||
class DataCollector:
|
||||
@@ -47,12 +47,11 @@ class DataCollector:
|
||||
def _upload_to_server(self, data_bytes: bytes, filename: str, folder: str) -> bool:
|
||||
"""向 ReinLoop 云服务器申请上传地址并上传控制数据。"""
|
||||
try:
|
||||
resp = requests.post(data_record_url, json={
|
||||
result = device_post({
|
||||
"type": "uploadDataFile",
|
||||
"fileName": filename,
|
||||
"folder": folder,
|
||||
}, timeout=30)
|
||||
result = resp.json()
|
||||
except Exception as e:
|
||||
self.log(f"向云服务器申请上传地址异常: {e}")
|
||||
return False
|
||||
|
||||
@@ -3,16 +3,12 @@
|
||||
|
||||
def heartbeat_device(timeout=5):
|
||||
"""Refresh the current device's Server heartbeat and return its timestamp."""
|
||||
import requests
|
||||
from api import data_record_url, the_folder
|
||||
from api import device_post
|
||||
|
||||
try:
|
||||
response = requests.post(data_record_url, json={
|
||||
result = device_post({
|
||||
"type": "deviceHeartbeat",
|
||||
"deviceId": the_folder,
|
||||
}, timeout=timeout)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
except Exception as exc:
|
||||
raise ValueError(f"设备心跳请求失败: {exc}") from exc
|
||||
if not result.get("success"):
|
||||
|
||||
@@ -16,7 +16,7 @@ from collections import deque
|
||||
from get_V import measure_volume
|
||||
from ind_collector import collect_data_with_prbs
|
||||
|
||||
from api import base_url, data_record_url, the_folder
|
||||
from api import device_post, the_folder
|
||||
|
||||
|
||||
class IdentificationManager:
|
||||
@@ -66,12 +66,11 @@ class IdentificationManager:
|
||||
"""
|
||||
# Step 1: 向业务服务器申请一次性上传地址(不传文件内容)
|
||||
try:
|
||||
resp = requests.post(data_record_url, json={
|
||||
result = device_post({
|
||||
"type": "uploadDataFile",
|
||||
"fileName": filename,
|
||||
"folder": folder,
|
||||
}, timeout=30)
|
||||
result = resp.json()
|
||||
except Exception as e:
|
||||
self.log(f"向云服务器申请上传地址异常: {e}")
|
||||
return False
|
||||
|
||||
@@ -130,15 +130,12 @@ def parse_identification_config_csv(csv_text: str) -> dict:
|
||||
def download_identification_config(timeout=20) -> dict:
|
||||
"""Download the current customer's CSV config through the cloud server."""
|
||||
import requests
|
||||
from api import data_record_url, the_folder
|
||||
from api import device_post
|
||||
|
||||
try:
|
||||
response = requests.post(data_record_url, json={
|
||||
result = device_post({
|
||||
"type": "getIdentificationConfig",
|
||||
"deviceId": the_folder,
|
||||
}, timeout=timeout)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
except Exception as exc:
|
||||
raise ValueError(f"连接云服务器辨识配置服务失败: {exc}") from exc
|
||||
|
||||
|
||||
@@ -2,13 +2,10 @@
|
||||
|
||||
|
||||
def _post(payload, timeout=10):
|
||||
import requests
|
||||
from api import data_record_url
|
||||
from api import device_post
|
||||
|
||||
try:
|
||||
response = requests.post(data_record_url, json=payload, timeout=timeout)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
result = device_post(payload, timeout=timeout)
|
||||
except Exception as exc:
|
||||
raise ValueError(f"辨识反馈服务请求失败: {exc}") from exc
|
||||
if not result.get("success"):
|
||||
@@ -18,13 +15,10 @@ def _post(payload, timeout=10):
|
||||
|
||||
def register_identification_result(run_id: str, timeout=10) -> None:
|
||||
"""Register one uploaded CSV as the customer's current review target."""
|
||||
from api import the_folder
|
||||
|
||||
if not run_id:
|
||||
raise ValueError("辨识结果缺少 run_id")
|
||||
_post({
|
||||
"type": "registerIdentificationResult",
|
||||
"deviceId": the_folder,
|
||||
"runId": run_id,
|
||||
"fileName": run_id,
|
||||
}, timeout=timeout)
|
||||
@@ -32,11 +26,8 @@ def register_identification_result(run_id: str, timeout=10) -> None:
|
||||
|
||||
def get_identification_feedback(run_id: str, timeout=10):
|
||||
"""Return None while pending, otherwise return the integer 0 or 1."""
|
||||
from api import the_folder
|
||||
|
||||
result = _post({
|
||||
"type": "getIdentificationFeedback",
|
||||
"deviceId": the_folder,
|
||||
"runId": run_id,
|
||||
}, timeout=timeout)
|
||||
if not result.get("ready"):
|
||||
@@ -49,10 +40,7 @@ def get_identification_feedback(run_id: str, timeout=10):
|
||||
|
||||
def acknowledge_identification_feedback(run_id: str, timeout=10) -> None:
|
||||
"""Delete the consumed review record so stale feedback cannot be reused."""
|
||||
from api import the_folder
|
||||
|
||||
_post({
|
||||
"type": "ackIdentificationFeedback",
|
||||
"deviceId": the_folder,
|
||||
"runId": run_id,
|
||||
}, timeout=timeout)
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
|
||||
import threading
|
||||
import io
|
||||
import requests
|
||||
import torch
|
||||
from stable_baselines3 import SAC
|
||||
|
||||
# 关键:禁用 PyTorch 内部多线程,防止在 PyInstaller daemon 线程中 segfault
|
||||
torch.set_num_threads(1)
|
||||
|
||||
from api import base_url, data_record_url, the_folder
|
||||
from api import device_post, the_folder
|
||||
|
||||
|
||||
class ModelManager:
|
||||
@@ -50,8 +49,7 @@ class ModelManager:
|
||||
def fetch_models():
|
||||
try:
|
||||
payload = {"type": "listModels", "folder": f"{the_folder}/model_config"}
|
||||
resp = requests.post(data_record_url, json=payload, timeout=10)
|
||||
result = resp.json()
|
||||
result = device_post(payload, timeout=10)
|
||||
|
||||
if result.get("success"):
|
||||
files = result.get("files", [])
|
||||
@@ -98,8 +96,7 @@ class ModelManager:
|
||||
|
||||
# 获取临时下载 URL
|
||||
payload = {"type": "downloadModel", "fileID": file_id}
|
||||
resp = requests.post(data_record_url, json=payload, timeout=15)
|
||||
result = resp.json()
|
||||
result = device_post(payload, timeout=15)
|
||||
|
||||
if not result.get("success"):
|
||||
err = result.get('errMsg', '未知错误')
|
||||
|
||||
@@ -84,13 +84,10 @@ def validate_volume_config(config) -> dict:
|
||||
|
||||
|
||||
def _post_volume_request(payload, timeout=10):
|
||||
import requests
|
||||
from api import data_record_url
|
||||
from api import device_post
|
||||
|
||||
try:
|
||||
response = requests.post(data_record_url, json=payload, timeout=timeout)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
result = device_post(payload, timeout=timeout)
|
||||
except Exception as exc:
|
||||
raise ValueError(f"连接云端容积参数服务失败: {exc}") from exc
|
||||
|
||||
@@ -101,11 +98,8 @@ def _post_volume_request(payload, timeout=10):
|
||||
|
||||
def create_volume_config_request(timeout=10) -> dict:
|
||||
"""Create exactly one cloud request after the customer clicks Test."""
|
||||
from api import the_folder
|
||||
|
||||
result = _post_volume_request({
|
||||
"type": "createVolumeConfigRequest",
|
||||
"deviceId": the_folder,
|
||||
}, timeout=timeout)
|
||||
if not result.get("requestId") or not result.get("expiresAtMs"):
|
||||
raise ValueError("云端未返回有效的容积参数请求编号")
|
||||
@@ -118,11 +112,9 @@ def create_volume_config_request(timeout=10) -> dict:
|
||||
def poll_volume_config_request(request_id: str, timeout=10) -> dict:
|
||||
"""Poll one request; download and validate JSON only when it is ready."""
|
||||
import requests
|
||||
from api import the_folder
|
||||
|
||||
result = _post_volume_request({
|
||||
"type": "getVolumeConfigRequest",
|
||||
"deviceId": the_folder,
|
||||
"requestId": request_id,
|
||||
}, timeout=timeout)
|
||||
if result.get("expired"):
|
||||
@@ -145,10 +137,7 @@ def poll_volume_config_request(request_id: str, timeout=10) -> dict:
|
||||
|
||||
def acknowledge_volume_config_request(request_id: str, timeout=10) -> None:
|
||||
"""Delete the consumed/abandoned request and its temporary JSON file."""
|
||||
from api import the_folder
|
||||
|
||||
_post_volume_request({
|
||||
"type": "ackVolumeConfigRequest",
|
||||
"deviceId": the_folder,
|
||||
"requestId": request_id,
|
||||
}, timeout=timeout)
|
||||
|
||||
@@ -47,7 +47,7 @@ LICENSE_FILE = "license.lic"
|
||||
# 巡检间隔(分钟)
|
||||
DEFAULT_CHECK_INTERVAL = 5
|
||||
ONLINE_CHECK_TIMEOUT_SECONDS = 5
|
||||
DEFAULT_OFFLINE_HOURS = 72
|
||||
DEFAULT_OFFLINE_HOURS = 100
|
||||
|
||||
# 过期后宽限期(小时),给用户保存工作的时间
|
||||
GRACE_PERIOD_HOURS = 2
|
||||
|
||||
@@ -15,6 +15,10 @@ def load_data_collector_module():
|
||||
api = types.ModuleType("api")
|
||||
api.base_url = "https://cloud.example"
|
||||
api.data_record_url = "https://cloud.example"
|
||||
api.device_post = lambda payload, timeout=10: {
|
||||
"success": True,
|
||||
"uploadMetadata": {"url": "https://upload.example"}
|
||||
}
|
||||
api.the_folder = "customer-a/line-1"
|
||||
requests = types.ModuleType("requests")
|
||||
|
||||
|
||||
@@ -16,26 +16,12 @@ class DeviceHeartbeatTests(unittest.TestCase):
|
||||
def test_sends_current_device_id_to_server(self):
|
||||
calls = []
|
||||
requests_module = types.ModuleType("requests")
|
||||
|
||||
class Response:
|
||||
def raise_for_status(self):
|
||||
return None
|
||||
|
||||
def json(self):
|
||||
return {"success": True, "lastSeenAt": "2026-07-28T00:00:00.000Z"}
|
||||
|
||||
def post(url, json, timeout):
|
||||
calls.append((url, json, timeout))
|
||||
return Response()
|
||||
|
||||
requests_module.post = post
|
||||
api_module = types.ModuleType("api")
|
||||
api_module.data_record_url = "https://server.example"
|
||||
api_module.the_folder = "company/line"
|
||||
api_module.device_post = lambda payload, timeout=5: (
|
||||
calls.append((payload, timeout)) or {"success": True, "lastSeenAt": "2026-07-28T00:00:00.000Z"}
|
||||
)
|
||||
with patch.dict(sys.modules, {"requests": requests_module, "api": api_module}):
|
||||
timestamp = HEARTBEAT.heartbeat_device(timeout=7)
|
||||
|
||||
self.assertEqual(timestamp, "2026-07-28T00:00:00.000Z")
|
||||
self.assertEqual(calls, [("https://server.example", {
|
||||
"type": "deviceHeartbeat", "deviceId": "company/line"
|
||||
}, 7)])
|
||||
self.assertEqual(calls, [({"type": "deviceHeartbeat"}, 7)])
|
||||
@@ -87,33 +87,23 @@ class IdentificationConfigTests(unittest.TestCase):
|
||||
def test_download_requests_customer_config_and_validates_it(self):
|
||||
calls = []
|
||||
|
||||
class FakeResponse:
|
||||
def __init__(self, body=None, text=None):
|
||||
self.body = body
|
||||
self.text = text
|
||||
|
||||
def raise_for_status(self):
|
||||
return None
|
||||
|
||||
def json(self):
|
||||
return self.body
|
||||
|
||||
requests_module = types.ModuleType("requests")
|
||||
requests_module.RequestException = Exception
|
||||
|
||||
def post(url, json, timeout):
|
||||
calls.append(("post", url, json, timeout))
|
||||
return FakeResponse({"success": True, "url": "https://temp/config"})
|
||||
def device_post(payload, timeout):
|
||||
calls.append(("device_post", payload, timeout))
|
||||
return {"success": True, "url": "https://temp/config"}
|
||||
|
||||
def get(url, timeout):
|
||||
calls.append(("get", url, timeout))
|
||||
return FakeResponse(text=config_csv(VALID_CONFIG))
|
||||
return types.SimpleNamespace(
|
||||
text=config_csv(VALID_CONFIG),
|
||||
raise_for_status=lambda: None
|
||||
)
|
||||
|
||||
requests_module.post = post
|
||||
requests_module.get = get
|
||||
api_module = types.ModuleType("api")
|
||||
api_module.data_record_url = "https://cloud/data_record"
|
||||
api_module.the_folder = "客户A"
|
||||
api_module.device_post = device_post
|
||||
|
||||
with patch.dict(sys.modules, {
|
||||
"requests": requests_module,
|
||||
@@ -122,28 +112,14 @@ class IdentificationConfigTests(unittest.TestCase):
|
||||
result = download_identification_config(timeout=7)
|
||||
|
||||
self.assertEqual(result["repeat"], 2)
|
||||
self.assertEqual(calls[0], (
|
||||
"post",
|
||||
"https://cloud/data_record",
|
||||
{"type": "getIdentificationConfig", "deviceId": "客户A"},
|
||||
7,
|
||||
))
|
||||
self.assertEqual(calls[0], ("device_post", {"type": "getIdentificationConfig"}, 7))
|
||||
self.assertEqual(calls[1], ("get", "https://temp/config", 7))
|
||||
|
||||
def test_download_reports_cloud_rejection(self):
|
||||
class FakeResponse:
|
||||
def raise_for_status(self):
|
||||
return None
|
||||
|
||||
def json(self):
|
||||
return {"success": False, "errMsg": "配置不存在"}
|
||||
|
||||
requests_module = types.ModuleType("requests")
|
||||
requests_module.RequestException = Exception
|
||||
requests_module.post = lambda *args, **kwargs: FakeResponse()
|
||||
api_module = types.ModuleType("api")
|
||||
api_module.data_record_url = "https://cloud/data_record"
|
||||
api_module.the_folder = "客户A"
|
||||
api_module.device_post = lambda *args, **kwargs: {"success": False, "errMsg": "配置不存在"}
|
||||
|
||||
with patch.dict(sys.modules, {
|
||||
"requests": requests_module,
|
||||
|
||||
@@ -57,28 +57,13 @@ class VolumeConfigTests(unittest.TestCase):
|
||||
|
||||
def test_customer_creates_exactly_one_request_instruction(self):
|
||||
calls = []
|
||||
|
||||
class FakeResponse:
|
||||
def raise_for_status(self):
|
||||
return None
|
||||
|
||||
def json(self):
|
||||
return {
|
||||
"success": True,
|
||||
"requestId": "request-1",
|
||||
"expiresAtMs": 123456,
|
||||
}
|
||||
|
||||
requests_module = types.ModuleType("requests")
|
||||
|
||||
def post(url, json, timeout):
|
||||
calls.append((url, json, timeout))
|
||||
return FakeResponse()
|
||||
|
||||
requests_module.post = post
|
||||
api_module = types.ModuleType("api")
|
||||
api_module.data_record_url = "https://cloud/data_record"
|
||||
api_module.the_folder = "客户A"
|
||||
api_module.device_post = lambda payload, timeout=10: (calls.append((payload, timeout)) or {
|
||||
"success": True,
|
||||
"requestId": "request-1",
|
||||
"expiresAtMs": 123456,
|
||||
})
|
||||
|
||||
with patch.dict(sys.modules, {
|
||||
"requests": requests_module,
|
||||
@@ -90,30 +75,16 @@ class VolumeConfigTests(unittest.TestCase):
|
||||
"request_id": "request-1",
|
||||
"expires_at_ms": 123456,
|
||||
})
|
||||
self.assertEqual(calls, [(
|
||||
"https://cloud/data_record",
|
||||
{"type": "createVolumeConfigRequest", "deviceId": "客户A"},
|
||||
7,
|
||||
)])
|
||||
self.assertEqual(calls, [({"type": "createVolumeConfigRequest"}, 7)])
|
||||
|
||||
def test_pending_request_does_not_download_a_file(self):
|
||||
calls = []
|
||||
|
||||
class FakeResponse:
|
||||
def raise_for_status(self):
|
||||
return None
|
||||
|
||||
def json(self):
|
||||
return {"success": True, "ready": False, "expired": False}
|
||||
|
||||
requests_module = types.ModuleType("requests")
|
||||
requests_module.post = lambda *args, **kwargs: (
|
||||
calls.append(("post", kwargs["json"])) or FakeResponse()
|
||||
)
|
||||
requests_module.get = lambda *args, **kwargs: calls.append(("get", args[0]))
|
||||
api_module = types.ModuleType("api")
|
||||
api_module.data_record_url = "https://cloud/data_record"
|
||||
api_module.the_folder = "客户A"
|
||||
api_module.device_post = lambda payload, timeout=10: (
|
||||
calls.append(("device_post", payload)) or {"success": True, "ready": False, "expired": False}
|
||||
)
|
||||
|
||||
with patch.dict(sys.modules, {
|
||||
"requests": requests_module,
|
||||
@@ -122,36 +93,25 @@ class VolumeConfigTests(unittest.TestCase):
|
||||
result = poll_volume_config_request("request-1")
|
||||
|
||||
self.assertEqual(result, {"ready": False, "expired": False})
|
||||
self.assertEqual(calls, [("post", {
|
||||
self.assertEqual(calls, [("device_post", {
|
||||
"type": "getVolumeConfigRequest",
|
||||
"deviceId": "客户A",
|
||||
"requestId": "request-1",
|
||||
})])
|
||||
|
||||
def test_ready_request_downloads_and_validates_json(self):
|
||||
class FakeResponse:
|
||||
def __init__(self, body):
|
||||
self.body = body
|
||||
|
||||
def raise_for_status(self):
|
||||
return None
|
||||
|
||||
def json(self):
|
||||
return self.body
|
||||
|
||||
requests_module = types.ModuleType("requests")
|
||||
requests_module.post = lambda *args, **kwargs: FakeResponse({
|
||||
requests_module.post = lambda *args, **kwargs: types.SimpleNamespace()
|
||||
api_module = types.ModuleType("api")
|
||||
api_module.device_post = lambda payload, timeout=10: {
|
||||
"success": True,
|
||||
"ready": True,
|
||||
"expired": False,
|
||||
"url": "https://temp/volume.json",
|
||||
})
|
||||
requests_module.get = lambda *args, **kwargs: FakeResponse(
|
||||
dict(VALID_CONFIG)
|
||||
}
|
||||
requests_module.get = lambda *args, **kwargs: types.SimpleNamespace(
|
||||
raise_for_status=lambda: None,
|
||||
json=lambda: dict(VALID_CONFIG)
|
||||
)
|
||||
api_module = types.ModuleType("api")
|
||||
api_module.data_record_url = "https://cloud/data_record"
|
||||
api_module.the_folder = "客户A"
|
||||
|
||||
with patch.dict(sys.modules, {
|
||||
"requests": requests_module,
|
||||
@@ -163,18 +123,9 @@ class VolumeConfigTests(unittest.TestCase):
|
||||
self.assertEqual(result["config"], VALID_CONFIG)
|
||||
|
||||
def test_create_request_reports_server_rejection(self):
|
||||
class FakeResponse:
|
||||
def raise_for_status(self):
|
||||
return None
|
||||
|
||||
def json(self):
|
||||
return {"success": False, "errMsg": "尚未配置"}
|
||||
|
||||
requests_module = types.ModuleType("requests")
|
||||
requests_module.post = lambda *args, **kwargs: FakeResponse()
|
||||
api_module = types.ModuleType("api")
|
||||
api_module.data_record_url = "https://cloud/data_record"
|
||||
api_module.the_folder = "客户A"
|
||||
api_module.device_post = lambda *args, **kwargs: {"success": False, "errMsg": "尚未配置"}
|
||||
|
||||
with patch.dict(sys.modules, {
|
||||
"requests": requests_module,
|
||||
@@ -185,21 +136,9 @@ class VolumeConfigTests(unittest.TestCase):
|
||||
|
||||
def test_acknowledges_the_same_request_for_cleanup(self):
|
||||
calls = []
|
||||
|
||||
class FakeResponse:
|
||||
def raise_for_status(self):
|
||||
return None
|
||||
|
||||
def json(self):
|
||||
return {"success": True, "deleted": 1}
|
||||
|
||||
requests_module = types.ModuleType("requests")
|
||||
requests_module.post = lambda *args, **kwargs: (
|
||||
calls.append(kwargs["json"]) or FakeResponse()
|
||||
)
|
||||
api_module = types.ModuleType("api")
|
||||
api_module.data_record_url = "https://cloud/data_record"
|
||||
api_module.the_folder = "客户A"
|
||||
api_module.device_post = lambda payload, timeout=10: (calls.append(payload) or {"success": True, "deleted": 1})
|
||||
|
||||
with patch.dict(sys.modules, {
|
||||
"requests": requests_module,
|
||||
@@ -209,7 +148,6 @@ class VolumeConfigTests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(calls, [{
|
||||
"type": "ackVolumeConfigRequest",
|
||||
"deviceId": "客户A",
|
||||
"requestId": "request-1",
|
||||
}])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user