master
go 310 lines 7.9 KB
Raw
1 //go:build cgo
2
3 package jmx
4
5 import (
6 "context"
7 "errors"
8 "testing"
9
10 "github.com/netdata/netdata/go/plugins/plugin/ibm.d/protocols/jmxbridge"
11 )
12
13 type nopLogger struct{}
14
15 func (nopLogger) Debugf(string, ...any) {}
16 func (nopLogger) Infof(string, ...any) {}
17 func (nopLogger) Warningf(string, ...any) {}
18 func (nopLogger) Errorf(string, ...any) {}
19
20 type fakeBridge struct {
21 started bool
22 responses map[string]*jmxbridge.Response
23 }
24
25 func (f *fakeBridge) Start(context.Context, jmxbridge.Command) error {
26 f.started = true
27 return nil
28 }
29
30 func (f *fakeBridge) Send(ctx context.Context, cmd jmxbridge.Command) (*jmxbridge.Response, error) {
31 if !f.started {
32 return nil, errors.New("bridge not started")
33 }
34 target, _ := cmd["target"].(string)
35 if target == "" {
36 return nil, errors.New("missing target")
37 }
38 resp := f.responses[target]
39 if resp == nil {
40 return &jmxbridge.Response{Status: "OK", Data: map[string]any{}}, nil
41 }
42 return resp, nil
43 }
44
45 func (f *fakeBridge) Shutdown() {
46 f.started = false
47 }
48
49 func TestClientFetchJVM(t *testing.T) {
50 bridge := &fakeBridge{
51 responses: map[string]*jmxbridge.Response{
52 "JVM": {
53 Status: "OK",
54 Data: map[string]any{
55 "heap": map[string]any{
56 "used": 512.0,
57 "committed": 1024.0,
58 "max": 2048.0,
59 },
60 "nonheap": map[string]any{
61 "used": 128.0,
62 "committed": 256.0,
63 },
64 "gc": map[string]any{
65 "count": 12.0,
66 "time": 345.0,
67 },
68 "threads": map[string]any{
69 "count": 44.0,
70 "daemon": 30.0,
71 "peak": 60.0,
72 "totalStarted": 100.0,
73 },
74 "classes": map[string]any{
75 "loaded": 5000.0,
76 "unloaded": 200.0,
77 },
78 "cpu": map[string]any{
79 "processCpuUsage": 0.42,
80 },
81 "uptime": 900.0,
82 },
83 },
84 },
85 }
86
87 client, err := NewClient(Config{JMXURL: "service:jmx:iiop://localhost:2809"}, nopLogger{}, WithBridge(bridge))
88 if err != nil {
89 t.Fatalf("unexpected error: %v", err)
90 }
91
92 if err := client.Start(context.Background()); err != nil {
93 t.Fatalf("start failed: %v", err)
94 }
95
96 stats, err := client.FetchJVM(context.Background())
97 if err != nil {
98 t.Fatalf("fetch JVM failed: %v", err)
99 }
100
101 if stats.Heap.Used != 512.0 || stats.Heap.Max != 2048.0 {
102 t.Fatalf("unexpected heap stats: %+v", stats.Heap)
103 }
104 if stats.Threads.Count != 44.0 || stats.Threads.Peak != 60.0 {
105 t.Fatalf("unexpected thread stats: %+v", stats.Threads)
106 }
107 if stats.Classes.Loaded != 5000.0 || stats.CPU.ProcessUsage != 0.42 {
108 t.Fatalf("unexpected stats: classes=%+v cpu=%+v", stats.Classes, stats.CPU)
109 }
110 if stats.Uptime != 900.0 {
111 t.Fatalf("unexpected uptime: %.2f", stats.Uptime)
112 }
113 }
114
115 func TestClientFetchThreadPools(t *testing.T) {
116 bridge := &fakeBridge{
117 responses: map[string]*jmxbridge.Response{
118 "THREADPOOLS": {
119 Status: "OK",
120 Data: map[string]any{
121 "threadPools": []any{
122 map[string]any{
123 "name": "Default",
124 "poolSize": 50.0,
125 "activeCount": 5.0,
126 "maximumPoolSize": 75.0,
127 },
128 map[string]any{
129 "name": "WebContainer",
130 "poolSize": 80.0,
131 "activeCount": 12.0,
132 "maximumPoolSize": 100.0,
133 },
134 },
135 },
136 },
137 },
138 }
139
140 client, err := NewClient(Config{JMXURL: "service:jmx:iiop://localhost:2809"}, nopLogger{}, WithBridge(bridge))
141 if err != nil {
142 t.Fatalf("unexpected error: %v", err)
143 }
144
145 if err := client.Start(context.Background()); err != nil {
146 t.Fatalf("start failed: %v", err)
147 }
148
149 pools, err := client.FetchThreadPools(context.Background(), 10)
150 if err != nil {
151 t.Fatalf("fetch thread pools failed: %v", err)
152 }
153
154 if len(pools) != 2 {
155 t.Fatalf("expected 2 pools, got %d", len(pools))
156 }
157 if pools[0].Name != "Default" || pools[0].ActiveCount != 5.0 {
158 t.Fatalf("unexpected pool[0]: %+v", pools[0])
159 }
160 if pools[1].PoolSize != 80.0 || pools[1].MaximumPoolSize != 100.0 {
161 t.Fatalf("unexpected pool[1]: %+v", pools[1])
162 }
163 }
164
165 func TestClientFetchJDBCPools(t *testing.T) {
166 bridge := &fakeBridge{
167 responses: map[string]*jmxbridge.Response{
168 "JDBC": {
169 Status: "OK",
170 Data: map[string]any{
171 "jdbcPools": []any{
172 map[string]any{
173 "name": "DefaultDS",
174 "poolSize": 40.0,
175 "numConnectionsUsed": 5.0,
176 "numConnectionsFree": 10.0,
177 "avgWaitTime": 1.5,
178 "avgInUseTime": 3.2,
179 "numConnectionsCreated": 120.0,
180 "numConnectionsDestroyed": 80.0,
181 "waitingThreadCount": 2.0,
182 },
183 },
184 },
185 },
186 },
187 }
188
189 client, err := NewClient(Config{JMXURL: "service:jmx:iiop://localhost:2809"}, nopLogger{}, WithBridge(bridge))
190 if err != nil {
191 t.Fatalf("unexpected error: %v", err)
192 }
193
194 if err := client.Start(context.Background()); err != nil {
195 t.Fatalf("start failed: %v", err)
196 }
197
198 pools, err := client.FetchJDBCPools(context.Background(), 5)
199 if err != nil {
200 t.Fatalf("fetch jdbc pools failed: %v", err)
201 }
202
203 if len(pools) != 1 {
204 t.Fatalf("expected 1 pool, got %d", len(pools))
205 }
206 p := pools[0]
207 if p.Name != "DefaultDS" || p.PoolSize != 40 || p.NumConnectionsUsed != 5 {
208 t.Fatalf("unexpected pool contents: %+v", p)
209 }
210 if p.AvgWaitTime != 1.5 || p.AvgInUseTime != 3.2 {
211 t.Fatalf("unexpected timing values: %+v", p)
212 }
213 }
214
215 func TestClientFetchJMSDestinations(t *testing.T) {
216 bridge := &fakeBridge{
217 responses: map[string]*jmxbridge.Response{
218 "JMS": {
219 Status: "OK",
220 Data: map[string]any{
221 "jmsDestinations": []any{
222 map[string]any{
223 "name": "Queue1",
224 "type": "queue",
225 "messagesCurrentCount": 12.0,
226 "messagesPendingCount": 3.0,
227 "messagesAddedCount": 40.0,
228 "consumerCount": 5.0,
229 },
230 },
231 },
232 },
233 },
234 }
235
236 client, err := NewClient(Config{JMXURL: "service:jmx:iiop://localhost:2809"}, nopLogger{}, WithBridge(bridge))
237 if err != nil {
238 t.Fatalf("unexpected error: %v", err)
239 }
240
241 if err := client.Start(context.Background()); err != nil {
242 t.Fatalf("start failed: %v", err)
243 }
244
245 dests, err := client.FetchJMSDestinations(context.Background(), 5)
246 if err != nil {
247 t.Fatalf("fetch jms destinations failed: %v", err)
248 }
249
250 if len(dests) != 1 {
251 t.Fatalf("expected 1 destination, got %d", len(dests))
252 }
253 d := dests[0]
254 if d.Name != "Queue1" || d.Type != "queue" || d.MessagesCurrentCount != 12 {
255 t.Fatalf("unexpected destination contents: %+v", d)
256 }
257 if d.MessagesPendingCount != 3 || d.MessagesAddedCount != 40 || d.ConsumerCount != 5 {
258 t.Fatalf("unexpected message/consumer values: %+v", d)
259 }
260 }
261
262 func TestClientFetchApplications(t *testing.T) {
263 bridge := &fakeBridge{
264 responses: map[string]*jmxbridge.Response{
265 "APPLICATIONS": {
266 Status: "OK",
267 Data: map[string]any{
268 "applications": []any{
269 map[string]any{
270 "name": "sample-app",
271 "module": "moduleA",
272 "requestCount": 120.0,
273 "averageResponseTime": 0.24,
274 "activeSessions": 5.0,
275 "liveSessions": 7.0,
276 "sessionCreates": 60.0,
277 "sessionInvalidates": 10.0,
278 "transactionsCommitted": 40.0,
279 "transactionsRolledBack": 2.0,
280 },
281 },
282 },
283 },
284 },
285 }
286
287 client, err := NewClient(Config{JMXURL: "service:jmx:iiop://localhost:2809"}, nopLogger{}, WithBridge(bridge))
288 if err != nil {
289 t.Fatalf("unexpected error: %v", err)
290 }
291 if err := client.Start(context.Background()); err != nil {
292 t.Fatalf("start failed: %v", err)
293 }
294
295 metrics, err := client.FetchApplications(context.Background(), 10, true, true)
296 if err != nil {
297 t.Fatalf("fetch applications failed: %v", err)
298 }
299
300 if len(metrics) != 1 {
301 t.Fatalf("expected 1 application, got %d", len(metrics))
302 }
303 app := metrics[0]
304 if app.Name != "sample-app" || app.Module != "moduleA" || app.Requests != 120 {
305 t.Fatalf("unexpected application metric: %+v", app)
306 }
307 if app.ActiveSessions != 5 || app.TransactionsCommitted != 40 {
308 t.Fatalf("unexpected session/transaction metrics: %+v", app)
309 }
310 }