Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,40 @@ import (
"testing"

"github.com/pelletier/go-toml/v2"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TestLoadFirstRunAfterConfigDirRecreated reproduces the scenario where a user
// deletes ~/.config/lstk/ and then runs lstk. newLogger() recreates the directory
// (for lstk.log) before config.Load() is called, so Load() must detect the
// MISSING FILE — not just the directory — to correctly set firstRun=true.
func TestLoadFirstRunAfterConfigDirRecreated(t *testing.T) {
// Cannot run in parallel: mutates process-wide HOME env and viper state.
fakeHome := t.TempDir()
t.Setenv("HOME", fakeHome)
t.Setenv("XDG_CONFIG_HOME", "")
viper.Reset()
t.Cleanup(viper.Reset)

// ~/.config/ exists so the creation dir is ~/.config/lstk (not the OS default).
homeConfig := filepath.Join(fakeHome, ".config")
require.NoError(t, os.MkdirAll(homeConfig, 0755))

// Simulate newLogger() recreating ~/.config/lstk/ (with a log file) after
// the user deleted the whole directory but before config.Load() runs.
configDir := filepath.Join(homeConfig, "lstk")
require.NoError(t, os.MkdirAll(configDir, 0755))
require.NoError(t, os.WriteFile(filepath.Join(configDir, "lstk.log"), []byte("log\n"), 0600))
// config.toml is intentionally absent — the user deleted it along with the dir.

firstRun, err := Load()

require.NoError(t, err)
assert.True(t, firstRun, "Load() should return firstRun=true when the config directory exists but config.toml does not")
}

func TestSetInFileAppendsWhenKeyAbsent(t *testing.T) {
path := filepath.Join(t.TempDir(), "config.toml")
original := `# User comment
Expand Down
55 changes: 55 additions & 0 deletions test/integration/emulator_select_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,61 @@ func TestEmulatorSelectionReappearsAfterFailedFirstRun(t *testing.T) {
<-outputCh
}

// Deleting the config directory after a successful run must trigger the emulator
// selector again on the next run — the selector is gated on the config file being
// absent, so the directory alone must not count as "already configured".
func TestEmulatorSelectionReappearsAfterConfigDirDeleted(t *testing.T) {
requireDocker(t)
t.Parallel()
if runtime.GOOS == "windows" {
t.Skip("PTY not supported on Windows")
}

tmpHome := t.TempDir()
require.NoError(t, os.MkdirAll(filepath.Join(tmpHome, ".config"), 0755))
e := env.Environ(testEnvWithHome(tmpHome, tmpHome)).
With(env.DisableEvents, "1").
With(env.AuthToken, "test-token")

// Resolve where lstk would create the config, then pre-create it so lstk
// believes this is not a first run (simulates a previous successful start).
configPath, _, err := runLstk(t, testContext(t), "", e, "config", "path")
require.NoError(t, err)
require.NoError(t, os.MkdirAll(filepath.Dir(configPath), 0755))
require.NoError(t, os.WriteFile(configPath, []byte("[[containers]]\ntype = \"aws\"\ntag = \"latest\"\nport = \"4566\"\n"), 0644))
require.FileExists(t, configPath)

// Delete the entire config directory — this is what the user reported.
require.NoError(t, os.RemoveAll(filepath.Dir(configPath)))
require.NoFileExists(t, configPath)

// The next run must show the emulator selector again, not silently default to AWS.
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()

cmd := exec.CommandContext(ctx, binaryPath(), "start")
cmd.Env = e

ptmx, err := pty.Start(cmd)
require.NoError(t, err, "failed to start lstk in PTY")
defer func() { _ = ptmx.Close() }()

out := &syncBuffer{}
outputCh := make(chan struct{})
go func() {
_, _ = io.Copy(out, ptmx)
close(outputCh)
}()

require.Eventually(t, func() bool {
return bytes.Contains(out.Bytes(), []byte("Which emulator would you like to use?"))
}, 10*time.Second, 100*time.Millisecond,
"emulator selection prompt should reappear after the config directory is deleted")

cancel()
<-outputCh
}

func TestFirstRunChecksDockerBeforeAuthAndSelection(t *testing.T) {
t.Parallel()
if runtime.GOOS == "windows" {
Expand Down
Loading