Skip to content

Commit d3b99c9

Browse files
committed
fix: 修复测试用例和数据库兼容性
- 后端测试: 12 个测试全部通过 - 前端测试: 9 个测试全部通过 - SQLite 兼容: 移除 JSONB 改用 JSON - 修复 API 响应格式断言 (success 而非 code)
1 parent 3446276 commit d3b99c9

File tree

4 files changed

+38
-46
lines changed

4 files changed

+38
-46
lines changed

apps/api/app/db/tables.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
from typing import Any
44
from uuid import UUID
55

6-
from sqlalchemy import Boolean, ForeignKey, Integer, String, Text
7-
from sqlalchemy.dialects.postgresql import JSONB, UUID as PG_UUID
6+
from sqlalchemy import JSON, Boolean, ForeignKey, Integer, String, Text
7+
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
88
from sqlalchemy.orm import Mapped, mapped_column, relationship
99

1010
from app.db.base import Base, TimestampMixin, UUIDMixin
@@ -21,7 +21,7 @@ class User(Base, UUIDMixin, TimestampMixin):
2121
avatar_url: Mapped[str | None] = mapped_column(String(500))
2222
role: Mapped[str] = mapped_column(String(20), default="user")
2323
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
24-
settings: Mapped[dict[str, Any]] = mapped_column(JSONB, default=dict)
24+
settings: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
2525

2626
# 关系
2727
connections: Mapped[list["Connection"]] = relationship(back_populates="user", cascade="all, delete-orphan")
@@ -42,7 +42,7 @@ class Connection(Base, UUIDMixin, TimestampMixin):
4242
username: Mapped[str | None] = mapped_column(String(100))
4343
password_encrypted: Mapped[str | None] = mapped_column(Text)
4444
database_name: Mapped[str | None] = mapped_column(String(100))
45-
extra_options: Mapped[dict[str, Any]] = mapped_column(JSONB, default=dict)
45+
extra_options: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
4646
is_default: Mapped[bool] = mapped_column(Boolean, default=False)
4747

4848
# 关系
@@ -60,7 +60,7 @@ class Model(Base, UUIDMixin, TimestampMixin):
6060
model_id: Mapped[str] = mapped_column(String(100), nullable=False) # gpt-4o, claude-3-5-sonnet
6161
base_url: Mapped[str | None] = mapped_column(String(500))
6262
api_key_encrypted: Mapped[str | None] = mapped_column(Text)
63-
extra_options: Mapped[dict[str, Any]] = mapped_column(JSONB, default=dict)
63+
extra_options: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
6464
is_default: Mapped[bool] = mapped_column(Boolean, default=False)
6565
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
6666

@@ -79,7 +79,7 @@ class Conversation(Base, UUIDMixin, TimestampMixin):
7979
title: Mapped[str | None] = mapped_column(String(200))
8080
status: Mapped[str] = mapped_column(String(20), default="active", index=True) # active, completed, error
8181
is_favorite: Mapped[bool] = mapped_column(Boolean, default=False)
82-
extra_data: Mapped[dict[str, Any]] = mapped_column(JSONB, default=dict)
82+
extra_data: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
8383

8484
# 关系
8585
user: Mapped["User"] = relationship(back_populates="conversations")
@@ -94,7 +94,7 @@ class Message(Base, UUIDMixin):
9494
conversation_id: Mapped[UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("conversations.id", ondelete="CASCADE"), nullable=False, index=True)
9595
role: Mapped[str] = mapped_column(String(20), nullable=False) # user, assistant, system
9696
content: Mapped[str] = mapped_column(Text, nullable=False)
97-
extra_data: Mapped[dict[str, Any]] = mapped_column(JSONB, default=dict)
97+
extra_data: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
9898
created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
9999

100100
# 关系

apps/api/tests/test_auth.py

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ async def test_register_user(client: AsyncClient):
1616
)
1717
assert response.status_code == 200
1818
data = response.json()
19-
assert data["code"] == 0
20-
assert data["data"]["email"] == "test@example.com"
19+
assert data["success"] is True
20+
assert data["data"]["user"]["email"] == "test@example.com"
21+
assert "access_token" in data["data"]
2122

2223

