/ packages / stateManager / patching_test.ts
patching_test.ts
  1  import StateManager from "./mod.ts";
  2  import { MemStore } from "@massmarket/store";
  3  import type { PushedPatchSet } from "@massmarket/client";
  4  import type { CodecValue, Path } from "@massmarket/utils/codec";
  5  import { extractEntriesFromHAMT, fetchAndDecode } from "@massmarket/utils";
  6  import { assertEquals } from "@std/assert/equals";
  7  
  8  Deno.test("Database Testings", async (t) => {
  9    const testFiles = [
 10      "ManifestOkay",
 11      "ListingOkay",
 12      "OrderOkay",
 13      "ShopOkay",
 14      "InventoryOkay",
 15      "UserFlows/SimpleShoppingTrip",
 16    ];
 17  
 18    const skippedTest = new Set(
 19      [
 20        // similar array issue to below. How to deal with appending to an "empty" array
 21        "TestGenerateVectorsShopOkay/append-listing-image",
 22        "TestGenerateVectorsShopOkay/add-tag",
 23        "TestGenerateVectorsShopOkay/append-listing-to-tag",
 24        "TestGenerateVectorsShopOkay/append-listing-to-tag2",
 25      ],
 26    );
 27  
 28    const skippedAfterCheck = new Set(
 29      [
 30        /* Related to Issue #342: figure out how to handle big IDs
 31        "Listings" => Map(3) {
 32          -       18446744073709551615n => Map(4) {
 33          +       18446744073709552000 => Map(4) {
 34        */
 35        "TestGenerateVectorsShopOkay/biggest_item_ID",
 36        "TestGenerateVectorsShopOkay/add-order",
 37        "TestGenerateVectorsShopOkay/add-order2",
 38        "TestGenerateVectorsShopOkay/biggest_order_ID",
 39        "TestGenerateVectorsShopOkay/biggest_item_ID#01",
 40  
 41        "TestGenerateVectorsManifestOkay/remove_a_payee", // this removes the last payee from the manifest. The stateManager leaves an empty Map, while the tests do not.
 42        "TestGenerateVectorsManifestOkay/remove_a_shipping_region", // same
 43        "TestGenerateVectorsListingOkay/remove_an_image", // same but with an array
 44        "TestGenerateVectorsListingOkay/replace_expectedInStockBy", // the tests vectors automatically remove an instock field
 45        "TestGenerateVectorsListingOkay/remove_stock_status", // removing the last item of an array
 46        "TestGenerateVectorsListingOkay/remove_an_option", // removing the last item from a map
 47      ],
 48    );
 49  
 50    for (const testFile of testFiles) {
 51      await t.step(testFile, async (tt) => {
 52        const rawTestVector = await fetchAndDecode(testFile) as Map<
 53          string,
 54          unknown
 55        >;
 56        const snapShots = rawTestVector.get("Snapshots") as Map<
 57          string,
 58          Map<string, CodecValue>
 59        >[];
 60        const rawPatchSet = rawTestVector.get("PatchSet") as Map<
 61          string,
 62          unknown
 63        >;
 64        const id = (rawPatchSet.get("Header") as Map<string, unknown>).get(
 65          "ShopID",
 66        ) as bigint;
 67        const testVectors = createtestvectors(
 68          snapShots,
 69          rawPatchSet,
 70        );
 71        let passing = true;
 72        for (const test of testVectors) {
 73          if (passing && !skippedTest.has(test.description)) {
 74            passing = await tt.step(test.description, async () => {
 75              const store = new MemStore();
 76              const before = test.snapshot.before;
 77              const after = test.snapshot.after;
 78              const sm = new StateManager({
 79                store,
 80                id,
 81                defaultState: before,
 82              });
 83              await sm.open();
 84              const stream = sm.createWriteStream();
 85              const writer = stream.getWriter();
 86              await writer.write(test.patchSet);
 87              await writer.close();
 88              if (skippedAfterCheck.has(test.description)) {
 89                console.log("Skipping state check!!!!!!!");
 90              } else {
 91                const r = await sm.root;
 92                assertEquals(r, after);
 93              }
 94              await sm.close();
 95            });
 96          }
 97        }
 98      });
 99    }
100  });
101  
102  function createtestvectors(
103    snapShots: Map<string, CodecValue>[],
104    patchSet: Map<string, unknown>,
105  ): {
106    description: string;
107    snapshot: {
108      before: CodecValue;
109      after: CodecValue;
110    };
111    patchSet: PushedPatchSet;
112  }[] {
113    const header = patchSet.get("Header") as CodecValue;
114  
115    function fixListings(vector: Map<string, CodecValue>) {
116      ["Orders", "Listings", "Accounts", "Inventory"].forEach((key) => {
117        const val = extractEntriesFromHAMT(
118          vector.get(key),
119        ) as CodecValue;
120        vector.set(key, val);
121      });
122      return vector;
123    }
124  
125    return (patchSet.get("Patches") as Map<string, unknown>[]).map(
126      (patch, i) => {
127        const snapshot = snapShots[i] as Map<
128          string,
129          unknown
130        >;
131        const [before, after] = ["Before", "After"].map((key) => {
132          const a = snapshot.get(key)! as Map<string, Map<string, CodecValue>>;
133          const val = a.get("Value")!;
134          return fixListings(val);
135        });
136  
137        return {
138          description: snapshot!.get("Name") as string,
139          snapshot: {
140            before,
141            after,
142          },
143          patchSet: {
144            patches: [{
145              Op: patch.get("Op") as "add" | "remove" | "replace",
146              Path: patch.get("Path") as Path,
147              Value: patch.get("Value") as CodecValue,
148            }],
149            header,
150            signer: "0xdeafbeef",
151            sequence: 0,
152          },
153        };
154      },
155    );
156  }