import json import tempfile import unittest from pathlib import Path from types import SimpleNamespace import admin_service as admin import classroom_service as classroom class AdminServiceTests(unittest.TestCase): def setUp(self): self.temp_dir = tempfile.TemporaryDirectory() self.root = Path(self.temp_dir.name) self.data = self.root / "classroom_data" self.templates = self.data / "templates" self.projects = self.root / "projects" self.templates.mkdir(parents=True) self.projects.mkdir() (self.data / "course.json").write_text(json.dumps({ "title": "測試課程", "weeks": [{ "id": "week02", "week": 2, "title": "舊標題", "project": "舊專題", "output": "舊產出", "template": False, }], }, ensure_ascii=False), encoding="utf-8") (self.data / "classes.json").write_text(json.dumps([{"code": "L6A", "name": "測試班", "students": []}], ensure_ascii=False), encoding="utf-8") self.old = (classroom.DATA, classroom.TEMPLATES, classroom.CHECKPOINTS, classroom.VERSIONS, classroom.PASSWORD, classroom.RUNTIME, classroom.DB, admin.ROOT, admin.PROJECTS, admin.commit_changes) classroom.DATA = self.data classroom.TEMPLATES = self.templates classroom.CHECKPOINTS = self.data / "checkpoints" classroom.VERSIONS = self.data / "versions" classroom.PASSWORD = "teacher-test" classroom.RUNTIME = self.root / "runtime" classroom.DB = classroom.RUNTIME / "classroom.sqlite3" classroom.init_db() admin.ROOT = self.root admin.PROJECTS = self.projects self.commits = [] def fake_commit(additions, deletions, message): self.commits.append((additions, deletions, message)) return SimpleNamespace(commit_url="https://example.test/commit") admin.commit_changes = fake_commit def tearDown(self): classroom.DATA, classroom.TEMPLATES, classroom.CHECKPOINTS, classroom.VERSIONS, classroom.PASSWORD, classroom.RUNTIME, classroom.DB, admin.ROOT, admin.PROJECTS, admin.commit_changes = self.old self.temp_dir.cleanup() def test_teacher_updates_week_and_template_files(self): request = admin.WeekSave( password="teacher-test", week_id="week02", title="新標題", project="新專題", output="新產出", files={"main.py": 'print("ok")\n', "data/case.json": "{}\n"}, ) result = admin.save_week(request) course = json.loads((self.data / "course.json").read_text(encoding="utf-8")) self.assertTrue(result["ok"]) self.assertEqual(course["weeks"][0]["title"], "新標題") self.assertTrue(course["weeks"][0]["template"]) self.assertEqual((self.templates / "week02" / "main.py").read_text(), 'print("ok")\n') self.assertEqual(len(self.commits), 1) def test_teacher_creates_renames_and_deletes_demo_project(self): created = admin.save_demo(admin.DemoSave( password="teacher-test", class_name="進階班", student_name="Alex", project_name="互動故事", files={"main.py": 'print("start")\n'}, )) original = admin.ProjectRef(**created["project"]) project_root = self.projects / "進階班" / "Alex" / "互動故事" (project_root / "picture.png").write_bytes(b"png") renamed = admin.save_demo(admin.DemoSave( password="teacher-test", original=original, class_name="進階班", student_name="Alex", project_name="互動故事完整版", files={"main.py": 'print("done")\n'}, )) renamed_root = self.projects / "進階班" / "Alex" / "互動故事完整版" self.assertFalse(project_root.exists()) self.assertEqual((renamed_root / "main.py").read_text(), 'print("done")\n') self.assertEqual((renamed_root / "picture.png").read_bytes(), b"png") admin.delete_demo(admin.DemoRequest( password="teacher-test", project=admin.ProjectRef(**renamed["project"]) )) self.assertFalse(renamed_root.exists()) self.assertEqual(len(self.commits), 3) def test_teacher_backs_up_and_manages_submitted_content(self): state = classroom.empty("L6A", "alex", "Alex", "week02") state["project_name"] = "謎案" state["files"] = {"main.py": 'print("saved")\n'} state["final_files"] = dict(state["files"]) state["submitted_at"] = 123456789 classroom.save(state) result = admin.backup_submissions(admin.ClassroomBackupRequest(password="teacher-test", class_code="L6A", week_id="week02")) saved = self.data / "submissions" / "L6A" / "alex" / "week02.json" self.assertEqual(result["count"], 1) self.assertTrue(saved.is_file()) detail = admin.admin_submission(admin.ClassroomSubmissionRequest(password="teacher-test", class_code="L6A", student_id="alex", week_id="week02")) self.assertIn("saved", detail["submission"]["files"]["main.py"]) admin.delete_submission(admin.ClassroomSubmissionRequest(password="teacher-test", class_code="L6A", student_id="alex", week_id="week02")) self.assertFalse(saved.exists()) self.assertEqual(classroom.submission_catalog(), []) if __name__ == "__main__": unittest.main()