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 }