Checkpoints.test.js 5.8 KB

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