Skip to content

Commit 1cdeb61

Browse files
Merge branch 'development' into add-tst-grpc-client
2 parents 42b1907 + 2097b67 commit 1cdeb61

File tree

4 files changed

+845
-40
lines changed

4 files changed

+845
-40
lines changed

examples/grpc/grpc-unary-server/main_test.go

Lines changed: 96 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,51 +12,116 @@ import (
1212
"google.golang.org/grpc/credentials/insecure"
1313

1414
"gofr.dev/examples/grpc/grpc-unary-server/server"
15+
"gofr.dev/pkg/gofr/testutil"
1516
)
1617

17-
func TestGRPCServer(t *testing.T) {
18-
client, conn := createGRPCClient(t)
18+
func TestMain(m *testing.M) {
19+
os.Setenv("GOFR_TELEMETRY", "false")
20+
m.Run()
21+
}
22+
23+
func TestIntegration_UnaryServer(t *testing.T) {
24+
configs := testutil.NewServerConfigs(t)
25+
26+
go main()
27+
time.Sleep(100 * time.Millisecond) // Giving some time to start the server
28+
29+
// Create gRPC client connection
30+
conn, err := grpc.Dial(configs.GRPCHost, grpc.WithTransportCredentials(insecure.NewCredentials()))
31+
require.NoError(t, err, "Failed to connect to unary server")
1932
defer conn.Close()
2033

34+
client := server.NewHelloClient(conn)
35+
2136
tests := []struct {
22-
desc string
23-
request *server.HelloRequest
24-
expectedErr error
25-
responseMessage string
37+
desc string
38+
name string
39+
expected string
2640
}{
27-
{"SayHello with name", &server.HelloRequest{Name: "John"},
28-
nil, "Hello John!"},
29-
{"SayHello without name", &server.HelloRequest{},
30-
nil, "Hello World!"},
31-
{"Name exceeding limit", &server.HelloRequest{Name: "This name exceeds the allowed maximum length."},
32-
nil, "Hello This name exceeds the allowed maximum length.!"},
41+
{"hello with name", "gofr", "Hello gofr!"},
42+
{"hello with empty name", "", "Hello World!"},
43+
{"hello with special chars", "!@#$%^&*", "Hello !@#$%^&*!"},
44+
{"hello with unicode", "你好世界", "Hello 你好世界!"},
45+
{"hello with long name", "ThisIsAVeryLongNameThatShouldStillWork", "Hello ThisIsAVeryLongNameThatShouldStillWork!"},
3346
}
3447

35-
for _, tc := range tests {
36-
resp, err := client.SayHello(context.Background(), tc.request)
37-
require.NoError(t, err)
38-
assert.Equal(t, tc.responseMessage, resp.Message)
48+
for i, tc := range tests {
49+
resp, err := client.SayHello(context.Background(), &server.HelloRequest{
50+
Name: tc.name,
51+
})
52+
53+
require.NoError(t, err, "TEST[%d], Failed.\n%s", i, tc.desc)
54+
assert.Equal(t, tc.expected, resp.Message, "TEST[%d], Failed.\n%s", i, tc.desc)
3955
}
56+
}
4057

41-
// case of empty request
42-
resp, err := client.SayHello(context.Background(), nil)
43-
assert.Equal(t, "Hello World!", resp.Message)
44-
require.NoError(t, err)
58+
func TestIntegration_UnaryServer_Concurrent(t *testing.T) {
59+
configs := testutil.NewServerConfigs(t)
4560

46-
// Test context cancellation
47-
ctx, cancel := context.WithCancel(context.Background())
48-
cancel()
49-
_, err = client.SayHello(ctx, &server.HelloRequest{Name: "Test"})
50-
assert.Equal(t, "rpc error: code = Canceled desc = context canceled", err.Error())
51-
}
61+
go main()
62+
time.Sleep(100 * time.Millisecond) // Giving some time to start the server
63+
64+
// Create gRPC client connection
65+
conn, err := grpc.Dial(configs.GRPCHost, grpc.WithTransportCredentials(insecure.NewCredentials()))
66+
require.NoError(t, err, "Failed to connect to unary server")
67+
defer conn.Close()
5268

53-
func createGRPCClient(t *testing.T) (server.HelloClient, *grpc.ClientConn) {
54-
conn, err := grpc.Dial(":9000", grpc.WithTransportCredentials(insecure.NewCredentials()))
55-
if err != nil {
56-
t.Errorf("did not connect: %s", err)
69+
client := server.NewHelloClient(conn)
70+
71+
numClients := 5
72+
done := make(chan bool, numClients)
73+
74+
for i := 0; i < numClients; i++ {
75+
go func(id int) {
76+
resp, err := client.SayHello(context.Background(), &server.HelloRequest{
77+
Name: "concurrent client " + string(rune(id)),
78+
})
79+
require.NoError(t, err, "Concurrent SayHello RPC failed for client %d", id)
80+
assert.Contains(t, resp.Message, "concurrent client", "Unexpected response message for concurrent client %d", id)
81+
done <- true
82+
}(i)
5783
}
5884

59-
return server.NewHelloClient(conn), conn
85+
// Wait for all concurrent clients to complete
86+
for i := 0; i < numClients; i++ {
87+
<-done
88+
}
89+
}
90+
91+
func TestIntegration_UnaryServer_ErrorHandling(t *testing.T) {
92+
configs := testutil.NewServerConfigs(t)
93+
94+
go main()
95+
time.Sleep(100 * time.Millisecond) // Giving some time to start the server
96+
97+
// Create gRPC client connection
98+
conn, err := grpc.Dial(configs.GRPCHost, grpc.WithTransportCredentials(insecure.NewCredentials()))
99+
require.NoError(t, err, "Failed to connect to unary server")
100+
defer conn.Close()
101+
102+
client := server.NewHelloClient(conn)
103+
104+
t.Run("ContextCancellation", func(t *testing.T) {
105+
ctx, cancel := context.WithCancel(context.Background())
106+
cancel() // Cancel immediately
107+
108+
_, err := client.SayHello(ctx, &server.HelloRequest{
109+
Name: "cancel test",
110+
})
111+
assert.Error(t, err, "Context cancellation should return error")
112+
assert.Contains(t, err.Error(), "context canceled")
113+
})
114+
115+
t.Run("TimeoutHandling", func(t *testing.T) {
116+
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond) // Very short timeout
117+
defer cancel()
118+
119+
_, err := client.SayHello(ctx, &server.HelloRequest{
120+
Name: "timeout test",
121+
})
122+
assert.Error(t, err, "Timeout should return error")
123+
assert.Contains(t, err.Error(), "context deadline exceeded")
124+
})
60125
}
61126

62127
func TestHelloProtoMethods(t *testing.T) {
@@ -70,12 +135,3 @@ func TestHelloProtoMethods(t *testing.T) {
70135
assert.Equal(t, "Hello World", resp.GetMessage())
71136
assert.Equal(t, "message:\"Hello World\"", resp.String())
72137
}
73-
74-
func TestMain(m *testing.M) {
75-
os.Setenv("GOFR_TELEMETRY", "false")
76-
77-
go main()
78-
time.Sleep(300 * time.Millisecond) // wait for server to boot
79-
80-
m.Run()
81-
}
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
package server
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
healthpb "google.golang.org/grpc/health/grpc_health_v1"
10+
11+
"gofr.dev/pkg/gofr"
12+
"gofr.dev/pkg/gofr/container"
13+
"gofr.dev/pkg/gofr/testutil"
14+
)
15+
16+
// createTestContext creates a test gofr.Context
17+
func createTestContext() *gofr.Context {
18+
container := &container.Container{}
19+
return &gofr.Context{
20+
Context: context.Background(),
21+
Container: container,
22+
}
23+
}
24+
25+
func TestGoFrHealthServer_Creation(t *testing.T) {
26+
t.Run("GetOrCreateHealthServer", func(t *testing.T) {
27+
// Test GoFr's getOrCreateHealthServer function
28+
healthServer := getOrCreateHealthServer()
29+
assert.NotNil(t, healthServer, "GoFr health server should not be nil")
30+
31+
// Test that it implements the GoFr interface (not the standard gRPC interface)
32+
// The GoFr health server has different method signatures
33+
assert.NotNil(t, healthServer, "Health server should not be nil")
34+
})
35+
36+
t.Run("HealthServerSingleton", func(t *testing.T) {
37+
// Test GoFr's singleton pattern for health server
38+
healthServer1 := getOrCreateHealthServer()
39+
healthServer2 := getOrCreateHealthServer()
40+
41+
assert.Equal(t, healthServer1, healthServer2, "GoFr health server should be singleton")
42+
})
43+
}
44+
45+
func TestGoFrHealthServer_Methods(t *testing.T) {
46+
_ = testutil.NewServerConfigs(t)
47+
48+
// Test GoFr's health server methods
49+
healthServer := getOrCreateHealthServer()
50+
ctx := createTestContext()
51+
52+
t.Run("CheckMethodExists", func(t *testing.T) {
53+
// Test that GoFr's Check method exists and accepts correct parameters
54+
req := &healthpb.HealthCheckRequest{
55+
Service: "test-service",
56+
}
57+
58+
// Test GoFr's Check method signature - this will fail with "unknown service" which is expected
59+
resp, err := healthServer.Check(ctx, req)
60+
assert.Error(t, err, "Health check should fail for unknown service")
61+
assert.Nil(t, resp, "Health check response should be nil for unknown service")
62+
assert.Contains(t, err.Error(), "unknown service", "Error should indicate unknown service")
63+
})
64+
65+
t.Run("WatchMethodExists", func(t *testing.T) {
66+
// Test that GoFr's Watch method exists and accepts correct parameters
67+
req := &healthpb.HealthCheckRequest{
68+
Service: "test-service",
69+
}
70+
71+
// Test GoFr's Watch method signature - this will panic with nil stream, but we're testing method existence
72+
assert.Panics(t, func() {
73+
healthServer.Watch(ctx, req, nil)
74+
}, "Watch should panic with nil stream, but method should exist")
75+
})
76+
}
77+
78+
func TestGoFrHealthServer_SetServingStatus(t *testing.T) {
79+
_ = testutil.NewServerConfigs(t)
80+
81+
// Test GoFr's SetServingStatus functionality
82+
healthServer := getOrCreateHealthServer()
83+
ctx := createTestContext()
84+
85+
t.Run("SetServingStatus", func(t *testing.T) {
86+
// Test GoFr's SetServingStatus method
87+
healthServer.SetServingStatus(ctx, "test-service", healthpb.HealthCheckResponse_SERVING)
88+
89+
// Verify the status was set
90+
req := &healthpb.HealthCheckRequest{
91+
Service: "test-service",
92+
}
93+
resp, err := healthServer.Check(ctx, req)
94+
require.NoError(t, err, "Health check should not fail")
95+
assert.Equal(t, healthpb.HealthCheckResponse_SERVING, resp.Status, "Service should be serving")
96+
})
97+
98+
t.Run("SetNotServingStatus", func(t *testing.T) {
99+
// Test GoFr's SetServingStatus with NOT_SERVING
100+
healthServer.SetServingStatus(ctx, "test-service-not-serving", healthpb.HealthCheckResponse_NOT_SERVING)
101+
102+
// Verify the status was set
103+
req := &healthpb.HealthCheckRequest{
104+
Service: "test-service-not-serving",
105+
}
106+
resp, err := healthServer.Check(ctx, req)
107+
require.NoError(t, err, "Health check should not fail")
108+
assert.Equal(t, healthpb.HealthCheckResponse_NOT_SERVING, resp.Status, "Service should not be serving")
109+
})
110+
}
111+
112+
func TestGoFrHealthServer_Shutdown(t *testing.T) {
113+
_ = testutil.NewServerConfigs(t)
114+
115+
// Test GoFr's Shutdown functionality
116+
healthServer := getOrCreateHealthServer()
117+
ctx := createTestContext()
118+
119+
t.Run("Shutdown", func(t *testing.T) {
120+
// Test GoFr's Shutdown method
121+
healthServer.Shutdown(ctx)
122+
123+
// After shutdown, all services should return NOT_SERVING
124+
req := &healthpb.HealthCheckRequest{
125+
Service: "any-service",
126+
}
127+
resp, err := healthServer.Check(ctx, req)
128+
// After shutdown, health checks should fail with "unknown service"
129+
assert.Error(t, err, "Health check should fail after shutdown")
130+
assert.Nil(t, resp, "Health check response should be nil after shutdown")
131+
assert.Contains(t, err.Error(), "unknown service", "Error should indicate unknown service after shutdown")
132+
})
133+
}
134+
135+
func TestGoFrHealthServer_Resume(t *testing.T) {
136+
_ = testutil.NewServerConfigs(t)
137+
138+
// Test GoFr's Resume functionality
139+
healthServer := getOrCreateHealthServer()
140+
ctx := createTestContext()
141+
142+
t.Run("Resume", func(t *testing.T) {
143+
// Test GoFr's Resume method
144+
healthServer.Resume(ctx)
145+
146+
// After resume, services should return to their previous status
147+
healthServer.SetServingStatus(ctx, "test-service-resume", healthpb.HealthCheckResponse_SERVING)
148+
149+
req := &healthpb.HealthCheckRequest{
150+
Service: "test-service-resume",
151+
}
152+
resp, err := healthServer.Check(ctx, req)
153+
require.NoError(t, err, "Health check should not fail")
154+
assert.Equal(t, healthpb.HealthCheckResponse_SERVING, resp.Status, "Service should be serving after resume")
155+
})
156+
}
157+
158+
func TestGoFrHealthServer_MultipleInstances(t *testing.T) {
159+
_ = testutil.NewServerConfigs(t)
160+
161+
// Test GoFr's singleton pattern
162+
t.Run("SingletonPattern", func(t *testing.T) {
163+
healthServer1 := getOrCreateHealthServer()
164+
healthServer2 := getOrCreateHealthServer()
165+
ctx := createTestContext()
166+
167+
assert.Equal(t, healthServer1, healthServer2, "GoFr health server should be singleton")
168+
169+
// Test that operations on one affect the other
170+
healthServer1.SetServingStatus(ctx, "singleton-test", healthpb.HealthCheckResponse_SERVING)
171+
172+
req := &healthpb.HealthCheckRequest{
173+
Service: "singleton-test",
174+
}
175+
resp, err := healthServer2.Check(ctx, req)
176+
require.NoError(t, err, "Health check should not fail")
177+
assert.Equal(t, healthpb.HealthCheckResponse_SERVING, resp.Status, "Singleton should share state")
178+
})
179+
}

0 commit comments

Comments
 (0)