test API

test

package

API reference for the test package.

S
struct

plugin

cmd/rfw/plugins/test/test.go:19-19
type plugin struct

Methods

Name
Method

Returns

string
func (*plugin) Name() string
{ return "test" }
Priority
Method

Returns

int
func (*plugin) Priority() int
{ return 0 }
Build
Method

Parameters

Returns

error
func (*plugin) Build(raw json.RawMessage) error
{
	cfg := struct {
		Packages []string `json:"packages"`
	}{Packages: []string{"./..."}}
	if len(raw) > 0 {
		if err := json.Unmarshal(raw, &cfg); err != nil {
			return fmt.Errorf("decode test plugin config: %w", err)
		}
	}
	for _, pattern := range configuredPackages(cfg.Packages) {
		cmd, err := packageTestCommand(pattern)
		if err != nil {
			return err
		}
		output, runErr := cmd.CombinedOutput()
		out := strings.TrimSpace(string(output))
		if runErr != nil {
			logging.Log.Error("go test failed", logging.F("plugin", "test"), logging.F("package", pattern), logging.F("output", out), logging.F("error", runErr.Error()))
			return runErr
		}
		logging.Log.Info("go test ok", logging.F("plugin", "test"), logging.F("package", pattern), logging.F("output", out))
	}
	return nil
}
ShouldRebuild
Method

Parameters

path string

Returns

bool
func (*plugin) ShouldRebuild(path string) bool
{
	return strings.HasSuffix(path, "_test.go")
}
F
function

init

cmd/rfw/plugins/test/test.go:21-21
func init()

{ plugins.Register(&plugin{}) }
F
function

configuredPackages

Parameters

packages
[]string

Returns

[]string
cmd/rfw/plugins/test/test.go:52-57
func configuredPackages(packages []string) []string

{
	if len(packages) == 0 {
		return []string{"."}
	}
	return packages
}
F
function

validatePackagePattern

Parameters

pkg
string

Returns

error
cmd/rfw/plugins/test/test.go:59-88
func validatePackagePattern(pkg string) error

{
	if pkg == "." || pkg == "./..." {
		return nil
	}
	if pkg == "" || strings.HasPrefix(pkg, "-") || strings.HasPrefix(pkg, "/") {
		return fmt.Errorf("test package %q is not a package pattern", pkg)
	}
	for _, char := range pkg {
		if unicode.IsSpace(char) || unicode.IsControl(char) {
			return fmt.Errorf("test package %q contains whitespace or control characters", pkg)
		}
		if char >= 'a' && char <= 'z' ||
			char >= 'A' && char <= 'Z' ||
			char >= '0' && char <= '9' ||
			strings.ContainsRune("-_./+", char) {
			continue
		}
		return fmt.Errorf("test package %q contains invalid character %q", pkg, char)
	}
	segments := strings.Split(strings.TrimPrefix(pkg, "./"), "/")
	for index, segment := range segments {
		if segment == "" || segment == "." || segment == ".." {
			return fmt.Errorf("test package %q escapes the project", pkg)
		}
		if segment == "..." && index != len(segments)-1 {
			return fmt.Errorf("test package %q has a non-terminal recursive wildcard", pkg)
		}
	}
	return nil
}
F
function

packageTestCommand

Parameters

pattern
string

Returns

error
cmd/rfw/plugins/test/test.go:90-133
func packageTestCommand(pattern string) (*exec.Cmd, error)

{
	if err := validatePackagePattern(pattern); err != nil {
		return nil, err
	}
	localPattern := pattern
	if pattern != "." && !strings.HasPrefix(pattern, "./") {
		moduleOutput, err := exec.Command("go", "list", "-m").Output()
		if err != nil {
			return nil, fmt.Errorf("resolve project module: %w", err)
		}
		modulePath := strings.TrimSpace(string(moduleOutput))
		switch {
		case pattern == modulePath:
			localPattern = "."
		case strings.HasPrefix(pattern, modulePath+"/"):
			localPattern = "./" + strings.TrimPrefix(pattern, modulePath+"/")
		default:
			return nil, fmt.Errorf("test package %q is outside module %q", pattern, modulePath)
		}
	}
	switch localPattern {
	case ".":
		return exec.Command("go", "test", "."), nil
	case "./...":
		return exec.Command("go", "test", "./..."), nil
	}

	relative := strings.TrimPrefix(localPattern, "./")
	recursive := strings.HasSuffix(relative, "/...")
	relative = strings.TrimSuffix(relative, "/...")
	directory, err := resolveProjectDirectory(relative)
	if err != nil {
		return nil, fmt.Errorf("test package %q: %w", pattern, err)
	}

	var cmd *exec.Cmd
	if recursive {
		cmd = exec.Command("go", "test", "./...")
	} else {
		cmd = exec.Command("go", "test", ".")
	}
	cmd.Dir = directory
	return cmd, nil
}
F
function

resolveProjectDirectory

Parameters

relative
string

Returns

string
error
cmd/rfw/plugins/test/test.go:135-160
func resolveProjectDirectory(relative string) (string, error)

