it('executorLzReceiveOption', async () => {
        // sanity check that generated options are the same with value as 0 and not provided.
        expect(Options.newOptions().addExecutorLzReceiveOption(1, 0)).toEqual(
            Options.newOptions().addExecutorLzReceiveOption(1)
        )
        await fc.assert(
            fc.asyncProperty(
                omnicounterIncrementTypeArbitrary,
                gasLimitArbitrary,
                nativeDropArbitrary,

                // Test the generation and submission of arbitrary LZ_RECEIVE Options.  The transaction should succeed,
                // and the options from the transaction receipt logs should match the generated input.
                async (type, gasLimit, nativeDrop) => {
                    const options = Options.newOptions().addExecutorLzReceiveOption(gasLimit, nativeDrop)
                    const packetSentEvents = await incrementAndReturnLogs(type, options)
                    expect(packetSentEvents).toEqual([
                        expect.objectContaining({
                            args: expect.objectContaining({
                                options: expect.toEqualCaseInsensitive(options.toHex()),
                            }),
                        }),
                    ])
                    const rawPacketOptions = packetSentEvents[0]!.args.options
                    expect(rawPacketOptions).toEqualCaseInsensitive(options.toHex())

                    // test decoding
                    const packetOptions = Options.fromOptions(rawPacketOptions)
                    const decodedExecutorLzReceiveOption = packetOptions.decodeExecutorLzReceiveOption()
                    expect(decodedExecutorLzReceiveOption).toEqual({
                        gas: gasLimit,
                        value: nativeDrop,
                    })
                    expect(packetOptions.decodeExecutorNativeDropOption()).toEqual([])
                    expect(packetOptions.decodeExecutorComposeOption()).toEqual([])
                    expect(packetOptions.decodeExecutorOrderedExecutionOption()).toEqual(false)
                }
            ),
            { numRuns: 20 }
        )
    })

Summary: Generated `executorLzReceiveOption` values should yield identical options when value is set to 0 or omitted, and for arbitrary LZ_RECEIVE options, transaction logs must match generated inputs, with correct decoding results for fields like gas and nativeDrop.


unknown

it('executorLzNativeDrop', async () => {
        const address = await ethSigner.signer.getAddress()
        await fc.assert(
            fc.asyncProperty(
                omnicounterIncrementTypeArbitrary,
                nativeDropArbitrary,

                // Test the generation and submission of arbitrary NATIVE_DROP Options.  The transaction should succeed,
                // and the options from the transaction receipt logs should match the generated input.
                async (type, nativeDrop) => {
                    const options = Options.newOptions()
                        .addExecutorNativeDropOption(nativeDrop, address)
                        // We also need to add a lzReceive option to avoid Executor_ZeroLzReceiveGasProvided error
                        .addExecutorLzReceiveOption(MIN_GAS_LIMIT)
                    const packetSentEvents = await incrementAndReturnLogs(type, options)
                    expect(packetSentEvents).toEqual([
                        expect.objectContaining({
                            args: expect.objectContaining({
                                options: expect.toEqualCaseInsensitive(options.toHex()),
                            }),
                        }),
                    ])
                    const rawPacketOptions = packetSentEvents[0]!.args.options
                    expect(rawPacketOptions).toEqualCaseInsensitive(options.toHex())

                    // test decoding
                    const packetOptions = Options.fromOptions(rawPacketOptions)
                    const decodedExecutorNativeDropOptions = packetOptions.decodeExecutorNativeDropOption()
                    expect(decodedExecutorNativeDropOptions).toEqual([
                        {
                            amount: nativeDrop,
                            receiver: expect.toEqualCaseInsensitive(makeBytes32(address)),
                        },
                    ])
                    expect(packetOptions.decodeExecutorLzReceiveOption()).toEqual({
                        gas: MIN_GAS_LIMIT,
                        value: BigInt(0),
                    })
                    expect(packetOptions.decodeExecutorComposeOption()).toEqual([])
                    expect(packetOptions.decodeExecutorOrderedExecutionOption()).toEqual(false)
                }
            ),
            { numRuns: 20 }
        )
    })

Summary: `executorLzNativeDrop` verifies that generated options with `NATIVE_DROP` and `lzReceive` produce transaction receipts with matching logs, ensuring correct encoding/decoding of options and expected execution outcomes.


it('executorOrderedExecutionOption', async () => {
        await fc.assert(
            fc.asyncProperty(
                omnicounterIncrementTypeArbitrary,

                // Test the generation and submission of arbitrary ORDERED Options.  The transaction should succeed, and the
                // options from the transaction receipt logs should match the generated input.
                async (type) => {
                    const options = Options.newOptions()
                        .addExecutorOrderedExecutionOption()
                        // We also need to add a lzReceive option to avoid Executor_ZeroLzReceiveGasProvided error
                        .addExecutorLzReceiveOption(MIN_GAS_LIMIT)
                    const packetSentEvents = await incrementAndReturnLogs(type, options)
                    expect(packetSentEvents).toHaveLength(1)
                    const rawPacketOptions = packetSentEvents[0]!.args.options.toLowerCase()
                    expect(rawPacketOptions).toBe(options.toHex().toLowerCase())

                    // test decoding
                    const packetOptions = Options.fromOptions(rawPacketOptions)
                    expect(packetOptions.decodeExecutorOrderedExecutionOption()).toBe(true)
                    expect(packetOptions.decodeExecutorLzReceiveOption()).toEqual({
                        gas: MIN_GAS_LIMIT,
                        value: BigInt(0),
                    })
                    expect(packetOptions.decodeExecutorComposeOption()).toEqual([])
                    expect(packetOptions.decodeExecutorNativeDropOption()).toEqual([])
                }
            ),
            { numRuns: 10 }
        )
    })

Summary: The function verifies that generating and submitting `ORDERED` options for execution in an omnicounter transaction produces expected logs, correctly decodes options, and ensures transaction success without errors, such as `Executor_ZeroLzReceiveGasProvided`.


it('stacked options', async () => {
        // custom gasLimit arbitrary so "stacked" compose options won't have a gasLimit sum that overflows MAX_UINT_128
        const stackedGasLimitArbitrary: fc.Arbitrary<bigint> = fc.bigInt({
            min: MIN_GAS_LIMIT,
            max: BigInt('0xFFFFFFFFFFFFFFFF'),
        })

        // custom nativeDrop arbitrary so "stacked" compose options won't have a nativeDrop sum that exceeds DEFAULT_MAX_NATIVE_DROP
        const stackedValueArbitrary: fc.Arbitrary<bigint> = fc.bigInt({
            min: DEFAULT_MIN_NATIVE_DROP,
            max: DEFAULT_MAX_NATIVE_DROP / BigInt(4),
        })

        const address = await ethSigner.signer.getAddress()
        await fc.assert(
            fc.asyncProperty(
                omnicounterIncrementTypeArbitrary,
                composedIndexArbitrary,
                stackedGasLimitArbitrary,
                stackedValueArbitrary,

                // Test the generation of multiple Options in a single Packet.  The transaction should succeed.  Options
                // should be decoded to match inputs.  gasLimit and nativeDrop should be summed for Packets that have
                // multiple COMPOSE options for the same index.
                async (type, index, gasLimit, stackedValue) => {
                    const options = Options.newOptions()
                        .addExecutorComposeOption(index, gasLimit, stackedValue)
                        .addExecutorLzReceiveOption(gasLimit, stackedValue)
                        .addExecutorNativeDropOption(stackedValue, address)
                        .addExecutorComposeOption(index, gasLimit, stackedValue) // Repeat executor compose option to make sure values/gasLimits are summed

                    const packetSentEvents = await incrementAndReturnLogs(type, options)
                    expect(packetSentEvents).toEqual([
                        expect.objectContaining({
                            args: expect.objectContaining({
                                options: expect.toEqualCaseInsensitive(options.toHex()),
                            }),
                        }),
                    ])
                    const packetOptions = Options.fromOptions(packetSentEvents[0]!.args.options)

                    // check executorComposeOption
                    const packetComposeOptions = packetOptions.decodeExecutorComposeOption()
                    expect(packetComposeOptions).toEqual([
                        {
                            index,
                            gas: gasLimit * BigInt(2),
                            // compose options with same index are summed (in this specific case, just multiplied by 2)
                            value: stackedValue * BigInt(2),
                        },
                    ])

                    // check executorLzReceiveOption
                    const packetLzReceiveOption = packetOptions.decodeExecutorLzReceiveOption()
                    expect(packetLzReceiveOption).toEqual({
                        gas: gasLimit,
                        value: stackedValue,
                    })

                    // check executorNativeDropOption
                    const packetNativeDropOptions = packetOptions.decodeExecutorNativeDropOption()
                    expect(packetNativeDropOptions).toEqual([
                        {
                            amount: stackedValue,
                            receiver: expect.toEqualCaseInsensitive(makeBytes32(address)),
                        },
                    ])
                }
            )
        )
    })

Summary: Multiple options in a single packet should decode to match the inputs, with `gasLimit` and `nativeDrop` values correctly summed for packets with repeated compose options.


it('child no arg event', async () => {
        const receipt = await (await child.emitNoArgEvent()).wait()
        expect(parseLogs(receipt, parent)).toMatchSnapshot()
        expect(parseLogsWithName(receipt, parent, 'NoArgEvent')).toEqual([]) // only the child should emit the event
        expect(parseLogsWithName(receipt, child, 'NoArgEvent')).toHaveLength(1)
        expect(parseLogsWithName(receipt, parallel, 'NoArgEvent')).toEqual([]) // parallel logs should be empty

        fc.assert(
            fc.property(fc.string(), (name) => {
                fc.pre(name !== 'NoArgEvent')
                expect(parseLogsWithName(receipt, parent, name)).toEqual([])
            })
        )
    })

Summary: Emitting a 'NoArgEvent' from a child contract should only be detected in the child's logs, not in the parent or parallel logs, and parsing logs with a different name should result in empty arrays.


