|
| 1 | +package hook |
| 2 | + |
| 3 | +import ( |
| 4 | + "os" |
| 5 | + "path/filepath" |
| 6 | + "testing" |
| 7 | + |
| 8 | + "github.com/stretchr/testify/assert" |
| 9 | + "github.com/stretchr/testify/require" |
| 10 | +) |
| 11 | + |
| 12 | +// TestFileSystemHookDiscovery_Discover_emptyDir returns no paths for an empty directory. |
| 13 | +func TestFileSystemHookDiscovery_Discover_emptyDir(t *testing.T) { |
| 14 | + dir := t.TempDir() |
| 15 | + d := FileSystemHookDiscovery{} |
| 16 | + paths, err := d.Discover(dir) |
| 17 | + require.NoError(t, err) |
| 18 | + assert.Empty(t, paths) |
| 19 | +} |
| 20 | + |
| 21 | +// TestFileSystemHookDiscovery_Discover_findsExecutables returns executable files. |
| 22 | +func TestFileSystemHookDiscovery_Discover_findsExecutables(t *testing.T) { |
| 23 | + dir := t.TempDir() |
| 24 | + |
| 25 | + // Create an executable file. |
| 26 | + execPath := filepath.Join(dir, "my-hook.sh") |
| 27 | + err := os.WriteFile(execPath, []byte("#!/bin/sh\necho ok\n"), 0o755) |
| 28 | + require.NoError(t, err) |
| 29 | + |
| 30 | + d := FileSystemHookDiscovery{} |
| 31 | + paths, err := d.Discover(dir) |
| 32 | + require.NoError(t, err) |
| 33 | + assert.Contains(t, paths, execPath) |
| 34 | +} |
| 35 | + |
| 36 | +// TestFileSystemHookDiscovery_Discover_ignoresNonExecutables skips regular files. |
| 37 | +func TestFileSystemHookDiscovery_Discover_ignoresNonExecutables(t *testing.T) { |
| 38 | + dir := t.TempDir() |
| 39 | + |
| 40 | + // Non-executable file. |
| 41 | + nonExecPath := filepath.Join(dir, "README.md") |
| 42 | + err := os.WriteFile(nonExecPath, []byte("docs"), 0o644) |
| 43 | + require.NoError(t, err) |
| 44 | + |
| 45 | + d := FileSystemHookDiscovery{} |
| 46 | + paths, err := d.Discover(dir) |
| 47 | + require.NoError(t, err) |
| 48 | + assert.NotContains(t, paths, nonExecPath) |
| 49 | +} |
| 50 | + |
| 51 | +// TestFileSystemHookDiscovery_Discover_recursive finds executables in subdirectories. |
| 52 | +func TestFileSystemHookDiscovery_Discover_recursive(t *testing.T) { |
| 53 | + dir := t.TempDir() |
| 54 | + subdir := filepath.Join(dir, "subdir") |
| 55 | + require.NoError(t, os.MkdirAll(subdir, 0o755)) |
| 56 | + |
| 57 | + hookPath := filepath.Join(subdir, "hook.sh") |
| 58 | + require.NoError(t, os.WriteFile(hookPath, []byte("#!/bin/sh\n"), 0o755)) |
| 59 | + |
| 60 | + d := FileSystemHookDiscovery{} |
| 61 | + paths, err := d.Discover(dir) |
| 62 | + require.NoError(t, err) |
| 63 | + assert.Contains(t, paths, hookPath) |
| 64 | +} |
| 65 | + |
| 66 | +// TestNewHookManager_defaultDiscovery verifies that a nil HookDiscovery in |
| 67 | +// ManagerConfig defaults to FileSystemHookDiscovery inside the Manager. |
| 68 | +func TestNewHookManager_defaultDiscovery(t *testing.T) { |
| 69 | + hm := newHookManager(t, t.TempDir()) |
| 70 | + hm.hookDiscovery = nil // reset to exercise the nil-default path via NewHookManager |
| 71 | + cfg := &ManagerConfig{ |
| 72 | + WorkingDir: t.TempDir(), |
| 73 | + TempDir: t.TempDir(), |
| 74 | + HookDiscovery: nil, // explicitly nil → should default |
| 75 | + AdmissionWebhookManager: hm.admissionWebhookManager, |
| 76 | + ConversionWebhookManager: hm.conversionWebhookManager, |
| 77 | + Logger: hm.logger, |
| 78 | + } |
| 79 | + hm2 := NewHookManager(cfg) |
| 80 | + require.NotNil(t, hm2) |
| 81 | + assert.IsType(t, FileSystemHookDiscovery{}, hm2.hookDiscovery) |
| 82 | +} |
| 83 | + |
| 84 | +// TestNewHookManager_injectedDiscovery verifies that a custom HookDiscovery is |
| 85 | +// stored as-is in the Manager. |
| 86 | +func TestNewHookManager_injectedDiscovery(t *testing.T) { |
| 87 | + stub := &stubDiscovery{} |
| 88 | + hm := newHookManager(t, t.TempDir()) |
| 89 | + cfg := &ManagerConfig{ |
| 90 | + WorkingDir: t.TempDir(), |
| 91 | + TempDir: t.TempDir(), |
| 92 | + HookDiscovery: stub, |
| 93 | + AdmissionWebhookManager: hm.admissionWebhookManager, |
| 94 | + ConversionWebhookManager: hm.conversionWebhookManager, |
| 95 | + Logger: hm.logger, |
| 96 | + } |
| 97 | + hm2 := NewHookManager(cfg) |
| 98 | + assert.Equal(t, stub, hm2.hookDiscovery) |
| 99 | +} |
| 100 | + |
| 101 | +// TestManager_Init_usesInjectedDiscovery verifies that Manager.Init calls |
| 102 | +// HookDiscovery.Discover rather than the filesystem when a custom discovery is |
| 103 | +// injected. An empty result means Init succeeds with zero hooks loaded. |
| 104 | +func TestManager_Init_usesInjectedDiscovery(t *testing.T) { |
| 105 | + stub := &stubDiscovery{paths: []string{}} // returns nothing |
| 106 | + hm := newHookManager(t, t.TempDir()) |
| 107 | + hm.hookDiscovery = stub |
| 108 | + |
| 109 | + err := hm.Init() |
| 110 | + require.NoError(t, err) |
| 111 | + assert.True(t, stub.called, "Discover should have been called") |
| 112 | + assert.Equal(t, []string{}, hm.GetHookNames()) |
| 113 | +} |
| 114 | + |
| 115 | +// TestManager_Init_discoveryError propagates discovery errors. |
| 116 | +func TestManager_Init_discoveryError(t *testing.T) { |
| 117 | + stub := &stubDiscovery{err: errStubDiscovery} |
| 118 | + hm := newHookManager(t, t.TempDir()) |
| 119 | + hm.hookDiscovery = stub |
| 120 | + |
| 121 | + err := hm.Init() |
| 122 | + require.Error(t, err) |
| 123 | + assert.ErrorIs(t, err, errStubDiscovery) |
| 124 | +} |
| 125 | + |
| 126 | +// ---- helpers ---- |
| 127 | + |
| 128 | +var errStubDiscovery = &testError{"stub discovery error"} |
| 129 | + |
| 130 | +type testError struct{ msg string } |
| 131 | + |
| 132 | +func (e *testError) Error() string { return e.msg } |
| 133 | + |
| 134 | +type stubDiscovery struct { |
| 135 | + paths []string |
| 136 | + err error |
| 137 | + called bool |
| 138 | +} |
| 139 | + |
| 140 | +func (s *stubDiscovery) Discover(_ string) ([]string, error) { |
| 141 | + s.called = true |
| 142 | + return s.paths, s.err |
| 143 | +} |
0 commit comments