test('Normalization works', () => {
    fc.assert(
      fc.property(electionAndBallots(groupContext, 1), eb => {
        const [ballot] = eb.ballots;
        const normalizedBallot = normalizeBallot(ballot, eb.manifest);

        normalizedBallot.contests.forEach(contest => {
          const mcontest = eb.manifest.getContest(contest.contestId);
          expect(mcontest).toBeTruthy();

          expect(contest.selections.map(s => s.selectionId)).toStrictEqual(
            mcontest?.selections.map(s => s.selectionId)
          );
        });
      }),
      fcFastConfig
    );

    // idempotent
    fc.assert(
      fc.property(electionAndBallots(groupContext, 1), eb => {
        const [ballot] = eb.ballots;
        expect(
          normalizeBallot(
            normalizeBallot(ballot, eb.manifest),
            eb.manifest
          ).equals(normalizeBallot(ballot, eb.manifest))
        ).toBe(true);
      }),
      fcFastConfig
    );
  })

Summary: Normalization ensures that the contests in a ballot match the manifest's selections and that applying normalization twice is equivalent to applying it once.


unknown

expect(() =>
      fc.assert(
        fc.property(arbitrary, (value) => {
          return true;
        })
      )
    )


it('should complete if all resolved', () =>
		fc.assert(
			fc.asyncProperty(fc.nat({ max: 100 }), fc.nat({ max: 100 }), async (errs, succs) => {
				const waiter = new MutableWaiter();
				const errors = new Array(errs).fill(new Error());
				errors.forEach((err) => waiter.wait(Promise.reject(err)));
				new Array(succs).fill(waiter.wait(Promise.resolve()));
				return expect(waiter.isCompleted()).resolves.toStrictEqual(errors);
			})
		))

Summary: `MutableWaiter` should resolve as completed when all promises, both rejected and resolved, are processed, meeting the expected error results.


it('should complete if not resolved yet', () =>
		fc.assert(
			fc.asyncProperty(fc.nat({ max: 1000 }), async (n) => {
				const waiter = new MutableWaiter();
				const resolves = new Array(n)
					.fill(() => {
						const r = { resolve: undefined as ((v: any) => void) | undefined };
						waiter.wait(
							new Promise((resolve) => {
								r.resolve = resolve;
							})
						);
						return r;
					})
					.map((f) => f());
				const completed = waiter.isCompleted();
				resolves.forEach((resolve) => resolve.resolve());
				return expect(completed).resolves.toStrictEqual([]);
			})
		))

Summary: The `MutableWaiter` should return an empty list when checked for completion before any promises are resolved.


Summary: The normalization of a ballot aligns contest selections with those in the manifest, and performing normalization twice yields an equivalent result as a single normalization.


t.notThrows(() => {
    fc.assert(equivalentToSecp256k1Node);
  })


testFn(`${label} (with seed=${customParams.seed})`, async () => {
        await fc.assert((fc.asyncProperty as any)(...(arbitraries as any), promiseProp), params);
    })

Summary: Asserts that a property holds true for generated test cases defined by the provided arbitraries and `promiseProp`, with custom parameters.


Summary: The test confirms that `equivalentToSecp256k1Node` performs without throwing any errors.


t.notThrows(() => {
    fc.assert(reversesUncompress);
    fc.assert(equivalentToSecp256k1Node);
    fc.assert(equivalentToElliptic);
  })

Summary: The test ensures that the properties `reversesUncompress`, `equivalentToSecp256k1Node`, and `equivalentToElliptic` hold true without throwing exceptions.


t.notThrows(() => {
    fc.assert(isEquivalentToDeriveUncompressedThenCompress);
    fc.assert(equivalentToSecp256k1Node);
    fc.assert(equivalentToElliptic);
  })


t.notThrows(() => {
    fc.assert(isEquivalentToDeriveCompressedThenUncompress);
    fc.assert(equivalentToSecp256k1Node);
    fc.assert(equivalentToElliptic);
  })


t.notThrows(() => {
    fc.assert(malleationIsJustNegation);
  })


test("Shuffling arrays doesn't affect equality", () => {
    fc.assert(
      fc.property(electionDescription(groupContext), manifest => {
        const copy = new Manifest(
          groupContext,
          manifest.electionScopeId,
          manifest.specVersion,
          manifest.electionType,
          manifest.startDate,
          manifest.endDate,
          shuffleArray(manifest.geopoliticalUnits),
          shuffleArray(manifest.parties),
          shuffleArray(manifest.candidates),
          shuffleArray(manifest.contests),
          shuffleArray(manifest.ballotStyles),
          manifest.name,
          manifest.contactInformation
        );

        expect(copy.equals(manifest)).toBe(true);
      }),
      fcFastConfig
    );
  })

Summary: Shuffling arrays of geopolitical units, parties, candidates, contests, and ballot styles in a `Manifest` does not affect its equality with the original.


Summary: The property ensures that the operation `malleationIsJustNegation` completes without throwing any errors.


test('Generators yield valid contests', () => {
    fc.assert(
      fc.property(electionAndBallots(groupContext), eb => {
        const contests = eb.manifest.contests;
        const contestsValid = contests.every(c => c.isValid());
        expect(contestsValid).toBe(true);
      }),
      fcFastConfig
    );
  })

Summary: Generators produce contests that are always valid.


t.notThrows(() => {
    fc.assert(malleateThenNormalizeEqualsInitial);
    fc.assert(equivalentToSecp256k1Node);
  })


Summary: The properties being tested ensure that operations malleate and normalize maintain consistency with the initial input, and results are equivalent to the `secp256k1` node calculations without throwing exceptions.


t.notThrows(() => {
    fc.assert(equivalentToSecp256k1Node);
    fc.assert(equivalentToElliptic);
  })


t.notThrows(() => {
    fc.assert(reversesCompress);
    fc.assert(equivalentToSecp256k1Node);
  })


t.notThrows(() => {
      fc.assert(equivalentToNative);
      fc.assert(equivalentToHashJs);
    })


t.notThrows(() => {
    fc.assert(roundTripWithPayloadLength(20));
    fc.assert(roundTripWithPayloadLength(24));
    fc.assert(roundTripWithPayloadLength(28));
    fc.assert(roundTripWithPayloadLength(32));
    fc.assert(roundTripWithPayloadLength(40));
    fc.assert(roundTripWithPayloadLength(48));
    fc.assert(roundTripWithPayloadLength(56));
    fc.assert(roundTripWithPayloadLength(64));
  })


test('Encryption/decryption inverses', () => {
    fc.assert(
      fc.property(
        electionAndBallots(groupContext),
        elementModQ(groupContext),
        elementModQ(groupContext),
        (eb, prev, seed) => {
          const nonces = new Nonces(seed);
          // This ends up running the verification twice: once while encrypting, and once while decrypting.
          // This is fine, because we'd like to catch verification errors at encryption time, if possible,
          // but we'll take them wherever we can get them.

          const encryptionState = new EncryptionState(
            groupContext,
            eb.manifest,
            eb.electionContext,
            true
          );
          const submittedBallots = eb.ballots.map((b, i) =>
            encryptBallot(encryptionState, b, prev, nonces.get(i)).submit(
              BallotState.CAST
            )
          );
          const decryptedBallots = submittedBallots.map(sb =>
            decryptAndVerifyBallot(
              eb.manifest,
              eb.electionContext.cryptoExtendedBaseHash,
              eb.keypair,
              sb
            )
          );

          expect(
            matchingArraysOfAnyElectionObjects(
              eb.ballots.map(ballot => normalizeBallot(ballot, eb.manifest)),
              decryptedBallots
            )
          ).toBe(true);
        }
      ),
      fcFastConfig
    );
  })

Summary: Encrypting and then decrypting ballots should preserve their original state, ensuring that the decrypted ballots match the normalized original ballots.


test('Encryption serialization/deserialization', () => {
    fc.assert(
      fc.property(
        electionAndBallots(groupContext, 1),
        elementModQ(groupContext),
        elementModQ(groupContext),
        (eb, codeSeed, encryptionSeed) => {
          const bCodecs = getBallotCodecsForContext(groupContext);
          const encryptionState = new EncryptionState(
            groupContext,
            eb.manifest,
            eb.electionContext,
            false
          );
          const submittedBallot = encryptBallot(
            encryptionState,
            eb.ballots[0],
            codeSeed,
            encryptionSeed
          ).submit(BallotState.CAST);

          const serializedBallot =
            bCodecs.submittedBallotCodec.encode(submittedBallot);

          const deserializedBallotMaybe =
            bCodecs.submittedBallotCodec.decode(serializedBallot);

          const deserializedBallot = eitherRightOrFail(deserializedBallotMaybe);

          expect(submittedBallot.equals(deserializedBallot)).toBe(true);
        }
      ),
      fcFastConfig
    );
  })

Summary: Serialization and deserialization of encrypted ballots should yield equivalent ballot objects after encoding and decoding processes.


test('Encrypt the same election twice; identical result', () => {
    fc.assert(
      fc.property(
        electionAndBallots(groupContext),
        elementModQ(groupContext),
        elementModQ(groupContext),
        (eb, prev, seed) => {
          const timestamp = Math.floor(Date.now() / 1000);
          const nonces = new Nonces(seed);

          const encryptionState = new EncryptionState(
            groupContext,
            eb.manifest,
            eb.electionContext,
            false
          );
          const submittedBallots1 = eb.ballots.map((b, i) =>
            encryptBallot(
              encryptionState,
              b,
              prev,
              nonces.get(i),
              timestamp
            ).submit(BallotState.CAST)
          );
          const submittedBallots2 = eb.ballots.map((b, i) =>
            encryptBallot(
              encryptionState,
              b,
              prev,
              nonces.get(i),
              timestamp
            ).submit(BallotState.CAST)
          );
          const matching = matchingArraysOfAnyElectionObjects(
            submittedBallots1,
            submittedBallots2
          );
          expect(matching).toBe(true);
        }
      ),
      fcFastConfig
    );
  })

Summary: Encrypting the same election twice results in identical submitted ballots.


test('Encryption nonces are all unique', () => {
    fc.assert(
      fc.property(
        electionAndBallots(groupContext),
        elementModQ(groupContext),
        elementModQ(groupContext),
        (eb, prev, seed) => {
          const nonces = new Nonces(seed);

          const encryptionState = new EncryptionState(
            groupContext,
            eb.manifest,
            eb.electionContext,
            false
          );

          const selectionHashes = eb.manifest.contests.flatMap(contest =>
            contest.selections.map(selection =>
              selection.cryptoHashElement.toBigint()
            )
          );

          expect(noRepeatingBigints(selectionHashes)).toBe(true);

          const encryptedBallots = eb.ballots.map((b, i) =>
            encryptBallot(encryptionState, b, prev, nonces.get(i))
          );

          const elGamalPads = encryptedBallots.flatMap(ballot =>
            ballot.contests.flatMap(contest =>
              contest.selections.map(selection =>
                selection.ciphertext.pad.toBigint()
              )
            )
          );

          expect(noRepeatingBigints(elGamalPads)).toBe(true);

          const selectionNonces: bigint[] = encryptedBallots.flatMap(ballot =>
            ballot.contests.flatMap(contest =>
              contest.selections.map(selection => {
                if (selection.selectionNonce === undefined) {
                  throw new Error('selectionNonce should not be undefined');
                } else {
                  return selection.selectionNonce.toBigint();
                }
              })
            )
          );

          const contestNonces: bigint[] = encryptedBallots.flatMap(ballot =>
            ballot.contests.flatMap(contest => contest.contestNonce.toBigint())
          );

          const ballotNonces: bigint[] = encryptedBallots.map(ballot =>
            ballot.ballotEncryptionSeed.toBigint()
          );

          const allBallotNonces = selectionNonces
            .concat(contestNonces)
            .concat(ballotNonces);

          expect(noRepeatingBigints(allBallotNonces)).toBe(true);

          const chaumPedersenBigintsCR = encryptedBallots.flatMap(ballot =>
            ballot.contests.flatMap(contest =>
              contest.selections.flatMap(selection => [
                selection.proof.proof0.c.toBigint(),
                selection.proof.proof0.r.toBigint(),
                selection.proof.proof1.c.toBigint(),
                selection.proof.proof1.r.toBigint(),
              ])
            )
          );

          const chaumPedersenBigintsA = encryptedBallots.flatMap(ballot =>
            ballot.contests.flatMap(contest =>
              contest.selections.flatMap(selection => [
                selection.proof.proof0.a.toBigint(),
                selection.proof.proof1.a.toBigint(),
              ])
            )
          );

          const chaumPedersenBigintsB = encryptedBallots.flatMap(ballot =>
            ballot.contests.flatMap(contest =>
              contest.selections.flatMap(selection => [
                selection.proof.proof0.b.toBigint(),
                selection.proof.proof1.b.toBigint(),
              ])
            )
          );

          expect(noRepeatingBigints(chaumPedersenBigintsCR)).toBe(true);
          expect(
            noRepeatingBigints(
              chaumPedersenBigintsB.concat(chaumPedersenBigintsA)
            )
          ).toBe(true);

          const absolutelyEverything = elGamalPads
            .concat(allBallotNonces)
            .concat(chaumPedersenBigintsA)
            .concat(chaumPedersenBigintsB)
            .concat(chaumPedersenBigintsCR);

          expect(noRepeatingBigints(absolutelyEverything)).toBe(true);
        }
      ),
      fcFastConfig
    );
  })

