CI / Frontend - type-check & test (push) Canceled after 0s
비슷한 임시 연습곡 다섯 개를 교체했다. 부르크뮐러와 슈만 원본 30곡의 출처와 권리 정보를 보존했다. 각 MIDI에 양손 연습용 도입부와 마무리를 넣고 난이도를 음표 밀도로 분류했다. MIDI 구조 검사 30개와 서버 격리 테스트 5개를 통과했다. 운영 백업: /home/syskiller/piano/backups/bach-to-basics/20261009-practice-30-intro-ending. 백엔드만 운영에 반영했고 HTTPS 공개 목록과 30개 다운로드를 확인했다. 회원 업로드 2건은 보존됐다. 실제 건반 연주감 검사는 남아 있다.
79 lines
3.3 KiB
Python
79 lines
3.3 KiB
Python
from __future__ import annotations
|
|
|
|
import importlib
|
|
import os
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
|
|
class LibraryDatabaseTest(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.temp = tempfile.TemporaryDirectory()
|
|
os.environ["LIBRARY_DATA_DIR"] = self.temp.name
|
|
os.environ.pop("BOOTSTRAP_ADMIN_USERNAME", None)
|
|
os.environ.pop("BOOTSTRAP_ADMIN_PASSWORD_FILE", None)
|
|
import library_db
|
|
|
|
self.db = importlib.reload(library_db)
|
|
self.db.initialize_database()
|
|
|
|
def tearDown(self) -> None:
|
|
self.temp.cleanup()
|
|
|
|
def test_password_hash_is_salted_and_verifiable(self) -> None:
|
|
first = self.db.hash_password("safe-password-123")
|
|
second = self.db.hash_password("safe-password-123")
|
|
self.assertNotEqual(first, second)
|
|
self.assertTrue(self.db.verify_password("safe-password-123", first))
|
|
self.assertFalse(self.db.verify_password("wrong-password", first))
|
|
|
|
def test_session_round_trip(self) -> None:
|
|
with self.db.connect() as connection:
|
|
cursor = connection.execute(
|
|
"""
|
|
INSERT INTO users (username, display_name, password_hash, created_at)
|
|
VALUES ('tester', '테스터', ?, ?)
|
|
""",
|
|
(self.db.hash_password("safe-password-123"), self.db.utc_now()),
|
|
)
|
|
user_id = cursor.lastrowid
|
|
token, csrf = self.db.create_session(user_id)
|
|
user = self.db.get_user_for_session(token)
|
|
self.assertIsNotNone(user)
|
|
self.assertEqual(user["username"], "tester")
|
|
self.assertEqual(user["csrf_token"], csrf)
|
|
self.db.delete_session(token)
|
|
self.assertIsNone(self.db.get_user_for_session(token))
|
|
|
|
def test_database_directory_is_private_project_data(self) -> None:
|
|
self.assertTrue(Path(self.temp.name, "library.sqlite3").is_file())
|
|
self.assertTrue(Path(self.temp.name, "midi").is_dir())
|
|
|
|
def test_graded_samples_are_two_hand_piano_solos(self) -> None:
|
|
for sample in self.db.SAMPLES:
|
|
self.assertEqual(sample["arrangement"], "piano_solo")
|
|
self.assertIn("양손", sample["tags"])
|
|
self.assertNotIn("오른손", sample["tags"])
|
|
self.assertNotIn("쉬운 반주", sample["tags"])
|
|
|
|
def test_practice_collection_is_seeded_as_public(self) -> None:
|
|
names = {f"practice-burgmuller-{number:02}.mid" for number in range(1, 19)}
|
|
names.update(f"practice-schumann-{number:02}.mid" for number in range(1, 13))
|
|
with self.db.connect() as connection:
|
|
rows = connection.execute(
|
|
"SELECT seed_key, visibility, min_tier, owner_id FROM midi_items WHERE seed_key IS NOT NULL"
|
|
).fetchall()
|
|
matching = {row["seed_key"]: row for row in rows if row["seed_key"] in names}
|
|
self.assertEqual(set(matching), names)
|
|
for name, row in matching.items():
|
|
self.assertEqual(row["visibility"], "public")
|
|
self.assertEqual(row["min_tier"], 0)
|
|
self.assertIsNone(row["owner_id"])
|
|
self.assertTrue(Path(self.temp.name, "midi", name).is_file())
|
|
self.assertFalse(any(row["seed_key"] in self.db.LEGACY_SAMPLE_KEYS for row in rows))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|