test_npm_provider.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. """
  2. Tests for the npm binary provider plugin.
  3. Tests cover:
  4. 1. Hook script execution
  5. 2. npm package installation
  6. 3. PATH and NODE_MODULES_DIR updates
  7. 4. JSONL output format
  8. """
  9. import json
  10. import os
  11. import shutil
  12. import subprocess
  13. import sys
  14. import tempfile
  15. from pathlib import Path
  16. import pytest
  17. from django.test import TestCase
  18. # Get the path to the npm provider hook
  19. PLUGIN_DIR = Path(__file__).parent.parent
  20. INSTALL_HOOK = next(PLUGIN_DIR.glob('on_Binary__*_npm_install.py'), None)
  21. def npm_available() -> bool:
  22. """Check if npm is installed."""
  23. return shutil.which('npm') is not None
  24. class TestNpmProviderHook(TestCase):
  25. """Test the npm binary provider installation hook."""
  26. def setUp(self):
  27. """Set up test environment."""
  28. self.temp_dir = tempfile.mkdtemp()
  29. self.lib_dir = Path(self.temp_dir) / 'lib' / 'x86_64-linux'
  30. self.lib_dir.mkdir(parents=True)
  31. def tearDown(self):
  32. """Clean up."""
  33. shutil.rmtree(self.temp_dir, ignore_errors=True)
  34. def test_hook_script_exists(self):
  35. """Hook script should exist."""
  36. self.assertTrue(INSTALL_HOOK and INSTALL_HOOK.exists(), f"Hook not found: {INSTALL_HOOK}")
  37. def test_hook_requires_lib_dir(self):
  38. """Hook should fail when LIB_DIR is not set."""
  39. env = os.environ.copy()
  40. env.pop('LIB_DIR', None) # Remove LIB_DIR
  41. result = subprocess.run(
  42. [
  43. sys.executable, str(INSTALL_HOOK),
  44. '--name=some-package',
  45. '--binary-id=test-uuid',
  46. '--machine-id=test-machine',
  47. ],
  48. capture_output=True,
  49. text=True,
  50. env=env,
  51. timeout=30
  52. )
  53. self.assertIn('LIB_DIR environment variable not set', result.stderr)
  54. self.assertEqual(result.returncode, 1)
  55. def test_hook_skips_when_npm_not_allowed(self):
  56. """Hook should skip when npm not in allowed binproviders."""
  57. env = os.environ.copy()
  58. env['LIB_DIR'] = str(self.lib_dir)
  59. result = subprocess.run(
  60. [
  61. sys.executable, str(INSTALL_HOOK),
  62. '--name=some-package',
  63. '--binary-id=test-uuid',
  64. '--machine-id=test-machine',
  65. '--binproviders=pip,apt', # npm not allowed
  66. ],
  67. capture_output=True,
  68. text=True,
  69. env=env,
  70. timeout=30
  71. )
  72. # Should exit cleanly (code 0) when npm not allowed
  73. self.assertIn('npm provider not allowed', result.stderr)
  74. self.assertEqual(result.returncode, 0)
  75. def test_hook_creates_npm_prefix(self):
  76. """Hook should create npm prefix directory."""
  77. assert npm_available(), "npm not installed"
  78. env = os.environ.copy()
  79. env['LIB_DIR'] = str(self.lib_dir)
  80. # Even if installation fails, the npm prefix should be created
  81. subprocess.run(
  82. [
  83. sys.executable, str(INSTALL_HOOK),
  84. '--name=nonexistent-xyz123',
  85. '--binary-id=test-uuid',
  86. '--machine-id=test-machine',
  87. ],
  88. capture_output=True,
  89. text=True,
  90. env=env,
  91. timeout=60
  92. )
  93. npm_prefix = self.lib_dir / 'npm'
  94. self.assertTrue(npm_prefix.exists())
  95. def test_hook_handles_overrides(self):
  96. """Hook should accept overrides JSON."""
  97. env = os.environ.copy()
  98. env['LIB_DIR'] = str(self.lib_dir)
  99. overrides = json.dumps({'npm': {'packages': ['custom-pkg']}})
  100. # Just verify it doesn't crash with overrides
  101. result = subprocess.run(
  102. [
  103. sys.executable, str(INSTALL_HOOK),
  104. '--name=test-pkg',
  105. '--binary-id=test-uuid',
  106. '--machine-id=test-machine',
  107. f'--overrides={overrides}',
  108. ],
  109. capture_output=True,
  110. text=True,
  111. env=env,
  112. timeout=60
  113. )
  114. # May fail to install, but should not crash parsing overrides
  115. self.assertNotIn('Failed to parse overrides JSON', result.stderr)
  116. if __name__ == '__main__':
  117. pytest.main([__file__, '-v'])