asdf/repotest/repotest.go
Trevor Brown 80ac9bb51c fix(golang-rewrite): allow directories returned by list-bin-paths to be absent from the file system
* create `repotest.WritePluginCallback` function
* add test for `list-bin-paths` callback that returns non-existent path
* if directory name returned by `list-bin-paths` doesn't exist skip it
2024-12-18 11:32:03 -05:00

219 lines
5.9 KiB
Go

// Package repotest contains various test helpers for tests that work with code
// relying on plugin Git repos and the asdf plugin index
//
// Three main actions:
//
// * Install plugin index repo into asdf (index contains records that point to
// local plugins defined by this package)
// * Install plugin into asdf data dir
// * Create local plugin repo that can be cloned into asdf
package repotest
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
cp "github.com/otiai10/copy"
)
const fixturesDir = "fixtures"
// Setup copies all files into place and initializes all repos for any Go test
// that needs either plugin repos or the plugin index repo.
func Setup(asdfDataDir string) error {
if err := InstallPluginIndex(asdfDataDir); err != nil {
return err
}
return nil
}
// WritePluginCallback is for creating new plugin callbacks on the fly.
func WritePluginCallback(pluginDir, callbackName, script string) error {
return os.WriteFile(filepath.Join(pluginDir, "bin", callbackName), []byte(script), 0o777)
}
// InstallPlugin copies in the specified plugin fixture into the asdfDataDir's
// plugin directory and initializes a Git repo for it so asdf treats it as
// installed.
func InstallPlugin(fixtureName, asdfDataDir, pluginName string) (string, error) {
root, err := getModuleRoot()
if err != nil {
return "", err
}
destDir := filepath.Join(asdfDataDir, "plugins")
return generatePluginInDir(root, fixtureName, destDir, pluginName)
}
// GeneratePlugin copies in the specified plugin fixture into a test directory
// and initializes a Git repo for it so it can be installed by asdf.
func GeneratePlugin(fixtureName, dir, pluginName string) (string, error) {
root, err := getModuleRoot()
if err != nil {
return "", err
}
fixturesDir := filepath.Join(dir, fixturesDir)
return generatePluginInDir(root, fixtureName, fixturesDir, pluginName)
}
// InstallPluginIndex generates and installs a plugin index Git repo inside of
// the provided asdf data directory.
func InstallPluginIndex(asdfDataDir string) error {
root, err := getModuleRoot()
if err != nil {
return err
}
// Copy in plugin index
source := filepath.Join(root, "test/fixtures/dummy_plugins_repo")
return cp.Copy(source, filepath.Join(asdfDataDir, "plugin-index"))
}
// GeneratePluginIndex generates a mock plugin index Git repo inside the given
// directory.
func GeneratePluginIndex(asdfDataDir string) (string, error) {
root, err := getModuleRoot()
if err != nil {
return "", err
}
// Copy in plugin index
source := filepath.Join(root, "test/fixtures/dummy_plugins_repo")
destination := filepath.Join(asdfDataDir, fixturesDir, "plugin-index")
err = cp.Copy(source, destination)
if err != nil {
return destination, fmt.Errorf("unable to copy in plugin index: %w", err)
}
// Generate git repo for plugin
return createGitRepo(destination)
}
func generatePluginInDir(root, fixtureName, outputDir, pluginName string) (string, error) {
// Copy in plugin files into output dir
pluginPath, err := copyInPlugin(root, fixtureName, outputDir, pluginName)
if err != nil {
return pluginPath, fmt.Errorf("unable to copy in plugin files: %w", err)
}
// Generate git repo for plugin
return createGitRepo(pluginPath)
}
func getModuleRoot() (string, error) {
cwd, err := os.Getwd()
if err != nil {
return "", fmt.Errorf("unable to get current working directory: %w", err)
}
root := findModuleRoot(cwd)
return root, nil
}
func createGitRepo(location string) (string, error) {
// Definitely some opportunities to refactor here. This code might be
// simplified by switching to the Go git library
err := runCmd("git", "-C", location, "init", "-q")
if err != nil {
return location, err
}
err = runCmd("git", "-C", location, "config", "user.name", "\"Test\"")
if err != nil {
return location, err
}
err = runCmd("git", "-C", location, "config", "user.email", "\"test@example.com\"")
if err != nil {
return location, err
}
err = runCmd("git", "-C", location, "add", "-A")
if err != nil {
return location, err
}
err = runCmd("git", "-C", location, "commit", "-q", "-m", "init repo")
if err != nil {
return location, err
}
err = runCmd("touch", filepath.Join(location, "README.md"))
if err != nil {
return location, err
}
err = runCmd("git", "-C", location, "add", "-A")
if err != nil {
return location, err
}
err = runCmd("git", "-C", location, "commit", "-q", "-m", "add readme")
if err != nil {
return location, err
}
// kind of ugly but I want a remote with a valid path so I use the same
// location as the remote. Probably should refactor
err = runCmd("git", "-C", location, "remote", "add", "origin", location)
if err != nil {
return location, err
}
return location, err
}
func copyInPlugin(root, name, destination, newName string) (string, error) {
source := filepath.Join(root, "test/fixtures/", name)
dest := filepath.Join(destination, newName)
return dest, cp.Copy(source, dest)
}
// Taken from https://github.com/golang/go/blob/9e3b1d53a012e98cfd02de2de8b1bd53522464d4/src/cmd/go/internal/modload/init.go#L1504C1-L1522C2 because that function is in an internal module
// and I can't rely on it.
func findModuleRoot(dir string) (roots string) {
if dir == "" {
panic("dir not set")
}
dir = filepath.Clean(dir)
// Look for enclosing go.mod.
for {
if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
return dir
}
d := filepath.Dir(dir)
if d == dir {
break
}
dir = d
}
return ""
}
// helper function to make running commands easier
func runCmd(cmdName string, args ...string) error {
cmd := exec.Command(cmdName, args...)
// Capture stdout and stderr
var stdout strings.Builder
var stderr strings.Builder
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
// If command fails print both stderr and stdout
fmt.Println("stdout:", stdout.String())
fmt.Println("stderr:", stderr.String())
return err
}
return nil
}