test_apt_provider.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. """
  2. Tests for the apt binary provider plugin.
  3. Tests cover:
  4. 1. Hook script execution
  5. 2. apt package availability detection
  6. 3. JSONL output format
  7. """
  8. import json
  9. import os
  10. import shutil
  11. import subprocess
  12. import sys
  13. import tempfile
  14. from pathlib import Path
  15. import pytest
  16. from django.test import TestCase
  17. # Get the path to the apt provider hook
  18. PLUGIN_DIR = Path(__file__).parent.parent
  19. INSTALL_HOOK = next(PLUGIN_DIR.glob('on_Binary__*_apt_install.py'), None)
  20. def apt_available() -> bool:
  21. """Check if apt is installed."""
  22. return shutil.which('apt') is not None or shutil.which('apt-get') is not None
  23. def is_linux() -> bool:
  24. """Check if running on Linux."""
  25. import platform
  26. return platform.system().lower() == 'linux'
  27. class TestAptProviderHook(TestCase):
  28. """Test the apt binary provider installation hook."""
  29. def setUp(self):
  30. """Set up test environment."""
  31. self.temp_dir = tempfile.mkdtemp()
  32. def tearDown(self):
  33. """Clean up."""
  34. shutil.rmtree(self.temp_dir, ignore_errors=True)
  35. def test_hook_script_exists(self):
  36. """Hook script should exist."""
  37. self.assertTrue(INSTALL_HOOK and INSTALL_HOOK.exists(), f"Hook not found: {INSTALL_HOOK}")
  38. def test_hook_skips_when_apt_not_allowed(self):
  39. """Hook should skip when apt not in allowed binproviders."""
  40. result = subprocess.run(
  41. [
  42. sys.executable, str(INSTALL_HOOK),
  43. '--name=wget',
  44. '--binary-id=test-uuid',
  45. '--machine-id=test-machine',
  46. '--binproviders=pip,npm', # apt not allowed
  47. ],
  48. capture_output=True,
  49. text=True,
  50. timeout=30
  51. )
  52. # Should exit cleanly (code 0) when apt not allowed
  53. self.assertIn('apt provider not allowed', result.stderr)
  54. self.assertEqual(result.returncode, 0)
  55. @pytest.mark.skipif(not is_linux(), reason="apt only available on Linux")
  56. def test_hook_detects_apt(self):
  57. """Hook should detect apt binary when available."""
  58. assert apt_available(), "apt not installed"
  59. result = subprocess.run(
  60. [
  61. sys.executable, str(INSTALL_HOOK),
  62. '--name=nonexistent-pkg-xyz123',
  63. '--binary-id=test-uuid',
  64. '--machine-id=test-machine',
  65. ],
  66. capture_output=True,
  67. text=True,
  68. timeout=30
  69. )
  70. # Should not say apt is not available
  71. self.assertNotIn('apt not available', result.stderr)
  72. def test_hook_handles_overrides(self):
  73. """Hook should accept overrides JSON."""
  74. overrides = json.dumps({
  75. 'apt': {'packages': ['custom-package-name']}
  76. })
  77. result = subprocess.run(
  78. [
  79. sys.executable, str(INSTALL_HOOK),
  80. '--name=test-pkg',
  81. '--binary-id=test-uuid',
  82. '--machine-id=test-machine',
  83. f'--overrides={overrides}',
  84. ],
  85. capture_output=True,
  86. text=True,
  87. timeout=30
  88. )
  89. # Should not crash parsing overrides
  90. self.assertNotIn('Traceback', result.stderr)
  91. @pytest.mark.skipif(not is_linux(), reason="apt only available on Linux")
  92. class TestAptProviderSystemBinaries(TestCase):
  93. """Test apt provider with system binaries."""
  94. def test_detect_existing_binary(self):
  95. """apt provider should detect already-installed system binaries."""
  96. assert apt_available(), "apt not installed"
  97. # Check for a binary that's almost certainly installed (like 'ls' or 'bash')
  98. result = subprocess.run(
  99. [
  100. sys.executable, str(INSTALL_HOOK),
  101. '--name=bash',
  102. '--binary-id=test-uuid',
  103. '--machine-id=test-machine',
  104. ],
  105. capture_output=True,
  106. text=True,
  107. timeout=60
  108. )
  109. # Parse JSONL output
  110. for line in result.stdout.split('\n'):
  111. line = line.strip()
  112. if line.startswith('{'):
  113. try:
  114. record = json.loads(line)
  115. if record.get('type') == 'Binary' and record.get('name') == 'bash':
  116. # Found bash
  117. self.assertTrue(record.get('abspath'))
  118. self.assertTrue(Path(record['abspath']).exists())
  119. return
  120. except json.JSONDecodeError:
  121. continue
  122. # apt may not be able to "install" bash (already installed)
  123. # Just verify no crash
  124. self.assertNotIn('Traceback', result.stderr)
  125. if __name__ == '__main__':
  126. pytest.main([__file__, '-v'])