import { retryOnce } from './retry-once'

describe('retryOnce', () => {
  it("resolves with the first attempt's value and never waits when it succeeds", async () => {
    const requestFn = jasmine.createSpy('requestFn').and.resolveTo('ok')
    const wait = jasmine.createSpy('wait').and.resolveTo(undefined)

    const result = await retryOnce(requestFn, { wait })

    expect(result).toBe('ok')
    expect(requestFn).toHaveBeenCalledTimes(1)
    expect(wait).not.toHaveBeenCalled()
  })

  it('retries exactly once after a rejection and resolves with the retry value', async () => {
    const requestFn = jasmine
      .createSpy('requestFn')
      .and.returnValues(Promise.reject('first-error'), Promise.resolve('ok'))
    const wait = jasmine.createSpy('wait').and.resolveTo(undefined)

    const result = await retryOnce(requestFn, { wait })

    expect(result).toBe('ok')
    expect(requestFn).toHaveBeenCalledTimes(2)
    expect(wait).toHaveBeenCalledTimes(1)
  })

  it('derives the pre-retry pause from random within [0, maxDelayMs)', async () => {
    const requestFn = jasmine
      .createSpy('requestFn')
      .and.returnValues(Promise.reject('err'), Promise.resolve('ok'))
    const wait = jasmine.createSpy('wait').and.resolveTo(undefined)

    await retryOnce(requestFn, { wait, random: () => 0.5 })
    expect(wait).toHaveBeenCalledWith(1500)

    wait.calls.reset()
    requestFn.and.returnValues(Promise.reject('err'), Promise.resolve('ok'))
    await retryOnce(requestFn, { wait, random: () => 0 })
    expect(wait).toHaveBeenCalledWith(0)

    wait.calls.reset()
    requestFn.and.returnValues(Promise.reject('err'), Promise.resolve('ok'))
    await retryOnce(requestFn, { wait, random: () => 0.999999 })
    expect(wait).toHaveBeenCalledWith(jasmine.any(Number))
    expect(wait.calls.mostRecent().args[0]).toBeLessThan(3000)
  })

  it('rejects with the second error when both attempts fail, never trying a third time', async () => {
    const requestFn = jasmine
      .createSpy('requestFn')
      .and.returnValues(
        Promise.reject('first-error'),
        Promise.reject('second-error')
      )
    const wait = jasmine.createSpy('wait').and.resolveTo(undefined)

    await expectAsync(retryOnce(requestFn, { wait })).toBeRejectedWith(
      'second-error'
    )
    expect(requestFn).toHaveBeenCalledTimes(2)
  })
})
