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
12 changes: 6 additions & 6 deletions backend/app/storage/plan_session_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ def _deserialize(self, data: Dict[str, Any]) -> PlanSession:

def create(self, session: PlanSession) -> PlanSession:
path = self._path(session.id)
with open(path, 'w') as f:
with open(path, 'w', encoding='utf-8') as f:
json.dump(self._serialize(session), f, indent=2, default=str)
return session

def get(self, session_id: str) -> Optional[PlanSession]:
path = self._path(session_id)
if not path.exists():
return None
with open(path, 'r') as f:
with open(path, 'r', encoding='utf-8') as f:
return self._deserialize(json.load(f))

def update(self, session: PlanSession) -> PlanSession:
Expand All @@ -102,7 +102,7 @@ def list_all(self, status: Optional[PlanSessionStatus] = None) -> List[PlanSessi
sessions = []
for p in sorted(self.base_path.glob("psess_*.json"), reverse=True):
try:
with open(p, 'r') as f:
with open(p, 'r', encoding='utf-8') as f:
s = self._deserialize(json.load(f))
if status is None or s.status == status:
sessions.append(s)
Expand Down Expand Up @@ -136,22 +136,22 @@ def _deserialize(self, data: Dict[str, Any]) -> CandidatePlan:

def create(self, candidate: CandidatePlan) -> CandidatePlan:
path = self._path(candidate.id)
with open(path, 'w') as f:
with open(path, 'w', encoding='utf-8') as f:
json.dump(self._serialize(candidate), f, indent=2, default=str)
return candidate

def get(self, candidate_id: str) -> Optional[CandidatePlan]:
path = self._path(candidate_id)
if not path.exists():
return None
with open(path, 'r') as f:
with open(path, 'r', encoding='utf-8') as f:
return self._deserialize(json.load(f))

def list_by_session(self, session_id: str) -> List[CandidatePlan]:
candidates = []
for p in sorted(self.base_path.glob("cplan_*.json")):
try:
with open(p, 'r') as f:
with open(p, 'r', encoding='utf-8') as f:
c = self._deserialize(json.load(f))
if c.sessionId == session_id:
candidates.append(c)
Expand Down
81 changes: 81 additions & 0 deletions backend/tests/test_pr_04_plan_session_encoding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""Test that plan_session_storage.py handles non-ASCII content via encoding=utf-8."""

from datetime import datetime

import pytest

from app.models.plan_session import (
PlanSession,
PlanSessionConfig,
PlanSessionStatus,
CandidatePlan,
)
from app.storage.plan_session_storage import PlanSessionStorage, CandidatePlanStorage


@pytest.fixture
def tmp_data_dir(tmp_path):
return str(tmp_path)


def test_session_roundtrip_non_ascii(tmp_data_dir):
"""Create a PlanSession with non-ASCII config and verify round-trip."""
storage = PlanSessionStorage(tmp_data_dir)
config = PlanSessionConfig(
userNotes="优化模型性能并提高代码覆盖率",
paperType="algorithmic_method",
)
session = PlanSession(
id="psess_test001",
config=config,
status=PlanSessionStatus.PENDING,
createdAt=datetime.now(),
)

storage.create(session)
fetched = storage.get("psess_test001")

assert fetched is not None
assert fetched.config.userNotes == "优化模型性能并提高代码覆盖率"


def test_list_all_non_ascii(tmp_data_dir):
"""Verify list_all returns sessions with non-ASCII content correctly."""
storage = PlanSessionStorage(tmp_data_dir)

for i, note in enumerate(["First note", "Deuxième note", "第三笔记"]):
config = PlanSessionConfig(userNotes=note, paperType="algorithmic_method")
session = PlanSession(
id=f"psess_test{i:03d}",
config=config,
status=PlanSessionStatus.PENDING,
createdAt=datetime.now(),
)
storage.create(session)

sessions = storage.list_all()
assert len(sessions) == 3
notes = {s.config.userNotes for s in sessions}
assert "First note" in notes
assert "Deuxième note" in notes
assert "第三笔记" in notes


def test_candidate_roundtrip_non_ascii(tmp_data_dir):
"""Create a CandidatePlan with non-ASCII content and verify round-trip."""
storage = CandidatePlanStorage(tmp_data_dir)
candidate = CandidatePlan(
id="cplan_test001",
sessionId="psess_001",
indexNumber=1,
title="使用遗传算法优化超参数配置",
planAbstract="本文提出了一种新的方法",
createdAt=datetime.now(),
)

storage.create(candidate)
fetched = storage.get("cplan_test001")

assert fetched is not None
assert fetched.title == "使用遗传算法优化超参数配置"
assert fetched.planAbstract == "本文提出了一种新的方法"