Summary: All encryption-related bigints involved in the election process, including selection hashes, ElGamal pads, nonces, and Chaum-Pedersen proofs, must be unique across all processed ballots.


test("Shuffling plaintext doesn't affect ciphertext ballots", () => {
    fc.assert(
      fc.property(
        electionAndBallots(groupContext, 1),
        elementModQ(groupContext),
        elementModQ(groupContext),
        (eb, prev, seed) => {
          const timestamp = Math.floor(Date.now() / 1000);
          const nonces = new Nonces(seed);
          const encryptionState = new EncryptionState(
            groupContext,
            eb.manifest,
            eb.electionContext,
            true
          );

          const [ballot] = eb.ballots;
          const ciphertextBallot = encryptBallot(
            encryptionState,
            ballot,
            prev,
            nonces.get(0),
            timestamp
          );

          const copy = new PlaintextBallot(
            ballot.ballotId,
            ballot.ballotStyleId,
            shuffleArray(
              ballot.contests.map(
                contest =>
                  new PlaintextContest(
                    contest.contestId,
                    shuffleArray(contest.selections)
                  )
              )
            )
          );
          const copyCiphertextBallot = encryptBallot(
            encryptionState,
            copy,
            prev,
            nonces.get(0),
            timestamp
          );

          expect(copyCiphertextBallot.equals(ciphertextBallot)).toBe(true);
        }
      ),
      fcFastConfig
    );
  })

Summary: Shuffling the plaintext contests and selections in a ballot does not alter the resulting encrypted ciphertext ballot.


t.notThrows(() => {
    fc.assert(correctsUpToTwoErrors(20));
    fc.assert(correctsUpToTwoErrors(24));
    fc.assert(correctsUpToTwoErrors(28));
    fc.assert(correctsUpToTwoErrors(32));
    fc.assert(correctsUpToTwoErrors(40));
    fc.assert(correctsUpToTwoErrors(48));
    fc.assert(correctsUpToTwoErrors(56));
    fc.assert(correctsUpToTwoErrors(64));
  })

Summary: The `correctsUpToTwoErrors` function should not throw exceptions when handling input lengths of 20, 24, 28, 32, 40, 48, 56, and 64.


test('Ballots with extraneous contests result in errors', () => {
    fc.assert(
      fc.property(
        electionAndBallots(groupContext, 1),
        elementModQ(groupContext),
        elementModQ(groupContext),
        fc.string(),
        (eb, prev, seed, bogusContestId) => {
          const nonces = new Nonces(seed);
          const encryptionState = new EncryptionState(
            groupContext,
            eb.manifest,
            eb.electionContext,
            true
          );
          const [ballot] = eb.ballots;

          fc.pre(
            !ballot.contests.map(c => c.contestId).includes(bogusContestId)
          );

          const ballotExtraneousContests = new PlaintextBallot(
            ballot.ballotId,
            ballot.ballotStyleId,
            [...ballot.contests, new PlaintextContest(bogusContestId, [])]
          );
          expect(() =>
            encryptBallot(
              encryptionState,
              ballotExtraneousContests,
              prev,
              nonces.get(0)
            )
          ).toThrow();
        }
      ),
      fcFastConfig
    );
  })

Summary: Ballots containing extraneous contests that are not part of the expected contest IDs result in encryption errors.


test('Encrypt conventionally & with async; identical result', async () => {
    await fc.assert(
      fc.asyncProperty(
        electionAndBallots(groupContext, 1),
        elementModQ(groupContext),
        elementModQ(groupContext),
        async (eb, prev, seed) => {
          const timestamp = Math.floor(Date.now() / 1000);
          const encryptionState = new EncryptionState(
            groupContext,
            eb.manifest,
            eb.electionContext,
            true
          );

          // log.info('encrypt-async-test', 'starting conventional encryption');
          for (const plaintextBallot of eb.ballots) {
            const normalizedBallot = normalizeBallot(
              plaintextBallot,
              eb.manifest
            );

            const encryptedBallot = encryptBallot(
              encryptionState,
              normalizedBallot,
              prev,
              seed,
              timestamp
            );

            const bCodecs = getBallotCodecsForContext(groupContext);
            const cCodecs = getCoreCodecsForContext(groupContext);

            const manifestJson = bCodecs.manifestCodec.encode(
              eb.manifest
            ) as object;
            const electionContextJson = cCodecs.electionContextCodec.encode(
              eb.electionContext
            ) as object;

            /* const manifestDecoded = */ eitherRightOrFail(
              bCodecs.manifestCodec.decode(manifestJson)
            );
            const electionContextDecoded = eitherRightOrFail(
              cCodecs.electionContextCodec.decode(electionContextJson)
            );

            expect(
              electionContextDecoded.jointPublicKey.element.isValidResidue()
            ).toBe(true);

            // log.info('encrypt-async-test', 'initializing async encryption');
            const asyncEncryptor = AsyncBallotEncryptor.create(
              groupContext,
              manifestJson,
              electionContextJson,
              true,
              normalizedBallot.ballotStyleId,
              normalizedBallot.ballotId,
              prev,
              seed,
              timestamp
            );

            // log.info('encrypt-async-test', 'launching async encryption');
            // launches encryption on each contest: note the absence of return values
            normalizedBallot.contests.forEach(contest =>
              asyncEncryptor.encrypt(contest)
            );

            const encryptedBallot2 = await asyncEncryptor.getEncryptedBallot();
            // log.info('encrypt-async-test', 'async tasks complete');

            expect(encryptedBallot.equals(encryptedBallot2)).toBe(true);

            // now, fetch the result from the serialized version and compare
            const encryptedBallot3 =
              await asyncEncryptor.getSerializedEncryptedBallot();

            const deserializedSubmittedBallot = eitherRightOrFail(
              bCodecs.submittedBallotCodec.decode(
                encryptedBallot3.serializedEncryptedBallot
              )
            );

            expect(
              deserializedSubmittedBallot.equals(
                encryptedBallot2.submit(BallotState.CAST)
              )
            ).toBe(true);
          }
        }
      ),
      fcFastConfig
    );
  })

Summary: Encryption results of ballots should be identical when done conventionally and asynchronously, including after serialization and deserialization.


test('An assertion does not throw for an infallible property', () => {
  fc.assert(
    fc.property(fc.nat().noShrink(), (seed) => {
      const g = dev.Gen.integer();
      const p = dev.property(g, () => true);

      const error = tryAssert(p, { seed });

      expect(error).toBeNull();
    }),
  );
})

Summary: Assertions do not throw an error when testing an infallible property that always returns true.


test('An assertion throws for a fallible predicate property', () => {
  fc.assert(
    fc.property(fc.nat().noShrink(), (seed) => {
      const g = dev.Gen.integer();
      const p = dev.property(g, (x) => x < 5);

      const error = tryAssert(p, { seed });

      expect(error).not.toBeNull();
      expect(error!.message).toMatch(
        new RegExp(
          [
            '^Property failed after \\d+ test\\(s\\)',
            'Reproduction: \\{ \\"seed\\": \\d+, \\"size\\": \\d+(, \\"path\\": \\".*\\")? \\}',
            'Counterexample: \\[5\\]$',
          ].join('\n'),
          'g',
        ),
      );
    }),
  );
})

Summary: An assertion throws an error when a property with a predicate that fails (`x < 5`) is tested, and the error message matches the expected pattern.


test('An assertion throws for a fallible throwing property', () => {
  fc.assert(
    fc.property(fc.nat().noShrink(), (seed) => {
      const g = dev.Gen.integer();
      const p = dev.property(g, (x) => {
        expect(x).toBeLessThan(5);
      });

      const error = tryAssert(p, { seed });

      expect(error).not.toBeNull();
      expect(error!.message).toMatch(
        new RegExp(
          [
            '^Property failed after \\d+ test\\(s\\)',
            'Reproduction: \\{ \\"seed\\": \\d+, \\"size\\": \\d+(, \\"path\\": \\".*\\")? \\}',
            'Counterexample: \\[5\\]',
          ].join('\n'),
          'g',
        ),
      );
    }),
  );
})

Summary: An assertion should throw an error if a property fails with a generated integer that is not less than 5, including appropriate error details in the message.


test('Given a succeeding property function, it returns unfalsified', () => {
  fc.assert(
    fc.property(domainGen.checkConfig(), domainGen.gens(), domainGen.passingFunc(), (config, gens, f) => {
      const checkResult = dev.check(dev.property(...gens, f), config);

      const expectedCheckResult: dev.CheckResult<[]> = {
        kind: 'unfalsified',
        iterations: expect.anything(),
        discards: expect.anything(),
      };
      expect(checkResult).toEqual(expectedCheckResult);
    }),
  );
})

Summary: A succeeding property function results in a `check` function returning `unfalsified`.


it('returns the same falsification when repeated with the resulting parameters', () => {
  fc.assert(
    fc.property(domainGen.checkConfig(), domainGen.gens(), domainGen.fallibleFunc(), (config, gens, f) => {
      const p = dev.property(...gens, f);

      const checkResult0 = dev.check(p, { ...config, iterations: 100 });
      if (checkResult0.kind !== 'falsified') return failwith('expected falsified');

      const checkResult1 = dev.check(p, {
        seed: checkResult0.seed,
        size: checkResult0.size,
        path: checkResult0.counterexample.path,
      });
      if (checkResult1.kind !== 'falsified') return failwith('expected falsified');

      expect(checkResult1.counterexample).toEqual(checkResult0.counterexample);
    }),
  );
})

Summary: Re-checking a falsified property with the same parameters should return the identical counterexample.


test("should yield zero", () => {
  fc.assert(
    fc.property(ZeroBigIntArbitrary, (num) => {
      assert.ok(ZeroBigInt.is(num));
    }),
    {
      verbose: true,
      numRuns: 100,
    }
  );
})

Summary: `ZeroBigInt` correctly identifies all generated big integers as zero.


test("should yield positive integers", () => {
  fc.assert(
    fc.property(PositiveBigIntArbitrary, (num) => {
      assert.ok(PositiveBigInt.is(num));
    }),
    {
      verbose: true,
      numRuns: 100,
    }
  );
})

Summary: `PositiveBigIntArbitrary` generates values that satisfy the `PositiveBigInt.is` condition, ensuring they are positive integers.


