Skip to content

Commit

Permalink
Document and update tests for paging
Browse files Browse the repository at this point in the history
  • Loading branch information
kjellmorten committed Apr 5, 2018
1 parent 5f2d571 commit 28c352b
Show file tree
Hide file tree
Showing 5 changed files with 356 additions and 205 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ The query object will look like this, for a request for items of type `entry`:
}
```

When the `pageSize` param is set in a request, it is taken as the max number of
documents to return in the response. When nothing else is specified, the first
page of documents is returned, and the `paging.next` prop on the response will
hold a params object that may be used to get the next page.

**Note 1:** This adapter is currently updating and deleting arrays of documents
by calling `updateOne` and `deleteOne` for every item in the array. This is not
the best method of doing it, so stay tuned for improvements.
Expand Down
350 changes: 348 additions & 2 deletions lib/adapter/getDocs-test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,353 @@
import test from 'ava'
import sinon from 'sinon'

import getDocs from './getDocs'

test('should exist', (t) => {
t.is(typeof getDocs, 'function')
// Helpers

const createFind = (items) => {
const docs = items.map((item) => ({...item, _id: `${item.type}:${item.id}`}))
const it = docs[Symbol.iterator]()

const cursor = {
// toArray returns all docs
toArray: async () => docs,
// Mimick limit method
limit: (size) => ({toArray: async () => docs.slice(0, size)}),
// Mimick next()
next: async () => it.next().value,
sort: () => cursor
}

return sinon.stub().resolves(cursor)
}

// Tests

test('should get items', async (t) => {
const find = createFind([{id: 'ent1', type: 'entry'}, {id: 'ent2', type: 'entry'}])
const getCollection = () => ({find})
const request = {
action: 'GET',
params: {
type: 'entry',
typePlural: 'entries'
},
endpoint: {
collection: 'documents',
db: 'database'
}
}

const response = await getDocs(getCollection, request)

t.is(response.status, 'ok')
t.is(response.data.length, 2)
t.is(response.data[0].id, 'ent1')
t.is(response.data[1].id, 'ent2')
t.true(find.calledWith({type: 'entry'}))
})

test('should get one item', async (t) => {
const find = createFind([{id: 'ent1', type: 'entry'}])
const getCollection = () => ({find})
const request = {
action: 'GET',
params: {
id: 'ent1',
type: 'entry',
typePlural: 'entries'
},
endpoint: {
collection: 'documents',
db: 'database'
}
}

const response = await getDocs(getCollection, request)

t.is(response.status, 'ok')
t.is(response.data.length, 1)
t.is(response.data[0].id, 'ent1')
t.true(find.calledWith({_id: 'entry:ent1'}))
})

test('should get with query', async (t) => {
const find = createFind([])
const getCollection = () => ({find})
const request = {
action: 'GET',
params: {
type: 'entry',
typePlural: 'entries'
},
endpoint: {
collection: 'documents',
db: 'database',
query: [
{path: 'type', param: 'type'},
{path: 'attributes\\.age.$gt', value: 18}
]
}
}
const expected = {
type: 'entry',
'attributes.age': {$gt: 18}
}

await getDocs(getCollection, request)

const arg = find.args[0][0]
t.deepEqual(arg, expected)
})

test('should get one page of items', async (t) => {
const find = createFind([
{id: 'ent1', type: 'entry'},
{id: 'ent2', type: 'entry'},
{id: 'ent3', type: 'entry'}
])
const getCollection = () => ({find})
const request = {
action: 'GET',
params: {
type: 'entry',
typePlural: 'entries',
pageSize: 2
},
endpoint: {
collection: 'documents',
db: 'database'
}
}

const response = await getDocs(getCollection, request)

t.is(response.status, 'ok')
t.is(response.data.length, 2)
t.is(response.data[0].id, 'ent1')
t.is(response.data[1].id, 'ent2')
t.true(find.calledWith({type: 'entry'}))
})

test('should return params for next page', async (t) => {
const find = createFind([
{id: 'ent1', type: 'entry'},
{id: 'ent2', type: 'entry'}
])
const getCollection = () => ({find})
const request = {
action: 'GET',
params: {
type: 'entry',
typePlural: 'entries',
pageSize: 2
},
endpoint: {
collection: 'documents',
db: 'database'
}
}
const expectedPaging = {
next: {
type: 'entry',
query: {_id: {$gte: 'entry:ent2'}},
pageAfter: 'entry:ent2',
pageSize: 2
}
}

const response = await getDocs(getCollection, request)

t.deepEqual(response.paging, expectedPaging)
})

test('should get second page of items', async (t) => {
const find = createFind([
{id: 'ent2', type: 'entry'},
{id: 'ent3', type: 'entry'},
{id: 'ent4', type: 'entry'}
])
const getCollection = () => ({find})
const request = {
action: 'GET',
params: {
type: 'entry',
typePlural: 'entries',
query: {_id: {$gte: 'entry:ent2'}},
pageAfter: 'entry:ent2',
pageSize: 2
},
endpoint: {
collection: 'documents',
db: 'database'
}
}
const expectedPaging = {
next: {
type: 'entry',
query: {_id: {$gte: 'entry:ent4'}},
pageAfter: 'entry:ent4',
pageSize: 2
}
}

const response = await getDocs(getCollection, request)

t.deepEqual(find.args[0][0], {type: 'entry', _id: {$gte: 'entry:ent2'}})
t.is(response.status, 'ok')
t.is(response.data.length, 2)
t.is(response.data[0].id, 'ent3')
t.is(response.data[1].id, 'ent4')
t.deepEqual(response.paging, expectedPaging)
})

test('should get empty result when we have passed the last page', async (t) => {
const find = createFind([
{id: 'ent4', type: 'entry'}
])
const getCollection = () => ({find})
const request = {
action: 'GET',
params: {
type: 'entry',
typePlural: 'entries',
query: {_id: {$gte: 'entry:ent4'}},
pageAfter: 'entry:ent4',
pageSize: 2
},
endpoint: {
collection: 'documents',
db: 'database'
}
}
const expectedPaging = {
next: null
}

const response = await getDocs(getCollection, request)

t.deepEqual(find.args[0][0], {type: 'entry', _id: {$gte: 'entry:ent4'}})
t.is(response.status, 'ok')
t.is(response.data.length, 0)
t.deepEqual(response.paging, expectedPaging)
})

test('should get empty result when the pageAfter doc is not found', async (t) => {
const find = createFind([
{id: 'ent5', type: 'entry'}
])
const getCollection = () => ({find})
const request = {
action: 'GET',
params: {
type: 'entry',
typePlural: 'entries',
query: {_id: {$gte: 'entry:ent4'}},
pageAfter: 'entry:ent4',
pageSize: 2
},
endpoint: {
collection: 'documents',
db: 'database'
}
}
const expectedPaging = {
next: null
}

const response = await getDocs(getCollection, request)

t.deepEqual(find.args[0][0], {type: 'entry', _id: {$gte: 'entry:ent4'}})
t.is(response.status, 'ok')
t.is(response.data.length, 0)
t.deepEqual(response.paging, expectedPaging)
})

test('should get second page of items when there is documents before the pageAfter', async (t) => {
const find = createFind([
{id: 'ent1', type: 'entry', attributes: {index: 1}},
{id: 'ent2', type: 'entry', attributes: {index: 1}},
{id: 'ent3', type: 'entry', attributes: {index: 2}},
{id: 'ent4', type: 'entry', attributes: {index: 3}}
])
const getCollection = () => ({find})
const request = {
action: 'GET',
params: {
type: 'entry',
typePlural: 'entries',
query: {'attributes.index': {$gte: 1}},
pageAfter: 'entry:ent2',
pageSize: 2
},
endpoint: {
collection: 'documents',
db: 'database',
sort: {'attributes.index': 1}
}
}
const expectedPaging = {
next: {
type: 'entry',
query: {'attributes.index': {$gte: 3}},
pageAfter: 'entry:ent4',
pageSize: 2
}
}

const response = await getDocs(getCollection, request)

t.deepEqual(find.args[0][0], {type: 'entry', 'attributes.index': {$gte: 1}})
t.is(response.status, 'ok')
t.is(response.data.length, 2)
t.is(response.data[0].id, 'ent3')
t.is(response.data[1].id, 'ent4')
t.deepEqual(response.paging, expectedPaging)
})

test('should return empty array when collection query comes back empty', async (t) => {
const find = createFind([])
const getCollection = () => ({find})
const request = {
action: 'GET',
params: {
type: 'entry',
typePlural: 'entries'
},
endpoint: {
collection: 'documents',
db: 'database'
}
}

const response = await getDocs(getCollection, request)

t.is(response.status, 'ok')
t.is(response.data.length, 0)
})

test('should return notfound when member query comes back empty', async (t) => {
const find = createFind([])
const getCollection = () => ({find})
const request = {
action: 'GET',
params: {
id: 'ent1',
type: 'entry',
typePlural: 'entries'
},
endpoint: {
collection: 'documents',
db: 'database'
}
}
const expected = {
status: 'notfound',
error: 'Could not find \'ent1\' of type \'entry\''
}

const response = await getDocs(getCollection, request)

t.deepEqual(response, expected)
})
2 changes: 1 addition & 1 deletion lib/adapter/getDocs.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const getData = async (cursor, pageSize) => {

const getPage = async (cursor, {pageSize = Infinity, pageAfter}) => {
// When pageAfter is set – loop until we find the doc with that _id
const foundFirst = moveToData(cursor, pageAfter)
const foundFirst = await moveToData(cursor, pageAfter)

// Get the number of docs specified with pageSize - or the rest of the docs
if (foundFirst) {
Expand Down
Loading

0 comments on commit 28c352b

Please sign in to comment.