diff --git a/cmd/root_test.go b/cmd/root_test.go index 83bf770..5fa0d13 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -50,8 +50,8 @@ func TestRunInit(t *testing.T) { } }) - if got := mustReadFile(t, filepath.Join(root, "miro.toml")); got != "test_dir = \"e2e\"\n" { - t.Fatalf("config = %q, want %q", got, "test_dir = \"e2e\"\n") + if got := mustReadFile(t, filepath.Join(root, "miro.toml")); got != "[miro]\n test_dir = \"e2e\"\n" { + t.Fatalf("config = %q, want %q", got, "[miro]\n test_dir = \"e2e\"\n") } info, err := os.Stat(filepath.Join(root, "e2e", "shell.sh")) if err != nil { @@ -152,7 +152,7 @@ func TestRunRecordRejectsAbsolutePathOutsideTestDir(t *testing.T) { if err := os.MkdirAll(wantDir, 0o755); err != nil { t.Fatalf("MkdirAll() error = %v", err) } - writeFile(t, filepath.Join(root, "miro.toml"), "test_dir = \"e2e\"\n") + writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = \"e2e\"\n") outside := filepath.Join(root, "outside", "spec") withWorkingDir(t, root, func() { @@ -206,7 +206,7 @@ func TestRunRecordFailsWhenDependencyMissing(t *testing.T) { if err := os.MkdirAll(wantDir, 0o755); err != nil { t.Fatalf("MkdirAll() error = %v", err) } - writeFile(t, filepath.Join(root, "miro.toml"), "test_dir = \"e2e\"\n") + writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = \"e2e\"\n") for _, tc := range []struct { name string diff --git a/internal/config/config.go b/internal/config/config.go index 7ecd74f..f595275 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -14,6 +14,10 @@ type Config struct { } type tomlConfig struct { + Miro tomlMiroConfig `toml:"miro"` +} + +type tomlMiroConfig struct { TestDir string `toml:"test_dir"` } @@ -28,27 +32,32 @@ func ReadConfig(path string) (Config, error) { } return Config{}, fmt.Errorf("failed to read %s: %v", path, err) } - if !meta.IsDefined("test_dir") { - return Config{}, fmt.Errorf("failed to read %s: missing required test_dir", path) + if !meta.IsDefined("miro") { + return Config{}, fmt.Errorf("failed to read %s: missing [miro] config", path) } - if raw.TestDir == "" { - return Config{}, fmt.Errorf("failed to read %s: empty test_dir", path) + if !meta.IsDefined("miro", "test_dir") { + return Config{}, fmt.Errorf("failed to read %s: missing required miro.test_dir", path) + } + if raw.Miro.TestDir == "" { + return Config{}, fmt.Errorf("failed to read %s: empty miro.test_dir", path) } return Config{ - TestDir: raw.TestDir, + TestDir: raw.Miro.TestDir, }, nil } // WriteConfig writes miro.toml. func WriteConfig(path string, cfg Config) error { if cfg.TestDir == "" { - return errors.New("empty test_dir") + return errors.New("empty miro.test_dir") } var buf bytes.Buffer if err := toml.NewEncoder(&buf).Encode(tomlConfig{ - TestDir: cfg.TestDir, + Miro: tomlMiroConfig{ + TestDir: cfg.TestDir, + }, }); err != nil { return fmt.Errorf("failed to encode %s: %v", path, err) } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 810e1ac..91d3d0f 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -18,27 +18,32 @@ func TestReadConfig(t *testing.T) { }{ { name: "with test dir", - content: "test_dir = \"custom/suite\"\n", + content: "[miro]\ntest_dir = \"custom/suite\"\n", wantDir: "custom/suite", }, { - name: "legacy miro table", - content: "[miro]\ntest_dir = \"custom/suite\"\n", - wantErr: "missing required test_dir", + name: "legacy top level key", + content: "test_dir = \"custom/suite\"\n", + wantErr: "missing [miro] config", + }, + { + name: "without miro table", + content: "", + wantErr: "missing [miro] config", }, { name: "without test dir", - content: "", - wantErr: "missing required test_dir", + content: "[miro]\n", + wantErr: "missing required miro.test_dir", }, { name: "empty test dir", - content: "test_dir = \"\"\n", - wantErr: "empty test_dir", + content: "[miro]\ntest_dir = \"\"\n", + wantErr: "empty miro.test_dir", }, { name: "invalid toml", - content: "test_dir = [\n", + content: "[miro]\ntest_dir = [\n", wantErr: "failed to read", }, { @@ -93,8 +98,8 @@ func TestWriteConfig(t *testing.T) { if err != nil { t.Fatalf("ReadFile() error = %v", err) } - if string(got) != "test_dir = \"e2e\"\n" { - t.Fatalf("config = %q, want %q", string(got), "test_dir = \"e2e\"\n") + if string(got) != "[miro]\n test_dir = \"e2e\"\n" { + t.Fatalf("config = %q, want %q", string(got), "[miro]\n test_dir = \"e2e\"\n") } } @@ -105,7 +110,7 @@ func TestWriteConfigEmptyTestDirFails(t *testing.T) { if err == nil { t.Fatal("WriteConfig() error = nil, want error") } - if err.Error() != "empty test_dir" { - t.Fatalf("WriteConfig() error = %q, want %q", err.Error(), "empty test_dir") + if err.Error() != "empty miro.test_dir" { + t.Fatalf("WriteConfig() error = %q, want %q", err.Error(), "empty miro.test_dir") } } diff --git a/internal/miro/record_test.go b/internal/miro/record_test.go index 11ffb37..c6280b7 100644 --- a/internal/miro/record_test.go +++ b/internal/miro/record_test.go @@ -15,7 +15,7 @@ func TestRecordCreatesRelativePath(t *testing.T) { root := t.TempDir() testDir := filepath.Join(root, "e2e") mustMkdirAll(t, testDir) - writeFile(t, filepath.Join(root, "miro.toml"), "test_dir = \"e2e\"\n") + writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = \"e2e\"\n") mustWriteRecordShell(t, testDir) addFakeRecordDependencies(t, "script") @@ -51,7 +51,7 @@ func TestRecordAcceptsExplicitTestDirPrefix(t *testing.T) { root := t.TempDir() testDir := filepath.Join(root, "e2e") mustMkdirAll(t, testDir) - writeFile(t, filepath.Join(root, "miro.toml"), "test_dir = \"e2e\"\n") + writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = \"e2e\"\n") mustWriteRecordShell(t, testDir) addFakeRecordDependencies(t, "script") @@ -83,7 +83,7 @@ func TestRecordAcceptsExplicitTestDirPrefix(t *testing.T) { func TestRecordRejectsAbsolutePathOutsideTestDir(t *testing.T) { root := t.TempDir() mustMkdirAll(t, filepath.Join(root, "e2e")) - writeFile(t, filepath.Join(root, "miro.toml"), "test_dir = \"e2e\"\n") + writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = \"e2e\"\n") outside := filepath.Join(root, "outside", "a", "b", "c") err := withWorkingDir(t, root, func() error { @@ -324,7 +324,7 @@ func TestRecordScenarioUsesDeterministicSandbox(t *testing.T) { func TestRecordFailsWhenRecorderShellMissing(t *testing.T) { root := t.TempDir() testDir := filepath.Join(root, "e2e") - writeFile(t, filepath.Join(root, "miro.toml"), "test_dir = \"e2e\"\n") + writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = \"e2e\"\n") mustMkdirAll(t, testDir) addFakeRecordDependencies(t, "script") diff --git a/internal/miro/testdir_test.go b/internal/miro/testdir_test.go index 95e6b6f..cc8e44d 100644 --- a/internal/miro/testdir_test.go +++ b/internal/miro/testdir_test.go @@ -19,8 +19,8 @@ func TestInitCreatesConfigAtProjectRoot(t *testing.T) { }) got := readFile(t, filepath.Join(root, "miro.toml")) - if got != "test_dir = \"e2e\"\n" { - t.Fatalf("config = %q, want %q", got, "test_dir = \"e2e\"\n") + if got != "[miro]\n test_dir = \"e2e\"\n" { + t.Fatalf("config = %q, want %q", got, "[miro]\n test_dir = \"e2e\"\n") } assertRecordShell(t, filepath.Join(root, "e2e", recordShellName)) } @@ -46,7 +46,7 @@ func TestInitUsesGitRoot(t *testing.T) { func TestInitLeavesExistingValidConfigUntouched(t *testing.T) { root := t.TempDir() - writeFile(t, filepath.Join(root, "miro.toml"), "test_dir = \"custom/suite\"\n") + writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = \"custom/suite\"\n") writeFile(t, filepath.Join(root, "custom", "suite", recordShellName), "outdated\n") withWorkingDir(t, root, func() struct{} { @@ -57,15 +57,15 @@ func TestInitLeavesExistingValidConfigUntouched(t *testing.T) { }) got := readFile(t, filepath.Join(root, "miro.toml")) - if got != "test_dir = \"custom/suite\"\n" { - t.Fatalf("config = %q, want %q", got, "test_dir = \"custom/suite\"\n") + if got != "[miro]\ntest_dir = \"custom/suite\"\n" { + t.Fatalf("config = %q, want %q", got, "[miro]\ntest_dir = \"custom/suite\"\n") } assertRecordShell(t, filepath.Join(root, "custom", "suite", recordShellName)) } func TestInitCreatesMissingConfiguredTestDir(t *testing.T) { root := t.TempDir() - writeFile(t, filepath.Join(root, "miro.toml"), "test_dir = \"custom/suite\"\n") + writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = \"custom/suite\"\n") withWorkingDir(t, root, func() struct{} { if err := Init(); err != nil { @@ -79,7 +79,7 @@ func TestInitCreatesMissingConfiguredTestDir(t *testing.T) { func TestInitFailsWhenExistingConfigInvalid(t *testing.T) { root := t.TempDir() - writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = \"e2e\"\n") + writeFile(t, filepath.Join(root, "miro.toml"), "test_dir = \"e2e\"\n") err := withWorkingDir(t, root, func() error { return Init() @@ -87,8 +87,8 @@ func TestInitFailsWhenExistingConfigInvalid(t *testing.T) { if err == nil { t.Fatal("Init() error = nil, want error") } - if !strings.Contains(err.Error(), "missing required test_dir") { - t.Fatalf("Init() error = %q, want missing test_dir error", err.Error()) + if !strings.Contains(err.Error(), "missing [miro] config") { + t.Fatalf("Init() error = %q, want missing [miro] config error", err.Error()) } } @@ -96,7 +96,7 @@ func TestResolveTestDirFromConfig(t *testing.T) { root := t.TempDir() configured := filepath.Join(root, "custom", "suite") mustMkdirAll(t, configured) - writeFile(t, filepath.Join(root, "miro.toml"), "test_dir = \"custom/suite\"\n") + writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = \"custom/suite\"\n") got := withWorkingDir(t, root, func() string { path, err := ResolveTestDir() @@ -129,7 +129,7 @@ func TestResolveTestDirMissingConfigFails(t *testing.T) { func TestResolveTestDirMissingTestDirFails(t *testing.T) { root := t.TempDir() - writeFile(t, filepath.Join(root, "miro.toml"), "") + writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\n") err := withWorkingDir(t, root, func() error { _, err := ResolveTestDir() @@ -139,14 +139,14 @@ func TestResolveTestDirMissingTestDirFails(t *testing.T) { if err == nil { t.Fatal("ResolveTestDir() error = nil, want error") } - if !strings.Contains(err.Error(), "missing required test_dir") { - t.Fatalf("ResolveTestDir() error = %q, want missing required test_dir", err.Error()) + if !strings.Contains(err.Error(), "missing required miro.test_dir") { + t.Fatalf("ResolveTestDir() error = %q, want missing required miro.test_dir", err.Error()) } } func TestResolveTestDirEmptyTestDirFails(t *testing.T) { root := t.TempDir() - writeFile(t, filepath.Join(root, "miro.toml"), "test_dir = \"\"\n") + writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = \"\"\n") err := withWorkingDir(t, root, func() error { _, err := ResolveTestDir() @@ -156,14 +156,14 @@ func TestResolveTestDirEmptyTestDirFails(t *testing.T) { if err == nil { t.Fatal("ResolveTestDir() error = nil, want error") } - if !strings.Contains(err.Error(), "empty test_dir") { - t.Fatalf("ResolveTestDir() error = %q, want empty test_dir", err.Error()) + if !strings.Contains(err.Error(), "empty miro.test_dir") { + t.Fatalf("ResolveTestDir() error = %q, want empty miro.test_dir", err.Error()) } } func TestResolveTestDirMalformedConfigFails(t *testing.T) { root := t.TempDir() - writeFile(t, filepath.Join(root, "miro.toml"), "test_dir = [\n") + writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = [\n") err := withWorkingDir(t, root, func() error { _, err := ResolveTestDir() @@ -181,7 +181,7 @@ func TestResolveTestDirMalformedConfigFails(t *testing.T) { func TestResolveTestDirConfiguredMissingDirectoryReturnsConfiguredPath(t *testing.T) { root := t.TempDir() want := filepath.Join(root, "missing") - writeFile(t, filepath.Join(root, "miro.toml"), "test_dir = \"missing\"\n") + writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = \"missing\"\n") got := withWorkingDir(t, root, func() string { path, err := ResolveTestDir() @@ -199,7 +199,7 @@ func TestResolveTestDirConfiguredMissingDirectoryReturnsConfiguredPath(t *testin func TestResolveTestDirConfiguredFileFails(t *testing.T) { root := t.TempDir() writeFile(t, filepath.Join(root, "case.txt"), "hello\n") - writeFile(t, filepath.Join(root, "miro.toml"), "test_dir = \"case.txt\"\n") + writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = \"case.txt\"\n") err := withWorkingDir(t, root, func() error { _, err := ResolveTestDir() @@ -218,7 +218,7 @@ func TestResolveTestDirUsesGitRoot(t *testing.T) { root := t.TempDir() mustGitInit(t, root) want := filepath.Join(root, "e2e") - writeFile(t, filepath.Join(root, "miro.toml"), "test_dir = \"e2e\"\n") + writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = \"e2e\"\n") subdir := filepath.Join(root, "nested", "dir") mustMkdirAll(t, subdir)