Skip to main content

DataStoreTestUtils

Shared setup helpers for the DataStore server specs. The two controller builders -- DataStoreTestUtils.setup (raw DataStores) and DataStoreTestUtils.setupDataStoreManager (a PlayerDataStoreManager) -- each own a [Maid] and register everything they create on it, so a single controller:destroy() tears it all down: the stores, the auto-save loop each starts once loaded, the helpers, the manager, and the service bag.

Functions

setup

DataStoreTestUtils.setup() → {...}

Builds the controller the DataStore specs share: a fresh DataStoreMock, a ServiceBag with an in-process MessagingServiceMock injected (so messaging-enabled stores never touch the real MessagingService), and builder methods that own everything they create on one Maid. destroy() tears it all down. Every builder defaults its key to "player_1"; pass a key to override.

Fields: mock, serviceBag. Builders: newDataStore(key?), newSessionLockedStore(key?, userIdList?), newLockHelper(key?) (returns helper, dataStore), newMessageHelper(dataStore?) (returns helper, dataStore), newServer(opts?) (returns dataStore, helper?; opts = { key?, messaging?, autoCloseOnRequest? }). Helpers: awaitOwn(dataStore) -> boolean (loads and reports whether we own the session).

promiseSimulatedShutdown

DataStoreTestUtils.promiseSimulatedShutdown(
userIds{PlayerUserId}?--

players still in the server when it began closing

) → Promise--

what BindToCloseService yields on, so the server cannot die until it settles

Simulates what a real Roblox server does when it shuts down, which is the only accurate model for the save path: Roblox fires PlayerRemoving for every player still in the server, giving those handlers the same "hold the shutdown open for me" treatment BindToClose gets. So the removals are what save and close each session; the close callback's job is only to wait for them to flush.

Destroying the manager is NOT a shutdown and never has been -- nothing in a live server destroys it.

awaitServiceShutdown

DataStoreTestUtils.awaitServiceShutdown(
playerDataStoreServicePlayerDataStoreService,
userIds{PlayerUserId}?,
timeoutnumber?--

defaults to 5

) → boolean--

false only if a shutdown was started and did not settle in time

Shuts down the manager a PlayerDataStoreService owns, the way Roblox would, and waits for it.

Call this from the destroy() of any spec that injects a datastore into the service and tears down by destroying its ServiceBag. manager:Destroy() destroys no stores -- they are only ever destroyed by a removal -- and a PlayerMock never fires the real Players.PlayerRemoving, so without this every store the spec loaded outlives it with its task.spawn auto-save loop running. In the shared test place that loop later fires inside another package's window and fails it.

userIds is only needed to model "PlayerRemoving landed first" for an ordering assertion. For cleanup, omit it: the close removes every store the manager still owns.

setupDataStoreManager

DataStoreTestUtils.setupDataStoreManager() → {
...
}

Builds the controller the PlayerDataStoreManager specs share: a session-locked manager wired to a fresh DataStoreMock (keyed user_<userId>), all owned by a Maid.

destroy() shuts the server down the way Roblox would (see DataStoreTestUtils.promiseSimulatedShutdown) and then tears the objects down. The shutdown is not optional bookkeeping: a store the spec loaded keeps its auto-save loop running until something removes it, and in the shared test place that loop outlives the spec and fires inside a later package's window.

Fields: manager, mock, serviceBag. Helpers: storeAndAwaitLock() -> boolean -- stores a value on user 1's store and waits for the session-locked load to write the lock envelope. promiseShutdown(userIds?) -> Promise.

newServiceBag

DataStoreTestUtils.newServiceBag(
maidMaid,
robloxMessagingServiceMessagingServiceMock?--

injected when provided

) → ServiceBag

Builds a ServiceBag with PlaceMessagingService registered and Init/Start'd, owned by the maid. Pass a Roblox MessagingService mock to inject it into PlaceMessagingService between Init and Start.

newDataStore

DataStoreTestUtils.newDataStore(
maidMaid,
keystring
) → DataStore

Builds a DataStore over mock and owns it with the maid.

