Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions backend/app/storage/review_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def get_review(review_id: str) -> Optional[Dict[str, Any]]:
path = os.path.join(REVIEWS_DIR, review_id, "meta.json")
if not os.path.isfile(path):
return None
with open(path) as f:
with open(path, encoding="utf-8") as f:
return json.load(f)


Expand All @@ -88,7 +88,7 @@ def list_reviews(paper_id: Optional[str] = None) -> List[Dict[str, Any]]:
meta = os.path.join(REVIEWS_DIR, name, "meta.json")
if os.path.isfile(meta):
try:
with open(meta) as f:
with open(meta, encoding="utf-8") as f:
data = json.load(f)
if paper_id and data.get("paperId") != paper_id:
continue
Expand All @@ -101,7 +101,7 @@ def list_reviews(paper_id: Optional[str] = None) -> List[Dict[str, Any]]:
def _save_record(review_id: str, record: Dict):
review_dir = os.path.join(REVIEWS_DIR, review_id)
os.makedirs(review_dir, exist_ok=True)
with open(os.path.join(review_dir, "meta.json"), "w") as f:
with open(os.path.join(review_dir, "meta.json"), "w", encoding="utf-8") as f:
json.dump(record, f, indent=2, default=str)


Expand Down Expand Up @@ -140,7 +140,7 @@ def create_improvement_request(data: Dict[str, Any]) -> Dict[str, Any]:
}
req_dir = os.path.join(IMPROVEMENT_REQUESTS_DIR, req_id)
os.makedirs(req_dir, exist_ok=True)
with open(os.path.join(req_dir, "meta.json"), "w") as f:
with open(os.path.join(req_dir, "meta.json"), "w", encoding="utf-8") as f:
json.dump(record, f, indent=2, default=str)

# Index under review
Expand All @@ -149,10 +149,10 @@ def create_improvement_request(data: Dict[str, Any]) -> Dict[str, Any]:
idx_path = os.path.join(review_dir, "requests.json")
idx = []
if os.path.isfile(idx_path):
with open(idx_path) as f:
with open(idx_path, encoding="utf-8") as f:
idx = json.load(f)
idx.append(record)
with open(idx_path, "w") as f:
with open(idx_path, "w", encoding="utf-8") as f:
json.dump(idx, f, indent=2, default=str)

return record
Expand All @@ -166,7 +166,7 @@ def list_improvement_requests(review_id: Optional[str] = None, paper_id: Optiona
meta = os.path.join(IMPROVEMENT_REQUESTS_DIR, name, "meta.json")
if os.path.isfile(meta):
try:
with open(meta) as f:
with open(meta, encoding="utf-8") as f:
data = json.load(f)
if review_id and data.get("reviewId") != review_id:
continue
Expand All @@ -184,7 +184,7 @@ def get_improvement_request(req_id: str) -> Optional[Dict[str, Any]]:
path = os.path.join(IMPROVEMENT_REQUESTS_DIR, req_id, "meta.json")
if not os.path.isfile(path):
return None
with open(path) as f:
with open(path, encoding="utf-8") as f:
return json.load(f)


Expand All @@ -195,6 +195,6 @@ def update_improvement_request(req_id: str, updates: Dict[str, Any]) -> Optional
record.update(updates)
record["updatedAt"] = _utcnow_iso()
req_dir = os.path.join(IMPROVEMENT_REQUESTS_DIR, req_id)
with open(os.path.join(req_dir, "meta.json"), "w") as f:
with open(os.path.join(req_dir, "meta.json"), "w", encoding="utf-8") as f:
json.dump(record, f, indent=2, default=str)
return record
94 changes: 94 additions & 0 deletions backend/tests/test_pr_02_review_encoding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"""Test that review_storage.py handles non-ASCII content via encoding=utf-8."""

import json
import os
import tempfile
import shutil
from unittest import mock

import pytest


@pytest.fixture
def tmp_reviews_dir(tmp_path):
d = tmp_path / "reviews"
d.mkdir()
return str(d)


@pytest.fixture
def tmp_impr_dir(tmp_path):
d = tmp_path / "improvement_requests"
d.mkdir()
return str(d)


def test_review_roundtrip_non_ascii(tmp_reviews_dir, tmp_impr_dir):
"""Create a review with Chinese characters and verify round-trip integrity."""
with mock.patch("app.storage.review_storage.REVIEWS_DIR", tmp_reviews_dir), \
mock.patch("app.storage.review_storage.IMPROVEMENT_REQUESTS_DIR", tmp_impr_dir):
from app.storage import review_storage

data = {
"paperId": "paper_test",
"reviewerProfile": "senior_reviewer",
"providerName": "moonshot",
"model": "moonshot-v1-8k",
"reviewKind": "standard",
"budgetMode": "balanced",
"ablationMode": "full",
}
record = review_storage.create_review(data)
review_id = record["id"]

# Update with non-ASCII content
review_storage.update_review(review_id, {
"markdownReport": "# 审阅报告\n\n这是一个测试。",
"findings": [{"text": "发现了严重的逻辑错误"}],
})

# Read back
fetched = review_storage.get_review(review_id)
assert fetched is not None
assert "审阅报告" in fetched["markdownReport"]
assert "发现了严重的逻辑错误" in fetched["findings"][0]["text"]


def test_list_reviews_non_ascii(tmp_reviews_dir, tmp_impr_dir):
"""Verify list_reviews returns non-ASCII content correctly."""
with mock.patch("app.storage.review_storage.REVIEWS_DIR", tmp_reviews_dir), \
mock.patch("app.storage.review_storage.IMPROVEMENT_REQUESTS_DIR", tmp_impr_dir):
from app.storage import review_storage

data = {"paperId": "paper_unicode"}
record = review_storage.create_review(data)
review_storage.update_review(record["id"], {
"markdownReport": "éèê üöä 世界"
})

reviews = review_storage.list_reviews(paper_id="paper_unicode")
assert len(reviews) == 1
assert "éèê" in reviews[0]["markdownReport"]
assert "世界" in reviews[0]["markdownReport"]


def test_improvement_request_non_ascii(tmp_reviews_dir, tmp_impr_dir):
"""Create an improvement request with non-ASCII fields and verify round-trip."""
with mock.patch("app.storage.review_storage.REVIEWS_DIR", tmp_reviews_dir), \
mock.patch("app.storage.review_storage.IMPROVEMENT_REQUESTS_DIR", tmp_impr_dir):
from app.storage import review_storage

# First create a review so the index dir exists
review_record = review_storage.create_review({"paperId": "p1"})

req = review_storage.create_improvement_request({
"reviewId": review_record["id"],
"paperId": "p1",
"description": "改善建议:添加更多的测试用例",
"suggestedEdit": "Replace with: Überprüfen Sie die Eingabe",
})

fetched = review_storage.get_improvement_request(req["id"])
assert fetched is not None
assert "改善建议" in fetched["description"]
assert "Überprüfen" in fetched["suggestedEdit"]