pixl-server-storage
Version:
A key/value/list storage component for the pixl-server framework.
1,350 lines (1,176 loc) • 64.8 kB
JavaScript
// Unit tests for Storage System - List
// Copyright (c) 2015 - 2016 Joseph Huckaby
// Released under the MIT License
var os = require('os');
var fs = require('fs');
var path = require('path');
var cp = require('child_process');
var async = require('async');
var Tools = require('pixl-tools');
module.exports = {
tests: [
function listCreate1(test) {
test.expect(1);
this.storage.listCreate( 'list1', {}, function(err, data) {
test.ok( !err, "No error creating list1: " + err );
test.done();
} );
},
function listGetEmpty1(test) {
test.expect(2);
this.storage.listGet( 'list1', 0, 0, function(err, items) {
test.ok( !!items, "Expected array for empty list" );
test.ok( !items.length, "Expected zero length in items array on empty list" );
test.done();
} );
},
function listPush1(test) {
var self = this;
test.expect(2);
this.storage.listPush( 'list1', { foo: 'bar', number: 123 }, function(err, data) {
test.ok( !err, "No error pushing onto list: " + err );
test.ok( Object.keys(self.storage.locks).length == 0, "No more locks leftover in storage" );
test.done();
} );
},
function listGet1(test) {
var self = this;
test.expect(16);
this.storage.listGet( 'list1', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 1, "List has 1 item: " + items.length );
test.ok( items[0].foo == 'bar', "List item value matches" );
// check internals
self.storage.get( 'list1', function(err, list) {
test.ok( !err, "No error fetching list header: " + err );
test.ok( !!list, "Got list data from header key" );
test.ok( list.type == 'list', "List type is list: " + list.type );
test.ok( list.length == 1, "List length is 1: " + list.length );
test.ok( list.first_page == 0, "List first_page is 0: " + list.first_page );
test.ok( list.last_page == 0, "List last_page is 0: " + list.last_page );
test.ok( list.page_size > 0, "List page_size is non-zero: " + list.page_size );
self.storage.get( 'list1/0', function(err, page) {
test.ok( !err, "No error fetching list page: " + err );
test.ok( !!page, "Got list page data" );
test.ok( page.type == 'list_page', "Page type is correct: " + page.type );
test.ok( !!page.items, "List page has items array" );
test.ok( page.items.length == 1, "List page has 1 item: " + page.items.length );
test.done();
} );
} ); // internals
} );
},
function listPop1(test) {
var self = this;
test.expect(4);
this.storage.listPop( 'list1', function(err, item) {
test.ok( !err, "No error popping list: " + err );
test.ok( !!item, "Item is true" );
test.ok( item.foo == 'bar', "List popped item value matches" );
test.ok( Object.keys(self.storage.locks).length == 0, "No more locks leftover in storage" );
test.done();
} );
},
function listGetEmpty2(test) {
var self = this;
test.expect(15);
this.storage.listGet( 'list1', 0, 0, function(err, items) {
test.ok( !err, "No error expected getting empty list again" );
test.ok( !!items, "Expected array for empty list" );
test.ok( !items.length, "Expected zero length in items array on empty list" );
// check internals
self.storage.get( 'list1', function(err, list) {
test.ok( !err, "No error fetching list header: " + err );
test.ok( !!list, "Got list data from header key" );
test.ok( list.type == 'list', "List type is list: " + list.type );
test.ok( list.length == 0, "List length is 0: " + list.length );
test.ok( list.first_page == 0, "List first_page is 0: " + list.first_page );
test.ok( list.last_page == 0, "List last_page is 0: " + list.last_page );
test.ok( list.page_size > 0, "List page_size is non-zero: " + list.page_size );
self.storage.get( 'list1/0', function(err, page) {
test.ok( !err, "No error fetching list page: " + err );
test.ok( !!page, "Got list page data" );
test.ok( page.type == 'list_page', "Page type is correct: " + page.type );
test.ok( !!page.items, "List page has items array" );
test.ok( page.items.length == 0, "List page has 0 items: " + page.items.length );
test.done();
} );
} ); // internals
} );
},
function listPush2(test) {
var self = this;
test.expect(13);
this.storage.listPush( 'list1', { foo: 'bar2', number: 124 }, function(err, data) {
test.ok( !err, "No error pushing list again: " + err );
// check internals
self.storage.get( 'list1', function(err, list) {
test.ok( !err, "No error fetching list header: " + err );
test.ok( !!list, "Got list data from header key" );
test.ok( list.type == 'list', "List type is list: " + list.type );
test.ok( list.length == 1, "List length is 1: " + list.length );
test.ok( list.first_page == 0, "List first_page is 0: " + list.first_page );
test.ok( list.last_page == 0, "List last_page is 0: " + list.last_page );
test.ok( list.page_size > 0, "List page_size is non-zero: " + list.page_size );
self.storage.get( 'list1/0', function(err, page) {
test.ok( !err, "No error fetching list page: " + err );
test.ok( !!page, "Got list page data" );
test.ok( page.type == 'list_page', "Page type is correct: " + page.type );
test.ok( !!page.items, "List page has items array" );
test.ok( page.items.length == 1, "List page has 1 item: " + page.items.length );
test.done();
} );
} ); // internals
} );
},
function listDelete1(test) {
var self = this;
test.expect(2);
this.storage.listDelete( 'list1', true, function(err, data) {
test.ok( !err, "No error deleting list: " + err );
test.ok( Object.keys(self.storage.locks).length == 0, "No more locks leftover in storage" );
test.done();
} );
},
function listGetEmpty3(test) {
test.expect(1);
this.storage.listGet( 'list1', 0, 0, function(err, items) {
test.ok( !!err, "Error expected getting deleted list" );
test.done();
} );
},
function listGetInfoEmpty1(test) {
var self = this;
test.expect(3);
this.storage.listGetInfo( 'list1', function(err, list) {
test.ok( !!err, "Error expected getting list info after delete" );
// check internals
self.storage.get( 'list1', function(err, list) {
test.ok( !!err, "Error expected fetching list header: " + err );
self.storage.get( 'list1/0', function(err, page) {
test.ok( !!err, "Error expected fetching list page: " + err );
test.done();
} );
} ); // internals
} );
},
function listCreate2(test) {
test.expect(1);
this.storage.listCreate( 'list2', {}, function(err, data) {
test.ok( !err, "No error creating list2: " + err );
test.done();
} );
},
function listPushMulti1(test) {
var self = this;
var idx = 0;
test.expect(1);
async.whilst(
function() { return idx < 10; },
function(callback) {
self.storage.listPush( 'list2', { foo: 'bar', number: idx++ }, function(err, data) {
callback(err);
} );
},
function(err) {
test.ok( !err, "No error pushing items to list: " + err );
test.done();
}
);
},
function listGetMulti1(test) {
test.expect(4);
this.storage.listGet( 'list2', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list2: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 10, "List has 10 items: " + items.length );
test.ok( items[5].number == 5, "List item 5 value matches" );
test.done();
} );
},
function listGetInfo1(test) {
test.expect(2);
this.storage.listGetInfo( 'list2', function(err, list) {
test.ok( !err, "No error getting list info after multi-push: " + err );
test.ok( list.first_page == list.last_page, "First page and last page are the same" );
test.done();
} );
},
function listPushNewPage1(test) {
// This push should create a new page
test.expect(1);
this.storage.listPush( 'list2', { foo: 'bar', number: 10 }, function(err, data) {
test.ok( !err, "No error pushing new page onto list: " + err );
test.done();
} );
},
function listGetMulti2(test) {
test.expect(4);
this.storage.listGet( 'list2', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list2: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 11, "List has 11 items: " + items.length );
test.ok( items[5].number == 5, "List item 5 value matches" );
test.done();
} );
},
function listGetCrossPage1(test) {
test.expect(5);
this.storage.listGet( 'list2', 9, 2, function(err, items) {
test.ok( !err, "No error fetching list2(9,2): " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 2, "List has 2 items: " + items.length );
test.ok( items[0].number == 9, "List item 0 value matches" );
test.ok( items[1].number == 10, "List item 1 value matches" );
test.done();
} );
},
function listGetInfo2(test) {
var self = this;
test.expect(19);
this.storage.listGetInfo( 'list2', function(err, list) {
test.ok( !err, "No error getting list info after new page push: " + err );
test.ok( list.first_page == list.last_page - 1, "First page and last page are one apart" );
// check internals
self.storage.get( 'list2', function(err, list) {
test.ok( !err, "No error fetching list header: " + err );
test.ok( !!list, "Got list data from header key" );
test.ok( list.type == 'list', "List type is list: " + list.type );
test.ok( list.length == 11, "List length is 11: " + list.length );
test.ok( list.first_page == 0, "List first_page is 0: " + list.first_page );
test.ok( list.last_page == 1, "List last_page is 1: " + list.last_page );
test.ok( list.page_size > 0, "List page_size is non-zero: " + list.page_size );
self.storage.get( 'list2/0', function(err, page) {
test.ok( !err, "No error fetching first list page: " + err );
test.ok( !!page, "Got list page data" );
test.ok( page.type == 'list_page', "Page type is correct: " + page.type );
test.ok( !!page.items, "List page has items array" );
test.ok( page.items.length == 10, "List page has 10 items: " + page.items.length );
self.storage.get( 'list2/1', function(err, page) {
test.ok( !err, "No error fetching second list page: " + err );
test.ok( !!page, "Got list page data" );
test.ok( page.type == 'list_page', "Page type is correct: " + page.type );
test.ok( !!page.items, "List page has items array" );
test.ok( page.items.length == 1, "List page has 1 item: " + page.items.length );
test.done();
} );
} );
} ); // internals
} );
},
function listEach(test) {
// iterate over list items using listEach
var num_items = 0;
this.storage.listEach( 'list2',
function(item, idx, callback) {
test.ok( !!item, "Got item" );
test.ok( item.number == idx, "Item has correct number property" );
num_items++;
callback();
},
function(err) {
test.ok( !err, "No error iterating list: " + err );
test.ok( num_items == 11, "Iterated 11 items: " + num_items );
test.done();
}
);
},
function listEachPage(test) {
// iterate over list pages
var num_pages = 0;
this.storage.listEachPage( 'list2',
function(items, callback) {
test.ok( !!items, "Got items from page" );
test.ok( !!items.length, "Nonzero items from page" );
num_pages++;
callback();
},
function(err) {
test.ok( !err, "No error iterating list: " + err );
test.ok( num_pages == 2, "Iterated 2 pages: " + num_pages );
test.done();
}
);
},
function listEachUpdate(test) {
// update some items
var self = this;
this.storage.listEachUpdate( 'list2',
function(item, idx, callback) {
if (idx % 2 == 1) {
item.odd = true;
callback(null, true);
}
else callback();
},
function(err) {
test.ok( !err, "No error iterating list: " + err );
self.storage.listGet( 'list2', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list2: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 11, "List has 11 items: " + items.length );
for (var idx = 0, len = items.length; idx < len; idx++) {
var item = items[idx];
if (idx % 2 == 1) test.ok( !!item.odd, "Odd item is now odd" );
else test.ok( !item.odd, "Even item is not odd" );
}
test.done();
} );
}
);
},
function listEachPageUpdate(test) {
// update some items, a page at a time
var self = this;
this.storage.listEachPageUpdate( 'list2',
function(items, callback) {
items.forEach( function(item) {
if (!item.odd) item.even = true;
} );
callback(null, true);
},
function(err) {
test.ok( !err, "No error iterating list: " + err );
self.storage.listGet( 'list2', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list2: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 11, "List has 11 items: " + items.length );
for (var idx = 0, len = items.length; idx < len; idx++) {
var item = items[idx];
if (idx % 2 == 0) test.ok( !!item.even, "Even item is now even" );
else test.ok( !item.even, "Odd item is not even" );
}
test.done();
} );
}
);
},
function listPop2(test) {
test.expect(3);
this.storage.listPop( 'list2', function(err, item) {
test.ok( !err, "No error popping list: " + err );
test.ok( !!item, "Item is true" );
test.ok( item.number == 10, "List popped item value matches 10: " + item.number );
test.done();
} );
},
function listGetInfo3(test) {
var self = this;
test.expect(15);
this.storage.listGetInfo( 'list2', function(err, list) {
test.ok( !err, "No error getting list info after new page push: " + err );
test.ok( list.first_page == list.last_page, "First page and last page are the same after pop" );
// check internals
self.storage.get( 'list2', function(err, list) {
test.ok( !err, "No error fetching list header: " + err );
test.ok( !!list, "Got list data from header key" );
test.ok( list.type == 'list', "List type is list: " + list.type );
test.ok( list.length == 10, "List length is 10: " + list.length );
test.ok( list.first_page == 0, "List first_page is 0: " + list.first_page );
test.ok( list.last_page == 0, "List last_page is 0: " + list.last_page );
test.ok( list.page_size > 0, "List page_size is non-zero: " + list.page_size );
self.storage.get( 'list2/0', function(err, page) {
test.ok( !err, "No error fetching first list page: " + err );
test.ok( !!page, "Got list page data" );
test.ok( page.type == 'list_page', "Page type is correct: " + page.type );
test.ok( !!page.items, "List page has items array" );
test.ok( page.items.length == 10, "List page has 10 items: " + page.items.length );
self.storage.get( 'list2/1', function(err, page) {
test.ok( !!err, "Expected error fetching second list page: " + err );
test.done();
} );
} );
} ); // internals
} );
},
function listPushNewPage2(test) {
// This push should create a new page (again)
test.expect(1);
this.storage.listPush( 'list2', { foo: 'bar', number: 10, again: 1 }, function(err, data) {
test.ok( !err, "No error pushing new page again: " + err );
test.done();
} );
},
function listGetInfo4(test) {
var self = this;
test.expect(19);
this.storage.listGetInfo( 'list2', function(err, list) {
test.ok( !err, "No error getting list info after new page push again: " + err );
test.ok( list.first_page == list.last_page - 1, "First page and last page are one apart again" );
// check internals
self.storage.get( 'list2', function(err, list) {
test.ok( !err, "No error fetching list header: " + err );
test.ok( !!list, "Got list data from header key" );
test.ok( list.type == 'list', "List type is list: " + list.type );
test.ok( list.length == 11, "List length is 11: " + list.length );
test.ok( list.first_page == 0, "List first_page is 0: " + list.first_page );
test.ok( list.last_page == 1, "List last_page is 1: " + list.last_page );
test.ok( list.page_size > 0, "List page_size is non-zero: " + list.page_size );
self.storage.get( 'list2/0', function(err, page) {
test.ok( !err, "No error fetching first list page: " + err );
test.ok( !!page, "Got list page data" );
test.ok( page.type == 'list_page', "Page type is correct: " + page.type );
test.ok( !!page.items, "List page has items array" );
test.ok( page.items.length == 10, "List page has 10 items: " + page.items.length );
self.storage.get( 'list2/1', function(err, page) {
test.ok( !err, "No error fetching second list page: " + err );
test.ok( !!page, "Got list page data" );
test.ok( page.type == 'list_page', "Page type is correct: " + page.type );
test.ok( !!page.items, "List page has items array" );
test.ok( page.items.length == 1, "List page has 1 item: " + page.items.length );
test.done();
} );
} );
} ); // internals
} );
},
function listShift1(test) {
var self = this;
test.expect(4);
this.storage.listShift( 'list2', function(err, item) {
test.ok( !err, "No error shifting list: " + err );
test.ok( !!item, "Item is true" );
test.ok( item.number === 0, "List popped item value matches 0" );
test.ok( Object.keys(self.storage.locks).length == 0, "No more locks leftover in storage" );
test.done();
} );
},
function listGet2(test) {
test.expect(5);
this.storage.listGet( 'list2', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 10, "List has 10 items: " + items.length );
test.ok( items[0].number == 1, "First item value matches 1" );
test.ok( items[9].number == 10, "Last item value matches 10" );
test.done();
} );
},
function listGetInfo5(test) {
var self = this;
test.expect(19);
this.storage.listGetInfo( 'list2', function(err, list) {
test.ok( !err, "No error getting list info after new page push again: " + err );
test.ok( list.first_page == list.last_page - 1, "First page and last page are one apart again still" );
// page 0 should have 9 items, and page 1 should have 1 item.
// check internals
self.storage.get( 'list2', function(err, list) {
test.ok( !err, "No error fetching list header: " + err );
test.ok( !!list, "Got list data from header key" );
test.ok( list.type == 'list', "List type is list: " + list.type );
test.ok( list.length == 10, "List length is 10: " + list.length );
test.ok( list.first_page == 0, "List first_page is 0: " + list.first_page );
test.ok( list.last_page == 1, "List last_page is 1: " + list.last_page );
test.ok( list.page_size > 0, "List page_size is non-zero: " + list.page_size );
self.storage.get( 'list2/0', function(err, page) {
test.ok( !err, "No error fetching first list page: " + err );
test.ok( !!page, "Got list page data" );
test.ok( page.type == 'list_page', "Page type is correct: " + page.type );
test.ok( !!page.items, "List page has items array" );
test.ok( page.items.length == 9, "List page has 9 items: " + page.items.length );
self.storage.get( 'list2/1', function(err, page) {
test.ok( !err, "No error fetching second list page: " + err );
test.ok( !!page, "Got list page data" );
test.ok( page.type == 'list_page', "Page type is correct: " + page.type );
test.ok( !!page.items, "List page has items array" );
test.ok( page.items.length == 1, "List page has 1 item: " + page.items.length );
test.done();
} );
} );
} ); // internals
} );
},
function listGetCrossPage2(test) {
// Trying multi-page fetch with partial data on first page
test.expect(5);
this.storage.listGet( 'list2', 8, 2, function(err, items) {
test.ok( !err, "No error fetching list2(8,2): " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 2, "List has 2 items: " + items.length );
test.ok( items[0].number == 9, "List item 0 value matches 9" );
test.ok( items[1].number == 10, "List item 1 value matches 10" );
test.done();
} );
},
function listPushMulti2(test) {
// Now filling up second page, should overflow onto third page
var self = this;
var idx = 0;
test.expect(1);
async.whilst(
function() { return idx < 10; },
function(callback) {
self.storage.listPush( 'list2', { foo: 'bar3', number: 11 + idx++ }, function(err, data) {
callback(err);
} );
},
function(err) {
test.ok( !err, "No error pushing items again: " + err );
test.done();
}
);
},
function listGet3(test) {
var self = this;
test.expect(27);
this.storage.listGet( 'list2', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 20, "List has 20 items: " + items.length );
test.ok( items[0].number == 1, "First item value matches 1" );
test.ok( items[19].number == 20, "Last item value matches 20" );
// page 0 should have 9 items, page 1 should have 10 items, and page 2 should have 1 item, totaling 20.
// check internals
self.storage.get( 'list2', function(err, list) {
test.ok( !err, "No error fetching list header: " + err );
test.ok( !!list, "Got list data from header key" );
test.ok( list.type == 'list', "List type is list: " + list.type );
test.ok( list.length == 20, "List length is 20: " + list.length );
test.ok( list.first_page == 0, "List first_page is 0: " + list.first_page );
test.ok( list.last_page == 2, "List last_page is 2: " + list.last_page );
test.ok( list.page_size > 0, "List page_size is non-zero: " + list.page_size );
self.storage.get( 'list2/0', function(err, page) {
test.ok( !err, "No error fetching first list page: " + err );
test.ok( !!page, "Got list page data" );
test.ok( page.type == 'list_page', "Page type is correct: " + page.type );
test.ok( !!page.items, "List page has items array" );
test.ok( page.items.length == 9, "List page has 9 items: " + page.items.length );
self.storage.get( 'list2/1', function(err, page) {
test.ok( !err, "No error fetching second list page: " + err );
test.ok( !!page, "Got list page data" );
test.ok( page.type == 'list_page', "Page type is correct: " + page.type );
test.ok( !!page.items, "List page has items array" );
test.ok( page.items.length == 10, "List page has 10 items: " + page.items.length );
self.storage.get( 'list2/2', function(err, page) {
test.ok( !err, "No error fetching third list page: " + err );
test.ok( !!page, "Got list page data" );
test.ok( page.type == 'list_page', "Page type is correct: " + page.type );
test.ok( !!page.items, "List page has items array" );
test.ok( page.items.length == 1, "List page has 1 item: " + page.items.length );
test.done();
} );
} );
} );
} ); // internals
} );
},
function listCut1(test) {
var self = this;
test.expect(4);
this.storage.listSplice( 'list2', 15, 2, null, function(err, items) {
test.ok( !err, "No error cutting list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items[0].foo == 'bar3', "List cut item value matches" );
test.ok( Object.keys(self.storage.locks).length == 0, "No more locks leftover in storage" );
test.done();
} );
},
function listGet4(test) {
test.expect(5);
this.storage.listGet( 'list2', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
//console.log("GOT ITEMS", items);
test.ok( items.length == 18, "List has 18 items: " + items.length );
test.ok( items[0].number == 1, "First item value matches 1" );
test.ok( items[17].number == 20, "Last item value matches 20" );
test.done();
} );
},
// Unshifting two items at beginning, should overflow first page and create new page at other end
function listUnshiftNewPage1(test) {
// These unshifts should create a new first page
var self = this;
test.expect(3);
this.storage.listUnshift( 'list2', { foo: 'bar4', number: 0 }, function(err, data) {
test.ok( !err, "No error unshifting list: " + err );
self.storage.listUnshift( 'list2', { foo: 'bar4', number: -1 }, function(err, data) {
test.ok( !err, "No error unshifting new page: " + err );
test.ok( Object.keys(self.storage.locks).length == 0, "No more locks leftover in storage" );
test.done();
} );
} );
},
function listGet5(test) {
test.expect(5);
this.storage.listGet( 'list2', 0, 0, function(err, items) {
test.debug( "List Items:", items );
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 20, "List has 20 items: " + items.length );
test.ok( items[0].number == -1, "First item value matches -1" );
test.ok( items[19].number == 20, "Last item value matches 20" );
test.done();
} );
},
// Cutting off last 2 items that were unshifted, this causes root page to move back to 0
function listCut2(test) {
test.expect(3);
this.storage.listSplice( 'list2', 0, 2, null, function(err, items) {
test.debug( "Items Cut:", items );
test.ok( !err, "No error cutting list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items[0].number == -1, "List cut item value matches -1" );
test.done();
} );
},
function listGet6(test) {
test.expect(5);
this.storage.listGet( 'list2', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 18, "List has 18 items: " + items.length );
test.ok( items[0].number == 1, "First item value matches 1" );
test.ok( items[17].number == 20, "Last item value matches 20" );
test.done();
} );
},
function listGet7(test) {
// Testing fetching 5 items from 'end' of list (without knowing length)
test.expect(5);
this.storage.listGet( 'list2', -5, 0, function(err, items) {
test.ok( !err, "No error fetching negative list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 5, "Got 5 items: " + items.length );
test.ok( items[0].number == 14, "First item value matches 14" );
test.ok( items[4].number == 20, "Last item value matches 20" );
test.done();
} );
},
// Adding 1000 items...
function listPushMulti1000(test) {
test.expect(1);
var items = [];
for (var idx = 0; idx < 1000; idx++) {
items.push({ foo: 'bar5', number: 1000 + idx });
}
this.storage.listPush( 'list2', items, function(err, data) {
test.ok( !err, "No error pushing 1000 items: " + err );
test.done();
} );
},
function listGet8(test) {
test.expect(5);
this.storage.listGet( 'list2', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 1018, "List has 1018 items: " + items.length );
test.ok( items[0].number == 1, "First item value matches 1" );
test.ok( items[1017].number == 1999, "Last item value matches 1999" );
test.done();
} );
},
function listEach1(test) {
// test listEach on large list with multiple pages
test.expect(2);
var num_items = 0;
this.storage.listEach( 'list2',
function(item, idx, callback) {
if (item) num_items++;
callback();
},
function(err) {
test.ok( !err, "No error iterating list: " + err );
test.ok( num_items == 1018, "Iterated 1018 items: " + num_items );
test.done();
}
);
},
// Fetching 45 items from numerous pages in the middle
function listGet9(test) {
test.expect(5);
this.storage.listGet( 'list2', 500, 45, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 45, "Got 45 items: " + items.length );
test.ok( items[0].number == 1482, "First item value matches 1482" );
test.ok( items[44].number == 1526, "Last item value matches 1526" );
test.done();
} );
},
// Cutting those 45 items out
function listCut3(test) {
test.expect(5);
this.storage.listSplice( 'list2', 500, 45, null, function(err, items) {
test.ok( !err, "No error cutting list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 45, "Got 45 items: " + items.length );
test.ok( items[0].number == 1482, "First item value matches 1482" );
test.ok( items[44].number == 1526, "Last item value matches 1526" );
test.done();
} );
},
function listGet10(test) {
test.expect(4);
this.storage.listGet( 'list2', 499, 1, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 1, "Got 1 item: " + items.length );
test.ok( items[0].number == 1481, "First item value matches 1481" );
test.done();
} );
},
function listGet11(test) {
test.expect(4);
this.storage.listGet( 'list2', 500, 1, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 1, "Got 1 item: " + items.length );
test.ok( items[0].number == 1527, "First item value matches 1527" );
test.done();
} );
},
function listGetInfo6(test) {
test.expect(2);
this.storage.listGetInfo( 'list2', function(err, list) {
test.ok( !err, "No error getting list info after new page push again: " + err );
test.ok( list.length == 973, "List has 973 items: " + list.length );
test.done();
} );
},
// Testing fetching 5 items from 'end' of list (without knowing length) -- again
function listGet12(test) {
// Testing fetching 5 items from 'end' of list (without knowing length)
test.expect(5);
this.storage.listGet( 'list2', -5, 0, function(err, items) {
test.ok( !err, "No error fetching negative list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 5, "Got 5 items: " + items.length );
test.ok( items[0].number == 1995, "First item value matches 1995" );
test.ok( items[4].number == 1999, "Last item value matches 1999" );
test.done();
} );
},
// Most difficult of all -- cut 11 items, one item at a time, from the second page (first page can shrink / move, second page cannot)
function listCutMultiInsane(test) {
var self = this;
var idx = 0;
// test.expect(1);
async.whilst(
function() { return idx < 11; },
function(callback) {
self.storage.listSplice( 'list2', 18, 1, null, function(err, items) {
test.ok( !err, "No error cutting list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 1, "Got 1 items: " + items.length );
test.ok( items[0].number == idx + 1000, "First item value matches 1482" );
if (err) return callback(err);
self.storage.listGet( 'list2', -1, 0, function(err, items) {
test.ok( !err, "No error fetching negative list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 1, "Got 1 items: " + items.length );
test.ok( items[0].number == 1999, "Last item value matches 1999" );
idx++;
callback(err);
} );
} );
},
function(err) {
test.ok( !err, "No error splicing insanity: " + err );
test.ok( Object.keys(self.storage.locks).length == 0, "No more locks leftover in storage" );
test.done();
}
);
},
function listGetInfo7(test) {
test.expect(2);
this.storage.listGetInfo( 'list2', function(err, list) {
test.ok( !err, "No error getting list info after multi-cut: " + err );
test.ok( list.length == 962, "List has 962 items: " + list.length );
test.done();
} );
},
function listGet13(test) {
// Testing fetching 5 items from 'end' of list (without knowing length)
test.expect(5);
this.storage.listGet( 'list2', -5, 0, function(err, items) {
test.ok( !err, "No error fetching negative list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 5, "Got 5 items: " + items.length );
test.ok( items[0].number == 1995, "First item value matches 1995" );
test.ok( items[4].number == 1999, "Last item value matches 1999" );
test.done();
} );
},
function listFind1(test) {
test.expect(4);
this.storage.listFind( 'list2', { foo: 'bar5', number: 1527 }, function(err, item, idx) {
test.ok( !err, "No error searching list: " + err );
test.ok( !!item, "Item is true" );
test.ok( item.foo == 'bar5', "Item foo matches bar5" );
test.ok( item.number == 1527, "Item value matches 1527" );
test.done();
} );
},
function listFindRegExp1(test) {
var self = this;
test.expect(7);
this.storage.listFind( 'list2', { foo: /^BAR5$/i, number: /1527/ }, function(err, item, idx) {
test.ok( !err, "No error searching list: " + err );
test.ok( !!item, "Item is true" );
test.ok( item.foo == 'bar5', "Item foo matches bar5" );
test.ok( item.number == 1527, "Item value matches 1527" );
// check negative case
self.storage.listFind( 'list2', { foo: /^bar6$/ }, function(err, item, idx) {
test.ok( !err, "No error expected searching list: " + err );
test.ok( !item, "Item is expected to be null" );
test.ok( idx == -1, "Item idx is expected to be -1: " + idx );
test.done();
} );
} );
},
function listFindBad1(test) {
test.expect(3);
this.storage.listFind( 'list2', { number: 2000 }, function(err, item, idx) {
test.ok( !err, "No error expected searching list: " + err );
test.ok( !item, "Item is expected to be null" );
test.ok( idx == -1, "Item idx is expected to be -1: " + idx );
test.done();
} );
},
function listCopy1(test) {
var self = this;
test.expect(5);
this.storage.listCopy( 'list2', 'list3', function(err) {
test.ok( !err, "No error expected copying list: " + err );
self.storage.listGet( 'list3', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 962, "New list3 has 962 items: " + items.length );
test.ok( items[961].number == 1999, "List item value matches" );
test.done();
} );
} );
},
function listRename1(test) {
var self = this;
test.expect(8);
this.storage.listRename( 'list3', 'list4', function(err) {
test.ok( !err, "No error expected renaming list: " + err );
self.storage.listGet( 'list3', 0, 0, function(err, items) {
test.ok( !!err, "Expected error fetching the now deleted list3" );
test.ok( !items, "Items is false" );
self.storage.listGet( 'list4', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 962, "New list4 has 962 items: " + items.length );
test.ok( items[961].number == 1999, "List item value matches" );
self.storage.listDelete( 'list4', true, function(err, data) {
test.ok( !err, "No error deleting list4: " + err );
test.done();
} );
} );
} );
} );
},
// Splice cut with a larger insert
function listSpliceInsertLarger(test) {
var self = this;
test.expect(8);
var to_insert = [
{ inserted: 1 },
{ inserted: 2 }
];
this.storage.listSplice( 'list2', 400, 1, to_insert, function(err, items) {
test.ok( !err, "No error splicing list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 1, "List cut 1 item" );
self.storage.listGet( 'list2', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 963, "list2 now has 963 items: " + items.length );
test.ok( items[400].inserted == 1, "Inserted item value matches" );
test.ok( items[962].number == 1999, "Last item value matches" );
test.done();
} );
} );
},
// Splice cut with a smaller insert
function listSpliceInsertSmaller(test) {
var self = this;
test.expect(8);
var to_insert = [
{ inserted: 3 }
];
this.storage.listSplice( 'list2', 410, 2, to_insert, function(err, items) {
test.ok( !err, "No error splicing list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 2, "List cut 2 items" );
self.storage.listGet( 'list2', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 962, "list2 now has 962 items: " + items.length );
test.ok( items[410].inserted == 3, "Inserted item value matches" );
test.ok( items[961].number == 1999, "Last item value matches" );
test.done();
} );
} );
},
// Splice with an equal cut + insert
function listSpliceInsertEqual(test) {
var self = this;
test.expect(8);
var to_insert = [
{ inserted: 4 },
{ inserted: 5 }
];
this.storage.listSplice( 'list2', 420, 2, to_insert, function(err, items) {
test.ok( !err, "No error splicing list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 2, "List cut 2 items" );
self.storage.listGet( 'list2', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 962, "list2 now has 962 items: " + items.length );
test.ok( items[420].inserted == 4, "Inserted item value matches" );
test.ok( items[961].number == 1999, "Last item value matches" );
test.done();
} );
} );
},
// 0-item cut splice with an insert
function listSpliceZeroInsert(test) {
var self = this;
test.expect(8);
var to_insert = [
{ inserted: 6 },
{ inserted: 7 }
];
this.storage.listSplice( 'list2', 430, 0, to_insert, function(err, items) {
test.ok( !err, "No error splicing list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 0, "List cut 0 items" );
self.storage.listGet( 'list2', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 964, "list2 now has 964 items: " + items.length );
test.ok( items[430].inserted == 6, "Inserted item value matches" );
test.ok( items[963].number == 1999, "Last item value matches" );
test.done();
} );
} );
},
// Splice insert with enough new items to cause a new page
function listSpliceInsertLarger2(test) {
var self = this;
test.expect(8);
var to_insert = [
{ inserted: 10 },
{ inserted: 11 },
{ inserted: 12 },
{ inserted: 13 },
{ inserted: 14 },
{ inserted: 15 },
{ inserted: 16 },
{ inserted: 17 },
{ inserted: 18 },
{ inserted: 19 },
{ inserted: 20, vegetable: "carrot" },
{ inserted: 21, vegetable: "carrot" },
{ inserted: 22, vegetable: "carrot" }
];
this.storage.listSplice( 'list2', 440, 1, to_insert, function(err, items) {
test.ok( !err, "No error splicing list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 1, "List cut 1 item" );
self.storage.listGet( 'list2', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 976, "list2 now has 976 items: " + items.length );
test.ok( items[440].inserted == 10, "Inserted item value matches" );
test.ok( items[975].number == 1999, "Last item value matches" );
test.done();
} );
} );
},
function listFindCut1(test) {
// test the listFindCut macro function
var self = this;
test.expect(10);
this.storage.listFindCut( 'list2', { inserted: 17 }, function(err, item) {
test.ok( !err, "No error after listFindCut: " + err );
test.ok( !!item, "Cut item is true" );
test.ok( item.inserted == 17, "Cut item value matches" );
test.ok( Object.keys(self.storage.locks).length == 0, "No locks remaining after listFindCut" );
self.storage.listGet( 'list2', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 975, "list2 now has 975 items: " + items.length );
test.ok( items[440].inserted == 10, "Item value matches before splice area" );
test.ok( items[447].inserted == 18, "Item value matches after splice area" );
test.ok( items[974].number == 1999, "Last item value matches" );
test.done();
} );
} );
},
function listFindReplace1(test) {
// test the listFindReplace macro function
var self = this;
test.expect(7);
this.storage.listFindReplace( 'list2', { inserted: 18 }, { replaced: 18, counter: 1 }, function(err) {
test.ok( !err, "No error after listFindReplace: " + err );
test.ok( Object.keys(self.storage.locks).length == 0, "No locks remaining after listFindReplace" );
self.storage.listGet( 'list2', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 975, "list2 still has 975 items: " + items.length );
test.ok( items[447].replaced == 18, "Item value matches after replace" );
test.ok( items[974].number == 1999, "Last item value matches" );
test.done();
} );
} );
},
function listFindUpdate1(test) {
// test the listFindUpdate macro function
var self = this;
var criteria = { replaced: 18 };
var updates = { replaced: 118, counter: "+1", newfoo: "hello" };
test.expect(9);
this.storage.listFindUpdate( 'list2', criteria, updates, function(err, item) {
test.ok( !err, "No error after listFindUpdate: " + err );
test.ok( Object.keys(self.storage.locks).length == 0, "No locks remaining after listFindUpdate" );
self.storage.listGet( 'list2', 0, 0, function(err, items) {
test.ok( !err, "No error fetching list: " + err );
test.ok( !!items, "Items is true" );
test.ok( items.length == 975, "list2 still has 975 items: " + items.length );
test.ok( items[447].replaced == 118, "Item value matches after update" );
test.ok( items[447].counter == 2, "Counter was successfully incremented" );
test.ok( items[447].newfoo == "hello", "New property was successfully added" );
test.ok( items[974].number == 1999, "Last item value matches" );
test.done();
} );
} );
},
function listFindEach1(test) {
// test listFindEach on large list with multiple pages
test.expect(8);
var num_items = 0;
var criteria = { vegetable: "carrot" };
this.storage.listFindEach( 'list2', criteria,
function(item, idx, callback) {
if (item) num_items++;
test.ok( !!item, "Item was passed to iterator" );
test.ok( item.vegetable == 'carrot', "Item has correct vegetable" );
callback();
},
function(err) {
test.ok( !err, "No error iterating list: " + err );
test.ok( num_items == 3, "Found 3 items: " + num_items );
test.done();
}
);
},
function listFindEachRegExp1(test) {
// test listFindEach on large list with multiple pages, using reg exp
test.expect(8);
var num_items = 0;
var criteria = { vegetable: /^CARROT$/i };
this.storage.listFindEach( 'list2', criteria,
function(item, idx, callback) {
if (item) num_items++;
test.ok( !!item, "Item was passed to iterator" );
test.ok( item.vegetable == 'carrot', "Item has correct vegetable" );
callback();
},
function(err) {
test.ok( !err, "No error iterating list: " + err );
test.ok( num_items == 3, "Found 3 items: " + num_items );
test.done();
}
);
},
// Deleting entire list
function listDelete2(test) {
test.expect(1);
this.storage.listDelete( 'list2', true, function(err, data) {
test.ok( !err, "No error deleting list2: " + err );
test.done();
} );
},
// Making sure list2 was deleted
function listGetEmpty4(test) {
test.expect(1);
this.storage.listGet( 'list2', 0, 0, function(err, items) {
test.ok( !!err, "Error expected getting deleted list2" );
test.done();
} );
},
function listGetInfoEmpty2(test) {
test.expect(1);
this.storage.listGetInfo( 'list2', function(err, list) {
test.ok( !!err, "Error expected getting list2 info after delete" );
test.done();
} );
},
function listShiftClear(test) {
// create list with 1 item, then shift it off, and make sure we have a clean empty list leftover
var self = this;
var key = 'clearlist1';
test.expect( 17 );
this.storage.listPush( key, { foo: 'bar' }, function(err) {
test.ok( !err, "No error pushing list: " + err );
self.storage.listShift( key, function(err, item) {
test.ok( !err, "No error shifting list: " + err );
self.storage.get( key, function(err, list) {
test.ok( !err, "No error fetching list header: " + err );
test.ok( !!list, "Got list data from header key" );
test.ok( list.type == 'list', "List type is list: " + list.type );
test.ok( list.length == 0, "List length is 0: " + list.length );
test.ok( list.first_page == 0, "List first_page is 0: " + list.first_page );
test.ok( list.last_page == 0, "List last_page is 0: " + list.last_page );
test.ok( list.page_size > 0, "List page_size is non-zero: " + list.page_size );
self.storage.get( key + '/0', function(err, page) {
test.ok( !err, "No error fetching list page: " + err );
test.ok( !!page, "Got list page data" );
test.ok( page.type == 'list_page', "Page type is correct: " + page.type );
test.ok( !!page.items, "List page has items array" );
test.ok( page.items.length == 0, "List page has 0 items: " + page.items.length );
self.storage.listDelete( key, true, function(err) {
test.ok( !err, "No error deleting list: " + err );
self.storage.get( key, function(err, list) {
test.ok( !!err, "Error expected fetching list header after delete: " + err );
self.storage.get( key + '/0', function(err, page) {
test.ok( !!err, "Error expected fetching list page after delete: " + err );
test.done();
} ); // get page
} ); // get header
} ); // delete
} ); // get page
} ); // get header
} ); // shift
} ); // push
},
function listPopClear(test) {
// create list with 1 item, then pop it off, and make sure we have a clean empty list leftover
var self = this;
var key = 'clearlist2';
test.expect( 17 );
this.storage.listPush( key, { foo: 'bar' }, function(err) {
test.ok( !err, "No error pushing list: " + err );
self.storage.listPop( key, function(err, item) {
test.ok( !err, "No error popping list: " + err );
self.storage.get( key, function(err, list) {
test.ok( !err, "No error fetching list header: " + err );
test.ok( !!list, "Got list data from header key" );
test.ok( list.type == 'list', "List type is list: " + list.type );
test.ok( list.length == 0, "List length is 0: " + list.length );
test.ok( list.first_page == 0, "List first_page is 0: " + list.first_page );
test.ok( list.last_page == 0, "List last_page is 0: " + list.last_page );
test.ok( list.page_size > 0, "List page_size is non-zero: " + list.page_size );
self.storage.get( key + '/0', function(err, page) {
test.ok( !err, "No error fetching list page: " + err );
test.ok( !!page, "Got list page data" );
test.ok( page.type == 'list_page', "Page type is correct: " + page.type );
test.ok( !!page.items, "List page has items array" );
test.ok( page.items.length == 0, "List page has 0 items: " + page.items.length );
self.storage.listDelete( key, true, function(err) {
test.ok( !err, "No error deleting list: " + err );
self.storage.get( key, function(err, list) {
test.ok( !!err, "Error expected fetching list header after delete: " + err );
self.storage.get( key + '/0', function(err, page) {
test.ok( !!err, "Error expected fetching list page after delete: " + err );
test.done();
} ); // get page
} ); // get header
} ); // delete