Test suite
When building a complex contract ecosystem with multiple moving parts and dependencies, comprehensive testing is essential. This includes testing for both positive and negative scenarios. Positive tests should verify that each function's side effect is as expected, while negative tests should cover every revert, preferably in every logical branch.
It is important to test the invariants required for ensuring security and also verify mathematical properties as specified in the white paper and documentation. Additionally, testing under various market conditions is essential for ensuring that the system operates as intended.
Even though the current testing suite touches on most functionalities, we recommend expanding it to include more edge cases, especially around the specific tokens that are used as well as the AMM (in this case, the UniswapV3 pools). This is especially important as the strategies are essentially in a symbiotic relationship with the UniswapV3 pools, and the behavior of the system is highly dependent on the behavior of the pools, which can be unpredictable under chaotic market conditions.
Good test coverage has multiple effects.
It finds bugs and design flaws early (preaudit or prerelease).
It gives insight into areas for optimization (e.g., gas cost).
It displays code maturity.
It bolsters customer trust in your product.
It improves understanding of how the code functions, integrates, and operates — for developers and auditors alike.
It increases development velocity long-term.
The last point seems contradictory, given the time investment to create and maintain tests. To expand upon that, tests help developers trust their own changes. It is difficult to know if a code refactor — or even just a small one-line fix — breaks something if there are no tests. This is especially true for new developers or those returning to the code after a prolonged absence. Tests have your back here. They are an indicator that the existing functionality most likely was not broken by your change to the code.