/ packages / stateManager / mod_test.ts
mod_test.ts
  1  import { assertEquals, assertNotEquals, equal } from "@std/assert";
  2  import { assertInstanceOf } from "@std/assert/instance-of";
  3  
  4  import { MemStore } from "@massmarket/store/mem";
  5  import {
  6    createTestBlockchainClient,
  7    createTestRelayClient,
  8  } from "@massmarket/client/test";
  9  import { RelayResponseError } from "@massmarket/client";
 10  import { type codec, randomBytes } from "@massmarket/utils";
 11  
 12  import StateManager from "./mod.ts";
 13  import { assertRejects } from "@std/assert/rejects";
 14  
 15  // we create the blockchain client outside of the tests since viem has a ws leak
 16  const blockchainClient = createTestBlockchainClient();
 17  const relayClient = await createTestRelayClient(blockchainClient);
 18  
 19  const root = new Map(Object.entries({
 20    Tags: new Map(),
 21    Orders: new Map(),
 22    Accounts: new Map(),
 23    Inventory: new Map(),
 24    Listings: new Map(),
 25    Manifest: new Map(),
 26    SchemeVersion: 1,
 27  }));
 28  
 29  Deno.test("Database Testings", async (t) => {
 30    const store = new MemStore();
 31    const sm = new StateManager({
 32      store,
 33      id: relayClient.shopId,
 34      defaultState: root,
 35    });
 36  
 37    await sm.open();
 38  
 39    await t.step("add a relay and set a key and retrieve it", async () => {
 40      // connect to the relay
 41      const { resolve, promise } = Promise.withResolvers();
 42      sm.events.on((manifestPatch) => {
 43        resolve(manifestPatch);
 44      }, ["Manifest"]);
 45  
 46      sm.addConnection(relayClient);
 47      // wait for manifest to be received
 48      await promise;
 49      const testAddr = Uint8Array.from([
 50        0xf0,
 51        0xf1,
 52        0xf2,
 53        0x03,
 54        0x04,
 55        0x05,
 56        0xf6,
 57        0xf7,
 58        0xf8,
 59        0x09,
 60        0x0a,
 61        0x0b,
 62        0xfc,
 63        0xfd,
 64        0xfe,
 65        0x0f,
 66        0x01,
 67        0x02,
 68        0xf3,
 69        0xf4,
 70      ]);
 71  
 72      const testCurrency = new Map<string, codec.CodecValue>([
 73        ["Address", testAddr],
 74        ["ChainID", 1337],
 75      ]);
 76      await sm.set(
 77        ["Manifest", "PricingCurrency"],
 78        testCurrency,
 79      );
 80      const value = await sm.get(["Manifest", "PricingCurrency"]);
 81      assertEquals(
 82        value,
 83        new Map<string, codec.CodecValue>([
 84          ["Address", testAddr],
 85          ["ChainID", 1337],
 86        ]),
 87      );
 88    });
 89  
 90    await t.step(
 91      "Make sure stateManager throws an error when a patch is rejected",
 92      async () => {
 93        const result = await assertRejects(() => sm.set(["Trash"], "Bad Value"));
 94        assertInstanceOf(result, RelayResponseError);
 95      },
 96    );
 97    await t.step("Make sure we can do more then one bad write", async () => {
 98      const result = await assertRejects(() =>
 99        sm.set(["Trash"], "Another Bad Value")
100      );
101      assertInstanceOf(result, RelayResponseError);
102    });
103  
104    await sm.close();
105  
106    await t.step("Make sure we can close the state manager", async () => {
107      const pricingCurrencyPath = ["Manifest", "PricingCurrency"];
108      const nonce = relayClient.keyCardNonce;
109      const nsm = new StateManager({
110        store,
111        id: relayClient.shopId,
112      });
113      await nsm.open();
114      nsm.addConnection(relayClient);
115      assertEquals(
116        nonce,
117        relayClient.keyCardNonce,
118        "the new state manager should have loaded the nonce",
119      );
120      const oldValue = await nsm.get(pricingCurrencyPath);
121      await nsm.close();
122  
123      // Test that we can set new values after reopening
124      const reopenedSm = new StateManager({
125        store,
126        id: relayClient.shopId,
127      });
128      await reopenedSm.open();
129      reopenedSm.addConnection(relayClient);
130  
131      // Set a new value
132      const newTestValue = new Map<string, codec.CodecValue>([
133        ["Address", randomBytes(20)],
134        ["ChainID", 1337],
135      ]);
136      await reopenedSm.set(pricingCurrencyPath, newTestValue);
137  
138      // wait until we see the new value from the relay
139      await new Promise((resolve) => {
140        reopenedSm.events.on((val) => {
141          if (equal(val, newTestValue)) {
142            resolve(void 0);
143          }
144        }, pricingCurrencyPath);
145      });
146  
147      // Verify the value was set correctly
148      const retrievedValue = await reopenedSm.get(pricingCurrencyPath);
149      assertNotEquals(
150        retrievedValue,
151        oldValue,
152        `should not be the old value`,
153      );
154      assertEquals(
155        retrievedValue,
156        newTestValue,
157        `should be able to set and get values after reopening`,
158      );
159      await reopenedSm.close();
160    });
161  
162    await relayClient.disconnect();
163  });