1import { Chunk, Effect, TxChunk } from "effect"
2import { describe, expect, it } from "vitest"
3
4describe("TxChunk", () => {
5 describe("constructors", () => {
6 it("make should create a TxChunk with initial chunk", async () => {
7 const program = Effect.tx(Effect.gen(function*() {
8 const chunk = Chunk.fromIterable([1, 2, 3])
9 const txChunk = yield* TxChunk.make(chunk)
10 const result = yield* TxChunk.get(txChunk)
11 return result
12 }))
13
14 const result = await Effect.runPromise(program)
15 expect(Chunk.toReadonlyArray(result)).toEqual([1, 2, 3])
16 })
17
18 it("empty should create an empty TxChunk", async () => {
19 const program = Effect.tx(Effect.gen(function*() {
20 const txChunk = yield* TxChunk.empty<number>()
21 const result = yield* TxChunk.get(txChunk)
22 return result
23 }))
24
25 const result = await Effect.runPromise(program)
26 expect(Chunk.toReadonlyArray(result)).toEqual([])
27 })
28
29 it("fromIterable should create a TxChunk from an iterable", async () => {
30 const program = Effect.tx(Effect.gen(function*() {
31 const txChunk = yield* TxChunk.fromIterable([4, 5, 6])
32 const result = yield* TxChunk.get(txChunk)
33 return result
34 }))
35
36 const result = await Effect.runPromise(program)
37 expect(Chunk.toReadonlyArray(result)).toEqual([4, 5, 6])
38 })
39 })
40
41 describe("basic operations", () => {
42 it("get should read the current chunk", async () => {
43 const program = Effect.tx(Effect.gen(function*() {
44 const txChunk = yield* TxChunk.fromIterable([1, 2, 3])
45 const result = yield* TxChunk.get(txChunk)
46 return result
47 }))
48
49 const result = await Effect.runPromise(program)
50 expect(Chunk.toReadonlyArray(result)).toEqual([1, 2, 3])
51 })
52
53 it("set should replace the entire chunk", async () => {
54 const program = Effect.tx(Effect.gen(function*() {
55 const txChunk = yield* TxChunk.fromIterable([1, 2, 3])
56 const newChunk = Chunk.fromIterable([7, 8, 9])
57
58 yield* TxChunk.set(txChunk, newChunk)
59
60 const result = yield* TxChunk.get(txChunk)
61 return result
62 }))
63
64 const result = await Effect.runPromise(program)
65 expect(Chunk.toReadonlyArray(result)).toEqual([7, 8, 9])
66 })
67
68 it("append should add element to the end", async () => {
69 const program = Effect.tx(Effect.gen(function*() {
70 const txChunk = yield* TxChunk.fromIterable([1, 2, 3])
71
72 yield* TxChunk.append(txChunk, 4)
73
74 const result = yield* TxChunk.get(txChunk)
75 return result
76 }))
77
78 const result = await Effect.runPromise(program)
79 expect(Chunk.toReadonlyArray(result)).toEqual([1, 2, 3, 4])
80 })
81
82 it("prepend should add element to the beginning", async () => {
83 const program = Effect.tx(Effect.gen(function*() {
84 const txChunk = yield* TxChunk.fromIterable([2, 3, 4])
85
86 yield* TxChunk.prepend(txChunk, 1)
87
88 const result = yield* TxChunk.get(txChunk)
89 return result
90 }))
91
92 const result = await Effect.runPromise(program)
93 expect(Chunk.toReadonlyArray(result)).toEqual([1, 2, 3, 4])
94 })
95
96 it("size should return the chunk size", async () => {
97 const program = Effect.tx(Effect.gen(function*() {
98 const txChunk = yield* TxChunk.fromIterable([1, 2, 3])
99 const size = yield* TxChunk.size(txChunk)
100 return size
101 }))
102
103 const result = await Effect.runPromise(program)
104 expect(result).toBe(3)
105 })
106
107 it("isEmpty should return true for empty chunk", async () => {
108 const program = Effect.tx(Effect.gen(function*() {
109 const txChunk = yield* TxChunk.empty<number>()
110 const isEmpty = yield* TxChunk.isEmpty(txChunk)
111 return isEmpty
112 }))
113
114 const result = await Effect.runPromise(program)
115 expect(result).toBe(true)
116 })
117
118 it("isEmpty should return false for non-empty chunk", async () => {
119 const program = Effect.tx(Effect.gen(function*() {
120 const txChunk = yield* TxChunk.fromIterable([1, 2, 3])
121 const isEmpty = yield* TxChunk.isEmpty(txChunk)
122 return isEmpty
123 }))
124
125 const result = await Effect.runPromise(program)
126 expect(result).toBe(false)
127 })
128
129 it("isNonEmpty should return false for empty chunk", async () => {
130 const program = Effect.tx(Effect.gen(function*() {
131 const txChunk = yield* TxChunk.empty<number>()
132 const isNonEmpty = yield* TxChunk.isNonEmpty(txChunk)
133 return isNonEmpty
134 }))
135
136 const result = await Effect.runPromise(program)
137 expect(result).toBe(false)
138 })
139
140 it("isNonEmpty should return true for non-empty chunk", async () => {
141 const program = Effect.tx(Effect.gen(function*() {
142 const txChunk = yield* TxChunk.fromIterable([1, 2, 3])
143 const isNonEmpty = yield* TxChunk.isNonEmpty(txChunk)
144 return isNonEmpty
145 }))
146
147 const result = await Effect.runPromise(program)
148 expect(result).toBe(true)
149 })
150 })
151
152 describe("transactional semantics", () => {
153 it("should perform atomic operations within a transaction", async () => {
154 const program = Effect.tx(Effect.gen(function*() {
155 const txChunk = yield* TxChunk.fromIterable([1, 2, 3])
156
157 yield* Effect.gen(function*() {
158 yield* TxChunk.append(txChunk, 4)
159 yield* TxChunk.prepend(txChunk, 0)
160 })
161
162 const result = yield* TxChunk.get(txChunk)
163 return result
164 }))
165
166 const result = await Effect.runPromise(program)
167 expect(Chunk.toReadonlyArray(result)).toEqual([0, 1, 2, 3, 4])
168 })
169
170 it("modify should transform and return a value atomically", async () => {
171 const program = Effect.tx(Effect.gen(function*() {
172 const txChunk = yield* TxChunk.fromIterable([1, 2, 3])
173
174 const oldSize = yield* TxChunk.modify(txChunk, (chunk) => [
175 Chunk.size(chunk),
176 Chunk.append(chunk, 4)
177 ])
178
179 const newChunk = yield* TxChunk.get(txChunk)
180 return { oldSize, newChunk }
181 }))
182
183 const result = await Effect.runPromise(program)
184 expect(result.oldSize).toBe(3)
185 expect(Chunk.toReadonlyArray(result.newChunk)).toEqual([1, 2, 3, 4])
186 })
187
188 it("update should transform the chunk", async () => {
189 const program = Effect.tx(Effect.gen(function*() {
190 const txChunk = yield* TxChunk.fromIterable([1, 2, 3])
191
192 yield* TxChunk.update(txChunk, (chunk) => Chunk.map(chunk, (x) => x * 2))
193
194 const result = yield* TxChunk.get(txChunk)
195 return result
196 }))
197
198 const result = await Effect.runPromise(program)
199 expect(Chunk.toReadonlyArray(result)).toEqual([2, 4, 6])
200 })
201 })
202
203 describe("slice operations", () => {
204 it("take should keep first n elements", async () => {
205 const program = Effect.tx(Effect.gen(function*() {
206 const txChunk = yield* TxChunk.fromIterable([1, 2, 3, 4, 5])
207
208 yield* TxChunk.take(txChunk, 3)
209
210 const result = yield* TxChunk.get(txChunk)
211 return result
212 }))
213
214 const result = await Effect.runPromise(program)
215 expect(Chunk.toReadonlyArray(result)).toEqual([1, 2, 3])
216 })
217
218 it("drop should remove first n elements", async () => {
219 const program = Effect.tx(Effect.gen(function*() {
220 const txChunk = yield* TxChunk.fromIterable([1, 2, 3, 4, 5])
221
222 yield* TxChunk.drop(txChunk, 2)
223
224 const result = yield* TxChunk.get(txChunk)
225 return result
226 }))
227
228 const result = await Effect.runPromise(program)
229 expect(Chunk.toReadonlyArray(result)).toEqual([3, 4, 5])
230 })
231
232 it("slice should extract elements from start to end", async () => {
233 const program = Effect.tx(Effect.gen(function*() {
234 const txChunk = yield* TxChunk.fromIterable([1, 2, 3, 4, 5])
235
236 yield* TxChunk.slice(txChunk, 1, 4)
237
238 const result = yield* TxChunk.get(txChunk)
239 return result
240 }))
241
242 const result = await Effect.runPromise(program)
243 expect(Chunk.toReadonlyArray(result)).toEqual([2, 3, 4])
244 })
245 })
246
247 describe("transform operations", () => {
248 it("map should transform each element", async () => {
249 const program = Effect.tx(Effect.gen(function*() {
250 const txChunk = yield* TxChunk.fromIterable([1, 2, 3])
251
252 yield* TxChunk.map(txChunk, (x) => x * 2)
253
254 const result = yield* TxChunk.get(txChunk)
255 return result
256 }))
257
258 const result = await Effect.runPromise(program)
259 expect(Chunk.toReadonlyArray(result)).toEqual([2, 4, 6])
260 })
261
262 it("filter should keep elements that satisfy predicate", async () => {
263 const program = Effect.tx(Effect.gen(function*() {
264 const txChunk = yield* TxChunk.fromIterable([1, 2, 3, 4, 5])
265
266 yield* TxChunk.filter(txChunk, (x) => x % 2 === 0)
267
268 const result = yield* TxChunk.get(txChunk)
269 return result
270 }))
271
272 const result = await Effect.runPromise(program)
273 expect(Chunk.toReadonlyArray(result)).toEqual([2, 4])
274 })
275 })
276
277 describe("concatenation operations", () => {
278 it("appendAll should concatenate chunk to the end", async () => {
279 const program = Effect.tx(Effect.gen(function*() {
280 const txChunk = yield* TxChunk.fromIterable([1, 2, 3])
281 const other = Chunk.fromIterable([4, 5, 6])
282
283 yield* TxChunk.appendAll(txChunk, other)
284
285 const result = yield* TxChunk.get(txChunk)
286 return result
287 }))
288
289 const result = await Effect.runPromise(program)
290 expect(Chunk.toReadonlyArray(result)).toEqual([1, 2, 3, 4, 5, 6])
291 })
292
293 it("prependAll should concatenate chunk to the beginning", async () => {
294 const program = Effect.tx(Effect.gen(function*() {
295 const txChunk = yield* TxChunk.fromIterable([4, 5, 6])
296 const other = Chunk.fromIterable([1, 2, 3])
297
298 yield* TxChunk.prependAll(txChunk, other)
299
300 const result = yield* TxChunk.get(txChunk)
301 return result
302 }))
303
304 const result = await Effect.runPromise(program)
305 expect(Chunk.toReadonlyArray(result)).toEqual([1, 2, 3, 4, 5, 6])
306 })
307
308 it("concat should concatenate another TxChunk to the end", async () => {
309 const program = Effect.tx(Effect.gen(function*() {
310 const txChunk1 = yield* TxChunk.fromIterable([1, 2, 3])
311 const txChunk2 = yield* TxChunk.fromIterable([4, 5, 6])
312
313 yield* TxChunk.concat(txChunk1, txChunk2)
314
315 const result = yield* TxChunk.get(txChunk1)
316 return result
317 }))
318
319 const result = await Effect.runPromise(program)
320 expect(Chunk.toReadonlyArray(result)).toEqual([1, 2, 3, 4, 5, 6])
321 })
322
323 it("concat should work atomically within transactions", async () => {
324 const program = Effect.tx(Effect.gen(function*() {
325 const txChunk1 = yield* TxChunk.fromIterable([1, 2])
326 const txChunk2 = yield* TxChunk.fromIterable([3, 4])
327 const txChunk3 = yield* TxChunk.fromIterable([5, 6])
328
329 yield* Effect.gen(function*() {
330 yield* TxChunk.concat(txChunk1, txChunk2)
331 yield* TxChunk.concat(txChunk1, txChunk3)
332 })
333
334 const result = yield* TxChunk.get(txChunk1)
335 return result
336 }))
337
338 const result = await Effect.runPromise(program)
339 expect(Chunk.toReadonlyArray(result)).toEqual([1, 2, 3, 4, 5, 6])
340 })
341 })
342
343 describe("guards", () => {
344 it("isTxChunk should identify TxChunk instances", async () => {
345 const program = Effect.tx(Effect.gen(function*() {
346 const txChunk = yield* TxChunk.fromIterable([1, 2, 3])
347 return [
348 TxChunk.isTxChunk(txChunk),
349 TxChunk.isTxChunk({}),
350 TxChunk.isTxChunk(null),
351 TxChunk.isTxChunk(undefined),
352 TxChunk.isTxChunk([1, 2, 3]),
353 TxChunk.isTxChunk(Chunk.fromIterable([1, 2, 3]))
354 ]
355 }))
356
357 const result = await Effect.runPromise(program)
358 expect(result).toEqual([true, false, false, false, false, false])
359 })
360 })
361})