多段激活后无需再带工单号;含空运群回复走机器人;复制补问清单时空货好时间不再吃成下一行编号。 Co-authored-by: Cursor <cursoragent@cursor.com>
128 lines
4.9 KiB
Python
128 lines
4.9 KiB
Python
"""
|
|
附件上传 H5:token 不含账号,过期 410,拒收图片。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
import unittest
|
|
|
|
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
|
if ROOT not in sys.path:
|
|
sys.path.insert(0, ROOT)
|
|
|
|
os.environ.setdefault("YTD_ENV", "test")
|
|
os.environ.setdefault("REDIS_BACKEND", "memory")
|
|
os.environ.setdefault("CHECKPOINT_BACKEND", "memory")
|
|
os.environ.setdefault("MESSAGE_STORE_BACKEND", "memory")
|
|
os.environ.setdefault("LEDGER_BACKEND", "memory")
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
|
|
from agent.channel.h5 import router as h5_router
|
|
from agent.channel.h5.copy_store import build_upload_url
|
|
from agent.channel.wecom.models import InboundMessage
|
|
from agent.handlers.attachment_inquiry import handle_attachment_inquiry, lookup_upload_sender
|
|
from agent.policy.attachment_wave import reset_attach_wave_book_for_test
|
|
from agent.policy.image_wave import reset_image_wave_book_for_test
|
|
from agent.policy.air_text_flow import reset_air_text_flow_for_test
|
|
from agent.policy.land_text_flow import reset_land_text_flow_for_test
|
|
from agent.policy.sea_text_flow import reset_sea_text_flow_for_test
|
|
|
|
|
|
class AttachmentUploadH5Tests(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
reset_attach_wave_book_for_test()
|
|
reset_image_wave_book_for_test()
|
|
reset_sea_text_flow_for_test()
|
|
reset_air_text_flow_for_test()
|
|
reset_land_text_flow_for_test()
|
|
app = FastAPI()
|
|
app.include_router(h5_router)
|
|
self.client = TestClient(app)
|
|
|
|
def _open_card(self) -> str:
|
|
handle_attachment_inquiry(
|
|
InboundMessage(sender_id="s1", message_id="m1", content="附件询价")
|
|
)
|
|
from agent.policy.attachment_wave import get_attach_wave_book
|
|
|
|
wave = get_attach_wave_book().active("s1")
|
|
self.assertIsNotNone(wave)
|
|
url = build_upload_url(wave.token)
|
|
self.assertIn("/upload/", url)
|
|
return wave.token
|
|
|
|
def test_open_page_ok_and_expired(self) -> None:
|
|
token = self._open_card()
|
|
self.assertEqual(lookup_upload_sender(token), "s1")
|
|
hit = self.client.get(f"/inquiry-form/upload/{token}")
|
|
self.assertEqual(hit.status_code, 200)
|
|
self.assertIn("Excel", hit.text)
|
|
self.assertNotIn("s1", hit.text)
|
|
miss = self.client.get("/inquiry-form/upload/not-a-real-token")
|
|
self.assertEqual(miss.status_code, 410)
|
|
self.assertIn("过期", miss.text)
|
|
|
|
def test_bare_token_opens_upload_page_not_json(self) -> None:
|
|
"""
|
|
公网 Nginx 会把 /inquiry-form/upload/{token} 改写成 /inquiry-form/{token}。
|
|
附件票必须出上传页,不能把补问骨架 JSON 直接给销售。
|
|
"""
|
|
token = self._open_card()
|
|
hit = self.client.get(f"/inquiry-form/{token}")
|
|
self.assertEqual(hit.status_code, 200)
|
|
self.assertIn("text/html", hit.headers.get("content-type", ""))
|
|
self.assertIn("Excel", hit.text)
|
|
self.assertNotIn("骨架", hit.text)
|
|
self.assertNotIn("s1", hit.text)
|
|
miss = self.client.get("/inquiry-form/not-a-real-token")
|
|
self.assertIn(miss.status_code, (410, 422))
|
|
|
|
def test_bare_token_post_png_rejected(self) -> None:
|
|
token = self._open_card()
|
|
hit = self.client.post(
|
|
f"/inquiry-form/{token}",
|
|
files=[("files", ("shot.png", b"\x89PNG", "image/png"))],
|
|
)
|
|
self.assertEqual(hit.status_code, 400)
|
|
self.assertIn("Excel", hit.text)
|
|
|
|
def test_upload_page_uses_card_chrome(self) -> None:
|
|
from agent.channel.h5.upload_page import expired_upload_page, render_upload_message, render_upload_page
|
|
|
|
page = render_upload_page()
|
|
self.assertIn('class="card"', page)
|
|
self.assertIn('class="drop"', page)
|
|
self.assertIn('id="picked"', page)
|
|
self.assertIn("一次可选多个", page)
|
|
self.assertIn("Excel", page)
|
|
self.assertNotIn("s1", page)
|
|
ok = render_upload_message("上传成功", "已收到,请回企微查看识别结果。")
|
|
dead = expired_upload_page()
|
|
self.assertIn('class="card"', ok)
|
|
self.assertIn('class="card"', dead)
|
|
self.assertIn("过期", dead)
|
|
self.assertIn("closeWindow", ok)
|
|
self.assertIn("wecom-jssdk", ok)
|
|
self.assertIn("jsapi-signature", ok)
|
|
self.assertIn("function isPhone()", ok)
|
|
self.assertNotIn("window.close()", ok)
|
|
self.assertNotIn("closeWindow", page)
|
|
self.assertNotIn("closeWindow", dead)
|
|
|
|
def test_post_png_rejected(self) -> None:
|
|
token = self._open_card()
|
|
hit = self.client.post(
|
|
f"/inquiry-form/upload/{token}",
|
|
files=[("files", ("shot.png", b"\x89PNG", "image/png"))],
|
|
)
|
|
self.assertEqual(hit.status_code, 400)
|
|
self.assertIn("Excel", hit.text)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|