diff options
Diffstat (limited to 'cmd/krino/matching_test.go')
| -rw-r--r-- | cmd/krino/matching_test.go | 99 |
1 files changed, 97 insertions, 2 deletions
diff --git a/cmd/krino/matching_test.go b/cmd/krino/matching_test.go index 614edc2..69c0eca 100644 --- a/cmd/krino/matching_test.go +++ b/cmd/krino/matching_test.go @@ -5,6 +5,7 @@ package main import ( "bytes" "encoding/json" + "fmt" "os" "path/filepath" "strings" @@ -148,8 +149,8 @@ func TestSortFlags(t *testing.T) { want string }{ {[]string{"-y", "-n"}, "-y and -n cannot be used together"}, - {nil, "applying files is not implemented yet; use -n to see what would happen"}, - {[]string{"--json"}, "--json is not implemented yet"}, // --json without -n stays an error + {nil, "not a terminal"}, // no -y, no -n, and the test's stdin is not one + {[]string{"--json"}, "--json is only valid with -n"}, // --json without -n stays an error } for _, tt := range tests { if code, _, errOut := runCLI(t, tt.args...); code != 2 || !strings.Contains(errOut, tt.want) { @@ -356,6 +357,100 @@ func TestDirectoryWarningNotCountedInWarningsField(t *testing.T) { // not stretched further), while a short name alongside it is still padded // out to the full 40-column cap — the layout stays a clean two-column grid // even though one row's first cell overruns it. +// TestApplyWithYesMovesFiles is brief 7's basic apply-path test: -y applies +// the plan with no prompt, the file actually moves, the outcome line says +// so, and the run is logged with both boundaries. +func TestApplyWithYesMovesFiles(t *testing.T) { + h := matchingFixture(t) + code, out, errOut := runCLI(t, "-y") + if code != 0 { + t.Fatalf("exit %d: %s", code, errOut) + } + if _, err := os.Stat(filepath.Join(h, "dl", "Work", "Acme", "inv1.txt")); err != nil { + t.Errorf("inv1.txt was not filed: %v", err) + } + if !strings.Contains(out, "applied") { + t.Errorf("no outcome line:\n%s", out) + } + log := filepath.Join(h, ".local", "state", "krino", "krino.log") + b, err := os.ReadFile(log) + if err != nil { + t.Fatalf("nothing was logged: %v", err) + } + if !strings.Contains(string(b), "run-start") || !strings.Contains(string(b), "run-end") { + t.Errorf("log lacks run boundaries:\n%s", b) + } +} + +// TestDryRunLogsNothing is Ruling 2: journal.Open must never be called at +// all in dry-run mode, since it materialises both the state directory and +// an empty log file as a side effect of merely opening it. +func TestDryRunLogsNothing(t *testing.T) { + h := matchingFixture(t) + if code, _, errOut := runCLI(t, "-n"); code != 0 { + t.Fatalf("exit %d: %s", code, errOut) + } + if _, err := os.Stat(filepath.Join(h, ".local", "state", "krino", "krino.log")); !os.IsNotExist(err) { + t.Error("a dry run wrote to the log") + } +} + +// TestRefusesWithoutTerminalAndWithoutFlags is spec §8.4: with neither -y +// nor -n, and stdin not a terminal (as in every test), krino refuses rather +// than guess. +func TestRefusesWithoutTerminalAndWithoutFlags(t *testing.T) { + matchingFixture(t) + code, _, errOut := runCLI(t) + if code != 2 || !strings.Contains(errOut, "not a terminal") { + t.Errorf("no-flags, no-terminal: %d %q", code, errOut) + } +} + +// TestSecondRunFailsImmediatelyWithYes is spec §11: a second krino on the +// same directory fails immediately with -y (wait = false) rather than +// piling up behind a stuck run, so a cron job never queues silently. +func TestSecondRunFailsImmediatelyWithYes(t *testing.T) { + h := matchingFixture(t) + held := filepath.Join(h, ".local", "state", "krino", "dl.lock") + if err := os.MkdirAll(filepath.Dir(held), 0o755); err != nil { + t.Fatal(err) + } + // A lock held by this very process, so it is not stale. + if err := os.WriteFile(held, []byte(fmt.Sprintf("pid %d\n", os.Getpid())), 0o644); err != nil { + t.Fatal(err) + } + code, _, errOut := runCLI(t, "-y") + if code != 1 || !strings.Contains(errOut, "another krino") { + t.Errorf("-y against a held lock: %d %q", code, errOut) + } +} + +// TestFlagsMustPrecedeDirectoryNames is Ruling 4: Go's flag package stops +// parsing at the first non-flag argument, so "krino dl -n" would otherwise +// silently take "-n" as a second directory name and never honour the dry +// run. A leftover argument starting with "-" is a usage error instead of a +// guess. +func TestFlagsMustPrecedeDirectoryNames(t *testing.T) { + home(t) + code, _, errOut := runCLI(t, "dl", "-n") + if code != 2 || !strings.Contains(errOut, "-n") { + t.Errorf("krino dl -n: %d %q", code, errOut) + } +} + +// TestNoColourEscapeToNonTerminal is Ruling 7's one pinned guarantee: a plan +// piped to a file or read by another tool must be plain text. tui.Colour(w) +// already returns false for anything that is not a terminal *os.File, and +// runCLI's stdout is a bytes.Buffer, so this holds end to end through the +// real command path, not just at the helper that decides it. +func TestNoColourEscapeToNonTerminal(t *testing.T) { + matchingFixture(t) + _, out, _ := runCLI(t, "-n") + if strings.ContainsRune(out, '\x1b') { + t.Errorf("escape byte reached a non-terminal writer:\n%q", out) + } +} + func TestLongNameNotPaddedLayoutIntact(t *testing.T) { h := home(t) dl := filepath.Join(h, "dl") |
