"""U-002 tests: c_project_create's directory-scaffolding + DB-persistence logic. Run: python3 -m unittest src.test_project_scaffold -v """ import os import shutil import stat import subprocess import tempfile import unittest from src import project_scaffold, schema def _init_git_repo(path): subprocess.run(["git", "init"], cwd=path, check=True, capture_output=True) class ProjectScaffoldTestBase(unittest.TestCase): def setUp(self): self.conn = schema.connect(":memory:") schema.create_schema(self.conn) self.tmpdir = tempfile.mkdtemp(prefix="graphlemon-u002-") def tearDown(self): self.conn.close() shutil.rmtree(self.tmpdir, ignore_errors=True) class HappyPathTest(ProjectScaffoldTestBase): def test_existing_git_repo_gets_graphlemon_subdir_and_init_commit(self): _init_git_repo(self.tmpdir) result = project_scaffold.create_project(self.conn, self.tmpdir) self.assertTrue(result["has_git_repo"]) self.assertTrue(result["graphlemon_subdir_initialized"]) self.assertTrue(os.path.isdir(os.path.join(self.tmpdir, ".graphlemon"))) log = subprocess.run( ["git", "log", "--oneline"], cwd=self.tmpdir, check=True, capture_output=True, text=True ) self.assertIn("graphlemon: initialize .graphlemon/ workspace", log.stdout) row = self.conn.execute( "SELECT project_id, target_folder_path, has_git_repo, graphlemon_subdir_initialized, status " "FROM project WHERE project_id = ?", (result["project_id"],), ).fetchone() self.assertIsNotNone(row) self.assertEqual(row[1], self.tmpdir) self.assertEqual(row[2], 1) self.assertEqual(row[3], 1) self.assertEqual(row[4], "active") def test_project_name_defaults_to_folder_name(self): _init_git_repo(self.tmpdir) result = project_scaffold.create_project(self.conn, self.tmpdir) row = self.conn.execute( "SELECT name FROM project WHERE project_id = ?", (result["project_id"],) ).fetchone() self.assertEqual(row[0], os.path.basename(self.tmpdir)) def test_explicit_name_used_when_given(self): _init_git_repo(self.tmpdir) result = project_scaffold.create_project(self.conn, self.tmpdir, name="Snake Game") row = self.conn.execute( "SELECT name FROM project WHERE project_id = ?", (result["project_id"],) ).fetchone() self.assertEqual(row[0], "Snake Game") class NonGitRepoConfirmationFlowTest(ProjectScaffoldTestBase): """02-V §2.1 / 03-C §3.0: never silently `git init`.""" def test_declines_without_confirmation_and_leaves_folder_untouched(self): with self.assertRaises(project_scaffold.GitInitDeclinedError): project_scaffold.create_project(self.conn, self.tmpdir, init_git_if_missing=False) self.assertFalse(os.path.isdir(os.path.join(self.tmpdir, ".git"))) self.assertFalse(os.path.isdir(os.path.join(self.tmpdir, ".graphlemon"))) count = self.conn.execute("SELECT COUNT(*) FROM project").fetchone()[0] self.assertEqual(count, 0) def test_initializes_git_when_explicitly_confirmed(self): result = project_scaffold.create_project(self.conn, self.tmpdir, init_git_if_missing=True) self.assertTrue(result["has_git_repo"]) self.assertTrue(os.path.isdir(os.path.join(self.tmpdir, ".git"))) self.assertTrue(os.path.isdir(os.path.join(self.tmpdir, ".graphlemon"))) class IdempotentCreationTest(ProjectScaffoldTestBase): def test_second_call_returns_existing_record_without_new_commit(self): _init_git_repo(self.tmpdir) first = project_scaffold.create_project(self.conn, self.tmpdir) second = project_scaffold.create_project(self.conn, self.tmpdir) self.assertEqual(first["project_id"], second["project_id"]) count = self.conn.execute("SELECT COUNT(*) FROM project").fetchone()[0] self.assertEqual(count, 1) log = subprocess.run( ["git", "log", "--oneline"], cwd=self.tmpdir, check=True, capture_output=True, text=True ) self.assertEqual(len(log.stdout.strip().splitlines()), 1) def test_idempotent_hit_skips_path_validation(self): # REQ-11 failure case: repeat calls on an already-registered path must # return the existing record even if the path itself is no longer # valid, not re-validate and error. _init_git_repo(self.tmpdir) first = project_scaffold.create_project(self.conn, self.tmpdir) shutil.rmtree(self.tmpdir) second = project_scaffold.create_project(self.conn, self.tmpdir) self.assertEqual(first["project_id"], second["project_id"]) class PathValidationErrorsTest(ProjectScaffoldTestBase): def test_nonexistent_path_raises_path_not_found(self): missing = os.path.join(self.tmpdir, "does-not-exist") with self.assertRaises(project_scaffold.PathNotFoundError): project_scaffold.create_project(self.conn, missing) def test_unwritable_path_raises_path_not_writable(self): os.chmod(self.tmpdir, stat.S_IRUSR | stat.S_IXUSR) try: if os.access(self.tmpdir, os.W_OK): self.skipTest("running as a user that bypasses permission bits (e.g. root)") with self.assertRaises(project_scaffold.PathNotWritableError): project_scaffold.create_project(self.conn, self.tmpdir) finally: os.chmod(self.tmpdir, stat.S_IRWXU) if __name__ == "__main__": unittest.main()