it('not parse an event with one arg from a different contract', async () => {
        await fc.assert(
            fc.asyncProperty(fc.integer({ min: 0 }), async (arg) => {
                const receipt = await (await child.emitOneArgEvent(arg)).wait()
                expect(parseLogsWithName(receipt, parent, 'OneArgEvent')).toEqual([])
            }),
            { numRuns: 10 }
        )
    })

Summary: Ensures that `parseLogsWithName` returns an empty array when attempting to parse an event with one argument emitted by a child contract, rather than the parent contract.


it('not parse an event with one arg with unknown name', async () => {
        await fc.assert(
            fc.asyncProperty(fc.integer({ min: 0 }), fc.string(), async (arg, name) => {
                fc.pre(name !== 'OneArgEvent')

                const receipt = await (await child.emitOneArgEvent(arg)).wait()
                expect(parseLogsWithName(receipt, parent, name)).toEqual([])
            }),
            { numRuns: 10 }
        )
    })

Summary: `parseLogsWithName` should return an empty array when attempting to parse an event with one argument using an unknown event name.


it('not parse an event with many args from a different contract', async () => {
        const eventNameArbitrary = fc.constantFrom('NoArgEvent', 'OneArgEvent', 'FourArgEvent')

        await fc.assert(
            fc.asyncProperty(fc.integer({ min: 0, max: 10 }), eventNameArbitrary, async (count, name) => {
                const receipt = await (await child.emitMany(count)).wait()

                expect(parseLogsWithName(receipt, child, name)).toHaveLength(count)
                expect(parseLogsWithName(receipt, parent, name)).toEqual([])
            }),
            { numRuns: 10 }
        )
    })

Summary: Parsing logs should detect events emitted from a specific contract and not from a different contract, even if the events have many arguments.


it('should pass an error through if it already is a ContractError', async () => {
            await fc.assert(
                fc.asyncProperty(endpointArbitrary, async (eid) => {
                    const errorParser = await createContractErrorParser({ contract, eid })
                    const error = new RevertError('A reason is worth a million bytes')
                    const parsedError = await errorParser(error)

                    expect(parsedError).toBe(error)
                })
            )
        })

Summary: A `ContractError` should remain unchanged when passed through the error parser.


it('should wait the end of act before moving to the next task', async () =>
      fc.assert(
        fc.asyncProperty(fc.infiniteStream(fc.nat()), async (seeds) => {
          // Arrange
          let locked = false;
          const updateLocked = (newLocked: boolean) => (locked = newLocked);
          const act = vi.fn().mockImplementation(async (f) => {
            expect(locked).toBe(false);
            updateLocked(true); // equivalent to: locked = true
            await f();
            updateLocked(false); // equivalent to: locked = false
          });
          const nextTaskIndex = buildSeededNextTaskIndex(seeds);
          const taskSelector: TaskSelector<unknown> = { clone: vi.fn(), nextTaskIndex };

          // Act
          const s = new SchedulerImplem(act, taskSelector);
          s.schedule(Promise.resolve(1));
          s.schedule(Promise.resolve(8));
          s.schedule(Promise.resolve(42));

          // Assert
          await s.waitAll();
          expect(locked).toBe(false);
        }),
      ))

Summary: A scheduler waits for the current task's execution to finish completely before moving to the next scheduled task.


it('should parse assert/panic', async () => {
            await fc.assert(
                fc.asyncProperty(endpointArbitrary, async (eid) => {
                    const errorParser = await createContractErrorParser({ contract, eid })
                    const error = await assertFailed(contract.throwWithAssert())
                    const parsedError = await errorParser(error)

                    expect(parsedError).toEqual(new PanicError(BigInt(1)))
                })
            )
        })

Summary: Parses assertion failures to verify they result in a `PanicError` with code `1`.


it('should parse assert/panic with code', async () => {
            await fc.assert(
                fc.asyncProperty(endpointArbitrary, async (eid) => {
                    const errorParser = await createContractErrorParser({ contract, eid })
                    const error = await assertFailed(contract.throwWithAssertWithCode())
                    const parsedError = await errorParser(error)

                    expect(parsedError).toEqual(new PanicError(BigInt(18)))
                })
            )
        })

Summary: The parsing of an assert or panic error should produce a `PanicError` with a specific code when invoked through a contract function.


it('should parse require with an argument', async () => {
            await fc.assert(
                fc.asyncProperty(endpointArbitrary, async (eid) => {
                    const errorParser = await createContractErrorParser({ contract, eid })
                    const error = await assertFailed(contract.throwWithRequireAndArgument('my bad'))
                    const parsedError = await errorParser(error)

                    expect(parsedError).toEqual(new RevertError('my bad'))
                })
            )
        })

Summary: Parsing a require statement with an argument should generate a `RevertError` with the specified message.


it('should end whenever possible while never launching multiple tasks at the same time', async () => {
      const schedulingTypeArb = fc.constantFrom(...(['none', 'init'] as const));
      const dependenciesArbFor = (currentItem: number) =>
        fc.uniqueArray(fc.nat({ max: currentItem - 2 }), { maxLength: currentItem - 1 });
      const buildAndAddScheduled = (
        s: SchedulerImplem<unknown>,
        unscheduled: Promise<unknown>,
        allPs: Promise<unknown>[],
        dependencies: number[],
        schedulingType: 'none' | 'init',
      ) => {
        const label = `p${allPs.length + 1}:${JSON.stringify(dependencies)}`;
        const deps = dependencies.map((id) => allPs[id]);
        let self: Promise<unknown>;
        if (schedulingType === 'init') {
          if (deps.length === 0) {
            self = s.schedule(unscheduled, label);
          } else {
            self = Promise.all(deps).then(() => s.schedule(unscheduled, label));
          }
        } else {
          self = deps.length !== 0 ? Promise.all([...deps, unscheduled]) : unscheduled;
        }
        allPs.push(self);
      };
      const scheduleResolution = (
        wrappingScheduler: fc.Scheduler<unknown>,
        p: ReturnType<typeof buildUnresolved>,
        pName: string,
        directResolved: boolean,
        ctx: fc.ContextValue,
      ): void => {
        if (directResolved) {
          ctx.log(`${pName} resolved (direct)`);
          p.resolve();
          return;
        }
        wrappingScheduler.schedule(Promise.resolve(`Resolve ${pName}`)).then(() => {
          ctx.log(`${pName} resolved`);
          p.resolve();
        });
      };
      await fc.assert(
        fc.asyncProperty(
          // Scheduler not being tested itself, it will be used to schedule when task will resolve
          fc.scheduler(),
          // Stream of positive integers used to tell which task should be selected by `nextTaskIndex`
          // whenever asked for the next task to be scheduled
          fc.infiniteStream(fc.nat()),
          // Define the tree of pre-requisite tasks:
          // - type of scheduling: should they be scheduled within the Schduler under test?
          // - when should they resolved: on init or when `fc.scheduler` decides it?
          // - what are the dependencies needed for this task?
          fc.tuple(schedulingTypeArb, fc.boolean()),
          fc.tuple(schedulingTypeArb, fc.boolean(), dependenciesArbFor(2)),
          fc.tuple(schedulingTypeArb, fc.boolean(), dependenciesArbFor(3)),
          fc.tuple(schedulingTypeArb, fc.boolean(), dependenciesArbFor(4)),
          fc.tuple(schedulingTypeArb, fc.boolean(), dependenciesArbFor(5)),
          fc.tuple(fc.boolean(), dependenciesArbFor(6)),
          fc.tuple(fc.boolean(), dependenciesArbFor(6)),
          fc.tuple(fc.boolean(), dependenciesArbFor(6)),
          // Extra boolean values used to add some delays between resolved Promises and next ones
          fc.infiniteStream(fc.boolean()),
          // Logger for easier troubleshooting
          fc.context(),
          async (
            wrappingScheduler,
            nextTaskIndexSeed,
            [schedulingType1, directResolve1],
            [schedulingType2, directResolve2, dependencies2],
            [schedulingType3, directResolve3, dependencies3],
            [schedulingType4, directResolve4, dependencies4],
            [schedulingType5, directResolve5, dependencies5],
            [directResolveA, finalDependenciesA],
            [directResolveB, finalDependenciesB],
            [directResolveC, finalDependenciesC],
            addExtraDelays,
            ctx,
          ) => {
            // Arrange
            const p1 = buildUnresolved();
            const p2 = buildUnresolved();
            const p3 = buildUnresolved();
            const p4 = buildUnresolved();
            const p5 = buildUnresolved();
            const rawAllPs = [p1, p2, p3, p4, p5];
            const pAwaitedA = buildUnresolved();
            const pAwaitedB = buildUnresolved();
            const pAwaitedC = buildUnresolved();
            let unknownTaskReleased = false;
            let multipleTasksReleasedAtTheSameTime: string | undefined = undefined;
            let alreadyRunningPx: typeof p1 | undefined = undefined;
            const taskSelector: TaskSelector<unknown> = {
              clone: vi.fn(),
              nextTaskIndex: (scheduledTasks) => {
                const selectedId = nextTaskIndexSeed.next().value % scheduledTasks.length;
                const selectedPx = rawAllPs.find((p) => p.p === scheduledTasks[selectedId].original);
                const newPx = `p${rawAllPs.indexOf(selectedPx!) + 1}`;
                ctx.log(`Releasing ${newPx}${selectedPx!.hasBeenResolved() ? ' (resolved)' : ''}`);

                if (
                  multipleTasksReleasedAtTheSameTime === undefined &&
                  alreadyRunningPx !== undefined &&
                  !alreadyRunningPx.hasBeenResolved()
                ) {
                  const oldPx = `p${rawAllPs.indexOf(alreadyRunningPx) + 1}`;
                  multipleTasksReleasedAtTheSameTime = `${oldPx} already running when releasing ${newPx}`;
                }
                alreadyRunningPx = selectedPx;
                unknownTaskReleased = unknownTaskReleased || alreadyRunningPx === undefined;
                return selectedId;
              },
            };
            const s = new SchedulerImplem((f) => f(), taskSelector);
            const allPs: Promise<unknown>[] = [];
            buildAndAddScheduled(s, p1.p, allPs, [], schedulingType1);
            buildAndAddScheduled(s, p2.p, allPs, dependencies2, schedulingType2);
            buildAndAddScheduled(s, p3.p, allPs, dependencies3, schedulingType3);
            buildAndAddScheduled(s, p4.p, allPs, dependencies4, schedulingType4);
            buildAndAddScheduled(s, p5.p, allPs, dependencies5, schedulingType5);
            const awaitedTaskA = Promise.all([...finalDependenciesA.map((id) => allPs[id]), pAwaitedA.p]);
            let resolvedA = false;
            s.waitFor(awaitedTaskA).then(() => {
              ctx.log(`A ended`);
              resolvedA = true;
            });
            const awaitedTaskB = Promise.all([...finalDependenciesB.map((id) => allPs[id]), pAwaitedB.p]);
            let resolvedB = false;
            s.waitFor(awaitedTaskB).then(() => {
              ctx.log(`B ended`);
              resolvedB = true;
            });
            const awaitedTaskC = Promise.all([...finalDependenciesC.map((id) => allPs[id]), pAwaitedC.p]);
            let resolvedC = false;
            s.waitFor(awaitedTaskC).then(() => {
              ctx.log(`C ended`);
              resolvedC = true;
            });

            // Act
            scheduleResolution(wrappingScheduler, p1, 'p1', directResolve1, ctx);
            scheduleResolution(wrappingScheduler, p2, 'p2', directResolve2, ctx);
            scheduleResolution(wrappingScheduler, p3, 'p3', directResolve3, ctx);
            scheduleResolution(wrappingScheduler, p4, 'p4', directResolve4, ctx);
            scheduleResolution(wrappingScheduler, p5, 'p5', directResolve5, ctx);
            scheduleResolution(wrappingScheduler, pAwaitedA, 'pAwaitedA', directResolveA, ctx);
            scheduleResolution(wrappingScheduler, pAwaitedB, 'pAwaitedB', directResolveB, ctx);
            scheduleResolution(wrappingScheduler, pAwaitedC, 'pAwaitedC', directResolveC, ctx);

            while (wrappingScheduler.count() > 0) {
              // Extra delays based on timeouts of 0ms can potentially trigger unwanted bugs: let's try to add some before waitOne.
              if (addExtraDelays.next().value) {
                await delay();
              }
              await wrappingScheduler.waitOne();
            }
            // Extra delay done after all the scheduling as wrappingScheduler only schedules triggers to resolve tasks
            // and never waits for their associated Promises to really resolve.
            await delay();

            // Assert
            // All awaited tasks should have resolved
            expect(resolvedA).toBe(true);
            expect(resolvedB).toBe(true);
            expect(resolvedC).toBe(true);
            // Only one scheduled task awaited by the scheduler at a given point in time
            expect(multipleTasksReleasedAtTheSameTime).toBe(undefined);
            // Only known tasks could be scheduled
            expect(unknownTaskReleased).toBe(false);
          },
        ),
      );
    })

