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()