it('only calls the property function once', () => {
  fc.assert(
    fc.property(domainGen.checkConfig(), domainGen.gens(), domainGen.fallibleFunc(), (config, gens, f) => {
      const checkResult0 = dev.check(dev.property(...gens, f), { ...config, iterations: 100 });

      if (checkResult0.kind !== 'falsified') return failwith('expected falsified');

      const spyF = spyOn(f);
      dev.check(dev.property(...gens, spyF), {
        seed: checkResult0.seed,
        size: checkResult0.size,
        path: checkResult0.counterexample.path,
      });

      expect(spyF).toBeCalledTimes(1);
    }),
  );
})

Summary: The property verifies that a function within a falsified property check is called exactly once using a specific seed, size, and path from a previous falsification.


test("should yield non-zero integers", () => {
  fc.assert(
    fc.property(NonZeroBigIntArbitrary, (num) => {
      assert.ok(NonZeroBigInt.is(num));
    }),
    {
      verbose: true,
      numRuns: 100,
    }
  );
})

Summary: NonZeroBigIntArbitrary generates only non-zero integers, verified by `NonZeroBigInt.is`.


test("should yield non-positive integers", () => {
  fc.assert(
    fc.property(NonPositiveBigIntArbitrary, (num) => {
      assert.ok(NonPositiveBigInt.is(num));
    }),
    {
      verbose: true,
      numRuns: 100,
    }
  );
})

Summary: `NonPositiveBigIntArbitrary` generates values that the `NonPositiveBigInt` type recognizes as valid non-positive integers.


test("should yield non-negative integers", () => {
  fc.assert(
    fc.property(NonNegativeBigIntArbitrary, (num) => {
      assert.ok(NonNegativeBigInt.is(num));
    }),
    {
      verbose: true,
      numRuns: 100,
    }
  );
})

Summary: NonNegativeBigIntArbitrary generates values verified by `NonNegativeBigInt.is` to ensure they are non-negative integers.


it('has resilience to parameter ordering', () => {
  fc.assert(
    fc.property(genShuffledRangeParams(), LocalGen.scaleMode(), ({ ordered, unordered }, scaleMode) => {
      const range1 = Range.createFrom(nativeCalculator, ordered.min, ordered.max, ordered.origin, scaleMode);
      const range2 = Range.createFrom(nativeCalculator, unordered.x, unordered.y, unordered.z, scaleMode);

      expect(range1.origin).toEqual(range2.origin);
      expect(range1.bounds).toEqual(range2.bounds);
    }),
  );
})

Summary: Creating a range from parameters should result in equivalent origins and bounds, regardless of parameter order.


test("should yield negative bigints", () => {
  fc.assert(
    fc.property(NegativeBigIntArbitrary, (num) => {
      assert.ok(NegativeBigInt.is(num));
    }),
    {
      verbose: true,
      numRuns: 100,
    }
  );
})

Summary: The property ensures that values generated by `NegativeBigIntArbitrary` are valid negative bigints.


test("should yield zero", () => {
  fc.assert(
    fc.property(ZeroArbitrary, (num) => {
      assert.ok(Zero.is(num));
    }),
    {
      verbose: true,
      numRuns: 100,
    }
  );
})

Summary: `Zero.is` should confirm that all numbers generated by `ZeroArbitrary` are zero.


it('returns 100 when n = min, and n < origin', () => {
    fc.assert(
      fc.property(
        genRangeParams().filter((x) => x.min < x.origin),
        LocalGen.scaleMode(),
        ({ min, max, origin }, scaleMode) => {
          const range = Range.createFrom(nativeCalculator, min, max, origin, scaleMode);

          const distance = range.getProportionalDistance(min);

          expect(distance).toEqual(100);
        },
      ),
    );
  })

Summary: When `n` equals `min` and `n` is less than `origin`, the proportional distance returned is 100.


test("should yield positive numbers", () => {
  fc.assert(
    fc.property(PositiveArbitrary, (num) => {
      assert.ok(Positive.is(num));
    }),
    {
      verbose: true,
      numRuns: 100,
    }
  );
})

Summary: Numbers generated by `PositiveArbitrary` are verified to be positive using `Positive.is(num)`.


test("should yield positive integers", () => {
  fc.assert(
    fc.property(PositiveIntArbitrary, (num) => {
      assert.ok(Positive.is(num));
      assert.ok(Int.is(num));
      assert.ok(PositiveInt.is(num));
    }),
    {
      verbose: true,
      numRuns: 100,
    }
  );
})

Summary: `PositiveIntArbitrary` generates numbers that satisfy the conditions of being positive integers, as confirmed by `Positive.is`, `Int.is`, and `PositiveInt.is`.


it('returns [min,max] when 0 <= size <= 100 ', () => {
      fc.assert(
        fc.property(genRangeParams(), domainGen.size(), ({ min, max, origin }, size) => {
          const range = Range.createFrom(nativeCalculator, min, max, origin, 'constant');

          const bounds = range.getSizedBounds(nativeCalculator.loadIntegerUnchecked(size));

          const expectedBounds: Bounds<number> = [min, max];
          expect(bounds).toMatchObject(expectedBounds);
        }),
      );
    })

Summary: `Range.getSizedBounds` returns `[min, max]` when the size is between 0 and 100.


test('GenTree.unfold(<acc>, <accToValue>, <accExpander>, <calculateComplexity>) => forall nodes, node.complexity = calculateComplexity(node.value)', () => {
  fc.assert(
    fc.property(
      domainGen.anything(),
      domainGen.func(domainGen.anything()),
      domainGen.func(domainGen.anyShrinks()),
      domainGen.func(domainGen.naturalNumber()),
      (acc, accToValue, accExpander, calculateComplexity) => {
        const tree = GenTree.unfold(acc, accToValue, accExpander, calculateComplexity);

        for (const node of pipe(GenTree.traverse(tree), take(10))) {
          expect(node.complexity).toEqual(calculateComplexity(node.value));
        }
      },
    ),
  );
})

Summary: `GenTree.unfold` generates nodes such that each node's complexity equals `calculateComplexity` applied to its value.


test("should yield non-zero numbers", () => {
  fc.assert(
    fc.property(NonZeroArbitrary, (num) => {
      assert.ok(NonZero.is(num));
    }),
    {
      verbose: true,
      numRuns: 100,
    }
  );
})

Summary: `NonZeroArbitrary` generates numbers that the `NonZero.is` function validates as non-zero.


test("should yield non-zero integers", () => {
  fc.assert(
    fc.property(NonZeroIntArbitrary, (num) => {
      assert.ok(NonZero.is(num));
      assert.ok(Int.is(num));
      assert.ok(NonZeroInt.is(num));
    }),
    {
      verbose: true,
      numRuns: 100,
    }
  );
})

Summary: Generated integers should be non-zero and classified as valid by `NonZero`, `Int`, and `NonZeroInt`.


test('It has an isomorphism with Array.prototype.map', () => {
  fc.assert(
    fc.property(domainGen.anyTree(), domainGen.anyFunc({ arity: 1 }), (tree, f) => {
      const mappedTree = GenTree.map(tree, f);

      const nodesMappedByTree = Array.from(GenTree.traverse(mappedTree));
      const nodesMappedByArray = Array.from(GenTree.traverse(tree)).map((node) => ({ ...node, value: f(node.value) }));

      expect(nodesMappedByTree).toEqual(nodesMappedByArray);
    }),
  );
})

Summary: `GenTree.map` produces a tree whose traversal matches the array mapping of the input tree's traversal with the same function.


test('GenTree.concat([], concatComplexity, <shrink>) => the root node contains the concatComplexity', () => {
  fc.assert(
    fc.property(domainGen.naturalNumber(), (concatComplexity) => {
      const genTreeConcat = GenTree.concat<unknown>([], () => concatComplexity, Shrink.none());

      const expectedNode: GenTree.Node<unknown[]> = {
        id: NodeId.EMPTY,
        value: [],
        complexity: concatComplexity,
      };
      expect(genTreeConcat.node).toEqual(expectedNode);
    }),
  );
})

Summary: `GenTree.concat` with an empty array assigns the root node a complexity equal to the provided `concatComplexity`.


test('GenTree.concat([tree], <calculateComplexity>, <shrink>) => the root node contains the root node of tree', () => {
  fc.assert(
    fc.property(domainGen.anyTree(), domainGen.naturalNumber(), (tree, concatComplexity) => {
      const treeConcat = GenTree.concat([tree], () => concatComplexity, Shrink.none());

      const expectedNode: GenTree.Node<unknown[]> = {
        id: tree.node.id,
        value: [tree.node.value],
        complexity: tree.node.complexity + concatComplexity,
      };
      expect(treeConcat.node).toEqual(expectedNode);
    }),
  );
})

Summary: The root node of the concatenated tree should contain the root node of the original tree with updated complexity.


test("should yield non-negative integers", () => {
  fc.assert(
    fc.property(NonNegativeIntArbitrary, (num) => {
      assert.ok(NonNegative.is(num));
      assert.ok(Int.is(num));
      assert.ok(NonNegativeInt.is(num));
    }),
    {
      verbose: true,
      numRuns: 100,
    }
  );
})

Summary: The property should verify that generated numbers are non-negative integers, satisfying `NonNegative.is`, `Int.is`, and `NonNegativeInt.is` conditions.


test("should yield negative numbers", () => {
  fc.assert(
    fc.property(NegativeArbitrary, (num) => {
      assert.ok(Negative.is(num));
    }),
    {
      verbose: true,
      numRuns: 100,
    }
  );
})

Summary: The test verifies that values generated by `NegativeArbitrary` are identified as negative by the `Negative.is` method.


it('is repeatable', () => {
  fc.assert(
    fc.property(domainGen.sampleConfig(), domainGen.gen(), (config, gen) => {
      const config0 = { ...config, seed: config.seed, iterations: 1 };
      const sample0 = dev.sample(gen, config0);

      const config1 = { seed: sample0.seed, size: sample0.size, iterations: 1 };
      const sample1 = dev.sample(gen, config1);

      expect(sample1).toEqual(sample0);
    }),
  );
})

Summary: The property checks that sampling a generator with a consistent seed and configuration produces repeatable results.


test("that sPoNGEcAsE contains at least one upper case and lower case character", () => {
  fc.assert(
    fc.property(arb.pathSegment(10, 15), (name) => {
      const newName = recase(name, ["sPoNGEcAsE"]);

      expect(newName.split("").some((c) => c === c.toUpperCase()));
      expect(newName.split("").some((c) => c === c.toLowerCase()));
    })
  );
})

Summary: The `recase` function ensures that converting a string to `sPoNGEcAsE` results in a mix of at least one uppercase and one lowercase character.


test('sample(gen.map(f)).values = sample(gen).values.map(f)', () => {
  fc.assert(
    fc.property(domainGen.sampleConfig(), domainGen.gen(), fc.func(fc.anything()), (config, gen, f) => {
      const genMapped = gen.map((x) => f(x));

      const mappedSample = dev.sampleTrees(genMapped, config);
      const unmappedSample = dev.sampleTrees(gen, config);

      const actualTrees = mappedSample.values.map(dev.GenTree.traverseGreedy);
      const expectedTrees = unmappedSample.values
        .map((tree) => dev.GenTree.map(tree, f))
        .map(dev.GenTree.traverseGreedy);
      expect(actualTrees).toEqual(expectedTrees);
    }),
  );
})

Summary: Mapping a function over a generated sample should produce equivalent values to generating a sample and then mapping the function over it.


test('gen.map(f) = Gen.map(gen, f)', () => {
    fc.assert(
      fc.property(domainGen.sampleConfig(), domainGen.gen(), fc.func(fc.anything()), (config, gen, f) => {
        const genMapped = gen.map((x) => f(x));
        const genMappedAlt = dev.Gen.map(gen, (x) => f(x));

        expect(dev.sample(genMapped, config)).toEqual(dev.sample(genMappedAlt, config));
      }),
    );
  })