Summary: The `SchedulerImplem` should ensure that all tasks are resolved whenever possible, without running multiple tasks concurrently, and only known tasks are scheduled.


it('should parse require with a custom error with no arguments', async () => {
            await fc.assert(
                fc.asyncProperty(endpointArbitrary, async (eid) => {
                    const errorParser = await createContractErrorParser({ contract, eid })
                    const error = await assertFailed(contract.throwWithCustomErrorAndNoArguments())
                    const parsedError = await errorParser(error)

                    expect(parsedError).toEqual(new CustomError('CustomErrorWithNoArguments', []))
                })
            )
        })

Summary: Parsing a custom error with no arguments should return an error object with the correct name and an empty arguments array.


it('should work for non-negative bigint strings', () => {
            fc.assert(
                fc.property(fc.bigInt({ min: BigInt(0) }), (value) => {
                    expect(UIntBigIntSchema.parse(String(value))).toBe(value)
                })
            )
        })

Summary: `UIntBigIntSchema.parse` correctly parses non-negative bigint strings back to their original bigint values.


it('should not work for negative bigint strings', () => {
            fc.assert(
                fc.property(fc.bigInt({ max: BigInt(-1) }), (value) => {
                    expect(() => UIntBigIntSchema.parse(String(value))).toThrow()
                })
            )
        })

Summary: `UIntBigIntSchema.parse` should throw an error for negative bigint strings.


it('should not work for negative integers', () => {
            fc.assert(
                fc.property(fc.integer({ max: -1 }), (value) => {
                    expect(() => UIntBigIntSchema.parse(value)).toThrow()
                })
            )
        })

Summary: `UIntBigIntSchema` should throw an error when parsing negative integers.


it('should work for non-negative integer strings', () => {
            fc.assert(
                fc.property(fc.integer({ min: 0 }), (value) => {
                    expect(UIntBigIntSchema.parse(String(value))).toBe(BigInt(value))
                })
            )
        })

Summary: `UIntBigIntSchema` correctly parses non-negative integer strings into their equivalent `BigInt` representations.


it('should not work for negative integer strings', () => {
            fc.assert(
                fc.property(fc.integer({ max: -1 }), (value) => {
                    expect(() => UIntBigIntSchema.parse(String(value))).toThrow()
                })
            )
        })

Summary: `UIntBigIntSchema.parse` throws an error when parsing negative integer strings represented as strings.


it('should throw a ZodError for invalid values', () => {
            fc.assert(
                fc.property(fc.anything(), (value) => {
                    let isValid = false

                    try {
                        isValid = UIntBigIntSchema.safeParse(value).success
                    } catch {
                        // This catch block is designed to catch errors caused by messed up standard methods
                        // like toString or toValue that fast check throws at us
                    }

                    fc.pre(!isValid)

                    // Here we expect that whatever we got, we'll not receive a SyntaxError
                    // (coming from a BigInt() constructor) but a ZodError
                    expect(() => UIntBigIntSchema.parse(value)).toThrow(ZodError)
                })
            )
        })

Summary: `UIntBigIntSchema` should throw a `ZodError` for invalid input values that fail to successfully parse.


it('should work for non-negative integers', () => {
            fc.assert(
                fc.property(fc.integer({ min: 0 }), (value) => {
                    expect(UIntNumberSchema.parse(value)).toBe(value)
                })
            )
        })

Summary: `UIntNumberSchema` accurately parses non-negative integers, returning the same value.


it('should not work for negative integers', () => {
            fc.assert(
                fc.property(fc.integer({ max: -1 }), (value) => {
                    expect(() => UIntNumberSchema.parse(value)).toThrow()
                })
            )
        })

Summary: `UIntNumberSchema.parse` throws an error when parsing negative integers.


it('should work for non-negative integer strings', () => {
            fc.assert(
                fc.property(fc.integer({ min: 0 }), (value) => {
                    expect(UIntNumberSchema.parse(String(value))).toBe(value)
                })
            )
        })

Summary: `UIntNumberSchema` correctly parses non-negative integer strings into their integer form.


it('should throw a ZodError for invalid values', () => {
            fc.assert(
                fc.property(fc.anything(), (value) => {
                    let isValid = false

                    try {
                        isValid = UIntNumberSchema.safeParse(value).success
                    } catch {
                        // This catch block is designed to catch errors caused by messed up standard methods
                        // like toString or toValue that fast check throws at us
                    }

                    fc.pre(!isValid)

                    // Here we expect that whatever we got, we'll not receive a SyntaxError
                    // (coming from a BigInt() constructor) but a ZodError
                    expect(() => UIntNumberSchema.parse(value)).toThrow(ZodError)
                })
            )
        })

Summary: `UIntNumberSchema` should throw a `ZodError` when parsing invalid values, ensuring that no `SyntaxError` is thrown during the process.


it('should work without contract name innit', () => {
            fc.assert(
                fc.property(pointArbitrary, (point) => {
                    fc.pre(!point.contractName)

                    expect(formatOmniPoint(point)).toBe(`[${point.address} @ ${formatEid(point.eid)}]`)
                })
            )
        })

Summary: The test confirms that `formatOmniPoint` correctly formats a point without a contract name as `[address @ formattedEid]`.


it('should work with contract name innit', () => {
            fc.assert(
                fc.property(pointArbitrary, (point) => {
                    fc.pre(!!point.contractName)

                    expect(formatOmniPoint(point)).toBe(
                        `[${point.address} (${point.contractName}) @ ${formatEid(point.eid)}]`
                    )
                })
            )
        })

Summary: The `formatOmniPoint` function correctly formats points that contain a `contractName` into the specified string representation.


it('should just work innit', () => {
            fc.assert(
                fc.property(vectorArbitrary, (vector) => {
                    expect(formatOmniVector(vector)).toBe(
                        `${formatOmniPoint(vector.from)} → ${formatOmniPoint(vector.to)}`
                    )
                })
            )
        })

Summary: `formatOmniVector` outputs a formatted string representation of a vector, showing its 'from' and 'to' points connected by an arrow.


it('should be true for referentially equal vector', () => {
                fc.assert(
                    fc.property(pointArbitrary, (point) => {
                        expect(arePointsEqual(point, point)).toBeTruthy()
                    })
                )
            })

Summary: `arePointsEqual` returns true for a vector that is referentially equal to itself.


it('should be true for value equal vector', () => {
                fc.assert(
                    fc.property(pointArbitrary, (point) => {
                        expect(arePointsEqual(point, { ...point })).toBeTruthy()
                    })
                )
            })

Summary: `arePointsEqual` returns true when comparing a point to itself.


