it('should produce distinct results from cryptoGetRandomValues for multiple calls with the default seed', async () => {
            const actor = await getCanisterActor<Actor>('canister');

            await fc.assert(
                fc.asyncProperty(
                    fc.integer({ min: 1, max: 100 }),
                    fc.integer({ min: 2, max: 10 }),
                    async (length, rawNumCalls) => {
                        const numCalls = normalizeNumCalls(length, rawNumCalls);

                        let results = new Set<string>();

                        for (let i = 0; i < numCalls; i++) {
                            const result =
                                await actor.cryptoGetRandomValues(length);

                            results.add(Buffer.from(result).toString('hex'));
                        }

                        expect(results.size).toBe(numCalls);
                    }
                ),
                defaultPropTestParams()
            );
        })

Summary: `cryptoGetRandomValues` should produce distinct results on successive calls with the default seed, even when limited by normalized call numbers for a given length.


unknown

it('should produce the same results from cryptoGetRandomValues when using the same seed', async () => {
            const actor = await getCanisterActor<Actor>('canister');

            await fc.assert(
                fc.asyncProperty(
                    fc.integer({ min: 1, max: 100 }),
                    fc.integer({ min: 2, max: 10 }),
                    fc.uint8Array({ minLength: 32, maxLength: 32 }),
                    async (length, rawNumCalls, seed) => {
                        const numCalls = normalizeNumCalls(length, rawNumCalls);

                        await actor.seed(seed);

                        let results: string[] = [];

                        for (let i = 0; i < numCalls; i++) {
                            const result =
                                await actor.cryptoGetRandomValues(length);

                            results.push(Buffer.from(result).toString('hex'));
                        }

                        await actor.seed(seed);

                        for (let i = 0; i < numCalls; i++) {
                            const result =
                                await actor.cryptoGetRandomValues(length);

                            expect(Buffer.from(result).toString('hex')).toBe(
                                results[i]
                            );
                        }
                    }
                ),
                defaultPropTestParams()
            );
        })

Summary: `cryptoGetRandomValues` produces consistent outputs when using the same seed across multiple calls.


it('should produce distinct results from cryptoGetRandomValues when using different seeds', async () => {
            const actor = await getCanisterActor<Actor>('canister');

            await fc.assert(
                fc.asyncProperty(
                    fc.integer({ min: 1, max: 100 }),
                    fc.integer({ min: 2, max: 10 }),
                    fc
                        .tuple(
                            fc.uint8Array({ minLength: 32, maxLength: 32 }),
                            fc.uint8Array({ minLength: 32, maxLength: 32 })
                        )
                        .filter(
                            ([seed1, seed2]) => sameSeed(seed1, seed2) === false
                        ),
                    async (length, rawNumCalls, [seed1, seed2]) => {
                        const numCalls = normalizeNumCalls(length, rawNumCalls);

                        await actor.seed(seed1);

                        let results: string[] = [];

                        for (let i = 0; i < numCalls; i++) {
                            const result =
                                await actor.cryptoGetRandomValues(length);

                            results.push(Buffer.from(result).toString('hex'));
                        }

                        await actor.seed(seed2);

                        for (let i = 0; i < numCalls; i++) {
                            const result =
                                await actor.cryptoGetRandomValues(length);

                            expect(
                                Buffer.from(result).toString('hex')
                            ).not.toBe(results[i]);
                        }
                    }
                ),
                defaultPropTestParams()
            );
        })

Summary: `cryptoGetRandomValues` should produce different results when initialized with different seeds.


it('should always reply with the input in alwaysReplyQuery and alwaysReplyUpdate', async () => {
            const context: Context<CandidValueConstraints> = {
                constraints: {}
            };
            await fc.assert(
                fc.asyncProperty(
                    CandidValueAndMetaArbGenerator(
                        context,
                        candidDefinitionArb(context, {}),
                        CandidValueArb
                    ),
                    fc.uuid(),
                    async (input, uuid) => {
                        const {
                            src: {
                                typeAnnotation: tsType,
                                imports,
                                typeObject: idlType,
                                variableAliasDeclarations
                            },
                            value: { agentArgumentValue }
                        } = input;
                        const canister = await setupCanister(
                            uuid,
                            idlType,
                            tsType,
                            Array.from(imports.add('msgArgData')),
                            variableAliasDeclarations
                        );

                        const queryResult =
                            await canister.alwaysReplyQuery(agentArgumentValue);
                        expect(queryResult).toEqual(agentArgumentValue);

                        const updateResult =
                            await canister.alwaysReplyUpdate(
                                agentArgumentValue
                            );
                        expect(updateResult).toEqual(agentArgumentValue);

                        cleanUpCanister(uuid);
                    }
                ),
                defaultPropTestParams()
            );
        })

Summary: Both `alwaysReplyQuery` and `alwaysReplyUpdate` should return the exact input they receive.


it('should always reject with the provided message in alwaysRejectQuery', async () => {
            const canister = await getCanisterActor<Actor>('canister');
            await fc.assert(
                fc.asyncProperty(fc.string(), async (message) => {
                    await expect(
                        canister.alwaysRejectQuery(message)
                    ).rejects.toThrow(
                        message.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
                    );
                }),
                defaultPropTestParams()
            );
        })

Summary: `alwaysRejectQuery` should always throw a rejection containing the provided message, ensuring proper handling of string escape characters.


it('should reply with even numbers and reject odd numbers in evenOrRejectUpdate', async () => {
            const canister = await getCanisterActor<Actor>('canister');
            await fc.assert(
                fc.asyncProperty(fc.bigInt(), async (number) => {
                    if (number % 2n === 0n) {
                        const result =
                            await canister.evenOrRejectUpdate(number);
                        expect(result).toBe(number);
                    } else {
                        await expect(
                            canister.evenOrRejectUpdate(number)
                        ).rejects.toThrow('Odd numbers are rejected');
                    }
                }),
                defaultPropTestParams()
            );
        })

Summary: The property ensures that `evenOrRejectUpdate` replies with the input number for even inputs and rejects with an error message for odd inputs.


it('should not print Fizz when not divisible by 3', () => {
  fc.assert(
    fc.property(
      fc.oneof(
        fc.nat().map((n) => n * 3 + 1),
        fc.nat().map((n) => n * 3 + 2)
      ),
      (n) => {
        expect(fizzbuzz(n)).not.toContain('Fizz');
      }
    )
  );
})

Summary: The `fizzbuzz` function should not return 'Fizz' for numbers not divisible by 3.


it('should print Buzz whenever divisible by 5', () => {
  fc.assert(
    fc.property(
      fc.nat().map((n) => n * 5),
      (n) => {
        expect(fizzbuzz(n)).toContain('Buzz');
      }
    )
  );
})

Summary: The `fizzbuzz` function should include "Buzz" in its output for numbers divisible by 5.


