master
go 256 lines 6.53 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package snmp
4
5 import (
6 "context"
7 "testing"
8 "time"
9
10 "github.com/stretchr/testify/assert"
11 "github.com/stretchr/testify/require"
12 )
13
14 func newTestFuncLicenses(cache *licenseCache) *funcLicenses {
15 return newFuncLicenses(cache)
16 }
17
18 func TestLicensesMethodConfig_Parameterless(t *testing.T) {
19 tests := map[string]struct {
20 method string
21 }{
22 "licenses method has no required params": {
23 method: licensesMethodID,
24 },
25 }
26
27 for name, tc := range tests {
28 t.Run(name, func(t *testing.T) {
29 cfg := licensesMethodConfig()
30 assert.Empty(t, cfg.RequiredParams)
31
32 params, err := newTestFuncLicenses(newLicenseCache()).MethodParams(context.Background(), tc.method)
33 require.NoError(t, err)
34 assert.Empty(t, params)
35 })
36 }
37 }
38
39 func TestFuncLicensesHandle(t *testing.T) {
40 cache := newLicenseCache()
41 now := time.Date(2026, time.April, 3, 10, 0, 0, 0, time.UTC)
42 cache.store(now, []licenseRow{
43 {
44 ID: "broken-license",
45 Source: "vendor",
46 Name: "Shared License",
47 StateRaw: "expired",
48 StateBucket: licenseStateBucketBroken,
49 ExpiryTS: now.Add(-time.Hour).Unix(),
50 HasExpiry: true,
51 UsagePercent: 100,
52 HasUsagePct: true,
53 Impact: "Feature disabled",
54 },
55 {
56 ID: "eval-license",
57 Source: "vendor",
58 Name: "Evaluation License",
59 StateRaw: "evaluation",
60 StateBucket: licenseStateBucketInformational,
61 },
62 {
63 ID: "healthy-license",
64 Source: "vendor",
65 Name: "Shared License",
66 StateRaw: "active",
67 StateBucket: licenseStateBucketHealthy,
68 ExpiryTS: now.Add(24 * time.Hour).Unix(),
69 HasExpiry: true,
70 UsagePercent: 45,
71 HasUsagePct: true,
72 },
73 {
74 ID: "ignored-license",
75 Source: "vendor",
76 Name: "Unused License",
77 StateRaw: "not_subscribed",
78 StateBucket: licenseStateBucketIgnored,
79 IsPerpetual: true,
80 },
81 })
82
83 resp := newTestFuncLicenses(cache).Handle(context.Background(), licensesMethodID, nil)
84 require.NotNil(t, resp)
85 assert.Equal(t, 200, resp.Status)
86 require.NotNil(t, resp.Columns)
87 require.Len(t, resp.Data.([][]any), 4)
88 assert.Equal(t, "License", resp.DefaultSortColumn)
89
90 rows := resp.Data.([][]any)
91 assert.Equal(t, "Shared License", rows[0][0])
92 assert.Equal(t, string(licenseStateBucketBroken), rows[0][1])
93 assert.Equal(t, "Evaluation License", rows[1][0])
94 assert.Equal(t, string(licenseStateBucketInformational), rows[1][1])
95 assert.Equal(t, "Shared License", rows[2][0])
96 assert.Equal(t, "Unused License", rows[3][0])
97 assert.Equal(t, string(licenseStateBucketIgnored), rows[3][1])
98 assert.NotEqual(t, rows[0][4], rows[2][4], "hidden row ID should stay unique when display labels collide")
99
100 rowOptions, ok := resp.Columns["rowOptions"]
101 require.True(t, ok)
102 assert.NotNil(t, rowOptions)
103
104 columns := resp.Columns
105 idCol, ok := columns["ID"].(map[string]any)
106 require.True(t, ok)
107 assert.Equal(t, false, idCol["visible"])
108 assert.Equal(t, true, idCol["unique_key"])
109 }
110
111 func TestFuncLicensesHandle_DefaultSortUsesDisplayedLicenseValue(t *testing.T) {
112 tests := map[string]struct {
113 rows []licenseRow
114 wantOrder []string
115 }{
116 "uses display label before hidden row id": {
117 rows: []licenseRow{
118 {
119 ID: "Zulu",
120 Source: "vendor",
121 StateBucket: licenseStateBucketHealthy,
122 },
123 {
124 ID: "alpha-id",
125 Source: "vendor",
126 Name: "Alpha",
127 StateBucket: licenseStateBucketHealthy,
128 },
129 },
130 wantOrder: []string{"Alpha", "Zulu"},
131 },
132 }
133
134 for name, tc := range tests {
135 t.Run(name, func(t *testing.T) {
136 cache := newLicenseCache()
137 cache.store(time.Date(2026, time.April, 3, 10, 0, 0, 0, time.UTC), tc.rows)
138
139 resp := newTestFuncLicenses(cache).Handle(context.Background(), licensesMethodID, nil)
140 require.NotNil(t, resp)
141 require.Equal(t, 200, resp.Status)
142
143 rows := resp.Data.([][]any)
144 require.Len(t, rows, len(tc.wantOrder))
145 for i, want := range tc.wantOrder {
146 assert.Equal(t, want, rows[i][0])
147 }
148 })
149 }
150 }
151
152 func TestLicenseRowUniqueKey_DistinctRows(t *testing.T) {
153 tests := map[string]struct {
154 left licenseRow
155 right licenseRow
156 }{
157 "escapes delimiter content": {
158 left: licenseRow{
159 Source: "source|a",
160 Table: "table",
161 ID: "id",
162 },
163 right: licenseRow{
164 Source: "source",
165 Table: "table|id",
166 ID: "",
167 },
168 },
169 "different tables stay distinct": {
170 left: licenseRow{
171 Source: "fortinet",
172 Table: "fgLicContractTable",
173 ID: "FortiCare",
174 Name: "FortiCare",
175 },
176 right: licenseRow{
177 Source: "fortinet",
178 Table: "fgLicVersionTable",
179 ID: "FortiCare",
180 Name: "FortiCare",
181 },
182 },
183 }
184
185 for name, tc := range tests {
186 t.Run(name, func(t *testing.T) {
187 assert.NotEqual(t, licenseRowUniqueKey(tc.left), licenseRowUniqueKey(tc.right))
188 })
189 }
190 }
191
192 func TestFuncLicensesHandleUnavailable(t *testing.T) {
193 tests := map[string]struct {
194 prepare func(cache *licenseCache)
195 }{
196 "before first collect": {},
197 "when no rows were collected": {
198 prepare: func(cache *licenseCache) {
199 cache.store(time.Date(2026, time.April, 3, 10, 0, 0, 0, time.UTC), nil)
200 },
201 },
202 }
203
204 for name, tc := range tests {
205 t.Run(name, func(t *testing.T) {
206 cache := newLicenseCache()
207 if tc.prepare != nil {
208 tc.prepare(cache)
209 }
210
211 resp := newTestFuncLicenses(cache).Handle(context.Background(), licensesMethodID, nil)
212 require.NotNil(t, resp)
213 assert.Equal(t, 503, resp.Status)
214 })
215 }
216 }
217
218 func TestFuncLicensesRemainingUsesCurrentTime(t *testing.T) {
219 tests := map[string]struct {
220 lastUpdate time.Time
221 expiry time.Time
222 }{
223 "uses current time instead of cache update time": {
224 lastUpdate: time.Now().UTC().Add(-time.Hour),
225 expiry: time.Now().UTC().Add(time.Hour),
226 },
227 }
228
229 for name, tc := range tests {
230 t.Run(name, func(t *testing.T) {
231 cache := newLicenseCache()
232 cache.store(tc.lastUpdate, []licenseRow{
233 {
234 ID: "license-1",
235 Name: "Time Sensitive",
236 StateBucket: licenseStateBucketHealthy,
237 ExpiryTS: tc.expiry.Unix(),
238 HasExpiry: true,
239 },
240 })
241
242 resp := newTestFuncLicenses(cache).Handle(context.Background(), licensesMethodID, nil)
243 require.NotNil(t, resp)
244 require.Equal(t, 200, resp.Status)
245
246 rows := resp.Data.([][]any)
247 require.Len(t, rows, 1)
248
249 remaining, ok := rows[0][8].(int64)
250 require.True(t, ok)
251 assert.GreaterOrEqual(t, remaining, int64((59*time.Minute)/time.Millisecond))
252 assert.LessOrEqual(t, remaining, int64((61*time.Minute)/time.Millisecond))
253 assert.Less(t, remaining, int64((2*time.Hour)/time.Millisecond))
254 })
255 }
256 }