Checkpoints.test.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. require('@openzeppelin/test-helpers');
  2. const { expect } = require('chai');
  3. const { VALUE_SIZES } = require('../../../scripts/generate/templates/Checkpoints.opts.js');
  4. const { expectRevertCustomError } = require('../../helpers/customError.js');
  5. const { expectRevert } = require('@openzeppelin/test-helpers');
  6. const $Checkpoints = artifacts.require('$Checkpoints');
  7. // The library name may be 'Checkpoints' or 'CheckpointsUpgradeable'
  8. const libraryName = $Checkpoints._json.contractName.replace(/^\$/, '');
  9. const first = array => (array.length ? array[0] : undefined);
  10. const last = array => (array.length ? array[array.length - 1] : undefined);
  11. contract('Checkpoints', function () {
  12. beforeEach(async function () {
  13. this.mock = await $Checkpoints.new();
  14. });
  15. for (const length of VALUE_SIZES) {
  16. describe(`Trace${length}`, function () {
  17. beforeEach(async function () {
  18. this.methods = {
  19. at: (...args) => this.mock.methods[`$at_${libraryName}_Trace${length}(uint256,uint32)`](0, ...args),
  20. latest: (...args) => this.mock.methods[`$latest_${libraryName}_Trace${length}(uint256)`](0, ...args),
  21. latestCheckpoint: (...args) =>
  22. this.mock.methods[`$latestCheckpoint_${libraryName}_Trace${length}(uint256)`](0, ...args),
  23. length: (...args) => this.mock.methods[`$length_${libraryName}_Trace${length}(uint256)`](0, ...args),
  24. push: (...args) => this.mock.methods[`$push(uint256,uint${256 - length},uint${length})`](0, ...args),
  25. lowerLookup: (...args) => this.mock.methods[`$lowerLookup(uint256,uint${256 - length})`](0, ...args),
  26. upperLookup: (...args) => this.mock.methods[`$upperLookup(uint256,uint${256 - length})`](0, ...args),
  27. upperLookupRecent: (...args) =>
  28. this.mock.methods[`$upperLookupRecent(uint256,uint${256 - length})`](0, ...args),
  29. };
  30. });
  31. describe('without checkpoints', function () {
  32. it('at zero reverts', async function () {
  33. // Reverts with array out of bound access, which is unspecified
  34. await expectRevert.unspecified(this.methods.at(0));
  35. });
  36. it('returns zero as latest value', async function () {
  37. expect(await this.methods.latest()).to.be.bignumber.equal('0');
  38. const ckpt = await this.methods.latestCheckpoint();
  39. expect(ckpt[0]).to.be.equal(false);
  40. expect(ckpt[1]).to.be.bignumber.equal('0');
  41. expect(ckpt[2]).to.be.bignumber.equal('0');
  42. });
  43. it('lookup returns 0', async function () {
  44. expect(await this.methods.lowerLookup(0)).to.be.bignumber.equal('0');
  45. expect(await this.methods.upperLookup(0)).to.be.bignumber.equal('0');
  46. expect(await this.methods.upperLookupRecent(0)).to.be.bignumber.equal('0');
  47. });
  48. });
  49. describe('with checkpoints', function () {
  50. beforeEach('pushing checkpoints', async function () {
  51. this.checkpoints = [
  52. { key: '2', value: '17' },
  53. { key: '3', value: '42' },
  54. { key: '5', value: '101' },
  55. { key: '7', value: '23' },
  56. { key: '11', value: '99' },
  57. ];
  58. for (const { key, value } of this.checkpoints) {
  59. await this.methods.push(key, value);
  60. }
  61. });
  62. it('at keys', async function () {
  63. for (const [index, { key, value }] of this.checkpoints.entries()) {
  64. const at = await this.methods.at(index);
  65. expect(at._value).to.be.bignumber.equal(value);
  66. expect(at._key).to.be.bignumber.equal(key);
  67. }
  68. });
  69. it('length', async function () {
  70. expect(await this.methods.length()).to.be.bignumber.equal(this.checkpoints.length.toString());
  71. });
  72. it('returns latest value', async function () {
  73. expect(await this.methods.latest()).to.be.bignumber.equal(last(this.checkpoints).value);
  74. const ckpt = await this.methods.latestCheckpoint();
  75. expect(ckpt[0]).to.be.equal(true);
  76. expect(ckpt[1]).to.be.bignumber.equal(last(this.checkpoints).key);
  77. expect(ckpt[2]).to.be.bignumber.equal(last(this.checkpoints).value);
  78. });
  79. it('cannot push values in the past', async function () {
  80. await expectRevertCustomError(
  81. this.methods.push(last(this.checkpoints).key - 1, '0'),
  82. 'CheckpointUnorderedInsertion',
  83. [],
  84. );
  85. });
  86. it('can update last value', async function () {
  87. const newValue = '42';
  88. // check length before the update
  89. expect(await this.methods.length()).to.be.bignumber.equal(this.checkpoints.length.toString());
  90. // update last key
  91. await this.methods.push(last(this.checkpoints).key, newValue);
  92. expect(await this.methods.latest()).to.be.bignumber.equal(newValue);
  93. // check that length did not change
  94. expect(await this.methods.length()).to.be.bignumber.equal(this.checkpoints.length.toString());
  95. });
  96. it('lower lookup', async function () {
  97. for (let i = 0; i < 14; ++i) {
  98. const value = first(this.checkpoints.filter(x => i <= x.key))?.value || '0';
  99. expect(await this.methods.lowerLookup(i)).to.be.bignumber.equal(value);
  100. }
  101. });
  102. it('upper lookup & upperLookupRecent', async function () {
  103. for (let i = 0; i < 14; ++i) {
  104. const value = last(this.checkpoints.filter(x => i >= x.key))?.value || '0';
  105. expect(await this.methods.upperLookup(i)).to.be.bignumber.equal(value);
  106. expect(await this.methods.upperLookupRecent(i)).to.be.bignumber.equal(value);
  107. }
  108. });
  109. it('upperLookupRecent with more than 5 checkpoints', async function () {
  110. const moreCheckpoints = [
  111. { key: '12', value: '22' },
  112. { key: '13', value: '131' },
  113. { key: '17', value: '45' },
  114. { key: '19', value: '31452' },
  115. { key: '21', value: '0' },
  116. ];
  117. const allCheckpoints = [].concat(this.checkpoints, moreCheckpoints);
  118. for (const { key, value } of moreCheckpoints) {
  119. await this.methods.push(key, value);
  120. }
  121. for (let i = 0; i < 25; ++i) {
  122. const value = last(allCheckpoints.filter(x => i >= x.key))?.value || '0';
  123. expect(await this.methods.upperLookup(i)).to.be.bignumber.equal(value);
  124. expect(await this.methods.upperLookupRecent(i)).to.be.bignumber.equal(value);
  125. }
  126. });
  127. });
  128. });
  129. }
  130. });