it('should print Fizz Buzz whenever divisible by 3 and 5', () => {
  fc.assert(
    fc.property(
      fc
        .nat()
        .map((n) => n * 3)
        .map((n) => n * 5),
      (n) => {
        expect(fizzbuzz(n)).toBe('Fizz Buzz');
      }
    )
  );
})

Summary: The function `fizzbuzz` returns "Fizz Buzz" for numbers divisible by both 3 and 5.


it("should produce an array such that the product equals the input", () => {
  fc.assert(
    fc.property(fc.integer({ min: 2, max: 2 ** 31 - 1 }), (n) => {
      const factors = decomposeIntoPrimes(n);
      const productOfFactors = factors.reduce((a, b) => a * b, 1);
      return productOfFactors === n;
    })
  );
})

Summary: Decomposing an integer into its prime factors should result in an array whose product equals the original integer.


it('should only print Fizz when divisible by 3 but not by 5', () => {
  fc.assert(
    fc.property(
      fc.oneof(
        fc
          .nat()
          .map((n) => n * 5 + 1)
          .map((n) => n * 3),
        fc
          .nat()
          .map((n) => n * 5 + 2)
          .map((n) => n * 3),
        fc
          .nat()
          .map((n) => n * 5 + 3)
          .map((n) => n * 3),
        fc
          .nat()
          .map((n) => n * 5 + 4)
          .map((n) => n * 3)
      ),
      (n) => {
        expect(fizzbuzz(n)).toBe('Fizz');
      }
    )
  );
})

Summary: `fizzbuzz` outputs "Fizz" for numbers divisible by 3 but not by 5.


it('should not print Buzz when not divisible by 5', () => {
  fc.assert(
    fc.property(
      fc.oneof(
        fc.nat().map((n) => n * 5 + 1),
        fc.nat().map((n) => n * 5 + 2),
        fc.nat().map((n) => n * 5 + 3),
        fc.nat().map((n) => n * 5 + 4)
      ),
      (n) => {
        expect(fizzbuzz(n)).not.toContain('Buzz');
      }
    )
  );
})

Summary: `fizzbuzz` should not include 'Buzz' for numbers not divisible by 5.


it('should print the value itself when not divisible by 3 and not divisible by 5', () => {
  fc.assert(
    fc.property(
      fc.oneof(
        fc.nat().map((n) => n * 15 + 1),
        fc.nat().map((n) => n * 15 + 2),
        fc.nat().map((n) => n * 15 + 4), // +3 would be divisible by 3
        fc.nat().map((n) => n * 15 + 7), // +5 would be divisible by 5, +6 would be divisible by 3
        fc.nat().map((n) => n * 15 + 8), // +9 would be divisible by 3, +10 would be divisible by 5
        fc.nat().map((n) => n * 15 + 11),
        fc.nat().map((n) => n * 15 + 13), // +12 would be divisible by 3
        fc.nat().map((n) => n * 15 + 14)
      ),
      (n) => {
        expect(fizzbuzz(n)).toBe(String(n));
      }
    )
  );
})

Summary: The function `fizzbuzz` should return the number itself as a string when the number is not divisible by 3 or 5.


it('should print Fizz whenever divisible by 3', () => {
  fc.assert(
    fc.property(
      fc.nat().map((n) => n * 3),
      (n) => {
        expect(fizzbuzz(n)).toContain('Fizz');
      }
    )
  );
})

Summary: `fizzbuzz` includes "Fizz" in the output when the input is divisible by 3.


it('should only print Buzz when divisible by 5 but not by 3', () => {
  fc.assert(
    fc.property(
      fc.oneof(
        fc
          .nat()
          .map((n) => n * 3 + 1)
          .map((n) => n * 5),
        fc
          .nat()
          .map((n) => n * 3 + 2)
          .map((n) => n * 5)
      ),
      (n) => {
        expect(fizzbuzz(n)).toBe('Buzz');
      }
    )
  );
})

Summary: `fizzbuzz` should output "Buzz" when a number is divisible by 5 but not by 3.


it("should only produce integer values in [2, 2**31-1] for factors", () => {
  fc.assert(
    fc.property(fc.integer({ min: 2, max: 2 ** 31 - 1 }), (n) => {
      const factors = decomposeIntoPrimes(n);
      for (const factor of factors) {
        expect(Number.isInteger(factor)).toBe(true);
        expect(factor).toBeGreaterThanOrEqual(2);
        expect(factor).toBeLessThanOrEqual(2 ** 31 - 1);
      }
    })
  );
})

Summary: Each factor produced by `decomposeIntoPrimes` should be an integer within the range [2, 2**31-1].


it('gets the init msg arg data from the canister', async () => {
            const constraints: DefinitionConstraints = {
                recursiveWeights: true,
                weights: {
                    func: 0, // random func is not supported for didc
                    null: 0, // null comes out of didc random as (null) and out of candidDecode as (null: null)
                    opt: 0, // None comes out of didc random as (null) and out of candidDecode as (null: null)
                    record: 0, // record property names don't get decoded as actual names
                    variant: 0, // variant property names don't get decoded as actual names
                    float32: 0, // float32 without a decimal point aren't accepted by candidEncodeQuery
                    float64: 0, // float64 with a decimal point is accepted by candidEncodeQuery
                    blob: 0, // blobs are different from didc random and candidDecode, but in a way that seems broken
                    vec: 0 // if given an empty vec, candidEncode will give a byte array representing a vec of empty regardless of the original type of the vec, additionally if given a vec of nat8 that is the same as blob which is broken
                }
            };
            await fc.assert(
                fc.asyncProperty(
                    candidDefinitionArb({ constraints }, {}),
                    candidDefinitionArb({ constraints }, {}),
                    candidDefinitionArb({ constraints }, {}),
                    async (deployArgDef, queryArgDef, updateArgDef) => {
                        const actor = await setupCanisters(
                            deployArgDef,
                            queryArgDef,
                            updateArgDef
                        );

                        await testDeploy(actor, deployArgDef, 'init');

                        await testDeploy(actor, deployArgDef, 'postUpgrade');

                        for (let i = 0; i < 100; i++) {
                            console.info('Query method iteration:', i);
                            await testCanisterMethod(queryArgDef, 'Query');
                        }
                        for (let i = 0; i < 10; i++) {
                            console.info('Update method iteration:', i);
                            await testCanisterMethod(updateArgDef, 'Update');
                        }
                    }
                ),
                defaultPropTestParams()
            );
        })

Summary: Setup and verify canister initialization and message argument data consistency through multiple deployment and call operations using random Candid inputs, ensuring correct data handling across initialization, post-upgrade, query, and update operations.


it('should return false for non-controller principal from a query method', async () => {
            const actor = await getCanisterActor<Actor>('canister');

            await fc.assert(
                fc.asyncProperty(
                    fc.uint8Array({ minLength: 29, maxLength: 29 }),
                    async (bytes) => {
                        const principal = Principal.fromUint8Array(bytes);

                        const isController =
                            await actor.queryIsController(principal);

                        expect(isController).toStrictEqual(false);
                    }
                ),
                defaultPropTestParams()
            );
        })

