| 256 | |
| 257 | @classmethod |
| 258 | def setUpClass(cls): |
| 259 | # Redirect DB_PATH + projects dirs to a tempdir so /api/rescan |
| 260 | # writes to a throwaway DB and scans a throwaway transcript dir |
| 261 | # instead of the user's real ~/.claude/usage.db and transcripts. |
| 262 | import dashboard as _d |
| 263 | import scanner as _s |
| 264 | cls._tmpdir = tempfile.TemporaryDirectory() |
| 265 | tmp = Path(cls._tmpdir.name) |
| 266 | tmp_projects = tmp / "projects" |
| 267 | tmp_projects.mkdir() |
| 268 | cls._patches = { |
| 269 | (_d, "DB_PATH"): (_d.DB_PATH, tmp / "usage.db"), |
| 270 | (_s, "DB_PATH"): (_s.DB_PATH, tmp / "usage.db"), |
| 271 | (_s, "PROJECTS_DIR"): (_s.PROJECTS_DIR, tmp_projects), |
| 272 | (_s, "DEFAULT_PROJECTS_DIRS"): (_s.DEFAULT_PROJECTS_DIRS, [tmp_projects]), |
| 273 | } |
| 274 | for (mod, name), (_orig, new) in cls._patches.items(): |
| 275 | setattr(mod, name, new) |
| 276 | |
| 277 | cls.server = HTTPServer(("127.0.0.1", 0), DashboardHandler) |
| 278 | cls.port = cls.server.server_address[1] |
| 279 | cls.thread = threading.Thread(target=cls.server.serve_forever) |
| 280 | cls.thread.daemon = True |
| 281 | cls.thread.start() |
| 282 | |
| 283 | @classmethod |
| 284 | def tearDownClass(cls): |