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_self_composed_beginner_midis_are_seeded_as_public(self) -> None: names = { "starlight-steps-beginner.mid", "starlight-steps-easy.mid", "evening-waltz-beginner.mid", "sunrise-arpeggio-easy.mid", "black-key-pentatonic-beginner.mid", } 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()) if __name__ == "__main__": unittest.main()