Summary: `queryIsController` should return false for principals constructed from 29-byte arrays that do not represent the canister controller.


it.each(testCases)(
                'should test jsonStringify and jsonParse with $type method',
                async ({ method }) => {
                    const executionParams = {
                        ...defaultPropTestParams(),
                        numRuns:
                            20 * Number(process.env.AZLE_PROPTEST_NUM_RUNS ?? 1)
                    };

                    await fc.assert(
                        fc.asyncProperty(recursiveValueArb, async (value) => {
                            const actor =
                                await getCanisterActor<Actor>('canister');

                            const jsonString = jsonStringify(value);

                            const returnedJsonString =
                                await actor[method](jsonString);
                            const parsedValue = jsonParse(returnedJsonString);

                            expect(returnedJsonString).toBe(jsonString);
                            expect(parsedValue).toEqual(value);
                        }),
                        executionParams
                    );
                }
            )

Summary: Verifies `jsonStringify` and `jsonParse` functions maintain value integrity through serialization and deserialization using the specified canister actor method.


it('should return true for whoami principal from a query method', async () => {
            const actor = await getCanisterActor<Actor>('canister');

            await fc.assert(
                fc.asyncProperty(fc.constant(undefined), async () => {
                    const principal = Principal.fromText(whoamiPrincipal());

                    const isController =
                        await actor.queryIsController(principal);

                    expect(isController).toStrictEqual(true);
                }),
                defaultPropTestParams()
            );
        })

Summary: `queryIsController` should return true when the queried principal is the `whoamiPrincipal`.


it('should return true for controller principal from an update method', async () => {
            const actor = await getCanisterActor<Actor>('canister');

            await fc.assert(
                fc.asyncProperty(
                    fc.uint8Array({ minLength: 29, maxLength: 29 }),
                    async (bytes) => {
                        const principal = Principal.fromUint8Array(bytes);

                        execSync(
                            `dfx canister update-settings canister --add-controller ${principal}`,
                            {
                                stdio: 'inherit'
                            }
                        );

                        const isController =
                            await actor.updateIsController(principal);

                        execSync(
                            `dfx canister update-settings canister --remove-controller ${principal}`,
                            {
                                stdio: 'inherit'
                            }
                        );

                        expect(isController).toStrictEqual(true);
                    }
                ),
                defaultPropTestParams()
            );
        })

Summary: Returns true for a principal designated as a controller during an update method call on an actor.


it('should send all cycles from intermediary to cycles canister and receive none back', async () => {
            const intermediaryCanister =
                await getCanisterActor<Actor>('intermediary');
            await fc.assert(
                fc.asyncProperty(fc.bigInt(0n, 10_000_000n), async (amount) => {
                    const result =
                        await intermediaryCanister.sendAllCycles(amount);
                    validateCyclesResult(result, amount, 'all');
                }),
                defaultPropTestParams()
            );
        })

Summary: Ensures that sending all cycles from the intermediary canister results in no cycles being refunded, with the entire amount sent and accepted matching the initial available cycles.


it('should send a portion of the cycles from intermediary to cycles canister and receive the rest back', async () => {
            const intermediaryCanister =
                await getCanisterActor<Actor>('intermediary');
            await fc.assert(
                fc.asyncProperty(
                    fc.bigInt(0n, 10_000_000n),
                    fc.bigInt(0n, 10_000_000n),
                    async (amountToSend, amountToAccept) => {
                        const result =
                            await intermediaryCanister.sendVariableCycles(
                                amountToSend,
                                amountToAccept
                            );
                        validateCyclesResult(
                            result,
                            amountToSend,
                            'variable',
                            amountToAccept
                        );
                    }
                ),
                defaultPropTestParams()
            );
        })

Summary: Transferring a portion of cycles results in the correct acceptance, refunding, and balance changes, based on specified amounts sent and accepted in variable mode.


it('should receive all cycles back to the intermediary if none are received by the cycles canister', async () => {
            const intermediaryCanister =
                await getCanisterActor<Actor>('intermediary');
            await fc.assert(
                fc.asyncProperty(fc.bigInt(0n, 10_000_000n), async (amount) => {
                    const result =
                        await intermediaryCanister.sendNoCycles(amount);
                    validateCyclesResult(result, amount, 'none');
                }),
                defaultPropTestParams()
            );
        })

Summary: When no cycles are received by the cycles canister, all cycles should be returned to the intermediary.


it('should send cycles in chunks from intermediary to cycles canister', async () => {
            const intermediaryCanister =
                await getCanisterActor<Actor>('intermediary');
            await fc.assert(
                fc.asyncProperty(
                    fc.bigInt(0n, 10_000_000n),
                    fc.bigInt(1n, 10n),
                    async (amount, numChunks) => {
                        const result =
                            await intermediaryCanister.sendCyclesByChunk(
                                amount,
                                numChunks
                            );

                        validateCyclesResult(result, amount, 'all');
                    }
                ),
                defaultPropTestParams()
            );
        })

Summary: Sending cycles from an intermediary to a canister results in the entire amount being accepted and properly accounted for in all fields of `CyclesResult`.


it('should burn the cycles', async () => {
            await fc.assert(
                fc.asyncProperty(
                    fc.bigInt(0n, 1_000_000_000_000_000n),
                    async (amount) => {
                        execSync(
                            `dfx ledger fabricate-cycles --canister canister --cycles 1000000000000000`,
                            { stdio: 'inherit' }
                        );

                        const actor = await getCanisterActor<Actor>('canister');

                        const cycleBalanceBefore =
                            await actor.getCycleBalance();
                        const cyclesBurned =
                            await actor.updateCyclesBurn(amount);
                        const cycleBalanceAfter = await actor.getCycleBalance();

                        expect(
                            cycleBalanceBefore -
                                cycleBalanceAfter -
                                cyclesBurned
                        ).toBeLessThanOrEqual(10_000_000n);
                    }
                ),
                defaultPropTestParams()
            );
        })

Summary: The function should accurately burn a specified amount of cycles from a canister, with the discrepancy between the cycle balance before and after being minimal.


it.each(testCases)(
            'should fill the $typedArray.name correctly and return the correct byte length for round $round.name',
            async ({ round, typedArray }) => {
                if (round.name === 'after upgrade-unchanged') {
                    execSync('dfx deploy canister --upgrade-unchanged');
                }

                if (round.name === 'after reinstall') {
                    execSync('dfx canister uninstall-code canister');
                    execSync('dfx deploy canister');
                }

                const actor = await getCanisterActor<Actor>('canister');
                const maxElements = Math.floor(
                    65_536 / typedArray.bytesPerElement
                );

                await fc.assert(
                    fc.asyncProperty(
                        fc.nat(maxElements),
                        fc.integer({ min: 2, max: 10 }),
                        async (length, rawNumCalls) => {
                            const numCalls = normalizeNumCalls(
                                length,
                                rawNumCalls
                            );

                            await validateCryptoGetRandomValues(
                                actor,
                                typedArray.name,
                                typedArray.bytesPerElement,
                                length,
                                numCalls
                            );
                        }
                    ),
                    defaultPropTestParams()
                );
            }
        )