{
	projectRoot, err := filepath.Abs(".")
	if err != nil {
		return "", err
	}
	resolvedRoot, err := filepath.EvalSymlinks(projectRoot)
	if err != nil {
		return "", err
	}
	resolvedDirectory, err := filepath.EvalSymlinks(filepath.Join(resolvedRoot, filepath.Clean(relative)))
	if err != nil {
		return "", err
	}
	info, err := os.Stat(resolvedDirectory)
	if err != nil {
		return "", err
	}
	if !info.IsDir() {
		return "", fmt.Errorf("%q is not a directory", relative)
	}
	pathWithinRoot, err := filepath.Rel(resolvedRoot, resolvedDirectory)
	if err != nil || pathWithinRoot == ".." || strings.HasPrefix(pathWithinRoot, ".."+string(filepath.Separator)) {
		return "", fmt.Errorf("%q resolves outside the project", relative)
	}
	return resolvedDirectory, nil
}
F
function

TestShouldRebuild

TestShouldRebuild verifies that the test plugin triggers rebuilds for Go test
files only.

Parameters

cmd/rfw/plugins/test/test_test.go:13-21
func TestShouldRebuild(t *testing.T)

{
	p := &plugin{}
	if !p.ShouldRebuild("foo_test.go") {
		t.Fatalf("expected rebuild for _test.go files")
	}
	if p.ShouldRebuild("main.go") {
		t.Fatalf("non-test files should not trigger rebuild")
	}
}
F
function

TestValidatePackagePattern

Parameters

cmd/rfw/plugins/test/test_test.go:23-34
func TestValidatePackagePattern(t *testing.T)

{
	for _, pattern := range []string{".", "./...", "./pkg/...", "./foo-bar", "example.com/team/module/pkg"} {
		if err := validatePackagePattern(pattern); err != nil {
			t.Errorf("expected %q to be valid: %v", pattern, err)
		}
	}
	for _, pattern := range []string{"", "-exec=sh", "../...", "/tmp/pkg", "./pkg name", "./pkg=./other", "./pkg/.../nested"} {
		if err := validatePackagePattern(pattern); err == nil {
			t.Errorf("expected %q to be rejected", pattern)
		}
	}
}
F
function

TestConfiguredPackagesDefaultsToCurrentPackage

Parameters

cmd/rfw/plugins/test/test_test.go:36-48
func TestConfiguredPackagesDefaultsToCurrentPackage(t *testing.T)

{
	for _, packages := range [][]string{nil, {}} {
		configured := configuredPackages(packages)
		if len(configured) != 1 || configured[0] != "." {
			t.Fatalf("configured packages %v, want current package", configured)
		}
	}

	configured := configuredPackages([]string{"./..."})
	if len(configured) != 1 || configured[0] != "./..." {
		t.Fatalf("configured packages %v, want recursive project pattern", configured)
	}
}
F
function

TestPackageTestCommand

Parameters

cmd/rfw/plugins/test/test_test.go:50-105
func TestPackageTestCommand(t *testing.T)

{
	root := t.TempDir()
	packageDirectory := filepath.Join(root, "pkg")
	if err := os.Mkdir(packageDirectory, 0o750); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(filepath.Join(root, "go.mod"), []byte("module example.com/team/module\n\ngo 1.25\n"), 0o600); err != nil {
		t.Fatal(err)
	}
	oldWorkingDirectory, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}
	if err := os.Chdir(root); err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() {
		if err := os.Chdir(oldWorkingDirectory); err != nil {
			t.Errorf("restore working directory: %v", err)
		}
	})

	cmd, err := packageTestCommand("./pkg/...")
	if err != nil {
		t.Fatalf("create package test command: %v", err)
	}
	if cmd.Path == "" || len(cmd.Args) != 3 || cmd.Args[1] != "test" || cmd.Args[2] != "./..." {
		t.Fatalf("unexpected command: path=%q args=%v", cmd.Path, cmd.Args)
	}
	resolvedPackageDirectory, err := filepath.EvalSymlinks(packageDirectory)
	if err != nil {
		t.Fatal(err)
	}
	if cmd.Dir != resolvedPackageDirectory {
		t.Fatalf("command directory %q, want %q", cmd.Dir, resolvedPackageDirectory)
	}

	moduleCommand, err := packageTestCommand("example.com/team/module/pkg")
	if err != nil {
		t.Fatalf("create module package test command: %v", err)
	}
	if moduleCommand.Dir != resolvedPackageDirectory || moduleCommand.Args[2] != "." {
		t.Fatalf("unexpected module command: dir=%q args=%v", moduleCommand.Dir, moduleCommand.Args)
	}
	if _, err := packageTestCommand("example.com/other/module/pkg"); err == nil {
		t.Fatal("expected external module package to be rejected")
	}

	outside := t.TempDir()
	if err := os.Symlink(outside, filepath.Join(root, "outside")); err != nil {
		t.Fatal(err)
	}
	if _, err := packageTestCommand("./outside"); err == nil {
		t.Fatal("expected symlink escape to be rejected")
	}
}