|
| 1 | +package frankenphp |
| 2 | + |
| 3 | +import ( |
| 4 | + "os" |
| 5 | + "path/filepath" |
| 6 | + "testing" |
| 7 | + "time" |
| 8 | + |
| 9 | + "github.com/stretchr/testify/assert" |
| 10 | + "github.com/stretchr/testify/require" |
| 11 | +) |
| 12 | + |
| 13 | +// setupBgWorker boots FrankenPHP with the given options (internal-package |
| 14 | +// variant), registers Shutdown as a t.Cleanup, and returns the absolute |
| 15 | +// path to the testdata directory. |
| 16 | +func setupBgWorker(t *testing.T, opts ...Option) (testDataDir string) { |
| 17 | + t.Helper() |
| 18 | + cwd, err := os.Getwd() |
| 19 | + require.NoError(t, err) |
| 20 | + testDataDir = cwd + "/testdata/" |
| 21 | + require.NoError(t, Init(opts...)) |
| 22 | + t.Cleanup(Shutdown) |
| 23 | + return |
| 24 | +} |
| 25 | + |
| 26 | +// requireSentinelEventually asserts that `path` appears on disk before the |
| 27 | +// deadline. Wraps require.Eventually so call sites stay short. |
| 28 | +func requireSentinelEventually(t testing.TB, path string, msgAndArgs ...any) { |
| 29 | + t.Helper() |
| 30 | + require.Eventually(t, func() bool { |
| 31 | + _, err := os.Stat(path) |
| 32 | + return err == nil |
| 33 | + }, 5*time.Second, 10*time.Millisecond, msgAndArgs...) |
| 34 | +} |
| 35 | + |
| 36 | +// TestNextScopeIsDistinct verifies the scope counter |
| 37 | +// hands out unique values on consecutive calls. |
| 38 | +func TestNextScopeIsDistinct(t *testing.T) { |
| 39 | + a := NextScope() |
| 40 | + b := NextScope() |
| 41 | + assert.NotEqual(t, a, b, "consecutive scopes must differ") |
| 42 | + assert.NotZero(t, a, "scopes must be non-zero (zero is the global scope)") |
| 43 | + assert.NotZero(t, b, "scopes must be non-zero (zero is the global scope)") |
| 44 | +} |
| 45 | + |
| 46 | +// TestBackgroundWorkerSameNameDifferentScope declares two named bg |
| 47 | +// workers with the same user-facing name in distinct scopes. Both must |
| 48 | +// Init successfully (the global workersByName collision check must |
| 49 | +// recognize bg workers as scope-isolated), each must produce its own |
| 50 | +// sentinel under its scope-specific directory. |
| 51 | +func TestBackgroundWorkerSameNameDifferentScope(t *testing.T) { |
| 52 | + scopeA := NextScope() |
| 53 | + scopeB := NextScope() |
| 54 | + |
| 55 | + tmp := t.TempDir() |
| 56 | + dirA := filepath.Join(tmp, "a") |
| 57 | + dirB := filepath.Join(tmp, "b") |
| 58 | + require.NoError(t, os.MkdirAll(dirA, 0o755)) |
| 59 | + require.NoError(t, os.MkdirAll(dirB, 0o755)) |
| 60 | + |
| 61 | + setupBgWorker(t, |
| 62 | + WithWorkers("shared", "testdata/bgworker/named.php", 1, |
| 63 | + WithWorkerBackground(), |
| 64 | + WithWorkerScope(scopeA), |
| 65 | + WithWorkerEnv(map[string]string{"BG_SENTINEL_DIR": dirA}), |
| 66 | + ), |
| 67 | + WithWorkers("shared", "testdata/bgworker/named.php", 1, |
| 68 | + WithWorkerBackground(), |
| 69 | + WithWorkerScope(scopeB), |
| 70 | + WithWorkerEnv(map[string]string{"BG_SENTINEL_DIR": dirB}), |
| 71 | + ), |
| 72 | + WithNumThreads(4), |
| 73 | + ) |
| 74 | + |
| 75 | + // Both lookups must exist and resolve "shared" to a *worker. |
| 76 | + require.NotNil(t, backgroundLookups[scopeA], "scope A lookup missing") |
| 77 | + require.NotNil(t, backgroundLookups[scopeB], "scope B lookup missing") |
| 78 | + assert.NotNil(t, backgroundLookups[scopeA]["shared"], "scope A must resolve 'shared'") |
| 79 | + assert.NotNil(t, backgroundLookups[scopeB]["shared"], "scope B must resolve 'shared'") |
| 80 | + // And they must be distinct *worker instances (not the same pointer). |
| 81 | + assert.NotSame(t, |
| 82 | + backgroundLookups[scopeA]["shared"], |
| 83 | + backgroundLookups[scopeB]["shared"], |
| 84 | + "each scope must own a distinct *worker for the same name") |
| 85 | + |
| 86 | + // Each scope's worker writes its sentinel under its own dir. |
| 87 | + requireSentinelEventually(t, filepath.Join(dirA, "shared"), "scope A worker did not touch sentinel") |
| 88 | + requireSentinelEventually(t, filepath.Join(dirB, "shared"), "scope B worker did not touch sentinel") |
| 89 | +} |
0 commit comments