newSessionLockedStore

DataStoreTestUtils.newSessionLockedStore(
maidMaid,
keystring,
userIdList{number}?--

defaults to { 1 }

) → DataStore

Builds a session-locked DataStore over mock and owns it with the maid.

newMessageHelper

DataStoreTestUtils.newMessageHelper(
maidMaid,
serviceBagServiceBag,
dataStoreDataStore
) → DataStoreMessageHelper

Builds a DataStoreMessageHelper over dataStore and owns it with the maid.

Show raw api
{
    "functions": [
        {
            "name": "setup",
            "desc": "Builds the controller the DataStore specs share: a fresh [DataStoreMock], a [ServiceBag] with an\nin-process [MessagingServiceMock] injected (so messaging-enabled stores never touch the real\nMessagingService), and builder methods that own everything they create on one Maid. `destroy()`\ntears it all down. Every builder defaults its key to `\"player_1\"`; pass a key to override.\n\nFields: `mock`, `serviceBag`.\nBuilders: `newDataStore(key?)`, `newSessionLockedStore(key?, userIdList?)`, `newLockHelper(key?)`\n(returns `helper, dataStore`), `newMessageHelper(dataStore?)` (returns `helper, dataStore`),\n`newServer(opts?)` (returns `dataStore, helper?`; `opts` = `{ key?, messaging?, autoCloseOnRequest? }`).\nHelpers: `awaitOwn(dataStore)` -> boolean (loads and reports whether we own the session).",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "{ ... }"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 40,
                "path": "src/datastore/src/Server/DataStoreTestUtils.lua"
            }
        },
        {
            "name": "promiseSimulatedShutdown",
            "desc": "Simulates what a real Roblox server does when it shuts down, which is the only accurate model for\nthe save path: Roblox fires PlayerRemoving for every player still in the server, giving those\nhandlers the same \"hold the shutdown open for me\" treatment BindToClose gets. So the removals are\nwhat save and close each session; the close callback's job is only to wait for them to flush.\n\nDestroying the manager is NOT a shutdown and never has been -- nothing in a live server destroys it.",
            "params": [
                {
                    "name": "manager",
                    "desc": "",
                    "lua_type": "PlayerDataStoreManager"
                },
                {
                    "name": "userIds",
                    "desc": "players still in the server when it began closing",
                    "lua_type": "{ PlayerUserId }?"
                }
            ],
            "returns": [
                {
                    "desc": "what BindToCloseService yields on, so the server cannot die until it settles",
                    "lua_type": "Promise"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 117,
                "path": "src/datastore/src/Server/DataStoreTestUtils.lua"
            }
        },
        {
            "name": "awaitServiceShutdown",
            "desc": "Shuts down the manager a [PlayerDataStoreService] owns, the way Roblox would, and waits for it.\n\nCall this from the `destroy()` of any spec that injects a datastore into the service and tears down\nby destroying its ServiceBag. `manager:Destroy()` destroys no stores -- they are only ever destroyed\nby a removal -- and a [PlayerMock] never fires the real `Players.PlayerRemoving`, so without this\nevery store the spec loaded outlives it with its `task.spawn` auto-save loop running. In the shared\ntest place that loop later fires inside another package's window and fails it.\n\n`userIds` is only needed to model \"PlayerRemoving landed first\" for an ordering assertion. For\ncleanup, omit it: the close removes every store the manager still owns.",
            "params": [
                {
                    "name": "playerDataStoreService",
                    "desc": "",
                    "lua_type": "PlayerDataStoreService"
                },
                {
                    "name": "userIds",
                    "desc": "",
                    "lua_type": "{ PlayerUserId }?"
                },
                {
                    "name": "timeout",
                    "desc": "defaults to 5",
                    "lua_type": "number?"
                }
            ],
            "returns": [
                {
                    "desc": "false only if a shutdown was started and did not settle in time",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 142,
                "path": "src/datastore/src/Server/DataStoreTestUtils.lua"
            }
        },
        {
            "name": "setupDataStoreManager",
            "desc": "Builds the controller the [PlayerDataStoreManager] specs share: a session-locked manager wired to\na fresh [DataStoreMock] (keyed `user_<userId>`), all owned by a Maid.\n\n`destroy()` shuts the server down the way Roblox would (see\n[DataStoreTestUtils.promiseSimulatedShutdown]) and then tears the objects down. The shutdown is not\noptional bookkeeping: a store the spec loaded keeps its auto-save loop running until something\nremoves it, and in the shared test place that loop outlives the spec and fires inside a later\npackage's window.\n\nFields: `manager`, `mock`, `serviceBag`.\nHelpers: `storeAndAwaitLock()` -> boolean -- stores a value on user 1's store and waits for the\nsession-locked load to write the lock envelope. `promiseShutdown(userIds?)` -> Promise.",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "{ manager: PlayerDataStoreManager, mock: DataStoreMock, ... }"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 185,
                "path": "src/datastore/src/Server/DataStoreTestUtils.lua"
            }
        },
        {
            "name": "newServiceBag",
            "desc": "Builds a [ServiceBag] with PlaceMessagingService registered and Init/Start'd, owned by the maid.\nPass a Roblox MessagingService mock to inject it into PlaceMessagingService between Init and Start.",
            "params": [
                {
                    "name": "maid",
                    "desc": "",
                    "lua_type": "Maid"
                },
                {
                    "name": "robloxMessagingService",
                    "desc": "injected when provided",
                    "lua_type": "MessagingServiceMock?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "ServiceBag"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 231,
                "path": "src/datastore/src/Server/DataStoreTestUtils.lua"
            }
        },
        {
            "name": "newDataStore",
            "desc": "Builds a [DataStore] over `mock` and owns it with the maid.",
            "params": [
                {
                    "name": "maid",
                    "desc": "",
                    "lua_type": "Maid"
                },
                {
                    "name": "mock",
                    "desc": "",
                    "lua_type": "DataStoreMock"
                },
                {
                    "name": "key",
                    "desc": "",
                    "lua_type": "string"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "DataStore"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 250,
                "path": "src/datastore/src/Server/DataStoreTestUtils.lua"
            }
        },
        {
            "name": "newSessionLockedStore",
            "desc": "Builds a session-locked [DataStore] over `mock` and owns it with the maid.",
            "params": [
                {
                    "name": "maid",
                    "desc": "",
                    "lua_type": "Maid"
                },
                {
                    "name": "mock",
                    "desc": "",
                    "lua_type": "DataStoreMock"
                },
                {
                    "name": "key",
                    "desc": "",
                    "lua_type": "string"
                },
                {
                    "name": "userIdList",
                    "desc": "defaults to { 1 }",
                    "lua_type": "{ number }?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "DataStore"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 263,
                "path": "src/datastore/src/Server/DataStoreTestUtils.lua"
            }
        },
        {
            "name": "newMessageHelper",
            "desc": "Builds a [DataStoreMessageHelper] over `dataStore` and owns it with the maid.",
            "params": [
                {
                    "name": "maid",
                    "desc": "",
                    "lua_type": "Maid"
                },
                {
                    "name": "serviceBag",
                    "desc": "",
                    "lua_type": "ServiceBag"
                },
                {
                    "name": "dataStore",
                    "desc": "",
                    "lua_type": "DataStore"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "DataStoreMessageHelper"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 278,
                "path": "src/datastore/src/Server/DataStoreTestUtils.lua"
            }
        }
    ],
    "properties": [],
    "types": [],
    "name": "DataStoreTestUtils",
    "desc": "Shared setup helpers for the DataStore server specs. The two controller builders --\n[DataStoreTestUtils.setup] (raw DataStores) and [DataStoreTestUtils.setupDataStoreManager]\n(a [PlayerDataStoreManager]) -- each own a [Maid] and register everything they create on it, so a\nsingle `controller:destroy()` tears it all down: the stores, the auto-save loop each starts once\nloaded, the helpers, the manager, and the service bag.",
    "source": {
        "line": 11,
        "path": "src/datastore/src/Server/DataStoreTestUtils.lua"
    }
}