Summary: `validateCryptoGetRandomValues` ensures that multiple calls to `cryptoGetRandomValues` produce unique arrays of correct byte length, with each byte within the valid range.


it.each(testCases)(
            'should throw quota exceeded error for $typedArray.name with byte length > 65_536 for round $round.name',
            async ({ round, typedArray }) => {
                if (round.name === 'after upgrade-unchanged') {
                    execSync('dfx deploy canister --upgrade-unchanged');
                }

                if (round.name === 'after reinstall') {
                    execSync('dfx canister uninstall-code canister');
                    execSync('dfx deploy canister');
                }

                const actor = await getCanisterActor<Actor>('canister');
                const minElementsToExceed =
                    Math.floor(65_536 / typedArray.bytesPerElement) + 1;

                await fc.assert(
                    fc.asyncProperty(
                        fc.integer({
                            min: minElementsToExceed,
                            max: 1_000_000
                        }),
                        async (length) => {
                            await expect(
                                actor.cryptoGetRandomValues(
                                    typedArray.name,
                                    length
                                )
                            ).rejects.toThrow(
                                'QuotaExceeded: array cannot be larger than 65_536 bytes'
                            );
                        }
                    ),
                    defaultPropTestParams()
                );
            }
        )

Summary: The test confirms that calling `cryptoGetRandomValues` with a typed array exceeding 65,536 bytes consistently throws a "QuotaExceeded" error.


it('should not hit the instruction limit with chunking in a timer', async () => {
            const actor = await getCanisterActor<Actor>('canister');

            await fc.assert(
                fc.asyncProperty(fc.nat({ max }), async (constant) => {
                    await actor.measureSumTimer(
                        20_000_000 + 1_000_000 * constant,
                        true
                    );

                    // We want to give the timer enough time to at least get started
                    await new Promise((resolve) => setTimeout(resolve, 5_000));

                    let continueLoop: boolean = true;

                    while (continueLoop) {
                        const timerStarted = await actor.getTimerStarted();

                        expect(timerStarted).toStrictEqual(true);

                        const timerEnded = await actor.getTimerEnded();

                        if (timerEnded === true) {
                            const timerInstructions =
                                await actor.getTimerInstructions();

                            expect(timerInstructions).toBeGreaterThanOrEqual(
                                updateCallInstructionLimit
                            );

                            continueLoop = false;
                        } else {
                            await new Promise((resolve) =>
                                setTimeout(resolve, 5_000)
                            );
                        }
                    }
                }),
                defaultPropTestParams()
            );
        })

Summary: The timer with chunking should start and finish without exceeding the instruction limit.


it('sets and gets certified data', async () => {
            await fc.assert(
                fc.asyncProperty(
                    fc.uint8Array({ minLength: 0, maxLength: 32 }),
                    async (data) => {
                        const agent = await createAuthenticatedAgent(whoami());
                        const actor = await getCanisterActor<Actor>(
                            CANISTER_NAME,
                            {
                                agent
                            }
                        );

                        await actor.setData(data);

                        await testCertifiedData(
                            data,
                            actor,
                            agent,
                            CANISTER_NAME
                        );
                    }
                ),
                {
                    ...defaultPropTestParams(),
                    numRuns:
                        Number(process.env.AZLE_PROPTEST_NUM_RUNS ?? 1) * 10
                }
            );
        })

Summary: The property should hold that setting and retrieving certified data from a canister results in the data matching the expected value, with the certificate verification succeeding.


it('sets certified data in post upgrade', async () => {
            await fc.assert(
                fc.asyncProperty(
                    fc.uint8Array({ minLength: 0, maxLength: 32 }),
                    async (data) => {
                        uninstallCanister();
                        deployCanister();
                        const agent = await createAuthenticatedAgent(whoami());
                        const actor = await getCanisterActor<Actor>(
                            CANISTER_NAME,
                            {
                                agent
                            }
                        );

                        // Check that getCertificate returns an empty array initially
                        await testCertifiedData(
                            new Uint8Array(),
                            actor,
                            agent,
                            CANISTER_NAME
                        );

                        deployCanister({
                            setData: true,
                            force: true,
                            data
                        });

                        await testCertifiedData(
                            data,
                            actor,
                            agent,
                            CANISTER_NAME
                        );
                    }
                ),
                defaultPropTestParams()
            );
        })

Summary: Certified data in a canister is accurately set and verified after deployment and upgrade, ensuring it matches expected values.


it('always returns undefined (which comes out of the canister as [] (ie None)) when trying to get data certificate in update method', async () => {
            await fc.assert(
                fc.asyncProperty(
                    fc.uint8Array({ minLength: 0, maxLength: 32 }),
                    async (data) => {
                        const actor =
                            await getCanisterActor<Actor>(CANISTER_NAME);

                        expect(
                            await actor.getDataCertificateInUpdate()
                        ).toEqual([]);

                        await actor.setData(data);

                        expect(
                            await actor.getDataCertificateInUpdate()
                        ).toEqual([]);
                    }
                ),
                defaultPropTestParams()
            );
        })

Summary: Calling `getDataCertificateInUpdate` in an update method always returns `undefined`, represented as an empty array `[]`.


it('throws error when trying to set certified data longer than 32 bytes', async () => {
            await fc.assert(
                fc.asyncProperty(
                    fc.uint8Array({ minLength: 0, maxLength: 32 }), // valid data
                    fc.uint8Array({ minLength: 33, maxLength: 100 }), // invalid data
                    async (validData, invalidData) => {
                        const agent = await createAuthenticatedAgent(whoami());
                        const canisterId = getCanisterId(CANISTER_NAME);
                        const actor = await getCanisterActor<Actor>(
                            CANISTER_NAME,
                            {
                                agent
                            }
                        );

                        // First verify we can set valid data
                        await actor.setData(validData);
                        await testCertifiedData(
                            validData,
                            actor,
                            agent,
                            CANISTER_NAME
                        );

                        // Then verify invalid data throws error
                        const expectedErrorMessage = new RegExp(
                            `Call failed:\\s*Canister: ${canisterId}\\s*Method: setData \\(update\\)\\s*"Request ID": "[a-f0-9]{64}"\\s*"Error code": "IC0504"\\s*"Reject code": "5"\\s*"Reject message": "Error from Canister ${canisterId}:`
                        );
                        await expect(
                            actor.setData(invalidData)
                        ).rejects.toThrow(expectedErrorMessage);
                        await expect(
                            actor.setData(invalidData)
                        ).rejects.toThrow(
                            'Canister violated contract: ic0_certified_data_set failed because the passed data must be no larger than 32 bytes'
                        );

                        // Verify the certificate still contains the valid data
                        await testCertifiedData(
                            validData,
                            actor,
                            agent,
                            CANISTER_NAME
                        );
                    }
                ),
                defaultPropTestParams()
            );
        })

