import { expect } from 'chai';
import { Lock } from './lock';

function sleep(ms: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

describe('Lock', function () {
  it('should allow to create a lock promise that can be resolved programmatically', async function () {
    const lock = new Lock();
    const [token, wasUnlocked] = await Promise.all([
      lock.lock(),
      (async () => {
        await sleep(50);
        return lock.unlock();
      })(),
    ]);
    expect(lock.isUnlockToken(token)).to.equal(true);
    expect(wasUnlocked).to.equal(true);
  });

  it('throws when trying to create locks when locked', function () {
    const lock = new Lock();

    // eslint-disable-next-line @typescript-eslint/no-floating-promises
    lock.lock();

    let err!: Error;

    try {
      // eslint-disable-next-line @typescript-eslint/no-floating-promises
      lock.lock();
    } catch (e: any) {
      err = e;
    } finally {
      lock.unlock();
    }

    expect(err).to.be.instanceof(Error);
    expect(err)
      .to.have.property('message')
      .match(/Can't create another lock while locked/);
  });

  describe('unlock', function () {
    it('should return false if lock is not locked', function () {
      const lock = new Lock();
      expect(lock.unlock()).to.equal(false);
    });
  });

  describe('isLocked', function () {
    it('shoult return current lock status', function () {
      const lock = new Lock();
      expect(lock.isLocked()).to.equal(false);
      // eslint-disable-next-line @typescript-eslint/no-floating-promises
      lock.lock();
      expect(lock.isLocked()).to.equal(true);
      lock.unlock();
      expect(lock.isLocked()).to.equal(false);
    });
  });
});
