package php import ( "os" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestIsLaravelProject_Good(t *testing.T) { t.Run("valid Laravel project with artisan and composer.json", func(t *testing.T) { dir := t.TempDir() // Create artisan file artisanPath := filepath.Join(dir, "artisan") err := os.WriteFile(artisanPath, []byte("#!/usr/bin/env php\n"), 0755) require.NoError(t, err) // Create composer.json with laravel/framework composerJSON := `{ "name": "test/laravel-project", "require": { "php": "^8.2", "laravel/framework": "^11.0" } }` composerPath := filepath.Join(dir, "composer.json") err = os.WriteFile(composerPath, []byte(composerJSON), 0644) require.NoError(t, err) assert.True(t, IsLaravelProject(dir)) }) t.Run("Laravel in require-dev", func(t *testing.T) { dir := t.TempDir() // Create artisan file artisanPath := filepath.Join(dir, "artisan") err := os.WriteFile(artisanPath, []byte("#!/usr/bin/env php\n"), 0755) require.NoError(t, err) // Create composer.json with laravel/framework in require-dev composerJSON := `{ "name": "test/laravel-project", "require-dev": { "laravel/framework": "^11.0" } }` composerPath := filepath.Join(dir, "composer.json") err = os.WriteFile(composerPath, []byte(composerJSON), 0644) require.NoError(t, err) assert.True(t, IsLaravelProject(dir)) }) } func TestIsLaravelProject_Bad(t *testing.T) { t.Run("missing artisan file", func(t *testing.T) { dir := t.TempDir() // Create composer.json but no artisan composerJSON := `{ "name": "test/laravel-project", "require": { "laravel/framework": "^11.0" } }` composerPath := filepath.Join(dir, "composer.json") err := os.WriteFile(composerPath, []byte(composerJSON), 0644) require.NoError(t, err) assert.False(t, IsLaravelProject(dir)) }) t.Run("missing composer.json", func(t *testing.T) { dir := t.TempDir() // Create artisan but no composer.json artisanPath := filepath.Join(dir, "artisan") err := os.WriteFile(artisanPath, []byte("#!/usr/bin/env php\n"), 0755) require.NoError(t, err) assert.False(t, IsLaravelProject(dir)) }) t.Run("composer.json without Laravel", func(t *testing.T) { dir := t.TempDir() // Create artisan file artisanPath := filepath.Join(dir, "artisan") err := os.WriteFile(artisanPath, []byte("#!/usr/bin/env php\n"), 0755) require.NoError(t, err) // Create composer.json without laravel/framework composerJSON := `{ "name": "test/symfony-project", "require": { "symfony/framework-bundle": "^7.0" } }` composerPath := filepath.Join(dir, "composer.json") err = os.WriteFile(composerPath, []byte(composerJSON), 0644) require.NoError(t, err) assert.False(t, IsLaravelProject(dir)) }) t.Run("invalid composer.json", func(t *testing.T) { dir := t.TempDir() // Create artisan file artisanPath := filepath.Join(dir, "artisan") err := os.WriteFile(artisanPath, []byte("#!/usr/bin/env php\n"), 0755) require.NoError(t, err) // Create invalid composer.json composerPath := filepath.Join(dir, "composer.json") err = os.WriteFile(composerPath, []byte("not valid json{"), 0644) require.NoError(t, err) assert.False(t, IsLaravelProject(dir)) }) t.Run("empty directory", func(t *testing.T) { dir := t.TempDir() assert.False(t, IsLaravelProject(dir)) }) t.Run("non-existent directory", func(t *testing.T) { assert.False(t, IsLaravelProject("/non/existent/path")) }) } func TestIsFrankenPHPProject_Good(t *testing.T) { t.Run("project with octane and frankenphp config", func(t *testing.T) { dir := t.TempDir() // Create composer.json with laravel/octane composerJSON := `{ "require": { "laravel/octane": "^2.0" } }` err := os.WriteFile(filepath.Join(dir, "composer.json"), []byte(composerJSON), 0644) require.NoError(t, err) // Create config directory and octane.php configDir := filepath.Join(dir, "config") err = os.MkdirAll(configDir, 0755) require.NoError(t, err) octaneConfig := ` 'frankenphp', ];` err = os.WriteFile(filepath.Join(configDir, "octane.php"), []byte(octaneConfig), 0644) require.NoError(t, err) assert.True(t, IsFrankenPHPProject(dir)) }) t.Run("project with octane but no config file", func(t *testing.T) { dir := t.TempDir() // Create composer.json with laravel/octane composerJSON := `{ "require": { "laravel/octane": "^2.0" } }` err := os.WriteFile(filepath.Join(dir, "composer.json"), []byte(composerJSON), 0644) require.NoError(t, err) // No config file - should still return true (assume frankenphp) assert.True(t, IsFrankenPHPProject(dir)) }) t.Run("project with octane but unreadable config file", func(t *testing.T) { if os.Geteuid() == 0 { t.Skip("root can read any file") } dir := t.TempDir() // Create composer.json with laravel/octane composerJSON := `{ "require": { "laravel/octane": "^2.0" } }` err := os.WriteFile(filepath.Join(dir, "composer.json"), []byte(composerJSON), 0644) require.NoError(t, err) // Create config directory and octane.php with no read permissions configDir := filepath.Join(dir, "config") err = os.MkdirAll(configDir, 0755) require.NoError(t, err) octanePath := filepath.Join(configDir, "octane.php") err = os.WriteFile(octanePath, []byte(" 'swoole', ];` err = os.WriteFile(filepath.Join(configDir, "octane.php"), []byte(octaneConfig), 0644) require.NoError(t, err) assert.False(t, IsFrankenPHPProject(dir)) }) }