Summary: Mapping a function over a generator using `gen.map(f)` should yield the same result as using `Gen.map(gen, f)`.


it("includes new projects on include", async () => {
    fc.assert(
      fc.property(arb.folderPath(true), (rootPath) => {
        const folder = new TestFolder(rootPath);

        const projects = new Projects();
        const project = new Project(folder, basicSettings);
        projects.add(project);

        expect(projects.count).toBe(1);
        expect(projects.list()[0]).toBe(project);
        expect(projects.find(project.folder.path)).toBe(project);
        expect(projects.bestMatch(project.folder.path)).toBe(project);
      })
    );
  })

Summary: Adding a new project to `Projects` should result in the project being countable, listable, findable by folder path, and the best match by folder path.


it("removes projects by path", async () => {
    fc.assert(
      fc.property(arb.folderPath(true), (rootPath) => {
        const folder = new TestFolder(rootPath);

        const projects = new Projects();
        const project = new Project(folder, basicSettings);
        projects.add(project);
        projects.remove(project.folder.path);

        expect(projects.count).toBe(0);
        expect(projects.list()[0]).toBe(undefined);
        expect(projects.find(project.folder.path)).toBe(null);
        expect(projects.bestMatch(project.folder.path)).toBe(null);
      })
    );
  })

Summary: Projects should be removable by specifying their path, verified by checking the project's absence in the list, count, and search results.


test('Gen.integer().between(x, y) *produces* integers in the range [x, y]', () => {
  fc.assert(
    fc.property(
      domainGen.sampleConfig(),
      domainGen.setOfSize(domainGen.integer(), 2),
      fc.boolean(),
      (config, [a, b], shouldBias) => {
        const [x, y] = [a, b].sort((a, b) => a - b);
        const gen = shouldBias ? dev.Gen.integer().between(x, y) : dev.Gen.integer().between(x, y).noBias();

        const sample = dev.sample(gen, config);

        for (const value of sample.values) {
          expect(value).toEqual(Math.round(value));
          expect(value).toBeGreaterThanOrEqual(x);
          expect(value).toBeLessThanOrEqual(y);
        }
      },
    ),
  );
})

Summary: `Gen.integer().between(x, y)` produces integers within the inclusive range `[x, y]`, ensuring each value is an integer and falls within the specified bounds.


test('Gen.integer().between(x, y), x > 0, y >= x = Gen.integer().between(x, y).origin(x) *because* the origin is clipped', () => {
  fc.assert(
    fc.property(
      domainGen.sampleConfig(),
      domainGen.integer({ min: 1 }),
      domainGen.integer({ min: 1 }),
      (config, a, b) => {
        const [x, y] = [a, b].sort((a, b) => a - b);
        const gen = dev.Gen.integer().between(x, y);
        const genAlt = dev.Gen.integer().between(x, y).origin(x);

        expect(dev.sample(gen, config)).toEqual(dev.sample(genAlt, config));
      },
    ),
  );
})

Summary: `Gen.integer().between(x, y)` with `x > 0` and `y >= x` should produce the same samples as `Gen.integer().between(x, y).origin(x)` because the origin is clipped.


test('Gen.integer().between(x, y), x < 0, y <= x = Gen.integer().between(x, y).origin(x) *because* the origin is clipped', () => {
  fc.assert(
    fc.property(
      domainGen.sampleConfig(),
      domainGen.integer({ max: -1 }),
      domainGen.integer({ max: -1 }),
      (config, a, b) => {
        const [x, y] = [a, b].sort((a, b) => b - a);
        const gen = dev.Gen.integer().between(x, y);
        const genAlt = dev.Gen.integer().between(x, y).origin(x);

        expect(dev.sample(gen, config)).toEqual(dev.sample(genAlt, config));
      },
    ),
  );
})

Summary: For `Gen.integer().between(x, y)` where `x < 0` and `y <= x`, the origin is clipped to `x`, resulting in the same sample output as explicitly setting the origin to `x`.


test('default(origin) = 0', () => {
    fc.assert(
      fc.property(domainGen.sampleConfig(), (config) => {
        const genDefault = dev.Gen.integer();
        const genLinear = dev.Gen.integer().origin(0);

        expect(dev.sample(genDefault, config)).toEqual(dev.sample(genLinear, config));
      }),
    );
  })

Summary: The test checks if the default origin of integers generated by `dev.Gen.integer()` is equivalent to explicitly setting the origin to 0.


test('Gen.integer().between(x, y) = Gen.integer().between(y, x) *because* it is resilient to parameter order', () => {
    fc.assert(
      fc.property(domainGen.sampleConfig(), domainGen.setOfSize(domainGen.integer(), 3), (config, [a, b, c]) => {
        const [x, z, y] = [a, b, c].sort((a, b) => a - b);
        const gen = dev.Gen.integer().origin(z);
        const genBetween = gen.between(x, y);
        const genBetweenAlt = gen.between(y, x);

        expect(dev.sample(genBetween, config)).toEqual(dev.sample(genBetweenAlt, config));
      }),
    );
  })

Summary: `Gen.integer().between(x, y)` is invariant to the order of parameters, producing equivalent samples when `x` and `y` are swapped.


test('sample(gen.filter(false)) *throws* exhausted', () => {
  fc.assert(
    fc.property(domainGen.sampleConfig(), domainGen.gen(), (config, gen) => {
      const genFiltered = gen.filter(() => false);

      expect(() => dev.sample(genFiltered, config)).toThrow('Exhausted after');
    }),
  );
})

Summary: Filtering a generator with a condition that is never met raises an "Exhausted after" error when sampled.


test('Gen.array(gen).ofMaxLength(x) *produces* arrays with length equal to *oracle* Gen.integer().between(0, x)', () => {
  fc.assert(
    fc.property(domainGen.sampleConfig(), domainGen.gen(), genArrayLength(), (config, elementGen, x) => {
      const genArray = dev.Gen.array(elementGen)
        .ofMaxLength(x)
        .map((arr) => arr.length);
      const genInteger = dev.Gen.integer().between(0, x);

      expect(dev.sample(genArray, { ...config, iterations: 1 })).toEqual(
        dev.sample(genInteger, { ...config, iterations: 1 }),
      );
    }),
  );
})

Summary: `Gen.array(gen).ofMaxLength(x)` produces arrays whose lengths match those generated by `Gen.integer().between(0, x)`.


test('default(min) = 0', () => {
    fc.assert(
      fc.property(domainGen.sampleConfig(), domainGen.gen(), (config, elementGen) => {
        const genDefault = dev.Gen.array(elementGen);
        const genAlt = dev.Gen.array(elementGen).ofMinLength(0);

        expect(dev.sample(genDefault, config)).toEqual(dev.sample(genAlt, config));
      }),
    );
  })

Summary: The default minimum length of arrays generated by `dev.Gen.array` should be zero, matching arrays generated with an explicit minimum length of zero.


test('Gen.array(gen) = gen.array()', () => {
    fc.assert(
      fc.property(domainGen.sampleConfig(), domainGen.gen(), (config, elementGen) => {
        const genArray = dev.Gen.array(elementGen);
        const genArrayAlt = elementGen.array();

        expect(dev.sample(genArray, config)).toEqual(dev.sample(genArrayAlt, config));
      }),
    );
  })

Summary: `Gen.array(gen)` should produce the same sample output as `gen.array()` given the same configuration and element generator.


test('Gen.array(gen).betweenLengths(x, y) = Gen.array(gen).betweenLengths(y, x)', () => {
    fc.assert(
      fc.property(
        domainGen.sampleConfig(),
        domainGen.gen(),
        genArrayLength(),
        genArrayLength(),
        (config, elementGen, x, y) => {
          const genArray = dev.Gen.array(elementGen).betweenLengths(x, y);
          const genArrayAlt = dev.Gen.array(elementGen).betweenLengths(y, x);

          expect(dev.sample(genArray, config)).toEqual(dev.sample(genArrayAlt, config));
        },
      ),
    );
  })

Summary: Generating arrays with `Gen.array(gen).betweenLengths(x, y)` should produce the same output as `Gen.array(gen).betweenLengths(y, x)`.


test("order of elements is maintained", () => {
    const arb = fc.array(fc.nat({ max: 1024 }), {
      maxLength: 10,
    })

    fc.assert(
      fc.property(arb, sizes => {
        const arrs = sizes.map(arrayOfSize)
        const vecs = arrs.map(fromArray)
        const merged = concatMany(vecs)

        assertEqualElements(merged, arrs.flat())
      }),
      { numRuns: 100 }
    )
  })

Summary: The order of elements should be maintained when merging multiple arrays into a single vector using `concatMany`.


test("height invariant is maintained", () => {
    const arb = fc.array(fc.nat({ max: 32_768 }), {
      maxLength: 10,
    })

    fc.assert(
      fc.property(arb, sizes => {
        const arrs = sizes.map(arrayOfSize)
        const vecs = arrs.map(fromArray)
        const merged = concatMany(vecs)

        const length = arrs.flat().length
        const heightLeastDense =
          length > 0 ? Math.log(length) / Math.log(M - E_MAX) : 0
        const heightMostDense =
          length > 0 ? Math.ceil(Math.log(length) / Math.log(M)) - 1 : 0

        expect(merged.root.height).toBeLessThanOrEqual(heightLeastDense)
        expect(merged.root.height).toBeGreaterThanOrEqual(heightMostDense)
      }),
      { numRuns: 1000 }
    )
  })

Summary: The height of the resultant RRB tree after concatenating multiple vectors should be within a calculated range based on density, ensuring the height invariant is maintained.


it.concurrent('returns true if the objects are equal', () => {
      fc.assert(
        fc.property(cloneValueObjArb, ([valueObj, otherObj]) => {
          expect(valueObj.equals(otherObj)).toBe(true);
          expect(otherObj.equals(valueObj)).toBe(true);
        }),
      );
    })

Summary: The property should hold that `valueObj.equals(otherObj)` and `otherObj.equals(valueObj)` both return true when the objects are equal.


it.concurrent('immutable.is returns true when setting to a clone', () => {
      fc.assert(
        fc.property(withMapArb(cloneValueObjArb), ([valueMap, otherMap]) => {
          expect(is(valueMap, otherMap)).toBe(true);
        }),
      );
    })

Summary: The test verifies that `immutable.is` returns true when comparing a map to its clone.


it("lists all files (except ignored) with '**/*'", async () => {
    await fc.assert(
      fc.asyncProperty(
        arb.folderPath(true),
        fc.set(arb.filePath({ minLength: 2, maxLength: 2 }), {
          minLength: 2,
          maxLength: 2,
        }),
        async (rootPath, files) => {
          const root = seedFolder(rootPath, files, []);
          const ignore = files.slice(0, files.length / 2);

          const project = new Project(root, { ...basicSettings, ignore });
          const entries = await project.list(Glob.ANYTHING, "file");
          const paths = entries.map(([path]) => path);

          for (const file of paths) {
            expect(paths.includes(file)).toBe(!ignore.includes(file));
          }
        }
      )
    );
  })

Summary: `Project` lists all files using the pattern '**/*', excluding those specified in the ignore list.


it('should return passed cell for any string value', () => {
      fc.assert(
        fc.property(fc.string(), (value) => {
          expect(handleEmptyCell(value, (data) => generateGridCell(data))).toEqual(
            generateGridCell(value),
          );
        }),
      );
    })

Summary: `handleEmptyCell` should return the generated grid cell for any string value.


it('should return passed cell for any non-empty string value when allowFalsy is false', () => {
      fc.assert(
        fc.property(fc.string({ minLength: 1 }), (value) => {
          expect(handleEmptyCell(value, (data) => generateGridCell(data))).toEqual(
            generateGridCell(value),
          );
        }),
      );
    })

Summary: When `allowFalsy` is false, `handleEmptyCell` should return the given grid cell for any non-empty string.


