-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
session.go
179 lines (156 loc) · 4.27 KB
/
session.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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package restest
import (
"log"
"testing"
"time"
res "github.com/jirenius/go-res"
"github.com/jirenius/go-res/logger"
)
// DefaultTimeoutDuration is the duration the session awaits any message before
// timing out.
const DefaultTimeoutDuration = 1 * time.Second
// Session represents a test session with a res server
type Session struct {
*MockConn
s *res.Service
cfg *SessionConfig
cl chan struct{}
logPrinted bool
}
// SessionConfig represents the configuration for a session.
type SessionConfig struct {
TestName string
KeepLogger bool
NoReset bool
ValidateReset bool
ResetResources []string
ResetAccess []string
FailSubscription bool
MockConnConfig
}
// NewSession creates a new Session and connects the service to a mock NATS
// connection.
//
// A service logger will by default be set to a new MemLogger. To set any other
// logger, add the option:
//
// WithLogger(logger)
//
// If the tests sends any query event, a real NATS instance is required, which
// is slower than using the default mock connection. To use a real NATS
// instance, add the option:
//
// WithGnatsd
func NewSession(t *testing.T, service *res.Service, opts ...func(*SessionConfig)) *Session {
cfg := &SessionConfig{
MockConnConfig: MockConnConfig{TimeoutDuration: DefaultTimeoutDuration},
}
for _, opt := range opts {
opt(cfg)
}
c := NewMockConn(t, &cfg.MockConnConfig)
s := &Session{
MockConn: c,
s: service,
cl: make(chan struct{}),
cfg: cfg,
}
if cfg.FailSubscription {
c.FailNextSubscription()
}
if !cfg.KeepLogger {
service.SetLogger(logger.NewMemLogger().SetTrace(true).SetFlags(log.Ltime))
}
go func() {
defer s.StopServer()
defer close(s.cl)
if err := s.s.Serve(c); err != nil {
panic("test: failed to start service: " + err.Error())
}
}()
if !s.cfg.NoReset {
msg := s.GetMsg()
if msg == nil {
// The channel is closed
t.Fatal("expected a system.reset, but got no message")
}
if s.cfg.ValidateReset {
msg.AssertSystemReset(cfg.ResetResources, cfg.ResetAccess)
} else {
msg.AssertSubject("system.reset")
}
}
return s
}
// Service returns the associated res.Service.
func (s *Session) Service() *res.Service {
return s.s
}
// Close closes the session.
func (s *Session) Close() error {
// Check for panics
e := recover()
defer func() {
// Re-panic
if e != nil {
panic(e)
}
}()
// Output memlog if test failed or we are panicking
if e != nil || s.t.Failed() {
s.printLog()
}
// Try to shutdown the service
ch := make(chan error)
go func() {
ch <- s.s.Shutdown()
}()
// Await the closing
var err error
select {
case err = <-ch:
case <-time.After(s.cfg.TimeoutDuration):
s.t.Fatalf("failed to shutdown service: timeout")
}
return err
}
// WithKeepLogger sets the KeepLogger option, to prevent Session to override the
// service logger with its own MemLogger.
func WithKeepLogger(cfg *SessionConfig) { cfg.KeepLogger = true }
// WithGnatsd sets the UseGnatsd option to use a real NATS instance.
//
// This option should be set if the test involves query events.
func WithGnatsd(cfg *SessionConfig) { cfg.UseGnatsd = true }
// WithTest sets the TestName option.
//
// The test name will be outputted when logging test errors.
func WithTest(name string) func(*SessionConfig) {
return func(cfg *SessionConfig) { cfg.TestName = name }
}
// WithoutReset sets the NoReset option to not expect an initial system.reset
// event on server start.
func WithoutReset(cfg *SessionConfig) { cfg.NoReset = true }
// WithFailSubscription sets FailSubscription to make first subscription to fail.
func WithFailSubscription(cfg *SessionConfig) { cfg.FailSubscription = true }
// WithReset sets the ValidateReset option to validate that the system.reset
// includes the specific access and resources strings.
func WithReset(resources []string, access []string) func(*SessionConfig) {
return func(cfg *SessionConfig) {
cfg.ResetResources = resources
cfg.ResetAccess = access
cfg.ValidateReset = true
}
}
func (s *Session) printLog() {
if s.logPrinted {
return
}
s.logPrinted = true
if s.cfg.TestName != "" {
s.t.Logf("Failed test %s", s.cfg.TestName)
}
// Print log if we have a MemLogger
if l, ok := s.s.Logger().(*logger.MemLogger); ok {
s.t.Logf("Trace log:\n%s", l)
}
}