Files
coder/coderd/x/chatd/runner_test.go
T
Hugo Dutka 658a04d28f pr 3
2026-06-04 18:51:22 +00:00

138 lines
4.6 KiB
Go

package chatd //nolint:testpackage // Uses unexported chatworker helpers.
import (
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/testutil"
"github.com/coder/quartz"
)
func TestRunner_IgnoresDuplicateStateNotifications(t *testing.T) {
t.Parallel()
f := newWorkerTestFixture(t)
chat := f.createRunningChat(t)
starter := newBlockingTaskStarter(false)
startWorker(t, testOptions(t, f, starter))
starter.waitCall(t, taskKindGeneration, chat.ID)
latest, err := f.db.GetChatByID(testutil.Context(t, testutil.WaitShort), chat.ID)
require.NoError(t, err)
publishChatUpdate(t, f, latest)
publishChatUpdate(t, f, latest)
starter.assertNoCall(t)
}
func TestRunner_CancelsActiveTaskWhenHistoryChanges(t *testing.T) {
t.Parallel()
f := newWorkerTestFixture(t)
chat := f.createRunningChat(t)
starter := newBlockingTaskStarter(false)
startWorker(t, testOptions(t, f, starter))
first := starter.waitCall(t, taskKindGeneration, chat.ID)
updated := commitAssistantStep(t, f, chat.ID, "first step")
require.Greater(t, updated.HistoryVersion, first.input.HistoryVersion)
requireTaskCanceled(t, first)
second := starter.waitCall(t, taskKindGeneration, chat.ID)
require.Equal(t, updated.HistoryVersion, second.input.HistoryVersion)
}
func TestRunner_CancelsActiveTaskWhenStatusChanges(t *testing.T) {
t.Parallel()
f := newWorkerTestFixture(t)
chat := f.createRunningChat(t)
starter := newBlockingTaskStarter(false)
startWorker(t, testOptions(t, f, starter))
first := starter.waitCall(t, taskKindGeneration, chat.ID)
updated := interruptChat(t, f, chat.ID)
require.Equal(t, database.ChatStatusInterrupting, updated.Status)
requireTaskCanceled(t, first)
second := starter.waitCall(t, taskKindInterrupt, chat.ID)
require.Equal(t, updated.HistoryVersion, second.input.HistoryVersion)
}
func TestRunner_CleansUpOnOwnershipTakeover(t *testing.T) {
t.Parallel()
f := newWorkerTestFixture(t)
chat := f.createRunningChat(t)
starter := newBlockingTaskStarter(false)
startWorker(t, testOptions(t, f, starter))
first := starter.waitCall(t, taskKindGeneration, chat.ID)
acquireChat(t, f, chat.ID, uuid.New(), uuid.New())
requireTaskCanceled(t, first)
starter.assertNoCall(t)
}
func TestRunner_SerializesReplacementTasksForSameHistoryAndStatus(t *testing.T) {
t.Parallel()
f := newWorkerTestFixture(t)
chat := f.createRunningChat(t)
starter := newBlockingTaskStarter(true)
defer starter.releaseAll()
startWorker(t, testOptions(t, f, starter))
first := starter.waitCall(t, taskKindGeneration, chat.ID)
forceExecutionStateAndPublish(t, f, chat.ID, database.ChatStatusInterrupting, false)
starter.waitCall(t, taskKindInterrupt, chat.ID)
forceExecutionStateAndPublish(t, f, chat.ID, database.ChatStatusRunning, false)
starter.assertNoCall(t)
starter.release(t, 0)
replacement := starter.waitCall(t, taskKindGeneration, chat.ID)
require.Equal(t, first.input.HistoryVersion, replacement.input.HistoryVersion)
}
func TestRunner_AllowsReplacementForDifferentHistoryOrStatus(t *testing.T) {
t.Parallel()
f := newWorkerTestFixture(t)
chat := f.createRunningChat(t)
starter := newBlockingTaskStarter(true)
defer starter.releaseAll()
startWorker(t, testOptions(t, f, starter))
first := starter.waitCall(t, taskKindGeneration, chat.ID)
updated := commitAssistantStep(t, f, chat.ID, "different history")
second := starter.waitCall(t, taskKindGeneration, chat.ID)
require.Greater(t, second.input.HistoryVersion, first.input.HistoryVersion)
require.Equal(t, updated.HistoryVersion, second.input.HistoryVersion)
}
func TestWorker_RoutesDatabaseSyncStateToActiveRunner(t *testing.T) {
t.Parallel()
f := newWorkerTestFixture(t)
chat := f.createRunningChat(t)
clock := quartz.NewMock(t)
starter := newBlockingTaskStarter(false)
opts := testOptions(t, f, starter)
opts.Clock = clock
opts.RunnerSyncInterval = time.Minute
startWorker(t, opts)
first := starter.waitCall(t, taskKindGeneration, chat.ID)
forceExecutionState(t, f, chat.ID, database.ChatStatusInterrupting, false)
clock.Advance(time.Minute).MustWait(testutil.Context(t, testutil.WaitLong))
requireTaskCanceled(t, first)
starter.waitCall(t, taskKindInterrupt, chat.ID)
}
func TestWorker_CleanupStopsRoutingAndCancelsTasks(t *testing.T) {
t.Parallel()
f := newWorkerTestFixture(t)
chat := f.createRunningChat(t)
starter := newBlockingTaskStarter(false)
startWorker(t, testOptions(t, f, starter))
first := starter.waitCall(t, taskKindGeneration, chat.ID)
latest := acquireChat(t, f, chat.ID, uuid.New(), uuid.New())
requireTaskCanceled(t, first)
publishChatUpdate(t, f, latest)
starter.assertNoCall(t)
}