it('merged abort controller aborts with constituent reason', () => {
    fc.assert(
      fc.property(argArbWithSelectionAndReasons(1), ([args, abortControllers]) => {
        const [[abortController, reason]] = abortControllers;
        const result = mergeAbortControllers(...args);

        abortController.abort(reason);
        expect(abortController.signal.reason).toBe(reason);
        expect(result.signal.reason).toBe(abortController.signal.reason);
      }),
    );
  })

Summary: A merged abort controller should abort with the reason provided to one of its constituent controllers.


it('merged abort controller only reflects the first abort', () => {
    fc.assert(
      fc.property(argArbWithSelectionAndReasons(), ([args, abortControllers]) => {
        const [[firstAbortController]] = abortControllers;
        const result = mergeAbortControllers(...args);

        abortControllers.forEach(([abortController, reason]) => {
          abortController.abort(reason);
        });
        expect(result.signal.reason).toBe(firstAbortController.signal.reason);
      }),
    );
  })

Summary: `mergeAbortControllers` returns a signal that reflects only the reason from the first `AbortController` that is aborted.


it("does not affect the returned result for a deterministic IO", () =>
    fc.assert(
      fc.asyncProperty(
        io,
        fc.integer({ min: 0, max: 10 }),
        async (io, retryCount) => {
          expect(await io.retry(retryCount).runSafe()).toEqual(
            await io.runSafe()
          );
        }
      )
    ))

Summary: The result of a deterministic IO operation should remain unchanged by retrying it a specified number of times.


it("raises a Timeout error if the action takes longer than the timeout", () =>
    fc.assert(
      fc.asyncProperty(
        io,
        fc.integer({ min: 0, max: 8 }).map((x) => x * 5),
        fc.integer({ min: 0, max: 8 }).map((x) => x * 5),
        async (io, msToComplete, msToTimeout) => {
          fc.pre(msToComplete > msToTimeout);
          await expect(
            io
              .delay(msToComplete, "milliseconds")
              .timeout(msToTimeout, "milliseconds")
              .run()
          ).rejects.toEqual(new TimeoutError());
        }
      )
    ))

Summary: A `TimeoutError` is raised when an asynchronous action's duration exceeds the specified timeout limit.


it("returns the same result as the base action if it completes within the timeout", () =>
    fc.assert(
      fc.asyncProperty(
        io,
        fc.integer({ min: 0, max: 8 }).map((x) => x * 5),
        fc.integer({ min: 0, max: 8 }).map((x) => x * 5),
        async (io, msToComplete, msToTimeout) => {
          fc.pre(msToComplete < msToTimeout);
          expect(
            await io
              .delay(msToComplete, "milliseconds")
              .timeout(msToTimeout, "milliseconds")
              .runSafe()
          ).toEqual(await io.runSafe());
        }
      )
    ))

Summary: The action should return the same result if it finishes within the specified timeout duration.


it("does not include other project files", async () => {
    await fc.assert(
      fc.asyncProperty(
        arb.folderPath(true),
        fc.set(arb.folderPath(), { minLength: 2 }),
        async (rootPath, folders) => {
          const root = seedFolder(rootPath, [], folders);
          const otherRoot = folders[folders.length - 1];
          const configPath = join(rootPath, otherRoot, TIDIER_CONFIG_NAME);
          root.volume[configPath] = "{}";

          const project = new Project(root, basicSettings);
          const entries = await project.list(Glob.ANYTHING, "folder");

          expect(entries.map(([path]) => path).includes(otherRoot)).toBe(false);
        }
      )
    );
  })

Summary: A `Project` instance should exclude other project files not located in its root when listing folder entries.


it("can be created with an initial value", () =>
    fc.assert(
      fc.asyncProperty(fc.anything(), async (initialValue) => {
        const program = Ref.create(initialValue).andThen(Ref.get);
        const result = await program.run();
        expect(result).toBe(initialValue);
      })
    ))

Summary: `Ref.create` initializes with a value, and `Ref.get` retrieves that same value.


it("can be updated using the set instance method", () =>
    fc.assert(
      fc.asyncProperty(
        fc.anything(),
        fc.anything(),
        async (initialValue, newValue) => {
          const program = Ref.create(initialValue)
            .through((ref) => ref.set(newValue))
            .andThen((ref) => ref.get);
          const result = await program.run();
          expect(result).toBe(newValue);
        }
      )
    ))

Summary: `Ref` updates correctly using the `set` method, ensuring the retrieved value matches the new value set.


it("can be updated using the set static method", () =>
    fc.assert(
      fc.asyncProperty(
        fc.anything(),
        fc.anything(),
        async (initialValue, newValue) => {
          const program = Ref.create(initialValue)
            .through(Ref.set(newValue))
            .andThen(Ref.get);
          const result = await program.run();
          expect(result).toBe(newValue);
        }
      )
    ))

Summary: Updating a reference using the `set` method should result in the reference holding the new value.


it("reads the file with the specified path on load and reload", async () => {
    await fc.assert(
      fc.asyncProperty(
        arb.folderPath(true),
        arb.fileName(),
        arbitraryIgnorePaths(),
        async (root, fileName, contents) => {
          const folder = new TestFolder(root, {
            [`${root}/${fileName}`]: contents.join("\n"),
          });
          const readFileSpy = jest.spyOn(folder, "readFile");
          const ignorefile = await Ignorefile.load(folder, fileName);
          await ignorefile.reload();

          expect(readFileSpy).toHaveBeenCalledWith(fileName);
          expect(readFileSpy).toHaveBeenLastCalledWith(fileName);
          expect(readFileSpy).toHaveBeenCalledTimes(2);
        }
      )
    );
  })

Summary: Upon loading and reloading, `Ignorefile` should read the specified file path twice.


it("can be updated with a pure function using the modify static method", () =>
    fc.assert(
      fc.asyncProperty(fc.integer(), async (initialValue) => {
        const program = Ref.create(initialValue).andThen((ref) =>
          // return a tuple of the result of "modify" and the ref's value
          // afterwards, to check they are equal.
          IO.sequence([Ref.modify((x: number) => x + 1)(ref), ref.get] as const)
        );

        const result = await program.run();
        expect(result[0]).toBe(initialValue + 1);
        expect(result[1]).toBe(initialValue + 1);
      })
    ))

Summary: Verifies that the `Ref.modify` method correctly updates a reference's value with a pure function, ensuring the result of the modification and the subsequent retrieval from the reference are equal to the incremented initial value.


it("does not add duplicate ignorefiles", () => {
    fc.assert(
      fc.property(arb.filePath(), (path) => {
        const folder = new TestFolder(path);
        const ignorefiles = [
          new Ignorefile({ path, folder, patterns: [], semantics: "glob" }),
          new Ignorefile({ path, folder, patterns: [], semantics: "glob" }),
        ];

        const ignore = new ProjectIgnore();

        for (const ignorefile of ignorefiles) {
          ignore.useIgnorefile(ignorefile);
        }

        expect(ignore.ignorefiles).toHaveLength(1);
      })
    );
  })

Summary: Adding duplicate ignore files should result in only one instance being maintained.


it("adds another IO which will run after the target IO, but retains the original result", () =>
    fc.assert(
      fc.asyncProperty(
        arbitraries.io,
        arbitraries.successfulIo,
        async (firstIo, secondIo) => {
          const firstIoOutcome = await firstIo.runSafe();

          const throughFunc = jest.fn(() => secondIo);
          const resultThroughAdditionalIo = await firstIo
            .through(throughFunc)
            .runSafe();

          expect(resultThroughAdditionalIo).toEqual(firstIoOutcome);

          if (firstIoOutcome.kind === OutcomeKind.Succeeded) {
            expect(throughFunc).toHaveBeenCalledTimes(1);
            expect(throughFunc).toHaveBeenCalledWith(firstIoOutcome.value);
          } else {
            expect(throughFunc).toHaveBeenCalledTimes(0);
          }
        }
      )
    ))

Summary: An additional IO operation is executed after the target IO without altering the original result, and the subsequent IO is invoked only if the initial IO succeeds.


it("always produces the same value when mapped with the identity function", () =>
      fc.assert(
        fc.asyncProperty(arbitraries.successfulIo, async (io) => {
          const identity = <X>(x: X) => x;
          expect(await io.map(identity).run()).toBe(await io.run());
        })
      ))

Summary: Mapping with the identity function on a functor should produce the same value as the original functor when executed.


it("always produces the same value when mapping two functions separately or as one", () =>
      fc.assert(
        fc.asyncProperty(
          arbitraries.successfulIo,
          arbitraries.unaryFunction,
          arbitraries.unaryFunction,
          async (io, f, g) => {
            const doubleMapped = io.map(f).map(g);
            const composed = io.map((x) => g(f(x)));
            expect(await doubleMapped.run()).toBe(await composed.run());
          }
        )
      ))

Summary: Mapping two functions separately produces the same value as mapping their composition.


it("ignores files that are ignored by settings", () => {
    fc.assert(
      fc.property(arbitraryIgnorePaths(), (paths) => {
        const ignore = new ProjectIgnore();
        ignore.use(paths);

        expect(ignore.patterns).toEqual(paths);

        for (const path of paths) {
          expect(ignore.ignores(path)).toBe(true);
        }
      })
    );
  })

Summary: When provided with a set of paths to ignore, `ProjectIgnore` should mark each of those paths as ignored.


it("ignores files that are ignored by ignorefiles", () => {
    fc.assert(
      fc.property(arbitraryIgnorePaths(), (paths) => {
        const ignore = new ProjectIgnore();
        const ignorefile = new Ignorefile({
          path: "/.gitignore",
          folder: new TestFolder("/"),
          patterns: paths,
          semantics: "gitignore",
        });

        ignore.useIgnorefile(ignorefile);

        for (const path of paths) {
          expect(ignore.ignores(path)).toBe(true);
        }
      })
    );
  })

Summary: Files listed in ignorefiles should be marked as ignored by the `ProjectIgnore` class.


test("different paths do not match each other", () => {
  fc.assert(
    fc.property(
      fc.set(fc.oneof(arb.filePath(), arb.folderPath()), {
        minLength: 2,
        maxLength: 2,
      }),
      ([one, theNext]) => !new Glob(one).matches(theNext)
    )
  );
})

Summary: Different file paths or folder paths should not match each other using the `Glob` class.


it("always produces the same value when mapped with the identity function", () =>
      fc.assert(
        fc.asyncProperty(arbitraries.unsuccessfulIo, async (unsuccessfulIo) => {
          const identity = <X>(x: X) => x;
          expect(await unsuccessfulIo.mapError(identity).runSafe()).toEqual(
            await unsuccessfulIo.runSafe()
          );
        })
      ))

Summary: Mapping with the identity function on `unsuccessfulIo` should produce the same value as before mapping.


it("always produces the same value when mapping two functions separately or as one", () =>
      fc.assert(
        fc.asyncProperty(
          arbitraries.unsuccessfulIo,
          arbitraries.unaryFunction,
          arbitraries.unaryFunction,
          async (unsuccessfulIo, f, g) => {
            const doubleMapped = unsuccessfulIo.mapError(f).mapError(g);
            const composed = unsuccessfulIo.mapError((x) => g(f(x)));
            expect(await doubleMapped.runSafe()).toEqual(
              await composed.runSafe()
            );
          }
        )
      ))

Summary: Mapping two functions separately or as one should produce the same result when applied to an error within `unsuccessfulIo`.


it("creates an IO which performs the side-effect when run", () =>
    fc.assert(
      fc.asyncProperty(fc.anything(), (effectResult) => {
        let effectPerformedCount = 0;
        const effect = () => {
          effectPerformedCount += 1;
          return effectResult;
        };
        const io = IO(effect);
        expect(effectPerformedCount).toBe(0);
        const result = io.run();
        expect(effectPerformedCount).toBe(1);
        return expect(result).resolves.toBe(effectResult);
      })
    ))

Summary: An `IO` executes a side-effect exactly once when its `run` method is called, and returns a promise resolving to the effect's result.


