chore: land the remaining low-severity items from the whole-project review
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>
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -145,6 +146,50 @@ func TestNormalizeJobsFillsDefaults(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestNormalizeJobsReassignsDuplicateIDs pins the fix for a hand-edited
|
||||
// jobs.json carrying two entries with the same ID: without reassignment both
|
||||
// would share one JobRuntime, one schedule-cache entry, and one SeedStats
|
||||
// bucket, so editing or deleting either would silently affect both.
|
||||
func TestNormalizeJobsReassignsDuplicateIDs(t *testing.T) {
|
||||
jobs := []domain.Job{
|
||||
{ID: 5, Name: "First"},
|
||||
{ID: 5, Name: "Second"},
|
||||
{ID: 5, Name: "Third"},
|
||||
}
|
||||
|
||||
normalizeJobs(jobs)
|
||||
|
||||
seen := make(map[int]bool, len(jobs))
|
||||
for _, job := range jobs {
|
||||
if seen[job.ID] {
|
||||
t.Fatalf("ID %d assigned to more than one job after normalization: %+v", job.ID, jobs)
|
||||
}
|
||||
seen[job.ID] = true
|
||||
}
|
||||
if jobs[0].ID != 5 {
|
||||
t.Errorf("first occurrence should keep its ID: got %d, want 5", jobs[0].ID)
|
||||
}
|
||||
if jobs[1].ID == 5 || jobs[2].ID == 5 {
|
||||
t.Errorf("later duplicates should be reassigned away from 5: got %d, %d", jobs[1].ID, jobs[2].ID)
|
||||
}
|
||||
}
|
||||
|
||||
// TestResolveConfiguredPathCleansAbsolutePaths pins the fix for two spellings
|
||||
// of the same absolute path (forward vs. backslashes) resolving to different
|
||||
// strings: UpdateSettings compares this against Paths.JobsPath as strings to
|
||||
// decide whether the jobs file is changing, so an uncleaned path here could
|
||||
// trigger a spurious adoption against the file already in use.
|
||||
func TestResolveConfiguredPathCleansAbsolutePaths(t *testing.T) {
|
||||
if runtime.GOOS != "windows" {
|
||||
t.Skip("backslash vs. forward-slash spellings of the same path are a Windows-only ambiguity")
|
||||
}
|
||||
got := ResolveConfiguredPath(`C:\app`, "C:/data/jobs.json")
|
||||
want := ResolveConfiguredPath(`C:\app`, `C:\data\jobs.json`)
|
||||
if got != want {
|
||||
t.Errorf("forward-slash and backslash spellings resolved differently: %q vs %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadOrCreateConfigCreatesDefaultsOnFirstRun(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
paths := Paths{
|
||||
|
||||
Reference in New Issue
Block a user