Skip to content

Commit

Permalink
fix some tests
Browse files Browse the repository at this point in the history
  • Loading branch information
alvrs committed Aug 14, 2024
1 parent 5d17ce9 commit a5536e5
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 44 deletions.
2 changes: 1 addition & 1 deletion packages/zustand-query/src/actions/getConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@ describe("getConfig", () => {
registerTable({ store, table: namespacedTable });

attest(getConfig({ store, table: { label: "test" } })).equals(rootTable);
attest(getConfig({ store, table: { label: "test", namespace: "namespace" } })).equals(namespacedTable);
attest(getConfig({ store, table: { label: "test", namespaceLabel: "namespace" } })).equals(namespacedTable);
});
});
1 change: 1 addition & 0 deletions packages/zustand-query/src/actions/getTable.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe("getTable", () => {
label: "table1",
type: "table",
namespace: "namespace1",
namespaceLabel: "namespace1",
name: "table1",
tableId: "0x74626e616d65737061636531000000007461626c653100000000000000000000",
schema: {
Expand Down
1 change: 1 addition & 0 deletions packages/zustand-query/src/actions/registerTable.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe("registerTable", () => {
label: "table1",
type: "table",
namespace: "namespace1",
namespaceLabel: "namespace1",
name: "table1",
tableId: "0x74626e616d65737061636531000000007461626c653100000000000000000000",
schema: {
Expand Down
18 changes: 9 additions & 9 deletions packages/zustand-query/src/actions/registerTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,24 @@ export function registerTable<table extends Table>({
table,
}: RegisterTableArgs<table>): RegisterTableResult<table> {
// Pick only relevant keys from the table config, ignore keys like `codegen`, `deploy`
const { namespace, name, label, key, schema, type, tableId } = table;
const tableConfig = { namespace, name, label, key, schema, type, tableId };
const { namespace, namespaceLabel, name, label, key, schema, type, tableId } = table;
const tableConfig = { namespace, namespaceLabel, name, label, key, schema, type, tableId };

// Set config for table
store._.state.config[namespace] ??= {};
store._.state.config[namespace][label] = tableConfig;
store._.state.config[namespaceLabel] ??= {};
store._.state.config[namespaceLabel][label] = tableConfig;

// Init records map for table
store._.state.records[namespace] ??= {};
store._.state.records[namespace][label] ??= {};
store._.state.records[namespaceLabel] ??= {};
store._.state.records[namespaceLabel][label] ??= {};

// Init subscribers set for table
store._.tableSubscribers[namespace] ??= {};
store._.tableSubscribers[namespace][label] ??= new Set();
store._.tableSubscribers[namespaceLabel] ??= {};
store._.tableSubscribers[namespaceLabel][label] ??= new Set();

// Notify store subscribers
const storeUpdate = {
config: { [namespace]: { [label]: { prev: undefined, current: tableConfig } } },
config: { [namespaceLabel]: { [label]: { prev: undefined, current: tableConfig } } },
records: {},
};
store._.storeSubscribers.forEach((subscriber) => subscriber(storeUpdate));
Expand Down
33 changes: 18 additions & 15 deletions packages/zustand-query/src/actions/runQuery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { attest } from "@arktype/attest";
import { createStore } from "../createStore";
import { runQuery } from "./runQuery";
import { defineStore } from "@latticexyz/store";
import { Store } from "../common";
import { Store, StoreRecords, getQueryConfig } from "../common";
import { setRecord } from "./setRecord";
import { In, MatchRecord, NotIn, NotMatchRecord } from "../queryFragments";
import { Hex } from "viem";

describe("query", () => {
describe("runQuery", () => {
let store: Store;
const config = defineStore({
namespaces: {
Expand All @@ -18,10 +18,6 @@ describe("query", () => {
schema: { player: "bytes32", x: "int32", y: "int32" },
key: ["player"],
},
Health: {
schema: { player: "bytes32", health: "uint32" },
key: ["player"],
},
},
},
namespace2: {
Expand All @@ -30,13 +26,17 @@ describe("query", () => {
schema: { player: "bytes32", item: "bytes32", amount: "uint32" },
key: ["player", "item"],
},
Health: {
schema: { player: "bytes32", health: "uint32" },
key: ["player"],
},
},
},
},
});

const { Position, Health } = config.namespaces.namespace1.tables;
const { Inventory } = config.namespaces.namespace2.tables;
const { Position } = config.namespaces.namespace1.tables;
const { Inventory, Health } = config.namespaces.namespace2.tables;

beforeEach(() => {
store = createStore(config);
Expand Down Expand Up @@ -126,6 +126,8 @@ describe("query", () => {
"0x3": { player: "0x3", x: 3, y: 2 },
"0x4": { player: "0x4", x: 4, y: 1 },
},
},
namespace2: {
Health: {
"0x3": { player: "0x3", health: 3 },
"0x4": { player: "0x4", health: 4 },
Expand All @@ -136,20 +138,21 @@ describe("query", () => {
});

it("should include `records` only if the `includeRecords` option is provided", () => {
const resultWithoutRecords = runQuery({ store, query: [In(Position)] });
attest<"keys", keyof typeof resultWithoutRecords>();
const query = [In(Position)] as const;
const resultWithoutRecords = runQuery({ store, query });
attest<never | undefined, (typeof resultWithoutRecords)["records"]>();

const resultWithRecords = runQuery({ store, query: [In(Position)], options: { includeRecords: true } });
attest<"keys" | "records", keyof typeof resultWithRecords>();
const resultWithRecords = runQuery({ store, query, options: { includeRecords: true } });
attest<StoreRecords<getQueryConfig<typeof query>>, (typeof resultWithRecords)["records"]>();
});

it("should type the `records` in the result based on tables in the query", () => {
const result = runQuery({ store, query: [In(Position), In(Inventory)], options: { includeRecords: true } });
const result = runQuery({ store, query: [In(Position), In(Health)], options: { includeRecords: true } });

attest<"namespace1" | "namespace2", keyof (typeof result)["records"]>();
attest<"Position", keyof (typeof result)["records"]["namespace1"]>();
attest<"Inventory", keyof (typeof result)["records"]["namespace2"]>();
attest<"Health", keyof (typeof result)["records"]["namespace2"]>();
attest<{ player: Hex; x: number; y: number }, (typeof result)["records"]["namespace1"]["Position"][string]>();
attest<{ player: Hex; item: Hex; amount: number }, (typeof result)["records"]["namespace2"]["Inventory"][string]>();
attest<{ player: Hex; health: number }, (typeof result)["records"]["namespace2"]["Health"][string]>();
});
});
29 changes: 18 additions & 11 deletions packages/zustand-query/src/actions/runQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,34 @@ import {
import { getConfig } from "./getConfig";
import { getRecords } from "./getRecords";

type RunQueryOptions = CommonQueryOptions & {
export type RunQueryOptions = CommonQueryOptions & {
includeRecords?: boolean;
};

// TODO: is it feasible to type the store records return type based on the query?
export type RunQueryArgs = {
export type RunQueryArgs<query extends Query = Query, options extends RunQueryOptions = RunQueryOptions> = {
store: Store;
query: Query;
options?: RunQueryOptions;
query: query;
options?: options;
};

export type RunQueryResult<args extends RunQueryArgs = RunQueryArgs> = CommonQueryResult &
(args["options"] extends {
export type RunQueryResult<
query extends Query = Query,
options extends RunQueryOptions = RunQueryOptions,
> = CommonQueryResult &
(options extends {
includeRecords: true;
}
? {
records: StoreRecords<getQueryConfig<args["query"]>>;
records: StoreRecords<getQueryConfig<query>>;
}
: {});
: { records?: never });

export function runQuery<args extends RunQueryArgs>({ store, query, options }: args): RunQueryResult<args> {
export function runQuery<query extends Query, options extends RunQueryOptions>({
store,
query,
options,
}: RunQueryArgs<query, options>): RunQueryResult<query, options> {
// Only allow fragments with matching table keys for now
// TODO: we might be able to enable this if we add something like a `keySelector`
const expectedKeySchema = getKeySchema(getConfig({ store, table: query[0].table }));
Expand Down Expand Up @@ -63,7 +70,7 @@ export function runQuery<args extends RunQueryArgs>({ store, query, options }: a

// Early return if records are not requested
if (!options?.includeRecords) {
return { keys };
return { keys } as never;
}

const records: MutableStoreRecords = {};
Expand All @@ -73,5 +80,5 @@ export function runQuery<args extends RunQueryArgs>({ store, query, options }: a
const tableRecords = getRecords({ store, table, keys: Object.values(keys) });
records[namespaceLabel][label] ??= tableRecords;
}
return { keys, records };
return { keys, records } as never;
}
2 changes: 1 addition & 1 deletion packages/zustand-query/src/actions/subscribeQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export function subscribeQuery({ store, query, options }: SubscribeQueryArgs): S
key,
{ prev: undefined, current: record },
]),
);
) as never;
}
}

Expand Down
3 changes: 3 additions & 0 deletions packages/zustand-query/src/createStore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ describe("createStore", () => {
label: "table1",
type: "table",
namespace: "namespace1",
namespaceLabel: "namespace1",
name: "table1",
tableId: "0x74626e616d65737061636531000000007461626c653100000000000000000000",
schema: {
Expand Down Expand Up @@ -67,6 +68,7 @@ describe("createStore", () => {
label: "table1";
type: "table";
namespace: string;
namespaceLabel: "namespace1";
name: string;
tableId: Hex;
schema: {
Expand Down Expand Up @@ -305,6 +307,7 @@ describe("createStore", () => {
label: "table2",
name: "table2",
namespace: "namespace2",
namespaceLabel: "namespace2",
schema: {
field1: {
internalType: "uint256",
Expand Down
51 changes: 50 additions & 1 deletion packages/zustand-query/src/decorators/default.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { defineStore } from "@latticexyz/store";
import { describe, expect, it, vi } from "vitest";
import { createStore } from "../createStore";
import { defineTable } from "@latticexyz/store/config/v2";
import { In } from "../queryFragments";
import { Hex } from "viem";
import { runQuery } from "../actions";
import { StoreRecords, getQueryConfig } from "../common";

describe("store with default actions", () => {
describe("decodeKey", () => {
Expand Down Expand Up @@ -121,7 +125,7 @@ describe("store with default actions", () => {
const store = createStore();
store.registerTable({ table });

attest(store.getConfig({ table: { label: "test", namespace: "namespace" } })).equals(table);
attest(store.getConfig({ table: { label: "test", namespaceLabel: "namespace" } })).equals(table);
});
});

Expand Down Expand Up @@ -322,6 +326,51 @@ describe("store with default actions", () => {
});
});

describe("runQuery", () => {
const config = defineStore({
namespaces: {
namespace1: {
tables: {
Position: {
schema: { player: "bytes32", x: "int32", y: "int32" },
key: ["player"],
},
},
},
namespace2: {
tables: {
Health: {
schema: { player: "bytes32", health: "uint32" },
key: ["player"],
},
},
},
},
});
const store = createStore(config);
const { Position } = config.namespaces.namespace1.tables;
const { Health } = config.namespaces.namespace2.tables;

it("should include `records` only if the `includeRecords` option is provided", () => {
const query = [In(Position)] as const;
const resultWithoutRecords = store.runQuery({ query });
attest<never | undefined, (typeof resultWithoutRecords)["records"]>();

const resultWithRecords = store.runQuery({ query, options: { includeRecords: true } });
attest<StoreRecords<getQueryConfig<typeof query>>, (typeof resultWithRecords)["records"]>();
});

it("should type the `records` in the result based on tables in the query", () => {
const result = runQuery({ store, query: [In(Position), In(Health)], options: { includeRecords: true } });

attest<"namespace1" | "namespace2", keyof (typeof result)["records"]>();
attest<"Position", keyof (typeof result)["records"]["namespace1"]>();
attest<"Health", keyof (typeof result)["records"]["namespace2"]>();
attest<{ player: Hex; x: number; y: number }, (typeof result)["records"]["namespace1"]["Position"][string]>();
attest<{ player: Hex; health: number }, (typeof result)["records"]["namespace2"]["Health"][string]>();
});
});

describe("setRecord", () => {
it("should show a type warning if an invalid table, key or record is used", () => {
const config = defineStore({
Expand Down
19 changes: 13 additions & 6 deletions packages/zustand-query/src/decorators/default.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Store, StoreConfig } from "../common";
import { Query, Store, StoreConfig } from "../common";
import { DecodeKeyArgs, DecodeKeyResult, decodeKey } from "../actions/decodeKey";
import { DeleteRecordArgs, DeleteRecordResult, deleteRecord } from "../actions/deleteRecord";
import { EncodeKeyArgs, EncodeKeyResult, encodeKey } from "../actions/encodeKey";
Expand All @@ -9,7 +9,7 @@ import { GetRecordsArgs, GetRecordsResult, getRecords } from "../actions/getReco
import { GetTableArgs, GetTableResult, getTable } from "../actions/getTable";
import { GetTablesResult, getTables } from "../actions/getTables";
import { RegisterTableArgs, RegisterTableResult, registerTable } from "../actions/registerTable";
import { RunQueryArgs, RunQueryResult, runQuery } from "../actions/runQuery";
import { RunQueryArgs, RunQueryOptions, RunQueryResult, runQuery } from "../actions/runQuery";
import { SetRecordArgs, SetRecordResult, setRecord } from "../actions/setRecord";
import { SetRecordsArgs, SetRecordsResult, setRecords } from "../actions/setRecords";
import { SubscribeQueryArgs, SubscribeQueryResult, subscribeQuery } from "../actions/subscribeQuery";
Expand All @@ -26,7 +26,10 @@ export type StoreBoundGetRecordArgs<table extends Table = Table> = Omit<GetRecor
export type StoreBoundGetRecordsArgs<table extends Table = Table> = Omit<GetRecordsArgs<table>, "store">;
export type StoreBoundGetTableArgs<table extends Table = Table> = Omit<GetTableArgs<table>, "store">;
export type StoreBoundRegisterTableArgs<table extends Table = Table> = Omit<RegisterTableArgs<table>, "store">;
export type StoreBoundRunQueryArgs = Omit<RunQueryArgs, "store">;
export type StoreBoundRunQueryArgs<
query extends Query = Query,
options extends RunQueryOptions = RunQueryOptions,
> = Omit<RunQueryArgs<query, options>, "store">;
export type StoreBoundSetRecordArgs<table extends Table = Table> = Omit<SetRecordArgs<table>, "store">;
export type StoreBoundSetRecordsArgs<table extends Table = Table> = Omit<SetRecordsArgs<table>, "store">;
export type StoreBoundSubscribeQueryArgs = Omit<SubscribeQueryArgs, "store">;
Expand All @@ -47,7 +50,9 @@ export type DefaultActions<config extends StoreConfig = StoreConfig> = {
getTable: <table extends Table>(args: StoreBoundGetTableArgs<table>) => GetTableResult<table>;
getTables: () => GetTablesResult<config>;
registerTable: <table extends Table>(args: StoreBoundRegisterTableArgs<table>) => RegisterTableResult<table>;
runQuery: (args: StoreBoundRunQueryArgs) => RunQueryResult;
runQuery: <query extends Query, options extends RunQueryOptions>(
args: StoreBoundRunQueryArgs<query, options>,
) => RunQueryResult<query, options>;
setRecord: <table extends Table>(args: StoreBoundSetRecordArgs<table>) => SetRecordResult;
setRecords: <table extends Table>(args: StoreBoundSetRecordsArgs<table>) => SetRecordsResult;
subscribeQuery: (args: StoreBoundSubscribeQueryArgs) => SubscribeQueryResult;
Expand All @@ -67,11 +72,13 @@ export function defaultActions<config extends StoreConfig>(store: Store<config>)
getTable: <table extends Table>(args: StoreBoundGetTableArgs<table>) => getTable({ store, ...args }),
getTables: () => getTables({ store }),
registerTable: (args: StoreBoundRegisterTableArgs) => registerTable({ store, ...args }),
runQuery: (args: StoreBoundRunQueryArgs) => runQuery({ store, ...args }),
runQuery: <query extends Query, options extends RunQueryOptions>(args: StoreBoundRunQueryArgs<query, options>) =>
runQuery({ store, ...args }),
setRecord: <table extends Table>(args: StoreBoundSetRecordArgs<table>) => setRecord({ store, ...args }),
setRecords: <table extends Table>(args: StoreBoundSetRecordsArgs<table>) => setRecords({ store, ...args }),
subscribeQuery: (args: StoreBoundSubscribeQueryArgs) => subscribeQuery({ store, ...args }),
subscribeStore: (args: StoreBoundSubscribeStoreArgs) => subscribeStore({ store, ...args }),
subscribeStore: <config extends StoreConfig>(args: StoreBoundSubscribeStoreArgs<config>) =>
subscribeStore({ store, ...args }),
subscribeTable: <table extends Table>(args: StoreBoundSubscribeTableArgs<table>) =>
subscribeTable({ store, ...args }),
};
Expand Down

0 comments on commit a5536e5

Please sign in to comment.