mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(coderd): preseed AI providers for title tests (#27564)
This commit is contained in:
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/coder/coder/v2/cli"
|
||||
"github.com/coder/coder/v2/coderd"
|
||||
"github.com/coder/coder/v2/coderd/aibridged"
|
||||
"github.com/coder/coder/v2/coderd/database/pubsub"
|
||||
)
|
||||
|
||||
// StartTestAIBridgeDaemon wires an in-process aibridged daemon onto the
|
||||
@@ -37,6 +38,21 @@ func StartTestAIBridgeDaemon(
|
||||
metrics *aibridged.Metrics,
|
||||
) {
|
||||
t.Helper()
|
||||
StartTestAIBridgeDaemonWithPubsub(ctx, t, api, metrics, api.Pubsub)
|
||||
}
|
||||
|
||||
// StartTestAIBridgeDaemonWithPubsub is StartTestAIBridgeDaemon with an
|
||||
// explicit pubsub for the provider-reload subscription. A pubsub that is
|
||||
// disconnected from api.Pubsub cuts the daemon off from provider change
|
||||
// events, leaving the initial synchronous load as its only route source.
|
||||
func StartTestAIBridgeDaemonWithPubsub(
|
||||
ctx context.Context,
|
||||
t testing.TB,
|
||||
api *coderd.API,
|
||||
metrics *aibridged.Metrics,
|
||||
ps pubsub.Pubsub,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
logger := api.Logger.Named("aibridged").Leveled(slog.LevelDebug)
|
||||
cfg := api.DeploymentValues.AI.BridgeConfig
|
||||
@@ -63,7 +79,7 @@ func StartTestAIBridgeDaemon(
|
||||
// The reloader fetches providers from coderd over srv's DRPC client; the
|
||||
// subscription drives an initial load and refreshes on change events.
|
||||
reloader := cli.NewPoolRPCReloader(pool, srv.ClientContext, cfg, logger.Named("reloader"), nil, metrics)
|
||||
unsubscribe, err := aibridged.SubscribeProviderReload(ctx, api.Pubsub, reloader, logger.Named("subscriber"))
|
||||
unsubscribe, err := aibridged.SubscribeProviderReload(ctx, ps, reloader, logger.Named("subscriber"))
|
||||
if err != nil {
|
||||
t.Fatalf("subscribe provider reload: %v", err)
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/database/dbgen"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtestutil"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||
dbpubsub "github.com/coder/coder/v2/coderd/database/pubsub"
|
||||
"github.com/coder/coder/v2/coderd/externalauth"
|
||||
"github.com/coder/coder/v2/coderd/rbac"
|
||||
"github.com/coder/coder/v2/coderd/rbac/policy"
|
||||
@@ -141,6 +142,14 @@ func newChatClientWithAPIAndDatabase(t testing.TB, overrides ...func(*coderdtest
|
||||
return codersdk.NewExperimentalClient(client), api.Database, api
|
||||
}
|
||||
|
||||
func newChatClientWithoutAIBridge(t testing.TB, overrides ...func(*coderdtest.Options)) (*codersdk.ExperimentalClient, database.Store, *coderd.API) {
|
||||
t.Helper()
|
||||
|
||||
opts := newChatTestOptions(t, coderdtest.DeploymentValues(t), overrides...)
|
||||
client, _, api := coderdtest.NewWithAPI(t, opts)
|
||||
return codersdk.NewExperimentalClient(client), api.Database, api
|
||||
}
|
||||
|
||||
func insertTestChatQueuedMessage(
|
||||
ctx context.Context,
|
||||
t testing.TB,
|
||||
@@ -9540,9 +9549,10 @@ func TestRegenerateChatTitle(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client, db := newChatClientWithDatabase(t)
|
||||
client, db, api := newChatClientWithoutAIBridge(t)
|
||||
user := coderdtest.CreateFirstUser(t, client.Client)
|
||||
modelConfig := createTitleGenerationModelConfig(t, client)
|
||||
aibridgedtest.StartTestAIBridgeDaemon(t.Context(), t, api, nil)
|
||||
|
||||
chat := dbgen.Chat(t, db, database.Chat{
|
||||
OrganizationID: user.OrganizationID,
|
||||
@@ -9560,13 +9570,45 @@ func TestRegenerateChatTitle(t *testing.T) {
|
||||
require.Equal(t, "Test Chat", updated.Title)
|
||||
})
|
||||
|
||||
t.Run("NoPubsubDelivery", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client, db, api := newChatClientWithoutAIBridge(t)
|
||||
user := coderdtest.CreateFirstUser(t, client.Client)
|
||||
modelConfig := createTitleGenerationModelConfig(t, client)
|
||||
|
||||
// Wire the daemon's reload subscription to a pubsub coderd never
|
||||
// publishes to: gateway routes can then only come from the
|
||||
// synchronous initial load. This guards the invariant the
|
||||
// create-config-before-daemon pattern above relies on; if the
|
||||
// initial load is removed or made asynchronous, this fails
|
||||
// deterministically instead of reintroducing the startup race.
|
||||
isolated := dbpubsub.NewInMemory()
|
||||
t.Cleanup(func() { _ = isolated.Close() })
|
||||
aibridgedtest.StartTestAIBridgeDaemonWithPubsub(t.Context(), t, api, nil, isolated)
|
||||
|
||||
chat := dbgen.Chat(t, db, database.Chat{
|
||||
OrganizationID: user.OrganizationID,
|
||||
OwnerID: user.UserID,
|
||||
LastModelConfigID: modelConfig.ID,
|
||||
Title: "New Chat",
|
||||
})
|
||||
seedManualTitleSourceMessage(t, db, chat, modelConfig.ID)
|
||||
|
||||
updated, err := client.RegenerateChatTitle(ctx, chat.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "Test Chat", updated.Title)
|
||||
})
|
||||
|
||||
t.Run("DoesNotBumpHistoryVersion", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client, db := newChatClientWithDatabase(t)
|
||||
client, db, api := newChatClientWithoutAIBridge(t)
|
||||
user := coderdtest.CreateFirstUser(t, client.Client)
|
||||
modelConfig := createTitleGenerationModelConfig(t, client)
|
||||
aibridgedtest.StartTestAIBridgeDaemon(t.Context(), t, api, nil)
|
||||
|
||||
chat := dbgen.Chat(t, db, database.Chat{
|
||||
OrganizationID: user.OrganizationID,
|
||||
@@ -9614,9 +9656,10 @@ func TestRegenerateChatTitle(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client, db, api := newChatClientWithAPIAndDatabase(t)
|
||||
client, db, api := newChatClientWithoutAIBridge(t)
|
||||
firstUser := coderdtest.CreateFirstUser(t, client.Client)
|
||||
_ = createChatModelConfigWithTitleFailure(t, client)
|
||||
aibridgedtest.StartTestAIBridgeDaemon(t.Context(), t, api, nil)
|
||||
|
||||
chat, err := client.CreateChat(ctx, codersdk.CreateChatRequest{
|
||||
OrganizationID: firstUser.OrganizationID,
|
||||
@@ -9726,9 +9769,10 @@ func TestProposeChatTitle(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client, db := newChatClientWithDatabase(t)
|
||||
client, db, api := newChatClientWithoutAIBridge(t)
|
||||
user := coderdtest.CreateFirstUser(t, client.Client)
|
||||
modelConfig := createTitleGenerationModelConfig(t, client)
|
||||
aibridgedtest.StartTestAIBridgeDaemon(t.Context(), t, api, nil)
|
||||
|
||||
chat := dbgen.Chat(t, db, database.Chat{
|
||||
OrganizationID: user.OrganizationID,
|
||||
@@ -9774,9 +9818,10 @@ func TestProposeChatTitle(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client, db := newChatClientWithDatabase(t)
|
||||
client, db, api := newChatClientWithoutAIBridge(t)
|
||||
user := coderdtest.CreateFirstUser(t, client.Client)
|
||||
modelConfig := createTitleGenerationModelConfig(t, client)
|
||||
aibridgedtest.StartTestAIBridgeDaemon(t.Context(), t, api, nil)
|
||||
|
||||
workspaceBuild := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
||||
OrganizationID: user.OrganizationID,
|
||||
@@ -9809,9 +9854,10 @@ func TestProposeChatTitle(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client, db, api := newChatClientWithAPIAndDatabase(t)
|
||||
client, db, api := newChatClientWithoutAIBridge(t)
|
||||
firstUser := coderdtest.CreateFirstUser(t, client.Client)
|
||||
_ = createChatModelConfigWithTitleFailure(t, client)
|
||||
aibridgedtest.StartTestAIBridgeDaemon(t.Context(), t, api, nil)
|
||||
|
||||
chat, err := client.CreateChat(ctx, codersdk.CreateChatRequest{
|
||||
OrganizationID: firstUser.OrganizationID,
|
||||
|
||||
Reference in New Issue
Block a user