it("creates an IO which waits for a promise returned by the effect", () =>
    fc.assert(
      fc.asyncProperty(fc.anything(), (eventualValue) => {
        const effect = () => Promise.resolve(eventualValue);
        return expect(IO(effect).run()).resolves.toBe(eventualValue);
      })
    ))

Summary: An `IO` should properly wait for and resolve a promise returned by the effect, matching the expected value.


it("creates an IO which returns the wrapped values when run", () =>
    fc.assert(
      fc.asyncProperty(fc.anything(), (initialValue) =>
        expect(IO.wrap(initialValue).run()).resolves.toBe(initialValue)
      )
    ))

Summary: The `IO.wrap` function creates an IO that returns the wrapped value when `run` is called.


test("general casings return themselves", () => {
    fc.assert(
      fc.property(arb.generalCasing(), (casing) => {
        return resolveCasing(casing) === casing;
      })
    );
  })

Summary: The property ensures that the `resolveCasing` function returns the original casing for general casing inputs.


test("general aliases return a valid general casing", () => {
    fc.assert(
      fc.property(arb.generalCasingAlias(), (alias) => {
        return validateCasing(resolveCasing(alias)) !== null;
      })
    );
  })

Summary: General aliases should resolve to a valid general casing that passes validation.


it("creates an IO which rejects with the raised value when run", () =>
    fc.assert(
      fc.asyncProperty(fc.anything(), (raisedValue) =>
        expect(IO.raise(raisedValue).run()).rejects.toBe(raisedValue)
      )
    ))

Summary: `IO.raise` creates an IO instance that, when executed, rejects with the specified value.


it("passes the output of the parent IO to the next function", () =>
    fc.assert(
      fc.asyncProperty(fc.anything(), async (initialValue) => {
        const nextFunction = jest.fn(() => IO.void);
        await IO.wrap(initialValue).andThen(nextFunction).run();
        expect(nextFunction).toHaveBeenCalledTimes(1);
        expect(nextFunction).toHaveBeenCalledWith(initialValue);
      })
    ))

Summary: The output of an `IO` operation should be passed to the next function, which should be called once with that output.


it("makes no difference to IOs which succeed", () =>
    fc.assert(
      fc.asyncProperty(arbitraries.successfulIo, async (successfulIo) => {
        const withCatch = successfulIo.catch(() => IO.wrap("caught an error"));
        expect(await withCatch.run()).toEqual(await successfulIo.run());
      })
    ))

Summary: Successful IO operations remain unaffected by attaching a catch handler.


it("can always turn an unsuccessful IO into a successful one", () =>
    fc.assert(
      fc.asyncProperty(arbitraries.unsuccessfulIo, async (unsuccessfulIo) => {
        const withCatch = unsuccessfulIo.catch(() =>
          IO.wrap("caught an error")
        );
        expect(await withCatch.run()).toBe("caught an error");
      })
    ))

Summary: An unsuccessful IO operation can be transformed into a successful one by catching errors and wrapping the result with a predefined successful value.


it("makes no differences if it re-raises the error unchanged", () =>
    fc.assert(
      fc.asyncProperty(arbitraries.io, async (io) => {
        const withCatch = io.catch(IO.raise);
        expect(await withCatch.runSafe()).toEqual(await io.runSafe());
      })
    ))

Summary: Re-raising an error unchanged using `IO.raise` should result in no differences when compared to the original `io`.


it("fails to create a when the root is a file", async () => {
    await fc.assert(
      fc.asyncProperty(
        arb.folderPath(true),
        arb.fileName(),
        async (rootPath, filePath) => {
          seedVolume(rootPath, [filePath], []);
          const path = join(rootPath, filePath);

          const expected = expect(FileDirectory.resolve(path)).rejects;
          await expected.toThrow(/The path /);
          await expected.toThrow(filePath);
          await expected.toThrow(/does not resolve to a directory/);
        }
      )
    );
  })

Summary: A `FileDirectory.resolve` operation is expected to throw an error when the given path is a file rather than a directory.


it("combines an array of IOs into a single IO returning an array of the results", () =>
    fc.assert(
      fc.asyncProperty(fc.array(successfulIo), async (actions) => {
        const allResults = await Promise.all(actions.map((io) => io.run()));
        const parallelized = await IO.parallel(actions).run();
        expect(parallelized).toEqual(allResults);
      })
    ))

Summary: Combining an array of IO actions should return a single IO that produces an array of results equivalent to the results obtained by running each IO individually.


it("replaces the resulting value of a successful IO with a void value", () =>
    fc.assert(
      fc.asyncProperty(arbitraries.successfulIo, async (io) => {
        await expect(io.void.run()).resolves.toBe(void 0);
      })
    ))

Summary: A successful IO operation's result is replaced with a `void` value, verifying it resolves to `undefined`.


it("has no effect on an IO which fails", () =>
    fc.assert(
      fc.asyncProperty(arbitraries.unsuccessfulIo, async (io) => {
        const resultWithVoid = await io.void.runSafe();
        const resultWithoutVoid = await io.runSafe();
        expect(resultWithVoid).toEqual(resultWithoutVoid);
      })
    ))

Summary: An IO operation that fails retains the same result whether `void` is applied or not.


it("Propagates any errors raised by the open function", () =>
    fc.assert(
      fc.asyncProperty(arbitraries.unsuccessfulIo, async (willRaise) => {
        const bracketed = IO.bracket(willRaise, () => IO.void)(() => IO.void);
        expect(await bracketed.runSafe()).toEqual(await willRaise.runSafe());
      })
    ))

Summary: Errors raised by the `open` function are propagated when using `IO.bracket`.


it("Propagates any errors raised by the use function", () =>
    fc.assert(
      fc.asyncProperty(arbitraries.unsuccessfulIo, async (willRaise) => {
        const bracketed = IO.bracket(
          IO.wrap(null),
          () => IO.void
        )(() => willRaise);
        expect(await bracketed.runSafe()).toEqual(await willRaise.runSafe());
      })
    ))

Summary: The test ensures that `IO.bracket` correctly propagates errors raised by the `use` function when executed.


it("Propagates any errors raised by the close function", () =>
    fc.assert(
      fc.asyncProperty(
        arbitraries.io,
        arbitraries.unsuccessfulIo,
        async (mightRaise, willRaise) => {
          // If the `use` function raises, it still calls the `close` function.
          // If `close` also raises, the error from `close` takes priority.
          const bracketed = IO.bracket(
            IO.wrap(null),
            () => willRaise
          )(() => mightRaise);
          expect(await bracketed.runSafe()).toEqual(await willRaise.runSafe());
        }
      )
    ))

Summary: The test confirms that if both the `use` and `close` functions raise errors, the error from the `close` function is prioritized and propagated.


test('base64ToArray and arrayToBase64 roundtrip', () => {
	fc.assert(
		fc.property(
			fc.uint8Array({
				minLength: idLength,
				maxLength: idLength,
			}),
			(id) => {
				const base64 = arrayToBase64(id)
				const actual = base64ToArray(base64)
				expect(actual).toEqual(id)
			},
		),
	)
})

Summary: Converting an array to base64 and back should return the original array.


it("creates an IO which performs the side-effect when run", () =>
    fc.assert(
      fc.asyncProperty(fc.anything(), (effectResult) => {
        let effectPerformedCount = 0;
        const io = IO.cancelable(() => {
          effectPerformedCount += 1;
          return {
            promise: Promise.resolve(effectResult),
            cancel: () => {},
          };
        });
        expect(effectPerformedCount).toBe(0);
        const result = io.run();
        expect(effectPerformedCount).toBe(1);
        return expect(result).resolves.toBe(effectResult);
      })
    ))

Summary: An `IO` created with `IO.cancelable` performs a side-effect exactly once when run, returning a promise that resolves to the expected result.


test('base64urlToArray and arrayToBase64url roundtrip', () => {
	fc.assert(
		fc.property(
			fc.uint8Array({
				minLength: idLength,
				maxLength: idLength,
			}),
			(id) => {
				const base64url = arrayToBase64url(id)
				const actual = base64urlToArray(base64url)
				expect(actual).toEqual(id)
			},
		),
	)
})

Summary: `base64urlToArray` and `arrayToBase64url` should correctly convert between base64url strings and uint8 arrays, ensuring reversibility.


test("encoded, decode pairing ApplyChangesOnPrimary", () => {
  fc.assert(
    fc.property(
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      fc.array(
        fc.tuple(
          fc.string(),
          fc.uint8Array({ minLength: 1, maxLength: 10 }),
          fc.string(),
          fc.oneof(
            fc.string(),
            fc.boolean(),
            fc.integer(),
            fc.double(),
            fc.constant(null),
            fc.bigInt({
              min: BigInt(Number.MAX_SAFE_INTEGER) + 1n,
              max: 9223372036854775807n,
            }),
            fc.uint8Array()
          ),
          fc.bigIntN(64),
          fc.bigIntN(64),
          fc.oneof(
            fc.uint8Array({ minLength: 16, maxLength: 16 }),
            fc.constant(null)
          ),
          fc.bigIntN(64),
          fc.integer({ min: 0 })
        )
      ),
      fc.string(),
      fc.tuple(fc.bigIntN(64), fc.integer({ min: 0 })),
      (sender, changes, room, newLastSeen) => {
        const msg = {
          _tag: tags.ApplyChangesOnPrimary,
          _reqid: 0,
          sender,
          changes,
          room,
          newLastSeen,
        } as const;
        const encoded = encode(msg);
        const decoded = decode(encoded);
        expect(decoded).toEqual(msg);
      }
    )
  );
})

Summary: Encoding and decoding functions should be inverse operations for messages formatted with the `ApplyChangesOnPrimary` structure, ensuring the decoded message matches the original.


test("encoded, decode pairing CraeteDBOnPrimary", () => {
  fc.assert(
    fc.property(
      fc.string(),
      fc.bigIntN(64),
      fc.string(),
      (schemaName, schemaVersion, room) => {
        const msg = {
          _tag: tags.CreateDbOnPrimary,
          _reqid: 0,
          schemaName,
          schemaVersion,
          room,
        } as const;
        const encoded = encode(msg);
        const decoded = decode(encoded);
        expect(decoded).toEqual(msg);
      }
    )
  );
})

Summary: Encoding and decoding a `CreateDbOnPrimary` message should preserve the original message properties: `_tag`, `_reqid`, `schemaName`, `schemaVersion`, and `room`.


test("prepend append", () => {
  fc.assert(
    fc.property(
      fc.array(fc.oneof(fc.constant(1), fc.constant(-1))),
      (operations) => {
        const db = setupDb();

        const expected = [];
        let id = 1;
        for (const op of operations) {
          if (op == 1) {
            expected.push(id);
          } else {
            expected.unshift(id);
          }

          db.exec(`INSERT INTO todo VALUES (${id}, 1, ${op}, 'test')`);
          ++id;
        }

        let actual = db
          .execA("SELECT id FROM todo ORDER BY ordering")
          .map((r) => r[0]);
        expect(actual).toEqual(expected);
      }
    )
  );
})

Summary: Verifies that inserting values into the `todo` table with ordering operations (`prepend` or `append`) results in an ordered list of IDs that matches the expected sequence.


test("randomized inserts, updates, deletes then sync", () => {
  fc.assert(
    fc.property(
      fc
        .oneof(
          fc.constant("round-trip"),
          fc.constant("pairwise"),
          fc.constant("centralized"),
          fc.constant("loop")
        )
        .noShrink(),
      fc
        .shuffledSubarray(["modify", "delete", "reinsert", "create"], {
          minLength: 4,
        })
        .noShrink(),
      fc
        .array(fc.tuple(fc.integer(), fc.string(), fc.boolean()), {
          maxLength: 20,
        })
        .noShrink(),
      fc
        .array(fc.tuple(fc.integer(), fc.string(), fc.boolean()), {
          maxLength: 500,
        })
        .noShrink(),
      randomizedTestCase
    )
  );
})

