Testing Assertions
Build confidence that assertions block invalid transactions and allow valid ones.
Meta-Cognitive Protocol
Adopt the role of a Meta-Cognitive Reasoning Expert.
For every complex problem:
1.DECOMPOSE: Break into sub-problems
2.SOLVE: Address each with explicit confidence (0.0-1.0)
3.VERIFY: Check logic, facts, completeness, bias
4.SYNTHESIZE: Combine using weighted confidence
5.REFLECT: If confidence <0.8, identify weakness and retry
For simple questions, skip to direct answer.
Always output:
∙Clear answer
∙Confidence level
∙Key caveats
When to Use
- Writing unit, fuzz, or backtesting tests for assertions.
- Investigating false positives or gas-limit risks.
- Adding regression tests after protocol or assertion changes.
When NOT to Use
- You need help designing invariants or triggers. Use
designing-assertions.
- You only need implementation details. Use
implementing-assertions.
- You need a backtesting setup. Use
backtesting-assertions.
Test Directory Structure
assertions/test/
├── unit/ # Unit tests (.t.sol)
├── fuzz/ # Fuzz tests (.t.sol)
└── backtest/ # Backtest tests (.t.sol)
- Test files:
{ContractOrFeature}Assertion.t.sol (e.g., VaultOwnerAssertion.t.sol)
- Test functions: start with
test (e.g., testAssertionSetFeePasses, testAssertionSetFeeFails)
Run tests by type using Foundry profiles:
- All tests:
FOUNDRY_PROFILE=assertions pcl test
- Unit only:
FOUNDRY_PROFILE=assertions-unit pcl test
- Fuzz only:
FOUNDRY_PROFILE=assertions-fuzz pcl test
- Backtests only:
FOUNDRY_PROFILE=assertions-backtest pcl test
See pcl-assertion-workflow for full foundry.toml profile configuration.
Quick Start
Use CredibleTest and cl.assertion(...) to register a single assertion function for the next transaction.
One assertion per test: cl.assertion(...) registers exactly one assertion function. Create separate test functions for each assertion you want to verify.
cl.assertion(...) is consumed by the next external call (like vm.prank) and still requires a matching trigger.
Register full assertion contracts with cl.addAssertion(...) (usually in setUp) so cl.validate(...) can find them; this cheatcode is only available under pcl test.
Passing assertions persist state changes; failing assertions revert and roll back state.
For constructor args in tests, use abi.encodePacked(type(MyAssertion).creationCode, abi.encode(args)).
Test both passing and failing paths with vm.expectRevert.
Add at least one failing test per assertion function; use simple mock contracts when the protocol prevents invalid state. Create minimal mocks with the same function signature that do the wrong thing (e.g., don't mark nullifier as spent, send wrong amount).
If vm.expectRevert fails due to call depth issues (e.g., "call didn't revert at a lower depth than cheatcode call depth"), use a low-level call pattern instead:
(bool success,) = address(target).call(abi.encodeCall(target.someFunction, (args)));
assertFalse(success, "Should revert due to assertion failure");
Add batch helper contracts for multi-operation transactions.
If you must use fallback-based batches, call address(batch).call("") and assert on the success flag.
Consider property-based testing (Echidna) for state invariants.
For Forge cheatcodes (vm.*), see https://getfoundry.sh/forge/tests/cheatcodes and forge-std/src/Vm.sol in https://github.com/foundry-rs/forge-std; for Credible testing cheats (cl.*), see credible-std/src/CredibleTest.sol in https://github.com/phylaxsystems/credible-std plus https://docs.phylax.systems/credible/testing-assertions and https://docs.phylax.systems/credible/cheatcodes-reference.
Credible Layer overview: https://docs.phylax.systems/credible/credible-introduction.
Use pcl test for assertion tests because it includes the cl.addAssertion cheatcode; use forge test only for regular protocol tests.
pcl test accepts forge test flags (fuzzing, verbosity), but may lag Forge versions.
Tests are Solidity functions starting with test; convention is test/*.t.sol.
Use FOUNDRY_PROFILE=assertions (or unit/fuzz/backtest profiles) for predictable config.
If proxy/delegatecall makes call inputs unreliable, add a log-based assertion variant and test both.
Organize assertions into modular contracts by domain (access control, timelock, caps). Split further if you hit CreateContractSizeLimit, and test each contract separately.
For timelocked actions, include submit + skip or warp in tests so the action can execute before assertions run.
Core Test Patterns
- Positive path: expected to pass and keep state consistent.
- Negative path: expected to revert with the assertion message.
- Edge cases: zero supply, empty vaults, proxy upgrades, nested batches.
Gas Limit Checks
- Assertions are capped at 300k gas.
- The happy path is often the most expensive. Test with max sizes.
- Use
pcl test -vvvv to inspect full traces and per-call gas usage.
Rationalizations to Reject
- "One passing test is enough." Assertions must also fail on violations.
- "The protocol already reverts, so negative tests are pointless." Assertions still need a failing path.
- "Gas limits are a production problem." Exceeding 300k drops valid txs.
- "Fuzzing is optional." It finds edge cases that manual tests miss.
References
- Test Patterns
- PCL Test Parity
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: testing-assertions3description: Phylax Credible Layer assertions testing. Tests phylax/credible layer assertions with CredibleTest, fuzzing, and backtesting. Use when this capability is needed.4---56# Testing Assertions78Build confidence that assertions block invalid transactions and allow valid ones.910## Meta-Cognitive Protocol1112Adopt the role of a Meta-Cognitive Reasoning Expert.1314For every complex problem:151.DECOMPOSE: Break into sub-problems162.SOLVE: Address each with explicit confidence (0.0-1.0)173.VERIFY: Check logic, facts, completeness, bias184.SYNTHESIZE: Combine using weighted confidence195.REFLECT: If confidence <0.8, identify weakness and retry20For simple questions, skip to direct answer.2122Always output:23∙Clear answer24∙Confidence level25∙Key caveats2627## When to Use2829- Writing unit, fuzz, or backtesting tests for assertions.30- Investigating false positives or gas-limit risks.31- Adding regression tests after protocol or assertion changes.3233## When NOT to Use3435- You need help designing invariants or triggers. Use `designing-assertions`.36- You only need implementation details. Use `implementing-assertions`.37- You need a backtesting setup. Use `backtesting-assertions`.3839## Test Directory Structure4041```42assertions/test/43├── unit/ # Unit tests (.t.sol)44├── fuzz/ # Fuzz tests (.t.sol)45└── backtest/ # Backtest tests (.t.sol)46```4748- **Test files**: `{ContractOrFeature}Assertion.t.sol` (e.g., `VaultOwnerAssertion.t.sol`)49- **Test functions**: start with `test` (e.g., `testAssertionSetFeePasses`, `testAssertionSetFeeFails`)5051Run tests by type using Foundry profiles:5253- All tests: `FOUNDRY_PROFILE=assertions pcl test`54- Unit only: `FOUNDRY_PROFILE=assertions-unit pcl test`55- Fuzz only: `FOUNDRY_PROFILE=assertions-fuzz pcl test`56- Backtests only: `FOUNDRY_PROFILE=assertions-backtest pcl test`5758See `pcl-assertion-workflow` for full `foundry.toml` profile configuration.5960## Quick Start6162- Use `CredibleTest` and `cl.assertion(...)` to register a single assertion function for the next transaction.63- **One assertion per test**: `cl.assertion(...)` registers exactly one assertion function. Create separate test functions for each assertion you want to verify.64- `cl.assertion(...)` is consumed by the next external call (like `vm.prank`) and still requires a matching trigger.65- Register full assertion contracts with `cl.addAssertion(...)` (usually in `setUp`) so `cl.validate(...)` can find them; this cheatcode is only available under `pcl test`.66- Passing assertions persist state changes; failing assertions revert and roll back state.67- For constructor args in tests, use `abi.encodePacked(type(MyAssertion).creationCode, abi.encode(args))`.68- Test both passing and failing paths with `vm.expectRevert`.69- Add at least one failing test per assertion function; use simple mock contracts when the protocol prevents invalid state. Create minimal mocks with the same function signature that do the wrong thing (e.g., don't mark nullifier as spent, send wrong amount).70- If `vm.expectRevert` fails due to call depth issues (e.g., "call didn't revert at a lower depth than cheatcode call depth"), use a low-level call pattern instead:7172 ```solidity73 (bool success,) = address(target).call(abi.encodeCall(target.someFunction, (args)));74 assertFalse(success, "Should revert due to assertion failure");75 ```7677- Add batch helper contracts for multi-operation transactions.78- If you must use fallback-based batches, call `address(batch).call("")` and assert on the `success` flag.79- Consider property-based testing (Echidna) for state invariants.80- For Forge cheatcodes (`vm.*`), see <https://getfoundry.sh/forge/tests/cheatcodes> and `forge-std/src/Vm.sol` in <https://github.com/foundry-rs/forge-std>; for Credible testing cheats (`cl.*`), see `credible-std/src/CredibleTest.sol` in <https://github.com/phylaxsystems/credible-std> plus <https://docs.phylax.systems/credible/testing-assertions> and <https://docs.phylax.systems/credible/cheatcodes-reference>.81- Credible Layer overview: <https://docs.phylax.systems/credible/credible-introduction>.82- <u>Use `pcl test` for assertion tests because it includes the `cl.addAssertion` cheatcode; use `forge test` only for regular protocol tests.</u>83- `pcl test` accepts `forge test` flags (fuzzing, verbosity), but may lag Forge versions.84- Tests are Solidity functions starting with `test`; convention is `test/*.t.sol`.85- Use `FOUNDRY_PROFILE=assertions` (or unit/fuzz/backtest profiles) for predictable config.86- If proxy/delegatecall makes call inputs unreliable, add a log-based assertion variant and test both.87- Organize assertions into modular contracts by domain (access control, timelock, caps). Split further if you hit `CreateContractSizeLimit`, and test each contract separately.88- For timelocked actions, include `submit` + `skip` or `warp` in tests so the action can execute before assertions run.8990## Core Test Patterns9192- **Positive path**: expected to pass and keep state consistent.93- **Negative path**: expected to revert with the assertion message.94- **Edge cases**: zero supply, empty vaults, proxy upgrades, nested batches.9596## Gas Limit Checks9798- Assertions are capped at 300k gas.99- The happy path is often the most expensive. Test with max sizes.100- Use `pcl test -vvvv` to inspect full traces and per-call gas usage.101102## Rationalizations to Reject103104- "One passing test is enough." Assertions must also fail on violations.105- "The protocol already reverts, so negative tests are pointless." Assertions still need a failing path.106- "Gas limits are a production problem." Exceeding 300k drops valid txs.107- "Fuzzing is optional." It finds edge cases that manual tests miss.108109## References110111- [Test Patterns](references/test-patterns.md)112- [PCL Test Parity](references/pcl-test-parity.md)113114---115> Converted and distributed by [TomeVault](https://tomevault.io/claim/phylaxsystems) — claim your Tome and manage your conversions.116<!-- tomevault:4.0:skill_md:2026-04-16 -->