diff --git a/README.md b/README.md index 8e703b0..30e68d8 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,27 @@ -# Store +# BearMetalStore +A no-dep, lightweight, simple store for storing data in a JSON file. + +## Usage + +```ts +import { BearMetalStore } from "https://deno.land/x/bearmetalstore@v0.0.1/mod.ts"; + +const store = new BearMetalStore(); + +store.set("key", "value"); + +console.log(store.get("key")); +``` + +## API + +### `new BearMetalStore(storePath?: string)` + +Creates a new store. + +#### Parameters + +- `storePath?: string` + + The path to the store file. diff --git a/deno.json b/deno.json index c513337..65be207 100644 --- a/deno.json +++ b/deno.json @@ -6,5 +6,9 @@ "mod.ts", "store.ts" ], - "exports": "./mod.ts" + "exports": "./mod.ts", + "imports": { + "@std/assert": "jsr:@std/assert@^1.0.6", + "@std/testing": "jsr:@std/testing@^1.0.3" + } } diff --git a/deno.lock b/deno.lock new file mode 100644 index 0000000..f4a6992 --- /dev/null +++ b/deno.lock @@ -0,0 +1,50 @@ +{ + "version": "4", + "specifiers": { + "jsr:@std/assert@^1.0.6": "1.0.6", + "jsr:@std/data-structures@^1.0.4": "1.0.4", + "jsr:@std/fs@^1.0.4": "1.0.4", + "jsr:@std/internal@^1.0.4": "1.0.4", + "jsr:@std/path@^1.0.6": "1.0.6", + "jsr:@std/testing@^1.0.3": "1.0.3" + }, + "jsr": { + "@std/assert@1.0.6": { + "integrity": "1904c05806a25d94fe791d6d883b685c9e2dcd60e4f9fc30f4fc5cf010c72207", + "dependencies": [ + "jsr:@std/internal" + ] + }, + "@std/data-structures@1.0.4": { + "integrity": "fa0e20c11eb9ba673417450915c750a0001405a784e2a4e0c3725031681684a0" + }, + "@std/fs@1.0.4": { + "integrity": "2907d32d8d1d9e540588fd5fe0ec21ee638134bd51df327ad4e443aaef07123c", + "dependencies": [ + "jsr:@std/path" + ] + }, + "@std/internal@1.0.4": { + "integrity": "62e8e4911527e5e4f307741a795c0b0a9e6958d0b3790716ae71ce085f755422" + }, + "@std/path@1.0.6": { + "integrity": "ab2c55f902b380cf28e0eec501b4906e4c1960d13f00e11cfbcd21de15f18fed" + }, + "@std/testing@1.0.3": { + "integrity": "f98c2bee53860a5916727d7e7d3abe920dd6f9edace022e2d059f00d05c2cf42", + "dependencies": [ + "jsr:@std/assert", + "jsr:@std/data-structures", + "jsr:@std/fs", + "jsr:@std/internal", + "jsr:@std/path" + ] + } + }, + "workspace": { + "dependencies": [ + "jsr:@std/assert@^1.0.6", + "jsr:@std/testing@^1.0.3" + ] + } +} diff --git a/store.test.ts b/store.test.ts new file mode 100644 index 0000000..9c80d82 --- /dev/null +++ b/store.test.ts @@ -0,0 +1,89 @@ +// deno-lint-ignore-file no-explicit-any +import { BearMetalStore } from "./store.ts"; +import { assertEquals } from "@std/assert"; +import { + assertSpyCall, + assertSpyCallArgs, + spy, + type SpyLike, +} from "@std/testing/mock"; +import { beforeAll } from "@std/testing/bdd"; + +beforeAll(() => { + let store = "{}"; + Deno.readTextFileSync = spy(() => store); + Deno.writeTextFileSync = spy((_, string) => store = string); + + globalThis.addEventListener = spy(globalThis.addEventListener); + globalThis.removeEventListener = spy(globalThis.removeEventListener); +}); + +Deno.test("BearMetalStore Traditional", async (t) => { + const store = new BearMetalStore(); + + const listener = spy(); + globalThis.addEventListener(store.EVENT_NAME, listener); + + await t.step("set and get", () => { + store.set("key", "value"); + assertEquals(store.get("key"), "value"); + }); + + await t.step("write file called", () => { + assertSpyCall(Deno.writeTextFileSync as SpyLike, 0); + }); + + await t.step("event listener called", () => { + assertSpyCall(listener, 0); + }); + + await t.step("delete and get", () => { + store.delete("key"); + assertEquals(store.get("key"), undefined); + }); + + await t.step("event listeners removed", () => { + store[Symbol.dispose](); + assertSpyCallArgs(globalThis.removeEventListener as SpyLike, 0, [ + store.EVENT_NAME, + (store as any).readIn, + ]); + }); +}); +Deno.test("BearMetalStore `using` syntax", async (t) => { + let eventName = ""; + let readIn = () => {}; + { + using store = new BearMetalStore(); + eventName = store.EVENT_NAME; + readIn = (store as any).readIn; + + const listener = spy(); + globalThis.addEventListener(store.EVENT_NAME, listener); + + await t.step("set and get", () => { + store.set("key", "value"); + assertEquals(store.get("key"), "value"); + }); + + await t.step("write file called", () => { + assertSpyCall(Deno.writeTextFileSync as SpyLike, 0); + }); + + await t.step("event listener called", () => { + assertSpyCall(listener, 0); + }); + + await t.step("delete and get", () => { + store.delete("key"); + assertEquals(store.get("key"), undefined); + }); + } + + await t.step("event listeners removed", () => { + assertSpyCallArgs(globalThis.removeEventListener as SpyLike, 0, [ + eventName, + readIn, + ]); + }); +});