@quenty/datastore 13.40.0 → 13.41.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 +4 -0
- package/package.json +9 -9
- package/src/Server/DataStore.HookErrors.spec.lua +92 -0
- package/src/Server/DataStore.LoadErrors.spec.lua +133 -0
- package/src/Server/DataStore.Reentrance.spec.lua +119 -0
- package/src/Server/DataStore.SessionLock.spec.lua +478 -0
- package/src/Server/DataStore.SessionMessaging.spec.lua +71 -0
- package/src/Server/DataStore.TwoServerLock.spec.lua +190 -0
- package/src/Server/DataStore.lua +89 -18
- package/src/Server/DataStore.spec.lua +237 -0
- package/src/Server/DataStoreLockHelper.spec.lua +239 -0
- package/src/Server/DataStoreMessageHelper.spec.lua +134 -0
- package/src/Server/DataStoreNonRetryableLoadError.lua +61 -0
- package/src/Server/DataStoreTestUtils.lua +206 -0
- package/src/Server/GameDataStoreService.lua +26 -0
- package/src/Server/GameDataStoreService.spec.lua +215 -0
- package/src/Server/Mocks/DataStoreMock.lua +405 -0
- package/src/Server/Mocks/DataStoreMock.spec.lua +384 -0
- package/src/Server/Modules/DataStoreSnapshotUtils.spec.lua +91 -0
- package/src/Server/Modules/DataStoreStage.lua +8 -2
- package/src/Server/Modules/DataStoreStage.spec.lua +539 -0
- package/src/Server/Modules/DataStoreWriter.spec.lua +399 -0
- package/src/Server/PlayerDataStoreManager.RemovalCallbacks.spec.lua +99 -0
- package/src/Server/PlayerDataStoreManager.lua +36 -4
- package/src/Server/PlayerDataStoreManager.spec.lua +202 -0
- package/src/Server/PlayerDataStoreService.lua +18 -0
- package/src/Server/PlayerDataStoreService.spec.lua +218 -0
- package/src/Server/PrivateServerDataStoreService.lua +23 -1
- package/src/Server/PrivateServerDataStoreService.spec.lua +242 -0
- package/src/Server/Utility/DataStorePromises.lua +18 -5
- package/src/Server/Utility/DataStorePromises.spec.lua +363 -0
- package/src/jest.config.lua +3 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
--!nonstrict
|
|
2
|
+
--[[
|
|
3
|
+
Integration coverage for PrivateServerDataStoreService wired through a real ServiceBag, with the
|
|
4
|
+
underlying datastore injected via the SetRobloxDataStore test seam. It is not session-locking, so a
|
|
5
|
+
failing load rejects promptly rather than hanging. The datastore key defaults to "main" (or the
|
|
6
|
+
private-server id) unless a custom key is set via SetCustomKey before the datastore is resolved.
|
|
7
|
+
|
|
8
|
+
@class PrivateServerDataStoreService.spec.lua
|
|
9
|
+
]]
|
|
10
|
+
local require = require(script.Parent.loader).load(script)
|
|
11
|
+
|
|
12
|
+
local DataStoreMock = require("DataStoreMock")
|
|
13
|
+
local Jest = require("Jest")
|
|
14
|
+
local Maid = require("Maid")
|
|
15
|
+
local PromiseTestUtils = require("PromiseTestUtils")
|
|
16
|
+
local ServiceBag = require("ServiceBag")
|
|
17
|
+
|
|
18
|
+
local describe = Jest.Globals.describe
|
|
19
|
+
local expect = Jest.Globals.expect
|
|
20
|
+
local it = Jest.Globals.it
|
|
21
|
+
|
|
22
|
+
local function setup(mock)
|
|
23
|
+
local maid = Maid.new()
|
|
24
|
+
mock = mock or DataStoreMock.new()
|
|
25
|
+
|
|
26
|
+
local serviceBag = maid:Add(ServiceBag.new())
|
|
27
|
+
local service = serviceBag:GetService(require("PrivateServerDataStoreService"))
|
|
28
|
+
serviceBag:Init()
|
|
29
|
+
service:SetRobloxDataStore(mock)
|
|
30
|
+
serviceBag:Start()
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
service = service,
|
|
34
|
+
mock = mock,
|
|
35
|
+
destroy = function()
|
|
36
|
+
maid:DoCleaning()
|
|
37
|
+
end,
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe("PrivateServerDataStoreService.PromiseDataStore", function()
|
|
42
|
+
it("should resolve a datastore that loads successfully against a healthy mock", function()
|
|
43
|
+
local controller = setup()
|
|
44
|
+
|
|
45
|
+
local promise = controller.service:PromiseDataStore()
|
|
46
|
+
if not PromiseTestUtils.awaitSettled(promise) then
|
|
47
|
+
expect("hung").toEqual("settled")
|
|
48
|
+
controller:destroy()
|
|
49
|
+
return
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
local ok, dataStore = promise:Yield()
|
|
53
|
+
expect(ok).toEqual(true)
|
|
54
|
+
expect(dataStore).never.toBeNil()
|
|
55
|
+
|
|
56
|
+
local loadPromise = dataStore:PromiseLoadSuccessful()
|
|
57
|
+
if not PromiseTestUtils.awaitSettled(loadPromise) then
|
|
58
|
+
expect("hung").toEqual("settled")
|
|
59
|
+
controller:destroy()
|
|
60
|
+
return
|
|
61
|
+
end
|
|
62
|
+
expect((loadPromise:Wait())).toEqual(true)
|
|
63
|
+
|
|
64
|
+
controller:destroy()
|
|
65
|
+
end)
|
|
66
|
+
|
|
67
|
+
it("should return the same cached promise on repeated calls", function()
|
|
68
|
+
local controller = setup()
|
|
69
|
+
|
|
70
|
+
local first = controller.service:PromiseDataStore()
|
|
71
|
+
local second = controller.service:PromiseDataStore()
|
|
72
|
+
expect((first == second)).toEqual(true)
|
|
73
|
+
|
|
74
|
+
controller:destroy()
|
|
75
|
+
end)
|
|
76
|
+
end)
|
|
77
|
+
|
|
78
|
+
describe("PrivateServerDataStoreService.SetCustomKey", function()
|
|
79
|
+
it("should key the datastore by the custom key when set before resolving", function()
|
|
80
|
+
local controller = setup()
|
|
81
|
+
|
|
82
|
+
controller.service:SetCustomKey("mykey")
|
|
83
|
+
|
|
84
|
+
local promise = controller.service:PromiseDataStore()
|
|
85
|
+
if not PromiseTestUtils.awaitSettled(promise) then
|
|
86
|
+
expect("hung").toEqual("settled")
|
|
87
|
+
controller:destroy()
|
|
88
|
+
return
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
local ok, dataStore = promise:Yield()
|
|
92
|
+
expect(ok).toEqual(true)
|
|
93
|
+
expect((dataStore:GetKey())).toEqual("mykey")
|
|
94
|
+
|
|
95
|
+
controller:destroy()
|
|
96
|
+
end)
|
|
97
|
+
end)
|
|
98
|
+
|
|
99
|
+
describe("PrivateServerDataStoreService persistence", function()
|
|
100
|
+
it("should round-trip a stored value into the mock under the datastore key", function()
|
|
101
|
+
local controller = setup()
|
|
102
|
+
|
|
103
|
+
controller.service:SetCustomKey("mykey")
|
|
104
|
+
|
|
105
|
+
local promise = controller.service:PromiseDataStore()
|
|
106
|
+
if not PromiseTestUtils.awaitSettled(promise) then
|
|
107
|
+
expect("hung").toEqual("settled")
|
|
108
|
+
controller:destroy()
|
|
109
|
+
return
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
local ok, dataStore = promise:Yield()
|
|
113
|
+
expect(ok).toEqual(true)
|
|
114
|
+
|
|
115
|
+
local key = dataStore:GetKey()
|
|
116
|
+
dataStore:Store("motd", "hello")
|
|
117
|
+
|
|
118
|
+
local savePromise = dataStore:Save()
|
|
119
|
+
if not PromiseTestUtils.awaitSettled(savePromise) then
|
|
120
|
+
expect("hung").toEqual("settled")
|
|
121
|
+
controller:destroy()
|
|
122
|
+
return
|
|
123
|
+
end
|
|
124
|
+
expect((savePromise:Yield())).toEqual(true)
|
|
125
|
+
|
|
126
|
+
-- Non-session-locking, so the raw persisted value is the plain data table.
|
|
127
|
+
local raw = controller.mock:GetRaw(key)
|
|
128
|
+
expect(raw).never.toBeNil()
|
|
129
|
+
expect(raw.motd).toEqual("hello")
|
|
130
|
+
|
|
131
|
+
controller:destroy()
|
|
132
|
+
end)
|
|
133
|
+
end)
|
|
134
|
+
|
|
135
|
+
describe("PrivateServerDataStoreService.SetRobloxDataStore", function()
|
|
136
|
+
it("should throw when injected twice (already resolved)", function()
|
|
137
|
+
local controller = setup()
|
|
138
|
+
|
|
139
|
+
expect(function()
|
|
140
|
+
controller.service:SetRobloxDataStore(DataStoreMock.new())
|
|
141
|
+
end).toThrow("Already resolved robloxDataStore")
|
|
142
|
+
|
|
143
|
+
controller:destroy()
|
|
144
|
+
end)
|
|
145
|
+
|
|
146
|
+
it("should throw on a non-datastore argument", function()
|
|
147
|
+
local controller = setup()
|
|
148
|
+
|
|
149
|
+
-- isDataStore is validated before the already-resolved check, so a bad arg throws regardless.
|
|
150
|
+
expect(function()
|
|
151
|
+
controller.service:SetRobloxDataStore({})
|
|
152
|
+
end).toThrow("Bad robloxDataStore")
|
|
153
|
+
|
|
154
|
+
expect(function()
|
|
155
|
+
controller.service:SetRobloxDataStore(nil)
|
|
156
|
+
end).toThrow("Bad robloxDataStore")
|
|
157
|
+
|
|
158
|
+
controller:destroy()
|
|
159
|
+
end)
|
|
160
|
+
end)
|
|
161
|
+
|
|
162
|
+
describe("PrivateServerDataStoreService failure handling", function()
|
|
163
|
+
it("should resolve load as false (not hang) when the datastore is down", function()
|
|
164
|
+
local mock = DataStoreMock.new()
|
|
165
|
+
mock:FailAllRequests()
|
|
166
|
+
|
|
167
|
+
local controller = setup(mock)
|
|
168
|
+
|
|
169
|
+
local promise = controller.service:PromiseDataStore()
|
|
170
|
+
if not PromiseTestUtils.awaitSettled(promise, 5) then
|
|
171
|
+
expect("hung").toEqual("settled")
|
|
172
|
+
controller:destroy()
|
|
173
|
+
return
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
local ok, dataStore = promise:Yield()
|
|
177
|
+
expect(ok).toEqual(true)
|
|
178
|
+
|
|
179
|
+
-- Non-session-locking: a failing load rejects promptly, so this settles false rather than hanging.
|
|
180
|
+
local loadPromise = dataStore:PromiseLoadSuccessful()
|
|
181
|
+
if not PromiseTestUtils.awaitSettled(loadPromise, 5) then
|
|
182
|
+
expect("hung").toEqual("settled")
|
|
183
|
+
controller:destroy()
|
|
184
|
+
return
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
local loadOk, loadedOk = loadPromise:Yield()
|
|
188
|
+
expect(loadOk).toEqual(true)
|
|
189
|
+
expect(loadedOk).toEqual(false)
|
|
190
|
+
|
|
191
|
+
controller:destroy()
|
|
192
|
+
end)
|
|
193
|
+
end)
|
|
194
|
+
|
|
195
|
+
describe("PrivateServerDataStoreService teardown", function()
|
|
196
|
+
it("destroys its inner datastore when the service is destroyed", function()
|
|
197
|
+
local controller = setup()
|
|
198
|
+
|
|
199
|
+
local promise = controller.service:PromiseDataStore()
|
|
200
|
+
if not PromiseTestUtils.awaitSettled(promise, 5) then
|
|
201
|
+
expect("hung").toEqual("settled")
|
|
202
|
+
controller:destroy()
|
|
203
|
+
return
|
|
204
|
+
end
|
|
205
|
+
local _ok, dataStore = promise:Yield()
|
|
206
|
+
|
|
207
|
+
controller:destroy()
|
|
208
|
+
|
|
209
|
+
expect(getmetatable(dataStore)).toBeNil()
|
|
210
|
+
end)
|
|
211
|
+
|
|
212
|
+
it("flushes staged data synchronously to the underlying store when destroyed", function()
|
|
213
|
+
local controller = setup()
|
|
214
|
+
|
|
215
|
+
-- The cloud test place has a PrivateServerId, so pin the key rather than assuming "main".
|
|
216
|
+
controller.service:SetCustomKey("mykey")
|
|
217
|
+
|
|
218
|
+
local promise = controller.service:PromiseDataStore()
|
|
219
|
+
if not PromiseTestUtils.awaitSettled(promise, 5) then
|
|
220
|
+
expect("hung").toEqual("settled")
|
|
221
|
+
controller:destroy()
|
|
222
|
+
return
|
|
223
|
+
end
|
|
224
|
+
local _ok, dataStore = promise:Yield()
|
|
225
|
+
|
|
226
|
+
if not PromiseTestUtils.awaitSettled(dataStore:PromiseLoadSuccessful(), 5) then
|
|
227
|
+
expect("load hung").toEqual("load settled")
|
|
228
|
+
controller:destroy()
|
|
229
|
+
return
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
local key = dataStore:GetKey()
|
|
233
|
+
dataStore:Store("region", "us")
|
|
234
|
+
|
|
235
|
+
-- Destroy must fire a synchronous Save before tearing the store down.
|
|
236
|
+
controller:destroy()
|
|
237
|
+
|
|
238
|
+
local raw = controller.mock:GetRaw(key)
|
|
239
|
+
expect(raw).never.toBeNil()
|
|
240
|
+
expect(raw.region).toEqual("us")
|
|
241
|
+
end)
|
|
242
|
+
end)
|
|
@@ -9,6 +9,7 @@ local require = require(script.Parent.loader).load(script)
|
|
|
9
9
|
|
|
10
10
|
local DataStoreService = game:GetService("DataStoreService")
|
|
11
11
|
|
|
12
|
+
local DataStoreMock = require("DataStoreMock")
|
|
12
13
|
local PagesUtils = require("PagesUtils")
|
|
13
14
|
local Promise = require("Promise")
|
|
14
15
|
local Table = require("Table")
|
|
@@ -17,6 +18,18 @@ local DataStorePromises = {}
|
|
|
17
18
|
|
|
18
19
|
export type RobloxDataStore = DataStore
|
|
19
20
|
|
|
21
|
+
--[=[
|
|
22
|
+
Returns whether the given value can be used as a datastore by these helpers. A real
|
|
23
|
+
datastore is an `Instance`, but the first-class [DataStoreMock] is also accepted so tests
|
|
24
|
+
can stand in for a real datastore.
|
|
25
|
+
|
|
26
|
+
@param robloxDataStore any
|
|
27
|
+
@return boolean
|
|
28
|
+
]=]
|
|
29
|
+
function DataStorePromises.isDataStore(robloxDataStore: any): boolean
|
|
30
|
+
return typeof(robloxDataStore) == "Instance" or DataStoreMock.isDataStoreMock(robloxDataStore)
|
|
31
|
+
end
|
|
32
|
+
|
|
20
33
|
--[=[
|
|
21
34
|
Promises a Roblox datastore object with the name and scope. Generally only fails
|
|
22
35
|
when you haven't published the place.
|
|
@@ -70,7 +83,7 @@ end
|
|
|
70
83
|
@return Promise<T>
|
|
71
84
|
]=]
|
|
72
85
|
function DataStorePromises.getAsync<T>(robloxDataStore: DataStore, key: string): Promise.Promise<T>
|
|
73
|
-
assert(
|
|
86
|
+
assert(DataStorePromises.isDataStore(robloxDataStore), "Bad robloxDataStore")
|
|
74
87
|
assert(type(key) == "string", "Bad key")
|
|
75
88
|
|
|
76
89
|
return Promise.spawn(function(resolve, reject)
|
|
@@ -99,7 +112,7 @@ function DataStorePromises.updateAsync<T>(
|
|
|
99
112
|
key: string,
|
|
100
113
|
updateFunc: (T, DataStoreKeyInfo) -> T?
|
|
101
114
|
): Promise.Promise<(T, DataStoreKeyInfo)>
|
|
102
|
-
assert(
|
|
115
|
+
assert(DataStorePromises.isDataStore(robloxDataStore), "Bad robloxDataStore")
|
|
103
116
|
assert(type(key) == "string", "Bad key")
|
|
104
117
|
assert(type(updateFunc) == "function", "Bad updateFunc")
|
|
105
118
|
|
|
@@ -132,7 +145,7 @@ function DataStorePromises.setAsync(
|
|
|
132
145
|
value: string,
|
|
133
146
|
userIds: { number }?
|
|
134
147
|
): Promise.Promise<boolean>
|
|
135
|
-
assert(
|
|
148
|
+
assert(DataStorePromises.isDataStore(robloxDataStore), "Bad robloxDataStore")
|
|
136
149
|
assert(type(key) == "string", "Bad key")
|
|
137
150
|
assert(type(userIds) == "table" or userIds == nil, "Bad userIds")
|
|
138
151
|
|
|
@@ -159,7 +172,7 @@ function DataStorePromises.promiseIncrementAsync(
|
|
|
159
172
|
key: string,
|
|
160
173
|
delta: number
|
|
161
174
|
): Promise.Promise<boolean>
|
|
162
|
-
assert(
|
|
175
|
+
assert(DataStorePromises.isDataStore(robloxDataStore), "Bad robloxDataStore")
|
|
163
176
|
assert(type(key) == "string", "Bad key")
|
|
164
177
|
assert(type(delta) == "number" or delta == nil, "Bad delta")
|
|
165
178
|
|
|
@@ -181,7 +194,7 @@ end
|
|
|
181
194
|
@return Promise<boolean>
|
|
182
195
|
]=]
|
|
183
196
|
function DataStorePromises.removeAsync(robloxDataStore: DataStore, key: string): Promise.Promise<boolean>
|
|
184
|
-
assert(
|
|
197
|
+
assert(DataStorePromises.isDataStore(robloxDataStore), "Bad robloxDataStore")
|
|
185
198
|
assert(type(key) == "string", "Bad key")
|
|
186
199
|
|
|
187
200
|
return Promise.spawn(function(resolve, reject)
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
--!nonstrict
|
|
2
|
+
--[[
|
|
3
|
+
Characterization coverage for DataStorePromises, using DataStoreMock as the datastore.
|
|
4
|
+
@class DataStorePromises.spec.lua
|
|
5
|
+
]]
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local DataStoreMock = require("DataStoreMock")
|
|
9
|
+
local DataStorePromises = require("DataStorePromises")
|
|
10
|
+
local Jest = require("Jest")
|
|
11
|
+
local PromiseTestUtils = require("PromiseTestUtils")
|
|
12
|
+
|
|
13
|
+
local describe = Jest.Globals.describe
|
|
14
|
+
local expect = Jest.Globals.expect
|
|
15
|
+
local it = Jest.Globals.it
|
|
16
|
+
|
|
17
|
+
describe("DataStorePromises.isDataStore", function()
|
|
18
|
+
it("should be true for a DataStoreMock", function()
|
|
19
|
+
expect(DataStorePromises.isDataStore(DataStoreMock.new())).toEqual(true)
|
|
20
|
+
end)
|
|
21
|
+
|
|
22
|
+
it("should be false for a plain table", function()
|
|
23
|
+
expect(DataStorePromises.isDataStore({})).toEqual(false)
|
|
24
|
+
end)
|
|
25
|
+
|
|
26
|
+
it("should be false for nil", function()
|
|
27
|
+
expect(DataStorePromises.isDataStore(nil)).toEqual(false)
|
|
28
|
+
end)
|
|
29
|
+
|
|
30
|
+
it("should be false for primitives", function()
|
|
31
|
+
expect(DataStorePromises.isDataStore(5)).toEqual(false)
|
|
32
|
+
expect(DataStorePromises.isDataStore("str")).toEqual(false)
|
|
33
|
+
expect(DataStorePromises.isDataStore(true)).toEqual(false)
|
|
34
|
+
end)
|
|
35
|
+
|
|
36
|
+
it("should be true for an Instance", function()
|
|
37
|
+
expect(DataStorePromises.isDataStore(Instance.new("Folder"))).toEqual(true)
|
|
38
|
+
end)
|
|
39
|
+
end)
|
|
40
|
+
|
|
41
|
+
describe("DataStorePromises.promiseDataStore", function()
|
|
42
|
+
it("should resolve an Instance in the cloud test place", function()
|
|
43
|
+
local promise = DataStorePromises.promiseDataStore("SpecStore", "SpecScope")
|
|
44
|
+
if not PromiseTestUtils.awaitSettled(promise) then
|
|
45
|
+
expect("promise hung").toEqual("promise settled")
|
|
46
|
+
return
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
local ok, dataStore = promise:Yield()
|
|
50
|
+
expect(ok).toEqual(true)
|
|
51
|
+
expect(typeof(dataStore)).toEqual("Instance")
|
|
52
|
+
end)
|
|
53
|
+
|
|
54
|
+
it("should throw synchronously for a non-string name", function()
|
|
55
|
+
expect(function()
|
|
56
|
+
DataStorePromises.promiseDataStore(5 :: any, "scope")
|
|
57
|
+
end).toThrow("Bad name")
|
|
58
|
+
end)
|
|
59
|
+
|
|
60
|
+
it("should throw synchronously for a non-string scope", function()
|
|
61
|
+
expect(function()
|
|
62
|
+
DataStorePromises.promiseDataStore("name", 5 :: any)
|
|
63
|
+
end).toThrow("Bad scope")
|
|
64
|
+
end)
|
|
65
|
+
end)
|
|
66
|
+
|
|
67
|
+
describe("DataStorePromises.getAsync", function()
|
|
68
|
+
it("should resolve with the stored value", function()
|
|
69
|
+
local mock = DataStoreMock.new()
|
|
70
|
+
mock:SetRaw("key", { coins = 5 })
|
|
71
|
+
|
|
72
|
+
local promise = DataStorePromises.getAsync(mock, "key")
|
|
73
|
+
if not PromiseTestUtils.awaitSettled(promise) then
|
|
74
|
+
expect("promise hung").toEqual("promise settled")
|
|
75
|
+
return
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
local ok, value = promise:Yield()
|
|
79
|
+
expect(ok).toEqual(true)
|
|
80
|
+
expect(value.coins).toEqual(5)
|
|
81
|
+
end)
|
|
82
|
+
|
|
83
|
+
it("should resolve with nil for an unset key", function()
|
|
84
|
+
local mock = DataStoreMock.new()
|
|
85
|
+
|
|
86
|
+
local promise = DataStorePromises.getAsync(mock, "missing")
|
|
87
|
+
if not PromiseTestUtils.awaitSettled(promise) then
|
|
88
|
+
expect("promise hung").toEqual("promise settled")
|
|
89
|
+
return
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
local ok, value = promise:Yield()
|
|
93
|
+
expect(ok).toEqual(true)
|
|
94
|
+
expect(value).toEqual(nil)
|
|
95
|
+
end)
|
|
96
|
+
|
|
97
|
+
it("should reject when the store fails all requests", function()
|
|
98
|
+
local mock = DataStoreMock.new()
|
|
99
|
+
mock:FailAllRequests()
|
|
100
|
+
|
|
101
|
+
local promise = DataStorePromises.getAsync(mock, "key")
|
|
102
|
+
if not PromiseTestUtils.awaitSettled(promise) then
|
|
103
|
+
expect("promise hung").toEqual("promise settled")
|
|
104
|
+
return
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
local ok = promise:Yield()
|
|
108
|
+
expect(ok).toEqual(false)
|
|
109
|
+
end)
|
|
110
|
+
|
|
111
|
+
it("should throw synchronously for a bad robloxDataStore", function()
|
|
112
|
+
expect(function()
|
|
113
|
+
DataStorePromises.getAsync({} :: any, "key")
|
|
114
|
+
end).toThrow("Bad robloxDataStore")
|
|
115
|
+
end)
|
|
116
|
+
|
|
117
|
+
it("should throw synchronously for a non-string key", function()
|
|
118
|
+
local mock = DataStoreMock.new()
|
|
119
|
+
expect(function()
|
|
120
|
+
DataStorePromises.getAsync(mock, 5 :: any)
|
|
121
|
+
end).toThrow("Bad key")
|
|
122
|
+
end)
|
|
123
|
+
end)
|
|
124
|
+
|
|
125
|
+
describe("DataStorePromises.updateAsync", function()
|
|
126
|
+
it("should resolve with the updated value", function()
|
|
127
|
+
local mock = DataStoreMock.new()
|
|
128
|
+
mock:SetRaw("key", 1)
|
|
129
|
+
|
|
130
|
+
local promise = DataStorePromises.updateAsync(mock, "key", function(current)
|
|
131
|
+
return (current or 0) + 1
|
|
132
|
+
end)
|
|
133
|
+
if not PromiseTestUtils.awaitSettled(promise) then
|
|
134
|
+
expect("promise hung").toEqual("promise settled")
|
|
135
|
+
return
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
local ok, value = promise:Yield()
|
|
139
|
+
expect(ok).toEqual(true)
|
|
140
|
+
expect(value).toEqual(2)
|
|
141
|
+
end)
|
|
142
|
+
|
|
143
|
+
it("should pass the current value to the transform and update the store", function()
|
|
144
|
+
local mock = DataStoreMock.new()
|
|
145
|
+
mock:SetRaw("key", 10)
|
|
146
|
+
|
|
147
|
+
local seen
|
|
148
|
+
local promise = DataStorePromises.updateAsync(mock, "key", function(current)
|
|
149
|
+
seen = current
|
|
150
|
+
return current + 5
|
|
151
|
+
end)
|
|
152
|
+
if not PromiseTestUtils.awaitSettled(promise) then
|
|
153
|
+
expect("promise hung").toEqual("promise settled")
|
|
154
|
+
return
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
local ok = promise:Yield()
|
|
158
|
+
expect(ok).toEqual(true)
|
|
159
|
+
expect(seen).toEqual(10)
|
|
160
|
+
expect(mock:GetRaw("key")).toEqual(15)
|
|
161
|
+
end)
|
|
162
|
+
|
|
163
|
+
it("should reject when the store fails all requests", function()
|
|
164
|
+
local mock = DataStoreMock.new()
|
|
165
|
+
mock:FailAllRequests()
|
|
166
|
+
|
|
167
|
+
local promise = DataStorePromises.updateAsync(mock, "key", function()
|
|
168
|
+
return 1
|
|
169
|
+
end)
|
|
170
|
+
if not PromiseTestUtils.awaitSettled(promise) then
|
|
171
|
+
expect("promise hung").toEqual("promise settled")
|
|
172
|
+
return
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
local ok = promise:Yield()
|
|
176
|
+
expect(ok).toEqual(false)
|
|
177
|
+
end)
|
|
178
|
+
|
|
179
|
+
it("should throw synchronously for a bad robloxDataStore", function()
|
|
180
|
+
expect(function()
|
|
181
|
+
DataStorePromises.updateAsync({} :: any, "key", function()
|
|
182
|
+
return 1
|
|
183
|
+
end)
|
|
184
|
+
end).toThrow("Bad robloxDataStore")
|
|
185
|
+
end)
|
|
186
|
+
|
|
187
|
+
it("should throw synchronously for a non-string key", function()
|
|
188
|
+
local mock = DataStoreMock.new()
|
|
189
|
+
expect(function()
|
|
190
|
+
DataStorePromises.updateAsync(mock, 5 :: any, function()
|
|
191
|
+
return 1
|
|
192
|
+
end)
|
|
193
|
+
end).toThrow("Bad key")
|
|
194
|
+
end)
|
|
195
|
+
|
|
196
|
+
it("should throw synchronously for a non-function updateFunc", function()
|
|
197
|
+
local mock = DataStoreMock.new()
|
|
198
|
+
expect(function()
|
|
199
|
+
DataStorePromises.updateAsync(mock, "key", 5 :: any)
|
|
200
|
+
end).toThrow("Bad updateFunc")
|
|
201
|
+
end)
|
|
202
|
+
end)
|
|
203
|
+
|
|
204
|
+
describe("DataStorePromises.setAsync", function()
|
|
205
|
+
it("should resolve true and write the value", function()
|
|
206
|
+
local mock = DataStoreMock.new()
|
|
207
|
+
|
|
208
|
+
local promise = DataStorePromises.setAsync(mock, "key", "value")
|
|
209
|
+
if not PromiseTestUtils.awaitSettled(promise) then
|
|
210
|
+
expect("promise hung").toEqual("promise settled")
|
|
211
|
+
return
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
local ok, value = promise:Yield()
|
|
215
|
+
expect(ok).toEqual(true)
|
|
216
|
+
expect(value).toEqual(true)
|
|
217
|
+
expect(mock:GetRaw("key")).toEqual("value")
|
|
218
|
+
end)
|
|
219
|
+
|
|
220
|
+
it("should accept userIds", function()
|
|
221
|
+
local mock = DataStoreMock.new()
|
|
222
|
+
|
|
223
|
+
local promise = DataStorePromises.setAsync(mock, "key", "value", { 111, 222 })
|
|
224
|
+
if not PromiseTestUtils.awaitSettled(promise) then
|
|
225
|
+
expect("promise hung").toEqual("promise settled")
|
|
226
|
+
return
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
local ok = promise:Yield()
|
|
230
|
+
expect(ok).toEqual(true)
|
|
231
|
+
end)
|
|
232
|
+
|
|
233
|
+
it("should reject when the store fails all requests", function()
|
|
234
|
+
local mock = DataStoreMock.new()
|
|
235
|
+
mock:FailAllRequests()
|
|
236
|
+
|
|
237
|
+
local promise = DataStorePromises.setAsync(mock, "key", "value")
|
|
238
|
+
if not PromiseTestUtils.awaitSettled(promise) then
|
|
239
|
+
expect("promise hung").toEqual("promise settled")
|
|
240
|
+
return
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
local ok = promise:Yield()
|
|
244
|
+
expect(ok).toEqual(false)
|
|
245
|
+
end)
|
|
246
|
+
|
|
247
|
+
it("should throw synchronously for a bad robloxDataStore", function()
|
|
248
|
+
expect(function()
|
|
249
|
+
DataStorePromises.setAsync({} :: any, "key", "value")
|
|
250
|
+
end).toThrow("Bad robloxDataStore")
|
|
251
|
+
end)
|
|
252
|
+
|
|
253
|
+
it("should throw synchronously for a non-string key", function()
|
|
254
|
+
local mock = DataStoreMock.new()
|
|
255
|
+
expect(function()
|
|
256
|
+
DataStorePromises.setAsync(mock, 5 :: any, "value")
|
|
257
|
+
end).toThrow("Bad key")
|
|
258
|
+
end)
|
|
259
|
+
|
|
260
|
+
it("should throw synchronously for bad userIds", function()
|
|
261
|
+
local mock = DataStoreMock.new()
|
|
262
|
+
expect(function()
|
|
263
|
+
DataStorePromises.setAsync(mock, "key", "value", "notatable" :: any)
|
|
264
|
+
end).toThrow("Bad userIds")
|
|
265
|
+
end)
|
|
266
|
+
end)
|
|
267
|
+
|
|
268
|
+
describe("DataStorePromises.promiseIncrementAsync", function()
|
|
269
|
+
it("should resolve true and increment the value", function()
|
|
270
|
+
local mock = DataStoreMock.new()
|
|
271
|
+
mock:SetRaw("key", 5)
|
|
272
|
+
|
|
273
|
+
local promise = DataStorePromises.promiseIncrementAsync(mock, "key", 3)
|
|
274
|
+
if not PromiseTestUtils.awaitSettled(promise) then
|
|
275
|
+
expect("promise hung").toEqual("promise settled")
|
|
276
|
+
return
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
local ok, value = promise:Yield()
|
|
280
|
+
expect(ok).toEqual(true)
|
|
281
|
+
expect(value).toEqual(true)
|
|
282
|
+
expect(mock:GetRaw("key")).toEqual(8)
|
|
283
|
+
end)
|
|
284
|
+
|
|
285
|
+
it("should reject when the store fails all requests", function()
|
|
286
|
+
local mock = DataStoreMock.new()
|
|
287
|
+
mock:FailAllRequests()
|
|
288
|
+
|
|
289
|
+
local promise = DataStorePromises.promiseIncrementAsync(mock, "key", 1)
|
|
290
|
+
if not PromiseTestUtils.awaitSettled(promise) then
|
|
291
|
+
expect("promise hung").toEqual("promise settled")
|
|
292
|
+
return
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
local ok = promise:Yield()
|
|
296
|
+
expect(ok).toEqual(false)
|
|
297
|
+
end)
|
|
298
|
+
|
|
299
|
+
it("should throw synchronously for a bad robloxDataStore", function()
|
|
300
|
+
expect(function()
|
|
301
|
+
DataStorePromises.promiseIncrementAsync({} :: any, "key", 1)
|
|
302
|
+
end).toThrow("Bad robloxDataStore")
|
|
303
|
+
end)
|
|
304
|
+
|
|
305
|
+
it("should throw synchronously for a non-string key", function()
|
|
306
|
+
local mock = DataStoreMock.new()
|
|
307
|
+
expect(function()
|
|
308
|
+
DataStorePromises.promiseIncrementAsync(mock, 5 :: any, 1)
|
|
309
|
+
end).toThrow("Bad key")
|
|
310
|
+
end)
|
|
311
|
+
|
|
312
|
+
it("should throw synchronously for a non-number delta", function()
|
|
313
|
+
local mock = DataStoreMock.new()
|
|
314
|
+
expect(function()
|
|
315
|
+
DataStorePromises.promiseIncrementAsync(mock, "key", "notanumber" :: any)
|
|
316
|
+
end).toThrow("Bad delta")
|
|
317
|
+
end)
|
|
318
|
+
end)
|
|
319
|
+
|
|
320
|
+
describe("DataStorePromises.removeAsync", function()
|
|
321
|
+
it("should resolve true and clear the key", function()
|
|
322
|
+
local mock = DataStoreMock.new()
|
|
323
|
+
mock:SetRaw("key", "value")
|
|
324
|
+
|
|
325
|
+
local promise = DataStorePromises.removeAsync(mock, "key")
|
|
326
|
+
if not PromiseTestUtils.awaitSettled(promise) then
|
|
327
|
+
expect("promise hung").toEqual("promise settled")
|
|
328
|
+
return
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
local ok, value = promise:Yield()
|
|
332
|
+
expect(ok).toEqual(true)
|
|
333
|
+
expect(value).toEqual(true)
|
|
334
|
+
expect(mock:GetRaw("key")).toEqual(nil)
|
|
335
|
+
end)
|
|
336
|
+
|
|
337
|
+
it("should reject when the store fails all requests", function()
|
|
338
|
+
local mock = DataStoreMock.new()
|
|
339
|
+
mock:FailAllRequests()
|
|
340
|
+
|
|
341
|
+
local promise = DataStorePromises.removeAsync(mock, "key")
|
|
342
|
+
if not PromiseTestUtils.awaitSettled(promise) then
|
|
343
|
+
expect("promise hung").toEqual("promise settled")
|
|
344
|
+
return
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
local ok = promise:Yield()
|
|
348
|
+
expect(ok).toEqual(false)
|
|
349
|
+
end)
|
|
350
|
+
|
|
351
|
+
it("should throw synchronously for a bad robloxDataStore", function()
|
|
352
|
+
expect(function()
|
|
353
|
+
DataStorePromises.removeAsync({} :: any, "key")
|
|
354
|
+
end).toThrow("Bad robloxDataStore")
|
|
355
|
+
end)
|
|
356
|
+
|
|
357
|
+
it("should throw synchronously for a non-string key", function()
|
|
358
|
+
local mock = DataStoreMock.new()
|
|
359
|
+
expect(function()
|
|
360
|
+
DataStorePromises.removeAsync(mock, 5 :: any)
|
|
361
|
+
end).toThrow("Bad key")
|
|
362
|
+
end)
|
|
363
|
+
end)
|
package/src/jest.config.lua
CHANGED