mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
Implements the chatd stabilization RFC. Combines: - https://github.com/coder/coder/pull/25908 - https://github.com/coder/coder/pull/25923 - https://github.com/coder/coder/pull/26109 - https://github.com/coder/coder/pull/26110 - https://github.com/coder/coder/pull/26111 - https://github.com/coder/coder/pull/26112
156 lines
5.2 KiB
Go
156 lines
5.2 KiB
Go
package chatd
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"testing"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/mock/gomock"
|
|
|
|
"github.com/coder/coder/v2/coderd/database"
|
|
"github.com/coder/coder/v2/coderd/database/dbmock"
|
|
"github.com/coder/coder/v2/coderd/x/chatd/chatdebug"
|
|
"github.com/coder/coder/v2/testutil"
|
|
)
|
|
|
|
func TestRunnerDebugTurnEnsureCreatesOnce(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := testutil.Context(t, testutil.WaitLong)
|
|
runnerCtx, cancel := context.WithCancel(ctx)
|
|
defer cancel()
|
|
ctrl := gomock.NewController(t)
|
|
db := dbmock.NewMockStore(ctrl)
|
|
chatID := uuid.New()
|
|
runID := uuid.New()
|
|
modelConfigID := uuid.New()
|
|
svc := chatdebug.NewService(db, testutil.Logger(t), nil)
|
|
turn := newRunnerDebugTurn(runnerCtx, testutil.Logger(t))
|
|
|
|
db.EXPECT().InsertChatDebugRun(gomock.Any(), gomock.Any()).
|
|
DoAndReturn(func(_ context.Context, params database.InsertChatDebugRunParams) (database.ChatDebugRun, error) {
|
|
require.Equal(t, chatID, params.ChatID)
|
|
require.Equal(t, string(chatdebug.KindChatTurn), params.Kind)
|
|
require.Equal(t, string(chatdebug.StatusInProgress), params.Status)
|
|
require.Equal(t, sql.NullInt64{Int64: 123, Valid: true}, params.TriggerMessageID)
|
|
return database.ChatDebugRun{
|
|
ID: runID,
|
|
ChatID: chatID,
|
|
ModelConfigID: uuid.NullUUID{UUID: modelConfigID, Valid: true},
|
|
TriggerMessageID: sql.NullInt64{Int64: 123, Valid: true},
|
|
HistoryTipMessageID: sql.NullInt64{Int64: 456, Valid: true},
|
|
Kind: string(chatdebug.KindChatTurn),
|
|
Status: string(chatdebug.StatusInProgress),
|
|
Provider: sql.NullString{String: "anthropic", Valid: true},
|
|
Model: sql.NullString{String: "claude", Valid: true},
|
|
}, nil
|
|
}).Times(1)
|
|
|
|
debug := &generationDebug{
|
|
Enabled: true,
|
|
Service: svc,
|
|
Provider: "anthropic",
|
|
Model: "claude",
|
|
TriggerMessageID: 123,
|
|
HistoryTipMessageID: 456,
|
|
TriggerLabel: "hello",
|
|
ModelConfig: database.ChatModelConfig{ID: modelConfigID},
|
|
}
|
|
chat := database.Chat{ID: chatID}
|
|
|
|
firstCtx := turn.Ensure(ctx, chat, debug)
|
|
firstRun, ok := chatdebug.RunFromContext(firstCtx)
|
|
require.True(t, ok)
|
|
require.Equal(t, runID, firstRun.RunID)
|
|
|
|
secondCtx := turn.Ensure(ctx, chat, debug)
|
|
secondRun, ok := chatdebug.RunFromContext(secondCtx)
|
|
require.True(t, ok)
|
|
require.Equal(t, runID, secondRun.RunID)
|
|
}
|
|
|
|
func TestRunnerDebugTurnEnsureDisabledFirstAttemptStaysDisabled(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := testutil.Context(t, testutil.WaitLong)
|
|
ctrl := gomock.NewController(t)
|
|
db := dbmock.NewMockStore(ctrl)
|
|
svc := chatdebug.NewService(db, testutil.Logger(t), nil)
|
|
turn := newRunnerDebugTurn(ctx, testutil.Logger(t))
|
|
chat := database.Chat{ID: uuid.New()}
|
|
|
|
firstCtx := turn.Ensure(ctx, chat, nil)
|
|
_, ok := chatdebug.RunFromContext(firstCtx)
|
|
require.False(t, ok)
|
|
|
|
secondCtx := turn.Ensure(ctx, chat, &generationDebug{
|
|
Enabled: true,
|
|
Service: svc,
|
|
TriggerMessageID: 1,
|
|
ModelConfig: database.ChatModelConfig{ID: uuid.New()},
|
|
})
|
|
_, ok = chatdebug.RunFromContext(secondCtx)
|
|
require.False(t, ok)
|
|
}
|
|
|
|
func TestRunnerDebugTurnRecordOutcomePrecedence(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
turn := newRunnerDebugTurn(context.Background(), testutil.Logger(t))
|
|
turn.RecordOutcome(chatdebug.StatusCompleted)
|
|
require.True(t, turn.statusSet)
|
|
require.Equal(t, chatdebug.StatusCompleted, turn.status)
|
|
|
|
turn.RecordOutcome(chatdebug.StatusInterrupted)
|
|
require.Equal(t, chatdebug.StatusInterrupted, turn.status)
|
|
|
|
turn.RecordOutcome(chatdebug.StatusCompleted)
|
|
require.Equal(t, chatdebug.StatusInterrupted, turn.status)
|
|
|
|
turn.RecordOutcome(chatdebug.StatusError)
|
|
require.Equal(t, chatdebug.StatusError, turn.status)
|
|
}
|
|
|
|
func TestRunnerDebugTurnFinalizeOnce(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := testutil.Context(t, testutil.WaitLong)
|
|
runnerCtx, cancel := context.WithCancel(ctx)
|
|
defer cancel()
|
|
ctrl := gomock.NewController(t)
|
|
db := dbmock.NewMockStore(ctrl)
|
|
chatID := uuid.New()
|
|
runID := uuid.New()
|
|
svc := chatdebug.NewService(db, testutil.Logger(t), nil)
|
|
turn := newRunnerDebugTurn(runnerCtx, testutil.Logger(t))
|
|
|
|
db.EXPECT().InsertChatDebugRun(gomock.Any(), gomock.Any()).
|
|
Return(database.ChatDebugRun{
|
|
ID: runID,
|
|
ChatID: chatID,
|
|
Kind: string(chatdebug.KindChatTurn),
|
|
Status: string(chatdebug.StatusInProgress),
|
|
}, nil).
|
|
Times(1)
|
|
db.EXPECT().GetChatDebugStepsByRunID(gomock.Any(), runID).Return(nil, nil).Times(1)
|
|
db.EXPECT().UpdateChatDebugRun(gomock.Any(), gomock.Any()).
|
|
DoAndReturn(func(_ context.Context, params database.UpdateChatDebugRunParams) (database.ChatDebugRun, error) {
|
|
require.Equal(t, runID, params.ID)
|
|
require.Equal(t, chatID, params.ChatID)
|
|
require.Equal(t, sql.NullString{String: string(chatdebug.StatusError), Valid: true}, params.Status)
|
|
return database.ChatDebugRun{ID: runID, ChatID: chatID}, nil
|
|
}).Times(1)
|
|
|
|
turn.Ensure(ctx, database.Chat{ID: chatID}, &generationDebug{
|
|
Enabled: true,
|
|
Service: svc,
|
|
TriggerMessageID: 1,
|
|
ModelConfig: database.ChatModelConfig{ID: uuid.New()},
|
|
})
|
|
turn.RecordOutcome(chatdebug.StatusError)
|
|
turn.Finalize(ctx)
|
|
turn.Finalize(ctx)
|
|
}
|