import { expect } from 'chai'

import withDynamoPagination from '../withDynamoPagination'

describe('withDynamoPagination', () => {
  class Klazz {}
  const PaginatedKlazz = withDynamoPagination(Klazz)

  it('exports a function', () => {
    expect(withDynamoPagination).to.be.a('function')
  })

  it('returns a function', () => {
    expect(PaginatedKlazz).to.be.a('function')
  })

  it('returns a function that is bound to the correct prototype chains', () => {
    expect(Object.getPrototypeOf(PaginatedKlazz)).to.eq(Klazz)
    expect(Object.getPrototypeOf(PaginatedKlazz.prototype)).to.eq(Klazz.prototype)
  })

  it('assigns a default "pass thru" asyncResponseHandler instance method', () => {
    const instance = new PaginatedKlazz()
    const double = {}
    expect(instance.asyncResponseHandler(double)).to.eq(double)
  })

  context('paginatedGet', () => {
    it('is an available method', () => {
      const instance = new PaginatedKlazz()
      expect(instance.paginatedGet).to.be.a('function')
    })

    it('returns a promise', () => {
      const responseDouble = {
        json () { return Promise.resolve({}) },
        headers: { get () {} }
      }

      const instance = new PaginatedKlazz()
      instance.get = () => Promise.resolve(responseDouble)
      instance.dispatch = () => {}

      expect(instance.paginatedGet('lol', { actionCreator () {} })).to.be.a('promise')
    })

    it('captures async errors for ancillary work and rejects appropriately', (done) => {
      const responseDouble = {
        json () { return Promise.resolve({}) },
        headers: { get () {} }
      }

      const instance = new PaginatedKlazz()
      instance.get = () => Promise.resolve(responseDouble)
      instance.dispatch = sinon.stub().throws(new Error('oops!'))

      instance.paginatedGet('zomg', { actionCreator () {} }).catch(
        (err) => {
          expect(instance.dispatch.callCount).to.eq(1)
          expect(err.message).to.eq('oops!')
          done()
        }
      )
    })

    it('fetches so long as pagination headers are available, resolves with responses', (done) => {
      const get = sinon.stub()

      get.withArgs('X-Last-Evaluated-Hash-Key')
        .onFirstCall()
        .returns('lol')
        .onSecondCall()
        .returns('wut')

      get.withArgs('X-Last-Evaluated-Range-Key')
        .onFirstCall()
        .returns('lol')
        .onSecondCall()
        .returns('wut')

      const responseDouble = {
        json () { return Promise.resolve({}) },
        headers: { get }
      }

      const instance = new PaginatedKlazz()
      instance.get = () => Promise.resolve(responseDouble)
      instance.dispatch = sinon.stub()

      instance.paginatedGet('zomg', { actionCreator () {} }).then(
        (result) => {
          expect(result).to.eql([responseDouble, responseDouble, responseDouble])
          expect(get.callCount).to.eq(6)
          expect(instance.dispatch.callCount).to.eq(3)
          done()
        }
      )
    })
  })
})