Summary: Setting certified data over 32 bytes should throw an error, while setting data within the limit should succeed and retain validity.


it('gets the canister version from the canister', async () => {
            await fc.assert(
                fc.asyncProperty(
                    fc.nat({
                        max: 10
                    }),
                    async (timesToDeploy) => {
                        // Stop and delete the canister before every run
                        execSync(`dfx canister stop canister || true`, {
                            stdio: 'inherit'
                        });

                        execSync(`dfx canister delete canister || true`, {
                            stdio: 'inherit'
                        });

                        execSync(`dfx deploy canister`, {
                            stdio: 'inherit'
                        });

                        const startingVer = 1n; // Canister version starts at 0 when it's created but the deploy above will increment it to be at 1 by the time it gets here

                        const actor = await getCanisterActor<Actor>('canister');

                        await checkInitVersion(actor, startingVer);
                        await checkUpgradeVersion(actor);
                        await checkInspectVersion(actor, startingVer);
                        await checkQueryAndUpdateVersion(
                            actor,
                            startingVer + 1n
                        );

                        const versionsAtDeploy = Array.from(
                            { length: timesToDeploy },
                            (_, index) => BigInt(index * 2) + startingVer
                        );

                        for (const version of versionsAtDeploy) {
                            execSync(`dfx deploy canister --upgrade-unchanged`);

                            await checkInitVersion(actor);
                            await checkUpgradeVersion(actor, version);
                            await checkInspectVersion(actor, version);
                            await checkQueryAndUpdateVersion(actor, version);
                        }
                    }
                ),
                defaultPropTestParams()
            );
        })

checkInitVersion checkUpgradeVersion checkInspectVersion checkQueryAndUpdateVersion

Summary: The test ensures that the canister's version increments correctly across multiple deployments, starting from a defined version, and that the initial, post-upgrade, inspect, query, and update versions are as expected.


it('should deploy and check all canister id methods', async () => {
            await fc.assert(
                fc.asyncProperty(
                    fc.nat({
                        max: 254
                    }),
                    async (nat) => {
                        // We need to ensure that the canister id that we generate is on the
                        // correct local subnet. We've taken the binary representation of
                        // uxrrr-q7777-77774-qaaaq-cai and just add to the end of it.
                        // uxrrr-q7777-77774-qaaaq-cai is the first canister id you get
                        // if you deploy to the local replica.
                        const canisterId = Principal.fromUint8Array(
                            Uint8Array.from([
                                255,
                                255,
                                255,
                                255,
                                255,
                                144,
                                0,
                                1,
                                1,
                                1 + nat
                            ])
                        );
                        const canisterIdText = canisterId.toText();

                        execSync(`dfx canister stop canister`, {
                            stdio: 'inherit'
                        });

                        execSync(`dfx canister delete canister`, {
                            stdio: 'inherit'
                        });

                        execSync(
                            `dfx deploy canister --no-wallet --specified-id ${canisterIdText}`
                        );

                        const actor = await getCanisterActor<Actor>('canister');

                        const initCanisterSelf =
                            await actor.getInitCanisterSelf();
                        expect(initCanisterSelf[0]?.toText()).toEqual(
                            canisterIdText
                        );

                        const postUpgradeCanisterSelf =
                            await actor.getPostUpgradeCanisterSelf();
                        expect(postUpgradeCanisterSelf).toHaveLength(0);

                        const preUpgradeCanisterSelf =
                            await actor.getPreUpgradeCanisterSelf();
                        expect(preUpgradeCanisterSelf).toHaveLength(0);

                        await actor.setInspectMessageCanisterSelf();
                        const inspectMessageCanisterSelf =
                            await actor.getInspectMessageCanisterSelf();
                        expect(inspectMessageCanisterSelf[0]?.toText()).toEqual(
                            canisterIdText
                        );

                        const queryCanisterSelf =
                            await actor.getQueryCanisterSelf();
                        expect(queryCanisterSelf.toText()).toEqual(
                            canisterIdText
                        );

                        const updateCanisterSelf =
                            await actor.getUpdateCanisterSelf();
                        expect(updateCanisterSelf.toText()).toEqual(
                            canisterIdText
                        );

                        execSync(`dfx deploy canister --upgrade-unchanged`);

                        const postUpgradeCanisterSelfAfterUpgrade =
                            await actor.getPostUpgradeCanisterSelf();
                        expect(
                            postUpgradeCanisterSelfAfterUpgrade[0]?.toText()
                        ).toEqual(canisterIdText);

                        const preUpgradeCanisterSelfAfterUpgrade =
                            await actor.getPreUpgradeCanisterSelf();
                        expect(
                            preUpgradeCanisterSelfAfterUpgrade[0]?.toText()
                        ).toEqual(canisterIdText);
                    }
                ),
                defaultPropTestParams()
            );
        })

Summary: Deploying a canister with a specific ID ensures that various canister ID retrieval methods return the correct ID before and after upgrades.


it('should encode and decode candid values correctly', async () => {
            /* TODO: We aren't able to test the full range of candid values yet
             * because we are limited by the random values that didc generates.
             * As shown in the constraints below, several types need to be
             * disabled for these tests to work.
             *
             * We could potentially extend our arbitraries to support these
             * candid string formats, which would be straightforward but
             * time-consuming to implement. This would also reduce our
             * dependency on didc for testing.
             */
            /* TODO: once we get the arbs more robust, as per
             * https://github.com/demergent-labs/azle/issues/2276, it would be
             * good to do things in the reverse as well, that is to say, generate
             * an arb and use it's bytes to decode, then encode that result, and
             * make sure we get the bytes back.
             */
            const constraints: DefinitionConstraints = {
                recursiveWeights: true,
                weights: {
                    func: 0, // random func is not supported for didc
                    null: 0, // null comes out of didc random as (null) and out of candidDecode as (null: null)
                    opt: 0, // None comes out of didc random as (null) and out of candidDecode as (null: null)
                    record: 0, // record property names don't get decoded as actual names
                    variant: 0, // variant property names don't get decoded as actual names
                    float32: 0, // float32 without a decimal point aren't accepted by candidEncodeQuery
                    float64: 0, // float64 with a decimal point is accepted by candidEncodeQuery
                    blob: 0, // blobs are different from didc random and candidDecode, but in a way that seems broken
                    nat8: 0 // nat8 if paired with vec will make a blob, so we have to filter it out too
                }
            };
            await fc.assert(
                fc.asyncProperty(
                    candidDefinitionArb({ constraints }, {}),
                    async (candid) => {
                        // TODO IDL.Empty is a placeholder for void...not quite correct
                        const didVisitorResult = (
                            candid.definition.candidMeta.runtimeTypeObject ??
                            IDL.Empty
                        ).accept(new DidVisitor(), getDefaultVisitorData());
                        const candidString = didVisitorResult[0];
                        const command = `didc random -t '(${candidString})'`;
                        const candidValueString = execSync(command)
                            .toString()
                            .trim();
                        console.info(candidValueString);

                        const canister =
                            await getCanisterActor<Actor>('canister');

                        const encodedBytes =
                            await canister.candidEncodeQuery(candidValueString);

                        const decodedString =
                            await canister.candidDecodeQuery(encodedBytes);

                        expect(encodedBytes instanceof Uint8Array).toBe(true);
                        expect(decodedString).toBe(candidValueString);
                    }
                ),
                defaultPropTestParams()
            );
        })