Summary: The system performs a series of random inserts, updates, and deletes, followed by a synchronization check using different scenarios and orderings.


test("encoded, decode pairing ApplyChangesMsg", () => {
  fc.assert(
    fc.property(
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      fc.bigIntN(64),
      fc.tuple(fc.bigIntN(64), fc.integer({ min: 0 })),
      fc.tuple(fc.bigIntN(64), fc.integer({ min: 0 })),
      fc.array(
        fc.tuple(
          fc.string(),
          fc.uint8Array({ minLength: 1, maxLength: 10 }),
          fc.string(),
          fc.oneof(
            fc.string(),
            fc.boolean(),
            fc.integer(),
            fc.bigInt({
              min: BigInt(Number.MAX_SAFE_INTEGER) + 1n,
              max: 9223372036854775807n,
            }),
            fc.uint8Array()
          ),
          fc.bigIntN(64),
          fc.bigIntN(64),
          fc.bigIntN(64),
          fc.integer({ min: 0 })
        )
      ),
      (toDbid, fromDbid, schemaVersion, seqStart, seqEnd, changes) => {
        const serializer = new JsonSerializer();
        const msg = {
          _tag: tags.applyChanges,
          toDbid,
          fromDbid,
          schemaVersion,
          seqStart,
          seqEnd,
          changes,
        } as const;
        const encoded = JSON.stringify(serializer.encode(msg));
        const decoded = serializer.decode(JSON.parse(encoded));
        expect(decoded).toEqual(msg);
      }
    )
  );
})

Summary: Encoding and decoding of `ApplyChangesMsg` should be consistent when passed through `JsonSerializer`, ensuring the decoded message is equal to the original.


test("encode, decode pairing for GetChangesMsg", () => {
  fc.assert(
    fc.property(
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      fc.tuple(fc.bigIntN(64), fc.integer({ min: 0 })),
      fc.bigIntN(64),
      (dbid, requestorDbid, since, schemaVersion) => {
        const serializer = new JsonSerializer();
        const msg = {
          _tag: tags.getChanges,
          dbid,
          requestorDbid,
          since,
          schemaVersion,
        } as const;
        const encoded = JSON.stringify(serializer.encode(msg));
        const decoded = serializer.decode(JSON.parse(encoded));
        expect(decoded).toEqual(msg);
      }
    )
  );
})

Summary: Encoding and decoding a `GetChangesMsg` should be consistent, such that decoding the encoded message reproduces the original message.


test("StreamingChangesMsg", () => {
  fc.assert(
    fc.property(
      fc.tuple(fc.bigIntN(64), fc.integer({ min: 0 })),
      fc.tuple(fc.bigIntN(64), fc.integer({ min: 0 })),
      fc.array(
        fc.tuple(
          fc.string(),
          fc.uint8Array({ minLength: 1, maxLength: 10 }),
          fc.string(),
          fc.oneof(
            fc.string(),
            fc.boolean(),
            fc.integer(),
            fc.bigInt({
              min: BigInt(Number.MAX_SAFE_INTEGER) + 1n,
              max: 9223372036854775807n,
            }),
            fc.uint8Array()
          ),
          fc.bigIntN(64),
          fc.bigIntN(64),
          fc.bigIntN(64),
          fc.integer({ min: 0 })
        )
      ),
      (seqStart, seqEnd, changes) => {
        const serializer = new JsonSerializer();
        const msg = {
          _tag: tags.streamingChanges,
          seqStart,
          seqEnd,
          changes,
        } as const;
        const encoded = JSON.stringify(serializer.encode(msg));
        const decoded = serializer.decode(JSON.parse(encoded));
        expect(decoded).toEqual(msg);
      }
    )
  );
})

Summary: The property ensures that encoding and then decoding a `StreamingChangesMsg` via `JsonSerializer` results in the original message.


test("CreateOrMigrateResponse", () => {
  fc.assert(
    fc.property(
      fc.tuple(fc.bigIntN(64), fc.integer({ min: 0 })),
      fc.oneof(
        fc.constant("noop"),
        fc.constant("apply"),
        fc.constant("migrate")
      ),
      (seq, status) => {
        const serializer = new JsonSerializer();
        const msg = {
          _tag: tags.createOrMigrateResponse,
          seq,
          status,
        } as const;
        const encoded = JSON.stringify(serializer.encode(msg as any));
        const decoded = serializer.decode(JSON.parse(encoded));
        expect(decoded).toEqual(msg);
      }
    )
  );
})

Summary: The `JsonSerializer` should correctly encode and decode `CreateOrMigrateResponse` messages, preserving their structure and contents including `seq` and `status`.


test("CreateOrMigrateMsg", () => {
  fc.assert(
    fc.property(
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      fc.string(),
      fc.bigIntN(64),
      (dbid, requestorDbid, schemaName, schemaVersion) => {
        const serializer = new JsonSerializer();
        const msg = {
          _tag: tags.createOrMigrate,
          dbid,
          requestorDbid,
          schemaName,
          schemaVersion,
        } as const;
        const encoded = JSON.stringify(serializer.encode(msg));
        const decoded = serializer.decode(JSON.parse(encoded));
        expect(decoded).toEqual(msg);
      }
    )
  );
})

Summary: Ensures that encoding and then decoding a `CreateOrMigrateMsg` using `JsonSerializer` retains the original message structure and content.


test("GetLastSeenMsg", () => {
  fc.assert(
    fc.property(
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      (toDbid, fromDbid) => {
        const serializer = new JsonSerializer();
        const msg = {
          _tag: tags.getLastSeen,
          toDbid,
          fromDbid,
        } as const;
        const encoded = JSON.stringify(serializer.encode(msg));
        const decoded = serializer.decode(JSON.parse(encoded));
        expect(decoded).toEqual(msg);
      }
    )
  );
})

Summary: Verifies that a message containing `toDbid` and `fromDbid` fields is correctly encoded and then decoded by the `JsonSerializer`, preserving the original message structure and content.


test("GetLastSeenResponse", () => {
  fc.assert(
    fc.property(fc.tuple(fc.bigIntN(64), fc.integer({ min: 0 })), (seq) => {
      const serializer = new JsonSerializer();
      const msg = {
        _tag: tags.getLastSeenResponse,
        seq,
      } as const;
      const encoded = JSON.stringify(serializer.encode(msg));
      const decoded = serializer.decode(JSON.parse(encoded));
      expect(decoded).toEqual(msg);
    })
  );
})

Summary: The property involves encoding and decoding a `GetLastSeenResponse` message, ensuring that decoding the encoded message yields the original message.


test("ActivateSchemaMsg", () => {
  fc.assert(
    fc.property(fc.string(), fc.bigIntN(64), (name, version) => {
      const serializer = new JsonSerializer();
      const msg = {
        _tag: tags.activateSchema,
        name,
        version,
      } as const;
      const encoded = JSON.stringify(serializer.encode(msg));
      const decoded = serializer.decode(JSON.parse(encoded));
      expect(decoded).toEqual(msg);
    })
  );
})

Summary: `JsonSerializer` encodes and decodes `ActivateSchemaMsg` objects with string names and 64-bit integer versions correctly, maintaining object integrity.


test("encoded, decode pairing ApplyChangesMsg", () => {
  fc.assert(
    fc.property(
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      fc.bigIntN(64),
      fc.tuple(fc.bigIntN(64), fc.integer({ min: 0 })),
      fc.tuple(fc.bigIntN(64), fc.integer({ min: 0 })),
      fc.array(
        fc.tuple(
          fc.string(),
          fc.uint8Array({ minLength: 1, maxLength: 10 }),
          fc.string(),
          fc.oneof(
            fc.string(),
            fc.boolean(),
            fc.integer(),
            fc.bigInt({
              min: BigInt(Number.MAX_SAFE_INTEGER) + 1n,
              max: 9223372036854775807n,
            }),
            fc.uint8Array()
          ),
          fc.bigIntN(64),
          fc.bigIntN(64),
          fc.bigIntN(64),
          fc.integer({ min: 0 })
        )
      ),
      (toDbid, fromDbid, schemaVersion, seqStart, seqEnd, changes) => {
        const serializer = new BinarySerializer();
        const msg: ApplyChangesMsg = {
          _tag: tags.applyChanges,
          toDbid,
          fromDbid,
          schemaVersion,
          seqStart,
          seqEnd,
          changes,
        } as const;
        const encoded = serializer.encode(msg);
        const decoded = serializer.decode(encoded);
        expect(decoded).toEqual(msg);
      }
    )
  );
})

Summary: Encoding and decoding of `ApplyChangesMsg` should result in the original message remaining unchanged.


test("encode, decode pairing for GetChangesMsg", () => {
  fc.assert(
    fc.property(
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      fc.tuple(fc.bigIntN(64), fc.integer({ min: 0 })),
      fc.bigIntN(64),
      (dbid, requestorDbid, since, schemaVersion) => {
        const serializer = new BinarySerializer();
        const msg: GetChangesMsg = {
          _tag: tags.getChanges,
          dbid,
          requestorDbid,
          since,
          schemaVersion,
        } as const;
        const encoded = serializer.encode(msg);
        const decoded = serializer.decode(encoded);
        expect(decoded).toEqual(msg);
      }
    )
  );
})

Summary: `BinarySerializer` correctly encodes and decodes `GetChangesMsg` messages, ensuring the decoded message matches the original.


test("encode, decode pairing for EstablishOutboundStreamMsg", () => {
  fc.assert(
    fc.property(
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      fc.tuple(fc.bigIntN(64), fc.integer({ min: 0 })),
      fc.bigIntN(64),
      (toDbid, fromDbid, seqStart, schemaVersion) => {
        const serializer = new BinarySerializer();
        const msg: EstablishOutboundStreamMsg = {
          _tag: tags.establishOutboundStream,
          toDbid,
          fromDbid,
          seqStart,
          schemaVersion,
        } as const;
        const encoded = serializer.encode(msg);
        const decoded = serializer.decode(encoded);
        expect(decoded).toEqual(msg);
      }
    )
  );
})

Summary: Encoding and decoding an `EstablishOutboundStreamMsg` with `BinarySerializer` should yield equivalent messages.


test("encode, decode pairing for AckChangesMsg", () => {
  fc.assert(
    fc.property(fc.tuple(fc.bigIntN(64), fc.integer({ min: 0 })), (seqEnd) => {
      const serializer = new BinarySerializer();
      const msg = { _tag: tags.ackChanges, seqEnd } as const;
      const encoded = serializer.encode(msg);
      const decoded = serializer.decode(encoded);
      expect(decoded).toEqual(msg);
    })
  );
})

Summary: Encoding and decoding of `AckChangesMsg` should result in the original message.


test("StreamingChangesMsg", () => {
  fc.assert(
    fc.property(
      fc.tuple(fc.bigIntN(64), fc.integer({ min: 0 })),
      fc.tuple(fc.bigIntN(64), fc.integer({ min: 0 })),
      fc.array(
        fc.tuple(
          fc.string(),
          fc.uint8Array({ minLength: 1, maxLength: 10 }),
          fc.string(),
          fc.oneof(
            fc.string(),
            fc.boolean(),
            fc.integer(),
            fc.bigInt({
              min: BigInt(Number.MAX_SAFE_INTEGER) + 1n,
              max: 9223372036854775807n,
            }),
            fc.uint8Array()
          ),
          fc.bigIntN(64),
          fc.bigIntN(64),
          fc.bigIntN(64),
          fc.integer({ min: 0 })
        )
      ),
      (seqStart, seqEnd, changes) => {
        const serializer = new BinarySerializer();
        const msg = {
          _tag: tags.streamingChanges,
          seqStart,
          seqEnd,
          changes,
        } as const;
        const encoded = serializer.encode(msg);
        const decoded = serializer.decode(encoded);
        expect(decoded).toEqual(msg);
      }
    )
  );
})

