Mailspring/spec-nylas/models/query-spec.coffee
Evan Morikawa 00c74cd1a9 WIP: This is the initial diff for new folders & labels.
Summary:
There are now two objects, Folders & Labels. These inherit from `Category`
(that's what Eben said they were using on the backend).

There are two separate tasks.

1. MoveToFolderTask
2. ApplyLabelsTask

It turns out that the semantics between the two are quite different.
The reverse operation for moving to a folder is a bit tricky.

As of 7-8-15, the Tasks are pretty much complete. I need to write tests
for them still and do some manual testing in the client.

Test Plan: Writing specs

Reviewers: bengotow

Reviewed By: bengotow

Differential Revision: https://phab.nylas.com/D1724
2015-07-16 11:54:20 -04:00

170 lines
6.8 KiB
CoffeeScript

ModelQuery = require '../../src/flux/models/query'
{Matcher} = require '../../src/flux/attributes'
Message = require '../../src/flux/models/message'
Thread = require '../../src/flux/models/thread'
Namespace = require '../../src/flux/models/namespace'
describe "ModelQuery", ->
beforeEach ->
@db = {}
describe "where", ->
beforeEach ->
@q = new ModelQuery(Thread, @db)
@m1 = Thread.attributes.id.equal(4)
@m2 = Thread.attributes.labels.contains('label-id')
it "should accept an array of Matcher objects", ->
@q.where([@m1,@m2])
expect(@q._matchers.length).toBe(2)
expect(@q._matchers[0]).toBe(@m1)
expect(@q._matchers[1]).toBe(@m2)
it "should accept a single Matcher object", ->
@q.where(@m1)
expect(@q._matchers.length).toBe(1)
expect(@q._matchers[0]).toBe(@m1)
it "should append to any existing where clauses", ->
@q.where(@m1)
@q.where(@m2)
expect(@q._matchers.length).toBe(2)
expect(@q._matchers[0]).toBe(@m1)
expect(@q._matchers[1]).toBe(@m2)
it "should accept a shorthand format", ->
@q.where({id: 4, lastMessageTimestamp: 1234})
expect(@q._matchers.length).toBe(2)
expect(@q._matchers[0].attr.modelKey).toBe('id')
expect(@q._matchers[0].comparator).toBe('=')
expect(@q._matchers[0].val).toBe(4)
it "should return the query so it can be chained", ->
expect(@q.where({id: 4})).toBe(@q)
it "should immediately raise an exception if an un-queryable attribute is specified", ->
expect(-> @q.where({snippet: 'My Snippet'})).toThrow()
it "should immediately raise an exception if a non-existent attribute is specified", ->
expect(-> @q.where({looksLikeADuck: 'of course'})).toThrow()
describe "order", ->
beforeEach ->
@q = new ModelQuery(Thread, @db)
@o1 = Thread.attributes.lastMessageTimestamp.descending()
@o2 = Thread.attributes.subject.descending()
it "should accept an array of SortOrders", ->
@q.order([@o1,@o2])
expect(@q._orders.length).toBe(2)
it "should accept a single SortOrder object", ->
@q.order(@o2)
expect(@q._orders.length).toBe(1)
it "should extend any existing ordering", ->
@q.order(@o1)
@q.order(@o2)
expect(@q._orders.length).toBe(2)
expect(@q._orders[0]).toBe(@o1)
expect(@q._orders[1]).toBe(@o2)
it "should return the query so it can be chained", ->
expect(@q.order(@o2)).toBe(@q)
describe "include", ->
beforeEach ->
@q = new ModelQuery(Message, @db)
it "should throw an exception if the attribute is not a joined data attribute", ->
expect( =>
@q.include(Message.attributes.unread)
).toThrow()
it "should add the provided property to the list of joined properties", ->
expect(@q._includeJoinedData).toEqual([])
@q.include(Message.attributes.body)
expect(@q._includeJoinedData).toEqual([Message.attributes.body])
describe "includeAll", ->
beforeEach ->
@q = new ModelQuery(Message, @db)
it "should add all the JoinedData attributes of the class", ->
expect(@q._includeJoinedData).toEqual([])
@q.includeAll()
expect(@q._includeJoinedData).toEqual([Message.attributes.body])
describe "formatResult", ->
it "should always return a Number for counts", ->
q = new ModelQuery(Message, @db)
q.where({namespaceId: 'abcd'}).count()
expect(q.formatResult([["12"]])).toBe(12)
describe "sql", ->
beforeEach ->
@runScenario = (klass, scenario) ->
q = new ModelQuery(klass, @db)
Matcher.muid = 1
scenario.builder(q)
expect(q.sql().trim()).toBe(scenario.sql.trim())
it "should correctly generate queries with multiple where clauses", ->
@runScenario Namespace,
builder: (q) -> q.where({emailAddress: 'ben@nylas.com'}).where({id: 2})
sql: "SELECT `Namespace`.`data` FROM `Namespace` \
WHERE `Namespace`.`email_address` = 'ben@nylas.com' AND `Namespace`.`id` = 2"
it "should correctly generate COUNT queries", ->
@runScenario Thread,
builder: (q) -> q.where({namespaceId: 'abcd'}).count()
sql: "SELECT COUNT(*) as count FROM `Thread` \
WHERE `Thread`.`namespace_id` = 'abcd' "
it "should correctly generate LIMIT 1 queries for single items", ->
@runScenario Thread,
builder: (q) -> q.where({namespaceId: 'abcd'}).one()
sql: "SELECT `Thread`.`data` FROM `Thread` \
WHERE `Thread`.`namespace_id` = 'abcd' \
ORDER BY `Thread`.`last_message_timestamp` DESC LIMIT 1"
it "should correctly generate `contains` queries using JOINS", ->
@runScenario Thread,
builder: (q) -> q.where(Thread.attributes.labels.contains('label-id')).where({id: '1234'})
sql: "SELECT `Thread`.`data` FROM `Thread` \
INNER JOIN `Thread-Label` AS `M1` ON `M1`.`id` = `Thread`.`id` \
WHERE `M1`.`value` = 'label-id' AND `Thread`.`id` = '1234' \
ORDER BY `Thread`.`last_message_timestamp` DESC"
@runScenario Thread,
builder: (q) -> q.where([Thread.attributes.labels.contains('l-1'), Thread.attributes.labels.contains('l-2')])
sql: "SELECT `Thread`.`data` FROM `Thread` \
INNER JOIN `Thread-Label` AS `M1` ON `M1`.`id` = `Thread`.`id` \
INNER JOIN `Thread-Label` AS `M2` ON `M2`.`id` = `Thread`.`id` \
WHERE `M1`.`value` = 'l-1' AND `M2`.`value` = 'l-2' \
ORDER BY `Thread`.`last_message_timestamp` DESC"
it "should correctly generate queries with the class's naturalSortOrder when one is available and no other orders are provided", ->
@runScenario Thread,
builder: (q) -> q.where({namespaceId: 'abcd'})
sql: "SELECT `Thread`.`data` FROM `Thread` \
WHERE `Thread`.`namespace_id` = 'abcd' \
ORDER BY `Thread`.`last_message_timestamp` DESC"
@runScenario Thread,
builder: (q) -> q.where({namespaceId: 'abcd'}).order(Thread.attributes.lastMessageTimestamp.ascending())
sql: "SELECT `Thread`.`data` FROM `Thread` \
WHERE `Thread`.`namespace_id` = 'abcd' \
ORDER BY `Thread`.`last_message_timestamp` ASC"
@runScenario Namespace,
builder: (q) -> q.where({id: 'abcd'})
sql: "SELECT `Namespace`.`data` FROM `Namespace` \
WHERE `Namespace`.`id` = 'abcd' "
it "should correctly generate queries requesting joined data attributes", ->
@runScenario Message,
builder: (q) -> q.where({id: '1234'}).include(Message.attributes.body)
sql: "SELECT `Message`.`data`, IFNULL(`MessageBody`.`value`, '!NULLVALUE!') AS `body` \
FROM `Message` LEFT OUTER JOIN `MessageBody` ON `MessageBody`.`id` = `Message`.`id` \
WHERE `Message`.`id` = '1234' ORDER BY `Message`.`date` ASC"