it("should be false when addresses don't match", () => {
                fc.assert(
                    fc.property(pointArbitrary, addressArbitrary, (point, address) => {
                        fc.pre(point.address !== address)

                        expect(arePointsEqual(point, { ...point, address })).toBeFalsy()
                    })
                )
            })

Summary: Verifies that `arePointsEqual` returns false when comparing points with differing addresses.


it("should be false when endpoint IDs don't match", () => {
                fc.assert(
                    fc.property(pointArbitrary, endpointArbitrary, (point, eid) => {
                        fc.pre(point.eid !== eid)

                        expect(arePointsEqual(point, { ...point, eid })).toBeFalsy()
                    })
                )
            })

Summary: `arePointsEqual` should return false when the endpoint IDs of the points do not match.


it("should be false when contract names don't match", () => {
                fc.assert(
                    fc.property(pointArbitrary, nullableArbitrary(fc.string()), (point, contractName) => {
                        fc.pre(point.contractName !== contractName)

                        expect(arePointsEqual(point, { ...point, contractName })).toBeFalsy()
                    })
                )
            })

Summary: `arePointsEqual` returns false when the contract names of two points differ.


it('should be true for referentially equal vector', () => {
                fc.assert(
                    fc.property(vectorArbitrary, (vector) => {
                        expect(areVectorsEqual(vector, vector)).toBeTruthy()
                    })
                )
            })

Summary: `areVectorsEqual` returns true for referentially equal vectors.


it("should be false when from point doesn't match", () => {
                fc.assert(
                    fc.property(vectorArbitrary, pointArbitrary, (vector, from) => {
                        fc.pre(!arePointsEqual(vector.from, from))

                        expect(areVectorsEqual(vector, { ...vector, from })).toBeFalsy()
                    })
                )
            })

Summary: The property asserts that if the starting point of a vector doesn't match a given point, then the vectors are not considered equal.


it("should be false when to point doesn't match", () => {
                fc.assert(
                    fc.property(vectorArbitrary, pointArbitrary, (vector, to) => {
                        fc.pre(!arePointsEqual(vector.from, to))

                        expect(areVectorsEqual(vector, { ...vector, to })).toBeFalsy()
                    })
                )
            })

Summary: `areVectorsEqual` returns false when the `to` point of a vector does not match the expected `to` point.


it('should return true if the eids match', () => {
                fc.assert(
                    fc.property(pointArbitrary, pointArbitrary, (pointA, pointB) => {
                        expect(areSameEndpoint(pointA, { ...pointB, eid: pointA.eid })).toBeTruthy()
                    })
                )
            })

Summary: `areSameEndpoint` returns true if the `eid` properties of two points match.


it('should return false if the eids differ', () => {
                fc.assert(
                    fc.property(pointArbitrary, pointArbitrary, (pointA, pointB) => {
                        fc.pre(pointA.eid !== pointB.eid)

                        expect(areSameEndpoint(pointA, pointB)).toBeFalsy()
                    })
                )
            })

Summary: `areSameEndpoint` returns false if the `eid` values of two points differ.


it('should produce identical serialized values if the vector match', () => {
                fc.assert(
                    fc.property(pointArbitrary, (point) => {
                        expect(serializePoint(point)).toBe(serializePoint({ ...point }))
                    })
                )
            })

Summary: `serializePoint` should produce identical serialized values for identical vector inputs.


it('should produce identical serialized values if the vector match', () => {
                fc.assert(
                    fc.property(vectorArbitrary, (vector) => {
                        expect(serializeVector(vector)).toBe(serializeVector({ ...vector }))
                    })
                )
            })

Summary: Serialization produces identical values for matching vectors.


it("should produce different serialized values if the vector don't match", () => {
                fc.assert(
                    fc.property(vectorArbitrary, vectorArbitrary, (lineA, lineB) => {
                        fc.pre(!areVectorsEqual(lineA, lineB))

                        expect(serializeVector(lineA)).not.toBe(serializeVector(lineB))
                    })
                )
            })

Summary: Serializing unequal vectors produces different serialized values.


it('should return false if two points are not on the same stage', () => {
                fc.assert(
                    fc.property(endpointArbitrary, endpointArbitrary, addressArbitrary, (eid1, eid2, address) => {
                        fc.pre(endpointIdToStage(eid1) !== endpointIdToStage(eid2))

                        expect(
                            isVectorPossible({ from: { eid: eid1, address }, to: { eid: eid2, address } })
                        ).toBeFalsy()
                    })
                )
            })

Summary: The test checks that `isVectorPossible` returns false when two points have different stages as determined by their endpoint IDs.


it('should append eid to a value', () => {
            fc.assert(
                fc.property(endpointArbitrary, fc.dictionary(fc.string(), fc.anything()), (eid, value) => {
                    expect(withEid(eid)(value)).toEqual({ ...value, eid })
                })
            )
        })

Summary: Appending `eid` to a value creates an object that merges the original value with the `eid` key-value pair.


it('should return an empty array if configuration function returns null, undefined or an empty array', async () => {
            const createSdk = jest.fn()
            const configureNode = jest.fn()
            const configureNodes = createConfigureNodes<OmniGraph>(configureNode)

            await fc.assert(
                fc.asyncProperty(nodesArbitrary, async (contracts) => {
                    createSdk.mockClear()
                    configureNode.mockClear()

                    // We mock the node configurator to only return empty values
                    configureNode.mockResolvedValueOnce(null).mockResolvedValueOnce(undefined).mockResolvedValue([])

                    const graph = { contracts, connections: [] }
                    await expect(configureNodes(graph, createSdk)).resolves.toEqual([])

                    expect(createSdk).toHaveBeenCalledTimes(contracts.length)
                    expect(configureNode).toHaveBeenCalledTimes(contracts.length)
                })
            )
        })

Summary: `configureNodes` should return an empty array when `configureNode` resolves to null, undefined, or an empty array for each contract.


it('should be able to parse any nat serialized with toString(radix)', () =>
    fc.assert(
      fc.property(fc.maxSafeNat(), fc.integer({ min: 2, max: 36 }), (n, radix) => {
        // Arrange
        const stringifiedNat = n.toString(radix);

        // Act
        const out = tryParseStringifiedNat(stringifiedNat, radix);

        // Assert
        expect(out).toBe(n);
      }),
    ))

Summary: Parsing any natural number serialized with `toString(radix)` should yield the original number when using `tryParseStringifiedNat`.


it('should be able to unmap any mapped value', () =>
    fc.assert(
      fc.property(fc.maxSafeNat(), fc.constantFrom(...(['dec', 'oct', 'hex'] as const)), (n, base) => {
        // Arrange
        const stringifiedNat = natToStringifiedNatMapper([base, n]);

        // Act
        const out = natToStringifiedNatUnmapper(stringifiedNat);

        // Assert
        expect(out[0]).toBe(base);
        expect(out[1]).toBe(n);
      }),
    ))

Summary: `natToStringifiedNatUnmapper` correctly reverses the transformation performed by `natToStringifiedNatMapper` for mapped values.


it('should create an SDK and execute configurator for every node', async () => {
            // Just some random SDK factory
            const createSdk = jest.fn().mockImplementation((point: OmniPoint) => ({ sdkFor: point }))
            // And a configuration function that returns something that we can identify in the result
            const configureNode = jest
                .fn()
                .mockImplementation((node: OmniNode, sdk: unknown) => ({ point: node.point, sdk }))
            const configureNodes = createConfigureNodes<OmniGraph>(configureNode)

            await fc.assert(
                fc.asyncProperty(nodesArbitrary, async (contracts) => {
                    createSdk.mockClear()
                    configureNode.mockClear()

                    const graph = { contracts, connections: [] }
                    const transactions = await configureNodes(graph, createSdk)

                    // First we check that the transactions match what our configuration function would return
                    expect(transactions).toHaveLength(contracts.length)
                    expect(transactions).toEqual(
                        contracts.map(({ point }) => ({
                            point,
                            sdk: {
                                sdkFor: point,
                            },
                        }))
                    )

                    // Then we check that the configurations have been called properly
                    expect(createSdk).toHaveBeenCalledTimes(contracts.length)
                    expect(configureNode).toHaveBeenCalledTimes(contracts.length)

                    for (const contract of contracts) {
                        expect(createSdk).toHaveBeenCalledWith(contract.point)
                        expect(configureNode).toHaveBeenCalledWith(
                            contract,
                            expect.objectContaining({ sdkFor: contract.point }),
                            graph,
                            createSdk
                        )
                    }
                })
            )
        })

Summary: The test ensures that an SDK is created and a configurator function is executed for each node in a graph, verifying the transactions returned and that the SDK creation and configurator functions are called the expected number of times and with the correct arguments.


it('should return an empty array if configuration function returns null, undefined or an empty array', async () => {
            const createSdk = jest.fn()
            const configureEdge = jest.fn()
            const configureEdges = createConfigureEdges<OmniGraph>(configureEdge)

            await fc.assert(
                fc.asyncProperty(edgesArbitrary, async (connections) => {
                    createSdk.mockClear()
                    configureEdge.mockClear()

                    // We mock the node configurator to only return empty values
                    configureEdge.mockResolvedValueOnce(null).mockResolvedValueOnce(undefined).mockResolvedValue([])

                    const graph = { contracts: [], connections }
                    await expect(configureEdges(graph, createSdk)).resolves.toEqual([])

                    expect(createSdk).toHaveBeenCalledTimes(connections.length)
                    expect(configureEdge).toHaveBeenCalledTimes(connections.length)
                })
            )
        })

Summary: `configureEdges` should return an empty array when the configuration function yields null, undefined, or an empty array for each connection.


it('should be able to box and unbox any non-boxed value', () =>
    fc.assert(
      fc.property(fc.array(fc.anything({ withBoxedValues: false })), (data) => {
        // Arrange
        const source = unboxedToBoxedMapper(data);

        // Act / Assert
        expect(unboxedToBoxedUnmapper(source)).toBe(data);
      }),
    ))

