Files
antidrift/internal/evidence/context_test.go
T
felixm 13633ffabf Fix drift sync: per-window verdicts, forgiving class match, event-driven status bar
Three independent defects made the focus state feel flaky and let the OS
status bar disagree with the web UI:

- Status file lagged the web by up to a minute: it rendered only on a 60s
  ticker with no hook into state changes. notify() now fans out to multiple
  listeners (AddOnChange) and the writer has a coalesced Wake() so drift
  reaches the bar as promptly as the browser.

- A brief off-task visit could latch drift for the whole session. Sibling
  windows of one app (a browser's reading tab vs its chat tab) share a window
  class, but the judge verdict was cached by class alone, so one tab's verdict
  poisoned the rest and never re-judged. Cache is now keyed by class + scrubbed
  title (judgedWindows) so siblings are judged independently.

- Allowed-class matching was exact equality, so a short token ("brave") never
  matched the real WM_CLASS ("Brave-browser") and every window was routed to
  the LLM. Matching is now substring-based, and planning surfaces the live
  window class with a click-to-add chip so users pick a token that matches.

Also fix the planning checkbox alignment: the global full-width input rule was
stretching the "Enforce focus" checkbox; scope it out for checkboxes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 09:25:30 -04:00

90 lines
2.8 KiB
Go

package evidence
import (
"testing"
"antidrift/internal/domain"
)
func TestWindowClassMatchesCaseAndTrim(t *testing.T) {
ctx := domain.AllowedContext{WindowClasses: []string{" code "}}
if !windowClassAllowed(ctx, " Code ") {
t.Fatal("class should match case-insensitively after trim")
}
if windowClassAllowed(ctx, "firefox") {
t.Fatal("unrelated class must not match")
}
if windowClassAllowed(ctx, " ") {
t.Fatal("empty candidate must not match")
}
}
func TestWindowClassMatchesSubstring(t *testing.T) {
// Users type a short app name ("brave") but the real WM_CLASS is longer
// ("Brave-browser"). The allowed token matching as a substring of the actual
// class keeps the local on-task fast path working instead of silently routing
// every window to the LLM judge.
ctx := domain.AllowedContext{WindowClasses: []string{"brave"}}
if !windowClassAllowed(ctx, "Brave-browser") {
t.Fatal("short allowed token should match the longer real class")
}
if windowClassAllowed(ctx, "firefox") {
t.Fatal("unrelated class must not match")
}
}
func TestWindowTitleMatchesSubstring(t *testing.T) {
ctx := domain.AllowedContext{WindowTitleSubstrings: []string{" antidrift "}}
if !windowTitleAllowed(ctx, "Commitment OS - AntiDrift") {
t.Fatal("title substring should match after trim/casefold")
}
if windowTitleAllowed(ctx, "random video") {
t.Fatal("unrelated title must not match")
}
}
func TestDomainMatchesExactAndSubdomain(t *testing.T) {
ctx := domain.AllowedContext{Domains: []string{" GitHub.COM. "}}
if !domainAllowed(ctx, "github.com") {
t.Fatal("exact domain should match")
}
if !domainAllowed(ctx, " DOCS.GITHUB.COM. ") {
t.Fatal("subdomain should match after normalization")
}
if domainAllowed(ctx, "evilgithub.com") {
t.Fatal("suffix-without-dot must not match")
}
}
func TestCommandMatchesExecutableBasename(t *testing.T) {
ctx := domain.AllowedContext{Commands: []string{"cargo"}}
if !commandAllowed(ctx, "/home/felixm/.cargo/bin/cargo test") {
t.Fatal("basename of full path should match")
}
if !commandAllowed(ctx, "cargo") {
t.Fatal("bare command should match")
}
if commandAllowed(ctx, "/usr/bin/git status") {
t.Fatal("unrelated command must not match")
}
}
func TestMatchesAllowedClassOrTitle(t *testing.T) {
ctx := domain.AllowedContext{
WindowClasses: []string{"code"},
WindowTitleSubstrings: []string{"antidrift"},
}
if !MatchesAllowed(ctx, "Code", "anything") {
t.Fatal("class match should satisfy MatchesAllowed")
}
if !MatchesAllowed(ctx, "firefox", "my AntiDrift tab") {
t.Fatal("title match should satisfy MatchesAllowed")
}
if MatchesAllowed(ctx, "firefox", "youtube") {
t.Fatal("neither match must be off-task")
}
if MatchesAllowed(domain.AllowedContext{}, "code", "antidrift") {
t.Fatal("empty context matches nothing")
}
}