[client-app, client-sync] Add specs for detecting stuck sync/worker window

Summary: See title. Part of T7681.

Test Plan: ran the specs

Reviewers: evan, mark, juan

Reviewed By: juan

Differential Revision: https://phab.nylas.com/D4264
This commit is contained in:
Halla Moore 2017-03-27 15:44:18 -07:00
parent 73f0f3eeda
commit 4ad4596e2f
2 changed files with 112 additions and 0 deletions

View file

@ -0,0 +1,44 @@
import {ipcRenderer} from 'electron'
import SyncHealthChecker from '../lib/sync-health-checker'
const requestWithErrorResponse = () => {
return {
run: async () => {
throw new Error('ECONNREFUSED');
},
}
}
const activityData = {account1: {time: 1490305104619, activity: ['activity']}}
const requestWithDataResponse = () => {
return {
run: async () => {
return activityData
},
}
}
describe('SyncHealthChecker', () => {
describe('when the worker window is not available', () => {
beforeEach(() => {
spyOn(SyncHealthChecker, '_buildRequest').andCallFake(requestWithErrorResponse)
spyOn(ipcRenderer, 'send')
spyOn(NylasEnv, 'reportError')
})
it('attempts to restart it', async () => {
await SyncHealthChecker._checkSyncHealth();
expect(NylasEnv.reportError.calls.length).toEqual(1)
expect(ipcRenderer.send.calls[0].args[0]).toEqual('ensure-worker-window')
})
})
describe('when data is returned', () => {
beforeEach(() => {
spyOn(SyncHealthChecker, '_buildRequest').andCallFake(requestWithDataResponse)
})
it('stores the data', async () => {
await SyncHealthChecker._checkSyncHealth();
expect(SyncHealthChecker._lastSyncActivity).toEqual(activityData)
})
})
})

View file

@ -0,0 +1,68 @@
import {IdentityStore} from 'nylas-exports'
import {createLogger} from '../../src/shared/logger'
import LocalDatabaseConnector from '../../src/shared/local-database-connector'
import SyncProcessManager from '../../src/local-sync-worker/sync-process-manager'
import SyncActivity from '../../src/shared/sync-activity'
describe("SynccProcessManager", () => {
beforeEach(async () => {
global.Logger = createLogger()
spyOn(IdentityStore, 'identity').andReturn(true)
const db = await LocalDatabaseConnector.forShared();
await db.Account.create({id: 'test-account'})
})
afterEach(async () => {
const db = await LocalDatabaseConnector.forShared();
const accounts = db.Account.findAll();
return Promise.all(accounts.map((account) => account.destroy()))
})
describe("when a sync worker is stuck", () => {
beforeEach(() => {
spyOn(NylasEnv, 'reportError')
spyOn(SyncProcessManager, 'removeWorkerForAccountId').andCallThrough()
spyOn(SyncProcessManager, 'addWorkerForAccount').andCallThrough()
spyOn(SyncActivity, 'getLastSyncActivityForAccount').andReturn({
time: Date.now() - 2 * SyncProcessManager.MAX_WORKER_SILENCE_MS,
activity: ['activity'],
})
// Make sure the health check interval isn't automatically started
SyncProcessManager._check_health_interval = 1
})
it("detects it and recovers", async () => {
await SyncProcessManager.start()
expect(SyncProcessManager.removeWorkerForAccountId.calls.length).toEqual(0)
expect(SyncProcessManager.addWorkerForAccount.calls.length).toEqual(1)
await SyncProcessManager._checkHealth()
expect(NylasEnv.reportError.calls.length).toEqual(1)
expect(SyncProcessManager.removeWorkerForAccountId.calls.length).toEqual(1)
expect(SyncProcessManager.addWorkerForAccount.calls.length).toEqual(2)
})
it("doesn't have zombie workers come back to life", async () => {
await SyncProcessManager.start()
// Zombify a worker
const zombieSync = () => {
return new Promise(() => {}) // Never resolves
}
const zombieWorker = SyncProcessManager.workers()[0]
const origSync = zombieWorker.syncNow
zombieWorker.syncNow = zombieSync
zombieWorker.interrupt()
zombieWorker.syncNow()
// Make sure the worker is discarded by the manager
await SyncProcessManager._checkHealth()
expect(NylasEnv.reportError.calls.length).toEqual(1)
expect(SyncProcessManager.removeWorkerForAccountId.calls.length).toEqual(1)
expect(SyncProcessManager.addWorkerForAccount.calls.length).toEqual(2)
// Try to get the zombie to sync again, check that it doesn't.
const lastStart = zombieWorker._syncStart;
zombieWorker.syncNow = origSync
zombieWorker.interrupt({reason: 'Playing Frankenstein'})
await zombieWorker.syncNow()
expect(zombieWorker._syncStart).toEqual(lastStart)
})
})
})