diff --git a/.gitignore b/.gitignore index 0f53da4f7..d502bdbd8 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ newrelic_agent.log .elasticbeanstalk/* !.elasticbeanstalk/*.cfg.yml !.elasticbeanstalk/*.global.yml +/packages/local-sync/spec-saved-state.json diff --git a/packages/local-sync/package.json b/packages/local-sync/package.json index 5e866d3f2..d77e66392 100644 --- a/packages/local-sync/package.json +++ b/packages/local-sync/package.json @@ -26,6 +26,9 @@ "utf7": "^1.0.2", "vision": "4.1.0" }, + "scripts": { + "test": "../../../../node_modules/.bin/electron ../../../../ --test --spec-directory=$(pwd)/spec" + }, "repository": { "type": "git", "url": "git+https://github.com/nylas/K2.git" diff --git a/packages/local-sync/src/new-message-processor/spec/fixtures/1-99174-body.txt b/packages/local-sync/spec/fixtures/1-99174-body.txt similarity index 100% rename from packages/local-sync/src/new-message-processor/spec/fixtures/1-99174-body.txt rename to packages/local-sync/spec/fixtures/1-99174-body.txt diff --git a/packages/local-sync/src/new-message-processor/spec/fixtures/1-99174-headers.txt b/packages/local-sync/spec/fixtures/1-99174-headers.txt similarity index 100% rename from packages/local-sync/src/new-message-processor/spec/fixtures/1-99174-headers.txt rename to packages/local-sync/spec/fixtures/1-99174-headers.txt diff --git a/packages/local-sync/spec/fixtures/MessageFactory/parseFromImap/base-case.json b/packages/local-sync/spec/fixtures/MessageFactory/parseFromImap/base-case.json new file mode 100644 index 000000000..5fc82c150 --- /dev/null +++ b/packages/local-sync/spec/fixtures/MessageFactory/parseFromImap/base-case.json @@ -0,0 +1,5 @@ +{ + "imapMessage": {}, + "desiredParts": {}, + "result": {} +} diff --git a/packages/local-sync/src/new-message-processor/spec/fixtures/thread.js b/packages/local-sync/spec/fixtures/thread.js similarity index 100% rename from packages/local-sync/src/new-message-processor/spec/fixtures/thread.js rename to packages/local-sync/spec/fixtures/thread.js diff --git a/packages/local-sync/spec/local-sync-worker/fetch-folder-list-spec.js b/packages/local-sync/spec/local-sync-worker/fetch-folder-list-spec.js deleted file mode 100644 index 75d1187ba..000000000 --- a/packages/local-sync/spec/local-sync-worker/fetch-folder-list-spec.js +++ /dev/null @@ -1,115 +0,0 @@ -const {PromiseUtils} = require('isomorphic-core'); -const mockDatabase = require('./mock-database'); -const FetchFolderList = require('../../src/local-sync-worker/imap/fetch-folder-list') - -const testCategoryRoles = (db, mailboxes) => { - const mockLogger = { - info: () => {}, - debug: () => {}, - error: () => {}, - } - const mockImap = { - getBoxes: () => { - return Promise.resolve(mailboxes) - }, - } - return new FetchFolderList('fakeProvider', mockLogger).run(db, mockImap).then(() => { - const {Folder, Label} = db; - return PromiseUtils.props({ - folders: Folder.findAll(), - labels: Label.findAll(), - }).then(({folders, labels}) => { - const all = [].concat(folders, labels); - for (const category of all) { - expect(category.role).toEqual(mailboxes[category.name].role); - } - }) - }) -}; - -describe("FetchFolderList", () => { - beforeEach((done) => { - mockDatabase().then((db) => { - this.db = db; - done(); - }) - }) - - it("assigns roles when given a role attribute/flag", (done) => { - const mailboxes = { - 'Sent': {attribs: ['\\Sent'], role: 'sent'}, - 'Drafts': {attribs: ['\\Drafts'], role: 'drafts'}, - 'Spam': {attribs: ['\\Spam'], role: 'spam'}, - 'Trash': {attribs: ['\\Trash'], role: 'trash'}, - 'All Mail': {attribs: ['\\All'], role: 'all'}, - 'Important': {attribs: ['\\Important'], role: 'important'}, - 'Flagged': {attribs: ['\\Flagged'], role: 'flagged'}, - 'Inbox': {attribs: ['\\Inbox'], role: 'inbox'}, - 'TestFolder': {attribs: [], role: null}, - 'Receipts': {attribs: [], role: null}, - } - - testCategoryRoles(this.db, mailboxes).then(done, done.fail); - }) - - it("assigns missing roles by localized display names", (done) => { - const mailboxes = { - 'Sent': {attribs: [], role: 'sent'}, - 'Drafts': {attribs: ['\\Drafts'], role: 'drafts'}, - 'Spam': {attribs: ['\\Spam'], role: 'spam'}, - 'Trash': {attribs: ['\\Trash'], role: 'trash'}, - 'All Mail': {attribs: ['\\All'], role: 'all'}, - 'Important': {attribs: ['\\Important'], role: 'important'}, - 'Flagged': {attribs: ['\\Flagged'], role: 'flagged'}, - 'Inbox': {attribs: [], role: 'inbox'}, - } - - testCategoryRoles(this.db, mailboxes).then(done, done.fail); - }) - - it("doesn't assign a role more than once", (done) => { - const mailboxes = { - 'Sent': {attribs: [], role: null}, - 'Sent Items': {attribs: [], role: null}, - 'Drafts': {attribs: ['\\Drafts'], role: 'drafts'}, - 'Spam': {attribs: ['\\Spam'], role: 'spam'}, - 'Trash': {attribs: ['\\Trash'], role: 'trash'}, - 'All Mail': {attribs: ['\\All'], role: 'all'}, - 'Important': {attribs: ['\\Important'], role: 'important'}, - 'Flagged': {attribs: ['\\Flagged'], role: 'flagged'}, - 'Mail': {attribs: ['\\Inbox'], role: 'inbox'}, - 'inbox': {attribs: [], role: null}, - } - - testCategoryRoles(this.db, mailboxes).then(done, done.fail); - }) - - it("updates role assignments if an assigned category is deleted", (done) => { - let mailboxes = { - 'Sent': {attribs: [], role: null}, - 'Sent Items': {attribs: [], role: null}, - 'Drafts': {attribs: ['\\Drafts'], role: 'drafts'}, - 'Spam': {attribs: ['\\Spam'], role: 'spam'}, - 'Trash': {attribs: ['\\Trash'], role: 'trash'}, - 'All Mail': {attribs: ['\\All'], role: 'all'}, - 'Important': {attribs: ['\\Important'], role: 'important'}, - 'Flagged': {attribs: ['\\Flagged'], role: 'flagged'}, - 'Mail': {attribs: ['\\Inbox'], role: 'inbox'}, - } - - testCategoryRoles(this.db, mailboxes).then(() => { - mailboxes = { - 'Sent Items': {attribs: [], role: 'sent'}, - 'Drafts': {attribs: ['\\Drafts'], role: 'drafts'}, - 'Spam': {attribs: ['\\Spam'], role: 'spam'}, - 'Trash': {attribs: ['\\Trash'], role: 'trash'}, - 'All Mail': {attribs: ['\\All'], role: 'all'}, - 'Important': {attribs: ['\\Important'], role: 'important'}, - 'Flagged': {attribs: ['\\Flagged'], role: 'flagged'}, - 'Mail': {attribs: ['\\Inbox'], role: 'inbox'}, - } - - return testCategoryRoles(this.db, mailboxes).then(done, done.fail); - }, done.fail); - }) -}); diff --git a/packages/local-sync/spec/local-sync-worker/mock-database.js b/packages/local-sync/spec/local-sync-worker/mock-database.js deleted file mode 100644 index c2e20c47f..000000000 --- a/packages/local-sync/spec/local-sync-worker/mock-database.js +++ /dev/null @@ -1,55 +0,0 @@ -const LocalDatabaseConnector = require('../../src/shared/local-database-connector'); - -/* - * Mocks out various Model and Instance methods to prevent actually saving data - * to the sequelize database. Note that with the current implementation, only - * instances created with Model.build() are mocked out. - * - * Currently mocks out the following: - * Model - * .build() - * .findAll() - * Instance - * .destroy() - * .save() - * - */ -function mockDatabase() { - return LocalDatabaseConnector.forAccount(-1).then((db) => { - const data = {}; - for (const modelName of Object.keys(db.sequelize.models)) { - const model = db.sequelize.models[modelName]; - data[modelName] = {}; - - spyOn(model, 'findAll').and.callFake(() => { - return Promise.resolve( - Object.keys(data[modelName]).map(key => data[modelName][key]) - ); - }); - - const origBuild = model.build; - spyOn(model, 'build').and.callFake((...args) => { - const instance = origBuild.apply(model, args); - - spyOn(instance, 'save').and.callFake(() => { - if (instance.id == null) { - const sortedIds = Object.keys(data[modelName]).sort(); - const len = sortedIds.length; - instance.id = len ? +sortedIds[len - 1] + 1 : 0; - } - data[modelName][instance.id] = instance; - }); - - spyOn(instance, 'destroy').and.callFake(() => { - delete data[modelName][instance.id] - }); - - return instance; - }) - } - - return Promise.resolve(db); - }); -} - -module.exports = mockDatabase; diff --git a/packages/local-sync/spec/message-factory-spec.js b/packages/local-sync/spec/message-factory-spec.js new file mode 100644 index 000000000..c0643f26a --- /dev/null +++ b/packages/local-sync/spec/message-factory-spec.js @@ -0,0 +1,41 @@ +const path = require('path'); +const fs = require('fs'); +const LocalDatabaseConnector = require('../src/shared/local-database-connector'); +const {parseFromImap} = require('../src/shared/message-factory'); + +const FIXTURES_PATH = path.join(__dirname, 'fixtures') + +describe('MessageFactory', function MessageFactorySpecs() { + beforeEach(() => { + waitsForPromise(async () => { + const accountId = 'test-account-id'; + await LocalDatabaseConnector.ensureAccountDatabase(accountId); + const db = await LocalDatabaseConnector.forAccount(accountId); + const folder = await db.Folder.create({ + id: 'test-folder-id', + accountId: accountId, + version: 1, + name: 'Test Folder', + role: null, + }); + this.options = { accountId, db, folder }; + }) + }) + + describe("parseFromImap", () => { + const fixturesDir = path.join(FIXTURES_PATH, 'MessageFactory', 'parseFromImap'); + const filenames = fs.readdirSync(fixturesDir).filter(f => f.endsWith('.json')); + + filenames.forEach((filename) => { + it(`should correctly build message properties for ${filename}`, () => { + const inJSON = JSON.parse(fs.readFileSync(path.join(fixturesDir, filename))); + const {imapMessage, desiredParts, result} = inJSON; + + waitsForPromise(async () => { + const actual = await parseFromImap(imapMessage, desiredParts, this.options); + expect(actual).toEqual(result) + }); + }); + }) + }); +}); diff --git a/packages/local-sync/spec/threading-spec.js b/packages/local-sync/spec/threading-spec.js new file mode 100644 index 000000000..2313af432 --- /dev/null +++ b/packages/local-sync/spec/threading-spec.js @@ -0,0 +1,55 @@ +/* eslint global-require: 0 */ +/* eslint import/no-dynamic-require: 0 */ +// const path = require('path') +// const BASE_PATH = path.join(__dirname, 'fixtures') + +xdescribe('threading', function threadingSpecs() { + // it('adds the message to the thread', (done) => { + // const {message, reply} = require(`${BASE_PATH}/thread`) + // const accountId = 'a-1' + // const mockDb = { + // Thread: { + // findAll: () => { + // return Promise.resolve([{ + // id: 1, + // subject: "Loved your work and interests", + // messages: [message], + // }]) + // }, + // find: () => { + // return Promise.resolve(null) + // }, + // create: (thread) => { + // thread.id = 1 + // thread.addMessage = (newMessage) => { + // if (thread.messages) { + // thread.messages.push(newMessage.id) + // } else { + // thread.messages = [newMessage.id] + // } + // } + // return Promise.resolve(thread) + // }, + // }, + // Message: { + // findAll: () => { + // return Promise.resolve([message, reply]) + // }, + // find: () => { + // return Promise.resolve(reply) + // }, + // create: () => { + // message.setThread = (thread) => { + // message.thread = thread.id + // }; + // return Promise.resolve(message); + // }, + // }, + // } + // + // processMessage({db: mockDb, message: reply, accountId}).then((processed) => { + // expect(processed.thread).toBe(1) + // done() + // }) + // }); +}); diff --git a/packages/local-sync/src/local-sync-worker/imap/fetch-messages-in-folder.js b/packages/local-sync/src/local-sync-worker/imap/fetch-messages-in-folder.js index b5d5c30bd..f6a628cc9 100644 --- a/packages/local-sync/src/local-sync-worker/imap/fetch-messages-in-folder.js +++ b/packages/local-sync/src/local-sync-worker/imap/fetch-messages-in-folder.js @@ -239,8 +239,8 @@ class FetchMessagesInFolder { try { const messageValues = await MessageFactory.parseFromImap(imapMessage, desiredParts, { db: this._db, + folder: this._folder, accountId: this._db.accountId, - folderId: this._folder.id, }); const existingMessage = await Message.find({where: {id: messageValues.id}}); diff --git a/packages/local-sync/src/new-message-processor/spec/parsing-spec.js b/packages/local-sync/src/new-message-processor/spec/parsing-spec.js deleted file mode 100644 index e8025c0b7..000000000 --- a/packages/local-sync/src/new-message-processor/spec/parsing-spec.js +++ /dev/null @@ -1,23 +0,0 @@ -const path = require('path') -const fs = require('fs') -const {processMessage} = require('../processors/parsing') - -const BASE_PATH = path.join(__dirname, 'fixtures') - - -it('parses the message correctly', (done) => { - const bodyPath = path.join(BASE_PATH, '1-99174-body.txt') - const headersPath = path.join(BASE_PATH, '1-99174-headers.txt') - const rawBody = fs.readFileSync(bodyPath, 'utf8') - const rawHeaders = fs.readFileSync(headersPath, 'utf8') - const message = { rawHeaders, rawBody } - const bodyPart = `
In _data/apps.yml:
` - - processMessage({message}).then((processed) => { - expect(processed.headers['in-reply-to']).toEqual('