Files

144 lines
6.2 KiB
Python

"""端到端测试: 创建作业要求 → 上传项目包 → 检测 → 报告"""
import io
import tarfile
import textwrap
import unittest
from unittest import mock
from django.contrib.auth.models import User
from rest_framework.test import APITestCase
from requirements.models import Requirement, Submission
FLASK_PROJECT = textwrap.dedent("""\
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "ok"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8321)
""")
def make_tar_bytes(name: str, files: dict) -> bytes:
buf = io.BytesIO()
with tarfile.open(fileobj=buf, mode="w") as tf:
for fname, content in files.items():
data = content.encode()
info = tarfile.TarInfo(name=f"{name}/{fname}")
info.size = len(data)
tf.addfile(info, io.BytesIO(data))
return buf.getvalue()
class EndToEndTests(APITestCase):
@classmethod
def setUpTestData(cls):
cls.user = User.objects.create_user("tester", password="pass12345")
def setUp(self):
self._login()
def _login(self):
from rest_framework.authtoken.models import Token
token, _ = Token.objects.get_or_create(user=self.user)
self.client.credentials(HTTP_AUTHORIZATION=f"Token {token.key}")
def test_create_requirement_analyzes_spec(self):
self._login()
llm_json = (
'{"ai_inspection": true, '
'"stability": {"enabled": true, "requirements": "基准并发20错误率≤1%"}, '
'"functional": {"enabled": true, "points": ["首页可访问", "返回 ok"]}, '
'"performance": {"enabled": false, "requirements": ""}, '
'"security": {"enabled": false, "requirements": ""}}'
)
with mock.patch("requirements.llm.resolve_llm", return_value=("http://llm.test/v1", "k", "m")), \
mock.patch("requirements.llm._chat", return_value=llm_json):
resp = self.client.post("/api/requirements/create/", {
"title": "作业1-分析",
"content": "实现一个 web 服务, 首页返回 ok, 要求稳定度达到 99%",
}, format="json")
self.assertEqual(resp.status_code, 201, resp.content)
requirement = Requirement.objects.get(title="作业1-分析")
self.assertTrue(requirement.spec["stability"]["enabled"])
self.assertIn("首页可访问", requirement.spec["functional"]["points"])
def test_create_requirement_custom_llm(self):
self._login()
override = {"mode": "custom", "base_url": "http://custom.test/v1",
"api_key": "ck", "model": "custom-model"}
llm_json = '{"ai_inspection": true, "functional": {"enabled": true, "points": ["自定义功能点"]}}'
captured = {}
with mock.patch("requirements.llm.resolve_llm", side_effect=lambda ov: captured.update(ov) or ("http://custom.test/v1", "ck", "custom-model")), \
mock.patch("requirements.llm._chat", return_value=llm_json):
resp = self.client.post("/api/requirements/create/", {
"title": "custom-llm-作业",
"content": "任意",
"llm": override,
}, format="json")
self.assertEqual(resp.status_code, 201, resp.content)
self.assertEqual(captured["mode"], "custom")
requirement = Requirement.objects.get(title="custom-llm-作业")
self.assertEqual(requirement.llm_config["mode"], "custom")
self.assertIn("自定义功能点", requirement.spec["functional"]["points"])
def test_custom_llm_requires_fields(self):
self._login()
resp = self.client.post("/api/requirements/create/", {
"title": "缺字段", "content": "任意",
"llm": {"mode": "custom", "base_url": "", "model": ""},
}, format="json")
self.assertEqual(resp.status_code, 400)
def test_submission_full_pipeline(self):
self._login()
requirement = Requirement.objects.create(
title="作业1",
content="实现 web 服务",
spec={
"ai_inspection": True,
"stability": {"enabled": True, "requirements": "基准错误率≤1%"},
"functional": {"enabled": True, "points": ["首页可访问"]},
"performance": {"enabled": False, "requirements": ""},
"security": {"enabled": False, "requirements": ""},
},
)
tar_bytes = make_tar_bytes("proj", {"app.py": FLASK_PROJECT})
with mock.patch("requirements.llm.ai_summarize", return_value={
"summary": "代码结构清晰, 功能点完备",
"issues": [{"point": "首页可访问", "status": "pass", "detail": "/ 路由存在"}],
}), mock.patch.dict("django.conf.settings.__dict__", {
"STABILITY_BASE_DURATION": 2, "STABILITY_RAMPUP_DURATION": 2, "STABILITY_CONCURRENCY": 4,
}):
resp = self.client.post(
f"/api/requirements/{requirement.pk}/submissions/",
{"name": "tester-sub", "file": io.BytesIO(tar_bytes)},
format="multipart",
)
self.assertEqual(resp.status_code, 201, resp.content)
submission = Submission.objects.get()
if submission.status != "completed":
submission.report["error"] = submission.report.get("error", "?")
raise AssertionError(f"status={submission.status} report={submission.report}")
self.assertIn("ai_inspection", submission.report)
self.assertIn("stability", submission.report)
report = self.client.get(f"/api/submissions/{submission.pk}/report/")
self.assertEqual(report.status_code, 200)
self.assertIn("stability", report.json())
class AuthTests(APITestCase):
def test_requires_login(self):
resp = self.client.get("/api/requirements/")
self.assertIn(resp.status_code, (401, 403))
def test_token_login(self):
User.objects.create_user("alice", password="pass12345")
resp = self.client.post("/api/auth/token/", {"username": "alice", "password": "pass12345"})
self.assertEqual(resp.status_code, 200)
self.assertIn("token", resp.json())