18da021526
Phase 11 of PROJECT_REVIEW_PLAN.md: the themed cleanup pass over every low-severity finding still open (2.2-2.3, 3.4-3.6, 4.3-4.7, 6.4-6.7, 7.1-7.3, 8.2-8.3, 9.1-9.4, and the under-documented decisions in §10/§11). Behavioral fixes: - Reassign duplicate job IDs in a hand-edited jobs.json instead of letting two jobs share one runtime, schedule entry, and SeedStats bucket. - Disambiguate run-log file names that collide within the same second. - Compute AvgDurationMS as DurationSumMS/TimedRunCount instead of an incremental integer mean, so it always matches the seeded-from-logs average instead of drifting from truncation error. - Clean absolute paths in ResolveConfiguredPath so two spellings of the same jobs file do not trigger a spurious adoption. - Report InstallDesktopIcon failures through ErrorOccurred instead of discarding them silently. - Move settingsView's blocking AutostartStatus (PowerShell on Windows) off the UI thread. - Give notify-timing.tsv its own extension so CleanupLogs no longer manages it as a run log. - Replace the settingsView Save handler's second copy of validateConfig's rules with a bare parse, letting the Service's own error surface. Cleanups: - Delete collectActivity, the dead yaml tags on RunRecord, and the logArguments/LogArguments alias. - Fold the two systemTrayRegistered/mainWindowHidden globals into one trayState instance Run owns and threads through Settings and the single-instance reveal path. - Fix stale comments/docs: the frozen window-size restore claim, a reference to a renamed recordRun, README's "Pause all" and notification wording, the PowerShell quoting note for TESTS.md's coverage command, and scripts/test.bat's UTF-8 checkmarks under a non-UTF-8 code page. - Document the single-instance fallback's consequence and the unauthenticated instance-channel port in STANDARDS.md; record the config-shim retirement plan in ROADMAP.md. 3.5, 7.3, and 9.4 turned out to already be fixed by earlier phases; no change needed for those three. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
37 lines
1000 B
Go
37 lines
1000 B
Go
package runner
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
// TestUniqueLogPathAvoidsCollision pins the fix for two runs of the same job
|
|
// landing on the same second: without disambiguation the second write would
|
|
// silently overwrite the first.
|
|
func TestUniqueLogPathAvoidsCollision(t *testing.T) {
|
|
dir := t.TempDir()
|
|
const name = "20260101-120000_job.log"
|
|
|
|
first := uniqueLogPath(dir, name)
|
|
if first != filepath.Join(dir, name) {
|
|
t.Fatalf("first call: got %q, want the plain name", first)
|
|
}
|
|
if err := os.WriteFile(first, []byte("one"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
second := uniqueLogPath(dir, name)
|
|
if second == first {
|
|
t.Fatalf("second call returned the same path as an existing file: %q", second)
|
|
}
|
|
if err := os.WriteFile(second, []byte("two"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
third := uniqueLogPath(dir, name)
|
|
if third == first || third == second {
|
|
t.Fatalf("third call collided with an existing file: %q (existing: %q, %q)", third, first, second)
|
|
}
|
|
}
|