lint/pkg/php/runner_test.go
Snider af5c792da8 feat(lint): add pkg/detect + pkg/php — project detection and PHP QA toolchain
Add project type detection (pkg/detect) and complete PHP quality assurance
package (pkg/php) with formatter, analyser, audit, security, refactor,
mutation testing, test runner, pipeline stages, and QA runner that builds
process.RunSpec for orchestrated execution.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-09 13:13:30 +00:00

245 lines
7.6 KiB
Go

package php
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewQARunner(t *testing.T) {
runner := NewQARunner("/tmp/test", false)
assert.NotNil(t, runner)
}
func TestBuildSpecs_Audit(t *testing.T) {
dir := t.TempDir()
runner := NewQARunner(dir, false)
specs := runner.BuildSpecs([]string{"audit"})
require.Len(t, specs, 1)
assert.Equal(t, "audit", specs[0].Name)
assert.Equal(t, "composer", specs[0].Command)
assert.Contains(t, specs[0].Args, "--format=summary")
}
func TestBuildSpecs_Fmt_WithPint(t *testing.T) {
dir := t.TempDir()
vendorBin := filepath.Join(dir, "vendor", "bin")
os.MkdirAll(vendorBin, 0755)
os.WriteFile(filepath.Join(vendorBin, "pint"), []byte("#!/bin/sh"), 0755)
runner := NewQARunner(dir, false)
specs := runner.BuildSpecs([]string{"fmt"})
require.Len(t, specs, 1)
assert.Equal(t, "fmt", specs[0].Name)
assert.Contains(t, specs[0].Args, "--test")
assert.Equal(t, []string{"audit"}, specs[0].After)
}
func TestBuildSpecs_Fmt_Fix(t *testing.T) {
dir := t.TempDir()
vendorBin := filepath.Join(dir, "vendor", "bin")
os.MkdirAll(vendorBin, 0755)
os.WriteFile(filepath.Join(vendorBin, "pint"), []byte("#!/bin/sh"), 0755)
runner := NewQARunner(dir, true) // fix mode
specs := runner.BuildSpecs([]string{"fmt"})
require.Len(t, specs, 1)
assert.NotContains(t, specs[0].Args, "--test")
}
func TestBuildSpecs_Fmt_NoPint(t *testing.T) {
dir := t.TempDir()
runner := NewQARunner(dir, false)
specs := runner.BuildSpecs([]string{"fmt"})
assert.Empty(t, specs)
}
func TestBuildSpecs_Stan_WithPHPStan(t *testing.T) {
dir := t.TempDir()
vendorBin := filepath.Join(dir, "vendor", "bin")
os.MkdirAll(vendorBin, 0755)
os.WriteFile(filepath.Join(vendorBin, "phpstan"), []byte("#!/bin/sh"), 0755)
runner := NewQARunner(dir, false)
specs := runner.BuildSpecs([]string{"stan"})
require.Len(t, specs, 1)
assert.Equal(t, "stan", specs[0].Name)
assert.Contains(t, specs[0].Args, "--no-progress")
assert.Equal(t, []string{"fmt"}, specs[0].After)
}
func TestBuildSpecs_Stan_NotDetected(t *testing.T) {
dir := t.TempDir()
runner := NewQARunner(dir, false)
specs := runner.BuildSpecs([]string{"stan"})
assert.Empty(t, specs)
}
func TestBuildSpecs_Psalm_WithPsalm(t *testing.T) {
dir := t.TempDir()
vendorBin := filepath.Join(dir, "vendor", "bin")
os.MkdirAll(vendorBin, 0755)
os.WriteFile(filepath.Join(vendorBin, "psalm"), []byte("#!/bin/sh"), 0755)
runner := NewQARunner(dir, false)
specs := runner.BuildSpecs([]string{"psalm"})
require.Len(t, specs, 1)
assert.Equal(t, "psalm", specs[0].Name)
assert.Equal(t, []string{"stan"}, specs[0].After)
}
func TestBuildSpecs_Psalm_Fix(t *testing.T) {
dir := t.TempDir()
vendorBin := filepath.Join(dir, "vendor", "bin")
os.MkdirAll(vendorBin, 0755)
os.WriteFile(filepath.Join(vendorBin, "psalm"), []byte("#!/bin/sh"), 0755)
runner := NewQARunner(dir, true)
specs := runner.BuildSpecs([]string{"psalm"})
require.Len(t, specs, 1)
assert.Contains(t, specs[0].Args, "--alter")
}
func TestBuildSpecs_Test_Pest(t *testing.T) {
dir := t.TempDir()
vendorBin := filepath.Join(dir, "vendor", "bin")
os.MkdirAll(vendorBin, 0755)
os.WriteFile(filepath.Join(vendorBin, "pest"), []byte("#!/bin/sh"), 0755)
runner := NewQARunner(dir, false)
specs := runner.BuildSpecs([]string{"test"})
require.Len(t, specs, 1)
assert.Equal(t, "test", specs[0].Name)
assert.Equal(t, []string{"stan"}, specs[0].After)
}
func TestBuildSpecs_Test_PHPUnit(t *testing.T) {
dir := t.TempDir()
vendorBin := filepath.Join(dir, "vendor", "bin")
os.MkdirAll(vendorBin, 0755)
os.WriteFile(filepath.Join(vendorBin, "phpunit"), []byte("#!/bin/sh"), 0755)
runner := NewQARunner(dir, false)
specs := runner.BuildSpecs([]string{"test"})
require.Len(t, specs, 1)
assert.Contains(t, specs[0].Command, "phpunit")
}
func TestBuildSpecs_Test_WithPsalmDep(t *testing.T) {
dir := t.TempDir()
vendorBin := filepath.Join(dir, "vendor", "bin")
os.MkdirAll(vendorBin, 0755)
os.WriteFile(filepath.Join(vendorBin, "pest"), []byte("#!/bin/sh"), 0755)
os.WriteFile(filepath.Join(vendorBin, "psalm"), []byte("#!/bin/sh"), 0755)
runner := NewQARunner(dir, false)
specs := runner.BuildSpecs([]string{"test"})
require.Len(t, specs, 1)
assert.Equal(t, []string{"psalm"}, specs[0].After)
}
func TestBuildSpecs_Test_NoRunner(t *testing.T) {
dir := t.TempDir()
runner := NewQARunner(dir, false)
specs := runner.BuildSpecs([]string{"test"})
assert.Empty(t, specs)
}
func TestBuildSpecs_Rector(t *testing.T) {
dir := t.TempDir()
vendorBin := filepath.Join(dir, "vendor", "bin")
os.MkdirAll(vendorBin, 0755)
os.WriteFile(filepath.Join(vendorBin, "rector"), []byte("#!/bin/sh"), 0755)
runner := NewQARunner(dir, false)
specs := runner.BuildSpecs([]string{"rector"})
require.Len(t, specs, 1)
assert.True(t, specs[0].AllowFailure)
assert.Contains(t, specs[0].Args, "--dry-run")
assert.Equal(t, []string{"test"}, specs[0].After)
}
func TestBuildSpecs_Rector_Fix(t *testing.T) {
dir := t.TempDir()
vendorBin := filepath.Join(dir, "vendor", "bin")
os.MkdirAll(vendorBin, 0755)
os.WriteFile(filepath.Join(vendorBin, "rector"), []byte("#!/bin/sh"), 0755)
runner := NewQARunner(dir, true)
specs := runner.BuildSpecs([]string{"rector"})
require.Len(t, specs, 1)
assert.NotContains(t, specs[0].Args, "--dry-run")
}
func TestBuildSpecs_Infection(t *testing.T) {
dir := t.TempDir()
vendorBin := filepath.Join(dir, "vendor", "bin")
os.MkdirAll(vendorBin, 0755)
os.WriteFile(filepath.Join(vendorBin, "infection"), []byte("#!/bin/sh"), 0755)
runner := NewQARunner(dir, false)
specs := runner.BuildSpecs([]string{"infection"})
require.Len(t, specs, 1)
assert.True(t, specs[0].AllowFailure)
assert.Equal(t, []string{"test"}, specs[0].After)
}
func TestBuildSpecs_Unknown(t *testing.T) {
runner := NewQARunner(t.TempDir(), false)
specs := runner.BuildSpecs([]string{"unknown"})
assert.Empty(t, specs)
}
func TestBuildSpecs_Multiple(t *testing.T) {
dir := t.TempDir()
vendorBin := filepath.Join(dir, "vendor", "bin")
os.MkdirAll(vendorBin, 0755)
os.WriteFile(filepath.Join(vendorBin, "pint"), []byte("#!/bin/sh"), 0755)
os.WriteFile(filepath.Join(vendorBin, "phpstan"), []byte("#!/bin/sh"), 0755)
runner := NewQARunner(dir, false)
specs := runner.BuildSpecs([]string{"audit", "fmt", "stan"})
assert.Len(t, specs, 3)
}
func TestQACheckRunResult_GetIssueMessage(t *testing.T) {
tests := []struct {
name string
result QACheckRunResult
expected string
}{
{"passed returns empty", QACheckRunResult{Passed: true, Name: "audit"}, ""},
{"skipped returns empty", QACheckRunResult{Skipped: true, Name: "audit"}, ""},
{"audit", QACheckRunResult{Name: "audit"}, "found vulnerabilities"},
{"fmt", QACheckRunResult{Name: "fmt"}, "found style issues"},
{"stan", QACheckRunResult{Name: "stan"}, "found analysis errors"},
{"psalm", QACheckRunResult{Name: "psalm"}, "found type errors"},
{"test", QACheckRunResult{Name: "test"}, "tests failed"},
{"rector", QACheckRunResult{Name: "rector"}, "found refactoring suggestions"},
{"infection", QACheckRunResult{Name: "infection"}, "mutation testing did not pass"},
{"unknown", QACheckRunResult{Name: "whatever"}, "found issues"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.expected, tt.result.GetIssueMessage())
})
}
}
func TestQARunResult(t *testing.T) {
result := QARunResult{
Passed: true,
Duration: "1.5s",
Results: []QACheckRunResult{
{Name: "audit", Passed: true},
{Name: "fmt", Passed: true},
},
PassedCount: 2,
}
assert.True(t, result.Passed)
assert.Equal(t, 2, result.PassedCount)
assert.Equal(t, 0, result.FailedCount)
}