/* eslint-disable no-unused-expressions */

import sinon from 'sinon';
import { dedup } from '.';

const delay = (cb, t = 5) => new Promise((res, rej) => {
  setTimeout(() => cb().then(res, rej), t);
});

fdescribe('dedup', () => {
  describe('for operations other than READ', () => {
    it('just returns the original apiFn', () => {
      const user = { id: 'x' };
      const apiFn = sinon.spy(() => Promise.resolve({ ...user }));
      apiFn.operation = 'UPDATE';
      const wrappedApiFn = dedup({})({ apiFn, apiFnName: 'apiFn' });
      expect(wrappedApiFn).to.equal(apiFn);
    });
  });

  describe('for READ operations', () => {
    it('wraps the function', () => {
      const user = { id: 'x' };
      const apiFn = sinon.spy(() => Promise.resolve(user));
      apiFn.operation = 'READ';
      const wrappedApiFn = dedup({})({ apiFn, apiFnName: 'apiFn' });
      expect(wrappedApiFn).not.to.equal(apiFn);
    });

    it('makes several calls when apiFn is called with different args', () => {
      const user = { id: 'x' };
      const apiFn = sinon.spy(() => Promise.resolve({ ...user }));
      apiFn.operation = 'READ';
      const wrappedApiFn = dedup({})({ apiFn, apiFnName: 'apiFn' });
      return Promise.all([
        wrappedApiFn('x'),
        wrappedApiFn('y')
      ]).then(() => {
        expect(apiFn).to.have.been.calledTwice;
      });
    });

    it('only makes one call when apiFn is called in identical args', () => {
      const user = { id: 'x' };
      const apiFn = sinon.spy(() => delay(() => Promise.resolve({ ...user })));
      apiFn.operation = 'READ';
      const wrappedApiFn = dedup({})({ apiFn, apiFnName: 'apiFn' });
      return Promise.all([
        wrappedApiFn('x'),
        wrappedApiFn('x')
      ]).then(() => {
        expect(apiFn).to.have.been.calledOnce;
      });
    });

    it('passes the result of the single call to all callees', () => {
      const user = { id: 'x' };
      const apiFn = sinon.spy(() => delay(() => Promise.resolve({ ...user })));
      apiFn.operation = 'READ';
      const wrappedApiFn = dedup({})({ apiFn, apiFnName: 'apiFn' });
      return Promise.all([
        wrappedApiFn(),
        wrappedApiFn()
      ]).then((res) => {
        expect(res[0]).to.deep.equal(user);
        expect(res[1]).to.deep.equal(user);
        expect(res[0]).to.equal(res[1]);
      });
    });

    it('makes subsequent calls if another calls is made after the first one is resolved', () => {
      const user = { id: 'x' };
      const apiFn = sinon.spy(() => delay(() => Promise.resolve({ ...user })));
      apiFn.operation = 'READ';
      const wrappedApiFn = dedup({})({ apiFn, apiFnName: 'apiFn' });

      return wrappedApiFn().then(() => {
        return wrappedApiFn().then(() => {
          expect(apiFn).to.have.been.calledTwice;
        });
      });
    });

    // it('propagates errors to all callees', () => {
    //   const user = { id: 'x' };
    //   const apiFn = sinon.spy(delay(() => Promise.reject(user)));
    //   const wrappedApiFn = dedup({})({ apiFn, apiFnName: 'apiFn' });
    // });
  });
});

@@ -2834,19 +2834,16 @@
 %7D);%0A%0A   
- //
  it('pro
@@ -2881,27 +2881,24 @@
  () =%3E %7B%0A   
- //
    const use
@@ -2890,39 +2890,47 @@
       const 
-user = %7B id: 'x
+error = %7B error: 'ERROR
 ' %7D;%0A    // 
@@ -2922,29 +2922,26 @@
 ROR' %7D;%0A    
-//
   
- 
 const apiFn 
@@ -2952,16 +2952,22 @@
 non.spy(
+() =%3E 
 delay(()
@@ -2985,19 +2985,20 @@
 .reject(
-use
+erro
 r)));%0A  
@@ -2998,19 +2998,16 @@
 )));%0A   
- //
    const
@@ -3072,10 +3072,237 @@
     
-//
+  return Promise.all(%5B%0A        wrappedApiFn().catch((err) =%3E err),%0A        wrappedApiFn().catch((err) =%3E err)%0A      %5D).then((res) =%3E %7B%0A        expect(res%5B0%5D).to.equal(error);%0A        expect(res%5B0%5D).to.equal(res%5B1%5D);%0A      %7D);%0A   
  %7D);

Add spec for rejection case