master
go 350 lines 8.42 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 //go:build linux
4
5 package logind
6
7 import (
8 "context"
9 "errors"
10 "os"
11 "testing"
12
13 "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/collecttest"
14
15 "github.com/coreos/go-systemd/v22/login1"
16 "github.com/godbus/dbus/v5"
17 "github.com/stretchr/testify/assert"
18 "github.com/stretchr/testify/require"
19 )
20
21 var (
22 dataConfigJSON, _ = os.ReadFile("testdata/config.json")
23 dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
24 )
25
26 func Test_testDataIsValid(t *testing.T) {
27 for name, data := range map[string][]byte{
28 "dataConfigJSON": dataConfigJSON,
29 "dataConfigYAML": dataConfigYAML,
30 } {
31 require.NotNil(t, data, name)
32 }
33 }
34
35 func TestCollector_ConfigurationSerialize(t *testing.T) {
36 collecttest.TestConfigurationSerialize(t, &Collector{}, dataConfigJSON, dataConfigYAML)
37 }
38
39 func TestCollector_Init(t *testing.T) {
40 tests := map[string]struct {
41 config Config
42 wantFail bool
43 }{
44 "default config": {
45 wantFail: false,
46 config: New().Config,
47 },
48 }
49
50 for name, test := range tests {
51 t.Run(name, func(t *testing.T) {
52 collr := New()
53 collr.Config = test.config
54
55 if test.wantFail {
56 assert.Error(t, collr.Init(context.Background()))
57 } else {
58 assert.NoError(t, collr.Init(context.Background()))
59 }
60 })
61 }
62 }
63
64 func TestCollector_Charts(t *testing.T) {
65 assert.Equal(t, len(charts), len(*New().Charts()))
66 }
67
68 func TestCollector_Cleanup(t *testing.T) {
69 tests := map[string]struct {
70 wantClose bool
71 prepare func(l *Collector)
72 }{
73 "after New": {
74 wantClose: false,
75 prepare: func(l *Collector) {},
76 },
77 "after Init": {
78 wantClose: false,
79 prepare: func(l *Collector) { _ = l.Init(context.Background()) },
80 },
81 "after Check": {
82 wantClose: true,
83 prepare: func(l *Collector) { _ = l.Init(context.Background()); _ = l.Check(context.Background()) },
84 },
85 "after Collect": {
86 wantClose: true,
87 prepare: func(l *Collector) { _ = l.Init(context.Background()); l.Collect(context.Background()) },
88 },
89 }
90
91 for name, test := range tests {
92 t.Run(name, func(t *testing.T) {
93 collr := New()
94 m := prepareConnOK()
95 collr.newLogindConn = func(Config) (logindConnection, error) { return m, nil }
96 test.prepare(collr)
97
98 require.NotPanics(t, func() { collr.Cleanup(context.Background()) })
99
100 if test.wantClose {
101 assert.True(t, m.closeCalled)
102 } else {
103 assert.False(t, m.closeCalled)
104 }
105 })
106 }
107 }
108
109 func TestCollector_Check(t *testing.T) {
110 tests := map[string]struct {
111 wantFail bool
112 prepare func() *mockConn
113 }{
114 "success when response contains sessions and users": {
115 wantFail: false,
116 prepare: prepareConnOK,
117 },
118 "success when response does not contain sessions and users": {
119 wantFail: false,
120 prepare: prepareConnOKNoSessionsNoUsers,
121 },
122 "fail when error on list sessions": {
123 wantFail: true,
124 prepare: prepareConnErrOnListSessions,
125 },
126 "fail when error on get session properties": {
127 wantFail: true,
128 prepare: prepareConnErrOnGetSessionProperties,
129 },
130 "fail when error on list users": {
131 wantFail: true,
132 prepare: prepareConnErrOnListUsers,
133 },
134 "fail when error on get user property": {
135 wantFail: true,
136 prepare: prepareConnErrOnGetUserProperty,
137 },
138 }
139
140 for name, test := range tests {
141 t.Run(name, func(t *testing.T) {
142 collr := New()
143 require.NoError(t, collr.Init(context.Background()))
144 collr.conn = test.prepare()
145
146 if test.wantFail {
147 assert.Error(t, collr.Check(context.Background()))
148 } else {
149 assert.NoError(t, collr.Check(context.Background()))
150 }
151 })
152 }
153 }
154
155 func TestCollector_Collect(t *testing.T) {
156 tests := map[string]struct {
157 prepare func() *mockConn
158 expected map[string]int64
159 }{
160 "success when response contains sessions and users": {
161 prepare: prepareConnOK,
162 expected: map[string]int64{
163 "sessions_local": 3,
164 "sessions_remote": 0,
165 "sessions_state_active": 0,
166 "sessions_state_closing": 0,
167 "sessions_state_online": 3,
168 "sessions_type_console": 3,
169 "sessions_type_graphical": 0,
170 "sessions_type_other": 0,
171 "users_state_active": 3,
172 "users_state_closing": 0,
173 "users_state_lingering": 0,
174 "users_state_offline": 0,
175 "users_state_online": 0,
176 },
177 },
178 "success when response does not contain sessions and users": {
179 prepare: prepareConnOKNoSessionsNoUsers,
180 expected: map[string]int64{
181 "sessions_local": 0,
182 "sessions_remote": 0,
183 "sessions_state_active": 0,
184 "sessions_state_closing": 0,
185 "sessions_state_online": 0,
186 "sessions_type_console": 0,
187 "sessions_type_graphical": 0,
188 "sessions_type_other": 0,
189 "users_state_active": 0,
190 "users_state_closing": 0,
191 "users_state_lingering": 0,
192 "users_state_offline": 0,
193 "users_state_online": 0,
194 },
195 },
196 "fail when error on list sessions": {
197 prepare: prepareConnErrOnListSessions,
198 expected: map[string]int64(nil),
199 },
200 "fail when error on get session properties": {
201 prepare: prepareConnErrOnGetSessionProperties,
202 expected: map[string]int64(nil),
203 },
204 "fail when error on list users": {
205 prepare: prepareConnErrOnListUsers,
206 expected: map[string]int64(nil),
207 },
208 "fail when error on get user property": {
209 prepare: prepareConnErrOnGetUserProperty,
210 expected: map[string]int64(nil),
211 },
212 }
213
214 for name, test := range tests {
215 t.Run(name, func(t *testing.T) {
216 collr := New()
217 require.NoError(t, collr.Init(context.Background()))
218 collr.conn = test.prepare()
219
220 mx := collr.Collect(context.Background())
221
222 assert.Equal(t, test.expected, mx)
223 })
224 }
225 }
226
227 func prepareConnOK() *mockConn {
228 return &mockConn{
229 sessions: []login1.Session{
230 {Path: "/org/freedesktop/login1/session/_3156", User: "user1", ID: "123"},
231 {Path: "/org/freedesktop/login1/session/_3157", User: "user2", ID: "124"},
232 {Path: "/org/freedesktop/login1/session/_3158", User: "user3", ID: "125"},
233 },
234 users: []login1.User{
235 {Path: "/org/freedesktop/login1/user/_1000", Name: "user1", UID: 123},
236 {Path: "/org/freedesktop/login1/user/_1001", Name: "user2", UID: 124},
237 {Path: "/org/freedesktop/login1/user/_1002", Name: "user3", UID: 125},
238 },
239 errOnListSessions: false,
240 errOnGetSessionProperties: false,
241 errOnListUsers: false,
242 errOnGetUserProperty: false,
243 closeCalled: false,
244 }
245 }
246
247 func prepareConnOKNoSessionsNoUsers() *mockConn {
248 conn := prepareConnOK()
249 conn.sessions = nil
250 conn.users = nil
251 return conn
252 }
253
254 func prepareConnErrOnListSessions() *mockConn {
255 conn := prepareConnOK()
256 conn.errOnListSessions = true
257 return conn
258 }
259
260 func prepareConnErrOnGetSessionProperties() *mockConn {
261 conn := prepareConnOK()
262 conn.errOnGetSessionProperties = true
263 return conn
264 }
265
266 func prepareConnErrOnListUsers() *mockConn {
267 conn := prepareConnOK()
268 conn.errOnListUsers = true
269 return conn
270 }
271
272 func prepareConnErrOnGetUserProperty() *mockConn {
273 conn := prepareConnOK()
274 conn.errOnGetUserProperty = true
275 return conn
276 }
277
278 type mockConn struct {
279 sessions []login1.Session
280 users []login1.User
281
282 errOnListSessions bool
283 errOnGetSessionProperties bool
284 errOnListUsers bool
285 errOnGetUserProperty bool
286 closeCalled bool
287 }
288
289 func (m *mockConn) Close() {
290 m.closeCalled = true
291 }
292
293 func (m *mockConn) ListSessions() ([]login1.Session, error) {
294 if m.errOnListSessions {
295 return nil, errors.New("mock.ListSessions() error")
296 }
297 return m.sessions, nil
298 }
299
300 func (m *mockConn) GetSessionProperties(path dbus.ObjectPath) (map[string]dbus.Variant, error) {
301 if m.errOnGetSessionProperties {
302 return nil, errors.New("mock.GetSessionProperties() error")
303 }
304
305 var found bool
306 for _, s := range m.sessions {
307 if s.Path == path {
308 found = true
309 break
310 }
311 }
312
313 if !found {
314 return nil, errors.New("mock.GetUserProperty(): session is not found")
315 }
316
317 return map[string]dbus.Variant{
318 "Remote": dbus.MakeVariant("true"),
319 "Type": dbus.MakeVariant("tty"),
320 "State": dbus.MakeVariant("online"),
321 }, nil
322 }
323
324 func (m *mockConn) ListUsers() ([]login1.User, error) {
325 if m.errOnListUsers {
326 return nil, errors.New("mock.ListUsers() error")
327 }
328 return m.users, nil
329 }
330
331 func (m *mockConn) GetUserProperty(path dbus.ObjectPath, _ string) (*dbus.Variant, error) {
332 if m.errOnGetUserProperty {
333 return nil, errors.New("mock.GetUserProperty() error")
334 }
335
336 var found bool
337 for _, u := range m.users {
338 if u.Path == path {
339 found = true
340 break
341 }
342 }
343
344 if !found {
345 return nil, errors.New("mock.GetUserProperty(): user is not found")
346 }
347
348 v := dbus.MakeVariant("active")
349 return &v, nil
350 }