Summary: The property should ensure that candid values encode to bytes and decode back to their original string representation correctly.


it('should return the inspectMessage principal', async () => {
            await fc.assert(
                fc.asyncProperty(
                    fc.uint8Array({ minLength: 32, maxLength: 32 }),
                    async (bytes) => {
                        const identity = Ed25519KeyIdentity.generate(bytes);
                        const identityPrincipalText = identity
                            .getPrincipal()
                            .toText();

                        const actor = await getCanisterActor<Actor>(
                            'canister',
                            {
                                identity
                            }
                        );

                        await actor.setInspectMessageCaller();
                        const inspectMessageCaller =
                            await actor.getInspectMessageCaller();

                        expect(inspectMessageCaller[0]?.toText()).toBe(
                            identityPrincipalText
                        );
                    }
                ),
                defaultPropTestParams()
            );
        })

Summary: The test checks that the `getInspectMessageCaller` function returns the principal associated with the `identity` used in the canister actor setup.


it('should trigger low memory handler when memory limit is approached', async () => {
            await fc.assert(
                fc.asyncProperty(
                    fc.integer({ min: 0, max: 99 }),
                    fc.integer({
                        min: 90 * 1024 * 1024, // 90 MiB in bytes (about the smallest size of this azle canister)
                        max: HARD_LIMIT
                    }),
                    async (wasmMemoryThresholdPercentage, wasmMemoryLimit) => {
                        // eslint-disable-next-line no-param-reassign
                        wasmMemoryThresholdPercentage = 0; // TODO remove after https://github.com/demergent-labs/azle/issues/2613 is resolved
                        // Calculate actual threshold based on percentage
                        const wasmMemoryThreshold = Math.floor(
                            wasmMemoryLimit *
                                (wasmMemoryThresholdPercentage / 100)
                        );

                        const actor = await deployFreshCanister<Actor>(
                            CANISTER_NAME,
                            wasmMemoryThreshold,
                            wasmMemoryLimit
                        );

                        await validateInitialStatus(actor, wasmMemoryLimit);

                        await addBytesUntilLimitReached(actor);

                        await validateFinalStatus(
                            actor,
                            wasmMemoryLimit,
                            wasmMemoryThreshold
                        );
                    }
                ),
                defaultPropTestParams()
            );
        })

Summary: The low memory handler should be triggered when the canister's memory usage approaches the limit, confirming the initial and final status relative to the specified memory threshold and limit.


it('verifies certified variable updates', async () => {
            const canisterName = 'cert-var';
            const agent = await createAuthenticatedAgent(whoami());
            const certVarCanister = await getCanisterActor<Actor>(
                canisterName,
                {
                    agent
                }
            );
            await fc.assert(
                fc.asyncProperty(
                    fc.nat({ max: 4_294_967_295 }), // max value for Nat32
                    async (arbitraryValue) => {
                        await testCertifiedVariableUpdate(
                            arbitraryValue,
                            certVarCanister,
                            agent,
                            canisterName
                        );
                    }
                ),
                defaultPropTestParams()
            );
        })

Summary: Certified variable updates should correctly reflect changes by verifying that the updated value matches the expected outcome and that the associated certificate is valid and accurate.


t.test('literal', () => {
    fc.assert(fc.property(fc.float(), roundtrip))
    fc.assert(fc.property(fc.string(), roundtrip))
    fc.assert(fc.property(fc.boolean(), roundtrip))
  })

Summary: The `roundtrip` function maintains data integrity when applied to floats, strings, and booleans.


Summary: The property ensures that the `roundtrip` process remains consistent for floats, strings, and booleans.


t.test('enum (object literal)', () =>
    fc.assert(fc.property(fc.record({
      a: fc.float(),
      b: fc.float()
    }), (e) =>
      roundtrip(enumOf(e)))))

Summary: The test ensures that the `roundtrip` function correctly handles enums created from object literals by maintaining consistency through decoding and encoding processes.


test('fuzz', () => {
  fc.assert(
    fc.property(F.arbConsume, consume => {
      // F.c.log(consume)
      const out1: unknown[] = []
      const out2: unknown[] = []
      const val1 = F.consumeX(consume, out1)
      const val2 = F.consumeE(consume, out2)
      expect(out1).toEqual(out2)
      expect(val1).toEqual(val2)
    }),
    {},
  )
})

Summary: `F.consumeX` and `F.consumeE` should produce equivalent outputs and results when consuming the same input.


it('all some', () => {
    fc.assert(fc.property(fc.array(fc.integer()), (n) =>
      assertOptional(Optionals.sequence<number>(Arr.map(n, (x) => Optional.some(x))), Optionals.traverse<number, number>(n, (x) => Optional.some(x)))
    ));
  })

Summary: The test checks that `Optionals.sequence` and `Optionals.traverse` produce equivalent results when applied to arrays of integers wrapped in `Optional.some`.


it('some !== none, for any predicate', () => {
    fc.assert(fc.property(arbOptionSome(fc.integer()), (opt1) => {
      assert.isFalse(Optionals.equals(opt1, Optional.none(), boom));
    }));
  })

Summary: `Optionals.equals` should return false when comparing `some` with `none`, regardless of the predicate.


it('none !== some, for any predicate', () => {
    fc.assert(fc.property(arbOptionSome(fc.integer()), (opt1) => {
      assert.isFalse(Optionals.equals(Optional.none(), opt1, boom));
    }));
  })

Summary: `Optional.none()` is not equal to any `some` option, regardless of the predicate used.


it('Checking Optionals.equals(some(x), some(y), _, _ -> false) === false', () => {
    fc.assert(fc.property(arbOptionSome(fc.integer()), arbOptionSome(fc.integer()), (opt1, opt2) => {
      assert.isFalse(Optionals.equals(opt1, opt2, Fun.never));
    }));
  })

Summary: Optionals.equals returns false when comparing `some(x)` and `some(y)` with a function that always returns false.


