Skip to content

Commit 529c0ea

Browse files
committed
test(client-proxy): expand unit test coverage across packages
Lifts overall coverage in `packages/client-proxy` significantly by adding tests for previously untested code paths: - `internal/cfg`: 0% -> 100%. New `model_test.go` covers `Parse` defaults, env-var overrides for every field, and an invalid-integer error case. - `internal`: 0% -> 100%. New `info_test.go` covers `ServiceInfo` zero-value, transitions across `Healthy`/`Draining`/`Unhealthy`, idempotent same-state writes, and a race-safe concurrent get/set exercise. - `internal/proxy`: 40.8% -> 87.7%. - `paused_sandbox_resumer_grpc_test.go` exercises `NewGRPCPausedSandboxResumer` constructor branches (empty address, OAuth misconfiguration, insecure and TLS credential paths) and drives `Resume`/`Init`/`Close` against an in-process `bufconn` gRPC server, asserting metadata propagation (`MetadataSandboxRequestPort`, `MetadataTrafficAccessToken`, `MetadataEnvdAccessToken`), empty-token omission, server-side error surfacing, and auth-failure short-circuit. - `proxy_test.go` adds `NewClientProxy` tests covering construction with a no-op meter provider, idle-timeout wiring, repeated registration, and end-to-end handler responses for the not-found, permission-denied, resource-exhausted, still-transitioning, and invalid-host code paths. - `proxy_test.go` also adds an `errorCatalog` fake plus `TestCatalogResolution_CatalogReturnsGenericError` to cover the previously untested wrapped-error branch in `catalogResolution` that returns errors other than `ErrSandboxNotFound`. All new tests pass under `-race`, `go vet ./...` is clean, and `go build ./...` succeeds.
1 parent d224a77 commit 529c0ea

4 files changed

Lines changed: 557 additions & 0 deletions