Summary: Boxing and unboxing a non-boxed value should return the original value unchanged.


it('should create an SDK and execute configurator for every connection', async () => {
            // Just some random SDK factory
            const createSdk = jest.fn().mockImplementation((point: OmniPoint) => ({ sdkFor: point }))
            // And a configuration function that returns something that we can identify in the result
            const configureEdge = jest
                .fn()
                .mockImplementation((edge: OmniEdge, sdk: unknown) => ({ vector: edge.vector, sdk }))
            const configureEdges = createConfigureEdges<OmniGraph>(configureEdge)

            await fc.assert(
                fc.asyncProperty(edgesArbitrary, async (connections) => {
                    createSdk.mockClear()
                    configureEdge.mockClear()

                    const graph = { contracts: [], connections }
                    const transactions = await configureEdges(graph, createSdk)

                    // First we check that the transactions match what our configuration function would return
                    expect(transactions).toHaveLength(connections.length)
                    expect(transactions).toEqual(
                        connections.map(({ vector }) => ({
                            vector,
                            sdk: {
                                sdkFor: vector.from,
                            },
                        }))
                    )

                    // Then we check that the configurations have been called properly
                    expect(createSdk).toHaveBeenCalledTimes(connections.length)
                    expect(configureEdge).toHaveBeenCalledTimes(connections.length)

                    for (const contract of connections) {
                        expect(createSdk).toHaveBeenCalledWith(contract.vector.from)
                        expect(configureEdge).toHaveBeenCalledWith(
                            contract,
                            expect.objectContaining({ sdkFor: contract.vector.from }),
                            graph,
                            createSdk
                        )
                    }
                })
            )
        })

Summary: An SDK and configurator are created for each connection in the graph, with transactions matching the expected configuration outputs and all calls occurring the correct number of times.


it('should return a configurator that does nothing with no configurators', async () => {
            const createSdk = jest.fn()
            const emptyConfigurator = createConfigureMultiple()

            await fc.assert(
                fc.asyncProperty(graphArbitrary, async (graph) => {
                    expect(createSdk).not.toHaveBeenCalled()

                    await expect(emptyConfigurator(graph, createSdk)).resolves.toEqual([])
                })
            )
        })

Summary: An empty configurator should return an empty array and not call `createSdk` when no configurators are provided.


it('should be able to split any string mapped from chars into chars', () =>
    fc.assert(
      fc.property(fc.array(fc.nat({ max: 0xffff }).map((n) => String.fromCharCode(n))), (data) => {
        // Arrange
        const source = charsToStringMapper(data);

        // Act / Assert
        expect(charsToStringUnmapper(source)).toEqual(data);
      }),
    ))

Summary: Strings mapped from character arrays should split back into their original character arrays using `charsToStringUnmapper`.


it('should call Random to generate any integer in [0, length-1] when provided multiple values', () =>
      fc.assert(
        fc.property(
          fc.array(fc.anything(), { minLength: 2 }),
          fc.option(fc.integer({ min: 2 }), { nil: undefined }),
          fc.nat(),
          (values, biasFactor, mod) => {
            // Arrange
            const { instance: mrng, nextInt } = fakeRandom();
            nextInt.mockImplementation((a, b) => a + (mod % (b - a + 1)));

            // Act
            const arb = new ConstantArbitrary(values);
            const g = arb.generate(mrng, biasFactor);

            // Assert
            expect(nextInt).toHaveBeenCalledTimes(1);
            expect(nextInt).toHaveBeenCalledWith(0, values.length - 1);
            expect(values).toContainEqual(g.value);
          },
        ),
      ))

Summary: `ConstantArbitrary` uses `Random` to generate an integer within the range `[0, length-1]` for an array of values, and ensures it selects a valid element from the array.


it('should call all configurators', async () => {
                const createSdk = jest.fn()

                await fc.assert(
                    fc.asyncProperty(
                        graphArbitrary,
                        fc.array(transactionArbitrary),
                        async (graph, transactionGroups) => {
                            createSdk.mockClear()

                            // We create a configurator for every group of transactions
                            const configurators = transactionGroups.map((transactions) =>
                                jest.fn().mockResolvedValue(transactions)
                            )

                            // Now we execute these configurators
                            const multiConfigurator = createConfigureMultiple(...configurators)

                            // And expect to get all the transactions back
                            await expect(multiConfigurator(graph, createSdk)).resolves.toEqual(transactionGroups.flat())

                            // We also check that every configurator has been called
                            for (const configurator of configurators) {
                                expect(configurator).toHaveBeenCalledOnce()
                            }
                        }
                    )
                )
            })

Summary: `createConfigureMultiple` calls all configurators, and returns a flattened array of their transactions.


it('should add a single node', () => {
                fc.assert(
                    fc.property(nodeArbitrary, (node) => {
                        const builder = new OmniGraphBuilder()

                        builder.addNodes(node)
                        expect(builder.nodes).toEqual([node])
                    })
                )
            })

Summary: Adding a single node to `OmniGraphBuilder` should result in the builder containing precisely that node in its `nodes` array.


it('should shrink towards the first value if it was not already this one and to nil otherwise even without any context', () =>
      fc.assert(
        fc.property(fc.array(fc.anything(), { minLength: 1 }), fc.nat(), (values, mod) => {
          // Arrange
          const { instance: mrng, nextInt } = fakeRandom();
          nextInt.mockImplementation((a, b) => a + (mod % (b - a + 1)));

          // Act
          const arb = new ConstantArbitrary(values);
          const value = arb.generate(mrng, undefined);
          const shrinks = [...arb.shrink(value.value, undefined)];

          // Assert
          if (Object.is(value.value, values[0])) {
            expect(shrinks.map((v) => v.value)).toEqual([]);
          } else {
            expect(shrinks.map((v) => v.value)).toEqual([values[0]]);
          }
        }),
      ))

Summary: `ConstantArbitrary` shrinks towards the first value if the generated value is different, and shrinks to an empty array otherwise, when no context is provided.


it('should overwrite a node if the points are equal', () => {
                fc.assert(
                    fc.property(pointArbitrary, nodeConfigArbitrary, nodeConfigArbitrary, (point, configA, configB) => {
                        const builder = new OmniGraphBuilder()

                        const nodeA = { point, config: configA }
                        const nodeB = { point, config: configB }

                        builder.addNodes(nodeA, nodeB)
                        expect(builder.nodes).toEqual([nodeB])
                    })
                )
            })

Summary: Overwriting a node occurs when two nodes with the same point are added, resulting in the latter node being retained in the graph.


it('should not do anything when there are no nodes', () => {
                fc.assert(
                    fc.property(nodeArbitrary, (node) => {
                        const builder = new OmniGraphBuilder()

                        builder.removeNodeAt(node.point)
                        expect(builder.nodes).toEqual([])
                    })
                )
            })

Summary: `OmniGraphBuilder` retains no nodes after attempting to remove a node from an empty graph.


it('should unmap any string coming from the mapper', () =>
    fc.assert(
      fc.property(wordsArrayArbitrary, (words) => {
        // Arrange
        const { instance, canShrinkWithoutContext } = fakeArbitrary<string>();
        canShrinkWithoutContext.mockImplementation(
          (value): value is string => typeof value === 'string' && words.includes(value),
        );

        // Act
        const mapped = wordsToJoinedStringMapper(words);
        const unmapper = wordsToJoinedStringUnmapperFor(instance);
        const unmappedValue = unmapper(mapped);

        // Assert
        expect(unmappedValue).toEqual(words);
      }),
    ))

Summary: Unmapping a string derived from `wordsToJoinedStringMapper` should reconstruct the original array of words using `wordsToJoinedStringUnmapperFor`.


it('should unmap any string coming from the mapper', () =>
    fc.assert(
      fc.property(wordsArrayArbitrary, (words) => {
        // Arrange
        const { instance, canShrinkWithoutContext } = fakeArbitrary<string>();
        canShrinkWithoutContext.mockImplementation(
          (value): value is string => typeof value === 'string' && words.includes(value),
        );

        // Act
        const mapped = wordsToSentenceMapper(words);
        const unmapper = wordsToSentenceUnmapperFor(instance);
        const unmappedValue = unmapper(mapped);

        // Assert
        expect(unmappedValue).toEqual(words);
      }),
    ))

Summary: Unmapped value should equal the original array of words after mapping and unmapping a string.


it('should return self', () => {
                fc.assert(
                    fc.property(nodeArbitrary, (node) => {
                        const builder = new OmniGraphBuilder()

                        expect(builder.removeNodeAt(node.point)).toBe(builder)
                    })
                )
            })

Summary: `OmniGraphBuilder.removeNodeAt` should return the builder instance itself.


it('should remove all edges starting at the node', () => {
                fc.assert(
                    fc.property(
                        nodeArbitrary,
                        nodeArbitrary,
                        nodeArbitrary,
                        edgeConfigArbitrary,
                        (nodeA, nodeB, nodeC, edgeConfig) => {
                            fc.pre(!arePointsEqual(nodeA.point, nodeB.point))
                            fc.pre(!arePointsEqual(nodeA.point, nodeC.point))
                            fc.pre(!arePointsEqual(nodeB.point, nodeC.point))

                            const edgeAB = { vector: { from: nodeA.point, to: nodeB.point }, config: edgeConfig }
                            const edgeAC = { vector: { from: nodeA.point, to: nodeC.point }, config: edgeConfig }
                            const edgeBA = { vector: { from: nodeB.point, to: nodeA.point }, config: edgeConfig }
                            const edgeBC = { vector: { from: nodeB.point, to: nodeC.point }, config: edgeConfig }
                            const edgeCA = { vector: { from: nodeC.point, to: nodeA.point }, config: edgeConfig }
                            const edgeCB = { vector: { from: nodeC.point, to: nodeB.point }, config: edgeConfig }

                            fc.pre(isVectorPossible(edgeAB.vector))
                            fc.pre(isVectorPossible(edgeAC.vector))
                            fc.pre(isVectorPossible(edgeBA.vector))
                            fc.pre(isVectorPossible(edgeBC.vector))
                            fc.pre(isVectorPossible(edgeCA.vector))
                            fc.pre(isVectorPossible(edgeCB.vector))

                            const builder = new OmniGraphBuilder()

                            builder
                                .addNodes(nodeA, nodeB, nodeC)
                                .addEdges(edgeAB, edgeAC, edgeBA, edgeBC, edgeCA, edgeCB)
                                .removeNodeAt(nodeA.point)
                            expect(builder.edges).toEqual([edgeBA, edgeBC, edgeCA, edgeCB])
                        }
                    )
                )
            })

