/ src / iterator.test.ts
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  });