import io import json import os import unittest from unittest.mock import patch from app.utils.storage import DomainStorage class GuidanceStorageAnalyzeTest(unittest.TestCase): def setUp(self): self.storage_path = os.path.join(os.getcwd(), "data", "test-domains-storage.json") self.storage = DomainStorage(self.storage_path) self.storage._write({ "domains": [ { "token": "token-1", "domain": "\u8fc7\u5ea6\u8d1f\u503a", "note": "", "created_at": "2026-04-20T00:00:00", }, { "token": "token-2", "domain": "\u865a\u5047\u8d38\u6613", "note": "", "created_at": "2026-04-20T00:00:00", }, ], }) def tearDown(self): if os.path.exists(self.storage_path): os.remove(self.storage_path) def test_upload_only_saves_file_metadata(self): record = self.storage.save_guidance_file( "token-1", io.BytesIO("\u8d44\u4ea7\u8d1f\u503a\u7387\u9884\u8b66\u3002".encode()), "guide.txt", ) self.assertIn("file_id", record) self.assertNotIn("guidance_analysis", record) def test_analyze_guidance_writes_analysis_after_upload(self): record = self.storage.save_guidance_file( "token-1", io.BytesIO("\u8d44\u4ea7\u8d1f\u503a\u7387\u9884\u8b66\u3002".encode()), "guide.txt", ) fake_analysis = { "status": "done", "granularity": "low", "description_patterns": [{"description_pattern": "\u8d44\u4ea7\u8d1f\u503a\u7387\u9884\u8b66"}], } with patch.object(DomainStorage, "_analyze_guidance_file", return_value=fake_analysis) as mocked: results = self.storage.analyze_guidance( analysis_options={"granularity": "low"}, ) self.assertEqual(results[0]["guidance_analysis"], fake_analysis) mocked.assert_called_once() self.assertEqual(results[0]["file_id"], record["file_id"]) with open(self.storage_path, encoding="utf-8") as f: data = json.load(f) saved = data["domains"][0]["guidance_files"][0] self.assertEqual(saved["guidance_analysis"]["granularity"], "low") def test_analyze_guidance_filters_by_token_id(self): self.storage.save_guidance_file("token-1", io.BytesIO(b"debt"), "debt.txt") second = self.storage.save_guidance_file("token-2", io.BytesIO(b"trade"), "trade.txt") fake_analysis = {"status": "done", "granularity": "high", "description_patterns": []} with patch.object(DomainStorage, "_analyze_guidance_file", return_value=fake_analysis) as mocked: results = self.storage.analyze_guidance(token_id="token-2") self.assertEqual([item["file_id"] for item in results], [second["file_id"]]) mocked.assert_called_once() with open(self.storage_path, encoding="utf-8") as f: data = json.load(f) self.assertNotIn("guidance_analysis", data["domains"][0]["guidance_files"][0]) self.assertEqual(data["domains"][1]["guidance_files"][0]["file_id"], second["file_id"]) def test_analyze_guidance_without_token_analyzes_all_files(self): self.storage.save_guidance_file("token-1", io.BytesIO(b"debt"), "debt.txt") self.storage.save_guidance_file("token-2", io.BytesIO(b"trade"), "trade.txt") fake_analysis = {"status": "done", "granularity": "high", "description_patterns": []} with patch.object(DomainStorage, "_analyze_guidance_file", return_value=fake_analysis) as mocked: results = self.storage.analyze_guidance() self.assertEqual(len(results), 2) self.assertEqual(mocked.call_count, 2) def test_analyze_guidance_rejects_unknown_token_id(self): with self.assertRaisesRegex(ValueError, "token_id=missing"): self.storage.analyze_guidance(token_id="missing") def test_analyze_guidance_rejects_unknown_granularity(self): with self.assertRaisesRegex(ValueError, "granularity"): self.storage.analyze_guidance(analysis_options={"granularity": "middle"}) if __name__ == "__main__": unittest.main()