Skip to content

Commit ac96766

Browse files
authored
Test spring cleaning: ensure Sandboxes are terminated, clean out obsolete tests, etc. (#217)
* Always terminate Sandboxes in tests * Call closeEphemeral from onTestFinished * Remove irrelevant/unnecessary tests and asserts * Clean up unneccessary comments * Align Sandbox var naming in exampels ... and tests, while we're at it.
1 parent 2cbb80a commit ac96766

36 files changed

+151
-379
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Both client libraries are pre-1.0, and they have separate versioning.
66

77
- Enabled goroutine leak detection for all tests by default.
88
- Fixed a few remaining goroutine leaks.
9+
- Test clean-ups: ensure we always terminate Sandboxes, close ephemeral objects, etc.
910
- Added debug logging to `CloudBucketMount` creation in Go, bringing it in line with the JS SDK.
1011
- Updated the API for creating `CloudBucketMount`s in JS, using the same `modal.cloudBucketMounts.create()` pattern as other Modal objects, bringing it in line with the Go SDK.
1112

modal-go/app_test.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@ func TestParseGPUConfig(t *testing.T) {
1010
t.Parallel()
1111
g := gomega.NewWithT(t)
1212

13-
// Test empty string returns nil
1413
config, err := parseGPUConfig("")
1514
g.Expect(err).ShouldNot(gomega.HaveOccurred())
1615
g.Expect(config).Should(gomega.BeNil())
1716

18-
// Test single GPU type
1917
config, err = parseGPUConfig("T4")
2018
g.Expect(err).ShouldNot(gomega.HaveOccurred())
2119
g.Expect(config.GetCount()).To(gomega.Equal(uint32(1)))
@@ -31,7 +29,6 @@ func TestParseGPUConfig(t *testing.T) {
3129
g.Expect(config.GetCount()).To(gomega.Equal(uint32(1)))
3230
g.Expect(config.GetGpuType()).To(gomega.Equal("A100-80GB"))
3331

34-
// Test GPU type with count
3532
config, err = parseGPUConfig("A100-80GB:3")
3633
g.Expect(err).ShouldNot(gomega.HaveOccurred())
3734
g.Expect(config.GetCount()).To(gomega.Equal(uint32(3)))
@@ -42,13 +39,11 @@ func TestParseGPUConfig(t *testing.T) {
4239
g.Expect(config.GetCount()).To(gomega.Equal(uint32(2)))
4340
g.Expect(config.GetGpuType()).To(gomega.Equal("T4"))
4441

45-
// Test lowercase conversion
4642
config, err = parseGPUConfig("a100:4")
4743
g.Expect(err).ShouldNot(gomega.HaveOccurred())
4844
g.Expect(config.GetCount()).To(gomega.Equal(uint32(4)))
4945
g.Expect(config.GetGpuType()).To(gomega.Equal("A100"))
5046

51-
// Test invalid count formats
5247
_, err = parseGPUConfig("T4:invalid")
5348
g.Expect(err).Should(gomega.HaveOccurred())
5449
g.Expect(err.Error()).To(gomega.ContainSubstring("invalid GPU count: invalid"))

modal-go/client_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
func TestClientWithLogger(t *testing.T) {
1616
g := gomega.NewWithT(t)
1717

18-
// Use a buffer to capture log output
1918
r, w, err := os.Pipe()
2019
g.Expect(err).ShouldNot(gomega.HaveOccurred())
2120

modal-go/examples/sandbox-poll/main.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,43 +22,43 @@ func main() {
2222

2323
image := mc.Images.FromRegistry("alpine:3.21", nil)
2424

25-
// Create a sandbox that waits for input, then exits with code 42
26-
sandbox, err := mc.Sandboxes.Create(ctx, app, image, &modal.SandboxCreateParams{
25+
// Create a Sandbox that waits for input, then exits with code 42
26+
sb, err := mc.Sandboxes.Create(ctx, app, image, &modal.SandboxCreateParams{
2727
Command: []string{"sh", "-c", "read line; exit 42"},
2828
})
2929
if err != nil {
3030
log.Fatalf("Failed to create Sandbox: %v", err)
3131
}
32-
fmt.Printf("Started Sandbox: %s\n", sandbox.SandboxID)
32+
fmt.Printf("Started Sandbox: %s\n", sb.SandboxID)
3333
defer func() {
34-
if err := sandbox.Terminate(context.Background()); err != nil {
35-
log.Fatalf("Failed to terminate Sandbox %s: %v", sandbox.SandboxID, err)
34+
if err := sb.Terminate(context.Background()); err != nil {
35+
log.Fatalf("Failed to terminate Sandbox %s: %v", sb.SandboxID, err)
3636
}
3737
}()
3838

39-
initialPoll, err := sandbox.Poll(ctx)
39+
initialPoll, err := sb.Poll(ctx)
4040
if err != nil {
4141
log.Fatalf("Failed to poll Sandbox: %v", err)
4242
}
4343
fmt.Printf("Poll result while running: %v\n", initialPoll)
4444

4545
fmt.Println("\nSending input to trigger completion...")
46-
_, err = sandbox.Stdin.Write([]byte("hello, goodbye\n"))
46+
_, err = sb.Stdin.Write([]byte("hello, goodbye\n"))
4747
if err != nil {
4848
log.Fatalf("Failed to write to stdin: %v", err)
4949
}
50-
err = sandbox.Stdin.Close()
50+
err = sb.Stdin.Close()
5151
if err != nil {
5252
log.Fatalf("Failed to close stdin: %v", err)
5353
}
5454

55-
exitCode, err := sandbox.Wait(ctx)
55+
exitCode, err := sb.Wait(ctx)
5656
if err != nil {
5757
log.Fatalf("Failed to wait for Sandbox: %v", err)
5858
}
5959
fmt.Printf("\nSandbox completed with exit code: %d\n", exitCode)
6060

61-
finalPoll, err := sandbox.Poll(ctx)
61+
finalPoll, err := sb.Poll(ctx)
6262
if err != nil {
6363
log.Fatalf("Failed to poll Sandbox after completion: %v", err)
6464
}

modal-go/examples/sandbox-proxy/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ func main() {
3333
Proxy: proxy,
3434
})
3535
if err != nil {
36-
log.Fatalf("Failed to create sandbox: %v", err)
36+
log.Fatalf("Failed to create Sandbox: %v", err)
3737
}
38-
fmt.Printf("Created sandbox with proxy: %s\n", sb.SandboxID)
38+
fmt.Printf("Created Sandbox with proxy: %s\n", sb.SandboxID)
3939
defer func() {
4040
if err := sb.Terminate(context.Background()); err != nil {
4141
log.Fatalf("Failed to terminate Sandbox %s: %v", sb.SandboxID, err)

modal-go/examples/sandbox-tunnels/main.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func main() {
2626
// Create a Sandbox with Python's built-in HTTP server
2727
image := mc.Images.FromRegistry("python:3.12-alpine", nil)
2828

29-
sandbox, err := mc.Sandboxes.Create(ctx, app, image, &modal.SandboxCreateParams{
29+
sb, err := mc.Sandboxes.Create(ctx, app, image, &modal.SandboxCreateParams{
3030
Command: []string{"python3", "-m", "http.server", "8000"},
3131
EncryptedPorts: []int{8000},
3232
Timeout: 1 * time.Minute,
@@ -36,18 +36,18 @@ func main() {
3636
log.Fatalf("Failed to create Sandbox: %v", err)
3737
}
3838
defer func() {
39-
if err := sandbox.Terminate(context.Background()); err != nil {
40-
log.Fatalf("Failed to terminate Sandbox %s: %v", sandbox.SandboxID, err)
39+
if err := sb.Terminate(context.Background()); err != nil {
40+
log.Fatalf("Failed to terminate Sandbox %s: %v", sb.SandboxID, err)
4141
}
4242
}()
4343

44-
fmt.Printf("Sandbox created: %s\n", sandbox.SandboxID)
44+
fmt.Printf("Sandbox created: %s\n", sb.SandboxID)
4545

4646
fmt.Println("Waiting for server to start...")
4747
time.Sleep(3 * time.Second)
4848

4949
fmt.Println("Getting tunnel information...")
50-
tunnels, err := sandbox.Tunnels(ctx, 30*time.Second)
50+
tunnels, err := sb.Tunnels(ctx, 30*time.Second)
5151
if err != nil {
5252
log.Fatalf("Failed to get tunnels: %v", err)
5353
}

modal-go/examples/sandbox/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func main() {
2929
if err != nil {
3030
log.Fatalf("Failed to create Sandbox: %v", err)
3131
}
32-
fmt.Printf("sandbox: %s\n", sb.SandboxID)
32+
fmt.Printf("Created Sandbox with ID: %s\n", sb.SandboxID)
3333
defer func() {
3434
if err := sb.Terminate(context.Background()); err != nil {
3535
log.Fatalf("Failed to terminate Sandbox %s: %v", sb.SandboxID, err)

modal-go/test/auth_token_manager_test.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ func (m *mockAuthClient) AuthTokenGet(ctx context.Context, req *pb.AuthTokenGetR
3232
}.Build(), nil
3333
}
3434

35-
// Creates a JWT token for testing
3635
func createTestJWT(expiry int64) string {
3736
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
3837
"exp": expiry,
@@ -53,14 +52,12 @@ func TestAuthTokenManager_DecodeJWT(t *testing.T) {
5352
validToken := createTestJWT(123456789)
5453
mockClient.setAuthToken(validToken)
5554

56-
// FetchToken should decode and store the JWT
5755
_, err := manager.FetchToken(context.Background())
5856
g.Expect(err).ShouldNot(gomega.HaveOccurred())
5957

6058
g.Expect(manager.GetCurrentToken()).Should(gomega.Equal(validToken))
6159
}
6260

63-
// Setting the initial token and having it cached.
6461
func TestAuthTokenManager_InitialFetch(t *testing.T) {
6562
t.Parallel()
6663
g := gomega.NewWithT(t)
@@ -89,16 +86,13 @@ func TestAuthTokenManager_IsExpired(t *testing.T) {
8986

9087
manager := modal.NewAuthTokenManager(nil, slog.Default())
9188

92-
// Test not expired
9389
manager.SetToken("token", time.Now().Unix()+3600)
9490
g.Expect(manager.IsExpired()).Should(gomega.BeFalse())
9591

96-
// Test expired
9792
manager.SetToken("token", time.Now().Unix()-3600)
9893
g.Expect(manager.IsExpired()).Should(gomega.BeTrue())
9994
}
10095

101-
// Refreshing an expired token. Unlikely to occur since we refresh in background before expiry.
10296
func TestAuthTokenManager_RefreshExpiredToken(t *testing.T) {
10397
t.Parallel()
10498
g := gomega.NewWithT(t)
@@ -113,12 +107,10 @@ func TestAuthTokenManager_RefreshExpiredToken(t *testing.T) {
113107
manager.SetToken(expiringToken, now-60)
114108
mockClient.setAuthToken(freshToken)
115109

116-
// Start the background refresh goroutine
117110
err := manager.Start(context.Background())
118111
g.Expect(err).ToNot(gomega.HaveOccurred())
119112
defer manager.Stop()
120113

121-
// Wait for background goroutine to refresh the token
122114
g.Eventually(func() string {
123115
return manager.GetCurrentToken()
124116
}, "1s", "10ms").Should(gomega.Equal(freshToken))
@@ -138,18 +130,15 @@ func TestAuthTokenManager_RefreshNearExpiryToken(t *testing.T) {
138130
manager.SetToken(expiringToken, now+60)
139131
mockClient.setAuthToken(freshToken)
140132

141-
// Start the background refresh goroutine
142133
err := manager.Start(context.Background())
143134
g.Expect(err).ToNot(gomega.HaveOccurred())
144135
defer manager.Stop()
145136

146-
// Wait for background goroutine to refresh the token
147137
g.Eventually(func() string {
148138
return manager.GetCurrentToken()
149139
}, "1s", "10ms").Should(gomega.Equal(freshToken))
150140
}
151141

152-
// Should error out if no valid token is available.
153142
func TestAuthTokenManager_GetToken_ExpiredToken(t *testing.T) {
154143
t.Parallel()
155144
g := gomega.NewWithT(t)

modal-go/test/cls_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ func TestClsCall(t *testing.T) {
2020
instance, err := cls.Instance(ctx, nil)
2121
g.Expect(err).ShouldNot(gomega.HaveOccurred())
2222

23-
// Try accessing a non-existent method
2423
_, err = instance.Method("nonexistent")
2524
g.Expect(err).Should(gomega.BeAssignableToTypeOf(modal.NotFoundError{}))
2625

modal-go/test/function_call_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,9 @@ func TestFunctionSpawn(t *testing.T) {
5050
_, err = functionCall.Get(ctx, nil)
5151
g.Expect(err).Should(gomega.HaveOccurred())
5252

53-
// Spawn function with long running input.
5453
functionCall, err = sleep.Spawn(ctx, nil, map[string]any{"t": 5})
5554
g.Expect(err).ShouldNot(gomega.HaveOccurred())
5655

57-
// Get is now expected to timeout.
5856
timeout := 1 * time.Second
5957
_, err = functionCall.Get(ctx, &modal.FunctionCallGetParams{Timeout: &timeout})
6058
g.Expect(err).Should(gomega.HaveOccurred())

0 commit comments

Comments
 (0)