feat: store packaged user data under data directory

This commit is contained in:
chengma
2026-07-07 14:12:32 +08:00
parent caa04a9aa7
commit 148c4a73eb
33 changed files with 602 additions and 196 deletions
+115 -1
View File
@@ -18,7 +18,13 @@ class AppConfigTests(TempDirMixin, unittest.TestCase):
config = appconfig.load_config(config_path)
self.assertTrue(os.path.exists(config_path))
self.assertEqual("images", appconfig.image_dir(config))
self.assertEqual(os.path.join(temp_dir, "images"), appconfig.image_dir(config))
self.assertEqual(os.path.join(temp_dir, "chrome_user_data_dir"), appconfig.user_data_root(config))
self.assertEqual(os.path.join(temp_dir, "cmshopee.db"), appconfig.db_path(config))
self.assertEqual(temp_dir, appconfig.data_dir(config))
self.assertEqual(config_path, config["config_path"])
self.assertEqual(os.path.join(temp_dir, "config", "ai_models.json"), config["ai_models_path"])
self.assertEqual(os.path.join(temp_dir, "config", "cmhub.json"), config["cmhub_config_path"])
self.assertEqual(240, appconfig.response_timeout(config))
self.assertFalse(appconfig.ai_config(config)["generate_cover"])
@@ -31,6 +37,114 @@ class AppConfigTests(TempDirMixin, unittest.TestCase):
self.assert_removed(temp_dir)
def test_config_save_does_not_persist_runtime_paths(self):
with self.make_temp_dir() as temp_dir:
config_path = os.path.join(temp_dir, "config.json")
saved = appconfig.save_config(
{
"data_dir": os.path.join(temp_dir, "data"),
"config_path": "runtime-only",
"ai_models_path": "runtime-only",
"cmhub_config_path": "runtime-only",
"image_dir": "images",
},
path=config_path,
)
self.assertEqual(temp_dir, saved["data_dir"])
with open(config_path, "r", encoding="utf-8") as fh:
persisted = json.load(fh)
self.assertNotIn("data_dir", persisted)
self.assertNotIn("config_path", persisted)
self.assertNotIn("ai_models_path", persisted)
self.assertNotIn("cmhub_config_path", persisted)
self.assert_removed(temp_dir)
def test_data_paths_resolve_under_default_data_dir(self):
with self.make_temp_dir() as temp_dir:
data_root = os.path.join(temp_dir, "data")
cfg = {
"data_dir": data_root,
"user_data_root": "chrome_user_data_dir",
"image_dir": "images",
"db_path": "cmshopee.db",
}
self.assertEqual(os.path.join(data_root, "chrome_user_data_dir"), appconfig.user_data_root(cfg))
self.assertEqual(os.path.join(data_root, "images"), appconfig.image_dir(cfg))
self.assertEqual(os.path.join(data_root, "cmshopee.db"), appconfig.db_path(cfg))
self.assertEqual(os.path.join(data_root, "title_prompt.txt"), appconfig.title_prompt_path(cfg))
self.assertEqual(os.path.join(data_root, "prompts", "cover"), appconfig.cover_prompts_dir(cfg))
self.assertEqual(os.path.join(data_root, "logs"), appconfig.diagnostic_log_dir(cfg))
portable = dict(cfg)
portable["db_path"] = os.path.join("data", "cmshopee.db")
self.assertEqual(os.path.join(data_root, "cmshopee.db"), appconfig.db_path(portable))
self.assert_removed(temp_dir)
def test_prepare_data_dir_migrates_legacy_layout(self):
with self.make_temp_dir() as temp_dir:
with open(os.path.join(temp_dir, "config.json"), "w", encoding="utf-8") as fh:
fh.write("{}")
with open(os.path.join(temp_dir, "cmshopee.db"), "w", encoding="utf-8") as fh:
fh.write("db")
os.makedirs(os.path.join(temp_dir, "images"), exist_ok=True)
with open(os.path.join(temp_dir, "images", "cover.jpg"), "w", encoding="utf-8") as fh:
fh.write("image")
os.makedirs(os.path.join(temp_dir, "config"), exist_ok=True)
with open(os.path.join(temp_dir, "config", "cmhub.json"), "w", encoding="utf-8") as fh:
fh.write("{}")
data_root = os.path.join(temp_dir, "data")
prepared = appconfig.prepare_data_dir(base_dir=temp_dir, data_dir_path=data_root)
self.assertEqual(data_root, prepared)
self.assertTrue(os.path.exists(os.path.join(data_root, "config.json")))
self.assertTrue(os.path.exists(os.path.join(data_root, "cmshopee.db")))
self.assertTrue(os.path.exists(os.path.join(data_root, "images", "cover.jpg")))
self.assertTrue(os.path.exists(os.path.join(data_root, "config", "cmhub.json")))
self.assertFalse(os.path.exists(os.path.join(temp_dir, "config.json")))
self.assertFalse(os.path.exists(os.path.join(temp_dir, "images")))
self.assert_removed(temp_dir)
def test_prepare_data_dir_blocks_conflicting_migration(self):
with self.make_temp_dir() as temp_dir:
with open(os.path.join(temp_dir, "config.json"), "w", encoding="utf-8") as fh:
fh.write("{}")
data_root = os.path.join(temp_dir, "data")
os.makedirs(data_root, exist_ok=True)
with open(os.path.join(data_root, "config.json"), "w", encoding="utf-8") as fh:
fh.write("{}")
with self.assertRaises(appconfig.DataMigrationConflictError) as ctx:
appconfig.prepare_data_dir(base_dir=temp_dir, data_dir_path=data_root)
self.assertIn("无法自动迁移", str(ctx.exception))
self.assertTrue(os.path.exists(os.path.join(temp_dir, "config.json")))
self.assert_removed(temp_dir)
def test_prepare_data_dir_reports_unwritable_path(self):
with self.make_temp_dir() as temp_dir:
data_root = os.path.join(temp_dir, "data")
with open(data_root, "w", encoding="utf-8") as fh:
fh.write("not a directory")
with self.assertRaises(appconfig.DataDirectoryWriteError) as ctx:
appconfig.prepare_data_dir(
base_dir=temp_dir,
data_dir_path=data_root,
migrate=False,
)
self.assertIn("数据目录不可写", str(ctx.exception))
self.assert_removed(temp_dir)
def test_cmhub_defaults_old_config_and_key_helper(self):
with self.make_temp_dir() as temp_dir:
config_path = os.path.join(temp_dir, "config.json")