@quenty/influxdbclient 7.37.0 → 7.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/package.json +2 -2
- package/src/Server/Config/InfluxDBClientConfigUtils.spec.lua +86 -0
- package/src/Server/Config/InfluxDBWriteOptionUtils.spec.lua +94 -0
- package/src/Server/InfluxDBClient.lua +45 -3
- package/src/Server/InfluxDBClient.spec.lua +227 -0
- package/src/Server/InfluxDBService.lua +147 -0
- package/src/Server/InfluxDBService.spec.lua +151 -0
- package/src/Server/Mocks/InfluxDBRequestHandlerMock.lua +132 -0
- package/src/Server/Mocks/InfluxDBRequestHandlerMock.spec.lua +107 -0
- package/src/Server/Utils/InfluxDBErrorUtils.spec.lua +69 -0
- package/src/Server/Write/InfluxDBWriteAPI.lua +21 -2
- package/src/Server/Write/InfluxDBWriteAPI.spec.lua +322 -0
- package/src/Server/Write/InfluxDBWriteBuffer.spec.lua +144 -0
- package/src/Shared/Config/InfluxDBPointSettings.spec.lua +87 -0
- package/src/Shared/Utils/InfluxDBEscapeUtils.spec.lua +70 -0
- package/src/Shared/Write/InfluxDBPoint.spec.lua +373 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
@class InfluxDBWriteAPI.spec.lua
|
|
4
|
+
]]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local InfluxDBPoint = require("InfluxDBPoint")
|
|
9
|
+
local InfluxDBRequestHandlerMock = require("InfluxDBRequestHandlerMock")
|
|
10
|
+
local InfluxDBWriteAPI = require("InfluxDBWriteAPI")
|
|
11
|
+
local Jest = require("Jest")
|
|
12
|
+
local Promise = require("Promise")
|
|
13
|
+
local PromiseTestUtils = require("PromiseTestUtils")
|
|
14
|
+
|
|
15
|
+
local describe = Jest.Globals.describe
|
|
16
|
+
local expect = Jest.Globals.expect
|
|
17
|
+
local it = Jest.Globals.it
|
|
18
|
+
|
|
19
|
+
-- A fixed timestamp keeps the emitted line protocol (and therefore the request body) deterministic.
|
|
20
|
+
local FIXED_TIMESTAMP = DateTime.fromUnixTimestampMillis(1600000000000)
|
|
21
|
+
|
|
22
|
+
local function newPoint(): InfluxDBPoint.InfluxDBPoint
|
|
23
|
+
local point = InfluxDBPoint.new("temperature")
|
|
24
|
+
point:SetTimestamp(FIXED_TIMESTAMP)
|
|
25
|
+
point:AddFloatField("value", 23.5)
|
|
26
|
+
return point
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
-- Line protocol produced by newPoint() with default (empty) point settings.
|
|
30
|
+
local POINT_LINE = "temperature value=23.5 1600000000000"
|
|
31
|
+
|
|
32
|
+
-- Builds a write API wired to a request mock (so nothing hits the network) with a client config set.
|
|
33
|
+
local function newConfiguredAPI(configOverrides: { [string]: any }?)
|
|
34
|
+
local mock = InfluxDBRequestHandlerMock.new()
|
|
35
|
+
local api = InfluxDBWriteAPI.new("my-org", "my-bucket", nil, mock.Handler)
|
|
36
|
+
|
|
37
|
+
local config = {
|
|
38
|
+
url = "https://example.com",
|
|
39
|
+
token = "my-token",
|
|
40
|
+
}
|
|
41
|
+
for key, value in configOverrides or ({} :: { [string]: any }) do
|
|
42
|
+
config[key] = value
|
|
43
|
+
end
|
|
44
|
+
api:SetClientConfig(config)
|
|
45
|
+
|
|
46
|
+
return api, mock
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe("InfluxDBWriteAPI.new", function()
|
|
50
|
+
it("should default the precision to ms in the write url", function()
|
|
51
|
+
local api, mock = newConfiguredAPI()
|
|
52
|
+
api:QueuePoint(newPoint())
|
|
53
|
+
expect(PromiseTestUtils.awaitSettled(api:PromiseFlush())).toEqual(true)
|
|
54
|
+
|
|
55
|
+
expect((mock:GetLastRequest() :: any).Url).toEqual(
|
|
56
|
+
"https://example.com/api/v2/write?org=my-org&bucket=my-bucket&precision=ms"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
api:Destroy()
|
|
60
|
+
end)
|
|
61
|
+
|
|
62
|
+
it("should use a provided precision in the write url", function()
|
|
63
|
+
local mock = InfluxDBRequestHandlerMock.new()
|
|
64
|
+
local api = InfluxDBWriteAPI.new("my-org", "my-bucket", "ns", mock.Handler)
|
|
65
|
+
api:SetClientConfig({ url = "https://example.com", token = "my-token" })
|
|
66
|
+
api:QueuePoint(newPoint())
|
|
67
|
+
expect(PromiseTestUtils.awaitSettled(api:PromiseFlush())).toEqual(true)
|
|
68
|
+
|
|
69
|
+
expect((mock:GetLastRequest() :: any).Url).toEqual(
|
|
70
|
+
"https://example.com/api/v2/write?org=my-org&bucket=my-bucket&precision=ns"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
api:Destroy()
|
|
74
|
+
end)
|
|
75
|
+
|
|
76
|
+
it("should throw on a non-string org", function()
|
|
77
|
+
expect(function()
|
|
78
|
+
InfluxDBWriteAPI.new(5 :: any, "my-bucket")
|
|
79
|
+
end).toThrow("Bad org")
|
|
80
|
+
end)
|
|
81
|
+
|
|
82
|
+
it("should throw on a non-string bucket", function()
|
|
83
|
+
expect(function()
|
|
84
|
+
InfluxDBWriteAPI.new("my-org", 5 :: any)
|
|
85
|
+
end).toThrow("Bad bucket")
|
|
86
|
+
end)
|
|
87
|
+
|
|
88
|
+
it("should throw on a non-string, non-nil precision", function()
|
|
89
|
+
expect(function()
|
|
90
|
+
InfluxDBWriteAPI.new("my-org", "my-bucket", 5 :: any)
|
|
91
|
+
end).toThrow("Bad precision")
|
|
92
|
+
end)
|
|
93
|
+
|
|
94
|
+
it("should throw on a non-function, non-nil request handler", function()
|
|
95
|
+
expect(function()
|
|
96
|
+
InfluxDBWriteAPI.new("my-org", "my-bucket", nil, 5 :: any)
|
|
97
|
+
end).toThrow("Bad requestHandler")
|
|
98
|
+
end)
|
|
99
|
+
end)
|
|
100
|
+
|
|
101
|
+
describe("InfluxDBWriteAPI.SetClientConfig", function()
|
|
102
|
+
it("should throw on an invalid config", function()
|
|
103
|
+
local api = InfluxDBWriteAPI.new("my-org", "my-bucket")
|
|
104
|
+
|
|
105
|
+
expect(function()
|
|
106
|
+
api:SetClientConfig({ url = "https://example.com" } :: any)
|
|
107
|
+
end).toThrow("Bad clientConfig")
|
|
108
|
+
|
|
109
|
+
api:Destroy()
|
|
110
|
+
end)
|
|
111
|
+
end)
|
|
112
|
+
|
|
113
|
+
describe("InfluxDBWriteAPI.SetPrintDebugWriteEnabled", function()
|
|
114
|
+
it("should throw on a non-boolean value", function()
|
|
115
|
+
local api = InfluxDBWriteAPI.new("my-org", "my-bucket")
|
|
116
|
+
|
|
117
|
+
expect(function()
|
|
118
|
+
api:SetPrintDebugWriteEnabled("yes" :: any)
|
|
119
|
+
end).toThrow("Bad printDebugEnabled")
|
|
120
|
+
|
|
121
|
+
api:Destroy()
|
|
122
|
+
end)
|
|
123
|
+
end)
|
|
124
|
+
|
|
125
|
+
describe("InfluxDBWriteAPI.QueuePoint", function()
|
|
126
|
+
it("should throw on a non-point", function()
|
|
127
|
+
local api = InfluxDBWriteAPI.new("my-org", "my-bucket")
|
|
128
|
+
|
|
129
|
+
expect(function()
|
|
130
|
+
api:QueuePoint({} :: any)
|
|
131
|
+
end).toThrow("Bad point")
|
|
132
|
+
|
|
133
|
+
api:Destroy()
|
|
134
|
+
end)
|
|
135
|
+
|
|
136
|
+
it("should not send a request until flushed", function()
|
|
137
|
+
local api, mock = newConfiguredAPI()
|
|
138
|
+
api:QueuePoint(newPoint())
|
|
139
|
+
|
|
140
|
+
expect(#mock:GetRequests()).toEqual(0)
|
|
141
|
+
|
|
142
|
+
api:Destroy()
|
|
143
|
+
end)
|
|
144
|
+
end)
|
|
145
|
+
|
|
146
|
+
describe("InfluxDBWriteAPI.QueuePoints", function()
|
|
147
|
+
it("should throw on a non-table", function()
|
|
148
|
+
local api = InfluxDBWriteAPI.new("my-org", "my-bucket")
|
|
149
|
+
|
|
150
|
+
expect(function()
|
|
151
|
+
api:QueuePoints("nope" :: any)
|
|
152
|
+
end).toThrow("Bad points")
|
|
153
|
+
|
|
154
|
+
api:Destroy()
|
|
155
|
+
end)
|
|
156
|
+
|
|
157
|
+
it("should throw on a non-point entry in the list", function()
|
|
158
|
+
local api = InfluxDBWriteAPI.new("my-org", "my-bucket")
|
|
159
|
+
|
|
160
|
+
expect(function()
|
|
161
|
+
api:QueuePoints({ {} :: any })
|
|
162
|
+
end).toThrow("Bad point")
|
|
163
|
+
|
|
164
|
+
api:Destroy()
|
|
165
|
+
end)
|
|
166
|
+
|
|
167
|
+
it("should batch every queued point into a single newline-joined body", function()
|
|
168
|
+
local api, mock = newConfiguredAPI()
|
|
169
|
+
api:QueuePoints({ newPoint(), newPoint() })
|
|
170
|
+
expect(PromiseTestUtils.awaitSettled(api:PromiseFlush())).toEqual(true)
|
|
171
|
+
|
|
172
|
+
expect(#mock:GetRequests()).toEqual(1)
|
|
173
|
+
expect((mock:GetLastRequest() :: any).Body).toEqual(POINT_LINE .. "\n" .. POINT_LINE)
|
|
174
|
+
|
|
175
|
+
api:Destroy()
|
|
176
|
+
end)
|
|
177
|
+
end)
|
|
178
|
+
|
|
179
|
+
describe("InfluxDBWriteAPI.PromiseFlush", function()
|
|
180
|
+
it("should reject with no client configuration when none is set", function()
|
|
181
|
+
local mock = InfluxDBRequestHandlerMock.new()
|
|
182
|
+
local api = InfluxDBWriteAPI.new("my-org", "my-bucket", nil, mock.Handler)
|
|
183
|
+
api:QueuePoint(newPoint())
|
|
184
|
+
|
|
185
|
+
local outcome, payload = PromiseTestUtils.awaitOutcome(api:PromiseFlush())
|
|
186
|
+
expect(outcome).toEqual("rejected")
|
|
187
|
+
expect(payload).toEqual("No client configuration")
|
|
188
|
+
-- The batch is abandoned before a request is ever built.
|
|
189
|
+
expect(#mock:GetRequests()).toEqual(0)
|
|
190
|
+
|
|
191
|
+
api:Destroy()
|
|
192
|
+
end)
|
|
193
|
+
|
|
194
|
+
it("should send the buffered line protocol as the request body", function()
|
|
195
|
+
local api, mock = newConfiguredAPI()
|
|
196
|
+
api:QueuePoint(newPoint())
|
|
197
|
+
expect(PromiseTestUtils.awaitSettled(api:PromiseFlush())).toEqual(true)
|
|
198
|
+
|
|
199
|
+
expect((mock:GetLastRequest() :: any).Method).toEqual("POST")
|
|
200
|
+
expect((mock:GetLastRequest() :: any).Body).toEqual(POINT_LINE)
|
|
201
|
+
|
|
202
|
+
api:Destroy()
|
|
203
|
+
end)
|
|
204
|
+
|
|
205
|
+
it("should send a Token-prefixed authorization header for a string token", function()
|
|
206
|
+
local api, mock = newConfiguredAPI()
|
|
207
|
+
api:QueuePoint(newPoint())
|
|
208
|
+
expect(PromiseTestUtils.awaitSettled(api:PromiseFlush())).toEqual(true)
|
|
209
|
+
|
|
210
|
+
expect((mock:GetLastRequest() :: any).Headers.Authorization).toEqual("Token my-token")
|
|
211
|
+
|
|
212
|
+
api:Destroy()
|
|
213
|
+
end)
|
|
214
|
+
|
|
215
|
+
it("should strip a trailing slash from the configured url", function()
|
|
216
|
+
local api, mock = newConfiguredAPI({ url = "https://example.com/" })
|
|
217
|
+
api:QueuePoint(newPoint())
|
|
218
|
+
expect(PromiseTestUtils.awaitSettled(api:PromiseFlush())).toEqual(true)
|
|
219
|
+
|
|
220
|
+
expect((mock:GetLastRequest() :: any).Url).toEqual(
|
|
221
|
+
"https://example.com/api/v2/write?org=my-org&bucket=my-bucket&precision=ms"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
api:Destroy()
|
|
225
|
+
end)
|
|
226
|
+
|
|
227
|
+
it("should resolve and fire RequestFinished on a successful response", function()
|
|
228
|
+
local api, mock = newConfiguredAPI()
|
|
229
|
+
|
|
230
|
+
local response = {
|
|
231
|
+
Success = true,
|
|
232
|
+
StatusCode = 204,
|
|
233
|
+
StatusMessage = "No Content",
|
|
234
|
+
Headers = {},
|
|
235
|
+
Body = "",
|
|
236
|
+
}
|
|
237
|
+
mock:SetResponder(function()
|
|
238
|
+
return Promise.resolved(response)
|
|
239
|
+
end)
|
|
240
|
+
|
|
241
|
+
local finished = {}
|
|
242
|
+
api.RequestFinished:Connect(function(result)
|
|
243
|
+
table.insert(finished, result)
|
|
244
|
+
end)
|
|
245
|
+
|
|
246
|
+
api:QueuePoint(newPoint())
|
|
247
|
+
local outcome = PromiseTestUtils.awaitOutcome(api:PromiseFlush())
|
|
248
|
+
|
|
249
|
+
expect(outcome).toEqual("resolved")
|
|
250
|
+
expect(finished[1]).toBe(response)
|
|
251
|
+
|
|
252
|
+
api:Destroy()
|
|
253
|
+
end)
|
|
254
|
+
|
|
255
|
+
it("should reject with the parsed InfluxDB error when the body is a known error", function()
|
|
256
|
+
local api, mock = newConfiguredAPI()
|
|
257
|
+
mock:SetResponder(function()
|
|
258
|
+
return Promise.rejected({
|
|
259
|
+
Success = false,
|
|
260
|
+
StatusCode = 401,
|
|
261
|
+
StatusMessage = "Unauthorized",
|
|
262
|
+
Headers = {},
|
|
263
|
+
Body = '{"code":"unauthorized","message":"token required"}',
|
|
264
|
+
})
|
|
265
|
+
end)
|
|
266
|
+
|
|
267
|
+
api:QueuePoint(newPoint())
|
|
268
|
+
|
|
269
|
+
local outcome, payload = PromiseTestUtils.awaitOutcome(api:PromiseFlush())
|
|
270
|
+
expect(outcome).toEqual("rejected")
|
|
271
|
+
expect(payload.code).toEqual("unauthorized")
|
|
272
|
+
expect(payload.message).toEqual("token required")
|
|
273
|
+
|
|
274
|
+
api:Destroy()
|
|
275
|
+
end)
|
|
276
|
+
|
|
277
|
+
it("should reject with a formatted string when the error body is not a known error", function()
|
|
278
|
+
local api, mock = newConfiguredAPI()
|
|
279
|
+
mock:SetResponder(function()
|
|
280
|
+
return Promise.rejected({
|
|
281
|
+
Success = false,
|
|
282
|
+
StatusCode = 500,
|
|
283
|
+
StatusMessage = "Internal Server Error",
|
|
284
|
+
Headers = {},
|
|
285
|
+
Body = "not json",
|
|
286
|
+
})
|
|
287
|
+
end)
|
|
288
|
+
|
|
289
|
+
api:QueuePoint(newPoint())
|
|
290
|
+
|
|
291
|
+
local outcome, payload = PromiseTestUtils.awaitOutcome(api:PromiseFlush())
|
|
292
|
+
expect(outcome).toEqual("rejected")
|
|
293
|
+
expect(type(payload)).toEqual("string")
|
|
294
|
+
expect(string.find(payload, "500", 1, true) ~= nil).toEqual(true)
|
|
295
|
+
|
|
296
|
+
api:Destroy()
|
|
297
|
+
end)
|
|
298
|
+
|
|
299
|
+
it("should reject with the raw error when it is not an http response", function()
|
|
300
|
+
local api, mock = newConfiguredAPI()
|
|
301
|
+
mock:SetResponder(function()
|
|
302
|
+
return Promise.rejected("connection refused")
|
|
303
|
+
end)
|
|
304
|
+
|
|
305
|
+
api:QueuePoint(newPoint())
|
|
306
|
+
|
|
307
|
+
local outcome, payload = PromiseTestUtils.awaitOutcome(api:PromiseFlush())
|
|
308
|
+
expect(outcome).toEqual("rejected")
|
|
309
|
+
expect(payload).toEqual("connection refused")
|
|
310
|
+
|
|
311
|
+
api:Destroy()
|
|
312
|
+
end)
|
|
313
|
+
|
|
314
|
+
it("should resolve without a request when nothing is buffered", function()
|
|
315
|
+
local api, mock = newConfiguredAPI()
|
|
316
|
+
|
|
317
|
+
expect(PromiseTestUtils.awaitSettled(api:PromiseFlush())).toEqual(true)
|
|
318
|
+
expect(#mock:GetRequests()).toEqual(0)
|
|
319
|
+
|
|
320
|
+
api:Destroy()
|
|
321
|
+
end)
|
|
322
|
+
end)
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
@class InfluxDBWriteBuffer.spec.lua
|
|
4
|
+
]]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local InfluxDBWriteBuffer = require("InfluxDBWriteBuffer")
|
|
9
|
+
local InfluxDBWriteOptionUtils = require("InfluxDBWriteOptionUtils")
|
|
10
|
+
local Jest = require("Jest")
|
|
11
|
+
local Promise = require("Promise")
|
|
12
|
+
local PromiseTestUtils = require("PromiseTestUtils")
|
|
13
|
+
|
|
14
|
+
local describe = Jest.Globals.describe
|
|
15
|
+
local expect = Jest.Globals.expect
|
|
16
|
+
local it = Jest.Globals.it
|
|
17
|
+
|
|
18
|
+
-- Records each flush so tests can assert what was handed to the flush handler and when.
|
|
19
|
+
local function newRecordingBuffer(options: InfluxDBWriteOptionUtils.InfluxDBWriteOptions)
|
|
20
|
+
local flushes: { { string } } = {}
|
|
21
|
+
local buffer = InfluxDBWriteBuffer.new(options, function(entries)
|
|
22
|
+
table.insert(flushes, entries)
|
|
23
|
+
return Promise.resolved()
|
|
24
|
+
end)
|
|
25
|
+
|
|
26
|
+
return buffer, flushes
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
-- A large flush interval keeps the time-based flush from firing during a test; Destroy cancels it.
|
|
30
|
+
local function options(overrides: { [string]: number })
|
|
31
|
+
return InfluxDBWriteOptionUtils.createWriteOptions({
|
|
32
|
+
batchSize = overrides.batchSize or 1000,
|
|
33
|
+
maxBatchBytes = overrides.maxBatchBytes or 50_000_000,
|
|
34
|
+
flushIntervalSeconds = overrides.flushIntervalSeconds or 9999,
|
|
35
|
+
})
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe("InfluxDBWriteBuffer.Add", function()
|
|
39
|
+
it("should not flush before the batch size is reached", function()
|
|
40
|
+
local buffer, flushes = newRecordingBuffer(options({ batchSize = 3 }))
|
|
41
|
+
|
|
42
|
+
buffer:Add("a")
|
|
43
|
+
buffer:Add("b")
|
|
44
|
+
|
|
45
|
+
expect(#flushes).toEqual(0)
|
|
46
|
+
|
|
47
|
+
buffer:Destroy()
|
|
48
|
+
end)
|
|
49
|
+
|
|
50
|
+
it("should flush once the batch size is reached", function()
|
|
51
|
+
local buffer, flushes = newRecordingBuffer(options({ batchSize = 2 }))
|
|
52
|
+
|
|
53
|
+
buffer:Add("a")
|
|
54
|
+
buffer:Add("b")
|
|
55
|
+
|
|
56
|
+
expect(#flushes).toEqual(1)
|
|
57
|
+
expect(flushes[1]).toEqual({ "a", "b" })
|
|
58
|
+
|
|
59
|
+
buffer:Destroy()
|
|
60
|
+
end)
|
|
61
|
+
|
|
62
|
+
it("should preserve entry order across a flush", function()
|
|
63
|
+
local buffer, flushes = newRecordingBuffer(options({ batchSize = 3 }))
|
|
64
|
+
|
|
65
|
+
buffer:Add("first")
|
|
66
|
+
buffer:Add("second")
|
|
67
|
+
buffer:Add("third")
|
|
68
|
+
|
|
69
|
+
expect(flushes[1]).toEqual({ "first", "second", "third" })
|
|
70
|
+
|
|
71
|
+
buffer:Destroy()
|
|
72
|
+
end)
|
|
73
|
+
|
|
74
|
+
it("should reset after a flush so a later flush is empty", function()
|
|
75
|
+
local buffer, flushes = newRecordingBuffer(options({ batchSize = 2 }))
|
|
76
|
+
|
|
77
|
+
buffer:Add("a")
|
|
78
|
+
buffer:Add("b")
|
|
79
|
+
expect(#flushes).toEqual(1)
|
|
80
|
+
|
|
81
|
+
-- Nothing buffered now, so an explicit flush must not re-send the batch.
|
|
82
|
+
local promise = buffer:PromiseFlush()
|
|
83
|
+
expect(PromiseTestUtils.awaitSettled(promise)).toEqual(true)
|
|
84
|
+
expect(#flushes).toEqual(1)
|
|
85
|
+
|
|
86
|
+
buffer:Destroy()
|
|
87
|
+
end)
|
|
88
|
+
|
|
89
|
+
it("should flush when the byte ceiling is exceeded", function()
|
|
90
|
+
local buffer, flushes = newRecordingBuffer(options({ maxBatchBytes = 10 }))
|
|
91
|
+
|
|
92
|
+
-- 12 bytes + 1 newline separator clears the 10-byte ceiling on its own.
|
|
93
|
+
buffer:Add("abcdefghijkl")
|
|
94
|
+
|
|
95
|
+
expect(#flushes).toEqual(1)
|
|
96
|
+
expect(flushes[1]).toEqual({ "abcdefghijkl" })
|
|
97
|
+
|
|
98
|
+
buffer:Destroy()
|
|
99
|
+
end)
|
|
100
|
+
|
|
101
|
+
it("should throw on a non-string entry", function()
|
|
102
|
+
local buffer = newRecordingBuffer(options({}))
|
|
103
|
+
|
|
104
|
+
expect(function()
|
|
105
|
+
buffer:Add(5 :: any)
|
|
106
|
+
end).toThrow("Bad entry")
|
|
107
|
+
|
|
108
|
+
buffer:Destroy()
|
|
109
|
+
end)
|
|
110
|
+
end)
|
|
111
|
+
|
|
112
|
+
describe("InfluxDBWriteBuffer.PromiseFlush", function()
|
|
113
|
+
it("should flush pending entries below the batch size", function()
|
|
114
|
+
local buffer, flushes = newRecordingBuffer(options({ batchSize = 100 }))
|
|
115
|
+
|
|
116
|
+
buffer:Add("a")
|
|
117
|
+
buffer:Add("b")
|
|
118
|
+
expect(#flushes).toEqual(0)
|
|
119
|
+
|
|
120
|
+
local promise = buffer:PromiseFlush()
|
|
121
|
+
expect(PromiseTestUtils.awaitSettled(promise)).toEqual(true)
|
|
122
|
+
expect(flushes[1]).toEqual({ "a", "b" })
|
|
123
|
+
|
|
124
|
+
buffer:Destroy()
|
|
125
|
+
end)
|
|
126
|
+
|
|
127
|
+
it("should resolve without calling the handler when empty", function()
|
|
128
|
+
local buffer, flushes = newRecordingBuffer(options({}))
|
|
129
|
+
|
|
130
|
+
local promise = buffer:PromiseFlush()
|
|
131
|
+
expect(PromiseTestUtils.awaitSettled(promise)).toEqual(true)
|
|
132
|
+
expect(#flushes).toEqual(0)
|
|
133
|
+
|
|
134
|
+
buffer:Destroy()
|
|
135
|
+
end)
|
|
136
|
+
end)
|
|
137
|
+
|
|
138
|
+
describe("InfluxDBWriteBuffer.new", function()
|
|
139
|
+
it("should throw without a flush handler", function()
|
|
140
|
+
expect(function()
|
|
141
|
+
InfluxDBWriteBuffer.new(options({}), nil :: any)
|
|
142
|
+
end).toThrow("No promiseHandleFlush")
|
|
143
|
+
end)
|
|
144
|
+
end)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
@class InfluxDBPointSettings.spec.lua
|
|
4
|
+
]]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local InfluxDBPointSettings = require("InfluxDBPointSettings")
|
|
9
|
+
local Jest = require("Jest")
|
|
10
|
+
|
|
11
|
+
local describe = Jest.Globals.describe
|
|
12
|
+
local expect = Jest.Globals.expect
|
|
13
|
+
local it = Jest.Globals.it
|
|
14
|
+
|
|
15
|
+
describe("InfluxDBPointSettings.new", function()
|
|
16
|
+
it("should start with no default tags", function()
|
|
17
|
+
local settings = InfluxDBPointSettings.new()
|
|
18
|
+
|
|
19
|
+
expect(next(settings:GetDefaultTags())).toEqual(nil)
|
|
20
|
+
end)
|
|
21
|
+
|
|
22
|
+
it("should start with no convert time", function()
|
|
23
|
+
local settings = InfluxDBPointSettings.new()
|
|
24
|
+
|
|
25
|
+
expect(settings:GetConvertTime()).toEqual(nil)
|
|
26
|
+
end)
|
|
27
|
+
end)
|
|
28
|
+
|
|
29
|
+
describe("InfluxDBPointSettings.SetDefaultTags", function()
|
|
30
|
+
it("should store and return the tags", function()
|
|
31
|
+
local settings = InfluxDBPointSettings.new()
|
|
32
|
+
settings:SetDefaultTags({
|
|
33
|
+
host = "server1",
|
|
34
|
+
region = "us-east",
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
local tags = settings:GetDefaultTags()
|
|
38
|
+
expect(tags.host).toEqual("server1")
|
|
39
|
+
expect(tags.region).toEqual("us-east")
|
|
40
|
+
end)
|
|
41
|
+
|
|
42
|
+
it("should throw on a non-table", function()
|
|
43
|
+
local settings = InfluxDBPointSettings.new()
|
|
44
|
+
|
|
45
|
+
expect(function()
|
|
46
|
+
settings:SetDefaultTags(5 :: any)
|
|
47
|
+
end).toThrow("Bad tags")
|
|
48
|
+
end)
|
|
49
|
+
|
|
50
|
+
it("should throw when a tag value is not a string", function()
|
|
51
|
+
local settings = InfluxDBPointSettings.new()
|
|
52
|
+
|
|
53
|
+
expect(function()
|
|
54
|
+
settings:SetDefaultTags({ host = 5 :: any })
|
|
55
|
+
end).toThrow("Bad value")
|
|
56
|
+
end)
|
|
57
|
+
end)
|
|
58
|
+
|
|
59
|
+
describe("InfluxDBPointSettings.SetConvertTime", function()
|
|
60
|
+
it("should store and return the convert time function", function()
|
|
61
|
+
local settings = InfluxDBPointSettings.new()
|
|
62
|
+
local function convertTime(_value)
|
|
63
|
+
return 42
|
|
64
|
+
end
|
|
65
|
+
settings:SetConvertTime(convertTime)
|
|
66
|
+
|
|
67
|
+
expect(settings:GetConvertTime()).toBe(convertTime)
|
|
68
|
+
end)
|
|
69
|
+
|
|
70
|
+
it("should allow clearing the convert time with nil", function()
|
|
71
|
+
local settings = InfluxDBPointSettings.new()
|
|
72
|
+
settings:SetConvertTime(function(_value)
|
|
73
|
+
return 42
|
|
74
|
+
end)
|
|
75
|
+
settings:SetConvertTime(nil)
|
|
76
|
+
|
|
77
|
+
expect(settings:GetConvertTime()).toEqual(nil)
|
|
78
|
+
end)
|
|
79
|
+
|
|
80
|
+
it("should throw on a non-function", function()
|
|
81
|
+
local settings = InfluxDBPointSettings.new()
|
|
82
|
+
|
|
83
|
+
expect(function()
|
|
84
|
+
settings:SetConvertTime(5 :: any)
|
|
85
|
+
end).toThrow("Bad convertTime")
|
|
86
|
+
end)
|
|
87
|
+
end)
|
|
@@ -34,6 +34,30 @@ describe("InfluxDBEscapeUtils.quoted", function()
|
|
|
34
34
|
end)
|
|
35
35
|
end)
|
|
36
36
|
|
|
37
|
+
describe("InfluxDBEscapeUtils.measurement", function()
|
|
38
|
+
it("should escape commas", function()
|
|
39
|
+
expect(InfluxDBEscapeUtils.measurement("a,b")).toBe("a\\,b")
|
|
40
|
+
end)
|
|
41
|
+
|
|
42
|
+
it("should escape spaces", function()
|
|
43
|
+
expect(InfluxDBEscapeUtils.measurement("a b")).toBe("a\\ b")
|
|
44
|
+
end)
|
|
45
|
+
|
|
46
|
+
it("should not escape equals signs", function()
|
|
47
|
+
expect(InfluxDBEscapeUtils.measurement("a=b")).toBe("a=b")
|
|
48
|
+
end)
|
|
49
|
+
end)
|
|
50
|
+
|
|
51
|
+
describe("InfluxDBEscapeUtils.quoted", function()
|
|
52
|
+
it("should escape backslashes", function()
|
|
53
|
+
expect(InfluxDBEscapeUtils.quoted("a\\b")).toBe('"a\\\\b"')
|
|
54
|
+
end)
|
|
55
|
+
|
|
56
|
+
it("should wrap an empty string in quotes", function()
|
|
57
|
+
expect(InfluxDBEscapeUtils.quoted("")).toBe('""')
|
|
58
|
+
end)
|
|
59
|
+
end)
|
|
60
|
+
|
|
37
61
|
describe("InfluxDBEscapeUtils.tag", function()
|
|
38
62
|
it("should pass through fine", function()
|
|
39
63
|
local tag = InfluxDBEscapeUtils.tag("hi")
|
|
@@ -59,4 +83,50 @@ describe("InfluxDBEscapeUtils.tag", function()
|
|
|
59
83
|
local tag = InfluxDBEscapeUtils.tag("\nhi")
|
|
60
84
|
expect(tag).toBe("\\nhi")
|
|
61
85
|
end)
|
|
86
|
+
|
|
87
|
+
it("should escape commas and spaces", function()
|
|
88
|
+
expect(InfluxDBEscapeUtils.tag("a, b")).toBe("a\\,\\ b")
|
|
89
|
+
end)
|
|
90
|
+
end)
|
|
91
|
+
|
|
92
|
+
describe("InfluxDBEscapeUtils.createEscaper", function()
|
|
93
|
+
it("should replace only the characters in the sub table", function()
|
|
94
|
+
local escaper = InfluxDBEscapeUtils.createEscaper({
|
|
95
|
+
["#"] = "\\#",
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
expect(escaper("a#b#c")).toBe("a\\#b\\#c")
|
|
99
|
+
expect(escaper("abc")).toBe("abc")
|
|
100
|
+
end)
|
|
101
|
+
|
|
102
|
+
it("should escape Lua pattern magic characters used as keys", function()
|
|
103
|
+
local escaper = InfluxDBEscapeUtils.createEscaper({
|
|
104
|
+
["."] = "\\.",
|
|
105
|
+
["%"] = "\\%",
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
expect(escaper("a.b%c")).toBe("a\\.b\\%c")
|
|
109
|
+
end)
|
|
110
|
+
|
|
111
|
+
it("should throw on a non-table sub table", function()
|
|
112
|
+
expect(function()
|
|
113
|
+
InfluxDBEscapeUtils.createEscaper(5 :: any)
|
|
114
|
+
end).toThrow("Bad subTable")
|
|
115
|
+
end)
|
|
116
|
+
|
|
117
|
+
it("should throw on a multi-character key", function()
|
|
118
|
+
expect(function()
|
|
119
|
+
InfluxDBEscapeUtils.createEscaper({ ab = "x" })
|
|
120
|
+
end).toThrow("Bad char")
|
|
121
|
+
end)
|
|
122
|
+
end)
|
|
123
|
+
|
|
124
|
+
describe("InfluxDBEscapeUtils.createQuotedEscaper", function()
|
|
125
|
+
it("should wrap the escaped result in quotes", function()
|
|
126
|
+
local escaper = InfluxDBEscapeUtils.createQuotedEscaper({
|
|
127
|
+
["#"] = "\\#",
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
expect(escaper("a#b")).toBe('"a\\#b"')
|
|
131
|
+
end)
|
|
62
132
|
end)
|