mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-08 01:04:39 +08:00
a3ede94423
Summary: The TaskQueue does it's own throttling and has it's own processQueue retry timeout, no need for longPollConnected Remove dead code (OfflineError) Rename long connection state to status so we don't ask for `state.state` Remove long poll actions related to online/offline in favor of exposing connection state through NylasSyncStatusStore Consoliate notifications and account-error-heaer into a single package and organize files into sidebar vs. header. Update the DeveloperBarStore to query the sync status store for long poll statuses Test Plan: All existing tests pass Reviewers: juan, evan Reviewed By: evan Differential Revision: https://phab.nylas.com/D2835
220 lines
8.7 KiB
CoffeeScript
220 lines
8.7 KiB
CoffeeScript
Actions = require '../../src/flux/actions'
|
|
TaskQueue = require '../../src/flux/stores/task-queue'
|
|
Task = require '../../src/flux/tasks/task'
|
|
|
|
{APIError,
|
|
TimeoutError} = require '../../src/flux/errors'
|
|
|
|
noop = ->
|
|
|
|
describe "Task", ->
|
|
describe "initial state", ->
|
|
it "should set up queue state with additional information about local/remote", ->
|
|
task = new Task()
|
|
expect(task.queueState).toEqual({ isProcessing : false, localError : null, localComplete : false, remoteError : null, remoteAttempts : 0, remoteComplete : false, status: null, debugStatus: Task.DebugStatus.JustConstructed})
|
|
|
|
describe "runLocal", ->
|
|
beforeEach ->
|
|
class APITestTask extends Task
|
|
performLocal: -> Promise.resolve()
|
|
performRemote: -> Promise.resolve(Task.Status.Success)
|
|
@task = new APITestTask()
|
|
|
|
describe "when performLocal is not complete", ->
|
|
it "should run performLocal", ->
|
|
spyOn(@task, 'performLocal').andCallThrough()
|
|
@task.runLocal()
|
|
expect(@task.performLocal).toHaveBeenCalled()
|
|
|
|
describe "when performLocal rejects", ->
|
|
beforeEach ->
|
|
spyOn(NylasEnv, "reportError")
|
|
spyOn(@task, 'performLocal').andCallFake =>
|
|
Promise.reject(new Error("Oh no!"))
|
|
|
|
it "should save the error to the queueState", ->
|
|
@task.runLocal().catch(noop)
|
|
advanceClock()
|
|
expect(@task.performLocal).toHaveBeenCalled()
|
|
expect(@task.queueState.localComplete).toBe(false)
|
|
expect(@task.queueState.localError.message).toBe("Oh no!")
|
|
|
|
it "should reject with the error", ->
|
|
rejection = null
|
|
runs ->
|
|
@task.runLocal().catch (err) ->
|
|
rejection = err
|
|
waitsFor ->
|
|
rejection
|
|
runs ->
|
|
expect(rejection.message).toBe("Oh no!")
|
|
|
|
describe "when performLocal resolves", ->
|
|
beforeEach ->
|
|
spyOn(@task, 'performLocal').andCallFake -> Promise.resolve('Hooray')
|
|
|
|
it "should save that performLocal is complete", ->
|
|
@task.runLocal()
|
|
advanceClock()
|
|
expect(@task.queueState.localComplete).toBe(true)
|
|
|
|
it "should save that there was no performLocal error", ->
|
|
@task.runLocal()
|
|
advanceClock()
|
|
expect(@task.queueState.localError).toBe(null)
|
|
|
|
describe "runRemote", ->
|
|
beforeEach ->
|
|
@task.queueState.localComplete = true
|
|
|
|
it "should run performRemote", ->
|
|
spyOn(@task, 'performRemote').andCallThrough()
|
|
@task.runRemote()
|
|
advanceClock()
|
|
expect(@task.performRemote).toHaveBeenCalled()
|
|
|
|
it "it should resolve Continue if it already ran", ->
|
|
@task.queueState.remoteComplete = true
|
|
waitsForPromise =>
|
|
@task.runRemote().then (status) =>
|
|
expect(@task.queueState.status).toBe Task.Status.Continue
|
|
expect(status).toBe Task.Status.Continue
|
|
|
|
it "marks as complete if the task 'continue's", ->
|
|
spyOn(@task, 'performRemote').andCallFake ->
|
|
Promise.resolve(Task.Status.Continue)
|
|
@task.runRemote()
|
|
advanceClock()
|
|
expect(@task.performRemote).toHaveBeenCalled()
|
|
expect(@task.queueState.remoteError).toBe(null)
|
|
expect(@task.queueState.remoteComplete).toBe(true)
|
|
expect(@task.queueState.status).toBe(Task.Status.Continue)
|
|
|
|
it "marks as failed if the task reverts", ->
|
|
spyOn(@task, 'performRemote').andCallFake ->
|
|
Promise.resolve(Task.Status.Failed)
|
|
@task.runRemote()
|
|
advanceClock()
|
|
expect(@task.performRemote).toHaveBeenCalled()
|
|
expect(@task.queueState.remoteError).toBe(null)
|
|
expect(@task.queueState.remoteComplete).toBe(true)
|
|
expect(@task.queueState.status).toBe(Task.Status.Failed)
|
|
|
|
describe "when performRemote resolves", ->
|
|
beforeEach ->
|
|
spyOn(@task, 'performRemote').andCallFake ->
|
|
Promise.resolve(Task.Status.Success)
|
|
|
|
it "should save that performRemote is complete with no errors", ->
|
|
@task.runRemote()
|
|
advanceClock()
|
|
expect(@task.performRemote).toHaveBeenCalled()
|
|
expect(@task.queueState.remoteError).toBe(null)
|
|
expect(@task.queueState.remoteComplete).toBe(true)
|
|
expect(@task.queueState.status).toBe(Task.Status.Success)
|
|
|
|
it "should only allow the performRemote method to return a Task.Status", ->
|
|
result = null
|
|
err = null
|
|
|
|
class OKTask extends Task
|
|
performRemote: -> Promise.resolve(Task.Status.Retry)
|
|
|
|
@ok = new OKTask()
|
|
@ok.queueState.localComplete = true
|
|
@ok.runRemote().then (r) -> result = r
|
|
advanceClock()
|
|
expect(@ok.queueState.status).toBe(Task.Status.Retry)
|
|
expect(result).toBe(Task.Status.Retry)
|
|
|
|
class BadTask extends Task
|
|
performRemote: -> Promise.resolve('lalal')
|
|
@bad = new BadTask()
|
|
@bad.queueState.localComplete = true
|
|
@bad.runRemote().catch (e) -> err = e
|
|
advanceClock()
|
|
expect(err.message).toBe('performRemote returned lalal, which is not a Task.Status')
|
|
|
|
describe "when performRemote rejects multiple times", ->
|
|
beforeEach ->
|
|
spyOn(@task, 'performRemote').andCallFake =>
|
|
Promise.resolve(Task.Status.Failed)
|
|
|
|
it "should increment the number of attempts", ->
|
|
runs ->
|
|
@task.runRemote().catch(noop)
|
|
waitsFor ->
|
|
@task.queueState.remoteAttempts == 1
|
|
runs ->
|
|
@task.runRemote().catch(noop)
|
|
waitsFor ->
|
|
@task.queueState.remoteAttempts == 2
|
|
|
|
describe "when performRemote resolves with Task.Status.Failed", ->
|
|
beforeEach ->
|
|
spyOn(NylasEnv, "reportError")
|
|
@error = new APIError("Oh no!")
|
|
spyOn(@task, 'performRemote').andCallFake =>
|
|
Promise.resolve(Task.Status.Failed)
|
|
|
|
it "Should handle the error as a caught Failure", ->
|
|
waitsForPromise =>
|
|
@task.runRemote().then ->
|
|
throw new Error("Should not resolve")
|
|
.catch (err) =>
|
|
expect(@task.queueState.remoteError instanceof Error).toBe true
|
|
expect(@task.queueState.remoteAttempts).toBe(1)
|
|
expect(@task.queueState.status).toBe(Task.Status.Failed)
|
|
expect(NylasEnv.reportError).not.toHaveBeenCalled()
|
|
|
|
describe "when performRemote resolves with Task.Status.Failed and an error", ->
|
|
beforeEach ->
|
|
spyOn(NylasEnv, "reportError")
|
|
@error = new APIError("Oh no!")
|
|
spyOn(@task, 'performRemote').andCallFake =>
|
|
Promise.resolve([Task.Status.Failed, @error])
|
|
|
|
it "Should handle the error as a caught Failure", ->
|
|
waitsForPromise =>
|
|
@task.runRemote().then ->
|
|
throw new Error("Should not resolve")
|
|
.catch (err) =>
|
|
expect(@task.queueState.remoteError).toBe(@error)
|
|
expect(@task.queueState.remoteAttempts).toBe(1)
|
|
expect(@task.queueState.status).toBe(Task.Status.Failed)
|
|
expect(NylasEnv.reportError).not.toHaveBeenCalled()
|
|
|
|
describe "when performRemote rejects with Task.Status.Failed", ->
|
|
beforeEach ->
|
|
spyOn(NylasEnv, "reportError")
|
|
@error = new APIError("Oh no!")
|
|
spyOn(@task, 'performRemote').andCallFake =>
|
|
Promise.reject([Task.Status.Failed, @error])
|
|
|
|
it "Should handle the rejection as normal", ->
|
|
waitsForPromise =>
|
|
@task.runRemote().then ->
|
|
throw new Error("Should not resolve")
|
|
.catch (err) =>
|
|
expect(@task.queueState.remoteError).toBe(@error)
|
|
expect(@task.queueState.remoteAttempts).toBe(1)
|
|
expect(@task.queueState.status).toBe(Task.Status.Failed)
|
|
expect(NylasEnv.reportError).not.toHaveBeenCalled()
|
|
|
|
describe "when performRemote throws an unknown error", ->
|
|
beforeEach ->
|
|
spyOn(NylasEnv, "reportError")
|
|
@error = new Error("Oh no!")
|
|
spyOn(@task, 'performRemote').andCallFake =>
|
|
throw @error
|
|
|
|
it "Should handle the error as an uncaught error", ->
|
|
waitsForPromise =>
|
|
@task.runRemote().then ->
|
|
throw new Error("Should not resolve")
|
|
.catch (err) =>
|
|
expect(@task.queueState.remoteError).toBe(@error)
|
|
expect(@task.queueState.remoteAttempts).toBe(1)
|
|
expect(@task.queueState.status).toBe(Task.Status.Failed)
|
|
expect(@task.queueState.debugStatus).toBe(Task.DebugStatus.UncaughtError)
|
|
expect(NylasEnv.reportError).toHaveBeenCalledWith(@error)
|