Summary: Upon removing a node from the graph, all edges originating from that node should be removed, while edges starting from other nodes should remain.


it('should unmap any string coming from the mapper', () =>
    fc.assert(
      fc.property(
        fc.array(
          wordsArrayArbitrary.map((words) => wordsToSentenceMapper(words)),
          { minLength: 1 },
        ),
        (sentences) => {
          // Arrange / Act
          const mapped = sentencesToParagraphMapper(sentences);
          const unmappedValue = sentencesToParagraphUnmapper(mapped);

          // Assert
          expect(unmappedValue).toEqual(sentences);
        },
      ),
    ))

Summary: Mapping sentences to a paragraph and then unmapping should return the original sentences.


it('should fail if from is not in the graph', () => {
                fc.assert(
                    fc.property(edgeArbitrary, nodeConfigArbitrary, (edge, nodeConfig) => {
                        fc.pre(isVectorPossible(edge.vector))

                        const builder = new OmniGraphBuilder()

                        builder
                            .addNodes(
                                { point: edge.vector.from, config: nodeConfig },
                                { point: edge.vector.to, config: nodeConfig }
                            )
                            .removeNodeAt(edge.vector.from)

                        expect(() => builder.addEdges(edge)).toThrow()
                    })
                )
            })

Summary: An exception is thrown when attempting to add an edge from a node that has been removed from the graph.


it('should be able to split any string mapped from code-points into code-points', () =>
    fc.assert(
      fc.property(fc.array(fc.string({ unit: 'binary', minLength: 1, maxLength: 1 })), (data) => {
        // Arrange
        const source = codePointsToStringMapper(data);

        // Act / Assert
        expect(codePointsToStringUnmapper(source)).toEqual(data);
      }),
    ))

Summary: A string mapped from code-points can be split back into the original code-points array by `codePointsToStringUnmapper`.


it('should result into higher or equal maxLength given higher size', () => {
    fc.assert(
      fc.property(sizeArb, sizeArb, fc.integer({ min: 0, max: MaxLengthUpperBound }), (sa, sb, minLength) => {
        // Arrange
        const [smallSize, largeSize] = isSmallerSize(sa, sb) ? [sa, sb] : [sb, sa];

        // Act / Assert
        expect(maxLengthFromMinLength(minLength, smallSize)).toBeLessThanOrEqual(
          maxLengthFromMinLength(minLength, largeSize),
        );
      }),
    );
  })

Summary: `maxLengthFromMinLength` produces a maximum length that is greater than or equal when given a larger size input.


it('should fail if vector is not possible', () => {
                fc.assert(
                    fc.property(edgeArbitrary, nodeConfigArbitrary, (edge, nodeConfig) => {
                        fc.pre(!isVectorPossible(edge.vector))

                        const builder = new OmniGraphBuilder()

                        builder
                            .addNodes(
                                { point: edge.vector.from, config: nodeConfig },
                                { point: edge.vector.to, config: nodeConfig }
                            )
                            .removeNodeAt(edge.vector.from)

                        expect(() => builder.addEdges(edge)).toThrow()
                    })
                )
            })

Summary: The test ensures that when an edge's vector is not possible and a node at the vector's origin is removed, attempting to add such edges results in an error being thrown.


it('should not fail if to is not in the graph', () => {
                fc.assert(
                    fc.property(edgeArbitrary, nodeConfigArbitrary, (edge, nodeConfig) => {
                        fc.pre(isVectorPossible(edge.vector))

                        const builder = new OmniGraphBuilder()

                        builder
                            .addNodes(
                                { point: edge.vector.from, config: nodeConfig },
                                { point: edge.vector.to, config: nodeConfig }
                            )
                            .removeNodeAt(edge.vector.to)
                            .addEdges(edge)

                        expect(builder.edges).toEqual([edge])
                    })
                )
            })

Summary: Ensures that removing a node from the graph that matches `edge.vector.to` does not prevent adding the edge back to the graph.


it('should result into higher or equal maxLength given higher minLength', () => {
    fc.assert(
      fc.property(
        sizeArb,
        fc.integer({ min: 0, max: MaxLengthUpperBound }),
        fc.integer({ min: 0, max: MaxLengthUpperBound }),
        (size, minLengthA, minLengthB) => {
          // Arrange
          const [smallMinLength, largeMinLength] =
            minLengthA < minLengthB ? [minLengthA, minLengthB] : [minLengthB, minLengthA];

          // Act / Assert
          expect(maxLengthFromMinLength(smallMinLength, size)).toBeLessThanOrEqual(
            maxLengthFromMinLength(largeMinLength, size),
          );
        },
      ),
    );
  })

Summary: Increasing the minimum length should yield a maximum length that is higher or equal.


it('should add a single edge', () => {
                fc.assert(
                    fc.property(edgeArbitrary, nodeConfigArbitrary, (edge, nodeConfig) => {
                        fc.pre(isVectorPossible(edge.vector))

                        const builder = new OmniGraphBuilder()

                        builder
                            .addNodes({ point: edge.vector.from, config: nodeConfig })
                            .addNodes({ point: edge.vector.to, config: nodeConfig })
                            .addEdges(edge)
                        expect(builder.edges).toEqual([edge])
                    })
                )
            })

Summary: Adding a single edge to an `OmniGraphBuilder` should result in the builder's edges list containing that edge.


it('should not add a duplicate edge', () => {
                fc.assert(
                    fc.property(edgeArbitrary, nodeConfigArbitrary, (edge, nodeConfig) => {
                        fc.pre(isVectorPossible(edge.vector))

                        const builder = new OmniGraphBuilder()

                        builder
                            .addNodes({ point: edge.vector.from, config: nodeConfig })
                            .addNodes({ point: edge.vector.to, config: nodeConfig })
                            .addEdges(edge, edge)
                        expect(builder.edges).toEqual([edge])
                    })
                )
            })

Summary: OmniGraphBuilder should not include duplicate edges when adding the same edge twice.


it('should overwrite an edge if the points are equal', () => {
                fc.assert(
                    fc.property(
                        vectorArbitrary,
                        edgeConfigArbitrary,
                        edgeConfigArbitrary,
                        nodeConfigArbitrary,
                        (vector, configA, configB, nodeConfig) => {
                            fc.pre(isVectorPossible(vector))

                            const builder = new OmniGraphBuilder()

                            const edgeA = { vector, config: configA }
                            const edgeB = { vector, config: configB }

                            builder
                                .addNodes({ point: vector.from, config: nodeConfig })
                                .addNodes({ point: vector.to, config: nodeConfig })
                                .addEdges(edgeA, edgeB)
                            expect(builder.edges).toEqual([edgeB])
                        }
                    )
                )
            })

Summary: When two edges with identical points are added, the latter edge should overwrite the former in the `OmniGraphBuilder`.


it('should return self', () => {
                fc.assert(
                    fc.property(edgeArbitrary, (edge) => {
                        fc.pre(isVectorPossible(edge.vector))

                        const builder = new OmniGraphBuilder()

                        expect(builder.removeEdgeAt(edge.vector)).toBe(builder)
                    })
                )
            })

Summary: `OmniGraphBuilder.removeEdgeAt` method should return the builder instance itself.


it('should remove a edge at a specified vector', () => {
                fc.assert(
                    fc.property(edgeArbitrary, nodeConfigArbitrary, (edge, nodeConfig) => {
                        fc.pre(isVectorPossible(edge.vector))

                        const builder = new OmniGraphBuilder()

                        builder
                            .addNodes({ point: edge.vector.from, config: nodeConfig })
                            .addNodes({ point: edge.vector.to, config: nodeConfig })
                            .addEdges(edge)

                        builder.removeEdgeAt(edge.vector)
                        expect(builder.edges).toEqual([])
                    })
                )
            })

Summary: Removing an edge at a specified vector results in no edges remaining in the `OmniGraphBuilder`.


it('should not remove edges at different vectors', () => {
                fc.assert(
                    fc.property(edgeArbitrary, edgeArbitrary, nodeConfigArbitrary, (edgeA, edgeB, nodeConfig) => {
                        fc.pre(isVectorPossible(edgeA.vector))
                        fc.pre(isVectorPossible(edgeB.vector))
                        fc.pre(!areVectorsEqual(edgeA.vector, edgeB.vector))

                        const builder = new OmniGraphBuilder()

                        builder
                            .addNodes({ point: edgeA.vector.from, config: nodeConfig })
                            .addNodes({ point: edgeA.vector.to, config: nodeConfig })
                            .addNodes({ point: edgeB.vector.from, config: nodeConfig })
                            .addNodes({ point: edgeB.vector.to, config: nodeConfig })
                            .addEdges(edgeA, edgeB)
                        builder.removeEdgeAt(edgeA.vector)
                        expect(builder.edges).toEqual([edgeB])
                    })
                )
            })

Summary: `OmniGraphBuilder` retains edges at different vectors when one edge is removed.


it('should return undefined when there are no nodes', () => {
                const builder = new OmniGraphBuilder()

                fc.assert(
                    fc.property(pointArbitrary, (point) => {
                        expect(builder.getNodeAt(point)).toBeUndefined()
                    })
                )
            })

