fix: Windows command quoting, atomic JSON/log writes, restore dropped test
Implements items 1-3 of the whole-project review's suggested order (docs/PROJECT_REVIEW_PLAN.md): - Restore TestJobListViewIsCompact, accidentally dropped by 5b0e6fe; drop the redundant TestDefaultConfigUsesDetailedJobList row from TESTS.md and document the two other doc gaps the review found. - Fix quoteLeadingWindowsProgramPath to find the earliest file-extension match at a word boundary instead of the first extension in list order, so a .bat/.cmd command whose argument ends in .exe no longer has its whole command line mistaken for the program path. - Write gosentry.json, jobs.json, and run log files atomically (temp file + rename) so a crash or power loss mid-write can no longer leave a truncated file. Wire Service.Stop() into the app shutdown path so it actually runs, cancelling the run context for in-flight runs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
89be009040
commit
1242b22e4f
9 files changed
+253
-18
No files matched your search
@@ -51,3 +51,44 @@ func TestWindowsShellCommandLineQuotesUnquotedProgramPath(t *testing.T) {
|
||||
t.Fatalf("expected command line %q, got %q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestQuoteLeadingWindowsProgramPathPicksEarliestBoundedExtension pins the
|
||||
// fix for the quoting bug found in the whole-project review: the program
|
||||
// path must end at the *earliest* extension match that sits at a token
|
||||
// boundary, not the first extension in the .exe/.cmd/.bat/.com list order,
|
||||
// and not a substring match inside another word.
|
||||
func TestQuoteLeadingWindowsProgramPathPicksEarliestBoundedExtension(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
command string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "bat with unquoted argument",
|
||||
command: `C:\My Tools\run.bat D:\in.txt`,
|
||||
want: `"C:\My Tools\run.bat" D:\in.txt`,
|
||||
},
|
||||
{
|
||||
name: "bat program with exe argument",
|
||||
command: `C:\My Tools\run.bat C:\Windows\System32\notepad.exe`,
|
||||
want: `"C:\My Tools\run.bat" C:\Windows\System32\notepad.exe`,
|
||||
},
|
||||
{
|
||||
name: "cmd program with exe argument",
|
||||
command: `C:\Program Files\App\deploy.cmd D:\stage\setup.exe`,
|
||||
want: `"C:\Program Files\App\deploy.cmd" D:\stage\setup.exe`,
|
||||
},
|
||||
{
|
||||
name: "exe substring inside directory name",
|
||||
command: `C:\dir.exexample\My Tool\run.bat`,
|
||||
want: `"C:\dir.exexample\My Tool\run.bat"`,
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if got := quoteLeadingWindowsProgramPath(tc.command); got != tc.want {
|
||||
t.Fatalf("quoteLeadingWindowsProgramPath(%q) = %q, want %q", tc.command, got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user