File tree

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package cfg
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
func TestParse_Defaults(t *testing.T) {
10+
t.Setenv("HEALTH_PORT", "")
11+
t.Setenv("PROXY_PORT", "")
12+
t.Setenv("REDIS_URL", "")
13+
t.Setenv("REDIS_CLUSTER_URL", "")
14+
t.Setenv("REDIS_TLS_CA_BASE64", "")
15+
t.Setenv("REDIS_POOL_SIZE", "")
16+
t.Setenv("API_INTERNAL_GRPC_ADDRESS", "")
17+
t.Setenv("API_EDGE_GRPC_ADDRESS", "")
18+
t.Setenv("API_EDGE_GRPC_OAUTH_CLIENT_ID", "")
19+
t.Setenv("API_EDGE_GRPC_OAUTH_CLIENT_SECRET", "")
20+
t.Setenv("API_EDGE_GRPC_OAUTH_TOKEN_URL", "")
21+
22+
cfg, err := Parse()
23+
require.NoError(t, err)
24+
require.EqualValues(t, 3003, cfg.HealthPort)
25+
require.EqualValues(t, 3002, cfg.ProxyPort)
26+
require.Equal(t, 40, cfg.RedisPoolSize)
27+
require.Empty(t, cfg.RedisURL)
28+
require.Empty(t, cfg.RedisClusterURL)
29+
require.Empty(t, cfg.RedisTLSCABase64)
30+
require.Empty(t, cfg.APIInternalGRPCAddress)
31+
require.Empty(t, cfg.APIEdgeGRPCAddress)
32+
require.Empty(t, cfg.APIEdgeGRPCOAuthClientID)
33+
require.Empty(t, cfg.APIEdgeGRPCOAuthClientSecret)
34+
require.Empty(t, cfg.APIEdgeGRPCOAuthTokenURL)
35+
}
36+
37+
func TestParse_OverridesFromEnv(t *testing.T) {
38+
t.Setenv("HEALTH_PORT", "9001")
39+
t.Setenv("PROXY_PORT", "9002")
40+
t.Setenv("REDIS_URL", "redis://localhost:6379")
41+
t.Setenv("REDIS_CLUSTER_URL", "redis://cluster:6379")
42+
t.Setenv("REDIS_TLS_CA_BASE64", "Y2EtZGF0YQ==")
43+
t.Setenv("REDIS_POOL_SIZE", "12")
44+
t.Setenv("API_INTERNAL_GRPC_ADDRESS", "internal:5005")
45+
t.Setenv("API_EDGE_GRPC_ADDRESS", "edge:5006")
46+
t.Setenv("API_EDGE_GRPC_OAUTH_CLIENT_ID", "client-id")
47+
t.Setenv("API_EDGE_GRPC_OAUTH_CLIENT_SECRET", "client-secret")
48+
t.Setenv("API_EDGE_GRPC_OAUTH_TOKEN_URL", "https://tokens.example.com")
49+
50+
cfg, err := Parse()
51+
require.NoError(t, err)
52+
require.EqualValues(t, 9001, cfg.HealthPort)
53+
require.EqualValues(t, 9002, cfg.ProxyPort)
54+
require.Equal(t, "redis://localhost:6379", cfg.RedisURL)
55+
require.Equal(t, "redis://cluster:6379", cfg.RedisClusterURL)
56+
require.Equal(t, "Y2EtZGF0YQ==", cfg.RedisTLSCABase64)
57+
require.Equal(t, 12, cfg.RedisPoolSize)
58+
require.Equal(t, "internal:5005", cfg.APIInternalGRPCAddress)
59+
require.Equal(t, "edge:5006", cfg.APIEdgeGRPCAddress)
60+
require.Equal(t, "client-id", cfg.APIEdgeGRPCOAuthClientID)
61+
require.Equal(t, "client-secret", cfg.APIEdgeGRPCOAuthClientSecret)
62+
require.Equal(t, "https://tokens.example.com", cfg.APIEdgeGRPCOAuthTokenURL)
63+
}
64+
65+
func TestParse_InvalidIntegerReturnsError(t *testing.T) {
66+
t.Setenv("HEALTH_PORT", "not-a-number")
67+
68+
_, err := Parse()
69+
require.Error(t, err)
70+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package internal
2+
3+
import (
4+
"sync"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestServiceInfo_DefaultStatusIsZeroValue(t *testing.T) {
11+
t.Parallel()
12+
13+
s := &ServiceInfo{}
14+
require.Equal(t, ServiceHealth(""), s.GetStatus())
15+
}
16+
17+
func TestServiceInfo_SetAndGetStatus(t *testing.T) {
18+
t.Parallel()
19+
20+
s := &ServiceInfo{}
21+
ctx := t.Context()
22+
23+
s.SetStatus(ctx, Healthy)
24+
require.Equal(t, Healthy, s.GetStatus())
25+
26+
s.SetStatus(ctx, Draining)
27+
require.Equal(t, Draining, s.GetStatus())
28+
29+
s.SetStatus(ctx, Unhealthy)
30+
require.Equal(t, Unhealthy, s.GetStatus())
31+
}
32+
33+
func TestServiceInfo_SetSameStatusIsIdempotent(t *testing.T) {
34+
t.Parallel()
35+
36+
s := &ServiceInfo{}
37+
ctx := t.Context()
38+
39+
s.SetStatus(ctx, Healthy)
40+
s.SetStatus(ctx, Healthy)
41+
require.Equal(t, Healthy, s.GetStatus())
42+
}
43+
44+
func TestServiceInfo_ConcurrentAccess(t *testing.T) {
45+
t.Parallel()
46+
47+
s := &ServiceInfo{}
48+
ctx := t.Context()
49+
statuses := []ServiceHealth{Healthy, Draining, Unhealthy}
50+
51+
var wg sync.WaitGroup
52+
for i := range 50 {
53+
wg.Add(2)
54+
go func(idx int) {
55+
defer wg.Done()
56+
s.SetStatus(ctx, statuses[idx%len(statuses)])
57+
}(i)
58+
go func() {
59+
defer wg.Done()
60+
_ = s.GetStatus()
61+
}()
62+
}
63+
wg.Wait()
64+
65+
require.Contains(t, statuses, s.GetStatus())
66+
}
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
package proxy
2+
3+
import (
4+
"context"
5+
"errors"
6+
"net"
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
"google.golang.org/grpc"
11+
"google.golang.org/grpc/codes"
12+
"google.golang.org/grpc/credentials/insecure"
13+
"google.golang.org/grpc/metadata"
14+
"google.golang.org/grpc/status"
15+
"google.golang.org/grpc/test/bufconn"
16+
17+
proxygrpc "github.com/e2b-dev/infra/packages/shared/pkg/grpc/proxy"
18+
)
19+
20+
type fakeSandboxServer struct {
21+
proxygrpc.UnimplementedSandboxServiceServer
22+
23+
resp *proxygrpc.SandboxResumeResponse
24+
err error
25+
26+
gotSandboxID string
27+
gotMetadata metadata.MD
28+
}
29+
30+
func (f *fakeSandboxServer) ResumeSandbox(ctx context.Context, req *proxygrpc.SandboxResumeRequest) (*proxygrpc.SandboxResumeResponse, error) {
31+
f.gotSandboxID = req.GetSandboxId()
32+
if md, ok := metadata.FromIncomingContext(ctx); ok {
33+
f.gotMetadata = md
34+
}
35+
36+
if f.err != nil {
37+
return nil, f.err
38+
}
39+
40+
return f.resp, nil
41+
}
42+
43+
func startFakeServer(t *testing.T, srv proxygrpc.SandboxServiceServer) *grpc.ClientConn {
44+
t.Helper()
45+
46+
listener := bufconn.Listen(1024 * 1024)
47+
server := grpc.NewServer()
48+
proxygrpc.RegisterSandboxServiceServer(server, srv)
49+
50+
go func() {
51+
_ = server.Serve(listener)
52+
}()
53+
t.Cleanup(server.Stop)
54+
55+
conn, err := grpc.NewClient(
56+
"passthrough:///bufnet",
57+
grpc.WithTransportCredentials(insecure.NewCredentials()),
58+
grpc.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) {
59+
return listener.DialContext(ctx)
60+
}),
61+
)
62+
require.NoError(t, err)
63+
t.Cleanup(func() {
64+
_ = conn.Close()
65+
})
66+
67+
return conn
68+
}
69+
70+
func TestNewGRPCPausedSandboxResumer_EmptyAddressErrors(t *testing.T) {
71+
t.Parallel()
72+
73+
r, err := NewGRPCPausedSandboxResumer(t.Context(), " ", GRPCOAuthConfig{}, false)
74+
require.Error(t, err)
75+
require.Nil(t, r)
76+
}
77+
78+
func TestNewGRPCPausedSandboxResumer_OAuthMisconfiguredErrors(t *testing.T) {
79+
t.Parallel()
80+
81+
r, err := NewGRPCPausedSandboxResumer(t.Context(), "127.0.0.1:1234", GRPCOAuthConfig{ClientID: "only-id"}, false)
82+
require.Error(t, err)
83+
require.Nil(t, r)
84+
}
85+
86+
func TestNewGRPCPausedSandboxResumer_InsecureSucceeds(t *testing.T) {
87+
t.Parallel()
88+
89+
r, err := NewGRPCPausedSandboxResumer(t.Context(), "127.0.0.1:1234", GRPCOAuthConfig{}, false)
90+
require.NoError(t, err)
91+
require.NotNil(t, r)
92+
93+
closer, ok := r.(interface {
94+
Close(ctx context.Context) error
95+
})
96+
require.True(t, ok)
97+
require.NoError(t, closer.Close(t.Context()))
98+
}
99+
100+
func TestNewGRPCPausedSandboxResumer_TLSSucceeds(t *testing.T) {
101+
t.Parallel()
102+
103+
r, err := NewGRPCPausedSandboxResumer(t.Context(), "127.0.0.1:1234", GRPCOAuthConfig{}, true)
104+
require.NoError(t, err)
105+
require.NotNil(t, r)
106+
107+
closer, ok := r.(interface {
108+
Close(ctx context.Context) error
109+
})
110+
require.True(t, ok)
111+
require.NoError(t, closer.Close(t.Context()))
112+
}
113+
114+
func TestGRPCPausedSandboxResumer_ResumeSendsMetadataAndReturnsIP(t *testing.T) {
115+
t.Parallel()
116+
117+
srv := &fakeSandboxServer{
118+
resp: &proxygrpc.SandboxResumeResponse{OrchestratorIp: " 10.0.0.5 "},
119+
}
120+
conn := startFakeServer(t, srv)
121+
122+
r := &grpcPausedSandboxResumer{
123+
conn: conn,
124+
client: proxygrpc.NewSandboxServiceClient(conn),
125+
auth: noopGrpcResumeAuth{},
126+
}
127+
128+
ip, err := r.Resume(t.Context(), "sbx-123", 49983, "traffic-token", "envd-token")
129+
require.NoError(t, err)
130+
require.Equal(t, "10.0.0.5", ip)
131+
require.Equal(t, "sbx-123", srv.gotSandboxID)
132+
require.Equal(t, []string{"49983"}, srv.gotMetadata.Get(proxygrpc.MetadataSandboxRequestPort))
133+
require.Equal(t, []string{"traffic-token"}, srv.gotMetadata.Get(proxygrpc.MetadataTrafficAccessToken))
134+
require.Equal(t, []string{"envd-token"}, srv.gotMetadata.Get(proxygrpc.MetadataEnvdAccessToken))
135+
}
136+
137+
func TestGRPCPausedSandboxResumer_ResumeOmitsEmptyTokens(t *testing.T) {
138+
t.Parallel()
139+
140+
srv := &fakeSandboxServer{
141+
resp: &proxygrpc.SandboxResumeResponse{OrchestratorIp: "10.0.0.6"},
142+
}
143+
conn := startFakeServer(t, srv)
144+
145+
r := &grpcPausedSandboxResumer{
146+
conn: conn,
147+
client: proxygrpc.NewSandboxServiceClient(conn),
148+
auth: noopGrpcResumeAuth{},
149+
}
150+
151+
ip, err := r.Resume(t.Context(), "sbx-456", 8080, "", "")
152+
require.NoError(t, err)
153+
require.Equal(t, "10.0.0.6", ip)
154+
require.Empty(t, srv.gotMetadata.Get(proxygrpc.MetadataTrafficAccessToken))
155+
require.Empty(t, srv.gotMetadata.Get(proxygrpc.MetadataEnvdAccessToken))
156+
require.Equal(t, []string{"8080"}, srv.gotMetadata.Get(proxygrpc.MetadataSandboxRequestPort))
157+
}
158+
159+
func TestGRPCPausedSandboxResumer_ResumeReturnsServerError(t *testing.T) {
160+
t.Parallel()
161+
162+
srv := &fakeSandboxServer{
163+
err: status.Error(codes.NotFound, "missing"),
164+
}
165+
conn := startFakeServer(t, srv)
166+
167+
r := &grpcPausedSandboxResumer{
168+
conn: conn,
169+
client: proxygrpc.NewSandboxServiceClient(conn),
170+
auth: noopGrpcResumeAuth{},
171+
}
172+
173+
ip, err := r.Resume(t.Context(), "sbx", 80, "", "")
174+
require.Error(t, err)
175+
require.Empty(t, ip)
176+
st, ok := status.FromError(errors.Unwrap(err))
177+
require.True(t, ok)
178+
require.Equal(t, codes.NotFound, st.Code())
179+
}
180+
181+
type errAuth struct {
182+
err error
183+
}
184+
185+
func (e errAuth) authorize(_ context.Context) (context.Context, error) {
186+
return nil, e.err
187+
}
188+
189+
func TestGRPCPausedSandboxResumer_ResumeAuthError(t *testing.T) {
190+
t.Parallel()
191+
192+
srv := &fakeSandboxServer{
193+
resp: &proxygrpc.SandboxResumeResponse{OrchestratorIp: "10.0.0.7"},
194+
}
195+
conn := startFakeServer(t, srv)
196+
197+
authErr := errors.New("auth failed")
198+
r := &grpcPausedSandboxResumer{
199+
conn: conn,
200+
client: proxygrpc.NewSandboxServiceClient(conn),
201+
auth: errAuth{err: authErr},
202+
}
203+
204+
ip, err := r.Resume(t.Context(), "sbx", 80, "", "")
205+
require.ErrorIs(t, err, authErr)
206+
require.Empty(t, ip)
207+
}
208+
209+
func TestGRPCPausedSandboxResumer_Init(t *testing.T) {
210+
t.Parallel()
211+
212+
srv := &fakeSandboxServer{}
213+
conn := startFakeServer(t, srv)
214+
215+
r := &grpcPausedSandboxResumer{
216+
conn: conn,
217+
client: proxygrpc.NewSandboxServiceClient(conn),
218+
auth: noopGrpcResumeAuth{},
219+
}
220+
221+
// Should not panic
222+
r.Init(t.Context())
223+
}
224+
225+
func TestGRPCPausedSandboxResumer_Close(t *testing.T) {
226+
t.Parallel()
227+
228+
srv := &fakeSandboxServer{}
229+
conn := startFakeServer(t, srv)
230+
231+
r := &grpcPausedSandboxResumer{
232+
conn: conn,
233+
client: proxygrpc.NewSandboxServiceClient(conn),
234+
auth: noopGrpcResumeAuth{},
235+
}
236+
237+
require.NoError(t, r.Close(t.Context()))
238+
}

0 commit comments

Comments
 (0)