{"id":"xoifaii/mock","name":"mock","scope":"xoifaii","platform":"roblox","description":"In-memory DataStoreService and MemoryStoreService for Roblox that keep the real services' limits.","version":"1.0.1","latest":"1.0.1","versions":["1.0.0","1.0.1"],"license":"MIT","licenseRating":"safe","licenseCaveats":[],"licenseVerified":true,"dependencies":{},"integrity":"de21e6ef764a8b2d13b0ecdbd72d4ef678b0710a849af699fd223fe954c6bfda","likes":0,"downloads":0,"install":"forest install xoifaii/mock","url":"https://forest.dev/p/roblox/xoifaii/mock","files":"https://api.forest.dev/ai/package/roblox/xoifaii/mock/files","readme":"# Mock\r\n\r\nAn in-memory `DataStoreService` and `MemoryStoreService` for Roblox, for testing code that saves data\r\nwithout touching real DataStores. It keeps the real services' limits, so code tested against it meets\r\nthe same refusals it would meet live.\r\n\r\nIt has no dependencies. It's a rewrite of Ledger v6's mock (XoifaiI/Ledger at `4c35319`) as a\r\npackage of its own, with the same behaviour.\r\n\r\n## Use\r\n\r\n```luau\r\nlocal Mock = require(path.to.Mock)\r\n\r\nlocal Service = Mock.New({ Players = 10 })\r\n\r\nlocal Store = Service:GetDataStore(\"PlayerData\")\r\nStore:SetAsync(\"1\", { Gold = 10 })\r\nprint(Store:GetAsync(\"1\").Gold) --> 10\r\n\r\nlocal Map = Service:GetHashMap(\"Sessions\")\r\nMap:SetAsync(\"1\", \"server-a\", 60)\r\n```\r\n\r\nOne service answers both services' methods, so pass the same object wherever the code under test\r\ntakes a `DataStoreService` or a `MemoryStoreService`. Two pieces of code given the same service act\r\nlike two servers on one experience.\r\n\r\n### Options\r\n\r\n| Option | Default | What it does |\r\n| --- | --- | --- |\r\n| `Players` | 0 | Players in this server. The server's request budget grows with it. |\r\n| `CCU` | `Players` | Players across the experience. The experience's budget and MemoryStore quotas grow with it. |\r\n| `Throttled` | `true` | `false` turns every budget, quota and byte limit off. |\r\n\r\n## What it supports\r\n\r\n**Data stores** (`Service:GetDataStore(Name, Scope?)`): `GetAsync`, `SetAsync`, `UpdateAsync`,\r\n`RemoveAsync`, `GetVersionAsync`, `ListVersionsAsync`, `ListKeysAsync`, and\r\n`Service:GetRequestBudgetForRequestType`.\r\n\r\n**Hash maps** (`Service:GetHashMap(Name)`): `GetAsync`, `SetAsync`, `UpdateAsync`, `RemoveAsync`.\r\n\r\n## Limits it keeps\r\n\r\n- Names, scopes and keys up to their real lengths; values up to 4 MB as JSON; MemoryStore items up to 32 KB.\r\n- The data store request budget, per server and per experience, refilled over a minute. A request\r\n  short of budget waits in a queue, and fails once 30 are waiting.\r\n- How many bytes one key can be read and written per minute.\r\n- MemoryStore request units, for the experience and per map, and the memory quota. Both fail at once.\r\n- Versions kept for 30 days; a removed key still lists for that long.\r\n- Two writes on one key can meet: `UpdateAsync` runs its transform again, as the real one does.\r\n\r\n## Where it's stricter than the real services\r\n\r\n- An `UpdateAsync` transform that yields throws, on both services.\r\n- A MemoryStore item has to read back exactly as it went in. A buffer, a mixed table, a sparse\r\n  array or NaN is refused, where the real service would quietly mangle it.\r\n- Key listings come back in a fixed order that isn't key order, so code can't come to rely on an order.\r\n\r\n## Reading what happened\r\n\r\nThe service keeps counters a test can read: `Calls` (data store requests by type), `Conflicts`\r\n(transforms that ran again), `Units` and `UnitsByMap` (MemoryStore units spent), `Bytes` and\r\n`PeakBytes` (MemoryStore memory), and `Queued`. `Service:Reset()` forgets everything.\r\n","readmeTruncated":false}