Summary: `OmniGraphBuilder` returns undefined when attempting to get a node at any point if no nodes exist.


it('should return undefined when there are no nodes at a specified point', () => {
                fc.assert(
                    fc.property(nodesArbitrary, (nodes) => {
                        const node = nodes.at(-1)
                        fc.pre(node != null)

                        const builder = new OmniGraphBuilder()

                        builder.addNodes(...nodes)
                        builder.removeNodeAt(node!.point)
                        expect(builder.getNodeAt(node!.point)).toBeUndefined()
                    })
                )
            })

Summary: Removing a node at a specified point should result in `getNodeAt` returning undefined.


it('should return undefined when there are no edges at a specified vector', () => {
                fc.assert(
                    fc.property(edgesArbitrary, nodeConfigArbitrary, (edges, nodeConfig) => {
                        const edge = edges.at(-1)
                        fc.pre(edge != null)
                        fc.pre(edges.map((e) => e.vector).every(isVectorPossible))

                        const builder = new OmniGraphBuilder()
                        const nodes = edges.flatMap(({ vector: { from, to } }) => [
                            { point: from, config: nodeConfig },
                            { point: to, config: nodeConfig },
                        ])

                        builder
                            .addNodes(...nodes)
                            .addEdges(...edges)
                            .removeEdgeAt(edge!.vector)
                        expect(builder.getEdgeAt(edge!.vector)).toBeUndefined()
                    })
                )
            })

Summary: When an edge is removed from a specified vector in `OmniGraphBuilder`, `getEdgeAt` should return undefined for that vector.


it('should return edge when there is a edge at a specified vector', () => {
                fc.assert(
                    fc.property(edgesArbitrary, nodeConfigArbitrary, (edges, nodeConfig) => {
                        const edge = edges.at(-1)
                        fc.pre(edge != null)
                        fc.pre(edges.map((e) => e.vector).every(isVectorPossible))

                        const builder = new OmniGraphBuilder()
                        const nodes = edges.flatMap(({ vector: { from, to } }) => [
                            { point: from, config: nodeConfig },
                            { point: to, config: nodeConfig },
                        ])

                        builder.addNodes(...nodes).addEdges(...edges)
                        expect(builder.getEdgeAt(edge!.vector)).toBe(edge)
                    })
                )
            })

Summary: Returns the correct edge from `OmniGraphBuilder` when there is an edge at the specified vector.


it('should return an empty array when there are no edges', () => {
                const builder = new OmniGraphBuilder()

                fc.assert(
                    fc.property(pointArbitrary, (point) => {
                        expect(builder.getEdgesFrom(point)).toEqual([])
                    })
                )
            })

Summary: `OmniGraphBuilder` returns an empty array for points with no edges.


it('should return all edges that originate at a specific point', () => {
                fc.assert(
                    fc.property(edgesArbitrary, nodeConfigArbitrary, (edges, nodeConfig) => {
                        const edge = edges.at(-1)
                        fc.pre(edge != null)
                        fc.pre(edges.map((e) => e.vector).every(isVectorPossible))

                        const builder = new OmniGraphBuilder()
                        const nodes = edges.flatMap(({ vector: { from, to } }) => [
                            { point: from, config: nodeConfig },
                            { point: to, config: nodeConfig },
                        ])

                        builder.addNodes(...nodes).addEdges(...edges)

                        const edgesFrom = builder.edges.filter(({ vector }) =>
                            arePointsEqual(vector.from, edge!.vector.from)
                        )
                        expect(builder.getEdgesFrom(edge!.vector.from)).toEqual(edgesFrom)
                    })
                )
            })

Summary: `OmniGraphBuilder.getEdgesFrom` returns all edges originating from a specified point in the graph.


it('should return an empty array when there are no edges', () => {
                const builder = new OmniGraphBuilder()

                fc.assert(
                    fc.property(pointArbitrary, (point) => {
                        expect(builder.getEdgesTo(point)).toEqual([])
                    })
                )
            })

Summary: `OmniGraphBuilder.getEdgesTo(point)` returns an empty array when no edges are present.


it('should return all edges that end at a specific point', () => {
                fc.assert(
                    fc.property(edgesArbitrary, nodeConfigArbitrary, (edges, nodeConfig) => {
                        const edge = edges.at(-1)
                        fc.pre(edge != null)
                        fc.pre(edges.map((e) => e.vector).every(isVectorPossible))

                        const builder = new OmniGraphBuilder()
                        const nodes = edges.flatMap(({ vector: { from, to } }) => [
                            { point: from, config: nodeConfig },
                            { point: to, config: nodeConfig },
                        ])

                        builder.addNodes(...nodes).addEdges(...edges)

                        const edgesTo = builder.edges.filter(({ vector }) => arePointsEqual(vector.to, edge!.vector.to))
                        expect(builder.getEdgesTo(edge!.vector.to)).toEqual(edgesTo)
                    })
                )
            })

Summary: The test verifies that `OmniGraphBuilder` correctly returns all edges terminating at a specified point.


it('should return all successful transactions if they all go through', async () => {
                    await fc.assert(
                        fc.asyncProperty(fc.array(transactionArbitrary), async (transactions) => {
                            // We'll prepare some mock objects for this test
                            // to mock the transaction responses and receipts
                            const receipt = { transactionHash: '0x0' }

                            // Our successful wait will produce a receipt
                            const successfulWait = jest.fn().mockResolvedValue(receipt)
                            const successfulResponse: OmniTransactionResponse = {
                                transactionHash: '0x0',
                                wait: successfulWait,
                            }

                            // Our signAndSend will then use the map to resolve/reject transactions
                            const signAndSend = jest.fn().mockResolvedValue(successfulResponse)
                            const sign = jest.fn().mockRejectedValue('Oh god no')
                            const signerFactory: OmniSignerFactory = jest.fn().mockResolvedValue({ signAndSend, sign })
                            const signAndSendTransactions = createSignAndSend(signerFactory)

                            // Now we send all the transactions to the flow and observe the output
                            const [successful, errors, pending] = await signAndSendTransactions(transactions)

                            // Since we are executing groups of transactions in parallel,
                            // in general the order of successful transaction will not match the order of input transactions
                            expect(successful).toContainAllValues(
                                transactions.map((transaction) => ({ transaction, receipt }))
                            )
                            expect(errors).toEqual([])
                            expect(pending).toEqual([])

                            // What needs to match though is the order of successful transactions within groups
                            //
                            // For that we group the successful transactions and make sure those are equal to the grouped original transactions
                            const groupedSuccessful = groupTransactionsByEid(
                                successful.map(({ transaction }) => transaction)
                            )
                            expect(groupedSuccessful).toEqual(groupTransactionsByEid(transactions))

                            // We also check that the signer factory has been called with the eids
                            for (const transaction of transactions) {
                                expect(signerFactory).toHaveBeenCalledWith(transaction.point.eid)
                            }
                        })
                    )
                })

Summary: All transactions should succeed and produce receipts, with empty error and pending lists, and grouped transactions maintaining their original order.


it('should call onProgress for every successful transaction', async () => {
                    await fc.assert(
                        fc.asyncProperty(fc.array(transactionArbitrary), async (transactions) => {
                            // We'll prepare some mock objects for this test
                            // to mock the transaction responses and receipts
                            const receipt = { transactionHash: '0x0' }

                            // Our successful wait will produce a receipt
                            const successfulWait = jest.fn().mockResolvedValue(receipt)
                            const successfulResponse: OmniTransactionResponse = {
                                transactionHash: '0x0',
                                wait: successfulWait,
                            }

                            // Our signAndSend will then use the map to resolve/reject transactions
                            const signAndSend = jest.fn().mockResolvedValue(successfulResponse)
                            const sign = jest.fn().mockRejectedValue('Oh god no')
                            const signerFactory: OmniSignerFactory = jest.fn().mockResolvedValue({ signAndSend, sign })
                            const signAndSendTransactions = createSignAndSend(signerFactory)

                            const handleProgress = jest.fn()
                            const [successful] = await signAndSendTransactions(transactions, handleProgress)

                            // We check whether onProgress has been called for every transaction
                            for (const [index, transaction] of successful.entries()) {
                                expect(handleProgress).toHaveBeenNthCalledWith(
                                    index + 1,
                                    // We expect the transaction in question to be passed
                                    transaction,
                                    // As well as the list of all the successful transactions so far
                                    successful.slice(0, index + 1)
                                )
                            }
                        })
                    )
                })

Summary: The `onProgress` callback should be called for every successful transaction processed by `signAndSendTransactions`, passing the current transaction and the list of all successful transactions so far.


it('should only consider the received size when set to Size', () => {
    fc.assert(
      fc.property(sizeRelatedGlobalConfigArb, sizeArb, fc.boolean(), (config, size, specifiedMaxDepth) => {
        // Arrange / Act
        const computedDepthBias = withConfiguredGlobal(config, () =>
          depthBiasFromSizeForArbitrary(size, specifiedMaxDepth),
        );
        const expectedDepthBias = { xsmall: 1, small: 1 / 2, medium: 1 / 4, large: 1 / 8, xlarge: 1 / 16 }[size];

        // Assert
        expect(computedDepthBias).toBe(expectedDepthBias);
      }),
    );
  })

Summary: When set to Size, `depthBiasFromSizeForArbitrary` should compute depth bias based solely on the received size, matching expected values.


