chore(go): inject stdin and pass rc force explicitly

This commit is contained in:
Micheal Wilkinson
2026-03-21 20:45:05 +00:00
parent 038b109e7b
commit d642870a66
7 changed files with 66 additions and 66 deletions

View File

@@ -21,13 +21,15 @@ type App struct {
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
Verbose bool
Force bool
Quiet bool
Pretend bool
}
func New(stdout io.Writer, stderr io.Writer) (*App, error) {
func NewApp(stdin io.Reader, stdout io.Writer, stderr io.Writer) (*App, error) {
if stdin == nil {
return nil, errors.New("stdin reader cannot be nil")
}
if stdout == nil {
return nil, errors.New("stdout writer cannot be nil")
}
@@ -43,7 +45,7 @@ func New(stdout io.Writer, stderr io.Writer) (*App, error) {
return &App{
HomeDir: home,
ReposDir: filepath.Join(home, ".homesick", "repos"),
Stdin: os.Stdin,
Stdin: stdin,
Stdout: stdout,
Stderr: stderr,
}, nil
@@ -845,7 +847,7 @@ func gitOutput(dir string, args ...string) (string, error) {
// If a .homesickrc file exists in the castle root and no parity.rb wrapper
// already exists in .homesick.d, a Ruby wrapper script named parity.rb is
// written there before execution so that it sorts first.
func (a *App) Rc(castle string) error {
func (a *App) Rc(castle string, force bool) error {
castleRoot := filepath.Join(a.ReposDir, castle)
if _, err := os.Stat(castleRoot); err != nil {
if errors.Is(err, os.ErrNotExist) {
@@ -857,7 +859,7 @@ func (a *App) Rc(castle string) error {
homesickD := filepath.Join(castleRoot, ".homesick.d")
homesickRc := filepath.Join(castleRoot, ".homesickrc")
if _, err := os.Stat(homesickRc); err == nil && !a.Force {
if _, err := os.Stat(homesickRc); err == nil && !force {
return errors.New("refusing to run legacy .homesickrc without --force")
}

View File

@@ -6,9 +6,19 @@ import (
"testing"
)
func TestNewRejectsNilWriters(t *testing.T) {
func TestNewAppRejectsNilReaders(t *testing.T) {
t.Run("nil stdin", func(t *testing.T) {
app, err := NewApp(nil, &bytes.Buffer{}, &bytes.Buffer{})
if err == nil {
t.Fatal("expected error for nil stdin")
}
if app != nil {
t.Fatal("expected nil app for nil stdin")
}
})
t.Run("nil stdout", func(t *testing.T) {
app, err := New(nil, &bytes.Buffer{})
app, err := NewApp(new(bytes.Buffer), nil, &bytes.Buffer{})
if err == nil {
t.Fatal("expected error for nil stdout")
}
@@ -18,7 +28,7 @@ func TestNewRejectsNilWriters(t *testing.T) {
})
t.Run("nil stderr", func(t *testing.T) {
app, err := New(&bytes.Buffer{}, nil)
app, err := NewApp(new(bytes.Buffer), &bytes.Buffer{}, nil)
if err == nil {
t.Fatal("expected error for nil stderr")
}
@@ -49,11 +59,12 @@ func TestDeriveDestination(t *testing.T) {
}
}
func TestNewInitializesApp(t *testing.T) {
func TestNewAppInitializesApp(t *testing.T) {
stdin := new(bytes.Buffer)
stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
app, err := New(stdout, stderr)
app, err := NewApp(stdin, stdout, stderr)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -61,6 +72,9 @@ func TestNewInitializesApp(t *testing.T) {
t.Fatal("expected app instance")
}
if app.Stdin != stdin {
t.Fatal("expected stdin reader to be assigned")
}
if app.Stdout != stdout {
t.Fatal("expected stdout writer to be assigned")
}

View File

@@ -53,7 +53,7 @@ var _ io.Writer
// TestRc_UnknownCastleReturnsError ensures Rc returns an error when the
// castle directory does not exist.
func (s *RcSuite) TestRc_UnknownCastleReturnsError() {
err := s.app.Rc("nonexistent")
err := s.app.Rc("nonexistent", false)
require.Error(s.T(), err)
}
@@ -61,7 +61,7 @@ func (s *RcSuite) TestRc_UnknownCastleReturnsError() {
// .homesickrc are present.
func (s *RcSuite) TestRc_NoScriptsAndNoHomesickrcIsNoop() {
s.createCastle("dotfiles")
require.NoError(s.T(), s.app.Rc("dotfiles"))
require.NoError(s.T(), s.app.Rc("dotfiles", false))
}
// TestRc_HomesickrcRequiresForce ensures legacy .homesickrc does not run
@@ -71,7 +71,7 @@ func (s *RcSuite) TestRc_HomesickrcRequiresForce() {
homesickRc := filepath.Join(castleRoot, ".homesickrc")
require.NoError(s.T(), os.WriteFile(homesickRc, []byte("# ruby setup code\n"), 0o644))
err := s.app.Rc("dotfiles")
err := s.app.Rc("dotfiles", false)
require.Error(s.T(), err)
require.Contains(s.T(), err.Error(), "--force")
require.NoFileExists(s.T(), filepath.Join(castleRoot, ".homesick.d", "parity.rb"))
@@ -84,8 +84,7 @@ func (s *RcSuite) TestRc_HomesickrcRunsWithForce() {
homesickRc := filepath.Join(castleRoot, ".homesickrc")
require.NoError(s.T(), os.WriteFile(homesickRc, []byte("# ruby setup code\n"), 0o644))
s.app.Force = true
require.NoError(s.T(), s.app.Rc("dotfiles"))
require.NoError(s.T(), s.app.Rc("dotfiles", true))
require.FileExists(s.T(), filepath.Join(castleRoot, ".homesick.d", "parity.rb"))
}
@@ -103,7 +102,7 @@ func (s *RcSuite) TestRc_ExecutesScriptsInSortedOrder() {
require.NoError(s.T(), os.WriteFile(scriptA, []byte("#!/bin/sh\necho a >> "+orderFile+"\n"), 0o755))
require.NoError(s.T(), os.WriteFile(scriptB, []byte("#!/bin/sh\necho b >> "+orderFile+"\n"), 0o755))
require.NoError(s.T(), s.app.Rc("dotfiles"))
require.NoError(s.T(), s.app.Rc("dotfiles", false))
content, err := os.ReadFile(orderFile)
require.NoError(s.T(), err)
@@ -121,7 +120,7 @@ func (s *RcSuite) TestRc_SkipsNonExecutableFiles() {
// Write a script that would exit 1 if actually run — verify it is skipped.
require.NoError(s.T(), os.WriteFile(notExec, []byte("#!/bin/sh\nexit 1\n"), 0o644))
require.NoError(s.T(), s.app.Rc("dotfiles"))
require.NoError(s.T(), s.app.Rc("dotfiles", false))
}
// TestRc_HomesickrcCreatesRubyWrapper verifies that a .homesickrc file causes
@@ -130,9 +129,7 @@ func (s *RcSuite) TestRc_HomesickrcCreatesRubyWrapper() {
castleRoot := s.createCastle("dotfiles")
homesickRc := filepath.Join(castleRoot, ".homesickrc")
require.NoError(s.T(), os.WriteFile(homesickRc, []byte("# ruby setup code\n"), 0o644))
s.app.Force = true
require.NoError(s.T(), s.app.Rc("dotfiles"))
require.NoError(s.T(), s.app.Rc("dotfiles", true))
wrapperPath := filepath.Join(castleRoot, ".homesick.d", "parity.rb")
require.FileExists(s.T(), wrapperPath)
@@ -152,7 +149,6 @@ func (s *RcSuite) TestRc_HomesickrcWrapperNotOverwrittenIfExists() {
castleRoot := s.createCastle("dotfiles")
homesickRc := filepath.Join(castleRoot, ".homesickrc")
require.NoError(s.T(), os.WriteFile(homesickRc, []byte("# ruby setup code\n"), 0o644))
s.app.Force = true
homesickD := filepath.Join(castleRoot, ".homesick.d")
require.NoError(s.T(), os.MkdirAll(homesickD, 0o755))
@@ -160,7 +156,7 @@ func (s *RcSuite) TestRc_HomesickrcWrapperNotOverwrittenIfExists() {
originalContent := []byte("#!/bin/sh\n# pre-existing wrapper\n")
require.NoError(s.T(), os.WriteFile(wrapperPath, originalContent, 0o755))
require.NoError(s.T(), s.app.Rc("dotfiles"))
require.NoError(s.T(), s.app.Rc("dotfiles", true))
content, err := os.ReadFile(wrapperPath)
require.NoError(s.T(), err)
@@ -173,7 +169,6 @@ func (s *RcSuite) TestRc_HomesickrcWrapperCreatedBeforeExecution() {
castleRoot := s.createCastle("dotfiles")
homesickRc := filepath.Join(castleRoot, ".homesickrc")
require.NoError(s.T(), os.WriteFile(homesickRc, []byte("# ruby setup code\n"), 0o644))
s.app.Force = true
homesickD := filepath.Join(castleRoot, ".homesick.d")
require.NoError(s.T(), os.MkdirAll(homesickD, 0o755))
@@ -186,7 +181,7 @@ func (s *RcSuite) TestRc_HomesickrcWrapperCreatedBeforeExecution() {
"#!/bin/sh\n[ -f "+wrapperPath+" ] && echo present >> "+orderFile+"\n",
), 0o755))
require.NoError(s.T(), s.app.Rc("dotfiles"))
require.NoError(s.T(), s.app.Rc("dotfiles", true))
content, err := os.ReadFile(orderFile)
require.NoError(s.T(), err)
@@ -203,7 +198,7 @@ func (s *RcSuite) TestRc_FailingScriptReturnsError() {
failing := filepath.Join(homesickD, "10_fail.sh")
require.NoError(s.T(), os.WriteFile(failing, []byte("#!/bin/sh\nexit 42\n"), 0o755))
err := s.app.Rc("dotfiles")
err := s.app.Rc("dotfiles", false)
require.Error(s.T(), err)
}
@@ -217,7 +212,7 @@ func (s *RcSuite) TestRc_ScriptOutputForwarded() {
script := filepath.Join(homesickD, "10_output.sh")
require.NoError(s.T(), os.WriteFile(script, []byte("#!/bin/sh\necho hello\necho world >&2\n"), 0o755))
require.NoError(s.T(), s.app.Rc("dotfiles"))
require.NoError(s.T(), s.app.Rc("dotfiles", false))
require.Contains(s.T(), s.stdout.String(), "hello")
require.Contains(s.T(), s.stderr.String(), "world")
}
@@ -232,6 +227,6 @@ func (s *RcSuite) TestRc_ScriptsRunWithCwdSetToCastleRoot() {
script := filepath.Join(homesickD, "10_pwd.sh")
require.NoError(s.T(), os.WriteFile(script, []byte("#!/bin/sh\npwd\n"), 0o755))
require.NoError(s.T(), s.app.Rc("dotfiles"))
require.NoError(s.T(), s.app.Rc("dotfiles", false))
require.Contains(s.T(), s.stdout.String(), castleRoot)
}