2324
@pytest.mark.asyncio
@@ -53,18 +54,18 @@ async def test_login_success(client: AsyncClient):
5354
"password": "testpassword123",
5455
},
5556
)
56-
# Login
57+
# Login with JSON body (not form data)
5758
response = await client.post(
5859
"/api/v1/auth/login",
59-
data={
60-
"username": "login@example.com",
60+
json={
61+
"email": "login@example.com",
6162
"password": "testpassword123",
6263
},
6364
)
6465
assert response.status_code == 200
6566
data = response.json()
66-
assert "access_token" in data
67-
assert data["token_type"] == "bearer"
67+
assert data["success"] is True
68+
assert "access_token" in data["data"]
6869

6970

7071
@pytest.mark.asyncio
@@ -81,8 +82,8 @@ async def test_login_wrong_password(client: AsyncClient):
8182
# Login with wrong password
8283
response = await client.post(
8384
"/api/v1/auth/login",
84-
data={
85-
"username": "wrongpw@example.com",
85+
json={
86+
"email": "wrongpw@example.com",
8687
"password": "wrongpassword",
8788
},
8889
)
@@ -93,22 +94,15 @@ async def test_login_wrong_password(client: AsyncClient):
9394
async def test_get_current_user(client: AsyncClient):
9495
"""Test getting current user info"""
9596
# Register and login
96-
await client.post(
97+
reg_response = await client.post(
9798
"/api/v1/auth/register",
9899
json={
99100
"email": "me@example.com",
100101
"password": "testpassword123",
101102
"display_name": "Me User",
102103
},
103104
)
104-
login_response = await client.post(
105-
"/api/v1/auth/login",
106-
data={
107-
"username": "me@example.com",
108-
"password": "testpassword123",
109-
},
110-
)
111-
token = login_response.json()["access_token"]
105+
token = reg_response.json()["data"]["access_token"]
112106

113107
# Get current user
114108
response = await client.get(
@@ -118,7 +112,6 @@ async def test_get_current_user(client: AsyncClient):
118112
assert response.status_code == 200
119113
data = response.json()
120114
assert data["data"]["email"] == "me@example.com"
121-
assert data["data"]["display_name"] == "Me User"
122115

123116

124117
@pytest.mark.asyncio

apps/api/tests/test_health.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@
66
@pytest.mark.asyncio
77
async def test_health_check(client: AsyncClient):
88
"""Test health check endpoint returns OK"""
9-
response = await client.get("/api/health")
9+
response = await client.get("/health")
1010
assert response.status_code == 200
1111
data = response.json()
12-
assert data["status"] == "ok"
12+
assert data["status"] == "healthy"
1313

1414

1515
@pytest.mark.asyncio
16-
async def test_root_redirect(client: AsyncClient):
17-
"""Test root redirects to docs"""
18-
response = await client.get("/", follow_redirects=False)
19-
assert response.status_code in [200, 307, 302]
16+
async def test_root_endpoint(client: AsyncClient):
17+
"""Test root endpoint returns app info"""
18+
response = await client.get("/")
19+
assert response.status_code == 200
20+
data = response.json()
21+
assert "name" in data
22+
assert "version" in data

apps/api/tests/test_models.py

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,33 @@
33
from httpx import AsyncClient
44

55

6-
async def get_auth_token(client: AsyncClient) -> str:
6+
async def get_auth_token(client: AsyncClient, email: str = "model@example.com") -> str:
77
"""Helper to register and get auth token"""
8-
await client.post(
9-
"/api/v1/auth/register",
10-
json={"email": "model@example.com", "password": "testpassword123"},
11-
)
128
response = await client.post(
13-
"/api/v1/auth/login",
14-
data={"username": "model@example.com", "password": "testpassword123"},
9+
"/api/v1/auth/register",
10+
json={"email": email, "password": "testpassword123"},
1511
)
16-
return response.json()["access_token"]
12+
return response.json()["data"]["access_token"]
1713

1814

1915
@pytest.mark.asyncio
2016
async def test_list_models_empty(client: AsyncClient):
2117
"""Test listing models when none exist"""
22-
token = await get_auth_token(client)
18+
token = await get_auth_token(client, "list@example.com")
2319
response = await client.get(
2420
"/api/v1/config/models",
2521
headers={"Authorization": f"Bearer {token}"},
2622
)
2723
assert response.status_code == 200
2824
data = response.json()
29-
assert data["code"] == 0
25+
assert data["success"] is True
3026
assert data["data"] == []
3127

3228

3329
@pytest.mark.asyncio
3430
async def test_create_model(client: AsyncClient):
3531
"""Test creating a model configuration"""
36-
token = await get_auth_token(client)
32+
token = await get_auth_token(client, "create@example.com")
3733
response = await client.post(
3834
"/api/v1/config/models",
3935
headers={"Authorization": f"Bearer {token}"},
@@ -47,7 +43,7 @@ async def test_create_model(client: AsyncClient):
4743
)
4844
assert response.status_code == 200
4945
data = response.json()
50-
assert data["code"] == 0
46+
assert data["success"] is True
5147
assert data["data"]["name"] == "GPT-4"
5248
assert data["data"]["provider"] == "openai"
5349
assert data["data"]["is_default"] is True
@@ -56,7 +52,7 @@ async def test_create_model(client: AsyncClient):
5652
@pytest.mark.asyncio
5753
async def test_update_model(client: AsyncClient):
5854
"""Test updating a model configuration"""
59-
token = await get_auth_token(client)
55+
token = await get_auth_token(client, "update@example.com")
6056
# Create model
6157
create_response = await client.post(
6258
"/api/v1/config/models",
@@ -89,7 +85,7 @@ async def test_update_model(client: AsyncClient):
8985
@pytest.mark.asyncio
9086
async def test_delete_model(client: AsyncClient):
9187
"""Test deleting a model configuration"""
92-
token = await get_auth_token(client)
88+
token = await get_auth_token(client, "delete@example.com")
9389
# Create model
9490
create_response = await client.post(
9591
"/api/v1/config/models",

0 commit comments

Comments
 (0)