test_ftp_profiles.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. """Per-model FTP profile registry (#1401).
  2. Mirrors ``test_camera_profiles.py`` in shape — the FTP profile module
  3. follows the same pattern.
  4. """
  5. import ssl
  6. from backend.app.services.ftp_profiles import (
  7. DEFAULT_PROFILE,
  8. FTPProfile,
  9. get_ftp_profile,
  10. )
  11. def test_default_profile_does_not_cap_tls():
  12. """Default profile keeps the historical Python-default TLS negotiation
  13. (typically TLS 1.3 on Python 3.13). Capping would be a silent
  14. regression for users who work fine today."""
  15. assert DEFAULT_PROFILE.cap_tls_v1_2 is False
  16. def test_unknown_model_returns_default():
  17. """Unknown / missing models fall back to DEFAULT_PROFILE so the FTP
  18. path is never blocked on a missing entry."""
  19. assert get_ftp_profile(None) is DEFAULT_PROFILE
  20. assert get_ftp_profile("") is DEFAULT_PROFILE
  21. assert get_ftp_profile("Unknown Future Model") is DEFAULT_PROFILE
  22. def test_p2s_caps_tls_v1_2():
  23. """P2S firmware 01.02.00.00 trips a vsFTPd + TLS 1.3 session-reuse
  24. bug on the data channel; the profile must cap to TLS 1.2 so session
  25. resumption is synchronous (#1401, reporter @iitazz)."""
  26. profile = get_ftp_profile("P2S")
  27. assert profile.cap_tls_v1_2 is True
  28. def test_p2s_internal_ssdp_code_resolves_to_p2s():
  29. """SSDP internal code N7 → P2S profile. Camera profiles do the same
  30. thing — keeps callers free of the code↔display-name mapping."""
  31. profile = get_ftp_profile("N7")
  32. assert profile.cap_tls_v1_2 is True
  33. def test_lookup_is_case_insensitive():
  34. """Printer.model may carry mixed case; the lookup normalises."""
  35. assert get_ftp_profile("p2s").cap_tls_v1_2 is True
  36. assert get_ftp_profile("P2s").cap_tls_v1_2 is True
  37. def test_non_capped_models_still_default():
  38. """Spot-check: the models the user dogfoods today (X1C, H2D) stay on
  39. the default profile. Adding the P2S override must not accidentally
  40. flip these."""
  41. assert get_ftp_profile("X1C").cap_tls_v1_2 is False
  42. assert get_ftp_profile("H2D").cap_tls_v1_2 is False
  43. assert get_ftp_profile("P1S").cap_tls_v1_2 is False
  44. assert get_ftp_profile("A1").cap_tls_v1_2 is False
  45. def test_profile_is_frozen():
  46. """FTPProfile is a frozen dataclass — runtime mutation should raise.
  47. Same guarantee CameraProfile has."""
  48. try:
  49. DEFAULT_PROFILE.cap_tls_v1_2 = True # type: ignore[misc]
  50. except Exception as e:
  51. assert "frozen" in str(e).lower() or "FrozenInstanceError" in type(e).__name__
  52. return
  53. raise AssertionError("FTPProfile should be frozen but assignment succeeded")
  54. def test_cap_tls_v1_2_actually_applied_to_ssl_context():
  55. """Pins the integration: when ``cap_tls_v1_2=True`` is passed to the
  56. FTPS subclass, the SSL context's ``maximum_version`` is set to
  57. TLSv1.2. Guards against a future refactor that drops the wiring
  58. between profile and context (the registry would still look
  59. correct, but the cap would silently stop applying)."""
  60. from backend.app.services.bambu_ftp import ImplicitFTP_TLS
  61. capped = ImplicitFTP_TLS(cap_tls_v1_2=True)
  62. assert capped.ssl_context.maximum_version == ssl.TLSVersion.TLSv1_2
  63. uncapped = ImplicitFTP_TLS(cap_tls_v1_2=False)
  64. # MAXIMUM_SUPPORTED is the "no cap applied" sentinel for SSLContext.
  65. assert uncapped.ssl_context.maximum_version == ssl.TLSVersion.MAXIMUM_SUPPORTED
  66. def test_ftp_profile_dataclass_default_constructible():
  67. """Sanity: FTPProfile() with no args yields the default profile
  68. (every field has a default)."""
  69. fresh = FTPProfile()
  70. assert fresh == DEFAULT_PROFILE