Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions internal/relay/compose_lang_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
// SpawnBriefing): a translation that brings a double quote or an angle bracket
// in breaks every Windows spawn of a .cmd provider in that language.
func TestSpawnBriefingIsCmdSafeInEveryLanguage(t *testing.T) {
t.Parallel()
for _, lang := range prompt.Langs {
for _, route := range []SubagentRoute{RouteSessions, RouteCards, RouteNative} {
for _, hasTools := range []bool{true, false} {
Expand All @@ -28,6 +29,7 @@ func TestSpawnBriefingIsCmdSafeInEveryLanguage(t *testing.T) {
// format that drops or misnumbers one shows as %!, which an agent would read as
// part of the instruction.
func TestEveryMessageRendersInEveryLanguage(t *testing.T) {
t.Parallel()
for _, lang := range prompt.Langs {
messages := []string{
compose(lang, "", "t1", "task", false),
Expand Down Expand Up @@ -56,6 +58,7 @@ func TestEveryMessageRendersInEveryLanguage(t *testing.T) {
// The language is read when a message is composed, not when the relay starts,
// so a change in Settings reaches the next message.
func TestTheRelayTypesInThePromptLanguageReadLive(t *testing.T) {
t.Parallel()
term := newFakeTerminal("s2")
svc := newRelay(workspace(), term, nil)
lang := prompt.English
Expand All @@ -76,6 +79,7 @@ func TestTheRelayTypesInThePromptLanguageReadLive(t *testing.T) {
// Russian's one form also covers 21, so the nudge prints the count in every
// form and the noun agrees with it.
func TestTheRussianNudgeAgreesWithItsCount(t *testing.T) {
t.Parallel()
for count, want := range map[int]string{
1: "1 отправленная вами задача готова", 2: "2 отправленные вами задачи готовы",
5: "5 отправленных вами задач готовы", 11: "11 отправленных вами задач готовы",
Expand Down
3 changes: 3 additions & 0 deletions internal/relay/focus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
)

func TestFocusOpensTheCardAndRaisesTheWindow(t *testing.T) {
t.Parallel()
events := &fakeEvents{}
svc := newRelay(editorWorkspace("/src/lich", "/src/lich-wt"), newFakeTerminal("s1", "s2", "s3"), events)
var order []string
Expand Down Expand Up @@ -36,6 +37,7 @@ func TestFocusOpensTheCardAndRaisesTheWindow(t *testing.T) {
}

func TestFocusNamesTheLiveSessionsWhenTheTargetIsUnknown(t *testing.T) {
t.Parallel()
events := &fakeEvents{}
svc := newRelay(editorWorkspace("/src/lich", "/src/lich-wt"), newFakeTerminal("s1", "s3"), events)
raised := false
Expand All @@ -51,6 +53,7 @@ func TestFocusNamesTheLiveSessionsWhenTheTargetIsUnknown(t *testing.T) {
}

func TestFocusWorksWithoutAWindow(t *testing.T) {
t.Parallel()
svc := newRelay(editorWorkspace("/src/lich", "/src/lich-wt"), newFakeTerminal("s3"), nil)

if got, err := svc.Focus(FocusOptions{Target: "solo", Project: "revu"}); err != nil || got.ID != "s3" {
Expand Down
7 changes: 7 additions & 0 deletions internal/relay/gaps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
// comes later, on the same ticket, and has to reach the sender rather than be
// refused as an unknown ticket.
func TestAnAnswerAfterTheTurnEndedWithoutOneStillReachesTheSender(t *testing.T) {
t.Parallel()
term := newFakeTerminal("s1", "s2")
svc := newRelay(workspace(), term, nil)

Expand Down Expand Up @@ -44,6 +45,7 @@ func TestAnAnswerAfterTheTurnEndedWithoutOneStillReachesTheSender(t *testing.T)

// A late answer is still one answer: a second one on the same ticket is refused.
func TestALateAnswerIsTakenOnce(t *testing.T) {
t.Parallel()
svc := newRelay(workspace(), newFakeTerminal("s1", "s2"), nil)
sent, _ := svc.Send(context.Background(), SendOptions{From: "s1", Target: "docs", Prompt: "run the tests", WaitSeconds: 1})
svc.Observe("s2", "busy")
Expand All @@ -61,6 +63,7 @@ func TestALateAnswerIsTakenOnce(t *testing.T) {
// waiter used to be told the errand was still in progress on a ticket the sweep
// had already dropped, so the next wait on it answered "unknown ticket".
func TestATicketBeingWaitedOnOutlivesItsHour(t *testing.T) {
t.Parallel()
svc := newRelay(workspace(), newFakeTerminal("s1", "s2"), nil)
plant(svc, "t1", "s1", "s2", "docs")
start := time.Now()
Expand Down Expand Up @@ -101,6 +104,7 @@ func TestATicketBeingWaitedOnOutlivesItsHour(t *testing.T) {
// answering then is answering something else, and attributing it to the
// message mid-delivery sends the sender a report of work nobody did.
func TestATicketlessAnswerSkipsAMessageStillBeingDelivered(t *testing.T) {
t.Parallel()
term := newFakeTerminal("s1", "s2")
svc := newRelay(workspace(), term, nil)
svc.submitDelay = 5 * time.Millisecond
Expand Down Expand Up @@ -128,6 +132,7 @@ func TestATicketlessAnswerSkipsAMessageStillBeingDelivered(t *testing.T) {
// ticket is unknown: the ticket still takes a late answer for an hour, and a
// caller handed "expired" would stop looking for it.
func TestAWaitOnALapsedTicketIsToldItWentUnanswered(t *testing.T) {
t.Parallel()
svc := newRelay(workspace(), newFakeTerminal("s1", "s2"), nil)
sent, _ := svc.SendPrivate(context.Background(), SendOptions{From: "s1", Target: "docs", Prompt: "run the tests", WaitSeconds: 1})
svc.Observe("s2", "busy")
Expand All @@ -149,6 +154,7 @@ func TestAWaitOnALapsedTicketIsToldItWentUnanswered(t *testing.T) {
// A caller hanging up as the turn ends, with the late answer landing in
// between, must not have its leaving overwrite that answer with the stall.
func TestALateAnswerSurvivesTheWaiterHangingUp(t *testing.T) {
t.Parallel()
svc := newRelay(workspace(), newFakeTerminal("s1", "s2"), nil)
tk := &ticket{
fromID: "s1", targetID: "s2", target: "docs",
Expand Down Expand Up @@ -176,6 +182,7 @@ func TestALateAnswerSurvivesTheWaiterHangingUp(t *testing.T) {
// A ticket that ages out unanswered, open or lapsed, answers a wait on it with
// how it ended: "unknown ticket" would read as a ticket the caller mistyped.
func TestAWaitOnAnExpiredTicketSaysItExpired(t *testing.T) {
t.Parallel()
svc := newRelay(workspace(), newFakeTerminal("s1", "s2", "s3"), nil)
plant(svc, "open", "s1", "s2", "docs")
plant(svc, "lapsed", "s1", "s3", "api")
Expand Down
2 changes: 2 additions & 0 deletions internal/relay/handover_race_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func (g *gatedEvents) Emit(_ string, data any) {
// two critical sections apart, and a Wait between them found neither: "unknown
// ticket" for an errand that had just been answered.
func TestAWaitAsTheAnswerLandsNeverSeesAnUnknownTicket(t *testing.T) {
t.Parallel()
events := &gatedEvents{gate: "s2", reached: make(chan struct{}), release: make(chan struct{})}
svc := newRelay(workspace(), newFakeTerminal("s1", "s2"), events)
plant(svc, "t1", "s1", "s2", "docs")
Expand Down Expand Up @@ -64,6 +65,7 @@ func TestAWaitAsTheAnswerLandsNeverSeesAnUnknownTicket(t *testing.T) {
// ticket's marks are cleared after the lock is let go and before await runs,
// and holding that event parks the Wait there while the answer lands.
func TestAnAnswerAWaitCarriedOutIsNotAlsoFiled(t *testing.T) {
t.Parallel()
events := &gatedEvents{gate: "s3", reached: make(chan struct{}), release: make(chan struct{})}
svc := newRelay(workspace(), newFakeTerminal("s1", "s2", "s3"), events)
plant(svc, "t1", "s1", "s2", "docs")
Expand Down
10 changes: 10 additions & 0 deletions internal/relay/hangup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func assertStashedAndNudged(t *testing.T, svc *Service, term *fakeTerminal, answ
}

func TestACollectWhoseCallerHungUpLeavesTheNextResultToTheNudge(t *testing.T) {
t.Parallel()
term := newFakeTerminal("s1", "s2")
svc := newRelay(workspace(), term, nil)
plant(svc, "t1", "s1", "s2", "docs")
Expand Down Expand Up @@ -90,6 +91,7 @@ func TestACollectWhoseCallerHungUpLeavesTheNextResultToTheNudge(t *testing.T) {
// nudge, and the collector then finds its caller gone. The result must not be
// drained, and something still has to say it is there.
func TestAResultTheHungUpCollectorWasWokenForIsStillNudged(t *testing.T) {
t.Parallel()
term := newFakeTerminal("s1", "s2")
svc := newRelay(workspace(), term, nil)
svc.mu.Lock()
Expand All @@ -107,6 +109,7 @@ func TestAResultTheHungUpCollectorWasWokenForIsStillNudged(t *testing.T) {
}

func TestARenudgeLeavesAResultToTheCollectorStillHolding(t *testing.T) {
t.Parallel()
term := newFakeTerminal("s1", "s2")
svc := newRelay(workspace(), term, nil)
svc.mu.Lock()
Expand All @@ -124,6 +127,7 @@ func TestARenudgeLeavesAResultToTheCollectorStillHolding(t *testing.T) {
}

func TestCollectNowReturnsAtOnceWithWhoStillOwes(t *testing.T) {
t.Parallel()
term := newFakeTerminal("s1", "s2")
svc := newRelay(workspace(), term, nil)
plant(svc, "t1", "s1", "s2", "docs")
Expand All @@ -149,6 +153,7 @@ func TestCollectNowReturnsAtOnceWithWhoStillOwes(t *testing.T) {
}

func TestCollectNowRefusesACallerWithNoSession(t *testing.T) {
t.Parallel()
svc := newRelay(workspace(), newFakeTerminal(), nil)

if _, err := svc.CollectNow(CollectNowOptions{}); err == nil {
Expand All @@ -157,6 +162,7 @@ func TestCollectNowRefusesACallerWithNoSession(t *testing.T) {
}

func TestAWaitWhoseCallerHungUpLeavesTheAnswerToTheInbox(t *testing.T) {
t.Parallel()
term := newFakeTerminal("s1", "s2")
svc := newRelay(workspace(), term, nil)
plant(svc, "t1", "s1", "s2", "docs")
Expand Down Expand Up @@ -184,6 +190,7 @@ func TestAWaitWhoseCallerHungUpLeavesTheAnswerToTheInbox(t *testing.T) {
// frozen clock is the case the timer path has to end on its own: a deadline
// read off a clock that never moves would otherwise hold the line forever.
func TestACollectWhoseWaitRunsOutReturnsWhoStillOwes(t *testing.T) {
t.Parallel()
for _, tt := range []struct {
name string
clock func() time.Time
Expand Down Expand Up @@ -219,6 +226,7 @@ func TestACollectWhoseWaitRunsOutReturnsWhoStillOwes(t *testing.T) {
// and never read) had that news meant for its caller; with the caller gone it
// goes to the inbox.
func TestAnAbandonedWaitStashesTheOutcomeItWasHolding(t *testing.T) {
t.Parallel()
term := newFakeTerminal("s1", "s2")
svc := newRelay(workspace(), term, nil)
plant(svc, "t1", "s1", "s2", "docs")
Expand Down Expand Up @@ -246,6 +254,7 @@ func TestAnAbandonedWaitStashesTheOutcomeItWasHolding(t *testing.T) {
// Another caller still holding the ticket carries the outcome out itself, so
// one hanging up must not also stash it.
func TestAnAbandonedWaitLeavesTheOutcomeToAnotherWaiter(t *testing.T) {
t.Parallel()
term := newFakeTerminal("s1", "s2")
svc := newRelay(workspace(), term, nil)
plant(svc, "t1", "s1", "s2", "docs")
Expand All @@ -271,6 +280,7 @@ func TestAnAbandonedWaitLeavesTheOutcomeToAnotherWaiter(t *testing.T) {
// not only when one finishes: Esc is how a wait for results is abandoned, and
// the next turn end may be a long way off.
func TestAnInterruptedSenderIsNudged(t *testing.T) {
t.Parallel()
term := newFakeTerminal("s1", "s2")
svc := newRelay(workspace(), term, nil)
svc.Observe("s1", stateBusy)
Expand Down
15 changes: 15 additions & 0 deletions internal/relay/insert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func editorWorkspace(root, worktree string) fakeSessions {
}

func TestInsertPastesWithoutSubmitting(t *testing.T) {
t.Parallel()
term := newFakeTerminal("s1", "s2", "s3")
svc := newRelay(editorWorkspace("/src/lich", "/src/lich-wt"), term, nil)

Expand All @@ -42,6 +43,7 @@ func TestInsertPastesWithoutSubmitting(t *testing.T) {
}

func TestInsertStripsWhatWouldBreakOutOfThePaste(t *testing.T) {
t.Parallel()
term := newFakeTerminal("s3")
svc := newRelay(editorWorkspace("/src/lich", "/src/lich-wt"), term, nil)

Expand All @@ -54,6 +56,7 @@ func TestInsertStripsWhatWouldBreakOutOfThePaste(t *testing.T) {
}

func TestInsertRefusesWhatItCannotPaste(t *testing.T) {
t.Parallel()
svc := newRelay(editorWorkspace("/src/lich", "/src/lich-wt"), newFakeTerminal("s3"), nil)

if _, err := svc.Insert(InsertOptions{Target: "solo", Text: " \n\x1b "}); err == nil {
Expand All @@ -69,6 +72,7 @@ func TestInsertRefusesWhatItCannotPaste(t *testing.T) {
}

func TestInsertFindsTheSessionByProjectName(t *testing.T) {
t.Parallel()
term := newFakeTerminal("s3")
svc := newRelay(editorWorkspace("/src/lich", "/src/lich-wt"), term, nil)

Expand All @@ -79,6 +83,7 @@ func TestInsertFindsTheSessionByProjectName(t *testing.T) {
}

func TestInsertByDirectoryPrefersTheSessionRunningThere(t *testing.T) {
t.Parallel()
root := t.TempDir()
worktree := filepath.Join(root, "wt")
if err := os.Mkdir(worktree, 0o755); err != nil {
Expand All @@ -102,6 +107,7 @@ func TestInsertByDirectoryPrefersTheSessionRunningThere(t *testing.T) {
}

func TestInsertNamesEveryCandidateWhenTheDirectoryIsAmbiguous(t *testing.T) {
t.Parallel()
root := t.TempDir()
work := fakeSessions{projects: []store.Project{
{ID: "p1", Name: "lich", Path: root, Sessions: []store.Session{
Expand All @@ -117,6 +123,7 @@ func TestInsertNamesEveryCandidateWhenTheDirectoryIsAmbiguous(t *testing.T) {
}

func TestInsertFallsBackToTheProjectsSessionsWhenNoneRunInTheDirectory(t *testing.T) {
t.Parallel()
root := t.TempDir()
work := fakeSessions{projects: []store.Project{
{ID: "p1", Name: "lich", Path: root, Sessions: []store.Session{
Expand All @@ -132,6 +139,7 @@ func TestInsertFallsBackToTheProjectsSessionsWhenNoneRunInTheDirectory(t *testin
}

func TestInsertFindsNothingInAnUnknownProject(t *testing.T) {
t.Parallel()
svc := newRelay(editorWorkspace("/src/lich", "/src/lich-wt"), newFakeTerminal("s1"), nil)

for _, project := range []string{"nope", "/not/a/project"} {
Expand All @@ -146,6 +154,7 @@ func TestInsertFindsNothingInAnUnknownProject(t *testing.T) {
}

func TestInsertGoesInBesideTheUsersDraft(t *testing.T) {
t.Parallel()
term := newFakeTerminal("s3")
term.typeAt("s3", true)
svc := newRelay(editorWorkspace("/src/lich", "/src/lich-wt"), term, nil)
Expand All @@ -156,6 +165,7 @@ func TestInsertGoesInBesideTheUsersDraft(t *testing.T) {
}

func TestInsertRefusesASessionBlockedOnAPermission(t *testing.T) {
t.Parallel()
term := newFakeTerminal("s3")
svc := newRelay(editorWorkspace("/src/lich", "/src/lich-wt"), term, nil)
svc.reported["s3"] = stateWaiting
Expand All @@ -170,6 +180,7 @@ func TestInsertRefusesASessionBlockedOnAPermission(t *testing.T) {
}

func TestInsertGivesUpOnASessionThatNeverReachesItsPrompt(t *testing.T) {
t.Parallel()
term := newFakeTerminal("s3")
term.setUp("s3", true)
svc := newRelay(editorWorkspace("/src/lich", "/src/lich-wt"), term, nil)
Expand All @@ -184,6 +195,7 @@ func TestInsertGivesUpOnASessionThatNeverReachesItsPrompt(t *testing.T) {
}

func TestInsertSurfacesAWriteTheTerminalRefuses(t *testing.T) {
t.Parallel()
term := newFakeTerminal("s3")
term.writeErr = os.ErrClosed
svc := newRelay(editorWorkspace("/src/lich", "/src/lich-wt"), term, nil)
Expand All @@ -194,6 +206,7 @@ func TestInsertSurfacesAWriteTheTerminalRefuses(t *testing.T) {
}

func TestPeersCarryTheirDirectories(t *testing.T) {
t.Parallel()
svc := newRelay(editorWorkspace("/src/lich", "/src/lich-wt"), newFakeTerminal("s1", "s2"), nil)

peers, err := svc.Peers(PeersOptions{})
Expand All @@ -209,6 +222,7 @@ func TestPeersCarryTheirDirectories(t *testing.T) {
}

func TestSendNarrowsByProjectDirectory(t *testing.T) {
t.Parallel()
root := t.TempDir()
work := fakeSessions{projects: []store.Project{
{ID: "p1", Name: "a", Path: root, Sessions: []store.Session{{ID: "s1", Label: "api"}}},
Expand All @@ -223,6 +237,7 @@ func TestSendNarrowsByProjectDirectory(t *testing.T) {
}

func TestSamePath(t *testing.T) {
t.Parallel()
dir := t.TempDir()
link := filepath.Join(t.TempDir(), "link")
linked := os.Symlink(dir, link) == nil
Expand Down
Loading
Loading