Summary: The `BinarySerializer` correctly encodes and decodes `StreamingChangesMsg` objects, preserving their original properties.


test("CreateOrMigrateResponse", () => {
  fc.assert(
    fc.property(
      fc.tuple(fc.bigIntN(64), fc.integer({ min: 0 })),
      fc.oneof(
        fc.constant("noop"),
        fc.constant("apply"),
        fc.constant("migrate")
      ),
      (seq, status) => {
        const serializer = new BinarySerializer();
        const msg = {
          _tag: tags.createOrMigrateResponse,
          seq,
          status,
        } as const;
        const encoded = serializer.encode(msg as any);
        const decoded = serializer.decode(encoded);
        expect(decoded).toEqual(msg);
      }
    )
  );
})

Summary: Encoding and then decoding a `CreateOrMigrateResponse` message should return the original message object.


test("CreateOrMigrateMsg", () => {
  fc.assert(
    fc.property(
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      fc.string(),
      fc.bigIntN(64),
      (dbid, requestorDbid, schemaName, schemaVersion) => {
        const serializer = new BinarySerializer();
        const msg = {
          _tag: tags.createOrMigrate,
          dbid,
          requestorDbid,
          schemaName,
          schemaVersion,
        } as const;
        const encoded = serializer.encode(msg);
        const decoded = serializer.decode(encoded);
        expect(decoded).toEqual(msg);
      }
    )
  );
})

Summary: Encoding and decoding a `CreateOrMigrateMsg` should preserve its original content.


test("GetLastSeenMsg", () => {
  fc.assert(
    fc.property(
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      fc.uint8Array({ minLength: 16, maxLength: 16 }),
      (toDbid, fromDbid) => {
        const serializer = new BinarySerializer();
        const msg = {
          _tag: tags.getLastSeen,
          toDbid,
          fromDbid,
        } as const;
        const encoded = serializer.encode(msg);
        const decoded = serializer.decode(encoded);
        expect(decoded).toEqual(msg);
      }
    )
  );
})

Summary: Encoding and then decoding a message with `BinarySerializer` should return the original message for `GetLastSeenMsg` objects.


test("GetLastSeenResponse", () => {
  fc.assert(
    fc.property(fc.tuple(fc.bigIntN(64), fc.integer({ min: 0 })), (seq) => {
      const serializer = new BinarySerializer();
      const msg = {
        _tag: tags.getLastSeenResponse,
        seq,
      } as const;
      const encoded = serializer.encode(msg);
      const decoded = serializer.decode(encoded);
      expect(decoded).toEqual(msg);
    })
  );
})

Summary: Encoding and decoding a `GetLastSeenResponse` message with a tuple of a 64-bit integer and a non-negative integer should yield an equivalent message.


test("UploadSchemaMsg", () => {
  fc.assert(
    fc.property(
      fc.string(),
      fc.bigIntN(64),
      fc.string(),
      fc.boolean(),
      (name, version, content, activate) => {
        const serializer = new BinarySerializer();
        const msg = {
          _tag: tags.uploadSchema,
          name,
          version,
          content,
          activate,
        } as const;
        const encoded = serializer.encode(msg);
        const decoded = serializer.decode(encoded);
        expect(decoded).toEqual(msg);
      }
    )
  );
})

Summary: Encoding and decoding an `UploadSchemaMsg` using `BinarySerializer` should return the original message.


test("ActivateSchemaMsg", () => {
  fc.assert(
    fc.property(fc.string(), fc.bigIntN(64), (name, version) => {
      const serializer = new BinarySerializer();
      const msg = {
        _tag: tags.activateSchema,
        name,
        version,
      } as const;
      const encoded = serializer.encode(msg);
      const decoded = serializer.decode(encoded);
      expect(decoded).toEqual(msg);
    })
  );
})

Summary: Encoding and then decoding a schema activation message should return the original message intact.


it("should return an empty string if no valid fuzzed inputs are provided", () => {
      fc.assert(
        fc.property(fc.string(), (className) => {
          if (className.trim() === "") {
            return expect(cn(className)).toBe("");
          }

          expect(cn(className)).toBe(className.trim().replace(/\s{2,}/g, " "));
        }),
      );
    })

Summary: When no valid fuzzed inputs are provided, `cn` should return an empty string; otherwise, it trims the input and replaces consecutive spaces with a single space.


it('should handle date arithmetic correctly', () => {
      fc.assert(
        fc.property(
          fc.date({ min: new Date('2024-01-01'), max: new Date('2024-12-31') }),
          fc.integer({ min: -365, max: 365 }),
          (baseDate, dayOffset) => {
            // Property: Adding and subtracting days should be reversible
            const futureDate = new Date(baseDate.getTime() + dayOffset * 24 * 60 * 60 * 1000);
            const backToBase = new Date(futureDate.getTime() - dayOffset * 24 * 60 * 60 * 1000);

            // Should be equal within millisecond precision
            return Math.abs(baseDate.getTime() - backToBase.getTime()) < 1000;
          },
        ),
        getPropertyTestConfig('fast'),
      );
    })

Summary: Adding and then subtracting days from a base date should result in the original date, within millisecond precision.


it('should identify school days correctly', () => {
      fc.assert(
        fc.property(domainArbitraries.schoolDay, (date) => {
          // Property: School days should be Monday through Friday
          const dayOfWeek = date.getDay();
          return dayOfWeek >= 1 && dayOfWeek <= 5;
        }),
        getPropertyTestConfig('fast'),
      );
    })

Summary: School days are identified as Monday through Friday, based on the day of the week.


it('should calculate school weeks correctly', () => {
      fc.assert(
        fc.property(
          fc.tuple(domainArbitraries.schoolDay, domainArbitraries.schoolDay),
          ([startDate, endDate]) => {
            // Property: School week calculations should be consistent
            if (startDate > endDate) {
              [startDate, endDate] = [endDate, startDate];
            }

            const msPerDay = 24 * 60 * 60 * 1000;
            const msPerWeek = 7 * msPerDay;
            const timeDiff = endDate.getTime() - startDate.getTime();
            const daysDiff = Math.floor(timeDiff / msPerDay);
            const weeksDiff = Math.floor(timeDiff / msPerWeek);

            // Relationship between days and weeks should be consistent
            return weeksDiff >= 0 && weeksDiff <= Math.floor(daysDiff / 7);
          },
        ),
        getPropertyTestConfig('fast'),
      );
    })

Summary: The calculation of school weeks between two dates should be consistent with the difference in days, ensuring weeks are non-negative and not exceeding the total days divided by seven.


it('should validate term date ranges', () => {
      fc.assert(
        fc.property(
          fc.tuple(
            fc.date({ min: new Date('2024-09-01'), max: new Date('2024-12-20') }), // Fall term
            fc.date({ min: new Date('2025-01-08'), max: new Date('2025-06-30') }), // Spring term
          ),
          ([fallEnd, springStart]) => {
            // Property: Term breaks should provide adequate time off
            const msPerDay = 24 * 60 * 60 * 1000;
            const breakDays = Math.floor((springStart.getTime() - fallEnd.getTime()) / msPerDay);

            // Should have at least 10 days break between terms
            return breakDays >= 10;
          },
        ),
        getPropertyTestConfig('fast'),
      );
    })

Summary: Validates that the break between the fall and spring term dates is at least 10 days.


it('should maintain lesson time boundaries', () => {
      fc.assert(
        fc.property(domainArbitraries.validTimeSlot, (timeSlot) => {
          // Property: Lesson end time should equal start time + duration
          const expectedEndTime = new Date(
            timeSlot.start.getTime() + timeSlot.duration * 60 * 1000,
          );

          return Math.abs(timeSlot.end.getTime() - expectedEndTime.getTime()) < 1000; // Within 1 second
        }),
        getPropertyTestConfig('fast'),
      );
    })

Summary: Ensures that the lesson end time equals the start time plus the duration, within a 1-second margin.


it('should respect daily time limits', () => {
      fc.assert(
        fc.property(
          fc.array(domainArbitraries.validTimeSlot, { minLength: 1, maxLength: 8 }),
          (timeSlots) => {
            // Property: All time slots should fit within a school day
            const schoolStartMs = 8 * 60 * 60 * 1000; // 8 AM in milliseconds from midnight
            const schoolEndMs = 16 * 60 * 60 * 1000; // 4 PM in milliseconds from midnight

            return timeSlots.every((slot) => {
              const startOfDay = new Date(slot.start);
              startOfDay.setHours(0, 0, 0, 0);

              const startMs = slot.start.getTime() - startOfDay.getTime();
              const endMs = slot.end.getTime() - startOfDay.getTime();

              return startMs >= schoolStartMs && endMs <= schoolEndMs;
            });
          },
        ),
        getPropertyTestConfig('fast'),
      );
    })

Summary: Time slots should fit within the school day, starting no earlier than 8 AM and ending no later than 4 PM.


it('should handle duration arithmetic', () => {
      fc.assert(
        fc.property(
          fc.tuple(domainArbitraries.lessonDuration, domainArbitraries.lessonDuration),
          ([duration1, duration2]) => {
            // Property: Duration addition should be commutative
            const sum1 = duration1 + duration2;
            const sum2 = duration2 + duration1;

            return sum1 === sum2;
          },
        ),
        getPropertyTestConfig('fast'),
      );
    })

Summary: Duration addition should be commutative, ensuring that the sum of two durations is the same regardless of their order.


it("should parse CSV", async () => {
    await fc.assert(
      fc.asyncProperty(
        fc.gen().map((g) => {
          const header = g(FC.header, {
            // TextEncoderStream can't handle utf-16 string.
            fieldConstraints: {
              kindExcludes: ["string16bits"],
            },
          });
          const EOL = g(FC.eol);
          const csvData = g(FC.csvData, {
            // TextEncoderStream can't handle utf-16 string.
            fieldConstraints: {
              kindExcludes: ["string16bits"],
            },
            columnsConstraints: {
              minLength: header.length,
              maxLength: header.length,
            },
          });
          const EOF = g(fc.boolean);
          const csv = [
            header.map((v) => escapeField(v, { quote: true })).join(","),
            ...csvData.map((row) =>
              row.map((v) => escapeField(v, { quote: true })).join(","),
            ),
            ...(EOF ? [""] : []),
          ].join(EOL);
          const data =
            csvData.length >= 1
              ? csvData.map((row) =>
                  Object.fromEntries(row.map((v, i) => [header[i], v])),
                )
              : [];
          return {
            data,
            // csv:
            response: new Response(
              new SingleValueReadableStream(csv).pipeThrough(
                new TextEncoderStream(),
              ),
              {
                headers: {
                  "content-type": "text/csv",
                },
              },
            ),
          };
        }),
        async ({ data, response }) => {
          let i = 0;
          for await (const row of parseResponse(response)) {
            expect(data[i++]).toStrictEqual(row);
          }
        },
      ),
    );
  })

Summary: Parsing a CSV response to a stream should correctly convert each row into an equivalent JavaScript object representation, ensuring that the parsed data matches the original structured data used to generate the CSV.


it('should validate lesson distribution over time', () => {
      fc.assert(
        fc.property(domainArbitraries.unitTimeline, (timeline) => {
          // Property: Lesson distribution should be feasible
          const { startDate, endDate, lessonCount } = timeline;

          const msPerDay = 24 * 60 * 60 * 1000;
          const totalDays = Math.ceil((endDate.getTime() - startDate.getTime()) / msPerDay);
          const schoolDays = Math.floor((totalDays * 5) / 7); // Approximate school days (5/7 of total)

          // Should not exceed 1 lesson per school day on average
          const lessonsPerSchoolDay = lessonCount / Math.max(schoolDays, 1);

          return lessonsPerSchoolDay <= 1.5; // Allow some flexibility
        }),
        getPropertyTestConfig('fast'),
      );
    })

Summary: Lesson distribution within a given timeline should average no more than 1.5 lessons per school day.