feat: release v1.0.1 CADWorld 网站与 LeKiwi 智能抓放
web-platform-ci / Standalone decision service (no cloud credentials) (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
lekiwi-compatibility / cpu-compatibility (push) Has been cancelled
web-platform-ci / Standalone decision service (no cloud credentials) (pull_request) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (pull_request) Has been cancelled
web-platform-ci / Playwright E2E (pull_request) Has been cancelled
lekiwi-compatibility / cpu-compatibility (pull_request) Has been cancelled
web-platform-ci / Standalone decision service (no cloud credentials) (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
lekiwi-compatibility / cpu-compatibility (push) Has been cancelled
web-platform-ci / Standalone decision service (no cloud credentials) (pull_request) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (pull_request) Has been cancelled
web-platform-ci / Playwright E2E (pull_request) Has been cancelled
lekiwi-compatibility / cpu-compatibility (pull_request) Has been cancelled
集成同源 BYOK 会话隔离、精简模型设置、官方订阅入口和 HTTPS 发布运维;保留本地训练/调参与控制能力。同步 npm 版本及 CHANGELOG,记录公网真实 API 验收仍待用户凭据。
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
import asyncio
|
||||
import time
|
||||
import unittest
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from aiohttp.test_utils import TestClient, TestServer
|
||||
|
||||
from decision_server.server import PREFIX
|
||||
from decision_server.tests.test_service import plan_value, request_value
|
||||
from decision_server.tests.test_web_config import config
|
||||
from decision_server.web_config import COOKIE
|
||||
from decision_server.web_server import CATALOG, MANAGER, create_website_app
|
||||
from decision_server.web_sessions import Limits
|
||||
|
||||
|
||||
class WebsiteTests(unittest.IsolatedAsyncioTestCase):
|
||||
async def asyncSetUp(self):
|
||||
self.origin = "https://site.test"
|
||||
self.app = create_website_app(self.origin)
|
||||
self.client = TestClient(TestServer(self.app))
|
||||
await self.client.start_server()
|
||||
self.manager = self.app[MANAGER]
|
||||
self.app[CATALOG].refresh = AsyncMock()
|
||||
self.headers = {"Host": "site.test", "Origin": self.origin}
|
||||
|
||||
async def asyncTearDown(self):
|
||||
await self.client.close()
|
||||
|
||||
async def visitor(self):
|
||||
response = await self.client.post(PREFIX + "/session", json={}, headers=self.headers)
|
||||
self.assertEqual(response.status, 200)
|
||||
value = await response.json()
|
||||
cookie = response.cookies[COOKIE]
|
||||
self.assertTrue(cookie["httponly"])
|
||||
self.assertTrue(cookie["secure"])
|
||||
self.assertEqual(cookie["samesite"], "Strict")
|
||||
self.assertEqual(cookie["domain"], "")
|
||||
return {
|
||||
**self.headers,
|
||||
"Cookie": COOKIE + "=" + cookie.value,
|
||||
"X-CSRF-Token": value["csrfToken"],
|
||||
"X-Config-Version": "0",
|
||||
}, self.manager.values[cookie.value]
|
||||
|
||||
async def save(self, headers):
|
||||
response = await self.client.put(PREFIX + "/configuration", json=config(), headers=headers)
|
||||
self.assertEqual(response.status, 200, await response.text())
|
||||
headers["X-Config-Version"] = response.headers["X-Config-Version"]
|
||||
return await response.json()
|
||||
|
||||
async def test_boundary_no_cookie_csrf_origin_host_or_cross_site(self):
|
||||
response = await self.client.get(PREFIX + "/status", headers=self.headers)
|
||||
self.assertEqual(response.status, 401)
|
||||
headers, _ = await self.visitor()
|
||||
for patch_headers in (
|
||||
{"Origin": "https://evil.test"},
|
||||
{"Origin": ""},
|
||||
{"Host": "evil.test"},
|
||||
{"X-CSRF-Token": "bad"},
|
||||
{"Sec-Fetch-Site": "same-site"},
|
||||
):
|
||||
response = await self.client.put(
|
||||
PREFIX + "/configuration", json=config(), headers={**headers, **patch_headers}
|
||||
)
|
||||
self.assertEqual(response.status, 403)
|
||||
self.assertNotIn("Access-Control-Allow-Origin", response.headers)
|
||||
self.assertEqual(response.headers["Cache-Control"], "no-store")
|
||||
response = await self.client.post(
|
||||
PREFIX + "/session", json={}, headers={"Host": "site.test"}
|
||||
)
|
||||
self.assertEqual(response.status, 403)
|
||||
|
||||
async def test_atomic_credentials_no_metadata_files_and_stale_tab(self):
|
||||
a, av = await self.visitor()
|
||||
b, bv = await self.visitor()
|
||||
result = await self.save(a)
|
||||
self.assertNotIn("secret-fixture", str(result))
|
||||
self.assertFalse(bv.service.connections.values)
|
||||
self.assertFalse(hasattr(av.service.connections, "path"))
|
||||
response = await self.client.put(
|
||||
PREFIX + "/configuration", json=config(), headers={**a, "X-Config-Version": "0"}
|
||||
)
|
||||
self.assertEqual(response.status, 409)
|
||||
bad = config()
|
||||
bad["jev"]["apiKey"] = "invalid key"
|
||||
response = await self.client.put(PREFIX + "/configuration", json=bad, headers=a)
|
||||
self.assertEqual(response.status, 400)
|
||||
self.assertEqual(av.service.connections.values["jev"].key, "jev-secret-fixture")
|
||||
response = await self.client.get(PREFIX + "/status", headers=b)
|
||||
self.assertFalse((await response.json())["ready"])
|
||||
|
||||
async def test_two_visitors_identical_request_ids_and_cancel_isolation(self):
|
||||
a, av = await self.visitor()
|
||||
b, bv = await self.visitor()
|
||||
await self.save(a)
|
||||
await self.save(b)
|
||||
started = asyncio.Event()
|
||||
release = asyncio.Event()
|
||||
|
||||
async def provider(*_):
|
||||
started.set()
|
||||
await release.wait()
|
||||
return plan_value(), {}
|
||||
|
||||
with patch("decision_server.providers.openai.plan", side_effect=provider):
|
||||
pending = asyncio.create_task(
|
||||
self.client.post(PREFIX + "/plan", json=request_value(), headers=b)
|
||||
)
|
||||
await asyncio.wait_for(started.wait(), 2)
|
||||
response = await self.client.post(
|
||||
PREFIX + "/cancel", json={"runId": "run1", "requestId": "r1"}, headers=a
|
||||
)
|
||||
self.assertFalse((await response.json())["cancelled"])
|
||||
self.assertEqual(len(bv.service.active), 1)
|
||||
await self.client.delete(PREFIX + "/session", headers=a)
|
||||
self.assertTrue(av.closed)
|
||||
self.assertTrue(bv.service.connections.values)
|
||||
release.set()
|
||||
self.assertEqual((await pending).status, 200)
|
||||
self.assertEqual(self.manager.inference, 0)
|
||||
|
||||
async def test_ttl_status_does_not_refresh_and_credentials_destroyed(self):
|
||||
headers, visitor = await self.visitor()
|
||||
await self.save(headers)
|
||||
touched = visitor.touched
|
||||
await self.client.get(PREFIX + "/status", headers=headers)
|
||||
await self.client.post(PREFIX + "/session", json={}, headers=headers)
|
||||
self.assertEqual(visitor.touched, touched)
|
||||
visitor.touched = time.monotonic() - 1801
|
||||
response = await self.client.get(PREFIX + "/status", headers=headers)
|
||||
self.assertEqual(response.status, 401)
|
||||
self.assertFalse(visitor.service.connections.values)
|
||||
self.assertTrue(visitor.closed)
|
||||
|
||||
async def test_limits_ip_spoof_does_not_bypass_and_no_implicit_cli(self):
|
||||
self.manager.limits = Limits(ip_sessions=2, codex=1)
|
||||
a, av = await self.visitor()
|
||||
b, bv = await self.visitor()
|
||||
response = await self.client.post(
|
||||
PREFIX + "/session", json={}, headers={**self.headers, "X-Real-IP": "1.2.3.4"}
|
||||
)
|
||||
self.assertEqual(response.status, 429)
|
||||
self.manager.reserve_codex(av)
|
||||
with self.assertRaisesRegex(Exception, "subscription_capacity"):
|
||||
self.manager.reserve_codex(bv)
|
||||
av.codex_reserved = False
|
||||
with patch.object(av.service.codex, "start", new_callable=AsyncMock) as start:
|
||||
response = await self.client.get(PREFIX + "/codex/status", headers=a)
|
||||
self.assertFalse((await response.json())["loggedIn"])
|
||||
start.assert_not_called()
|
||||
await self.save(b)
|
||||
self.manager.inference = self.manager.limits.inference
|
||||
response = await self.client.post(PREFIX + "/plan", json=request_value(), headers=b)
|
||||
self.assertEqual(response.status, 429)
|
||||
self.manager.inference = 0
|
||||
|
||||
async def test_no_arbitrary_rpc_or_local_connection_or_queries(self):
|
||||
headers, _ = await self.visitor()
|
||||
for path in ("/connections", "/codex/exec", "/codex/rpc"):
|
||||
response = await self.client.post(PREFIX + path, json={}, headers=headers)
|
||||
self.assertIn(response.status, (404, 405))
|
||||
response = await self.client.get(PREFIX + "/status?key=fixture", headers=headers)
|
||||
self.assertEqual(response.status, 400)
|
||||
Reference in New Issue
Block a user