it('Checking Optionals.equals(some(x), some(y), f) iff. f(x, y)', () => {
    fc.assert(fc.property(arbOptionSome(fc.integer()), arbOptionSome(fc.integer()), fc.func(fc.boolean()), (a, b, f) => {
      const opt1 = Optional.some(a);
      const opt2 = Optional.some(b);
      return f(a, b) === Optionals.equals(opt1, opt2, f);
    }));
  })

Summary: `Optionals.equals(some(x), some(y), f)` should be true if and only if the function `f(x, y)` is true.


it('Checking some(x).fold(die, id) === x', () => {
    fc.assert(fc.property(fc.integer(), (json) => {
      const opt = Optional.some(json);
      const actual = opt.fold(Fun.die('Should not be none!'), Fun.identity);
      assert.deepEqual(actual, json);
    }));
  })

Summary: Calling `fold` on `Optional.some(x)` with a function that throws for `none` and the identity function for `some` should return `x`.


it('Checking some(x).isNone === false', () => {
    fc.assert(fc.property(arbOptionSome(fc.integer()), (opt) => !opt.isNone()));
  })

Summary: `some(x).isNone` should return false for any integer wrapped by `arbOptionSome`.


it('Checking some(x).getOr(v) === x', () => {
    fc.assert(fc.property(arbOptionSome(fc.integer()), arbOptionSome(fc.integer()), (a, b) => {
      assert.equal(Optional.some(a).getOr(b), a);
    }));
  })

Summary: `Optional.some(a).getOr(b)` returns `a`.


it('Checking some(x).or(oSomeValue) === some(x)', () => {
    fc.assert(fc.property(fc.integer(), arbOptionSome(fc.integer()), (json, other) => {
      const output = Optional.some(json).or(other);
      assert.isTrue(Optionals.is(output, json));
    }));
  })

Summary: `Optional.some(x).or(oSomeValue)` should equal `Optional.some(x)`.


it('Checking some(x).orThunk(_ -> oSomeValue) === some(x)', () => {
    fc.assert(fc.property(fc.integer(), arbOptionSome(fc.integer()), (i, other) => {
      const output = Optional.some(i).orThunk(() => other);
      assert.isTrue(Optionals.is(output, i));
    }));
  })

Summary: `some(x).orThunk(_ -> oSomeValue)` should return `some(x)`.


it('Checking some(x).map(f) === some(f(x))', () => {
    fc.assert(fc.property(fc.integer(), fc.func(fc.integer()), (a, f) => {
      const opt = Optional.some(a);
      const actual = opt.map(f);
      assert.equal(actual.getOrDie(), f(a));
    }));
  })

Summary: Verifies that mapping a function over `Optional.some(x)` is equivalent to `Optional.some(f(x))`.


it('Checking some(x).each(f) === undefined and f gets x', () => {
    fc.assert(fc.property(arbOptionSome(fc.integer()), (opt) => {
      let hack: number | null = null;
      const actual = opt.each((x) => {
        hack = x;
      });
      assert.isUndefined(actual);
      assert.equal(opt.getOrDie(), hack);
    }));
  })

Summary: `some(x).each(f)` returns undefined, and the function `f` receives `x` as its argument.


it('Given f :: s -> some(b), checking some(x).bind(f) === some(b)', () => {
    fc.assert(fc.property(fc.integer(), fc.func(arbOptionSome(fc.integer())), (i, f) => {
      const actual = Optional.some(i).bind(f);
      assert.deepEqual(actual, f(i));
    }));
  })

Summary: `Optional.some(x).bind(f)` results in the same value as `f(x)` when `f` returns a `some(b)`.


it('Checking some(x).exists(f) iff. f(x)', () => {
    fc.assert(fc.property(fc.integer(), fc.func(fc.boolean()), (i, f) => {
      const opt = Optional.some(i);
      if (f(i)) {
        assert.isTrue(opt.exists(f));
      } else {
        assert.isFalse(opt.exists(f));
      }
    }));
  })

Summary: The property should hold that `Optional.some(x).exists(f)` returns true if and only if `f(x)` is true.


it('Checking some(x).forall(_ -> false) === false', () => {
    fc.assert(fc.property(arbOptionSome(fc.integer()), (opt) => !opt.forall(Fun.never)));
  })

Summary: The test verifies that applying `forall` with a function returning `false` on `some(x)` results in `false`.


it('Checking some(x).forall(_ -> true) === true', () => {
    fc.assert(fc.property(arbOptionSome(fc.integer()), (opt) => opt.forall(Fun.always)));
  })

Summary: The property asserts that `forall` applied to `Some(x)` with a predicate that always returns true results in true.


it('Checking some(x).forall(f) iff. f(x)', () => {
    fc.assert(fc.property(fc.integer(), fc.func(fc.boolean()), (i, f) => {
      const opt = Optional.some(i);
      if (f(i)) {
        assert.isTrue(opt.forall(f));
      } else {
        assert.isFalse(opt.forall(f));
      }
    }));
  })

Summary: For an integer `i`, `Optional.some(i).forall(f)` should return true if and only if `f(i)` is true.


it('Checking some(x).toArray equals [ x ]', () => {
    fc.assert(fc.property(fc.integer(), (json) => {
      assert.deepEqual(Optional.some(json).toArray(), [ json ]);
    }));
  })

Summary: `Optional.some(x).toArray()` should return an array containing only `x`.


it('Checking some(x).toString equals "some(x)"', () => {
    fc.assert(fc.property(fc.integer(), (json) => {
      assert.equal(Optional.some(json).toString(), 'some(' + json + ')');
    }));
  })

Summary: `Optional.some(x).toString()` should return the string "some(x)" for integer inputs.


it('Checking none.getOrThunk(_ -> v) === v', () => {
    fc.assert(fc.property(fc.func(fc.integer()), (thunk) => {
      assert.equal(Optional.none<number>().getOrThunk(thunk), thunk());
    }));
  })

Summary: `Optional.none<number>().getOrThunk(thunk)` returns the result of invoking `thunk()`.


it('Checking none.getOrDie() always throws', () => {
  // Require non empty string of msg falsiness gets in the way.
    fc.assert(fc.property(fc.string(1, 40), (s) => {
      assert.throws(() => {
        Optional.none().getOrDie(s);
      });
    }));
  })

Summary: Calling `getOrDie` on `Optional.none()` always throws an exception.


it('Checking none.or(oSomeValue) === oSomeValue', () => {
    fc.assert(fc.property(fc.integer(), (i) => {
      const output = Optional.none().or(Optional.some(i));
      assert.isTrue(Optionals.is(output, i));
    }));
  })

Summary: `Optional.none().or(Optional.some(i))` should return an `Optional` containing `i`.


it('Checking none.orThunk(_ -> v) === v', () => {
    fc.assert(fc.property(fc.integer(), (i) => {
      const output = Optional.none().orThunk(() => Optional.some(i));
      assert.isTrue(Optionals.is(output, i));
    }));
  })

