EnumerableSet.behavior.js 3.82 KB
Newer Older
vicotor's avatar
vicotor committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');

function shouldBehaveLikeSet(values, methods, events) {
  const [valueA, valueB, valueC] = values;

  async function expectMembersMatch(set, values) {
    const contains = await Promise.all(values.map(value => methods.contains(set, value)));
    expect(contains.every(Boolean)).to.be.equal(true);

    const length = await methods.length(set);
    expect(length).to.bignumber.equal(values.length.toString());

    // To compare values we convert to strings to workaround Chai
    // limitations when dealing with nested arrays (required for BNs)
    const indexedValues = await Promise.all(
      Array(values.length)
        .fill()
        .map((_, index) => methods.at(set, index)),
    );
    expect(indexedValues.map(v => v.toString())).to.have.same.members(values.map(v => v.toString()));

    const returnedValues = await methods.values(set);
    expect(returnedValues.map(v => v.toString())).to.have.same.members(values.map(v => v.toString()));
  }

  it('starts empty', async function () {
    expect(await methods.contains(this.set, valueA)).to.equal(false);

    await expectMembersMatch(this.set, []);
  });

  describe('add', function () {
    it('adds a value', async function () {
      const receipt = await methods.add(this.set, valueA);
      expectEvent(receipt, events.addReturn, { ret0: true });

      await expectMembersMatch(this.set, [valueA]);
    });

    it('adds several values', async function () {
      await methods.add(this.set, valueA);
      await methods.add(this.set, valueB);

      await expectMembersMatch(this.set, [valueA, valueB]);
      expect(await methods.contains(this.set, valueC)).to.equal(false);
    });

    it('returns false when adding values already in the set', async function () {
      await methods.add(this.set, valueA);

      const receipt = await methods.add(this.set, valueA);
      expectEvent(receipt, events.addReturn, { ret0: false });

      await expectMembersMatch(this.set, [valueA]);
    });
  });

  describe('at', function () {
    it('reverts when retrieving non-existent elements', async function () {
      await expectRevert.unspecified(methods.at(this.set, 0));
    });
  });

  describe('remove', function () {
    it('removes added values', async function () {
      await methods.add(this.set, valueA);

      const receipt = await methods.remove(this.set, valueA);
      expectEvent(receipt, events.removeReturn, { ret0: true });

      expect(await methods.contains(this.set, valueA)).to.equal(false);
      await expectMembersMatch(this.set, []);
    });

    it('returns false when removing values not in the set', async function () {
      const receipt = await methods.remove(this.set, valueA);
      expectEvent(receipt, events.removeReturn, { ret0: false });

      expect(await methods.contains(this.set, valueA)).to.equal(false);
    });

    it('adds and removes multiple values', async function () {
      // []

      await methods.add(this.set, valueA);
      await methods.add(this.set, valueC);

      // [A, C]

      await methods.remove(this.set, valueA);
      await methods.remove(this.set, valueB);

      // [C]

      await methods.add(this.set, valueB);

      // [C, B]

      await methods.add(this.set, valueA);
      await methods.remove(this.set, valueC);

      // [A, B]

      await methods.add(this.set, valueA);
      await methods.add(this.set, valueB);

      // [A, B]

      await methods.add(this.set, valueC);
      await methods.remove(this.set, valueA);

      // [B, C]

      await methods.add(this.set, valueA);
      await methods.remove(this.set, valueB);

      // [A, C]

      await expectMembersMatch(this.set, [valueA, valueC]);

      expect(await methods.contains(this.set, valueB)).to.equal(false);
    });
  });
}

module.exports = {
  shouldBehaveLikeSet,
};