|
9 | 9 | # pylint: disable=attribute-defined-outside-init |
10 | 10 |
|
11 | 11 | import asyncio |
| 12 | +import os |
12 | 13 |
|
13 | 14 | from web import WebInterface |
14 | 15 | from web_helper import get_plan_renderer_js |
@@ -346,3 +347,80 @@ def run_currency_unit_tests(my_predbat, web): |
346 | 347 | my_predbat.num_cars = original_num_cars |
347 | 348 |
|
348 | 349 | return failed |
| 350 | + |
| 351 | + |
| 352 | +class FakeImageRequest: |
| 353 | + """A minimal aiohttp-request stand-in exposing only ``match_info``.""" |
| 354 | + |
| 355 | + def __init__(self, filename=None): |
| 356 | + """Store the route parameter a handler will read.""" |
| 357 | + self.match_info = {} if filename is None else {"filename": filename} |
| 358 | + |
| 359 | + |
| 360 | +def run_web_logo_image_tests(my_predbat): |
| 361 | + """Unit tests for the local logo image route (issue #4562). |
| 362 | +
|
| 363 | + The Predbat logo used to be fetched from raw.githubusercontent.com on every page |
| 364 | + load, so a GitHub outage or rate limit left the dashboard hanging for ~15s. The |
| 365 | + images now ship alongside the app and are served locally, with no network |
| 366 | + dependency at all. |
| 367 | + """ |
| 368 | + failed = 0 |
| 369 | + print("**** Running web logo image tests ****") |
| 370 | + |
| 371 | + web = make_web(my_predbat) |
| 372 | + |
| 373 | + print("Test: each bundled logo file is served with the right content type") |
| 374 | + expected_content_types = { |
| 375 | + "bat_logo.svg": "image/svg+xml", |
| 376 | + "bat_logo_light.png": "image/png", |
| 377 | + "bat_logo_dark.png": "image/png", |
| 378 | + } |
| 379 | + for filename, content_type in expected_content_types.items(): |
| 380 | + response = asyncio.run(web.html_logo_image(FakeImageRequest(filename))) |
| 381 | + if response.status != 200: |
| 382 | + print(f" ERROR: {filename} should serve with status 200, got {response.status}") |
| 383 | + failed += 1 |
| 384 | + if response.content_type != content_type: |
| 385 | + print(f" ERROR: {filename} should serve as {content_type}, got {response.content_type}") |
| 386 | + failed += 1 |
| 387 | + if not response.body: |
| 388 | + print(f" ERROR: {filename} should have a non-empty body") |
| 389 | + failed += 1 |
| 390 | + |
| 391 | + print("Test: an unknown or missing filename 404s rather than leaking the app directory") |
| 392 | + for request in [FakeImageRequest("predbat.py"), FakeImageRequest("../predbat.py"), FakeImageRequest()]: |
| 393 | + response = asyncio.run(web.html_logo_image(request)) |
| 394 | + if response.status != 404: |
| 395 | + print(f" ERROR: request for {request.match_info} should 404, got {response.status}") |
| 396 | + failed += 1 |
| 397 | + |
| 398 | + print("Test: a whitelisted filename whose file is genuinely absent on disk 404s cleanly") |
| 399 | + # Regression for issue #4568: when an install has updated predbat.py before its self-updater has |
| 400 | + # fetched these newly-added logo files (an old download.py running against a new release), the |
| 401 | + # image genuinely won't exist yet. This should degrade to a missing image, not raise. |
| 402 | + filename = "bat_logo_light.png" |
| 403 | + file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", filename) |
| 404 | + moved_path = file_path + ".test_moved" |
| 405 | + os.rename(file_path, moved_path) |
| 406 | + try: |
| 407 | + response = asyncio.run(web.html_logo_image(FakeImageRequest(filename))) |
| 408 | + if response.status != 404: |
| 409 | + print(f" ERROR: {filename} missing from disk should 404, got {response.status}") |
| 410 | + failed += 1 |
| 411 | + finally: |
| 412 | + os.rename(moved_path, file_path) |
| 413 | + |
| 414 | + print("Test: the page header no longer depends on GitHub for the logo") |
| 415 | + from web_helper import get_header_html |
| 416 | + |
| 417 | + header = get_header_html("Test", False, "./dash", [], "v1.0", "") |
| 418 | + if "githubusercontent" in header: |
| 419 | + print(" ERROR: the page header should not reference githubusercontent.com any more") |
| 420 | + failed += 1 |
| 421 | + if "./images/bat_logo" not in header: |
| 422 | + print(" ERROR: the page header should reference the local logo route") |
| 423 | + failed += 1 |
| 424 | + |
| 425 | + print("**** Web logo image tests completed ****") |
| 426 | + return failed |
0 commit comments