Skip to content

Commit 08f1ba9

Browse files
committed
Add durable extension acceptance fixtures
1 parent de4893d commit 08f1ba9

4 files changed

Lines changed: 199 additions & 0 deletions

File tree

internal/acctest/acctest.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,48 @@ func cleanupProxy(t testing.TB, client proxyCleaner, projectID, id string) {
243243
})
244244
}
245245

246+
// CleanupExtension registers a cleanup that deletes the extension from projectID;
247+
// empty means the env-configured default project.
248+
func CleanupExtension(t testing.TB, projectID, id string) {
249+
t.Helper()
250+
251+
cleanupExtension(t, ClientFromEnv(), projectID, id)
252+
}
253+
254+
type extensionCleaner interface {
255+
DefaultProjectID() string
256+
DeleteExtension(context.Context, string, string) error
257+
}
258+
259+
func cleanupExtension(t testing.TB, client extensionCleaner, projectID, id string) {
260+
t.Helper()
261+
262+
if id == "" {
263+
return
264+
}
265+
if !AcceptanceEnabled() {
266+
t.Fatalf("%s must be set to clean up Kernel acceptance test resources", EnvAcceptance)
267+
return
268+
}
269+
if os.Getenv(EnvAPIKey) == "" {
270+
t.Fatalf("%s must be set to clean up Kernel acceptance test resources", EnvAPIKey)
271+
return
272+
}
273+
274+
if projectID == "" {
275+
projectID = client.DefaultProjectID()
276+
}
277+
278+
t.Cleanup(func() {
279+
ctx, cancel := context.WithTimeout(context.Background(), cleanupTimeout)
280+
defer cancel()
281+
282+
if err := client.DeleteExtension(ctx, projectID, id); err != nil && !IsNotFound(err) {
283+
t.Errorf("cleanup Kernel extension %s: %v", id, err)
284+
}
285+
})
286+
}
287+
246288
func ClientFromEnv() kernelclient.Clients {
247289
return kernelclient.New(kernelclient.Config{
248290
APIKey: os.Getenv(EnvAPIKey),

internal/acctest/extension_test.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package acctest
2+
3+
import (
4+
"context"
5+
"errors"
6+
"testing"
7+
)
8+
9+
type fakeExtensionCleaner struct {
10+
defaultProjectID string
11+
delete func(context.Context, string, string) error
12+
}
13+
14+
func (f fakeExtensionCleaner) DefaultProjectID() string {
15+
return f.defaultProjectID
16+
}
17+
18+
func (f fakeExtensionCleaner) DeleteExtension(ctx context.Context, projectID, id string) error {
19+
return f.delete(ctx, projectID, id)
20+
}
21+
22+
func TestCleanupExtension(t *testing.T) {
23+
tests := map[string]struct {
24+
acceptance string
25+
apiKey string
26+
projectID string
27+
defaultProject string
28+
id string
29+
deleteErr error
30+
wantProjectID string
31+
wantCleanups int
32+
wantDelete bool
33+
wantFailure bool
34+
}{
35+
"empty ID is ignored": {},
36+
"acceptance disabled": {
37+
apiKey: "test-key",
38+
id: "extension_123",
39+
wantFailure: true,
40+
},
41+
"API key missing": {
42+
acceptance: "1",
43+
id: "extension_123",
44+
wantFailure: true,
45+
},
46+
"default project is resolved": {
47+
acceptance: "1",
48+
apiKey: "test-key",
49+
defaultProject: "project_default",
50+
id: "extension_123",
51+
wantProjectID: "project_default",
52+
wantCleanups: 1,
53+
wantDelete: true,
54+
},
55+
"not found is already clean": {
56+
acceptance: "1",
57+
apiKey: "test-key",
58+
projectID: "project_explicit",
59+
id: "extension_123",
60+
deleteErr: notFoundAPIError(),
61+
wantProjectID: "project_explicit",
62+
wantCleanups: 1,
63+
wantDelete: true,
64+
},
65+
"delete error is reported": {
66+
acceptance: "1",
67+
apiKey: "test-key",
68+
projectID: "project_explicit",
69+
id: "extension_123",
70+
deleteErr: errors.New("connection reset"),
71+
wantProjectID: "project_explicit",
72+
wantCleanups: 1,
73+
wantDelete: true,
74+
wantFailure: true,
75+
},
76+
}
77+
78+
for name, test := range tests {
79+
t.Run(name, func(t *testing.T) {
80+
t.Setenv(EnvAcceptance, test.acceptance)
81+
t.Setenv(EnvAPIKey, test.apiKey)
82+
83+
var gotID, gotProjectID string
84+
deleteCalled := false
85+
deadlineSet := false
86+
recorder := &testRecorder{TB: t}
87+
cleanupExtension(recorder, fakeExtensionCleaner{
88+
defaultProjectID: test.defaultProject,
89+
delete: func(ctx context.Context, projectID, id string) error {
90+
deleteCalled = true
91+
_, deadlineSet = ctx.Deadline()
92+
gotProjectID = projectID
93+
gotID = id
94+
return test.deleteErr
95+
},
96+
}, test.projectID, test.id)
97+
98+
if got, want := len(recorder.cleanups), test.wantCleanups; got != want {
99+
t.Fatalf("cleanupExtension registered %d cleanups, want %d", got, want)
100+
}
101+
if test.wantCleanups == 1 {
102+
recorder.cleanups[0]()
103+
}
104+
if recorder.failed != test.wantFailure {
105+
t.Fatalf("cleanupExtension failure = %t, want %t", recorder.failed, test.wantFailure)
106+
}
107+
if deleteCalled != test.wantDelete {
108+
t.Fatalf("cleanupExtension called delete = %t, want %t", deleteCalled, test.wantDelete)
109+
}
110+
if deleteCalled && !deadlineSet {
111+
t.Fatal("cleanupExtension called delete without a context deadline")
112+
}
113+
if test.wantDelete && (gotID != test.id || gotProjectID != test.wantProjectID) {
114+
t.Fatalf("cleanup extension scope/id = %q/%q, want %q/%q", gotProjectID, gotID, test.wantProjectID, test.id)
115+
}
116+
})
117+
}
118+
}

internal/kernelclient/client.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,14 @@ func (c Clients) GetExtension(ctx context.Context, projectID, idOrName string) (
185185
return c.extensions.Get(ctx, idOrName, scope(projectID)...)
186186
}
187187

188+
func (c Clients) UploadExtension(ctx context.Context, projectID string, params kernel.ExtensionUploadParams) (*kernel.ExtensionUploadResponse, error) {
189+
return c.extensions.Upload(ctx, params, scope(projectID, noMutationRetries())...)
190+
}
191+
192+
func (c Clients) DeleteExtension(ctx context.Context, projectID, id string) error {
193+
return c.extensions.Delete(ctx, id, scope(projectID, noMutationRetries())...)
194+
}
195+
188196
func (c Clients) CreateBrowserPool(ctx context.Context, projectID string, params kernel.BrowserPoolNewParams) (*kernel.BrowserPool, error) {
189197
return c.browserPools.New(ctx, params, scope(projectID, noMutationRetries())...)
190198
}

internal/kernelclient/client_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,26 @@ func TestMutationsDisableSDKRetriesAndUseExpectedScope(t *testing.T) {
370370
return clients.DeleteProxy(ctx, "project_123", "proxy_123")
371371
},
372372
},
373+
"extension upload": {
374+
method: http.MethodPost,
375+
path: "/extensions",
376+
projectID: "project_123",
377+
call: func(ctx context.Context, clients Clients) error {
378+
_, err := clients.UploadExtension(ctx, "project_123", kernel.ExtensionUploadParams{
379+
File: strings.NewReader("extension archive"),
380+
Name: kernel.String("Extension"),
381+
})
382+
return err
383+
},
384+
},
385+
"extension delete": {
386+
method: http.MethodDelete,
387+
path: "/extensions/extension_123",
388+
projectID: "project_123",
389+
call: func(ctx context.Context, clients Clients) error {
390+
return clients.DeleteExtension(ctx, "project_123", "extension_123")
391+
},
392+
},
373393
"browser pool create": {
374394
method: http.MethodPost,
375395
path: "/browser_pools",
@@ -496,6 +516,17 @@ func TestClientsDoNotExposeProxyHealthCheck(t *testing.T) {
496516
}
497517
}
498518

519+
func TestClientsDoNotExposeExtensionArchiveMethods(t *testing.T) {
520+
t.Parallel()
521+
522+
typ := reflect.TypeOf(Clients{})
523+
for _, name := range []string{"DownloadExtension", "DownloadExtensionFromChromeStore"} {
524+
if _, ok := typ.MethodByName(name); ok {
525+
t.Fatalf("Clients exposes extension archive method %s", name)
526+
}
527+
}
528+
}
529+
499530
type capturedRequest struct {
500531
Method string
501532
Path string

0 commit comments

Comments
 (0)