conftest.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. """Test fixtures for FTP service tests.
  2. Provides a real implicit FTPS server (via mock_ftp_server) and client factory
  3. for integration-style testing of BambuFTPClient against a live server.
  4. """
  5. import socket
  6. from unittest.mock import patch
  7. import pytest
  8. from backend.app.services.bambu_ftp import BambuFTPClient
  9. from backend.app.services.virtual_printer.certificate import CertificateService
  10. from backend.tests.unit.services.mock_ftp_server import MockBambuFTPServer
  11. @pytest.fixture(scope="session")
  12. def ftp_certs(tmp_path_factory):
  13. """Generate self-signed TLS certificates once per test session."""
  14. cert_dir = tmp_path_factory.mktemp("ftp_certs")
  15. svc = CertificateService(cert_dir, serial="TEST_FTP_SERVER")
  16. cert_path, key_path = svc.generate_certificates()
  17. return str(cert_path), str(key_path)
  18. def _find_free_port() -> int:
  19. """Find a free TCP port on localhost."""
  20. with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
  21. s.bind(("127.0.0.1", 0))
  22. return s.getsockname()[1]
  23. @pytest.fixture()
  24. def ftp_root(tmp_path):
  25. """Create temp directory with standard Bambu printer directory structure."""
  26. for d in ("cache", "timelapse", "model", "data", "data/Metadata"):
  27. (tmp_path / d).mkdir(parents=True, exist_ok=True)
  28. return tmp_path
  29. @pytest.fixture()
  30. def ftp_server(ftp_certs, ftp_root):
  31. """Start a mock implicit FTPS server, yield it, stop on cleanup."""
  32. cert_path, key_path = ftp_certs
  33. port = _find_free_port()
  34. server = MockBambuFTPServer(
  35. host="127.0.0.1",
  36. port=port,
  37. root_dir=str(ftp_root),
  38. cert_path=cert_path,
  39. key_path=key_path,
  40. access_code="12345678",
  41. )
  42. server.start()
  43. yield server
  44. server.stop()
  45. @pytest.fixture()
  46. def ftp_client_factory(ftp_server):
  47. """Factory that creates BambuFTPClient instances pointed at the mock server."""
  48. def _make_client(
  49. printer_model: str = "X1C",
  50. force_prot_c: bool = False,
  51. access_code: str = "12345678",
  52. timeout: float = 10.0,
  53. ) -> BambuFTPClient:
  54. client = BambuFTPClient(
  55. ip_address="127.0.0.1",
  56. access_code=access_code,
  57. timeout=timeout,
  58. printer_model=printer_model,
  59. force_prot_c=force_prot_c,
  60. )
  61. # Override port to point at mock server
  62. client.FTP_PORT = ftp_server.port
  63. return client
  64. return _make_client
  65. @pytest.fixture(autouse=True)
  66. def clear_ftp_mode_cache():
  67. """Clear BambuFTPClient mode cache before and after each test."""
  68. BambuFTPClient._mode_cache.clear()
  69. yield
  70. BambuFTPClient._mode_cache.clear()
  71. @pytest.fixture()
  72. def patch_ftp_port(ftp_server):
  73. """Patch FTP_PORT at class level for async wrapper tests.
  74. Async wrappers create their own BambuFTPClient instances internally,
  75. so we need to patch the class-level default port.
  76. """
  77. with patch.object(BambuFTPClient, "FTP_PORT", ftp_server.port):
  78. yield ftp_server