iterator.test.ts
1 import t, { type Test } from "tap"; 2 import * as u8 from "uint8arrays"; 3 4 import { IteratorStreamSearch } from "./iterator.js"; 5 import { MATCH } from "./search.js"; 6 7 t.test("iterator error handling", async (t: Test): Promise<void> => { 8 t.test( 9 "should handle iterator with throw method", 10 async (t: Test): Promise<void> => { 11 const error = new Error("test error"); 12 let throwCalled = false; 13 14 async function* gen() { 15 yield u8.fromString("hello"); 16 throw error; 17 } 18 19 const iter = gen(); 20 const originalThrow = iter.throw; 21 iter.throw = async function (e) { 22 throwCalled = true; 23 return originalThrow?.call(this, e) || { done: true, value: undefined }; 24 }; 25 26 const search = new IteratorStreamSearch("world", iter); 27 28 try { 29 for await (const _ of search) { 30 // consume iterator 31 } 32 t.fail("should have thrown"); 33 } catch (e) { 34 t.equal(e, error); 35 t.ok(throwCalled, "throw method should be called"); 36 } 37 38 t.end(); 39 }, 40 ); 41 42 t.test( 43 "should handle iterator throw returning done result and break", 44 async (t: Test): Promise<void> => { 45 const error = new Error("test error"); 46 let throwWasCalled = false; 47 48 async function* gen() { 49 yield u8.fromString("hello"); 50 throw error; 51 } 52 53 const iter = gen(); 54 iter.throw = async function () { 55 throwWasCalled = true; 56 return { done: true, value: undefined }; 57 }; 58 59 const search = new IteratorStreamSearch("world", iter); 60 61 const results: Uint8Array[] = []; 62 for await (const token of search) { 63 if (token !== MATCH) { 64 results.push(token); 65 } 66 } 67 68 t.ok(throwWasCalled, "throw method should be called"); 69 t.equal(results.length, 1); 70 t.equal(u8.toString(results[0]), "hello"); 71 72 t.end(); 73 }, 74 ); 75 76 t.test( 77 "should handle iterator throw returning not done and continue", 78 async (t: Test): Promise<void> => { 79 const error = new Error("test error"); 80 let throwWasCalled = false; 81 82 async function* gen() { 83 yield u8.fromString("hello"); 84 throw error; 85 } 86 87 const iter = gen(); 88 iter.throw = async function () { 89 throwWasCalled = true; 90 return { done: false, value: u8.fromString("recovered") }; 91 }; 92 93 const search = new IteratorStreamSearch("world", iter); 94 95 const results: Uint8Array[] = []; 96 for await (const token of search) { 97 if (token !== MATCH) { 98 results.push(token); 99 } 100 } 101 102 t.ok(throwWasCalled, "throw method should be called"); 103 t.ok(results.length >= 2, "should have at least 2 results"); 104 t.equal(u8.toString(results[0]), "hello"); 105 t.equal(u8.toString(results[1]), "recovered"); 106 107 t.end(); 108 }, 109 ); 110 111 t.test( 112 "should rethrow error when iterator has no throw method", 113 async (t: Test): Promise<void> => { 114 const error = new Error("test error"); 115 let errorThrown = false; 116 117 const asyncIterable = { 118 [Symbol.asyncIterator]() { 119 let count = 0; 120 return { 121 async next() { 122 if (count === 0) { 123 count++; 124 return { done: false, value: u8.fromString("hello") }; 125 } 126 throw error; 127 }, 128 }; 129 }, 130 }; 131 132 const search = new IteratorStreamSearch("world", asyncIterable); 133 134 try { 135 for await (const _ of search) { 136 // consume iterator 137 } 138 t.fail("should have thrown"); 139 } catch (e) { 140 errorThrown = true; 141 t.equal(e, error); 142 } 143 144 t.ok(errorThrown, "error should be thrown"); 145 146 t.end(); 147 }, 148 ); 149 150 t.end(); 151 });