Checkpoints.test.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. const { ethers } = require('hardhat');
  2. const { expect } = require('chai');
  3. const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
  4. const { VALUE_SIZES } = require('../../../scripts/generate/templates/Checkpoints.opts');
  5. describe('Checkpoints', function () {
  6. for (const length of VALUE_SIZES) {
  7. describe(`Trace${length}`, function () {
  8. const fixture = async () => {
  9. const mock = await ethers.deployContract('$Checkpoints');
  10. const methods = {
  11. at: (...args) => mock.getFunction(`$at_Checkpoints_Trace${length}`)(0, ...args),
  12. latest: (...args) => mock.getFunction(`$latest_Checkpoints_Trace${length}`)(0, ...args),
  13. latestCheckpoint: (...args) => mock.getFunction(`$latestCheckpoint_Checkpoints_Trace${length}`)(0, ...args),
  14. length: (...args) => mock.getFunction(`$length_Checkpoints_Trace${length}`)(0, ...args),
  15. push: (...args) => mock.getFunction(`$push(uint256,uint${256 - length},uint${length})`)(0, ...args),
  16. lowerLookup: (...args) => mock.getFunction(`$lowerLookup(uint256,uint${256 - length})`)(0, ...args),
  17. upperLookup: (...args) => mock.getFunction(`$upperLookup(uint256,uint${256 - length})`)(0, ...args),
  18. upperLookupRecent: (...args) =>
  19. mock.getFunction(`$upperLookupRecent(uint256,uint${256 - length})`)(0, ...args),
  20. };
  21. return { mock, methods };
  22. };
  23. beforeEach(async function () {
  24. Object.assign(this, await loadFixture(fixture));
  25. });
  26. describe('without checkpoints', function () {
  27. it('at zero reverts', async function () {
  28. // Reverts with array out of bound access, which is unspecified
  29. await expect(this.methods.at(0)).to.be.reverted;
  30. });
  31. it('returns zero as latest value', async function () {
  32. expect(await this.methods.latest()).to.equal(0n);
  33. const ckpt = await this.methods.latestCheckpoint();
  34. expect(ckpt[0]).to.be.false;
  35. expect(ckpt[1]).to.equal(0n);
  36. expect(ckpt[2]).to.equal(0n);
  37. });
  38. it('lookup returns 0', async function () {
  39. expect(await this.methods.lowerLookup(0)).to.equal(0n);
  40. expect(await this.methods.upperLookup(0)).to.equal(0n);
  41. expect(await this.methods.upperLookupRecent(0)).to.equal(0n);
  42. });
  43. });
  44. describe('with checkpoints', function () {
  45. beforeEach('pushing checkpoints', async function () {
  46. this.checkpoints = [
  47. { key: 2n, value: 17n },
  48. { key: 3n, value: 42n },
  49. { key: 5n, value: 101n },
  50. { key: 7n, value: 23n },
  51. { key: 11n, value: 99n },
  52. ];
  53. for (const { key, value } of this.checkpoints) {
  54. await this.methods.push(key, value);
  55. }
  56. });
  57. it('at keys', async function () {
  58. for (const [index, { key, value }] of this.checkpoints.entries()) {
  59. const at = await this.methods.at(index);
  60. expect(at._value).to.equal(value);
  61. expect(at._key).to.equal(key);
  62. }
  63. });
  64. it('length', async function () {
  65. expect(await this.methods.length()).to.equal(this.checkpoints.length);
  66. });
  67. it('returns latest value', async function () {
  68. const latest = this.checkpoints.at(-1);
  69. expect(await this.methods.latest()).to.equal(latest.value);
  70. expect(await this.methods.latestCheckpoint()).to.deep.equal([true, latest.key, latest.value]);
  71. });
  72. it('cannot push values in the past', async function () {
  73. await expect(this.methods.push(this.checkpoints.at(-1).key - 1n, 0n)).to.be.revertedWithCustomError(
  74. this.mock,
  75. 'CheckpointUnorderedInsertion',
  76. );
  77. });
  78. it('can update last value', async function () {
  79. const newValue = 42n;
  80. // check length before the update
  81. expect(await this.methods.length()).to.equal(this.checkpoints.length);
  82. // update last key
  83. await this.methods.push(this.checkpoints.at(-1).key, newValue);
  84. expect(await this.methods.latest()).to.equal(newValue);
  85. // check that length did not change
  86. expect(await this.methods.length()).to.equal(this.checkpoints.length);
  87. });
  88. it('lower lookup', async function () {
  89. for (let i = 0; i < 14; ++i) {
  90. const value = this.checkpoints.find(x => i <= x.key)?.value || 0n;
  91. expect(await this.methods.lowerLookup(i)).to.equal(value);
  92. }
  93. });
  94. it('upper lookup & upperLookupRecent', async function () {
  95. for (let i = 0; i < 14; ++i) {
  96. const value = this.checkpoints.findLast(x => i >= x.key)?.value || 0n;
  97. expect(await this.methods.upperLookup(i)).to.equal(value);
  98. expect(await this.methods.upperLookupRecent(i)).to.equal(value);
  99. }
  100. });
  101. it('upperLookupRecent with more than 5 checkpoints', async function () {
  102. const moreCheckpoints = [
  103. { key: 12n, value: 22n },
  104. { key: 13n, value: 131n },
  105. { key: 17n, value: 45n },
  106. { key: 19n, value: 31452n },
  107. { key: 21n, value: 0n },
  108. ];
  109. const allCheckpoints = [].concat(this.checkpoints, moreCheckpoints);
  110. for (const { key, value } of moreCheckpoints) {
  111. await this.methods.push(key, value);
  112. }
  113. for (let i = 0; i < 25; ++i) {
  114. const value = allCheckpoints.findLast(x => i >= x.key)?.value || 0n;
  115. expect(await this.methods.upperLookup(i)).to.equal(value);
  116. expect(await this.methods.upperLookupRecent(i)).to.equal(value);
  117. }
  118. });
  119. });
  120. });
  121. }
  122. });