it('should bail on the first wait error', async () => {
                await fc.assert(
                    fc.asyncProperty(
                        fc.array(transactionArbitrary),
                        transactionArbitrary,
                        fc.array(transactionArbitrary),
                        async (firstBatch, failedTransaction, secondBatch) => {
                            // We'll prepare some mock objects for this test
                            // to mock the transaction responses and receipts
                            const error = new Error('Failed transaction')
                            const receipt = { transactionHash: '0x0' }

                            // Our successful wait will produce a receipt
                            const successfulWait = jest.fn().mockResolvedValue(receipt)
                            const successfulResponse: OmniTransactionResponse = {
                                transactionHash: '0x0',
                                wait: successfulWait,
                            }

                            // Our unsuccessful wait will throw an error
                            const unsuccessfulWait = jest.fn().mockRejectedValue(error)
                            const unsuccessfulResponse: OmniTransactionResponse = {
                                transactionHash: '0x0',
                                wait: unsuccessfulWait,
                            }

                            // In order to resolve the good ones and reject the bad ones
                            // we'll prepare a map between a transaction and its response
                            //
                            // This map relies on the fact that we are passing the transaction object without modifying it
                            // so the objects are referentially equal
                            const implementations: Map<OmniTransaction, Promise<unknown>> = new Map([
                                ...firstBatch.map((t) => [t, Promise.resolve(successfulResponse)] as const),
                                ...secondBatch.map((t) => [t, Promise.resolve(successfulResponse)] as const),
                                [failedTransaction, Promise.resolve(unsuccessfulResponse)],
                            ])

                            const expectedSuccessful = [
                                // The first batch should all go through
                                ...firstBatch,
                                // The transactions that are not on the chain affected by the failed transaction should also pass
                                ...secondBatch.filter(({ point }) => point.eid !== failedTransaction.point.eid),
                            ]

                            const expectedPending = secondBatch.filter(
                                ({ point }) => point.eid === failedTransaction.point.eid
                            )

                            // Our signAndSend will then use the map to resolve/reject transactions
                            const signAndSend = jest.fn().mockImplementation((t) => implementations.get(t))
                            const sign = jest.fn().mockRejectedValue('Oh god no')
                            const signerFactory: OmniSignerFactory = jest.fn().mockResolvedValue({ signAndSend, sign })
                            const signAndSendTransactions = createSignAndSend(signerFactory)

                            // Now we send all the transactions to the flow and observe the output
                            const transactions = [...firstBatch, failedTransaction, ...secondBatch]
                            const [successful, errors, pending] = await signAndSendTransactions(transactions)

                            // Since we are executing groups of transactions in parallel,
                            // in general the order of successful transaction will not match the order of input transactions
                            expect(successful).toContainAllValues(
                                expectedSuccessful.map((transaction) => ({ transaction, receipt }))
                            )
                            expect(errors).toEqual([{ transaction: failedTransaction, error }])
                            expect(pending).toEqual([failedTransaction, ...expectedPending])

                            // What needs to match though is the order of successful transactions within groups
                            //
                            // For that we group the successful transactions and make sure those are equal to the grouped original transactions
                            const groupedSuccessful = groupTransactionsByEid(
                                successful.map(({ transaction }) => transaction)
                            )
                            expect(groupedSuccessful).toEqual(groupTransactionsByEid(expectedSuccessful))

                            // We also check that the signer factory has been called with the eids
                            expect(signerFactory).toHaveBeenCalledWith(failedTransaction.point.eid)
                            for (const transaction of firstBatch) {
                                expect(signerFactory).toHaveBeenCalledWith(transaction.point.eid)
                            }
                        }
                    )
                )
            })

Summary: Verifies that when processing a batch of transactions, the flow stops at the first transaction that fails and correctly categorizes successful, errored, and pending transactions.


it('should always return 0 if size is max whatever the global configuration', () => {
    fc.assert(
      fc.property(sizeRelatedGlobalConfigArb, fc.boolean(), (config, specifiedMaxDepth) => {
        // Arrange / Act
        const computedDepthBias = withConfiguredGlobal(config, () =>
          depthBiasFromSizeForArbitrary('max', specifiedMaxDepth),
        );

        // Assert
        expect(computedDepthBias).toBe(0);
      }),
    );
  })

Summary: `depthBiasFromSizeForArbitrary` should return 0 when the size is 'max', regardless of the global configuration settings.


it('should always return empty stream when current equals target', () =>
    fc.assert(
      fc.property(fc.maxSafeInteger(), fc.boolean(), (value, tryAsap) => {
        // Arrange / Act
        const shrinks = [...shrinkInteger(value, value, tryAsap)];

        // Assert
        expect(shrinks).toHaveLength(0);
      }),
    ))

Summary: An empty stream should be returned by `shrinkInteger` when the current value equals the target value.


it('should not bail on the first wait error', async () => {
                await fc.assert(
                    fc.asyncProperty(
                        fc.array(transactionArbitrary),
                        transactionArbitrary,
                        fc.array(transactionArbitrary),
                        async (firstBatch, failedTransaction, secondBatch) => {
                            // We'll prepare some mock objects for this test
                            // to mock the transaction responses and receipts
                            const error = new Error('Failed transaction')
                            const receipt = { transactionHash: '0x0' }

                            // Our successful wait will produce a receipt
                            const successfulWait = jest.fn().mockResolvedValue(receipt)
                            const successfulResponse: OmniTransactionResponse = {
                                transactionHash: '0x0',
                                wait: successfulWait,
                            }

                            // Our unsuccessful wait will throw an error
                            const unsuccessfulWait = jest.fn().mockRejectedValue(error)
                            const unsuccessfulResponse: OmniTransactionResponse = {
                                transactionHash: '0x0',
                                wait: unsuccessfulWait,
                            }

                            // In order to resolve the good ones and reject the bad ones
                            // we'll prepare a map between a transaction and its response
                            //
                            // This map relies on the fact that we are passing the transaction object without modifying it
                            // so the objects are referentially equal
                            const implementations: Map<OmniTransaction, Promise<unknown>> = new Map([
                                ...firstBatch.map((t) => [t, Promise.resolve(successfulResponse)] as const),
                                ...secondBatch.map((t) => [t, Promise.resolve(successfulResponse)] as const),
                                [failedTransaction, Promise.resolve(unsuccessfulResponse)],
                            ])

                            const expectedSuccessful = [
                                // The first batch should all go through
                                ...firstBatch,
                                // The second batch should all go through since they all were submitted and will all be mined
                                ...secondBatch,
                            ]

                            // Our signAndSend will then use the map to resolve/reject transactions
                            const signAndSend = jest.fn().mockImplementation((t) => implementations.get(t))
                            const sign = jest.fn().mockRejectedValue('Oh god no')
                            const signerFactory: OmniSignerFactory = jest.fn().mockResolvedValue({ signAndSend, sign })
                            const signAndSendTransactions = createSignAndSend(signerFactory)

                            // Now we send all the transactions to the flow and observe the output
                            const transactions = [...firstBatch, failedTransaction, ...secondBatch]
                            const [successful, errors, pending] = await signAndSendTransactions(transactions)

                            // Since we are executing groups of transactions in parallel,
                            // in general the order of successful transaction will not match the order of input transactions
                            expect(successful).toContainAllValues(
                                expectedSuccessful.map((transaction) => ({ transaction, receipt }))
                            )
                            expect(errors).toEqual([{ transaction: failedTransaction, error }])
                            expect(pending).toEqual([failedTransaction])

                            // What needs to match though is the order of successful transactions within groups
                            //
                            // For that we group the successful transactions and make sure those are equal to the grouped original transactions
                            const groupedSuccessful = groupTransactionsByEid(
                                successful.map(({ transaction }) => transaction)
                            )
                            expect(groupedSuccessful).toEqual(groupTransactionsByEid(expectedSuccessful))

                            // We also check that the signer factory has been called with the eids
                            expect(signerFactory).toHaveBeenCalledWith(failedTransaction.point.eid)
                            for (const transaction of firstBatch) {
                                expect(signerFactory).toHaveBeenCalledWith(transaction.point.eid)
                            }
                        }
                    )
                )
            })

Summary: The `signAndSendTransactions` function should continue processing transactions even if a single transaction fails, ensuring that other transactions succeed and correctly grouping them by execution order.


it('should only return errors if the submission fails', async () => {
                    await fc.assert(
                        fc.asyncProperty(fc.array(transactionArbitrary), async (transactions) => {
                            // We'll prepare some mock objects for this test
                            // to mock the transaction responses and receipts
                            const error = new Error('Failed transaction')

                            // Our signAndSend will then use the map to resolve/reject transactions
                            const signAndSendBatch = jest.fn().mockRejectedValue(error)
                            const signAndSend = jest.fn().mockRejectedValue('Oh god no')
                            const sign = jest.fn().mockRejectedValue('Oh god no')
                            const signerFactory: OmniSignerFactory = jest
                                .fn()
                                .mockResolvedValue({ signAndSend, signAndSendBatch, sign })
                            const signAndSendTransactions = createSignAndSend(signerFactory)

                            // Now we send all the transactions to the flow and observe the output
                            const [successful, errors, pending] = await signAndSendTransactions(transactions)

                            // Since we are executing groups of transactions in parallel,
                            // in general the order of successful transaction will not match the order of input transactions
                            expect(successful).toEqual([])
                            expect(errors).toContainAllValues(
                                transactions.map((transaction) => ({ transaction, error }))
                            )
                            expect(pending).toContainAllValues(transactions)

                            // We also check that the signer factory has been called with the eids
                            for (const transaction of transactions) {
                                expect(signerFactory).toHaveBeenCalledWith(transaction.point.eid)
                            }
                        })
                    )
                })

Summary: Sign and send operations should result in errors matching all transactions when each transaction in the batch fails, with no successful transactions reported.


it('should never include current in the stream', () =>
    fc.assert(
      fc.property(fc.maxSafeInteger(), fc.maxSafeInteger(), fc.boolean(), (current, target, tryAsap) => {
        // Arrange / Act
        const shrinks = [...shrinkInteger(current, target, tryAsap)];
        const values = shrinks.map((v) => v.value);

        // Assert
        expect(values).not.toContain(current);
      }),
    ))

Summary: The `shrinkInteger` function should produce a stream of values that do not include the current integer.