-
Notifications
You must be signed in to change notification settings - Fork 0
/
grpcmock.go
executable file
·97 lines (86 loc) · 2.61 KB
/
grpcmock.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// Package grpcmock provides functionality for mocking gRPC servers dynamically.
//
// Currently, only unary RPCs are supported.
//
// All functions in this package are safe for concurrent use by multiple goroutines.
package grpcmock
import (
"context"
"strings"
"sync"
"google.golang.org/grpc"
)
// UnaryResponse represents values returned by a unary RPC.
type UnaryResponse struct {
Resp interface{}
Err error
}
// rh represents a mock response/handler.
type rh struct {
response *UnaryResponse
handler grpc.UnaryHandler
}
// GRPCMock provides a gRPC interceptor and a set of methods for mocking gRPC servers.
type GRPCMock struct {
// methods stores mock response/handlers, where the map key is the short method name.
methods struct {
sync.RWMutex
m map[string]*rh
}
}
// New returns a new [GRPCMock].
func New() *GRPCMock {
gm := &GRPCMock{}
gm.methods.m = make(map[string]*rh)
return gm
}
// SetResponse sets a mock response and removes any mock handler for method.
func (gm *GRPCMock) SetResponse(method string, response *UnaryResponse) {
gm.methods.Lock()
gm.methods.m[method] = &rh{response: response}
gm.methods.Unlock()
}
// SetHandler sets a mock handler and removes any mock response for method.
func (gm *GRPCMock) SetHandler(method string, handler grpc.UnaryHandler) {
gm.methods.Lock()
gm.methods.m[method] = &rh{handler: handler}
gm.methods.Unlock()
}
// Unset removes any mock response or handler for method.
func (gm *GRPCMock) Unset(method string) {
gm.methods.Lock()
delete(gm.methods.m, method)
gm.methods.Unlock()
}
// Clear removes any mock response or handler for all methods.
func (gm *GRPCMock) Clear() {
gm.methods.Lock()
for method := range gm.methods.m {
delete(gm.methods.m, method)
}
gm.methods.Unlock()
}
// UnaryServerInterceptor returns a gRPC unary server interceptor which handles methods
// using mock responses or handlers. If no mock response or handler is set for a method,
// the registered handler will be used.
func (gm *GRPCMock) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
// Extract the short method name
n := strings.LastIndex(info.FullMethod, "/")
method := info.FullMethod[n+1:]
// Use a mock response/handler if available
gm.methods.RLock()
rh := gm.methods.m[method]
gm.methods.RUnlock()
if rh != nil {
if rh.response != nil {
return rh.response.Resp, rh.response.Err
}
if rh.handler != nil {
return rh.handler(ctx, req)
}
}
// Use the registered handler
return handler(ctx, req)
}
}