Skip to content

Commit fcdbba8

Browse files
test: add failure testcase scenario
1 parent ff8d3de commit fcdbba8

File tree

1 file changed

+197
-0
lines changed

1 file changed

+197
-0
lines changed

enterprise/coderd/prebuilds/claim_test.go

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package prebuilds_test
33
import (
44
"context"
55
"database/sql"
6+
"errors"
67
"strings"
78
"sync/atomic"
89
"testing"
@@ -66,6 +67,32 @@ func (m *storeSpy) ClaimPrebuiltWorkspace(ctx context.Context, arg database.Clai
6667
return result, err
6768
}
6869

70+
type errorStore struct {
71+
claimingErr error
72+
73+
database.Store
74+
}
75+
76+
func newErrorStore(db database.Store, claimingErr error) *errorStore {
77+
return &errorStore{
78+
Store: db,
79+
claimingErr: claimingErr,
80+
}
81+
}
82+
83+
func (es *errorStore) InTx(fn func(store database.Store) error, opts *database.TxOptions) error {
84+
// Pass failure store down into transaction store.
85+
return es.Store.InTx(func(store database.Store) error {
86+
newES := newErrorStore(store, es.claimingErr)
87+
88+
return fn(newES)
89+
}, opts)
90+
}
91+
92+
func (es *errorStore) ClaimPrebuiltWorkspace(ctx context.Context, arg database.ClaimPrebuiltWorkspaceParams) (database.ClaimPrebuiltWorkspaceRow, error) {
93+
return database.ClaimPrebuiltWorkspaceRow{}, es.claimingErr
94+
}
95+
6996
func TestClaimPrebuild(t *testing.T) {
7097
t.Parallel()
7198

@@ -284,6 +311,176 @@ func TestClaimPrebuild(t *testing.T) {
284311
}
285312
}
286313

314+
func TestClaimPrebuild_CheckDifferentErrors(t *testing.T) {
315+
t.Parallel()
316+
317+
if !dbtestutil.WillUsePostgres() {
318+
t.Skip("This test requires postgres")
319+
}
320+
321+
const (
322+
desiredInstances = 1
323+
presetCount = 2
324+
325+
expectedPrebuildsCount = desiredInstances * presetCount
326+
)
327+
328+
cases := map[string]struct {
329+
claimingErr error
330+
checkFn func(
331+
t *testing.T,
332+
ctx context.Context,
333+
store database.Store,
334+
userClient *codersdk.Client,
335+
user codersdk.User,
336+
templateVersionID uuid.UUID,
337+
presetID uuid.UUID,
338+
)
339+
}{
340+
"ErrNoClaimablePrebuiltWorkspaces is returned": {
341+
claimingErr: agplprebuilds.ErrNoClaimablePrebuiltWorkspaces,
342+
checkFn: func(
343+
t *testing.T,
344+
ctx context.Context,
345+
store database.Store,
346+
userClient *codersdk.Client,
347+
user codersdk.User,
348+
templateVersionID uuid.UUID,
349+
presetID uuid.UUID,
350+
) {
351+
// When: a user creates a new workspace with a preset for which prebuilds are configured.
352+
workspaceName := strings.ReplaceAll(testutil.GetRandomName(t), "_", "-")
353+
userWorkspace, err := userClient.CreateUserWorkspace(ctx, user.Username, codersdk.CreateWorkspaceRequest{
354+
TemplateVersionID: templateVersionID,
355+
Name: workspaceName,
356+
TemplateVersionPresetID: presetID,
357+
})
358+
359+
require.NoError(t, err)
360+
coderdtest.AwaitWorkspaceBuildJobCompleted(t, userClient, userWorkspace.LatestBuild.ID)
361+
362+
// Then: the number of running prebuilds hasn't changed because claiming prebuild is failed and we fallback to creating new workspace.
363+
currentPrebuilds, err := store.GetRunningPrebuiltWorkspaces(ctx)
364+
require.NoError(t, err)
365+
require.Equal(t, expectedPrebuildsCount, len(currentPrebuilds))
366+
},
367+
},
368+
"unexpected error during claim is returned": {
369+
claimingErr: errors.New("unexpected error during claim"),
370+
checkFn: func(
371+
t *testing.T,
372+
ctx context.Context,
373+
store database.Store,
374+
userClient *codersdk.Client,
375+
user codersdk.User,
376+
templateVersionID uuid.UUID,
377+
presetID uuid.UUID,
378+
) {
379+
// When: a user creates a new workspace with a preset for which prebuilds are configured.
380+
workspaceName := strings.ReplaceAll(testutil.GetRandomName(t), "_", "-")
381+
_, err := userClient.CreateUserWorkspace(ctx, user.Username, codersdk.CreateWorkspaceRequest{
382+
TemplateVersionID: templateVersionID,
383+
Name: workspaceName,
384+
TemplateVersionPresetID: presetID,
385+
})
386+
387+
// Then: unexpected error happened and was propagated all the way to the caller
388+
require.Error(t, err)
389+
require.ErrorContains(t, err, "unexpected error during claim")
390+
391+
// Then: the number of running prebuilds hasn't changed because claiming prebuild is failed.
392+
currentPrebuilds, err := store.GetRunningPrebuiltWorkspaces(ctx)
393+
require.NoError(t, err)
394+
require.Equal(t, expectedPrebuildsCount, len(currentPrebuilds))
395+
},
396+
},
397+
}
398+
399+
for name, tc := range cases {
400+
t.Run(name, func(t *testing.T) {
401+
t.Parallel()
402+
403+
// Setup.
404+
ctx := testutil.Context(t, testutil.WaitMedium)
405+
db, pubsub := dbtestutil.NewDB(t)
406+
errorStore := newErrorStore(db, tc.claimingErr)
407+
408+
logger := testutil.Logger(t)
409+
client, _, api, owner := coderdenttest.NewWithAPI(t, &coderdenttest.Options{
410+
Options: &coderdtest.Options{
411+
IncludeProvisionerDaemon: true,
412+
Database: errorStore,
413+
Pubsub: pubsub,
414+
},
415+
416+
EntitlementsUpdateInterval: time.Second,
417+
})
418+
419+
reconciler := prebuilds.NewStoreReconciler(errorStore, pubsub, codersdk.PrebuildsConfig{}, logger, quartz.NewMock(t))
420+
var claimer agplprebuilds.Claimer = &prebuilds.EnterpriseClaimer{}
421+
api.AGPL.PrebuildsClaimer.Store(&claimer)
422+
423+
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, templateWithAgentAndPresetsWithPrebuilds(desiredInstances))
424+
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
425+
coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
426+
presets, err := client.TemplateVersionPresets(ctx, version.ID)
427+
require.NoError(t, err)
428+
require.Len(t, presets, presetCount)
429+
430+
userClient, user := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleMember())
431+
432+
ctx = dbauthz.AsPrebuildsOrchestrator(ctx)
433+
434+
// Given: the reconciliation state is snapshot.
435+
state, err := reconciler.SnapshotState(ctx, errorStore)
436+
require.NoError(t, err)
437+
require.Len(t, state.Presets, presetCount)
438+
439+
// When: a reconciliation is setup for each preset.
440+
for _, preset := range presets {
441+
ps, err := state.FilterByPreset(preset.ID)
442+
require.NoError(t, err)
443+
require.NotNil(t, ps)
444+
actions, err := reconciler.CalculateActions(ctx, *ps)
445+
require.NoError(t, err)
446+
require.NotNil(t, actions)
447+
448+
require.NoError(t, reconciler.ReconcilePreset(ctx, *ps))
449+
}
450+
451+
// Given: a set of running, eligible prebuilds eventually starts up.
452+
runningPrebuilds := make(map[uuid.UUID]database.GetRunningPrebuiltWorkspacesRow, desiredInstances*presetCount)
453+
require.Eventually(t, func() bool {
454+
rows, err := errorStore.GetRunningPrebuiltWorkspaces(ctx)
455+
require.NoError(t, err)
456+
457+
for _, row := range rows {
458+
runningPrebuilds[row.CurrentPresetID.UUID] = row
459+
460+
agents, err := db.GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx, row.ID)
461+
require.NoError(t, err)
462+
463+
// Workspaces are eligible once its agent is marked "ready".
464+
for _, agent := range agents {
465+
require.NoError(t, db.UpdateWorkspaceAgentLifecycleStateByID(ctx, database.UpdateWorkspaceAgentLifecycleStateByIDParams{
466+
ID: agent.ID,
467+
LifecycleState: database.WorkspaceAgentLifecycleStateReady,
468+
StartedAt: sql.NullTime{Time: time.Now().Add(time.Hour), Valid: true},
469+
ReadyAt: sql.NullTime{Time: time.Now().Add(-1 * time.Hour), Valid: true},
470+
}))
471+
}
472+
}
473+
474+
t.Logf("found %d running prebuilds so far, want %d", len(runningPrebuilds), expectedPrebuildsCount)
475+
476+
return len(runningPrebuilds) == expectedPrebuildsCount
477+
}, testutil.WaitSuperLong, testutil.IntervalSlow)
478+
479+
tc.checkFn(t, ctx, errorStore, userClient, user, version.ID, presets[0].ID)
480+
})
481+
}
482+
}
483+
287484
func templateWithAgentAndPresetsWithPrebuilds(desiredInstances int32) *echo.Responses {
288485
return &echo.Responses{
289486
Parse: echo.ParseComplete,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy