Checkpoints.test.js 5.7 KB

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