package autotrack import ( "os" "path/filepath" "strings" "testing" "time" ) // TestEncodeLineByteParityWithPython pins the exact byte shape of a real line // from ~/.local/share/ticktock/activity-2026-07-07.jsonl written by the // Python daemon (json.dumps ", "/": " separators, key order, no escaping). func TestEncodeLineByteParityWithPython(t *testing.T) { r := Record{ Start: time.Date(2026, 7, 7, 8, 59, 19, 0, time.Local), End: time.Date(2026, 7, 7, 9, 0, 9, 0, time.Local), Secs: 49, App: "Trace", } want := `{"start": "2026-07-07T08:59:19", "end": "2026-07-07T09:00:09", "secs": 49, "app": "Trace", "title": "", "url": "", "domain": "", "idle": false}` if got := r.EncodeLine(); got != want { t.Errorf("byte mismatch:\n got %s\nwant %s", got, want) } } func TestEncodeLineNoHTMLEscaping(t *testing.T) { r := Record{ Start: time.Date(2026, 7, 7, 10, 0, 0, 0, time.Local), End: time.Date(2026, 7, 7, 10, 1, 0, 0, time.Local), Secs: 60, App: "Safari", Title: `R&D — café "x"`, URL: "https://example.com/a?b=1&c=2", Domain: "example.com", } got := r.EncodeLine() if !strings.Contains(got, `"title": "R&D — café \"x\""`) { t.Errorf("ensure_ascii=False parity broken: %s", got) } if !strings.Contains(got, `"url": "https://example.com/a?b=1&c=2"`) { t.Errorf("url should not be HTML-escaped: %s", got) } } func TestWriteSegmentDropsShortAndAppends(t *testing.T) { dir := filepath.Join(t.TempDir(), "data") // must be created by WriteSegment start := time.Date(2026, 7, 7, 9, 0, 0, 0, time.Local) short := &Segment{Ctx: Ctx{App: "Blip"}, Start: start, End: start.Add(10 * time.Second)} if err := WriteSegment(dir, short, 15); err != nil { t.Fatal(err) } if _, err := os.Stat(LogPath(dir, start)); !os.IsNotExist(err) { t.Fatal("sub-minSecs segment must be dropped (no file written)") } long := &Segment{Ctx: Ctx{App: "Trace", Title: "doc"}, Start: start, End: start.Add(50 * time.Second)} if err := WriteSegment(dir, long, 15); err != nil { t.Fatal(err) } if err := WriteSegment(dir, long, 15); err != nil { // append, not truncate t.Fatal(err) } b, err := os.ReadFile(LogPath(dir, start)) if err != nil { t.Fatal(err) } lines := strings.Split(strings.TrimRight(string(b), "\n"), "\n") if len(lines) != 2 { t.Fatalf("want 2 appended lines, got %d: %q", len(lines), string(b)) } want := `{"start": "2026-07-07T09:00:00", "end": "2026-07-07T09:00:50", "secs": 50, "app": "Trace", "title": "doc", "url": "", "domain": "", "idle": false}` if lines[0] != want { t.Errorf("line mismatch:\n got %s\nwant %s", lines[0], want) } if err := WriteSegment(dir, nil, 15); err != nil { t.Errorf("nil segment must be a no-op, got %v", err) } }