Summary: `Optional.none().orThunk(() => Optional.some(i))` should return `Optional` containing `i`.


it('Optional.getOr', () => {
    fc.assert(fc.property(fc.integer(), (x) => {
      assert.equal(Optional.none().getOr(x), x);
      assert.equal(Optional.none().getOrThunk(() => x), x);
    }));
    fc.assert(fc.property(fc.integer(), fc.integer(), (x, y) => {
      assert.equal(Optional.some(x).getOr(y), x);
      assert.equal(Optional.some(x).getOrThunk(Fun.die('boom')), x);
    }));
  })

Summary: `Optional.none` uses `getOr` and `getOrThunk` to return a default value, while `Optional.some` returns its contained value, ignoring the default.


it('fails for none vs some', () => {
    fc.assert(fc.property(fc.nat(), (n) => {
      assert.throw(() => {
        assertOptional(Optional.none(), Optional.some(n));
      });
    }));
  })

Summary: Ensures that comparing `Optional.none()` with `Optional.some(n)` results in an exception being thrown.


it('fails when some values are different', () => {
    fc.assert(fc.property(fc.nat(), (n) => {
      assert.throw(() => {
        assertOptional(Optional.some(n), Optional.some(n + 1));
      });
    }));
  })

Summary: `assertOptional` throws an error when comparing two `Optional` instances with different values.


it('passes for identical some arrays', () => {
    fc.assert(fc.property(fc.array(fc.nat()), (n) => {
      assertOptional(Optional.some(n), Optional.some(n));
    }));
  })

Summary: `assertOptional` confirms that two `Optional.some` arrays containing identical natural numbers pass the assertion.


it('StringMatch.matches(StringMatch.starts(s1), s1 + s) === true', () => {
    fc.assert(fc.property(
      fc.string(),
      fc.string(),
      (s, s1) => {
        assert.isTrue(StringMatch.matches(
          StringMatch.starts(s1, StringMatch.caseInsensitive),
          s1 + s
        ));
      }
    ));
  })

Summary: `StringMatch.matches` returns true when checking if a string `s1 + s` starts with `s1` in a case-insensitive manner.


it('StringMatch.matches(StringMatch.contains(s1), s1 + s) === true', () => {
    fc.assert(fc.property(
      fc.string(),
      fc.string(),
      (s, s1) => {
        assert.isTrue(StringMatch.matches(
          StringMatch.contains(s1, StringMatch.caseInsensitive),
          s1 + s
        ));
      }
    ));
  })

Summary: `StringMatch.matches` returns true when `StringMatch.contains` with `s1` is matched against `s1` concatenated with another string, using case-insensitive matching.


it('StringMatch.matches(StringMatch.contains(s1), s + s1) === true', () => {
    fc.assert(fc.property(
      fc.string(),
      fc.string(),
      (s, s1) => {
        assert.isTrue(StringMatch.matches(
          StringMatch.contains(s1, StringMatch.caseInsensitive),
          s + s1
        ));
      }
    ));
  })

Summary: `StringMatch.matches` returns true when the combined string contains `s1`, using a case-insensitive match.


it('StringMatch.matches(StringMatch.contains(s1), s) === s.indexOf(s1)', () => {
    fc.assert(fc.property(
      fc.asciiString(),
      fc.asciiString(1, 40),
      (s, s1) => {
        assert.equal(StringMatch.matches(
          StringMatch.contains(s1, StringMatch.caseInsensitive),
          s
        ), s.toLowerCase().indexOf(s1.toLowerCase()) > -1);
      }
    ));
  })

Summary: `StringMatch.matches` using `StringMatch.contains` should return true if and only if `s1` is a substring of `s`, regardless of case.


it('StringMatch.matches(StringMatch.contains(s), s) === true', () => {
    fc.assert(fc.property(
      fc.string(),
      (s) => {
        assert.isTrue(StringMatch.matches(
          StringMatch.contains(s, StringMatch.caseInsensitive),
          s
        ));
      }
    ));
  })

Summary: The test verifies that using `StringMatch.contains` with a string `s` and a case-insensitive option results in `StringMatch.matches` returning true for the same string `s`.


it('StringMatch.matches(StringMatch.exact(s), s + s1) === false', () => {
    fc.assert(fc.property(
      fc.string(1, 40),
      fc.string(1, 40),
      (s, s1) => {
        assert.isFalse(StringMatch.matches(
          StringMatch.exact(s, StringMatch.caseInsensitive),
          s + s1
        ));
      }
    ));
  })

Summary: `StringMatch.matches` returns false when attempting to match a concatenated string `s + s1` with `StringMatch.exact(s)` using case-insensitive comparison.


it('StringMatch.matches(StringMatch.exact(s), s) === true', () => {
    fc.assert(fc.property(
      fc.string(1, 40),
      (s) => {
        assert.isTrue(StringMatch.matches(
          StringMatch.exact(s, StringMatch.caseInsensitive),
          s
        ));
      }
    ));
  })

Summary: `StringMatch.matches` should return true when comparing a string with an exact match created from the same string, using case-insensitive match.


it('positive range', () => {
    fc.assert(fc.property(fc.char(), fc.integer(1, 100), (c, count) => {
      const actual = Strings.repeat(c, count);
      assert.equal(actual.length, count);
      assert.equal(actual.charAt(0), c);
      assert.equal(actual.charAt(actual.length - 1), c);
    }));

    fc.assert(fc.property(fc.asciiString(5), fc.integer(1, 100), (s, count) => {
      const actual = Strings.repeat(s, count);
      assert.equal(actual.length, count * s.length);
      assert.equal(actual.indexOf(s), 0);
      assert.equal(actual.lastIndexOf(s), actual.length - s.length);
    }));
  })

Summary: The `repeat` function should generate a string with the correct length, starting and ending with the original character or string, when repeated a specified number of times.


it('removeTrailing property', () => {
    fc.assert(fc.property(
      fc.asciiString(),
      fc.asciiString(),
      (prefix, suffix) => {
        assert.equal(Strings.removeTrailing(prefix + suffix, suffix), prefix);
      }
    ));
  })

Summary: `Strings.removeTrailing` removes the specified suffix from the end of a concatenated string, leaving only the prefix.


it('removeLeading removes prefix', () => {
    fc.assert(fc.property(
      fc.asciiString(),
      fc.asciiString(),
      (prefix, suffix) => {
        assert.equal(Strings.removeLeading(prefix + suffix, prefix), suffix);
      }
    ));
  })

Summary: `Strings.removeLeading` removes the specified prefix from the beginning of a string.


it('ensureTrailing is identity if string already ends with suffix', () => {
    fc.assert(fc.property(
      fc.string(),
      fc.string(),
      (prefix, suffix) => {
        const s = prefix + suffix;
        assert.equal(Strings.ensureTrailing(s, suffix), s);
      }));
  })

Summary: `Strings.ensureTrailing` returns the original string if it already ends with the given suffix.