05e173b293
Replace the WebView shell with SwiftUI screens, add account-scoped Wiki and TTS APIs with adaptive review and photo scan support, and keep web/iOS pages usable while data loads asynchronously. Co-authored-by: Cursor <cursoragent@cursor.com>
139 lines
4.4 KiB
Python
139 lines
4.4 KiB
Python
import unittest
|
|
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import sessionmaker
|
|
|
|
from database import Base
|
|
from models import QuizRecord, User, UserSettings, WikiPage, WikiSource, Word
|
|
from services.wiki_service import wiki_service
|
|
|
|
|
|
class WikiServiceTest(unittest.TestCase):
|
|
def setUp(self):
|
|
engine = create_engine("sqlite:///:memory:")
|
|
Base.metadata.create_all(engine)
|
|
self.db = sessionmaker(bind=engine)()
|
|
|
|
def tearDown(self):
|
|
self.db.close()
|
|
|
|
def create_user(self, username: str) -> User:
|
|
user = User(
|
|
username=username,
|
|
password_hash="test",
|
|
created_at="2026-06-06T00:00:00Z",
|
|
)
|
|
self.db.add(user)
|
|
self.db.flush()
|
|
self.db.add(
|
|
UserSettings(
|
|
user_id=user.id,
|
|
ai_wiki_enabled=1,
|
|
ai_wiki_auto_organize=1,
|
|
)
|
|
)
|
|
self.db.commit()
|
|
return user
|
|
|
|
def create_word(self, user: User, text: str) -> Word:
|
|
word = Word(
|
|
user_id=user.id,
|
|
book_id=0,
|
|
source_text=text,
|
|
target_text="测试",
|
|
source_lang="en",
|
|
target_lang="zh",
|
|
status="learning",
|
|
correct_count=1,
|
|
wrong_count=0,
|
|
consecutive_correct_count=1,
|
|
mastery_score=100,
|
|
train_count=1,
|
|
total_train_seconds=3,
|
|
difficulty=5.0,
|
|
stability_hours=24.0,
|
|
created_at="2026-06-06T00:00:00Z",
|
|
)
|
|
self.db.add(word)
|
|
self.db.commit()
|
|
return word
|
|
|
|
def test_ingest_builds_private_markdown_page(self):
|
|
user = self.create_user("alice")
|
|
word = self.create_word(user, "resilient")
|
|
wiki_service.ingest_word(self.db, user, word)
|
|
|
|
page = (
|
|
self.db.query(WikiPage)
|
|
.filter(WikiPage.user_id == user.id, WikiPage.category == "word")
|
|
.one()
|
|
)
|
|
self.assertIn("# resilient", page.content)
|
|
self.assertIn("WordLoop 原始学习事件 1 条", page.content)
|
|
self.assertEqual(
|
|
self.db.query(WikiSource).filter(WikiSource.user_id == user.id).count(),
|
|
1,
|
|
)
|
|
|
|
def test_quiz_ingest_updates_only_own_wiki(self):
|
|
alice = self.create_user("alice")
|
|
bob = self.create_user("bob")
|
|
word = self.create_word(alice, "resilient")
|
|
wiki_service.ingest_word(self.db, alice, word)
|
|
record = QuizRecord(
|
|
user_id=alice.id,
|
|
word_id=word.id,
|
|
question_type="spell",
|
|
user_answer="resilient",
|
|
correct_answer="resilient",
|
|
is_correct=1,
|
|
duration_seconds=2,
|
|
created_at="2026-06-06T00:01:00Z",
|
|
)
|
|
self.db.add(record)
|
|
self.db.commit()
|
|
wiki_service.ingest_quiz(self.db, alice, word, record)
|
|
|
|
alice_status = wiki_service.status(self.db, alice)
|
|
bob_status = wiki_service.status(self.db, bob)
|
|
self.assertGreater(alice_status["page_count"], 0)
|
|
self.assertEqual(alice_status["latest_page"]["source_count"], 2)
|
|
self.assertEqual(bob_status["page_count"], 0)
|
|
self.assertIsNone(bob_status["latest_page"])
|
|
|
|
def test_recompile_prunes_orphan_status_collection(self):
|
|
user = self.create_user("alice")
|
|
word = self.create_word(user, "resilient")
|
|
word.status = "new"
|
|
self.db.commit()
|
|
wiki_service.ingest_word(self.db, user, word)
|
|
|
|
word.status = "learning"
|
|
self.db.commit()
|
|
wiki_service.rebuild(self.db, user)
|
|
|
|
slugs = {
|
|
page.slug
|
|
for page in self.db.query(WikiPage).filter(WikiPage.user_id == user.id).all()
|
|
}
|
|
self.assertNotIn("learning/new", slugs)
|
|
self.assertIn("learning/learning", slugs)
|
|
|
|
def test_graph_returns_wiki_pages_and_links(self):
|
|
user = self.create_user("alice")
|
|
word = self.create_word(user, "resilient")
|
|
wiki_service.ingest_word(self.db, user, word)
|
|
|
|
graph = wiki_service.graph(self.db, user)
|
|
|
|
node_slugs = {node["slug"] for node in graph["nodes"]}
|
|
relations = {link["relation"] for link in graph["links"]}
|
|
self.assertIn("index", node_slugs)
|
|
self.assertIn(f"vocabulary/{word.id}-resilient", node_slugs)
|
|
self.assertIn("learning_status", relations)
|
|
self.assertIn("collection", relations)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|