--[[----------------------------------------------------------------------------

WFRxDocStoreTestFactory.lua

--------------------------------------------------------------------------------

ADOBE CONFIDENTIAL
------------------
Copyright 2015 Adobe Systems Incorporated
All Rights Reserved.

NOTICE: All information contained herein is, and remain the property of
Adobe Systems Incorporated and its suppliers, if any. The intellectual and
technical concepts contained herein are proprietary to Adobe Systems
Incorporated and its suppliers and may be covered by U.S. and Foreign
Patents, patents in process, and are protected by trade secret or copyright
law.  Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.

--------------------------------------------------------------------------------

This file allows us to share the test suite for WFRxDocStoreImp and
WFRxDocStore.

------------------------------------------------------------------------------]]

	-- RxLua
local Rx = require 'Rx'
local Observable = Rx.Observable

	-- WF.core.os
local KSPathUtils = require 'KSPathUtils'
local WFFileUtils = require 'WFFileUtils'

	-- WF.core.self_test
local WFRxTestCase = require 'WFRxTestCase'

	-- WF.core.utils
local KSIdGenerator = require 'KSIdGenerator'
local KSTableUtils = require 'KSTableUtils'
local WFCloseSupport = require 'WFCloseSupport'

local WFCapabilities = require "WFCapabilities"

--------------------------------------------------------------------------------

local TEST_WAIT_TIME = 6

local kRevIdPattern = '^[0-9a-f]+$'


--============================================================================--

return function( docStoreClassNamespace, options )

	-- SPAM [jandrew 2021-02-19]: We pass the class namespace into the factory
	-- instead of the class itself in order to delay the require associated
	-- with the namespace until test execution; this allows all uses of WFCapabilities
	-- within the class to work as expected.

	options = options or {}

	local explicitConflictRequest = not not options.explicitConflictRequest

	local RxDocStoreTestSuite = {}

	local coveragePath

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.setUp( _coveragePath )

		coveragePath = _coveragePath
		
	end

	----------------------------------------------------------------------------

	local function unrequireNamespace( namespace )
	
		_G[ docStoreClassNamespace ] = nil
		package.loaded[ docStoreClassNamespace ] = nil
	
	end

	----------------------------------------------------------------------------

	local function setUpDocStore( rxTest, optDocStore )

		local docStoreClass
		
		if not optDocStore then
		
			-- SPAM [jandrew 2021-05-17]: Some of the tests in this factory use doc
			-- store configuration flags in order to test unreleased code. These
			-- flags are analyzed at the time when the namespace is required. In order
			-- to guarantee that these flags will be picked up, we force the namespace
			-- to be unloaded so that the subsequent require will always reload the 
			-- namespace and therefore pick up any additional configuration flags.
			
			unrequireNamespace( docStoreClassNamespace )
			
			docStoreClass = require( docStoreClassNamespace )
			
		end
		
		local docStore = optDocStore or docStoreClass( ':memory:' )

		if docStoreClassNamespace == 'WFRxDocStoreImp' then
			docStore._TEST_unpackDocValue = true
		end

		rxTest:finally( function()

			if docStore and not WFCloseSupport.hasBeenOrWillBeClosed( docStore ) then

				local waitForThis = docStore:performIntegrityCheck()

				if coveragePath then
					waitForThis = waitForThis
						:concatMap( function()
							return docStore:DEBUG_dumpCoverage( coveragePath )
						end )
				end

				local function closeWhenDone()
					docStore:close()
					docStore = nil
				end

				return waitForThis:finally( closeWhenDone )

			end

		end )

		return docStore

	end

	RxDocStoreTestSuite.setUpDocStore = setUpDocStore

	----------------------------------------------------------------------------

	local function mapAwayRevHashCode( value )

		if type( value ) ~= 'table' then
			return value
		end

		local result = {}
		for k, v in pairs( value ) do

			if k == '_localId' then
			elseif k ~= '_revId' and k ~= 'revId' then
				result[ k ] = v
			else
				if #v == 32 and v:match( '^[0-9a-f]+$' ) then
					result[ k ] = "(valid hash)"
				else
					result[ k ] = v
				end
			end

		end

		return result

	end

	----------------------------------------------------------------------------

	local function setDocValueAndValidateRevId( rxTest, docStore, docValue )

		local revId

		rxTest:expectSingleValue(

			docStore:setDocValue( docValue )
				:map( function( rev )
					revId = rev
					return not not rev:match( '^[0-9a-f]+$' ), #rev, rev
				end ),

			true, 32, ':ignore:' )

		return revId

	end

	RxDocStoreTestSuite.setDocValueAndValidateRevId = setDocValueAndValidateRevId

	----------------------------------------------------------------------------

	local function setDocValueAndValidateSpaceId( rxTest, docStore, docValue )

		local spaceId

		rxTest:expectSingleValue(

			docStore:setDocValue( docValue )
			
				:flatMap( function()
				
					return docStore:docValue( docValue._id or docValue._localId )
				
				end )
				
				:map( function( docValue )
				
					spaceId = docValue._spaceId
					return not not spaceId:match( '^[0-9a-f]+$' ), #spaceId, spaceId
					
				end ),

			true, 32, ':ignore:' )

		return spaceId

	end

	----------------------------------------------------------------------------

	local function setDocValue_firstRevTest42( rxTest, docStore )

		return setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				test = 42,
			} )

	end

    ----------------------------------------------------------------------------

	local function deleteDB( path )

		WFFileUtils.deleteFile( path )
		WFFileUtils.deleteFile( path .. '-shm' )
		WFFileUtils.deleteFile( path .. '-wal' )

	end

	----------------------------------------------------------------------------

	-- @mark database path creation

	----------------------------------------------------------------------------

	local function testCreateDocStoreAtPath( testName, path, isRealFile )

		RxDocStoreTestSuite[ 'rxTestCreateDocStoreAtPath_' .. testName ] = function( rxTest )

			-- Since most of the tested paths are in-memory, hard to verify that
			-- anything is actually there.

			if isRealFile then

				deleteDB( path )

			end

			local docStoreClass = require( docStoreClassNamespace )

			local docStore = setUpDocStore( rxTest, docStoreClass( path ) )

			-- Set doc value and capture rev ID.

			local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

			rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
				{
					_id = 'testDoc',
					_localId = 1,
					_changeCounter = 1,
					_revId = firstRevId,
					test = 42
				} )

			if isRealFile then
				rxTest:expectEqual( true, WFFileUtils.exists( path ) == 'file' )
			end

			rxTest:expectOnlyCompleted( docStore:performIntegrityCheck() )

			rxTest:expectOnlyCompleted( docStore:haltAndDelete() )

			if isRealFile then
				rxTest:expectEqual( false, WFFileUtils.exists( path ) == 'file' )
			end

			docStore = nil

		end

	end

	testCreateDocStoreAtPath( 'temp', ':temp:' )

	testCreateDocStoreAtPath( 'temp42', ':temp:42' )

	testCreateDocStoreAtPath( 'empty', '' )

	testCreateDocStoreAtPath( 'fullName',
							   KSPathUtils.child( KSPathUtils.getStandardFilePath 'temp',
												  'RxDocStoreTestSuite' ),
							   true )

	RxDocStoreTestSuite.doTestCreateDocStoreAtPath = testCreateDocStoreAtPath
		-- Funky naming to prevent this func itself from being picked up
		-- as a test case.

	----------------------------------------------------------------------------

	-- @mark whole document values

	----------------------------------------------------------------------------

	local function testGetSetDocValue_accepts( testName, docValue )

		RxDocStoreTestSuite[ 'rxTestGetSetDocValueAccepts_' .. testName ] = function( rxTest )

			rxTest:setTimeLimit( TEST_WAIT_TIME )

			local docStore = setUpDocStore( rxTest )

			local myDocValue = KSTableUtils.deepCopy( docValue )

			local docId = myDocValue._id

			local expectedValue = KSTableUtils.deepCopy( myDocValue )
			expectedValue._changeCounter = 1

			local revId = setDocValueAndValidateRevId( rxTest, docStore, myDocValue )

			if myDocValue._deleted then

				expectedValue = {
					_id = docId,
					_empty = true,
					_deleted = true,
					_revId = revId,
				}

			else
				expectedValue._revId = revId
				expectedValue._localId = 1
			end

			rxTest:expectSingleValue( docStore:docValue( docId ), expectedValue )

		end

		RxDocStoreTestSuite[ 'rxTestBulkSetDocValueAccepts_' .. testName ] = function( rxTest )

			rxTest:setTimeLimit( TEST_WAIT_TIME )

			local docStore = setUpDocStore( rxTest )

			local myDocValue = KSTableUtils.deepCopy( docValue )

			local docId = myDocValue._id

			local expectedValue = KSTableUtils.deepCopy( myDocValue )
			expectedValue._changeCounter = 1
			expectedValue._revId = "(valid hash)"

			if myDocValue._deleted then
				expectedValue = {
					_id = docId,
					_empty = true,
					_deleted = true,
					_revId = "(valid hash)"
				}
			end

			rxTest:expectSingleValue(
				docStore:bulkSetDocValue { myDocValue },
				':ignore:' )

			rxTest:expectSingleValue(

				docStore:docValue( docId )
					:map( function( docValue )
						return mapAwayRevHashCode( docValue ), docValue
					end ),

				expectedValue, ':ignore:' )

		end

	end

	----------------------------------------------------------------------------

	local function testSetDocValue_rejects( testName, docValue )

		RxDocStoreTestSuite[ 'rxTestSetDocValueRejects_' .. testName ] = function( rxTest )

			rxTest:setTimeLimit( TEST_WAIT_TIME )

			local docStore = setUpDocStore( rxTest )

			rxTest:expectOnlyError(

				Observable.pcall( function()
					return docStore:setDocValue( docValue )
				end ),

				':ignore:' )

		end

		RxDocStoreTestSuite[ 'rxTestBulkSetDocValueRejects_' .. testName ] = function( rxTest )

			rxTest:setTimeLimit( TEST_WAIT_TIME )

			local docStore = setUpDocStore( rxTest )

			rxTest:expectOnlyError(

				Observable.pcall( function()
					return docStore:bulkSetDocValue { docValue }
				end ),

				':ignore:' )

		end

	end

	----------------------------------------------------------------------------

	local function testGetSetDocValue_acceptsDocId( testName, docId )

		testGetSetDocValue_accepts( 'docId_' .. testName, { _id = docId, test = 42 } )

	end

	testGetSetDocValue_acceptsDocId( 'testDoc', 'testDoc' )

	----------------------------------------------------------------------------

	local function testSetDocValue_rejectsDocId( testName, docId )

		testSetDocValue_rejects( 'docId_' .. testName, { _id = docId, test = 42 } )

	end

	testSetDocValue_rejectsDocId( 'nil', nil )

	testSetDocValue_rejectsDocId( 'boolean', true )

	testSetDocValue_rejectsDocId( 'number', -42 )

	testSetDocValue_rejectsDocId( 'table', {} )

	if options.testNonTransitableData then
		testSetDocValue_rejectsDocId( 'function', function() end )
	end

	testSetDocValue_rejectsDocId( 'underscore', '_' )

	testSetDocValue_rejectsDocId( 'changes', '_changes' )

	testSetDocValue_rejectsDocId( 'compact', '_compact' )

	testSetDocValue_rejectsDocId( '3xunderscore', '___' )

	-- Although WFRxDocStore is heavily inspired by CouchDB, since outright
	-- compatibility is not a goal, we disallow some of CouchDB's oddball(?)
	-- document naming behaviors.

	testSetDocValue_rejectsDocId( 'emptyString', '' )
	testSetDocValue_rejectsDocId( 'controlChar', 'abc\ndef' )

	testSetDocValue_rejectsDocId( 'design', '_design' )
	testSetDocValue_rejectsDocId( 'designSlash', '_design/' )
	testSetDocValue_rejectsDocId( 'designDoc', '_design/1' )
	testSetDocValue_rejectsDocId( 'designDoc', '_design/blah' )

	testSetDocValue_rejectsDocId( 'local', '_local' )
	testSetDocValue_rejectsDocId( 'localSlash', '_local/' )
	testSetDocValue_rejectsDocId( 'localFoo', '_local/foo' )

	----------------------------------------------------------------------------

	local function testGetSetDocValue_acceptsDocValue( testName, docValue )

		docValue._id = 'testDoc'
		testGetSetDocValue_accepts( 'docValue_' .. testName, docValue )

	end

	testGetSetDocValue_acceptsDocValue( 'emptyTable', {} )

	testGetSetDocValue_acceptsDocValue( 'emptyStringKey', { [ '' ] = 'empty string' } )

	testGetSetDocValue_acceptsDocValue( 'nestedArray',
		{
			nested = {
				"ein", "zwei", 3, "four", "five", true
			}
		} )

	testGetSetDocValue_acceptsDocValue( 'nestedArrayAndNestedHash',
		{
			test = 42,
			array = { 'a', 'b', 'c' },
			hash = { a = 1, b = 2, c = 3 },
		} )

	testGetSetDocValue_acceptsDocValue( 'gappyArray',
		{
			nested = { 'one', nil, 'three' }
		} )

	testGetSetDocValue_acceptsDocValue( 'nestedUnderscoreKey',
		{
			nested = {
				_blah = 42
					-- This wasn't legal in WFDocStore, but is legal in WFRxDocStore.
			},
		} )

	testGetSetDocValue_acceptsDocValue( 'nestedEmptyStringKey',
		{
			nested = {
				[ '' ] = 42
					-- This wasn't legal in WFDocStore, but is legal in WFRxDocStore.
			},
		} )

	testGetSetDocValue_acceptsDocValue( 'deletedWithOtherKeys',
		{
			_deleted = true,
			nothingElseAllowed = false,
				-- This wasn't legal in WFDocStore, but is legal in WFRxDocStore.
		} )

	testGetSetDocValue_acceptsDocValue( 'localOnly',
		{
			_localOnly = {
				someStuff = "that doesn't sync",
			},
		} )

	testGetSetDocValue_acceptsDocValue( 'localOnlyAndNormalBody',
		{
			_localOnly = {
				someStuff = "that doesn't sync",
			},
			andThis = {
				isStuff = "that does sync",
			},
		} )

	testGetSetDocValue_acceptsDocValue( 'labels',
		{
			_labels = {
				label1 = true,
				label2 = true,
			},
			blah = 42,
			flap = 45,
		} )

	----------------------------------------------------------------------------

	local function testSetDocValue_rejectsDocValue( testName, docValue, suppressDocId )

		if not suppressDocId then
			docValue._id = 'testDoc'
		end

		testSetDocValue_rejects( 'docValue_' .. testName, docValue )

	end

	testSetDocValue_rejectsDocValue( 'nil', nil, true )

	testSetDocValue_rejectsDocValue( 'number', 42, true )

	testSetDocValue_rejectsDocValue( 'noId', {}, true )

	if options.testNonTransitableData then
		testSetDocValue_rejectsDocValue( 'metatable', setmetatable( { test = 42 }, {} ) )
	end

	testSetDocValue_rejectsDocValue( 'topLevelUnderscore', { _blah = 42 } )

	testSetDocValue_rejectsDocValue( 'deletedFalse', { _deleted = false } )

	testSetDocValue_rejectsDocValue( 'deletedNumber', { _deleted = 43 } )

	testSetDocValue_rejectsDocValue( 'deletedTable', { _deleted = {} } )

	testSetDocValue_rejectsDocValue( 'slot', { _slot = 'main' } )
		-- Disallow WFDocStore concept.

	testSetDocValue_rejectsDocValue( 'hash', { _hash = 'abcdef' } )
		-- Disallow WFDocStore concept.

	testSetDocValue_rejectsDocValue( 'revIdNotNumber', { _revId = 42 } )

	testSetDocValue_rejectsDocValue( 'revIdsArrayEmpty', { _revIds = {} } )

	testSetDocValue_rejectsDocValue( 'revIdsNotArray', { _revIds = 42 } )

	testSetDocValue_rejectsDocValue( 'revIdsAndRev', { _revIds = 42, _revId = '9e54148a7b804ec8b9397aecdb1e715a' } )

	testSetDocValue_rejectsDocValue( 'topLevelNumericKey', { [ 42 ] = 'not a string' } )

	testSetDocValue_rejectsDocValue( 'topLevelBooleanKey', { [ true ] = 'not a string' } )

	if options.testNonTransitableData then
		testSetDocValue_rejectsDocValue( 'func', { func = function() end } )
	end

	testSetDocValue_rejectsDocValue( 'nestedBoolKey',
		{
			nested = {
				[ true ] = 'enforce non-string/number rule in nested tables',
			},
		} )

	testSetDocValue_rejectsDocValue( 'deeplyNestedBoolKey',
		{
			nested = {
				nested2 = {
					[ true ] = 'enforce non-string/number rule in deeply nested tables',
				},
			},
		} )

	testSetDocValue_rejectsDocValue( 'negativeNumberAsKey',
		{
			nested = {
				[ -1 ] = "no negative array indices"
			}
		} )

	testSetDocValue_rejectsDocValue( 'mixedArrayAndHash',
		{
			nested = { 1, 2, 3, four = "not allowed" }
		} )

	testSetDocValue_rejectsDocValue( 'zeroAsKey',
		{
			nested = {
				[ 0 ] = "no negative array indices"
			}
		} )

	testSetDocValue_rejectsDocValue( 'nonIntegerAsKey',
		{
			nested = {
				[ 1.745 ] = "no non-integer indices"
			}
		} )

	testSetDocValue_rejectsDocValue( 'infinityAsKey',
		{
			nested = {
				[ 1 / 0 ] = "to infinity and beyond, but not in doc values"
			}
		} )

	testSetDocValue_rejectsDocValue( 'negativeInfinityAsKey',
		{
			nested = {
				[ -1 / 0 ] = "to negative infinity and beyond, but not in doc values"
			}
		} )

	testSetDocValue_rejectsDocValue( 'infinityAsValue',
		{
			a = 1 / 0, -- infinity not allowed as value (?)
		} )

	testSetDocValue_rejectsDocValue( 'negativeInfinityAsValue',
		{
			a = -1 / 0, -- negative infinity not allowed as value (?)
		} )

	testSetDocValue_rejectsDocValue( 'NaNAsValue',
		{
			a = 0 / 0, -- NaN not allowed as value (?)
		} )

	-- Note that Lua doesn't allow you to construct a table with 0/0 as a key, so
	-- there's no need to explicitly test that case here.

	testSetDocValue_rejectsDocValue( 'localOnlyNotTable',
		{
			_localOnly = 42,
		} )

	testSetDocValue_rejectsDocValue( 'labelsNotTrue',
		{
			_labels = {
				label1 = false,
			},
			blah = 42,
			flap = 45,
		} )

	testSetDocValue_rejectsDocValue( 'labelsNotTable',
		{
			_labels = 'blah',
			blah = 42,
			flap = 45,
		} )

	testSetDocValue_rejectsDocValue( 'labelNamesNotString',
		{
			_labels = {
				[ 1 ] = true,
			},
			blah = 42,
		} )

	if options.testNonTransitableData then

		local t = {}
		t.nest = t

		testSetDocValue_rejectsDocValue( 'infiniteRecursion', t )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetDocValue_empty( rxTest )

		local docStore = setUpDocStore( rxTest )

		rxTest:expectSingleValue( docStore:docValue( 'noSuchDoc' ),
			{
				_id = 'noSuchDoc',
				_empty = true,
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetSetDocValue_replacingRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision of empty document.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		-- Replace that with second revision and read back.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = secondRevId,
				_changeCounter = 2,
				test = 45,
			} )

		-- Replace that with third revision. Read back again.

		local thirdRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = secondRevId,
				because = "NoSQL",
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = thirdRevId,
				_changeCounter = 3,
				because = "NoSQL",
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValue_replacingRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision of empty document.

		local firstRevId

		local expectedBulkResult1 = {
			{
				id = 'testDoc',
				localId = 1,
				ok = true,
				-- revId = (inserted later),
			}
		}

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					test = 42,
				}
			}
			:tap( function( results )
				local revId = results[ 1 ].revId
				if revId:match( kRevIdPattern ) then
					expectedBulkResult1[ 1 ].revId = revId
					firstRevId = revId
				end
			end ),

			expectedBulkResult1 )

		-- Replace that with second revision and read back.

		local secondRevId

		local expectedBulkResult2 = {
			{
				id = 'testDoc',
				localId = 1,
				ok = true,
				-- revId = (inserted later),
			}
		}

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					_revId = firstRevId,
					test = 45,
				}
			}
			:tap( function( results )
				local revId = results[ 1 ].revId
				if revId:match( kRevIdPattern ) then
					expectedBulkResult2[ 1 ].revId = revId
					secondRevId = revId
				end
			end ),

			expectedBulkResult2 )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = secondRevId,
				_changeCounter = 2,
				test = 45,
			} )

		-- Replace that with third revision. Read back again.

		local thirdRevId

		local expectedBulkResult3 = {
			{
				id = 'testDoc',
				localId = 1,
				ok = true,
				-- revId = (inserted later),
			}
		}

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					_revId = secondRevId,
					because = "NoSQL",
				}
			}
			:tap( function( results )
				local revId = results[ 1 ].revId
				if revId:match( kRevIdPattern ) then
					expectedBulkResult3[ 1 ].revId = revId
					thirdRevId = revId
				end
			end ),

			expectedBulkResult3 )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = thirdRevId,
				_changeCounter = 3,
				because = "NoSQL",
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetDocValue_specifyingRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision of empty document.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		-- Replace that with second revision.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )

		-- Replace that with third revision. Read back again.

		local thirdRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = secondRevId,
				because = "NoSQL",
			} )

		-- Now read back all three revisions.

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', firstRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = firstRevId,
				_changeCounter = 1,
				test = 42,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', secondRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = secondRevId,
				_changeCounter = 2,
				test = 45,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', thirdRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = thirdRevId,
				_changeCounter = 3,
				because = "NoSQL",
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetDocValue_specifyingRevArray( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision of empty document.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		-- Replace that with second revision.

		local secondRevId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectEqual( secondRevId,
			setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revIds = { firstRevId, secondRevId },
				test = 45,
			} ) )

		-- Replace that with third revision. Read back again.

		local thirdRevId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectEqual( thirdRevId,
			setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revIds = { firstRevId, secondRevId, thirdRevId },
				because = "NoSQL",
			} ) )

		-- Now read back all three revisions.

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', firstRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = firstRevId,
				_changeCounter = 1,
				test = 42,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', secondRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = secondRevId,
				_changeCounter = 2,
				test = 45,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', thirdRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = thirdRevId,
				_changeCounter = 3,
				because = "NoSQL",
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetDocValue_specifyingLabel( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision of empty document.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		-- Replace that with second revision.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				_labels = {
					findMe = true,
				},
				test = 45,
			} )

		-- Replace that with third revision.

		setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = secondRevId,
				because = "NoSQL",
			} )

		-- Now read back second revision by specifying label.

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', 'findMe' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = secondRevId,
				_labels = {
					findMe = true,
				},
				_changeCounter = 2,
				test = 45,
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetDocValue_specifyingDeletedRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create initial value.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		-- Replace that with second (deleted) revision.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				_deleted = true,
			} )

		-- Replace that with third revision. Read back again.

		local thirdRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = secondRevId,
				because = "NoSQL",
			} )

		-- Now read back all three revisions.

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', firstRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = firstRevId,
				_changeCounter = 1,
				test = 42,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', secondRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = secondRevId,
				_changeCounter = 2,
				_deleted = true,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', thirdRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = thirdRevId,
				_changeCounter = 3,
				because = "NoSQL",
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetDocValue_specifyingDeletedRevViaArray( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create initial value.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		-- Replace that with second (deleted) revision.

		local secondRevId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectEqual( secondRevId,
			setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revIds = { firstRevId, secondRevId },
				_deleted = true,
			} ) )

		-- Replace that with third revision. Read back again.

		local thirdRevId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectEqual( thirdRevId,
			setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revIds = { firstRevId, secondRevId, thirdRevId },
				because = "NoSQL",
			} ) )

		-- Now read back all three revisions.

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', firstRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = firstRevId,
				_changeCounter = 1,
				test = 42,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', secondRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = secondRevId,
				_changeCounter = 2,
				_deleted = true,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', thirdRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = thirdRevId,
				_changeCounter = 3,
				because = "NoSQL",
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetDocValue_deleteAndRecreate( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision of empty document.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		-- Replace that with second (deleted) revision.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				_deleted = true,
			} )

		-- Replace that with third revision (identical content to first rev).
		-- Read back again.

		local thirdRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = secondRevId,
				test = 42,
			} )

		-- Now read back all three revisions.

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', firstRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = firstRevId,
				_changeCounter = 1,
				test = 42,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', secondRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = secondRevId,
				_changeCounter = 2,
				_deleted = true,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', thirdRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = thirdRevId,
				_changeCounter = 3,
				test = 42,
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetDocValue_preserveDeletionInConflict( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision of empty document.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		-- Replace that with second (deleted) revision.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				_deleted = true,
			} )

		-- Now create another revision that derives from the first revision.
		-- This should be treated as a conflict with the existing deletion
		-- remaining as the winning revision.

		local conflictingSecondRevision = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					_revIds = { firstRevId, conflictingSecondRevision },
					test = 59,
				}
			},

			{
				{
					id = "testDoc",
					localId = 1,
					ok = true,
					revId = conflictingSecondRevision,
				},
			} )

		-- Make sure that the deletion remains as the winning revision.

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' )
			:map( function( docValue )
					return mapAwayRevHashCode( docValue )
				end ),
			{
				_id = 'testDoc',
				_empty = true,
				_deleted = true,
				_revId = '(valid hash)',
				_conflict = true,
			} )

		-- Now read back all three revisions.

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', firstRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = firstRevId,
				_conflict = true,
				_changeCounter = 1,
				test = 42,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', secondRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = secondRevId,
				_changeCounter = 2,
				_conflict = true,
				_deleted = true,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', conflictingSecondRevision ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = conflictingSecondRevision,
				_changeCounter = 3,
				_conflict = true,
				test = 59,
			} )

		-- Now check results of :currentRevisionsForDocument.

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'testDoc' ),

			{
				{
					_changeCounter = 3,
					_conflict = true,
					_id = "testDoc",
					_localId = 1,
					_revId = conflictingSecondRevision,
					test = 59,
				},
			} )

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'testDoc', true ), -- includeDeleted = true

			{
				{
					_changeCounter = 2,
					_conflict = true,
					_deleted = true,
					_id = "testDoc",
					_localId = 1,
					_revId = secondRevId,
				},
				{
					_changeCounter = 3,
					_conflict = true,
					_id = "testDoc",
					_localId = 1,
					_revId = conflictingSecondRevision,
					test = 59,
				},
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetDocValue_preserveEditInConflictWithDelete( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision of empty document.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		-- Replace that with second revision.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 56,
			} )

		-- Now create another revision that derives from the first revision
		-- and deletes the document. This should be treated as a conflict
		-- with the existing edit remaining as the winning revision.

		local conflictingSecondRevision = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					_revIds = { firstRevId, conflictingSecondRevision },
					_deleted = true,
				}
			},

			{
				{
					id = "testDoc",
					localId = 1,
					ok = true,
					revId = conflictingSecondRevision,
				},
			} )

		-- Make sure that the deletion remains as the winning revision.

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_revId = secondRevId,
				_changeCounter = 3,
				_localId = 1,
				_conflict = true,
				test = 56,
			} )

		-- Now read back all three revisions.

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', firstRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = firstRevId,
				_changeCounter = 1,
				_conflict = true,
				test = 42,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', secondRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = secondRevId,
				_changeCounter = 2,
				_conflict = true,
				test = 56,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', conflictingSecondRevision ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = conflictingSecondRevision,
				_changeCounter = 3,
				_conflict = true,
				_deleted = true,
			} )

		-- Now check results of :currentRevisionsForDocument.

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'testDoc' ),

			{
				{
					_changeCounter = 2,
					_conflict = true,
					_id = "testDoc",
					_localId = 1,
					_revId = secondRevId,
					test = 56,
				},
			} )

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'testDoc', true ), -- includeDeleted = true

			{
				{
					_changeCounter = 2,
					_conflict = true,
					_id = "testDoc",
					_localId = 1,
					_revId = secondRevId,
					test = 56,
				},
				{
					_id = 'testDoc',
					_localId = 1,
					_revId = conflictingSecondRevision,
					_changeCounter = 3,
					_conflict = true,
					_deleted = true,
				},
			} )
	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetDocValue_specifyingNonExistentRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision of empty document.

		setDocValue_firstRevTest42( rxTest, docStore )

		-- Now read back a different (bogus) revision.

		rxTest:expectOnlyError(

			Observable.pcall( function()

				return docStore:docValue( 'testDoc', '1-bogus' )

			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetDocValueAndOngoingDocValue( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValue = docStore:ongoingDocValue( 'testDoc' )
		local odv = rxTest:observe( docValue )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Create first revision of empty document.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = firstRevId,
			_changeCounter = 1,
			test = 42,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev1 )

		odv:expectData( expectedValueRev1 )

		-- Replace that with second revision and read back.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = secondRevId,
			_changeCounter = 2,
			test = 45,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev2 )

		odv:expectData( expectedValueRev2 )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetDocValue_preserveLocalOnly( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValue = docStore:ongoingDocValue( 'testDoc' )
		local odv = rxTest:observe( docValue )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Create first revision of empty document.

		local firstRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_localOnly = {
					blah = 'local stuff',
				},
				test = 42,
			} )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = firstRevId,
			_changeCounter = 1,
			_localOnly = {
				blah = 'local stuff',
			},
			test = 42,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev1 )

		odv:expectData( expectedValueRev1 )

		-- Replace that with second revision and read back.
		-- Since _localOnly was not provided here, it should not be replaced.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			_localOnly = {
				blah = 'local stuff',
			},
			_revId = secondRevId,
			_changeCounter = 2,
			test = 45,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev2 )

		odv:expectData( expectedValueRev2 )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetDocValue_localOnlyChangeDoesntBumpRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValue = docStore:ongoingDocValue( 'testDoc' )
		local odv = rxTest:observe( docValue )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Create first revision of empty document.

		local firstRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_localOnly = {
					blah = 'local stuff',
				},
				test = 42,
			} )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = firstRevId,
			_changeCounter = 1,
			_localOnly = {
				blah = 'local stuff',
			},
			test = 42,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev1 )

		odv:expectData( expectedValueRev1 )

		-- Replace that with second revision which only changes _localOnly values and read back.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				_localOnly = {
					blah = 'different local stuff',
				},
				test = 42,
			} )

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			_localOnly = {
				blah = 'different local stuff',
			},
			_revId = secondRevId,
			_changeCounter = 2,
			test = 42,
		}

		rxTest:expectEqual( firstRevId, secondRevId )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev2 )

		odv:expectData( expectedValueRev2 )

	end
	
	--------------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetDocValue_specifyRevId( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )
		
		local revId = KSIdGenerator.uuidInLowercaseHex()
		
		-- Create first revision of empty document.

		local firstRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_localOnly = {
					blah = 'local stuff',
				},
				_newRevId = revId,
				test = 42,
			} )
			
		rxTest:expectEqual( revId, firstRevId )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = firstRevId,
			_changeCounter = 1,
			_localOnly = {
				blah = 'local stuff',
			},
			test = 42,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev1 )
		
	end

	--------------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetDocValue_specifySpaceId( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )
		
		local spaceId = KSIdGenerator.uuidInLowercaseHex()
		
		local setSpaceId = setDocValueAndValidateSpaceId( rxTest, docStore,
			{
				_id = 'testDoc',
				_localOnly = {
					blah = 'local stuff',
				},
				_spaceId = spaceId,
				test = 42,
			} )
			
		rxTest:expectEqual( spaceId, setSpaceId )
		
	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValueAndOngoingDocValue_revIdsArray( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValue = docStore:ongoingDocValue( 'testDoc' )
		local odv = rxTest:observe( docValue )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Create first revision of empty document.

		local firstRevId = '33ec614e3b594c918ffb53768be037e9'

		rxTest:expectSingleValue(

			docStore:setDocValue {
				_id = 'testDoc',
				_revIds = { firstRevId },
				test = 42,
			},

			firstRevId )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = firstRevId,
			_changeCounter = 1,
			test = 42,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev1 )

		odv:expectData( expectedValueRev1 )

		-- Replace that with second revision and read back.

		local secondRevId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectEqual( secondRevId,
			setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revIds = { firstRevId, secondRevId },
				test = 45,
			} ) )

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = secondRevId,
			_changeCounter = 2,
			test = 45,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev2 )

		odv:expectData( expectedValueRev2 )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValueAndOngoingDocValue_preserveLocalOnly( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValue = docStore:ongoingDocValue( 'testDoc' )
		local odv = rxTest:observe( docValue )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Create first revision of empty document.

		local firstRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_localOnly = {
					blah = 'local stuff',
				},
				test = 42,
			} )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_localOnly = {
				blah = 'local stuff',
			},
			_revId = firstRevId,
			_changeCounter = 1,
			test = 42,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev1 )

		odv:expectData( expectedValueRev1 )

		-- Replace that with second revision and read back.
		-- Since no _localOnly value was provided here, the previous one
		-- should be left in place.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			_localOnly = {
				blah = 'local stuff',
			},
			_revId = secondRevId,
			_changeCounter = 2,
			test = 45,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev2 )

		odv:expectData( expectedValueRev2 )

		-- Replace that with a third revision and read back.
		-- Since a _localOnly value was provided here, the previous one
		-- should be replaced in full.

		local thirdRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = secondRevId,
				_localOnly = {
					something = 'new',
				},
				test = 52,
			} )

		local expectedValueRev3 = {
			_id = 'testDoc',
			_localId = 1,
			_localOnly = {
				something = 'new',
			},
			_revId = thirdRevId,
			_changeCounter = 3,
			test = 52,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev3 )

		odv:expectData( expectedValueRev3 )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValueAndOngoingDocValue_rollback( rxTest )

		-- TODO [scouten 2015-05-13]: Right now I think there's a bug that would
		-- cause :ongoingDocValue to incorrectly report a new doc value if a subsequent
		-- change in the same transaction would be rolled back.

		-- Something like:

			-- Observe doc 'test_1'.

			-- Set values for 'test_1' and 'test_2'.

			-- Verify that notification goes out for new value on 'test_1'.

			-- Bulk set values for 'test_1' and 'test_2'.
			-- The value for 'test_2' should trigger a validation failure.

			-- Verify that no new notification goes out for 'test_1'.
			-- (Right now I think this would fail.)

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValueAndOngoingDocValue_createConflict( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValue = docStore:ongoingDocValue( 'testDoc' )
		local odv = rxTest:observe( docValue )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Create first revision of empty document.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = firstRevId,
			_changeCounter = 1,
			test = 42,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev1 )

		odv:expectData( expectedValueRev1 )

		-- Replace that with second revision and read back.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = secondRevId,
			_changeCounter = 2,
			test = 45,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev2 )

		odv:expectData( expectedValueRev2 )

		-- Create a conflicting second revision.
		-- Make sure docValue and ongoingDocValue both get _conflict marker.

		local conflictingRevId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					_revIds = { firstRevId, conflictingRevId },
					test = 49,
				}
			},

			{
				{
					id = "testDoc",
					localId = 1,
					ok = true,
					revId = conflictingRevId,
				},
			} )

		local expectedValueAfterConflict

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' )
				:map( function( docValue )
					expectedValueAfterConflict = docValue
					return docValue._conflict, docValue
				end ),

			true, ':ignore:' )

		odv:expectData( expectedValueAfterConflict )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc', conflictingRevId ),

			{
				_id = 'testDoc',
				_localId = 1,
				_changeCounter = 3,
				_conflict = true,
				_revId = conflictingRevId,
				test = 49,
			} )

		-- Now check results of :currentRevisionsForDocument.

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'testDoc' ),

			{
				{
					_changeCounter = 2,
					_conflict = true,
					_id = "testDoc",
					_localId = 1,
					_revId = secondRevId,
					test = 45,
				},
				{
					_changeCounter = 3,
					_conflict = true,
					_id = "testDoc",
					_localId = 1,
					_revId = conflictingRevId,
					test = 49,
				},
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValueAndOngoingDocValue_createConflict_force( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValue = docStore:ongoingDocValue( 'testDoc' )
		local odv = rxTest:observe( docValue )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Create first revision of empty document.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = firstRevId,
			_changeCounter = 1,
			test = 42,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev1 )

		odv:expectData( expectedValueRev1 )

		-- Replace that with second revision and read back.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = secondRevId,
			_changeCounter = 2,
			test = 45,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev2 )

		odv:expectData( expectedValueRev2 )

		-- Create a conflicting second revision.
		-- Make sure docValue and ongoingDocValue both get _conflict marker.
		-- Typically a conflicting rev will lose, but with force = true, it wins.

		local conflictingRevId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					_revIds = { firstRevId, conflictingRevId, force = true },
					test = 49,
				}
			},

			{
				{
					id = "testDoc",
					localId = 1,
					ok = true,
					revId = conflictingRevId,
				},
			} )

		local expectedValueAfterConflict

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' )
				:map( function( docValue )
					expectedValueAfterConflict = docValue
					return docValue._conflict, docValue
				end ),

			true,
			{
				_id = 'testDoc',
				_localId = 1,
				_changeCounter = 3,
				_conflict = true,
				_revId = conflictingRevId,
				test = 49,
			} )

		odv:expectData( expectedValueAfterConflict )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc', secondRevId ),

			{
				_id = 'testDoc',
				_localId = 1,
				_conflict = true,
				_revId = secondRevId,
				_changeCounter = 2,
				test = 45,
			} )

		-- Now check results of :currentRevisionsForDocument.

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'testDoc' ),

			{
				{
					_changeCounter = 3,
					_conflict = true,
					_id = "testDoc",
					_localId = 1,
					_revId = conflictingRevId,
					test = 49,
				},
				{
					_changeCounter = 2,
					_conflict = true,
					_id = "testDoc",
					_localId = 1,
					_revId = secondRevId,
					test = 45,
				},
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValueAndOngoingDocValue_createDeletionConflict_force( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValue = docStore:ongoingDocValue( 'testDoc' )
		local odv = rxTest:observe( docValue )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Create first revision of empty document.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = firstRevId,
			_changeCounter = 1,
			test = 42,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev1 )

		odv:expectData( expectedValueRev1 )

		-- Replace that with second revision and read back.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = secondRevId,
			_changeCounter = 2,
			test = 45,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev2 )

		odv:expectData( expectedValueRev2 )

		-- Create a conflicting first revision, which is a deletion.
		-- Make sure docValue and ongoingDocValue both get _conflict marker.
		-- Typically a conflicting rev will lose, but with force = true, it wins.
		-- It should also delete any existing revision.

		local conflictingRevId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					_revIds = { conflictingRevId, force = true },
					_deleted = true,
				}
			},

			{
				{
					id = "testDoc",
					localId = 1,
					ok = true,
					revId = conflictingRevId,
				},
			} )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' )
				:map( function( docValue )
					return mapAwayRevHashCode( docValue )
				end ),

			{
				_id = 'testDoc',
				_empty = true,
				_deleted = true,
				_revId = '(valid hash)'
			} )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc', secondRevId ),

			{
				_id = 'testDoc',
				_localId = 1,
				_revId = secondRevId,
				_changeCounter = 2,
				test = 45,
			} )

		-- Now check results of :currentRevisionsForDocument.

		rxTest:expectSingleValue( docStore:currentRevisionsForDoc( 'testDoc' ), {} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValueAndOngoingDocValue_createConflictViaRevIds( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValue = docStore:ongoingDocValue( 'testDoc' )
		local odv = rxTest:observe( docValue )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Create first revision of empty document.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = firstRevId,
			_changeCounter = 1,
			test = 42,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev1 )

		odv:expectData( expectedValueRev1 )

		-- Replace that with second revision and read back.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = secondRevId,
			_changeCounter = 2,
			test = 45,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev2 )

		odv:expectData( expectedValueRev2 )

		-- Create a conflicting second revision.
		-- Make sure docValue and ongoingDocValue both get _conflict marker.

		local conflictingRevId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					_revIds = { firstRevId, conflictingRevId },
					test = 49,
				}
			},

			{
				{
					id = "testDoc",
					localId = 1,
					ok = true,
					revId = conflictingRevId,
				},
			} )

		-- Expected value should update to reflect conflict, but the locally-originated
		-- version should win.

		expectedValueRev2._conflict = true
		expectedValueRev2._changeCounter = 3

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev2 )

		odv:expectData( expectedValueRev2 )

		-- Now check results of :currentRevisionsForDocument.

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'testDoc' ),

			{
				{
					_changeCounter = 2,
					_conflict = true,
					_id = "testDoc",
					_localId = 1,
					_revId = secondRevId,
					test = 45,
				},
				{
					_changeCounter = 3,
					_conflict = true,
					_id = "testDoc",
					_localId = 1,
					_revId = conflictingRevId,
					test = 49,
				},
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValueAndOngoingDocValue_noConflictWhenChangingLocalOnly( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValue = docStore:ongoingDocValue( 'testDoc' )
		local odv = rxTest:observe( docValue )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Create first revision of empty document.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = firstRevId,
			_changeCounter = 1,
			test = 42,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev1 )

		odv:expectData( expectedValueRev1 )

		-- Replace that with second revision and read back.
		-- In this case, the only change we make is to _localOnly values.
		-- Because of that, the revision ID should be the same as previous.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				_localOnly = {
					blah = 45,
				},
				test = 42, -- same as before
			} )

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			_localOnly = {
				blah = 45,
			},
			_revId = secondRevId,
			_changeCounter = 2,
			test = 42,
		}

		rxTest:expectEqual( firstRevId, secondRevId )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev2 )

		odv:expectData( expectedValueRev2 )

		-- Create a different second revision.
		-- There should be no _conflict marker, because the _localOnly change
		-- didn't cause the revision ID to change.

		local nonConflictingRevId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					_revIds = { firstRevId, nonConflictingRevId },
					test = 49,
				}
			},

			{
				{
					id = "testDoc",
					localId = 1,
					ok = true,
					revId = nonConflictingRevId,
				},
			} )

		-- Expected value should update to reflect the merger of _localOnly changes
		-- and second revision from external source.share

		local expectedValueRev3 = {
			_id = 'testDoc',
			_localId = 1,
			_localOnly = {
				blah = 45,
			},
			_revId = nonConflictingRevId,
			_changeCounter = 3,
			test = 49,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev3 )

		odv:expectData( expectedValueRev3 )

		-- Now check results of :currentRevisionsForDocument.

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'testDoc' ),
			{ expectedValueRev3 } )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValueAndOngoingDocValue_createConflictWithNoCommonHistory( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValue = docStore:ongoingDocValue( 'testDoc' )
		local odv = rxTest:observe( docValue )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Create first revision of empty document.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = firstRevId,
			_changeCounter = 1,
			test = 42,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev1 )

		odv:expectData( expectedValueRev1 )

		-- Replace that with second revision and read back.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = secondRevId,
			_changeCounter = 2,
			test = 45,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev2 )

		odv:expectData( expectedValueRev2 )

		-- Create a conflicting second revision.
		-- Make sure docValue and ongoingDocValue both get _conflict marker.

		local conflictingRevId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					_revIds = { conflictingRevId },
						-- This test differs from above only in that it doesn't
						-- include firstRevId here.
					test = 49,
				}
			},

			{
				{
					id = "testDoc",
					localId = 1,
					ok = true,
					revId = conflictingRevId,
				},
			} )

		-- Expected value should update to reflect conflict, but the locally-originated
		-- version should win.

		expectedValueRev2._conflict = true
		expectedValueRev2._changeCounter = 3

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev2 )

		odv:expectData( expectedValueRev2 )

		-- Now check results of :currentRevisionsForDocument.

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'testDoc' ),

			{
				{
					_changeCounter = 2,
					_conflict = true,
					_id = "testDoc",
					_localId = 1,
					_revId = secondRevId,
					test = 45,
				},
				{
					_changeCounter = 3,
					_conflict = true,
					_id = "testDoc",
					_localId = 1,
					_revId = conflictingRevId,
					test = 49,
				},
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValueAndOngoingDocValue_updateExistingRevViaRevIds( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValue = docStore:ongoingDocValue( 'testDoc' )
		local odv = rxTest:observe( docValue )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Create first revision of empty document.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = firstRevId,
			_changeCounter = 1,
			test = 42,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev1 )

		odv:expectData( expectedValueRev1 )

		-- Replace that with second revision and read back.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = secondRevId,
			_changeCounter = 2,
			test = 45,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev2 )

		odv:expectData( expectedValueRev2 )

		-- Create a modified second revision with same rev ID. (Oz likes to do that
		-- now and then, ya know.) Should register as an update to existing rev without
		-- any actual change.

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					_revIds = { firstRevId, secondRevId },
					test = 49,
				}
			},

			':ignore:' )

		-- Expected value should update to reflect new value, but should not be shown
		-- as a conflict. It should also maintain the existing rev ID.

		local expectedValueRev2bis = {
			_id = 'testDoc',
			_revId = secondRevId,
			_localId = 1,
			_changeCounter = 3,
			test = 49,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev2bis )

		odv:expectData( expectedValueRev2bis )

		-- If we subsequently derive from this revision, it should appear as a successor
		-- to this revision and not the previous one (i.e. not a conflict).

		local thirdRevId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					_revIds = { firstRevId, secondRevId, thirdRevId },
					test = 52,
				}
			},

			':ignore:' )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_revId = thirdRevId,
				_localId = 1,
				-- conflict = nil, -- should not be a conflict
				_changeCounter = 4,
				test = 52,
			} )

		-- Now check results of :currentRevisionsForDocument.

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'testDoc' ),

			{
				{
					_changeCounter = 4,
					_id = "testDoc",
					_localId = 1,
					_revId = thirdRevId,
					test = 52,
				},
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetDocValue_runUntilDone( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local myDocValue = {
			_id = 'testDoc',
			run = 'untilDone, yeah?',
		}

		local docId = myDocValue._id

		local expectedValue = KSTableUtils.deepCopy( myDocValue )
		expectedValue._changeCounter = 1
		expectedValue._localId = 1

		docStore:setDocValue( myDocValue )
			-- Intentionally not observing response.

		rxTest:expectSingleValue(

			docStore:docValue( docId )
				:map( function( docValue )
					docValue._revId = nil
					return docValue
				end ),

			expectedValue )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetDoc_localId( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore, {
			_id = 1,
			_revId = firstRevId,
			gone = 'girl',
		} )

		local expectedValue = {
			_id = 'testDoc',
			_localId = 1,
			_changeCounter = 2,
			_revId = secondRevId,
			gone = 'girl',
		}

		rxTest:expectSingleValue(

			docStore:docValue( 1 ),

			expectedValue )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetDoc_localId_disallowNewDoc( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local myDocValue = {
			_id = 1,
			gone = 'girl',
		}

		rxTest:expectOnlyError(

			Observable.pcall( function()

				return docStore:setDocValue( myDocValue )
				-- not allowed to create a new document using integer local id
				-- (setDocValue can only be used to replace an existing document.)

			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValue_runUntilDone( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local myDocValue = {
			_id = 'testDoc',
			run = 'untilDone, yeah?',
		}

		local docId = myDocValue._id

		local expectedValue = KSTableUtils.deepCopy( myDocValue )
		expectedValue._changeCounter = 1
		expectedValue._revId = "(valid hash)"

		docStore:bulkSetDocValue { myDocValue }
			-- Intentionally not observing response.

		rxTest:expectSingleValue(

			docStore:docValue( docId )
				:map( function( docValue )
					return mapAwayRevHashCode( docValue ), docValue
				end ),

			expectedValue, ':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestDeletingAndReplacingRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValue = docStore:ongoingDocValue( 'testDoc' )
		local odv = rxTest:observe( docValue )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Create first revision of empty document.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = firstRevId,
			_changeCounter = 1,
			test = 42,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev1 )

		odv:expectData( expectedValueRev1 )

		-- Replace that with second (deleted) revision and read back.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				_deleted = true,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' )
			:map( function( docValue )
					return mapAwayRevHashCode( docValue )
				end ),
			{
				_id = 'testDoc',
				_empty = true,
				_deleted = true,
				_revId = '(valid hash)',
			} )

		odv:expectData( {
			_id = 'testDoc',
			_empty = true,
		} )

		-- Replace that with third (undeleted) revision. Read back again.

		local thirdRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = secondRevId,
				test = 45,
			} )

		local expectedValueRev3 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = thirdRevId,
			_changeCounter = 3,
			test = 45,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev3 )

		odv:expectData( expectedValueRev3 )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValue_deletingAndReplacingRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValue = docStore:ongoingDocValue( 'testDoc' )
		local odv = rxTest:observe( docValue )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Create first revision of empty document.

		local expectedBulkResult1 = {
			{
				id = 'testDoc',
				localId = 1,
				ok = true,
				-- revId = (inserted later),
			}
		}

		local firstRevId

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					test = 42,
				}
			}
			:tap( function( results )
				firstRevId = results[ 1 ].revId
				expectedBulkResult1[ 1 ].revId = firstRevId
			end ),

			expectedBulkResult1 )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = firstRevId,
			_changeCounter = 1,
			test = 42,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev1 )

		odv:expectData( expectedValueRev1 )

		-- Replace that with second (deleted) revision and read back.

		local expectedBulkResult2 = {
			{
				id = 'testDoc',
				localId = 1,
				ok = true,
				-- revId = (inserted later),
			}
		}

		local secondRevId -- filled in later

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					_revId = firstRevId,
					_deleted = true,
				}
			}
			:tap( function( results )
				secondRevId = results[ 1 ].revId
				expectedBulkResult2[ 1 ].revId = secondRevId
			end ),

			expectedBulkResult2 )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' )
				:map( function( docValue )
					return mapAwayRevHashCode( docValue )
				end ),
			{
				_id = 'testDoc',
				_empty = true,
				_deleted = true,
				_revId = '(valid hash)',
			} )

		odv:expectData( {
			_id = 'testDoc',
			_empty = true,
		} )

		-- Replace that with third (undeleted) revision. Read back again.

		local expectedBulkResult3 = {
			{
				id = 'testDoc',
				localId = 1,
				ok = true,
				-- revId = (inserted later),
			}
		}

		local thirdRevId

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					_revId = secondRevId,
					test = 45,
				}
			}
			:tap( function( results )
				thirdRevId = results[ 1 ].revId
				expectedBulkResult3[ 1 ].revId = thirdRevId
			end ),

			expectedBulkResult3 )

		local expectedValueRev3 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = thirdRevId,
			_changeCounter = 3,
			test = 45,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev3 )

		odv:expectData( expectedValueRev3 )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestDeletingRevAndStartingOver( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValue = docStore:ongoingDocValue( 'testDoc' )
		local odv = rxTest:observe( docValue )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Create first revision of empty document.

		local firstRevId

		local expectedBulkResult1 = {
			{
				id = 'testDoc',
				localId = 1,
				ok = true,
				-- revId = (inserted later),
			}
		}

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					test = 42,
				}
			}
			:tap( function( results )
				firstRevId = results[ 1 ].revId
				expectedBulkResult1[ 1 ].revId = firstRevId
			end ),

			expectedBulkResult1 )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = firstRevId,
			_changeCounter = 1,
			test = 42,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev1 )

		odv:expectData( expectedValueRev1 )

		-- Replace that with second (deleted) revision and read back.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				_deleted = true,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), {
			_id = 'testDoc',
			_empty = true,
			_deleted = true,
			_revId = secondRevId,
		} )

		odv:expectData( {
			_id = 'testDoc',
			_empty = true,
		} )

		-- Create a new revision without specifying deleted rev as parent.
		-- Read back again.

		local thirdRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = secondRevId,
				test = 45,
			} )

		local expectedValueRev3 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = thirdRevId,
			_changeCounter = 3,
			test = 45,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev3 )

		odv:expectData( expectedValueRev3 )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValue_deletingRevAndStartingOver( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValue = docStore:ongoingDocValue( 'testDoc' )
		local odv = rxTest:observe( docValue )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Create first revision of empty document.

		local firstRevId

		local expectedBulkResult1 = {
			{
				id = 'testDoc',
				localId = 1,
				ok = true,
				-- revId = (inserted later),
			}
		}

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					test = 42,
				}
			}
			:tap( function( results )
				firstRevId = results[ 1 ].revId
				expectedBulkResult1[ 1 ].revId = firstRevId
			end ),

			expectedBulkResult1 )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = firstRevId,
			_changeCounter = 1,
			test = 42,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev1 )

		odv:expectData( expectedValueRev1 )

		-- Replace that with second (deleted) revision and read back.

		local expectedBulkResult2 = {
			{
				id = 'testDoc',
				localId = 1,
				ok = true,
				-- revId = (inserted later),
			}
		}

		local secondRevId

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					_revId = firstRevId,
					_deleted = true,
				}
			}
			:tap( function( results )
				secondRevId = results[ 1 ].revId
				expectedBulkResult2[ 1 ].revId = secondRevId
			end ),

			expectedBulkResult2 )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_empty = true,
				_deleted = true,
				_revId = secondRevId,
			} )

		odv:expectData( {
			_id = 'testDoc',
			_empty = true,
		} )

		-- Create a new revision without specifying deleted rev as parent.
		-- Read back again.

		local expectedBulkResult3 = {
			{
				id = 'testDoc',
				localId = 1,
				ok = true,
				-- revId = (inserted later),
			}
		}

		local thirdRevId

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					test = 45,
				}
			}
			:tap( function( results )
				thirdRevId = results[ 1 ].revId
				expectedBulkResult3[ 1 ].revId = thirdRevId
			end ),

			expectedBulkResult3 )

		local expectedValueRev3 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = thirdRevId,
			_changeCounter = 3,
			test = 45,
		}

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), expectedValueRev3 )

		odv:expectData( expectedValueRev3 )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetSetDocValue_errorReplacingWrongRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		setDocValue_firstRevTest42( rxTest, docStore )

		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:setDocValue {
					_id = 'testDoc',
					_revId = '1-bogusRevId',
					test = 45,
				}
			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValue_errorReplacingWrongRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		setDocValue_firstRevTest42( rxTest, docStore )

		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:bulkSetDocValue {
					{
						_id = 'testDoc',
						_revId = '1-bogusRevId',
						test = 45,
					}
				}
			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetSetDocValue_errorReplacingNonCurrentRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		rxTest:expectSingleValue(

			docStore:setDocValue {
				_id = 'testDoc',
				_localId = 1,
				_revId = firstRevId,
				test = 45,
			},

			':ignore:' )

		-- Now try to derive (again) from first rev. (Shouldn't be allowed.)

		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:setDocValue {
					_id = 'testDoc',
					_revId = firstRevId,
					test = 92,
				}
			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValue_errorReplacingNonCurrentRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )

		-- Now try to derive from first rev. (Shouldn't be allowed.)

		rxTest:expectOnlyError(

			Observable.pcall( function()

				return docStore:bulkSetDocValue {
					{
						_id = 'testDoc',
						_revId = firstRevId,
						test = 92,
					}
				}

			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetSetDocValue_errorReplacingWrongDeletedRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				_deleted = true,
			} )

		rxTest:expectOnlyError(

			Observable.pcall( function()

				return docStore:setDocValue {
					_id = 'testDoc',
					_revId = '1-bogus',
					test = 42,
				}

			end ),

			':ignore:' )

	end
	
	--------------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetDocValue_errorSpecifyingNewRevId( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				_deleted = true,
			} )

		rxTest:expectOnlyError(

			Observable.pcall( function()

				return docStore:setDocValue {
					_id = 'testDoc',
					_revId = firstRevId,
					_newRevId = 42,
					test = 42,
				}

			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValue_errorReplacingWrongDeletedRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		rxTest:expectOnlyError(

			Observable.pcall( function()

				return docStore:bulkSetDocValue {
					{
						_id = 'testDoc',
						_revId = '1-bogus',
						test = 42,
					}
				}

			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValue_errorDerivingFromNonExistentRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		rxTest:expectOnlyError(

			Observable.pcall( function()

				return docStore:bulkSetDocValue {
					{
						_id = 'testDoc',
						_revId = '1-noSuchRev',
						test = 42,
					}
				}

			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetSetDocValue_errorDerivingFromNonExistentRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		rxTest:expectOnlyError(

			Observable.pcall( function()

				return docStore:setDocValue {
					_id = 'testDoc',
					_revId = '1-noSuchRev',
					test = 42,
				}

			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValue_errorDerivingFromNonExistentRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		rxTest:expectOnlyError(

			Observable.pcall( function()

				return docStore:bulkSetDocValue {
					{
						_id = 'testDoc',
						_revId = '1-noSuchRev',
						test = 42,
					}
				}

			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetDocValue_errorConflict( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		rxTest:expectSingleValue(

			docStore:setDocValue {
				_id = '6e1295ed6c29495e54cc05947f18c8af',
				title = "There is Nothing Left to Lose",
				artist = "Foo Fighters",
			},

			':ignore:' )

		rxTest:expectOnlyError(

			Observable.pcall( function()

				return docStore:setDocValue {
					_id = '6e1295ed6c29495e54cc05947f18c8af',
					title = "There is Nothing Left to Lose",
					artist = "Foo Fighters",
					year = "1997",
				}

			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValue_errorConflict( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		rxTest:expectSingleValue(

			docStore:setDocValue {
				_id = '6e1295ed6c29495e54cc05947f18c8af',
				title = "There is Nothing Left to Lose",
				artist = "Foo Fighters",
			},

			':ignore:' )

		rxTest:expectOnlyError(

			Observable.pcall( function()

				return docStore:bulkSetDocValue {
					{
						_id = '6e1295ed6c29495e54cc05947f18c8af',
						title = "There is Nothing Left to Lose",
						artist = "Foo Fighters",
						year = "1997",
					}
				}

			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValue_accepts200Docs( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValues = {}
		for i = 1, 200 do
			docValues[ i ] = {
				_id = 'test_' .. tostring( i ),
				foo = 'bar',
				counter = i,
			}
		end

		local expectedResult = {}

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue( docValues )
				:map( function( results )

					for i = 1, 200 do
						expectedResult[ i ] = {
							id = 'test_' .. tostring( i ),
							localId = i,
							ok = true,
						}
					end

					local sanitizedResult = {}

					for i = 1, 200 do
						sanitizedResult[ i ] = results[ i ]
						sanitizedResult[ i ].revId = nil
					end

					return sanitizedResult, results

				end ),

			expectedResult, ':ignore:' )

		rxTest:expectSingleValue(

			docStore:docValue( 'test_1' )
				:map( function( value )
					return mapAwayRevHashCode( value ), value
				end ),

			{
				_id = 'test_1',
				_changeCounter = 1,
				_revId = "(valid hash)",
				foo = 'bar',
				counter = 1,
			},
			':ignore:' )

		rxTest:expectSingleValue(

			docStore:docValue( 'test_100' )
				:map( function( value )
					return mapAwayRevHashCode( value ), value
				end ),

			{
				_id = 'test_100',
				_changeCounter = 100,
				_revId = "(valid hash)",
				foo = 'bar',
				counter = 100,
			},
			':ignore:' )

		rxTest:expectSingleValue(

			docStore:docValue( 'test_200' )
				:map( function( value )
					return mapAwayRevHashCode( value ), value
				end ),

			{
				_id = 'test_200',
				_changeCounter = 200,
				_revId = "(valid hash)",
				foo = 'bar',
				counter = 200,
			},
			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkDocValue( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValues = {}
		for i = 1, 100 do
			docValues[ i ] = {
				_id = 'test_' .. tostring( i ),
				foo = 'bar',
				counter = i,
			}
		end

		rxTest:expectSingleValue(
			docStore:bulkSetDocValue( docValues ),
			':ignore:' )

		rxTest:expectSingleValue(
			docStore:deleteDoc( 'test_49' ),
			':ignore:' )

		rxTest:expectSingleValue(

			docStore:bulkDocValue { 'test_1', 'test_15', 'test_48', 'test_49', 'test_99', 'test_100' }
				:map( function( result )
					for i = 1, #result do
						result[ i ] = mapAwayRevHashCode( result[ i ] )
					end
					return result
				end ),

			{
				{
					_id = 'test_1',
					_changeCounter = 1,
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 1,
				},
				{
					_id = 'test_15',
					_changeCounter = 15,
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 15,
				},
				{
					_id = 'test_48',
					_changeCounter = 48,
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 48,
				},
				{
					_id = 'test_49',
					_changeCounter = 101,
					_revId = "(valid hash)",
					_deleted = true,
					_empty = true,
				},
				{
					_id = 'test_99',
					_changeCounter = 99,
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 99,
				},
				{
					_id = 'test_100',
					_changeCounter = 100,
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 100,
				},
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkDocValue_fieldSpec( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValues = {}
		for i = 1, 5 do
			docValues[ i ] = {
				_id = 'test_' .. tostring( i ),
				foo = 'bar',
				cstr = tostring(i),
				counter = i,
				sub = {
					subf = string.format("subf-%d", i),
					sub2 = {
						foo = "bar",
						ssc = i
					}
				}
			}
		end

		rxTest:expectSingleValue(
			docStore:bulkSetDocValue( docValues ),
			':ignore:' )

		rxTest:expectSingleValue(
			docStore:bulkDocValue( { 'test_1', 'test_3', 'test_4' }, {
				_id = true,
				cstr = true
			} ),
			{
				{
					_id = 'test_1',
					cstr = "1"
				},
				{
					_id = 'test_3',
					cstr = "3"
				},
				{
					_id = 'test_4',
					cstr = "4"
				},
			} )
			
		rxTest:expectSingleValue(
			docStore:bulkDocValue( { 'test_1', 'test_2', 'test_5' }, {
				_id = true,
				sub = {
					sub2 = true
				}
			} ),
			{
				{
					_id = 'test_1',
					sub = {
						sub2 = {
							foo = "bar",
							ssc = 1
						}
					}
				},
				{
					_id = 'test_2',
					sub = {
						sub2 = {
							foo = "bar",
							ssc = 2
						}
					}
				},
				{
					_id = 'test_5',
					sub = {
						sub2 = {
							foo = "bar",
							ssc = 5
						}
					}
				},
			} )
		rxTest:expectSingleValue(
			docStore:bulkDocValue( { 'test_3', 'test_5', 'test_2' }, {
				_id = true,
				sub = {
					subf = true,
					sub2 = {
						ssc = true
					}
				}
			} ),
			{
				{
					_id = 'test_3',
					sub = {
						subf = "subf-3",
						sub2 = {
							ssc = 3
						}
					}
				},
				{
					_id = 'test_5',
					sub = {
						subf = "subf-5",
						sub2 = {
							ssc = 5
						}
					}
				},
				{
					_id = 'test_2',
					sub = {
						subf = "subf-2",
						sub2 = {
							ssc = 2
						}
					}
				},
			} )
			

		-- using a subtable spec will result in no value for non-table fields
		-- using a value other than true also results in no value
		--
		-- this could be more strict in some cases, but the doc store generally
		-- allows for flexible data, so for now just filter out fields that fail
		-- to match up structurally
		--
		-- this is intended for very simple specification of field subsets
		rxTest:expectSingleValue(
			docStore:bulkDocValue( { 'test_4', 'test_1' }, {
				_id = true,
				foo = "bar",
				sub = {
					subf = { -- table does not match a scalar value
						never = true
					}
				}
			} ),
			{
				{
					_id = 'test_4',
					sub = {
					}
				},
				{
					_id = 'test_1',
					sub = {
					}
				},
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkDocValue_label( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValues = { label = 'original' }
		for i = 1, 100 do
			docValues[ i ] = {
				_id = 'test_' .. tostring( i ),
				foo = 'bar',
				counter = i,
			}
		end

		rxTest:expectSingleValue(
			docStore:bulkSetDocValue( docValues ),
			':ignore:' )

		rxTest:expectSingleValue(

			docStore:docValue( 'test_15' )
				:flatMapLatest( function( docValue )
					return docStore:setDocValue {
						_id = 'test_15',
						_revId = docValue._revId,
						_labels = { test_label = true },
						foo = "This rev got labeled",
					}
				end ),

			':ignore:' )

		rxTest:expectSingleValue(

			docStore:bulkDocValue { label = 'original', 'test_1', 'test_15', 'test_48', 'test_99', 'test_100' }
				:map( function( result )
					for i = 1, #result do
						result[ i ] = mapAwayRevHashCode( result[ i ] )
					end
					return result
				end ),

			{
				{
					_id = 'test_1',
					_changeCounter = 1,
					_labels = {
						original = true,
					},
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 1,
				},
				{
					_id = 'test_15',
					_changeCounter = 15,
					_labels = {
						original = true,
					},
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 15,
				},
				{
					_id = 'test_48',
					_changeCounter = 48,
					_labels = {
						original = true,
					},
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 48,
				},
				{
					_id = 'test_99',
					_changeCounter = 99,
					_labels = {
						original = true,
					},
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 99,
				},
				{
					_id = 'test_100',
					_changeCounter = 100,
					_labels = {
						original = true,
					},
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 100,
				},
			} )

		rxTest:expectSingleValue(

			docStore:bulkDocValue { label = 'test_label', 'test_1', 'test_15', 'test_48', 'test_99', 'test_100' }
				:map( function( result )
					for i = 1, #result do
						result[ i ] = mapAwayRevHashCode( result[ i ] )
					end
					return result
				end ),

			{
				{
					_id = 'test_15',
					_changeCounter = 101,
					_revId = "(valid hash)",
					_labels = {
						test_label = true,
					},
					foo = "This rev got labeled",
				},
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkDocValue_numericId( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValues = {}
		for i = 1, 100 do
			docValues[ i ] = {
				_id = 'test_' .. tostring( i ),
				foo = 'bar',
				counter = i,
			}
		end

		rxTest:expectSingleValue(
			docStore:bulkSetDocValue( docValues ),
			':ignore:' )

		rxTest:expectSingleValue(
			docStore:deleteDoc( 'test_49' ),
			':ignore:' )

		rxTest:expectSingleValue(

			docStore:bulkDocValue { 1, 15, 48, 49, 99, 100 }
				:map( function( result )
					for i = 1, #result do
						result[ i ] = mapAwayRevHashCode( result[ i ] )
					end
					return result
				end ),

			{
				{
					_id = 'test_1',
					_changeCounter = 1,
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 1,
				},
				{
					_id = 'test_15',
					_changeCounter = 15,
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 15,
				},
				{
					_id = 'test_48',
					_changeCounter = 48,
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 48,
				},
				{
					_id = 'test_49',
					_changeCounter = 101,
					_revId = "(valid hash)",
					_deleted = true,
					_empty = true,
				},
				{
					_id = 'test_99',
					_changeCounter = 99,
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 99,
				},
				{
					_id = 'test_100',
					_changeCounter = 100,
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 100,
				},
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkDocValue_numericIdAndLabel( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValues = { label = 'original' }
		for i = 1, 100 do
			docValues[ i ] = {
				_id = 'test_' .. tostring( i ),
				foo = 'bar',
				counter = i,
			}
		end

		rxTest:expectSingleValue(
			docStore:bulkSetDocValue( docValues ),
			':ignore:' )

		rxTest:expectSingleValue(

			docStore:docValue( 'test_15' )
				:flatMapLatest( function( docValue )
					return docStore:setDocValue {
						_id = 'test_15',
						_revId = docValue._revId,
						_labels = { test_label = true },
						foo = "This rev got labeled",
					}
				end ),

			':ignore:' )

		rxTest:expectSingleValue(

			docStore:bulkDocValue { label = 'original', 1, 15, 48, 99, 100 }
				:map( function( result )
					for i = 1, #result do
						result[ i ] = mapAwayRevHashCode( result[ i ] )
					end
					return result
				end ),

			{
				{
					_id = 'test_1',
					_changeCounter = 1,
					_labels = {
						original = true,
					},
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 1,
				},
				{
					_id = 'test_15',
					_changeCounter = 15,
					_labels = {
						original = true,
					},
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 15,
				},
				{
					_id = 'test_48',
					_changeCounter = 48,
					_labels = {
						original = true,
					},
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 48,
				},
				{
					_id = 'test_99',
					_changeCounter = 99,
					_labels = {
						original = true,
					},
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 99,
				},
				{
					_id = 'test_100',
					_changeCounter = 100,
					_labels = {
						original = true,
					},
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 100,
				},
			} )

		rxTest:expectSingleValue(

			docStore:bulkDocValue { label = 'test_label', 1, 15, 48, 99, 100 }
				:map( function( result )
					for i = 1, #result do
						result[ i ] = mapAwayRevHashCode( result[ i ] )
					end
					return result
				end ),

			{
				{
					_id = 'test_15',
					_changeCounter = 101,
					_revId = "(valid hash)",
					_labels = {
						test_label = true,
					},
					foo = "This rev got labeled",
				},
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkDocValue_includeInitialChangeCounter( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValues = {}
		for i = 1, 100 do
			docValues[ i ] = {
				_id = 'test_' .. tostring( i ),
				foo = 'bar',
				counter = i,
			}
		end

		rxTest:expectSingleValue(
			docStore:bulkSetDocValue( docValues ),
			':ignore:' )

		rxTest:expectSingleValue(

			docStore:bulkDocValue { includeInitialChangeCounter = true, 1, 15, 48, 99, 100 }
				:map( function( result )
					for i = 1, #result do
						result[ i ] = mapAwayRevHashCode( result[ i ] )
					end
					return result
				end ),

			{
				{
					_id = 'test_1',
					_changeCounter = 1,
					_initialChangeCounter = 1,
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 1,
				},
				{
					_id = 'test_15',
					_changeCounter = 15,
					_initialChangeCounter = 15,
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 15,
				},
				{
					_id = 'test_48',
					_changeCounter = 48,
					_initialChangeCounter = 48,
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 48,
				},
				{
					_id = 'test_99',
					_changeCounter = 99,
					_initialChangeCounter = 99,
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 99,
				},
				{
					_id = 'test_100',
					_changeCounter = 100,
					_initialChangeCounter = 100,
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 100,
				},
			} )

		rxTest:expectSingleValue(

			docStore:bulkApplyOverlay {
				{
					_id = 'test_1',
					foo = 'foo',
				},
				{
					_id = 'test_15',
					foo = 'foo',
				},
				{
					_id = 'test_48',
					foo = 'foo',
				},
				{
					_id = 'test_99',
					foo = 'foo',
				},
				{
					_id = 'test_100',
					foo = 'foo',
				},
			},

			':ignore:' )


		-- Verify we get the first changeCounter in _initialChangeCounter when using
		-- includeInitialChangeCounter.

		rxTest:expectSingleValue(

			docStore:bulkDocValue { includeInitialChangeCounter = true, 1, 15, 48, 99, 100 }
				:map( function( result )
					for i = 1, #result do
						result[ i ] = mapAwayRevHashCode( result[ i ] )
					end
					return result
				end ),

			{
				{
					_id = 'test_1',
					_changeCounter = 101,
					_initialChangeCounter = 1,
					_revId = "(valid hash)",
					foo = 'foo',
					counter = 1,
				},
				{
					_id = 'test_15',
					_changeCounter = 102,
					_initialChangeCounter = 15,
					_revId = "(valid hash)",
					foo = 'foo',
					counter = 15,
				},
				{
					_id = 'test_48',
					_changeCounter = 103,
					_initialChangeCounter = 48,
					_revId = "(valid hash)",
					foo = 'foo',
					counter = 48,
				},
				{
					_id = 'test_99',
					_changeCounter = 104,
					_initialChangeCounter = 99,
					_revId = "(valid hash)",
					foo = 'foo',
					counter = 99,
				},
				{
					_id = 'test_100',
					_changeCounter = 105,
					_initialChangeCounter = 100,
					_revId = "(valid hash)",
					foo = 'foo',
					counter = 100,
				},
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValue_error_malformedDoc( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		rxTest:expectOnlyError(

			Observable.pcall( function()

				return docStore:bulkSetDocValue {
					{
						bogus = true, -- no _id field
					}
				}

			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValue_errorConflict( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValues = {}
		for i = 1, 5 do
			docValues[ i ] = {
				_id = 'test_' .. tostring( i ),
				foo = 'bar',
				counter = i,
			}
		end

		setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				test = 42,
			} )

		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:bulkSetDocValue( docValues )
			end ),

			':ignore:' )

		rxTest:expectSingleValue(

			docStore:changedDocs { fromChangeCounter = 1 }
				:map( function( result )

					local changedDoc1 = result[ 1 ]
					local gotConflictHash = changedDoc1
						and changedDoc1._id == 'test_1'
						and changedDoc1._conflict == true

					return gotConflictHash, result

				end ),

			false, ':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValue_conflictParentExistsViaRevIds( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local parentRev1 = setDocValue_firstRevTest42( rxTest, docStore )

		local localSecondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = parentRev1,
				test = 45,
			} )

		local remoteSecondRevId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					_revIds = { parentRev1, remoteSecondRevId },
					test = 49,
				},
			},

			':ignore:' )

		-- Newly conflicting rev should lose in this scenario.

		rxTest:expectSingleValue(

			docStore:changedDocs { fromChangeCounter = 1 },

			{
				{
					_changeCounter = 3,
					_conflict = true,
					_id = "testDoc",
					_localId = 1,
					_revId = localSecondRevId,
					test = 45,
				},
				continueFrom = 4,
			} )

		-- Now check results of :currentRevisionsForDocument.

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'testDoc' ),

			{
				{
					_changeCounter = 2,
					_conflict = true,
					_id = "testDoc",
					_localId = 1,
					_revId = localSecondRevId,
					test = 45,
				},
				{
					_changeCounter = 3,
					_conflict = true,
					_id = "testDoc",
					_localId = 1,
					_revId = remoteSecondRevId,
					test = 49,
				},
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValue_conflictParentExistsErrorNoForce( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local parentRev1 = setDocValue_firstRevTest42( rxTest, docStore )

		setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = parentRev1,
				test = 45,
			} )

		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:bulkSetDocValue {
					{
						_id = 'testDoc',
						_revId = parentRev1,
						test = 49,
					},
				}
			end ),

			':ignore:' )

		rxTest:expectSingleValue(

			docStore:changedDocs { fromChangeCounter = 1 }
				:map( function( result )

					local changedDoc1 = result[ 1 ]
					local gotConflictHash = changedDoc1
						and changedDoc1._id == 'testDoc'
						and changedDoc1._conflict == true

					return gotConflictHash, result

				end ),

			false, ':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestOngoingDocValue( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValue = docStore:ongoingDocValue( 'testDoc' )
		local odv = rxTest:observe( docValue )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Create first revision of empty document.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		odv:expectData {
			_id = 'testDoc',
			_localId = 1,
			_revId = firstRevId,
			_changeCounter = 1,
			test = 42,
		}

		-- Replace that with second revision and read back.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = secondRevId,
			_changeCounter = 2,
			test = 45,
		}

		odv:expectData( expectedValueRev2 )

		-- Replace that with second revision and read back.

		local thirdRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = secondRevId,
				because = "NoSQL",
			} )

		local expectedValueRev3 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = thirdRevId,
			_changeCounter = 3,
			because = "NoSQL",
		}

		odv:expectData( expectedValueRev3 )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestOngoingDocValue_localOnlyContent( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValue = docStore:ongoingDocValue( 'testDoc' )
		local odv = rxTest:observe( docValue )

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Create first revision of empty document.

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		odv:expectData {
			_id = 'testDoc',
			_localId = 1,
			_revId = firstRevId,
			_changeCounter = 1,
			test = 42,
		}

		-- Replace that with second revision and read back.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				_localOnly = {
					hh = "Hamburg",
				},
				test = 45,
			} )

		odv:expectData( {
			_id = 'testDoc',
			_localId = 1,
			_revId = secondRevId,
			_changeCounter = 2,
			_localOnly = {
				hh = "Hamburg",
			},
			test = 45,
		} )

		-- Replace that with second revision and read back.
		-- Note special-case handling: _localOnly value is preserved
		-- because we didn't replace it.

		local thirdRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = secondRevId,
				because = "NoSQL",
			} )

		odv:expectData( {
			_id = 'testDoc',
			_localId = 1,
			_localOnly = {
				hh = "Hamburg",
			},
			_revId = thirdRevId,
			_changeCounter = 3,
			because = "NoSQL",
		} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestOngoingLocalDocValue( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docName = 'testDoc'
		
		local docValue = {
		
			_id = docName,
			counter = 1
			
		}

		local expectedDocValue = {
			_id = docName,
			counter = 1
		}

		rxTest:expectOnlyCompleted( docStore:setLocalDocValue( KSTableUtils.deepCopy( docValue ) ) )

		rxTest:expectSingleValue( docStore:localDocValue( docName ), expectedDocValue )

		local ongoingdocValue = docStore:ongoingLocalDocValue( docName )
		local odv = rxTest:observe( ongoingdocValue )

		odv:expectDataWithinTime( 1, expectedDocValue )

		expectedDocValue.counter = 2
		docValue.counter = 2
		rxTest:expectOnlyCompleted( docStore:setLocalDocValue( KSTableUtils.deepCopy( docValue ) ) )

		odv:expectData( expectedDocValue )

		expectedDocValue.counter = 3
		docValue.counter = 3
		rxTest:expectOnlyCompleted( docStore:setLocalDocValue( KSTableUtils.deepCopy( docValue ) ) )

		odv:expectData( expectedDocValue )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestCurrentRevisionsForDoc( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Test response when no revisions at all.

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'testDoc' ),
			{} )

		-- Test response when one revision.

		local rev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_localOnly = {
					blah = 45,
				},
				test = 42,
			} )

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'testDoc' ),

			{
				{
					_id = 'testDoc',
					_localId = 1,
					_changeCounter = 1,
					_localOnly = {
						blah = 45,
					},
					_revId = rev1Id,
					test = 42,
				}
			} )

		-- Test response when one current revision and one obsolete revision.

		local rev2Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = rev1Id,
				_localOnly = {
					blah = 48,
				},
				test = 49,
			} )

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'testDoc' ),

			{
				{
					_id = 'testDoc',
					_localId = 1,
					_changeCounter = 2,
					_revId = rev2Id,
					_localOnly = {
						blah = 48,
					},
					test = 49,
				}
			} )

		-- Now introduce a conflict. The conflicting revision is always marked as
		-- the losing revision.

		local rev1ConflictId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					_revIds = { rev1ConflictId },
					showMeAConflict = true,
				}
			},

			':ignore:' )

		local rev2Value = {
				_id = 'testDoc',
				_changeCounter = 2,
				_localId = 1,
				_localOnly = {
					blah = 48,
				},
				_revId = rev2Id,
				_conflict = true,
				test = 49,
			}

		local rev1ConflictValue = {
				_id = 'testDoc',
				_changeCounter = 3,
				_localId = 1,
				_localOnly = {
					blah = 48,
				},
				_revId = rev1ConflictId,
				_conflict = true,
				showMeAConflict = true,
			}

		local expectedCurrentRevs_afterConflict = { rev2Value, rev1ConflictValue }

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'testDoc' ),
			expectedCurrentRevs_afterConflict )

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'testDoc', true ),
			expectedCurrentRevs_afterConflict )

		-- Now resolve the conflict.

		local deletedRevId

		rxTest:expectSingleValue(

			docStore:deleteDoc( 'testDoc', rev1ConflictId )
				:tap( function( rev )
					deletedRevId = rev
				end ),

			':ignore:' )

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'testDoc' ),
			{
				{
					_id = 'testDoc',
					_changeCounter = 2,
					_localId = 1,
					_localOnly = {
						blah = 48,
					},
					_revId = rev2Id,
					test = 49,
				},
			} )

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'testDoc', true ),
			{
				{
					_id = 'testDoc',
					_changeCounter = 2,
					_localId = 1,
					_localOnly = {
						blah = 48,
					},
					_revId = rev2Id,
					test = 49,
				},
				{
					_id = 'testDoc',
					_changeCounter = 4,
					_localId = 1,
					_localOnly = {
						blah = 48,
					},
					_revId = deletedRevId,
					_deleted = true,
					showMeAConflict = true,
				}
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestDeleteDoc( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local ongoingDocValue = docStore:ongoingDocValue( 'testDoc' )

		local odv = rxTest:observe( ongoingDocValue )

		local deletedDocStream = rxTest:observe( docStore:deletedDocsSinceNamedChangeCounter( 'rxTestDeleteDoc' )

			:filter( function( docValues )
				return #docValues > 0 -- will fire once with an empty array of deleted docs
			end)
			:map( function( docValues )
				local docValue = docValues[ 1 ]
				return docValue._id
			end ) )

		local spec = {
			_id = true,
			test = true
		}

		local reducedDeletedDocStream = rxTest:observe( 
			docStore:deletedDocsSinceNamedChangeCounter( 'rxTestDeleteDoc2', spec )
				:filter( function( docValues )
					return #docValues > 0 -- will fire once with an empty array of deleted docs
				end)
				:map( function( docValues )
					local docValue = docValues[ 1 ]
					docValue._changeCounter = nil
					return docValue
				end ) )

		odv:expectData( 
			{
				_id = 'testDoc',
				_empty = true,
			} )

		-- Create first revision of empty document.

		local rev1Id = setDocValue_firstRevTest42( rxTest, docStore )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = rev1Id,
			_changeCounter = 1,
			test = 42,
		}

		odv:expectDataWithinTime( 1, expectedValueRev1 )

		-- Replace that with second (deleted) revision.

		local rev2Id

		rxTest:expectSingleValue(

			docStore:deleteDoc( 'testDoc' )
			:tap( function( revId )
				rev2Id = revId
			end ),

			':ignore:' )

		deletedDocStream:expectDataWithinTime( 1, 'testDoc' )
		
		reducedDeletedDocStream:expectData( {
			_id = 'testDoc',
			test = 42
		} )

		rxTest:expectEqual( true, not not rev2Id:match( '^[0-9a-f]+$' ) )

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = rev2Id,
			_changeCounter = 2,
			_deleted = true,
			test = 42,
		}

		-- Note that :ongoingDocValue does not distinguish between
		-- documents that have never existed and those that have been deleted.

		odv:expectData {
			_id = 'testDoc',
			_empty = true,
		}

		-- Replace that with third revision. Read back again.

		local rev3Id

		rxTest:expectSingleValue(

			docStore:setDocValue {
				_id = 'testDoc',
				_localId = 1,
				_revId = rev2Id,
				because = "NoSQL",
			}
			:tap( function( revId )
				rev3Id = revId
			end ),

			':ignore:' )

		local expectedValueRev3 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = rev3Id,
			_changeCounter = 3,
			because = "NoSQL",
		}

		odv:expectDataWithinTime( 1, expectedValueRev3 )

		-- Now read back all three revisions.

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', rev1Id ),
								  expectedValueRev1 )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', rev2Id ),
								  expectedValueRev2 )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', rev3Id ),
								  expectedValueRev3 )

		deletedDocStream:dispose()

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestDeleteDocWF1625( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local ongoingDocValue = docStore:ongoingDocValue( 'testDoc' )

		local odv = rxTest:observe( ongoingDocValue )

		local newContinueFrom = 0

		-- The following Rx Chain is based on setupPurgeOnDelete within the
		-- LrmOzPurgeAgent.
		
		local deletedDocStream = rxTest:observe( docStore:deletedDocsSinceNamedChangeCounter( 'rxTestDeleteDoc' )

			:flatMap( function( docValues, continueFrom )

				return Rx.Observable.from( docValues )
				
					:map( function( docValue )
					
						return docValue._id -- This will never fire.
				
					end )
				
					:toArray() 
				
					:flatMap( function( docIds )
				
						newContinueFrom = continueFrom
				
						return docStore:updateDeletedDocsSinceNamedChangeCounter(
								'rxTestDeleteDoc', continueFrom )
								
								:map( function()
								
									return docIds
								
								end )
					
					end )

			end ) )

		-- Create first revision of empty document.
		
		local rev1Id = setDocValue_firstRevTest42( rxTest, docStore )

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = rev1Id,
			_changeCounter = 1,
			test = 42,
		}

		odv:expectDataWithinTime( 1, expectedValueRev1 )
		
		deletedDocStream:expectDataWithinTime( 1, {} )
		
		rxTest:expectEqual( 2, newContinueFrom )

		-- Replace that with second (deleted) revision.

		local rev2Id

		rxTest:expectSingleValue(

			docStore:deleteDoc( 'testDoc' )
			:tap( function( revId )
				rev2Id = revId
			end ),

			':ignore:' )

		deletedDocStream:expectDataWithinTime( 1, { 'testDoc' } )
		
		rxTest:expectEqual( 3, newContinueFrom )

		-- Replace that with third revision. Read back again.

		local rev3Id

		rxTest:expectSingleValue(

			docStore:setDocValue {
				_id = 'testDoc',
				_localId = 1,
				_revId = rev2Id,
				because = "NoSQL",
			}
			:tap( function( revId )
				rev3Id = revId
			end ),

			':ignore:' )

		local expectedValueRev3 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = rev3Id,
			_changeCounter = 3,
			because = "NoSQL",
		}

		odv:expectDataWithinTime( 1, expectedValueRev3 )

		deletedDocStream:expectDataWithinTime( 1, {} )
		
		rxTest:expectEqual( 4, newContinueFrom )

		deletedDocStream:dispose()

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestDeletedDocsSinceNamedChangeCounter( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local firstDocId = 'docId1'

		local ongoingDocValue = docStore:ongoingDocValue( firstDocId )

		local odv = rxTest:observe( ongoingDocValue )

		-- Create first revision of empty document.

		local rev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = firstDocId,
				test = 42,
			} )

		odv:expectData( 
			{
				_id = firstDocId,
				_empty = true,
			} )

		local expectedValueRev1 = {
			_id = firstDocId,
			_localId = 1,
			_revId = rev1Id,
			_changeCounter = 1,
			test = 42,
		}

		odv:expectData( expectedValueRev1 )

		-- Listen on a deletedDocStream which has not yet to report a deleted doc.

		local deletedDocStreamWithoutDeletions = rxTest:observe( docStore:deletedDocsSinceNamedChangeCounter( 'rxTestDeletedDocStream' )

			:map( function( docValues, continueFrom )
				local numberOfDocValues = #docValues
				return numberOfDocValues, continueFrom
			end ) )

		-- It should fire once with an empty list of docIds and an updated continueFrom

		deletedDocStreamWithoutDeletions:expectDataWithinTime( 1, 0, 2 )

		-- Replace first revision with second (deleted) revision.

		local rev2Id

		rxTest:expectSingleValue(

			docStore:deleteDoc( firstDocId )
			:tap( function( revId )
				rev2Id = revId
			end ),

			':ignore:' )

		odv:expectData( 
			{
				_id = firstDocId,
				_empty = true,
			} )

		-- Start listening on the deletion stream after the deletion.
		
		local deletedDocStream = rxTest:observe( docStore:deletedDocsSinceNamedChangeCounter( 'rxTestDeletedDocStream' )

			:filter( function( docValues)
				return next( docValues )
			end )

			:map( function( docValues, continueFrom )
				local docValue = docValues[ 1 ]
				return docValue._id, continueFrom
			end ) )

		-- We should recieve the deletion even though it happened in the past.
		
		deletedDocStream:expectDataWithinTime( 1, firstDocId, 3 )

		-- Create first revision of a new empty document.

		local secondDocId = 'docId2'

		local ongoingDocValue2 = docStore:ongoingDocValue( secondDocId )

		local odv2 = rxTest:observe( ongoingDocValue2 )

		local doc2Rev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = secondDocId,
				test = 42,
			} )

		odv2:expectData( 
			{
				_id = secondDocId,
				_empty = true,
			} )

		local doc2ExpectedValueRev1 = {
			_id = secondDocId,
			_localId = 2,
			_revId = doc2Rev1Id,
			_changeCounter = 3,
			test = 42,
		}

		odv2:expectData( doc2ExpectedValueRev1 )

		-- Replace that with second (deleted) revision.

		local doc2Rev2Id

		rxTest:expectSingleValue(

			docStore:deleteDoc( secondDocId )
			:tap( function( revId )
				doc2Rev2Id = revId
			end ),

			':ignore:' )

		-- We should receive this deletion on the deletion stream as well.
		
		deletedDocStream:expectDataWithinTime( 1, secondDocId, 5 )

		-- Start listening on the deletion stream again. This time, provide a changeCounter
		-- of 3. We should only see the deletion associated with the second document.
		
		rxTest:expectSingleValue( docStore:updateDeletedDocsSinceNamedChangeCounter( 'valueOf3', 3 ), true )
		
		local deletedDocStream2 = rxTest:observe( docStore:deletedDocsSinceNamedChangeCounter( 'valueOf3' )

			:map( function( docValues, continueFrom )
				local docValue = docValues[ 1 ]
				return docValue._id, continueFrom
			end ) )
		
		deletedDocStream2:expectData( secondDocId, 5 )

		deletedDocStreamWithoutDeletions:dispose()
		deletedDocStream:dispose()
		deletedDocStream2:dispose()
		
	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestDeleteDoc_runUntilDone( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		setDocValue_firstRevTest42( rxTest, docStore )

		-- Delete this document, but don't watch observable.

		docStore:deleteDoc( 'testDoc' )

		-- Now check doc value. Should be empty.

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' )
				:map( function( docValue )
					return mapAwayRevHashCode( docValue )
				end ),

			{
				_id = 'testDoc',
				_empty = true,
				_deleted = true,
				_revId = '(valid hash)',
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestDeleteDoc_empty( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Delete a document that was never created. Should succeed.
		-- Wouldn't want the infamous "the document could not be deleted
		-- because it could not be found" message!

		rxTest:expectSingleValue( docStore:deleteDoc( 'testDoc' ), ':ignore:' )

		-- Changes feed should be empty after this no-op.

		rxTest:expectSingleValue( docStore:changedDocs { fromChangeCounter = 1 },
								  ':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestDeleteDoc_errorNoSuchRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision of empty document.

		setDocValue_firstRevTest42( rxTest, docStore )

		-- Delete a different rev that we never created.

		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:deleteDoc( 'testDoc', KSIdGenerator.uuidInLowercaseHex() )
			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestDeleteDoc_alreadyDeleted( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision of empty document.

		setDocValue_firstRevTest42( rxTest, docStore )

		-- Replace that with second (deleted) revision.

		local deletedRevId

		rxTest:expectSingleValue(

			docStore:deleteDoc( 'testDoc' )
				:tap( function( revId )
					deletedRevId = revId
				end ),

			':ignore:' )

		-- Delete document again. Should be a no-op.

		rxTest:expectSingleValue(
			docStore:deleteDoc( 'testDoc' ),
			true )

		-- Delete document specifying deleted revision. Should be a no-op.

		rxTest:expectSingleValue(

			docStore:deleteDoc( 'testDoc', deletedRevId )
				:map( function( revId )
					return revId == deletedRevId, revId
				end ),

			true, ':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestDeleteDoc_conflictResolution( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local rev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				test = 42,
			} )

		local rev2Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				_revId = rev1Id,
				test = 44,
			} )

		local rev3Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				_revId = rev2Id,
				test = 48,
			} )

		local ongoingDocValue = docStore:ongoingDocValue( 'test_1' )
		local odv = rxTest:observe( ongoingDocValue )

		local expectedChangedDocsResult_beforeConflict = {

			continueFrom = 4,

			{
				_id = 'test_1',
				_localId = 1,
				_revId = rev3Id,
				_changeCounter = 3,
				test = 48,
			}

		}

		odv:expectData( expectedChangedDocsResult_beforeConflict[ 1 ] )

		rxTest:expectSingleValue( docStore:changedDocs { fromChangeCounter = 1 },
								  expectedChangedDocsResult_beforeConflict )

		local rev2ConflictId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'test_1',
					_localId = 1,
					_revIds = { rev1Id, rev2ConflictId },
					test = 96,
				}
			},

			':ignore:' )

		local expectedChangedDocsResult_afterConflict = {

			continueFrom = 5,

			{
				_id = 'test_1',
				_localId = 1,
				_revId = rev3Id,
				_conflict = true,
				_changeCounter = 4,
				test = 48,
			}

		}

		odv:expectData( expectedChangedDocsResult_afterConflict[ 1 ] )

		rxTest:expectSingleValue(
			docStore:changedDocs { fromChangeCounter = 1 },
			expectedChangedDocsResult_afterConflict )

		rxTest:expectSingleValue(
			docStore:deleteDoc( 'test_1', rev2ConflictId ),
			':ignore:' )

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'test_1' ),

			{
				{
					_changeCounter = 3,
					_id = "test_1",
					_localId = 1,
					_revId = rev3Id,
					test = 48,
				},
			} )

		local expectedChangedDocsResult_afterConflictResolved = {

			continueFrom = 6,

			{
				_id = 'test_1',
				_localId = 1,
				_revId = rev3Id,
				_changeCounter = 5,
				test = 48,
			}

		}

		odv:expectData( expectedChangedDocsResult_afterConflictResolved[ 1 ] )

		rxTest:expectSingleValue(
			docStore:changedDocs { fromChangeCounter = 1 },
			expectedChangedDocsResult_afterConflictResolved )

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'test_1' ),

			{
				{
					_changeCounter = 3,
					_id = "test_1",
					_localId = 1,
					_revId = rev3Id,
					test = 48,
				},
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestDeleteDoc_conflictAndLocalOnly( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local rev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				test = 42,
			} )

		local rev2Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				_revId = rev1Id,
				test = 44,
			} )

		local rev3Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				_revId = rev2Id,
				test = 48,
			} )

		local ongoingDocValue = docStore:ongoingDocValue( 'test_1' )
		local odv = rxTest:observe( ongoingDocValue )

		local expectedChangedDocsResult_beforeConflict = {

			continueFrom = 4,

			{
				_id = 'test_1',
				_localId = 1,
				_revId = rev3Id,
				_changeCounter = 3,
				test = 48,
			}

		}

		odv:expectData( expectedChangedDocsResult_beforeConflict[ 1 ] )

		rxTest:expectSingleValue( docStore:changedDocs { fromChangeCounter = 1 },
								  expectedChangedDocsResult_beforeConflict )

		-- Introduce a _localOnly annotation in this revision, which will be deleted.
		-- Annotations are not versioned and apply to all versions of the document,
		-- so we expect this annotation to persist even after we delete the conflicting
		-- revision.

		local rev2ConflictId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'test_1',
					_localId = 1,
					_revIds = { rev1Id, rev2ConflictId },
					_localOnly = {
						keepMe = "around",
					},
					test = 96,
				}
			},

			':ignore:' )

		local expectedChangedDocsResult_afterConflict = {

			continueFrom = 5,

			{
				_id = 'test_1',
				_localId = 1,
				_revId = rev3Id,
				_conflict = true,
				_changeCounter = 4,
				_localOnly = {
					keepMe = "around",
				},
				test = 48,
			}

		}

		odv:expectData( expectedChangedDocsResult_afterConflict[ 1 ] )

		rxTest:expectSingleValue(
			docStore:changedDocs { fromChangeCounter = 1 },
			expectedChangedDocsResult_afterConflict )

		rxTest:expectSingleValue(
			docStore:deleteDoc( 'test_1', rev2ConflictId ),
			':ignore:' )

		local expectedChangedDocsResult_afterConflictResolved = {

			continueFrom = 6,

			{
				_id = 'test_1',
				_localId = 1,
				_revId = rev3Id,
				_changeCounter = 5,
				_localOnly = {
					keepMe = "around",
				},
				test = 48,
			}

		}

		odv:expectData( expectedChangedDocsResult_afterConflictResolved[ 1 ] )

		rxTest:expectSingleValue(
			docStore:changedDocs { fromChangeCounter = 1 },
			expectedChangedDocsResult_afterConflictResolved )

		-- Now check results of :currentRevisionsForDocument.

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'test_1' ),

			{
				{
					_changeCounter = 3,
					_id = "test_1",
					_localId = 1,
					_localOnly = {
						keepMe = "around",
					},
					_revId = rev3Id,
					test = 48,
				},
			} )

	end
	
	--------------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestDeleteDoc_DontMoveLabel( rxTest )
	
		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local updatedRevId
		
		rxTest:expectSingleValue(
			
			docStore:bulkSetDocValue( { 
			
				label = 'oz',
				{
					_id = 'test_1',
					test = 100,
				}
			} )
			:tap( function( localRevIds )
				updatedRevId = localRevIds[ 1 ].revId
			end ), ':ignore:' )
			
		rxTest:expectSingleValue( 
		
			docStore:docValue( 'test_1', 'oz' )
				:map( function( docValue )
				
					return docValue._revId
					
				end ),
				
			updatedRevId )
			
		rxTest:expectSingleValue( docStore:deleteDoc( 'test_1' ), ':ignore:' )
		
		rxTest:expectSingleValue( 
		
			docStore:docValue( 'test_1', 'oz' )
				:map( function( docValue )
				
					return docValue._revId
					
				end ),
				
			updatedRevId )
		
	end

	----------------------------------------------------------------------------

	-- @mark revision labels

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetSetDocValue_label( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision and assign it a label.

		local firstRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_labels = {
					label1 = true,
				},
				test = 42,
			} )

		-- Replace that with second revision and read back.
		-- Should not have label at this revision.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = secondRevId,
				_changeCounter = 2,
				test = 45,
			} )

		-- Read first revision back again. Should have label.

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', firstRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_labels = {
					label1 = true,
				},
				_revId = firstRevId,
				_changeCounter = 1,
				test = 42,
			} )

		-- Create third revision and move label.

		local thirdRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_labels = {
					label1 = true,
				},
				_revId = secondRevId,
				because = "NoSQL",
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_labels = {
					label1 = true,
				},
				_revId = thirdRevId,
				_changeCounter = 3,
				because = "NoSQL",
			} )

		-- The revision history should just show the latest labeled revision

		rxTest:expectSingleValue( docStore:docRevisionHistory( 'testDoc' ),		
			{
				{
					revId = thirdRevId,
					status = "available"
				}
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetSetDocValue_conflictWithLabel( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision.

		local firstRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				test = 42,
			} )
	
	
		-- Create second revision.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )
			
		-- Create third - conflicting - revision as if it came from Oz, i.e. with a _revIds array instead 
		-- of a single _revId 

		-- BTW, confusing things happen if you just pass in _revIds = { firstRevId }. This should never in
		-- in practice, though

		local conflictingRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revIds = { firstRevId, KSIdGenerator.uuidInLowercaseHex() }, -- fake the Oz revision Id
				_labels = {
					label1 = true,
				},
				test = 23,
			} )
		
		-- Read back the first revision to make sure it still exists

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc', firstRevId ),

			{
				_id = 'testDoc',
				_changeCounter = 1,
				_conflict = true,
				_localId = 1,
				_revId = firstRevId,
				test = 42,
			} )
		
		-- Read back last (conflicting) winning revision 

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_changeCounter = 3,
				_conflict = true,
				_localId = 1,
				_revId = secondRevId,
				test = 45,
			} )

		
		-- resolve the conflict by removing the 2nd revision
		rxTest:expectSingleValue( 
			docStore:deleteRevisionAsNonCurrent( 'testDoc', secondRevId ),
			':ignore:'  )

		-- make sure the conflict is gone		
		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_changeCounter = 4,
				_localId = 1,
				_labels = {
					label1 = true,
				},
				_revId = conflictingRevId,
				test = 23,
			} )
		
		-- verify that firstRevId is still available
		
		rxTest:expectSingleValue(
		
			docStore:docRevisionHistory( 'testDoc' ),
			
			{
				{
					revId = conflictingRevId,
					status = "available",
				},
				{
					revId = firstRevId,
					status = "available",
				},
			} )	
		
		-- now simulate a new revision coming in from Oz
		
		local ozRevId = KSIdGenerator.uuidInLowercaseHex()
		setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revIds = { firstRevId, conflictingRevId, ozRevId }, 
				_labels = {
					label1 = true,
				},
				test = 17,
			} )
		
		-- Now all but the last revision should be gone

		rxTest:expectSingleValue(
		
			docStore:docRevisionHistory( 'testDoc' ),
			
			{
				{
					revId = ozRevId,
					status = "available",
				}
			} )	
		
		-- perform an explicit integrity check
		rxTest:expectOnlyCompleted( docStore:performIntegrityCheck() )
		
		rxTest:expectOnlyCompleted( docStore:haltAndDelete() )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetSetDocValue_conflictWithLabelResolveOther( rxTest )

		-- This is essentially the same test as rxTestGetSetDocValue_conflictWithLabel above,
		-- just the conflict resolution goes the other way

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision.

		local firstRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				test = 42,
			} )
	
	
		-- Create second revision.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )
			
		-- Create third - conflicting - revision as if it came from Oz, i.e. with a _revIds array instead 
		-- of a single _revId 

		-- BTW, confusing things happen if you just pass in _revIds = { firstRevId }. This should never in
		-- in practice, though

		local conflictingRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revIds = { firstRevId, KSIdGenerator.uuidInLowercaseHex() }, -- fake the Oz revision Id
				_labels = {
					label1 = true,
				},
				test = 23,
			} )
		
		-- Read back the first revision to make sure it still exists

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc', firstRevId ),

			{
				_id = 'testDoc',
				_changeCounter = 1,
				_conflict = true,
				_localId = 1,
				_revId = firstRevId,
				test = 42,
			} )
		
		-- Read back last (conflicting) winning revision 

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_changeCounter = 3,
				_conflict = true,
				_localId = 1,
				_revId = secondRevId,
				test = 45,
			} )

		
		-- resolve the conflict by removing the conflicting revision
		rxTest:expectSingleValue( 
			docStore:deleteRevisionAsNonCurrent( 'testDoc', conflictingRevId ),
			':ignore:'  )

		-- make sure the conflict is gone		
		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_changeCounter = 4,
				_localId = 1,
				_revId = secondRevId,
				test = 45,
			} )
		
		-- verify that firstRevId is still available
		
		rxTest:expectSingleValue(
		
			docStore:docRevisionHistory( 'testDoc' ),
			
			{
				{
					revId = secondRevId,
					status = "available",
				},
				{
					revId = firstRevId,
					status = "available",
				},
			} )	
		
		-- now simulate a new revision coming in from Oz
		
		local ozRevId = KSIdGenerator.uuidInLowercaseHex()
		setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revIds = { firstRevId, secondRevId, ozRevId }, 
				_labels = {
					label1 = true,
				},
				test = 17,
			} )
		
		-- Now all but the last revision should be gone

		rxTest:expectSingleValue(
		
			docStore:docRevisionHistory( 'testDoc' ),
			
			{
				{
					revId = ozRevId,
					status = "available",
				}
			} )	
		
		-- perform an explicit integrity check
		rxTest:expectOnlyCompleted( docStore:performIntegrityCheck() )

		rxTest:expectOnlyCompleted( docStore:haltAndDelete() )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetSetDocValue_multipleLabels( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision and assign it two label.

		local firstRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_labels = {
					label1 = true,
					label2 = true,
				},
				test = 42,
			} )

		-- Replace that with second revision. Move one of the labels.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_labels = {
					label2 = true,
					label3 = true,
				},
				_revId = firstRevId,
				test = 45,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_labels = {
					label2 = true,
					label3 = true,
				},
				_revId = secondRevId,
				_changeCounter = 2,
				test = 45,
			} )

		-- Read first revision back again. Should have label1, but not 2 or 3.

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', firstRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_labels = {
					label1 = true,
				},
				_revId = firstRevId,
				_changeCounter = 1,
				test = 42,
			} )

		-- Create third revision and move two of three labels.

		local thirdRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_labels = {
					label1 = true,
					label3 = true,
				},
				_revId = secondRevId,
				because = "NoSQL",
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_labels = {
					label1 = true,
					label3 = true,
				},
				_revId = thirdRevId,
				_changeCounter = 3,
				because = "NoSQL",
			} )

		-- Read first revision back again. Should result in an error because it was purged.

		rxTest:expectOnlyError(

			Observable.pcall( function()

				return docStore:setDocValue {
					_id = 'testDoc',
					_revId = firstRevId,
					test = 42,
				}

			end ),
			':ignore:' ) 

		-- Second rev should still have label2.

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', secondRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_labels = {
					label2 = true,
				},
				_revId = secondRevId,
				_changeCounter = 2,
				test = 45,
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValue_replacingRev_bulkLabel( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision of empty document.

		local firstRevId

		local expectedBulkResult1 = {
			{
				id = 'testDoc',
				localId = 1,
				ok = true,
				-- revId = (inserted later),
			}
		}

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				label = 'label1',
				{
					_id = 'testDoc',
					test = 42,
				}
			}
			:tap( function( results )
				local revId = results[ 1 ].revId
				if revId:match( kRevIdPattern ) then
					expectedBulkResult1[ 1 ].revId = revId
					firstRevId = revId
				end
			end ),

			expectedBulkResult1 )

		-- Replace that with second revision and read back.

		local secondRevId

		local expectedBulkResult2 = {
			{
				id = 'testDoc',
				localId = 1,
				ok = true,
				-- revId = (inserted later),
			}
		}

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				label = 'label2',
				{
					_id = 'testDoc',
					_revId = firstRevId,
					test = 45,
				}
			}
			:tap( function( results )
				local revId = results[ 1 ].revId
				if revId:match( kRevIdPattern ) then
					expectedBulkResult2[ 1 ].revId = revId
					secondRevId = revId
				end
			end ),

			expectedBulkResult2 )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = secondRevId,
				_changeCounter = 2,
				_labels = {
					label2 = true,
				},
				test = 45,
			} )

		-- Confirm that label1 stayed with first revision.

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc', firstRevId ),

			{
				_id = 'testDoc',
				_localId = 1,
				_changeCounter = 1,
				_revId = firstRevId,
				_labels = {
					label1 = true,
				},
				test = 42,
			} )

		-- Replace that with third revision. Read back again.

		local thirdRevId

		local expectedBulkResult3 = {
			{
				id = 'testDoc',
				localId = 1,
				ok = true,
				-- revId = (inserted later),
			}
		}

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'testDoc',
					_revId = secondRevId,
					because = "NoSQL",
				}
			}
			:tap( function( results )
				local revId = results[ 1 ].revId
				if revId:match( kRevIdPattern ) then
					expectedBulkResult3[ 1 ].revId = revId
					thirdRevId = revId
				end
			end ),

			expectedBulkResult3 )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = thirdRevId,
				_changeCounter = 3,
				because = "NoSQL",
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkSetDocValue_error_invalidLabelName( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		rxTest:expectOnlyError(

			Observable.pcall( function()

				return docStore:bulkSetDocValue {
					label = '', -- non-empty string is not allowed
					{
						_id = 'testDoc',
						test = 42,
					}
				}

			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetLabel( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision and assign it a label.

		local firstRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_labels = {
					label1 = true,
				},
				test = 42,
			} )

		-- Replace that with second revision and read back.
		-- Should not have label at this revision.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = secondRevId,
				_changeCounter = 2,
				test = 45,
			} )

		-- Add another label and read back.

		rxTest:expectOnlyCompleted( docStore:setLabel( 'testDoc', 'label2', firstRevId ) )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', firstRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = firstRevId,
				_labels = {
					label1 = true,
					label2 = true,
				},
				_changeCounter = 1,
				test = 42,
			} )

		-- Create third revision.

		local thirdRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = secondRevId,
				because = "NoSQL",
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = thirdRevId,
				_changeCounter = 3,
				because = "NoSQL",
			} )

		-- Move label and read back.

		rxTest:expectOnlyCompleted( docStore:setLabel( 'testDoc', 'label2', thirdRevId ) )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = thirdRevId,
				_labels = {
					label2 = true,
				},
				_changeCounter = 3,
				because = "NoSQL",
			} )

		-- Also verify bulkDocValue

		rxTest:expectSingleValue( docStore:bulkDocValue( { 'testDoc' } ),
			{
				{
					_id = 'testDoc',
					_localId = 1,
					_revId = thirdRevId,
					_labels = {
						label2 = true,
					},
					_changeCounter = 3,
					because = "NoSQL",
				} 
			} )


		-- Read first revision back again. Should no longer have second label.

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', firstRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = firstRevId,
				_changeCounter = 1,
				_labels = {
					label1 = true,
				},
				test = 42,
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetLabel_runUntilDone( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision and assign it a label.

		local firstRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_labels = {
					label1 = true,
				},
				test = 42,
			} )

		-- Replace that with second revision and read back.
		-- Should not have label at this revision.

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = secondRevId,
				_changeCounter = 2,
				test = 45,
			} )

		-- Add another label and read back.
		-- Do not watch the observable.

		docStore:setLabel( 'testDoc', 'label2', firstRevId )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', firstRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = firstRevId,
				_labels = {
					label1 = true,
					label2 = true,
				},
				_changeCounter = 1,
				test = 42,
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetLabel_errorNoSuchDoc( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:setLabel( 'noSuchDoc', 'label', 'noSuchRevEither' )
			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetLabel_errorNoSuchRev( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		setDocValue_firstRevTest42( rxTest, docStore )

		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:setLabel( 'testDoc', 'label', 'noSuchRev' )
			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetLabel_invalidLabelName( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		setDocValue_firstRevTest42( rxTest, docStore )

		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:setLabel( 'testDoc', 42, 'noSuchRev' )
			end ),

			':ignore:' )

	end
	
	--------------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestRemoveLabel( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision and assign it a label.

		local firstRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_labels = {
					label1 = true,
				},
				test = 42,
			} )

		-- check the label is there
		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_labels = {
					label1 = true
				},
				_revId = firstRevId,
				_changeCounter = 1,
				test = 42,
			} )
		
		-- remove the label
		rxTest:expectOnlyCompleted( docStore:removeLabel( 'testDoc', 'label1', firstRevId ) )
		
		-- validate that the label is no longer on the doc
		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = firstRevId,
				_changeCounter = 1,
				test = 42,
			} )

	end
	
	--------------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestRemoveLabel_errorNoSuchDoc( rxTest )
		
		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )
		
		-- remove the label for a doc that doesn't exist
		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:removeLabel( 'noSuchDoc', 'label', 'noSuchRev' )
			end ),

			':ignore:' )
	end
	
	--------------------------------------------------------------------------------
	
	function RxDocStoreTestSuite.rxTestRemoveLabel_errorNoSuchRev( rxTest )
	
		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )
		
		-- create a doc with a label
		local firstRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_labels = {
					label1 = true,
				},
				test = 42,
			} )
			
		-- remove the label for a revision that doesn't exist
		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:removeLabel( 'testDoc', 'label1', 'noSuchRev' )
			end ),

			':ignore:' )
		
		-- make sure the label is still where it should be
		rxTest:expectSingleValue( docStore:getRevisionForLabel( 'testDoc', 'label1' ), firstRevId )
	end
	
	--------------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestRemoveLabel_noSuchLabel( rxTest )
		
		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )
		
		-- create a doc with a label
		local firstRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_labels = {
					label1 = true,
				},
				test = 42,
			} )
			
		-- remove a label that doesn't exist
		rxTest:expectOnlyCompleted( docStore:removeLabel( 'testDoc', 'label', firstRevId ) )
		
		-- make sure the correct label remains
		rxTest:expectSingleValue( docStore:getRevisionForLabel( 'testDoc', 'label1' ), firstRevId )
	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetLabel_getRevisionForLabel( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision and assign it a label.

		local firstRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_labels = {
					label1 = true,
				},
				test = 42,
			} )


		rxTest:expectSingleValue ( docStore:getRevisionForLabel( 'testDoc', 'label1' ), firstRevId )

		-- Add a second revision

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )

		rxTest:expectOnlyCompleted( docStore:setLabel( 'testDoc', 'label2', secondRevId ) )

		rxTest:expectSingleValue ( docStore:getRevisionForLabel( 'testDoc', 'label2' ), secondRevId )

		-- Move fist label to second revision

		rxTest:expectOnlyCompleted( docStore:setLabel( 'testDoc', 'label1', secondRevId ) )

		rxTest:expectSingleValue ( docStore:getRevisionForLabel( 'testDoc', 'label1' ), secondRevId )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetLabel_errorNoSuchDoc( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:getRevisionForLabel( 'noSuchDoc', 'label' )
			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetLabel_invalidLabelName( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		setDocValue_firstRevTest42( rxTest, docStore )

		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:getRevisionForLabel( 'testDoc', 'noSuchLabel' )
			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestUpdateLabelFromRevisionList_updateLabel( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision and assign it a label.

		local firstRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_labels = {
					label = true,
				},
				test = 42,
			} )


		rxTest:expectSingleValue ( docStore:getRevisionForLabel( 'testDoc', 'label' ), firstRevId )

		-- Add a second revision

		local secondRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				test = 45,
			} )

		-- and a third revision which revId we don't need

		setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = secondRevId,
				test = 47,
			} )

		-- Update label from revision list

		local revisionList = {
								firstRevId,
								secondRevId,
								'8da6f0baf1124cd1927e364c24f71058',
								'12d02c9b05db43498f792505336ad7a6',
								'bd6ed1dd2dcd4e17b390124e37d2ed49'
							 }

		rxTest:expectOnlyCompleted( docStore:updateLabelFromRevisionList( 'testDoc', 'label', revisionList ) )

		rxTest:expectSingleValue ( docStore:getRevisionForLabel( 'testDoc', 'label' ), secondRevId )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestUpdateLabelFromRevisionList_errorNoSuchDoc( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local revisionList = {
								'8da6f0baf1124cd1927e364c24f71058',
								'12d02c9b05db43498f792505336ad7a6',
								'bd6ed1dd2dcd4e17b390124e37d2ed49'
							 }

		local docStore = setUpDocStore( rxTest )

		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:updateLabelFromRevisionList( 'noSuchDoc', 'label', revisionList )
			end ),

			':ignore:' )

	end

	----------------------------------------------------------------------------

	-- @mark partial doc values

	----------------------------------------------------------------------------

	local applyOverlayTests = {}

	local function applyOverlayAndValidateRevId( rxTest, docStore, overlay )

		local revId

		rxTest:expectSingleValue(

			docStore:applyOverlay( overlay )
				:map( function( rev )
					revId = rev
					return not not rev:match( '^[0-9a-f]+$' ), #rev, rev
				end ),

			true, 32, ':ignore:' )

		return revId

	end

	local function bulkApplyOverlayToOneAndValidateRevId( rxTest, docStore, overlay )

		local revId

		rxTest:expectSingleValue(

			docStore:bulkApplyOverlay{ overlay }
				:map( function( revs )
					revId = revs[ 1 ]
					return #revs, not not revId:match( '^[0-9a-f]+$' ), #revId, revId
				end ),

			1, true, 32, ':ignore:' )

		return revId

	end
	
	local function applyOverlayToIdsToOneAndValidateRevId( rxTest, docStore, overlay )

		local revId

		local id = overlay._id
		overlay._id = nil

		rxTest:expectSingleValue(

			docStore:applyOverlayToIds( overlay, { id } )
				:map( function( revs )
					revId = revs[ 1 ]
					return #revs, not not revId:match( '^[0-9a-f]+$' ), #revId, revId
				end ),

			1, true, 32, ':ignore:' )

		return revId

	end

	----------------------------------------------------------------------------

	function applyOverlayTests.empty( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local rcc = rxTest:observe( docStore:changeCounter() )
		rcc:expectData( 0 )

		-- Set doc value and capture rev ID.

		local rev1Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				test = 42,
			} )

		rcc:expectData( 1 )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev1Id,
				_changeCounter = 1,
				test = 42,
			} )

	end

	----------------------------------------------------------------------------

	function applyOverlayTests.twice( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local rcc = rxTest:observe( docStore:changeCounter() )
		rcc:expectData( 0 )

		local rev1Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				test = 42,
			} )

		rcc:expectData( 1 )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev1Id,
				_changeCounter = 1,
				test = 42,
			} )

		local rev2Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				blah = 45,
			} )

		rcc:expectData( 2 )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev2Id,
				_changeCounter = 2,
				test = 42,
				blah = 45,
			} )

	end

	----------------------------------------------------------------------------

	function applyOverlayTests.runUntilDone( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local rcc = rxTest:observe( docStore:changeCounter() )
		rcc:expectData( 0 )

		docStore:applyOverlay {
			_id = 'testDoc',
			test = 42,
		}
			-- Intentionally not observing result.

		rcc:expectData( 1 )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' )
				:map( function( docValue )
					docValue._revId = nil
					return docValue
				end ),

			{
				_id = 'testDoc',
				_localId = 1,
				_changeCounter = 1,
				test = 42,
			} )

	end

	----------------------------------------------------------------------------

	function applyOverlayTests.doesNotMoveLabel( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision and assign it a label.

		local firstRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_labels = {
					label1 = true,
				},
				test = 42,
			} )

		-- Update that with second revision and read back.
		-- Should not have label at this revision.

		local secondRevId = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				test = 45,
			} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = secondRevId,
				_changeCounter = 2,
				test = 45,
			} )

		-- Read first revision back again. Should have label.

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', firstRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_labels = {
					label1 = true,
				},
				_revId = firstRevId,
				_changeCounter = 1,
				test = 42,
			} )

	end
	
	--------------------------------------------------------------------------------

	function applyOverlayTests.applyOverlayMoveLabel( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision and assign it a label.

		setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_labels = {
					label1 = true,
				},
				test = 42,
			} )

		-- Update that with second revision and move label.

		local secondRevId

		rxTest:expectSingleValue(

			docStore:bulkApplyOverlay( {
				label = 'label1',
				{
					_id = 'testDoc',
					test = 45,
				}
			} )
			:tap( function( revs )
				secondRevId = revs[ 1 ]
			end ),

			':ignore:' )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = secondRevId,
				_changeCounter = 2,
				_labels = {
					label1 = true,
				},
				test = 45,
			} )

		-- The revision history should just show the latest labeled revision

		rxTest:expectSingleValue( docStore:docRevisionHistory( 'testDoc' ),		
			{
				{
					revId = secondRevId,
					status = "available"
				}
			} )

	end
	
	--------------------------------------------------------------------------------

	function applyOverlayTests.applyOverlayDontMoveLabel( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision and assign it a label.

		local firstRevId = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_labels = {
					label1 = true,
				},
				test = 42,
			} )

		-- Update that with second revision.

		local secondRevId

		rxTest:expectSingleValue(

			docStore:bulkApplyOverlay( {
				{
					_id = 'testDoc',
					test = 45,
				}
			} )
			:tap( function( revs )
				secondRevId = revs[ 1 ]
			end ),

			':ignore:' )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = secondRevId,
				_changeCounter = 2,
				test = 45,
			} )

		-- Read first revision back again. Should have label.

		rxTest:expectSingleValue( docStore:docValue( 'testDoc', firstRevId ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = firstRevId,
				_changeCounter = 1,
				_labels = {
					label1 = true,
				},
				test = 42,
			} )

	end

	----------------------------------------------------------------------------

	function applyOverlayTests.updateOnlyLocalOnly( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local rcc = rxTest:observe( docStore:changeCounter() )
		rcc:expectData( 0 )

		local rev1Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				test = 42,
			} )

		rcc:expectData( 1 )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev1Id,
				_changeCounter = 1,
				test = 42,
			} )

		local rev2Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_localOnly = {
					blah = 45,
				}
			} )

		rcc:expectData( 2 )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev2Id,
				_changeCounter = 2,
				_localOnly = {
					blah = 45,
				},
				test = 42,
			} )

	end

	----------------------------------------------------------------------------

	function applyOverlayTests.noopOverlay( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local rcc = rxTest:observe( docStore:changeCounter() )
		rcc:expectData( 0 )

		local rev1Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				test = 42,
			} )

		rcc:expectData( 1 )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev1Id,
				_changeCounter = 1,
				test = 42,
			} )

		local rev2Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
			} )

		rxTest:expectEqual( rev1Id, rev2Id )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev1Id,
				_changeCounter = 1,
				test = 42,
			} )

		rxTest:expectSingleValue(
			docStore:changeCounter():take( 1 ),
			1 )

	end
	
	function applyOverlayTests.noopOverlayWithNewRevId( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local rcc = rxTest:observe( docStore:changeCounter() )
		rcc:expectData( 0 )

		local rev1Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				test = 42,
			} )

		rcc:expectData( 1 )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev1Id,
				_changeCounter = 1,
				test = 42,
			} )

		local rev2Id = KSIdGenerator.uuidInLowercaseHex()
		local rev2IdResult = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_newRevId = rev2Id
			} )

		rxTest:expectEqual( rev2Id, rev2IdResult )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev2Id,
				_changeCounter = 2,
				test = 42,
			} )

		rxTest:expectSingleValue(
			docStore:changeCounter():take( 1 ),
			2 )

	end
	
	--------------------------------------------------------------------------------

	function applyOverlayTests.deleteDocPermanent( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision of empty document.

		setDocValue_firstRevTest42( rxTest, docStore )

		-- Replace that with second (deleted) revision.

		local deletedRevId

		rxTest:expectSingleValue(

			docStore:deleteDoc( 'testDoc' )
				:tap( function( revId )
					deletedRevId = revId
				end ),

			':ignore:' )

		rxTest:expectSingleValue( 
			docStore:docValue( 'testDoc' )
				:map( function( docValue )
				
					return docValue
					
				end ),
			{
				_empty = true,
				_deleted = true,
				_id = 'testDoc',
				_revId = deletedRevId,
			} )
		
		-- Delete document again. Should be a no-op.

		local updatedRevId = applyOverlayAndValidateRevId( rxTest, docStore, {
			_id = 'testDoc',
			name = 'foo',
		} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), {
			_empty = true,
			_deleted = true,
			_id = 'testDoc',
			_revId = updatedRevId
		} )
			
			
		updatedRevId = applyOverlayAndValidateRevId( rxTest, docStore, {
			_id = 'testDoc',
			_localOnly = {
				test = 'foo',
			}
		} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), {
			_empty = true,
			_deleted = true,
			_id = 'testDoc',
			_revId = updatedRevId
		} )
	end
	
	--------------------------------------------------------------------------------

	function applyOverlayTests.deleteDocWithOverlay( applyOverlayAndValidateRevId, rxTest )
	
		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision of empty document.

		setDocValue_firstRevTest42( rxTest, docStore )

		-- Delete document again. Should be a no-op.

		local updatedRevId = applyOverlayAndValidateRevId( rxTest, docStore, {
			_id = 'testDoc',
			_deleted = true
		} )

		rxTest:expectSingleValue( docStore:docValue( 'testDoc' ), {
			_empty = true,
			_deleted = true,
			_id = 'testDoc',
			_revId = updatedRevId
		} )
	
	end 
	
	----------------------------------------------------------------------------

	function applyOverlayTests.conflict( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local rcc = rxTest:observe( docStore:changeCounter() )
		rcc:expectData( 0 )

		local rev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				test = 42,
			} )

		rcc:expectData( 1 )

		local rev2Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				_revId = rev1Id,
				test = 44,
			} )

		rcc:expectData( 2 )

		local rev3Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				_revId = rev2Id,
				test = 48,
			} )

		rcc:expectData( 3 )

		local rev2ConflictId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'test_1',
					_localId = 1,
					_revIds = { rev1Id, rev2ConflictId },
					test = 96,
				}
			},

			':ignore:' )

		rcc:expectData( 4 )

		rxTest:expectSingleValue(

			docStore:docValue( 'test_1' ),

			{
				_id = 'test_1',
				_localId = 1,
				_changeCounter = 4,
				_revId = rev3Id,
				_conflict = true,
				test = 48,
			} )

		local finalRevId = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				highway = 412,
			} )

		rxTest:expectSingleValue(

			docStore:docValue( 'test_1' ),

			{
				_id = 'test_1',
				_localId = 1,
				_changeCounter = 5,
				_revId = finalRevId,
				_conflict = true,
				test = 48,
				highway = 412,
			} )

		rcc:expectData( 5 )

		-- Now check results of :currentRevisionsForDocument.

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'test_1' ),

			{
				{
					_changeCounter = 5,
					_conflict = true,
					_id = "test_1",
					_localId = 1,
					_revId = finalRevId,
					highway = 412,
					test = 48,
				},
				{
					_changeCounter = 4,
					_conflict = true,
					_id = "test_1",
					_localId = 1,
					_revId = rev2ConflictId,
					test = 96,
				},
			} )

	end

	----------------------------------------------------------------------------

	function applyOverlayTests.nestedTable( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				test = 42,
			} )

		local rev2Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				blah = {
					a = 1,
					b = 2,
					[ "foo" ] = 42,
				}
			} )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev2Id,
				_changeCounter = 2,
				test = 42,
				blah = {
					a = 1,
					b = 2,
					[ "foo" ] = 42,
				},
			} )

	end

	----------------------------------------------------------------------------

	function applyOverlayTests.nestedTableReplacingScalar( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				test = 42,
				blah = 95,
			} )

		local rev2Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				blah = {
					a = 1,
					b = 2,
					[ "foo" ] = 42,
				}
			} )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev2Id,
				_changeCounter = 2,
				test = 42,
				blah = {
					a = 1,
					b = 2,
					[ "foo" ] = 42,
				},
			} )

	end

	----------------------------------------------------------------------------

	function applyOverlayTests.mergeNestedTable( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				test = 42,
				blah = {
					a = 1,
				}
			} )

		local rev2Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				blah = {
					b = 2,
					[ "foo" ] = 42,
				}
			} )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev2Id,
				_changeCounter = 2,
				test = 42,
				blah = {
					a = 1,
					b = 2,
					[ "foo" ] = 42,
				},
			} )

	end

	----------------------------------------------------------------------------

	function applyOverlayTests.deleteNestedTable( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				test = 42,
				blah = {
					a = 1,
					b = 2,
					[ "foo" ] = 42,
				}
			} )

		local rev2Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				blah = {
					_delete = true,
				}
			} )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev2Id,
				_changeCounter = 2,
				test = 42,
			} )

	end

	----------------------------------------------------------------------------

	function applyOverlayTests.deleteScalar( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				test = 42,
				blah = "zip",
			} )

		local rev2Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				blah = {
					_delete = true,
				}
			} )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev2Id,
				_changeCounter = 2,
				test = 42,
			} )

	end

	----------------------------------------------------------------------------

	function applyOverlayTests.deleteNothing( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local rev1Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				test = 42,
			} )

		applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				blah = {
					_delete = true,
				}
			} )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev1Id,
				_changeCounter = 1,
				test = 42,
			} )

	end

	----------------------------------------------------------------------------

	function applyOverlayTests.replaceExisting( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				test = 42,
				nested = {
					a = 1,
					b = 2,
					[ "foo" ] = 42,
				}
			} )

		local rev2Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				nested = {
					_replace = true,
					a = 1,
					b = 2,
					blah = 'c',
						-- Oz doesn't let us have both numeric and string keys in same table,
						-- so we enforce that rule here, too.
				},
			} )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev2Id,
				_changeCounter = 2,
				test = 42,
				nested = {
					a = 1,
					b = 2,
					blah = 'c',
				},
			} )

	end

	----------------------------------------------------------------------------

	function applyOverlayTests.replaceNothing( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				test = 42,
			} )

		local rev2Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				nested = {
					_replace = true,
					a = 1,
					b = 2,
					blah = 'c',
				},
			} )

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc' ),

			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev2Id,
				_changeCounter = 2,
				test = 42,
				nested = {
					a = 1,
					b = 2,
					blah = 'c',
				},
			} )

	end
	
	--------------------------------------------------------------------------------

	function applyOverlayTests.notAllowTypeChange( applyOverlayAndValidateRevId, rxTest )
	
		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				type = 'asset',
				test = 42,
			} )
			
		rxTest:expectOnlyError( 
		
			Observable.pcall( function()
			
				return docStore:applyOverlay {
					_id = 'testDoc',
					type = 'album',
				}
				
			end ), ':ignore:' )
		
	end
	
	--------------------------------------------------------------------------------

	function applyOverlayTests.notAllowTypeChangeViaBulk( applyOverlayAndValidateRevId, rxTest )
	
		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				type = 'asset',
				test = 42,
			} )
			
		rxTest:expectOnlyError( 
		
			Observable.pcall( function()
			
				return docStore:bulkApplyOverlay { {
					_id = 'testDoc',
					type = 'album',
				} }
				
			end ), ':ignore:' )
		
	end
	
	--------------------------------------------------------------------------------

	function applyOverlayTests.notAllowSubtypeChange( applyOverlayAndValidateRevId, rxTest )
	
		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				type = 'asset',
				subtype = 'image',
				test = 42,
			} )
			
		rxTest:expectOnlyError( 
		
			Observable.pcall( function()
			
				return docStore:applyOverlay {
					_id = 'testDoc',
					subtype = 'video',
				}
				
			end ), ':ignore:' )
		
	end
	
	--------------------------------------------------------------------------------

	function applyOverlayTests.notAllowSubtypeChangeViaBulk( applyOverlayAndValidateRevId, rxTest )
	
		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				type = 'asset',
				subtype = 'image',
				test = 42,
			} )
			
		rxTest:expectOnlyError( 
		
			Observable.pcall( function()
			
				return docStore:bulkApplyOverlay { {
					_id = 'testDoc',
					subtype = 'video',
				} }
				
			end ), ':ignore:' )
		
	end

	----------------------------------------------------------------------------

	function applyOverlayTests.afterConflictResolution( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local rev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				test = 42,
			} )

		local rev2Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				_revId = rev1Id,
				test = 44,
			} )

		local rev3Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				_revId = rev2Id,
				test = 48,
			} )

		local ongoingDocValue = docStore:ongoingDocValue( 'test_1' )
		
		local odv = rxTest:observe( ongoingDocValue )

		odv:expectData {
			_id = 'test_1',
			_localId = 1,
			_revId = rev3Id,
			_changeCounter = 3,
			test = 48,
		}

		local rev2ConflictId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue
				{
					{
						_id = 'test_1',
						_localId = 1,
						_revIds = { rev1Id, rev2ConflictId },
						test = 96,
					}
				},

			':ignore:' )

		odv:expectData {
			_id = 'test_1',
			_localId = 1,
			_revId = rev3Id,
			_conflict = true,
			_changeCounter = 4,
			test = 48,
		}

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'test_1' ),

			{
				{
					_changeCounter = 3,
					_conflict = true,
					_id = "test_1",
					_localId = 1,
					_revId = rev3Id,
					test = 48,
				},
				{
					_changeCounter = 4,
					_conflict = true,
					_id = "test_1",
					_localId = 1,
					_revId = rev2ConflictId,
					test = 96,
				},
			} )

		rxTest:expectSingleValue(
			docStore:deleteDoc( 'test_1', rev2ConflictId ),
			':ignore:' )

		odv:expectData {
			_id = 'test_1',
			_localId = 1,
			_revId = rev3Id,
			_changeCounter = 5,
			test = 48,
		}

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'test_1' ),

			{
				{
					_changeCounter = 3,
					_id = "test_1",
					_localId = 1,
					_revId = rev3Id,
					test = 48,
				},
			} )

		local rev4Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				nein = 'failedIntellectual',
			} )

		odv:expectData {
			_id = 'test_1',
			_localId = 1,
			_revId = rev4Id,
			_changeCounter = 6,
			test = 48,
			nein = 'failedIntellectual',
		}

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'test_1' ),

			{
				{
					_changeCounter = 6,
					_id = "test_1",
					_localId = 1,
					_revId = rev4Id,
					nein = "failedIntellectual",
					test = 48,
				},
			} )

	end
	
	--------------------------------------------------------------------------------

	function applyOverlayTests.specifyRevId( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )
		
		local revId = KSIdGenerator.uuidInLowercaseHex()
		
		local rev1Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				_newRevId = revId,
				test = 42,
			} )
			
		rxTest:expectEqual( revId, rev1Id )
			
	end

	--------------------------------------------------------------------------------

	function applyOverlayTests.specifyNewLabelAndPreserveLabels( applyOverlayAndValidateRevId, rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )
		local id = KSIdGenerator.uuidInLowercaseHex()


		-- Validate that we can set a new label via applyOverlay.

		local revId = KSIdGenerator.uuidInLowercaseHex()
		local rev1Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = id,
				_newRevId = revId,
				_newLabel = '_myTestLabel',
				test = 42,
			} )

		rxTest:expectEqual( revId, rev1Id )

		rxTest:expectSingleValue( docStore:docValue( id ), {
			_changeCounter = 1,
			_localId = 1,
			_id = id,
			_revId = revId,
			_labels = {
				_myTestLabel = true,
			},
			test = 42,
		} )


		-- Validate that we preserve all existing labels via applyOverlay.

		local revId2 = KSIdGenerator.uuidInLowercaseHex()
		local rev1Id2 = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = id,
				_newRevId = revId2,
				_preserveLabels = true,
				test = 43,
			} )

		rxTest:expectEqual( revId2, rev1Id2 )

		rxTest:expectSingleValue( docStore:docValue( id ), {
			_changeCounter = 2,
			_localId = 1,
			_id = id,
			_revId = revId2,
			_labels = {
				_myTestLabel = true,
			},
			test = 43,
		} )


		-- Validate that preserving labels and adding new labels via applyOverlay don't conflict.

		local revId3 = KSIdGenerator.uuidInLowercaseHex()
		local rev1Id3 = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = id,
				_newRevId = revId3,
				_preserveLabels = true,
				_newLabel = '_myNewTestLabel',
				test = 44,
			} )

		rxTest:expectEqual( revId3, rev1Id3 )

		rxTest:expectSingleValue( docStore:docValue( id ), {
			_changeCounter = 3,
			_localId = 1,
			_id = id,
			_revId = revId3,
			_labels = {
				_myTestLabel = true,
				_myNewTestLabel = true,
			},
			test = 44,
		} )

	end

	----------------------------------------------------------------------------
	-- Assemble applyOverlay and bulkApplyOverlay variants
	----------------------------------------------------------------------------

	for testName, fn in pairs( applyOverlayTests ) do
	
		RxDocStoreTestSuite[ 'rxTestApplyOverlay_' .. testName ] = function( rxTest )
			return fn( applyOverlayAndValidateRevId, rxTest )
		end
		
		RxDocStoreTestSuite[ 'rxTestBulkApplyOverlay_' .. testName ] = function( rxTest )
			return fn( bulkApplyOverlayToOneAndValidateRevId, rxTest )
		end
		
		-- The following list of tests have slightly different behavior for ApplyOverlayToIds
		-- because it does not allow overlays that contain certain specified fields such as
		-- rev ids, new ids, etc.
		
		local deniedListTestNamesForApplyOverlayToIds = {
			[ "noopOverlayWithNewRevId" ] = true,
			[ "specifyRevId" ] = true,
			[ "specifyNewLabelAndPreserveLabels" ] = true,
		}
		
		if not deniedListTestNamesForApplyOverlayToIds[ testName ] then
		
			RxDocStoreTestSuite[ 'rxTestApplyOverlayToIds_' .. testName ] = function( rxTest )
				return fn( applyOverlayToIdsToOneAndValidateRevId, rxTest )
			end
			
		end
		
	end

	----------------------------------------------------------------------------

	local function testApplyOverlay_rejects( testName, overlay )

		-- The following list of tests have slightly different behavior for ApplyOverlayToIds
		-- because it does not allow overlays that contain certain specified fields such as
		-- rev ids, new ids, etc.
		
		local ApplyOverlayToIdsOnlyTestNames = {
			[ "overlayContainsId" ] = true,
			[ "overlayContainsNewRevId" ] = true,
			[ "overlayContainsNewId" ] = true,
			[ "overlayContainsNewIdOverlay" ] = true,
		}

		if not ApplyOverlayToIdsOnlyTestNames[ testName ] then

			RxDocStoreTestSuite[ 'rxTestBulkApplyOverlayRejects_' .. testName ] = function( rxTest )

				rxTest:setTimeLimit( TEST_WAIT_TIME )

				local docStore = setUpDocStore( rxTest )

				rxTest:expectOnlyError(

					Observable.pcall( function()

						return docStore:bulkApplyOverlay{ overlay }

					end ),

					':ignore:' )

			end

			RxDocStoreTestSuite[ 'rxTestApplyOverlayRejects_' .. testName ] = function( rxTest )

				rxTest:setTimeLimit( TEST_WAIT_TIME )

				local docStore = setUpDocStore( rxTest )

				rxTest:expectOnlyError(

					Observable.pcall( function()

						return docStore:applyOverlay( overlay )

					end ),

					':ignore:' )

			end
		
		end
		
		
		RxDocStoreTestSuite[ 'rxTestApplyOverlayToIdsRejects_' .. testName ] = function( rxTest )

			rxTest:setTimeLimit( TEST_WAIT_TIME )

			local docStore = setUpDocStore( rxTest )

			rxTest:expectOnlyError(

				Observable.pcall( function()

					local id = overlay._id
					
					if testName ~= 'overlayContainsId' then
						overlay._id = nil
					end

					return docStore:applyOverlayToIds( overlay, { id } )

				end ),

				':ignore:' )

		end

	end

	testApplyOverlay_rejects( 'overlayIsNil', nil )

	testApplyOverlay_rejects( 'overlayIsBoolean', true )

	testApplyOverlay_rejects( 'overlayIsNumber', 42 )

	testApplyOverlay_rejects( 'missingId', {} )

	testApplyOverlay_rejects( 'idNotStringOrLocalInteger', { _id = false } )
	testApplyOverlay_rejects( 'negativeLocalId', { _id = -4 } )
	testApplyOverlay_rejects( 'zeroLocalId', { _id = 0 } )

	testApplyOverlay_rejects( 'slot',
		{
			_id = 'valid',
			_slot = 'not allowed',
		} )

	testApplyOverlay_rejects( 'rev',
		{
			_id = 'valid',
			_revId = '1-badbeef',
		} )

	testApplyOverlay_rejects( 'labels',
		{
			_id = 'valid',
			_labels = {
				label1 = true,
			},
		} )
	
	testApplyOverlay_rejects( 'overlayContainsId',
		{
			_id = 'valid',
		} )
	
	testApplyOverlay_rejects( 'overlayContainsNewRevId',
		{
			_id = 'valid',
			_newRevId = 'yummybeef'
		} )
		
	testApplyOverlay_rejects( 'overlayContainsNewId',
		{
			_id = 'valid',
			_newId = 'yummybeef'
		} )
		
	testApplyOverlay_rejects( 'overlayContainsNewIdOverlay',
		{
			_id = 'valid',
			_newIdOverlay = 'yummybeef'
		} )
	
	--------------------------------------------------------------------------------
	
	-- @mark local id <-> doc id translation
	
	--------------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestLocalIdToDocId( rxTest )
		
		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )
		
		rxTest:waitFor( docStore:setDocValue( {	
				_id = 'testDocId_1',
				test = 'foo'
			} ) )
		
		rxTest:expectOnlyCompleted( docStore:_DEBUG_clearIdCache() )
		
		rxTest:expectSingleValue( docStore:localIdToDocId( 1 ), 'testDocId_1' )
		
		rxTest:expectSingleValue( docStore:localIdToDocId( 2 ), nil )
		
		rxTest:expectOnlyError(

				Observable.pcall( function()
					return docStore:localIdToDocId( 'notanumber' )
				end ),
				':ignore:' )

		-- test includeDeleted		
		rxTest:waitFor( docStore:deleteDoc( 'testDocId_1' ) )
		
		rxTest:expectSingleValue( docStore:localIdToDocId( 1 ), nil )

		rxTest:expectSingleValue( docStore:localIdToDocId( 1, true ), 'testDocId_1' )
		
	end
	
	--------------------------------------------------------------------------------
	
	function RxDocStoreTestSuite.rxTestBulkLocalIdToDocId( rxTest )
		
		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )
		
		for i = 1, 10, 1 do
			rxTest:waitFor( docStore:setDocValue( {	
					_id = 'testDocId_' .. tostring(i),
					test = 'foo'
				} ) )
		end
		
		rxTest:expectOnlyCompleted( docStore:_DEBUG_clearIdCache() )
		
		rxTest:expectOnlyError(
			Observable.pcall( function()
				return docStore:bulkLocalIdToDocIdAsMap( 1 )
			end ),
			':ignore:' )
			
		rxTest:expectOnlyError(
			Observable.pcall( function()
				return docStore:bulkLocalIdToDocIdAsMap( 'notatable' )
			end ),
			':ignore:' )
		
		rxTest:expectOnlyError(
			Observable.pcall( function()
				return docStore:bulkLocalIdToDocIdAsMap( { 'notanumber' } )
			end ),
			':ignore:' )
		
		rxTest:expectSingleValue( 
			docStore:bulkLocalIdToDocIdAsMap( { 1, 3, 5, 7, 9 } ), 
				{ [1] = 'testDocId_1',
				  [3] = 'testDocId_3',
				  [5] = 'testDocId_5',
				  [7] = 'testDocId_7',
				  [9] = 'testDocId_9' } )
				  
		rxTest:expectSingleValue( docStore:bulkLocalIdToDocIdAsMap( {} ), {} )
		
		rxTest:expectSingleValue( docStore:bulkLocalIdToDocIdAsMap( { 1, 15 } ), { [1] = 'testDocId_1' } )

		-- test includeDeleted
		rxTest:waitFor( docStore:deleteDoc( 'testDocId_1' ) )
		
		rxTest:expectSingleValue( 
			docStore:bulkLocalIdToDocIdAsMap( { 1, 3, 5, 7, 9 } ), 
				{ [3] = 'testDocId_3',
				  [5] = 'testDocId_5',
				  [7] = 'testDocId_7',
				  [9] = 'testDocId_9' } )
		
		rxTest:expectSingleValue( 
			docStore:bulkLocalIdToDocIdAsMap( { 1, 3, 5, 7, 9 }, true ), 
				{ [1] = 'testDocId_1',
				  [3] = 'testDocId_3',
				  [5] = 'testDocId_5',
				  [7] = 'testDocId_7',
				  [9] = 'testDocId_9' } )
	end
	
	--------------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestDocIdToLocalId( rxTest )
		
		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )
		
		rxTest:waitFor( docStore:setDocValue( {	
				_id = 'testDocId_1',
				test = 'foo'
			} ) )
		
		rxTest:expectOnlyCompleted( docStore:_DEBUG_clearIdCache() )
		
		rxTest:expectSingleValue( docStore:docIdToLocalId( 'testDocId_1' ), 1 )
		
		rxTest:expectSingleValue( docStore:docIdToLocalId( 'testDocId_2' ), nil )
		
		rxTest:expectOnlyError(

				Observable.pcall( function()
					return docStore:docIdToLocalId( 1 ) -- not a string
				end ),
				':ignore:' )
		
		-- test includeDeleted
		rxTest:waitFor( docStore:deleteDoc( 'testDocId_1' ) )
		
		rxTest:expectSingleValue( docStore:docIdToLocalId( 'testDocId_1' ), nil )

		rxTest:expectSingleValue( docStore:docIdToLocalId( 'testDocId_1', true ), 1 )
		
	end
	
	--------------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestBulkDocIdToLocalId( rxTest )
	
		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )
		
		for i = 1, 10, 1 do
			rxTest:waitFor( docStore:setDocValue( {	
					_id = 'testDocId_' .. tostring(i),
					test = 'foo'
				} ) )
		end
		
		rxTest:expectOnlyCompleted( docStore:_DEBUG_clearIdCache() )
		
		rxTest:expectOnlyError(
			Observable.pcall( function()
				return docStore:bulkDocIdToLocalIdAsMap( 1 )
			end ),
			':ignore:' )
			
		rxTest:expectOnlyError(
			Observable.pcall( function()
				return docStore:bulkDocIdToLocalIdAsMap( 'notatable' )
			end ),
			':ignore:' )
		
		rxTest:expectOnlyError(
			Observable.pcall( function()
				return docStore:bulkDocIdToLocalIdAsMap( { 1 } )
			end ),
			':ignore:' )
		
		rxTest:expectSingleValue( 
			docStore:bulkDocIdToLocalIdAsMap( { 'testDocId_1', 'testDocId_3', 'testDocId_5', 'testDocId_7', 'testDocId_9' } ), 
				{ testDocId_1 = 1,
				  testDocId_3 = 3,
				  testDocId_5 = 5,
				  testDocId_7 = 7,
				  testDocId_9 = 9 } )
				  
		rxTest:expectSingleValue( docStore:bulkDocIdToLocalIdAsMap( {} ), {} )
		
		rxTest:expectSingleValue( docStore:bulkDocIdToLocalIdAsMap( { 'testDocId_1', 'testDocId_15' } ), { testDocId_1 = 1 } )
		
		-- test includeDeleted
		rxTest:waitFor( docStore:deleteDoc( 'testDocId_1' ) )
		
		rxTest:expectSingleValue( 
			docStore:bulkDocIdToLocalIdAsMap( { 'testDocId_1', 'testDocId_3', 'testDocId_5', 'testDocId_7', 'testDocId_9' } ), 
				{ testDocId_3 = 3,
				  testDocId_5 = 5,
				  testDocId_7 = 7,
				  testDocId_9 = 9 } )
				  
		rxTest:expectSingleValue( 
			docStore:bulkDocIdToLocalIdAsMap( { 'testDocId_1', 'testDocId_3', 'testDocId_5', 'testDocId_7', 'testDocId_9' }, true ), 
				{ testDocId_1 = 1,
				  testDocId_3 = 3,
				  testDocId_5 = 5,
				  testDocId_7 = 7,
				  testDocId_9 = 9 } )
	end
	
	----------------------------------------------------------------------------

	-- @mark change notification

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestChangeCounter( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local changeCounter = docStore:changeCounter()

		local rcc = rxTest:observe( changeCounter )
		rcc:expectData( 0 )

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		rcc:expectData( 1 )

		setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				rating = 3,
			} )

		rcc:expectData( 2 )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestChangeCounter_bulkApplyOverlayNop( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local changeCounter = docStore:changeCounter()

		local rcc = rxTest:observe( changeCounter )
		rcc:expectData( 0 )

		rxTest:expectSingleValue(

			docStore:applyOverlay {
				_id = 'test_1',
				blah = true,
			},

			':ignore:' )

		rcc:expectData( 1 )

		rxTest:expectSingleValue(

			docStore:bulkApplyOverlay {
				{
					_id = 'test_2',
					rating = 3,
				},
				{
					_id = 'test_1',
					blah = true, -- intentional no-op
				},
			},

			':ignore:' )

		rcc:expectData( 2 )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestChangeCounterHappensOutsideOfTransaction( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local changeCounter = docStore:changeCounter()

		local rcc = rxTest:observe( changeCounter )
		rcc:expectData( 0 )

		-- This isn't something you'd normally do, but it serves as a proxy
		-- for other things that might be a bit more reasonable. Here we want
		-- to ensure that the change counter gets updated outside of a transaction
		-- so we don't attempt to create a nested transaction.

		local disposable 
	
		local disposeFn = function()
			
			disposable:dispose()
			disposable = nil
			
		end
		
		disposable = docStore:changeCounter()
			:subscribe( function( ccValue )

				-- When the change counter updates, immediately try to
				-- do another operation. If we're still inside the transaction,
				-- this will cause an error.

				if ccValue == 1 then
					docStore:setDocValue {
						_id = 'testDoc2',
						rating = 3,
					}
				end

			end,
			disposeFn,
			disposeFn )

		setDocValue_firstRevTest42( rxTest, docStore )

		rcc:expectData( 1 )

		rcc:expectData( 2 )

	end

	----------------------------------------------------------------------------

	-- TODO [scouten 2015-08-05]: Add test to verify that we can subscribe to
	-- this multiple times.

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestChangeCounterOnce( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		rxTest:expectSingleValue( docStore:changeCounterOnce(), 0 )

		local firstRevId = setDocValue_firstRevTest42( rxTest, docStore )

		rxTest:expectSingleValue( docStore:changeCounterOnce(), 1 )

		setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = firstRevId,
				rating = 3,
			} )

		rxTest:expectSingleValue( docStore:changeCounterOnce(), 2 )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestChangedDocs( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Write two documents.

		local testDocRev1Id = setDocValue_firstRevTest42( rxTest, docStore )

		local anotherDocRev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'another_photo',
				favorite = true,
				title = "My Nifty Photo",
			} )

		-- Read them back via :changedDocs.

		rxTest:expectSingleValue(

			docStore:changedDocs { fromChangeCounter = 1 },

			{

				{
					_id = 'testDoc',
					_localId = 1,
					_changeCounter = 1,
					_revId = testDocRev1Id,
					test = 42,
				},

				{
					_id = 'another_photo',
					_localId = 2,
					_changeCounter = 2,
					_revId = anotherDocRev1Id,
					favorite = true,
					title = "My Nifty Photo",
				},

				continueFrom = 3,

			} )

		-- Read them back via :changedDocs with limit of 1.

		rxTest:expectSingleValue(

			docStore:changedDocs { fromChangeCounter = 1, limit = 1 },

			{

				{
					_id = 'testDoc',
					_localId = 1,
					_changeCounter = 1,
					_revId = testDocRev1Id,
					test = 42,
				},

				continueFrom = 2,

			} )

		-- Read them back via :changedDocs with limit that's too large.
		-- Should still just give us what it's willing to give us.

		rxTest:expectSingleValue(

			docStore:changedDocs { fromChangeCounter = 1, limit = 1000000 },

			{

				{
					_id = 'testDoc',
					_localId = 1,
					_changeCounter = 1,
					_revId = testDocRev1Id,
					test = 42,
				},

				{
					_id = 'another_photo',
					_localId = 2,
					_changeCounter = 2,
					_revId = anotherDocRev1Id,
					favorite = true,
					title = "My Nifty Photo",
				},

				continueFrom = 3,

			} )

		-- Change one of the docs again.

		local testDocRev2Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = testDocRev1Id,
				test = 53,
			} )

		-- Read this change (only) back via :changedDocs.

		rxTest:expectSingleValue(

			docStore:changedDocs { fromChangeCounter = 3 },

			{

				{
					_id = 'testDoc',
					_localId = 1,
					_changeCounter = 3,
					_revId = testDocRev2Id,
					test = 53,
				},

				continueFrom = 4,

			} )

		-- Now delete one of the docs.

		local testDocRev3Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = testDocRev2Id,
				_deleted = true,
			} )

		-- Read this change (only) back via :changedDocs.

		rxTest:expectSingleValue(

			docStore:changedDocs { fromChangeCounter = 4 },

			{

				{
					_id = 'testDoc',
					_localId = 1,
					_changeCounter = 4,
					_deleted = true,
					_revId = testDocRev3Id,
				},

				continueFrom = 5,

			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestChangedDocs_filterByTypeSubtypeMap( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Write two documents.

		local testDocRevId = setDocValue_firstRevTest42( rxTest, docStore )

		local imageDocRev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'another_photo',
				title = "My Nifty Photo",
				type = 'asset',
				subtype = 'image'
			} )

		local videoDocRev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'another_video',
				title = "My Nifty Video",
				type = 'asset',
				subtype = 'video'
			} )

		local albumAssetDocRev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'anAlbumAsset',
				title = "My Nifty Album Asset",
				type = 'album_asset'
			} )

		-- Read them back via :changedDocs filtering for only images.

		rxTest:expectSingleValue(

			docStore:changedDocs { 
				fromChangeCounter = 1,
				filterByTypeSubtypeMap = {
					[ 'asset' ] = { 'image' }
				}
			},

			{
				{
					_id = 'another_photo',
					_localId = 2,
					_changeCounter = 2,
					_revId = imageDocRev1Id,
					title = "My Nifty Photo",
					type = 'asset',
					subtype = 'image'
				},

				continueFrom = 5,

			} )

		-- Read them back via :changedDocs filtering for only videos.

		rxTest:expectSingleValue(

			docStore:changedDocs { 
				fromChangeCounter = 1,
				filterByTypeSubtypeMap = {
					[ 'asset' ] = { 'video' }
				}
			},

			{
				{
					_id = 'another_video',
					_localId = 3,
					_changeCounter = 3,
					_revId = videoDocRev1Id,
					title = "My Nifty Video",
					type = 'asset',
					subtype = 'video'
				},
				
				continueFrom = 5,

			} )

		-- Read them back via :changedDocs filters for image and video assets

		rxTest:expectSingleValue(

			docStore:changedDocs { 
				fromChangeCounter = 1,
				filterByTypeSubtypeMap = {
					[ 'asset' ] = { 'image', 'video' }
				}
			},

			{
				{
					_id = 'another_photo',
					_localId = 2,
					_changeCounter = 2,
					_revId = imageDocRev1Id,
					title = "My Nifty Photo",
					type = 'asset',
					subtype = 'image'
				},

				{
					_id = 'another_video',
					_localId = 3,
					_changeCounter = 3,
					_revId = videoDocRev1Id,
					title = "My Nifty Video",
					type = 'asset',
					subtype = 'video'
				},

				continueFrom = 5,

			} )

		-- Read them back via :changedDocs filters for all assets

		rxTest:expectSingleValue(

			docStore:changedDocs { 
				fromChangeCounter = 1,
				filterByTypeSubtypeMap = {
					[ 'asset' ] = { '*' }
				}
			},

			{
				{
					_id = 'another_photo',
					_localId = 2,
					_changeCounter = 2,
					_revId = imageDocRev1Id,
					title = "My Nifty Photo",
					type = 'asset',
					subtype = 'image'
				},

				{
					_id = 'another_video',
					_localId = 3,
					_changeCounter = 3,
					_revId = videoDocRev1Id,
					title = "My Nifty Video",
					type = 'asset',
					subtype = 'video'
				},

				continueFrom = 5,

			} )

		-- Read them back via :changedDocs filters for all album assets

		rxTest:expectSingleValue(

			docStore:changedDocs { 
				fromChangeCounter = 1,
				filterByTypeSubtypeMap = {
					[ 'album_asset' ] = { 'NULL' }
				}
			},

			{
				{
					_id = 'anAlbumAsset',
					_localId = 4,
					_changeCounter = 4,
					_revId = albumAssetDocRev1Id,
					title = "My Nifty Album Asset",
					type = 'album_asset',
				},
				
				continueFrom = 5,

			} )

		-- Read them back via :changedDocs filters for all docs

		rxTest:expectSingleValue(

			docStore:changedDocs { 
				fromChangeCounter = 1,
				filterByTypeSubtypeMap = {
					[ '*' ] = { '*' }
				}
			},

			{
				{
					_changeCounter = 1,
					_id = "testDoc",
					_localId = 1,
					_revId = testDocRevId,
					test = 42,
				},
				{
					_changeCounter = 2,
					_id = "another_photo",
					_localId = 2,
					_revId = imageDocRev1Id,
					subtype = "image",
					title = "My Nifty Photo",
					type = "asset",
				},
				{
					_changeCounter = 3,
					_id = "another_video",
					_localId = 3,
					_revId = videoDocRev1Id,
					subtype = "video",
					title = "My Nifty Video",
					type = "asset",
				},
				{
					_changeCounter = 4,
					_id = "anAlbumAsset",
					_localId = 4,
					_revId = albumAssetDocRev1Id,
					title = "My Nifty Album Asset",
					type = "album_asset",
				},
				continueFrom = 5,
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestChangedDocs_IncludeAndExcludeSpaceIdDocs( rxTest )

		local WFRxDocStore = require 'WFRxDocStore'

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Write two documents, one with a space Id and one without.

		local spaceId = KSIdGenerator.uuidInLowercaseHex()

		local docWithSpaceIdRev = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'another_photo',
				favorite = true,
				title = "My Nifty Photo",
				_spaceId = spaceId
			} )

		local docWithoutSpaceIdRev = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'another_photo2',
				favorite = true,
				title = "My Nifty Photo 2",
			} )

		-- Read them back via :changedDocs. By default, both documents
		-- should be included

		rxTest:expectSingleValue(

			docStore:changedDocs { fromChangeCounter = 1 },

			{
				{
					_id = 'another_photo',
					_localId = 1,
					_changeCounter = 1,
					_revId = docWithSpaceIdRev,
					favorite = true,
					title = "My Nifty Photo",
					_spaceId = spaceId
				},
				
				{
					_id = 'another_photo2',
					_localId = 2,
					_changeCounter = 2,
					_revId = docWithoutSpaceIdRev,
					favorite = true,
					title = "My Nifty Photo 2",
				},

				continueFrom = 3,

			} )

		-- Read them back via :changedDocs with includeDocumentsFromSource
		-- set to catalog.

		rxTest:expectSingleValue(

			docStore:changedDocs { 
				fromChangeCounter = 1, 
				includeDocumentsFromSource = WFRxDocStore.changedDocsSourceCatalogDocuments
			},
			{
				{
					_id = 'another_photo2',
					_localId = 2,
					_changeCounter = 2,
					_revId = docWithoutSpaceIdRev,
					favorite = true,
					title = "My Nifty Photo 2",
				},

				continueFrom = 3,

			} )

		-- Read them back via :changedDocs with includeDocumentsFromSource
		-- set to all. We should receive both documents.

		rxTest:expectSingleValue(

			docStore:changedDocs { 
				fromChangeCounter = 1, 
				includeDocumentsFromSource = WFRxDocStore.changedDocsSourceAllDocuments
			},
			{
				{
					_id = 'another_photo',
					_localId = 1,
					_changeCounter = 1,
					_revId = docWithSpaceIdRev,
					favorite = true,
					title = "My Nifty Photo",
					_spaceId = spaceId
				},
				
				{
					_id = 'another_photo2',
					_localId = 2,
					_changeCounter = 2,
					_revId = docWithoutSpaceIdRev,
					favorite = true,
					title = "My Nifty Photo 2",
				},

				continueFrom = 3,

			} )

		-- Read them back via :changedDocs with includeDocumentsFromSource
		-- set to space feed only.
		
		rxTest:expectSingleValue(

			docStore:changedDocs { 
				fromChangeCounter = 1, 
				includeDocumentsFromSource = WFRxDocStore.changedDocsSourceSpaceFeedDocuments,
			},
			{
				{
					_id = 'another_photo',
					_localId = 1,
					_changeCounter = 1,
					_revId = docWithSpaceIdRev,
					favorite = true,
					title = "My Nifty Photo",
					_spaceId = spaceId
				},

				continueFrom = 3,

			} )

		-- Read them back via :changedDocs with includeDocumentsFromSource
		-- set to an invalid value. The default of both should be used.

		rxTest:expectSingleValue(

			docStore:changedDocs { 
				fromChangeCounter = 1, 
				includeDocumentsFromSource = "Foo",
			},
			{
			
				{
					_id = 'another_photo',
					_localId = 1,
					_changeCounter = 1,
					_revId = docWithSpaceIdRev,
					favorite = true,
					title = "My Nifty Photo",
					_spaceId = spaceId
				},
				{
					_id = 'another_photo2',
					_localId = 2,
					_changeCounter = 2,
					_revId = docWithoutSpaceIdRev,
					favorite = true,
					title = "My Nifty Photo 2",
				},

				continueFrom = 3,

			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestChangedDocs_labelsWhenInConflict( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local rev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				test = 42,
				_labels = {
					foo = true
				}
			} )

		local rev2Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				_revId = rev1Id,
				test = 44,
				_labels = {
					bar = true
				}
			} )

		local rev3Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				_revId = rev2Id,
				test = 48,
				_labels = {
					baz = true
				}
			} )

		rxTest:expectSingleValue(

			docStore:changedDocs { fromChangeCounter = 1 },

			{

				continueFrom = 4,

				{
					_id = 'test_1',
					_localId = 1,
					_labels = {
						baz = true,
					},
					_revId = rev3Id,
					_changeCounter = 3,
					test = 48,
				}

			} )

		-- Now introduce a conflict and make sure :changedDocs
		-- reads it back correctly.

		local rev2ConflictId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'test_1',
					_localId = 1,
					_revIds = { rev1Id, rev2ConflictId },
					test = 96,
					_labels = {
						zab = true
					}
				}
			},

			':ignore:' )

		rxTest:expectSingleValue(

			docStore:changedDocs { fromChangeCounter = 1 },

			{

				continueFrom = 5,

				{
					_id = 'test_1',
					_localId = 1,
					_labels = {
						baz = true,
					},
					_revId = rev3Id,
					_conflict = true,
					_changeCounter = 4,
					test = 48,
				}

			} )

		-- Now check results of :currentRevisionsForDocument.

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'test_1' ),

			{
				{
					_changeCounter = 3,
					_conflict = true,
					_id = "test_1",
					_localId = 1,
					_revId = rev3Id,
					test = 48,
				},
				{
					_changeCounter = 4,
					_conflict = true,
					_id = "test_1",
					_localId = 1,
					_revId = rev2ConflictId,
					test = 96,
				},
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestChangedDocs_conflictAddedLater( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local rev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				test = 42,
			} )

		local rev2Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				_revId = rev1Id,
				test = 44,
			} )

		local rev3Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				_revId = rev2Id,
				test = 48,
			} )

		rxTest:expectSingleValue(

			docStore:changedDocs { fromChangeCounter = 1 },

			{

				continueFrom = 4,

				{
					_id = 'test_1',
					_localId = 1,
					_revId = rev3Id,
					_changeCounter = 3,
					test = 48,
				}

			} )

		-- Now introduce a conflict and make sure :changedDocs
		-- reads it back correctly.

		local rev2ConflictId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'test_1',
					_localId = 1,
					_revIds = { rev1Id, rev2ConflictId },
					test = 96,
				}
			},

			':ignore:' )

		rxTest:expectSingleValue(

			docStore:changedDocs { fromChangeCounter = 1 },

			{

				continueFrom = 5,

				{
					_id = 'test_1',
					_localId = 1,
					_revId = rev3Id,
					_conflict = true,
					_changeCounter = 4,
					test = 48,
				}

			} )

		-- Now check results of :currentRevisionsForDocument.

		rxTest:expectSingleValue(

			docStore:currentRevisionsForDoc( 'test_1' ),

			{
				{
					_changeCounter = 3,
					_conflict = true,
					_id = "test_1",
					_localId = 1,
					_revId = rev3Id,
					test = 48,
				},
				{
					_changeCounter = 4,
					_conflict = true,
					_id = "test_1",
					_localId = 1,
					_revId = rev2ConflictId,
					test = 96,
				},
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestChangedDocs_empty( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		rxTest:expectSingleValue(

			docStore:changedDocs { fromChangeCounter = 1 },

			{ continueFrom = 1 } )

	end

	----------------------------------------------------------------------------

	local function testChangedDocs_rejects( testName, argList )

		RxDocStoreTestSuite[ 'rxTestChangedDocsRejects_' .. testName ] = function( rxTest )

			rxTest:setTimeLimit( TEST_WAIT_TIME )

			local docStore = setUpDocStore( rxTest )

			rxTest:expectOnlyError(

				Observable.pcall( function()

					return docStore:changedDocs( argList )

				end ),

				':ignore:' )

		end

	end

	testChangedDocs_rejects( 'nil', nil )

	testChangedDocs_rejects( 'number', 42 )

	testChangedDocs_rejects( 'emptyTable', {} )

	testChangedDocs_rejects( 'invalidFrom', { fromChangeCounter = -42 } )

	testChangedDocs_rejects( 'invalidLimit', { fromChangeCounter = 1, limit = 0 } )

	testChangedDocs_rejects( 'invalidLimitBogus', { fromChangeCounter = 1, limit = 'bogus' } )
	
	-- SPAM [jandrew 2021-09-30]: Additional testing of `changedDocs` can be found
	-- within WFRxIndexStoreTest, as reindexing is strongly driven by the implementation.

	----------------------------------------------------------------------------

--[x[ -- TODO [scouten 2014-08-16]: ALSO NEED TO VERIFY CONFLICT RESOLUTION ]x]--

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestChangesFeed( rxTest )

		if not options.testChangesFeed then

			return

		end

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Write two documents.

		local testDocRev1Id = setDocValue_firstRevTest42( rxTest, docStore )

		local anotherDocRev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'another_photo',
				favorite = true,
				title = "My Nifty Photo",
			} )

		-- Read them back via :_raw_changesFeed.

		local feed = rxTest:observe( docStore:_raw_changesFeed( 0 ) )

		feed:expectData {
			_id = 'testDoc',
			_localId = 1,
			_changeCounter = 1,
			_revId = testDocRev1Id,
			test = 42,
		}

		feed:expectData {
			_id = 'another_photo',
			_localId = 2,
			_changeCounter = 2,
			_revId = anotherDocRev1Id,
			favorite = true,
			title = "My Nifty Photo",
		}

		-- Change one of the docs again.

		local testDocRev2Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = testDocRev1Id,
				test = 53,
			} )

		-- Read this change (only) back via feed.

		feed:expectData {
			_id = 'testDoc',
			_localId = 1,
			_changeCounter = 3,
			_revId = testDocRev2Id,
			test = 53,
		}

		-- Now delete one of the docs.

		local testDocRev3Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = testDocRev2Id,
				_deleted = true,
			} )

		-- Read this change (only) back via feed.

		feed:expectData {
			_id = 'testDoc',
			_localId = 1,
			_changeCounter = 4,
			_deleted = true,
			_revId = testDocRev3Id,
		}

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestChangesFeed( rxTest )

		if not options.testChangesFeed then

			return

		end

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Write two documents.

		local testDocRev1Id = setDocValue_firstRevTest42( rxTest, docStore )

		local anotherDocRev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'another_photo',
				favorite = true,
				title = "My Nifty Photo",
			} )

		-- Read only the second document back via :_raw_changesFeed.

		local feed = rxTest:observe( docStore:_raw_changesFeed( 2 ) )

		feed:expectData {
			_id = 'another_photo',
			_localId = 2,
			_changeCounter = 2,
			_revId = anotherDocRev1Id,
			favorite = true,
			title = "My Nifty Photo",
		}

		-- Change one of the docs again.

		local testDocRev2Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = testDocRev1Id,
				test = 53,
			} )

		-- Read this change (only) back via feed.

		feed:expectData {
			_id = 'testDoc',
			_localId = 1,
			_changeCounter = 3,
			_revId = testDocRev2Id,
			test = 53,
		}

		-- Now delete one of the docs.

		local testDocRev3Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = testDocRev2Id,
				_deleted = true,
			} )

		-- Read this change (only) back via feed.

		feed:expectData {
			_id = 'testDoc',
			_localId = 1,
			_changeCounter = 4,
			_deleted = true,
			_revId = testDocRev3Id,
		}

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestChangesFeed_conflict( rxTest )

		if not options.testChangesFeed then

			return

		end

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local rev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				test = 42,
			} )

		local rev2Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				_revId = rev1Id,
				test = 44,
			} )

		local rev3Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				_revId = rev2Id,
				test = 48,
			} )

		local feed = rxTest:observe( docStore:_raw_changesFeed( 0 ) )

		feed:expectData {
			_id = 'test_1',
			_localId = 1,
			_revId = rev3Id,
			_changeCounter = 3,
			test = 48,
		}

		-- Now introduce a conflict and make sure feed
		-- reads it back correctly.

		local rev2ConflictId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue(

			docStore:bulkSetDocValue {
				{
					_id = 'test_1',
					_localId = 1,
					_revIds = { rev1Id, rev2ConflictId },
					test = 96,
				}
			},

			':ignore:' )

		feed:expectData {
			_id = 'test_1',
			_localId = 1,
			_revId = rev3Id,
			_conflict = true,
			_changeCounter = 4,
			test = 48,
		}

		-- Now resolve the conflict and make sure feed updates correctly.

		rxTest:expectSingleValue(
			docStore:deleteDoc( 'test_1', rev2ConflictId ),
			':ignore:' )

		feed:expectData {
			_id = 'test_1',
			_localId = 1,
			_revId = rev3Id,
			_changeCounter = 5,
			test = 48,
		}

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestChangesFeed_emptyStart( rxTest )

		if not options.testChangesFeed then

			return

		end

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local feed = rxTest:observe( docStore:_raw_changesFeed() )
			-- Should have no values yet. (How to test for nothing happens?)

		-- Write two documents and read them back via feed.

		local testDocRev1Id = setDocValue_firstRevTest42( rxTest, docStore )

		feed:expectData {
			_id = 'testDoc',
			_localId = 1,
			_changeCounter = 1,
			_revId = testDocRev1Id,
			test = 42,
		}

		local anotherDocRev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'another_photo',
				favorite = true,
				title = "My Nifty Photo",
			} )

		feed:expectData {
			_id = 'another_photo',
			_localId = 2,
			_changeCounter = 2,
			_revId = anotherDocRev1Id,
			favorite = true,
			title = "My Nifty Photo",
		}

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestDocRevisionHistory( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		rxTest:expectSingleValue(

			docStore:docRevisionHistory( 'testDoc' ),
			{} )

		local rev1Id = setDocValue_firstRevTest42( rxTest, docStore )

		local expectedHistoryAfterRev1 = {
			{
				revId = rev1Id,
				status = 'available',
			},
		}

		rxTest:expectSingleValue(
			docStore:docRevisionHistory( 'testDoc' ),
			expectedHistoryAfterRev1 )

		rxTest:expectSingleValue(
			docStore:docRevisionHistory( 'testDoc', rev1Id ),
			expectedHistoryAfterRev1 )

		local rev2Id = applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				timeToDest = '04:23',
			} )

		local expectedHistoryAfterRev2 = {
			{
				revId = rev2Id,
				status = 'available',
			},
			{
				revId = rev1Id,
				status = 'available',
			},
		}

		rxTest:expectSingleValue(
			docStore:docRevisionHistory( 'testDoc' ),
			expectedHistoryAfterRev2 )

		rxTest:expectSingleValue(
			docStore:docRevisionHistory( 'testDoc', rev2Id ),
			expectedHistoryAfterRev2 )

		rxTest:expectSingleValue(
			docStore:docRevisionHistory( 'testDoc', rev1Id ),
			expectedHistoryAfterRev1 )

		-- apply local only overlay
		applyOverlayAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_localOnly = {
					timeToDest = '04:23',
				}
			} )
			
		local expectedHistoryAfterLocalOnlyUpdate = {
			{ 	
				revId = rev2Id,
				status = 'available',
			},
			{
				revId = rev2Id,
				status = 'available',
			},
			{
				revId = rev1Id,
				status = 'available',
			},
		}
		
		rxTest:expectSingleValue(
			docStore:docRevisionHistory( 'testDoc' ),
			expectedHistoryAfterLocalOnlyUpdate )
		
		local rev3Id
		
		rxTest:expectSingleValue(

			docStore:deleteDoc( 'testDoc' )
				:tap( function( rev )
					rev3Id = rev
				end ),

			':ignore:' )
		
		rxTest:expectSingleValue(

			docStore:docRevisionHistory( 'testDoc' ),

			{
				{
					revId = rev3Id,
					status = 'deleted',
				},
				{ 	
					revId = rev2Id,
					status = 'available',
				},
				{
					revId = rev2Id,
					status = 'available',
				},
				{
					revId = rev1Id,
					status = 'available',
				},
			} )

	end

	----------------------------------------------------------------------------

	-- @mark local (non-syncing) documents

	----------------------------------------------------------------------------

	local function testGetSetLocalDocValue_accepts( testName, docValue )

		RxDocStoreTestSuite[ 'rxTestGetSetLocalDocValueAccepts_' .. testName ] = function( rxTest )

			rxTest:setTimeLimit( TEST_WAIT_TIME )

			local docStore = setUpDocStore( rxTest )

			local myDocValue = KSTableUtils.deepCopy( docValue )

			local docId = myDocValue._id

			local expectedValue = KSTableUtils.deepCopy( myDocValue )

			-- Set doc value.

			rxTest:expectOnlyCompleted( docStore:setLocalDocValue( myDocValue ) )

			-- And read it back.

			rxTest:expectSingleValue( docStore:localDocValue( docId ), expectedValue )

		end

	end

	----------------------------------------------------------------------------

	local function testSetLocalDocValue_rejects( testName, docValue )

		RxDocStoreTestSuite[ 'rxTestSetLocalDocValueRejects_' .. testName ] = function( rxTest )

			rxTest:setTimeLimit( TEST_WAIT_TIME )

			local docStore = setUpDocStore( rxTest )

			rxTest:expectOnlyError(

				Observable.pcall( function()
					return docStore:setLocalDocValue( docValue )
				end ),

				':ignore:' )

		end

	end

	----------------------------------------------------------------------------

	local function testGetSetLocalDocValue_acceptsDocId( testName, docId )

		testGetSetLocalDocValue_accepts( 'docId_' .. testName, { _id = docId, test = 42 } )

	end

	testGetSetLocalDocValue_acceptsDocId( 'testDoc', 'testDoc' )

	----------------------------------------------------------------------------

	local function testSetLocalDocValue_rejectsDocId( testName, docId )

		testSetLocalDocValue_rejects( 'docId_' .. testName, { _id = docId, test = 42 } )

	end

	testSetLocalDocValue_rejectsDocId( 'nil', nil )

	testSetLocalDocValue_rejectsDocId( 'boolean', true )

	testSetLocalDocValue_rejectsDocId( 'number', -42 )

	testSetLocalDocValue_rejectsDocId( 'table', {} )

	if options.testNonTransitableData then
		testSetLocalDocValue_rejectsDocId( 'function', function() end )
	end

	testSetLocalDocValue_rejectsDocId( 'emptyString', '' )

	testSetLocalDocValue_rejectsDocId( 'underscore', '_' )

	testSetLocalDocValue_rejectsDocId( 'changes', '_changes' )

	testSetLocalDocValue_rejectsDocId( 'compact', '_compact' )

	testSetLocalDocValue_rejectsDocId( '3xunderscore', '___' )

	testSetLocalDocValue_rejectsDocId( 'design', '_design' )

	testSetLocalDocValue_rejectsDocId( 'designSlash', '_design/' )
	testSetLocalDocValue_rejectsDocId( 'designDoc', '_design/1' )
	testSetLocalDocValue_rejectsDocId( 'designDoc', '_design/blah' )

	testSetLocalDocValue_rejectsDocId( 'controlChar', 'abc\ndef' )

	testSetLocalDocValue_rejectsDocId( 'local', '_local' )
	testSetLocalDocValue_rejectsDocId( 'localSlash', '_local/' )
	testSetLocalDocValue_rejectsDocId( 'localFoo', '_local/foo' )

	----------------------------------------------------------------------------

	local function testGetSetLocalDocValue_acceptsDocValue( testName, docValue )

		docValue._id = 'testDoc'
		testGetSetLocalDocValue_accepts( 'docValue_' .. testName, docValue )

	end

	-- We are also accepting CouchDB syncing doc semantics for doc values.

	testGetSetLocalDocValue_acceptsDocValue( 'emptyTable', {} )

	testGetSetLocalDocValue_acceptsDocValue( 'emptyStringKey', { [ '' ] = 'empty string' } )

	testGetSetLocalDocValue_acceptsDocValue( 'nestedArray',
		{
			_id = 'nestedArray',
			nested = {
				"ein", "zwei", 3, "four", "five", true
			}
		} )

	testGetSetLocalDocValue_acceptsDocValue( 'nestedArrayAndNestedHash',
		{
			_id = 'nestedArrayAndNestedHash',
			test = 42,
			array = { 'a', 'b', 'c' },
			hash = { a = 1, b = 2, c = 3 },
		} )

	testGetSetLocalDocValue_acceptsDocValue( 'gappyArray',
		{
			_id = 'gappyArray',
			nested = { 'one', nil, 'three' }
		} )

	testGetSetLocalDocValue_acceptsDocValue( 'nestedUnderscoreKey',
		{
			_id = 'nestedUnderscoreKey',
			nested = {
				_blah = 42
					-- This wasn't legal in WFDocStore, but is legal in WFRxDocStore.
			},
		} )

	testGetSetLocalDocValue_acceptsDocValue( 'nestedEmptyStringKey',
		{
			_id = 'nestedEmptyStringKey',
			nested = {
				[ '' ] = 42
					-- This wasn't legal in WFDocStore, but is legal in WFRxDocStore.
			},
		} )

	----------------------------------------------------------------------------

	local function testSetLocalDocValue_rejectsDocValue( testName, docValue, suppressDocId )

		if not suppressDocId then
			docValue._id = 'testDoc'
		end

		testSetLocalDocValue_rejects( 'docValue_' .. testName, docValue )

	end

	testSetLocalDocValue_rejectsDocValue( 'nil', nil, true )

	testSetLocalDocValue_rejectsDocValue( 'number', 42, true )

	testSetLocalDocValue_rejectsDocValue( 'noId', {}, true )

	if options.testNonTransitableData then
		testSetLocalDocValue_rejectsDocValue( 'metatable', setmetatable( { test = 42 }, {} ) )
	end

	testSetLocalDocValue_rejectsDocValue( 'topLevelUnderscore', { _blah = 42 } )

	testSetLocalDocValue_rejectsDocValue( 'deletedFalse', { _deleted = false } )

	testSetLocalDocValue_rejectsDocValue( 'deletedNumber', { _deleted = 43 } )

	testSetLocalDocValue_rejectsDocValue( 'deletedTable', { _deleted = {} } )

	testSetLocalDocValue_rejectsDocValue( 'rev', { _revId = '1-2345' } )
		-- Disallow versioning concept.

	testSetLocalDocValue_rejectsDocValue( 'deleted', { _deleted = true } )
		-- For now, local docs can't be deleted.

	testSetLocalDocValue_rejectsDocValue( 'slot', { _slot = 'main' } )
		-- Disallow WFDocStore concept.

	testSetLocalDocValue_rejectsDocValue( 'hash', { _hash = 'abcdef' } )
		-- Disallow WFDocStore concept.

	testSetLocalDocValue_rejectsDocValue( 'revIdNotNumber', { _revId = 42 } )

	testSetLocalDocValue_rejectsDocValue( 'topLevelNumericKey', { [ 42 ] = 'not a string' } )

	testSetLocalDocValue_rejectsDocValue( 'topLevelBooleanKey', { [ true ] = 'not a string' } )

	if options.testNonTransitableData then
		testSetLocalDocValue_rejectsDocValue( 'func', { func = function() end } )
	end

	testSetLocalDocValue_rejectsDocValue( 'nestedBoolKey',
		{
			nested = {
				[ true ] = 'enforce non-string/number rule in nested tables',
			},
		} )

	testSetLocalDocValue_rejectsDocValue( 'deeplyNestedBoolKey',
		{
			nested = {
				nested2 = {
					[ true ] = 'enforce non-string/number rule in deeply nested tables',
				},
			},
		} )

	testSetLocalDocValue_rejectsDocValue( 'negativeNumberAsKey',
		{
			nested = {
				[ -1 ] = "no negative array indices"
			}
		} )

	testSetLocalDocValue_rejectsDocValue( 'mixedArrayAndHash',
		{
			nested = { 1, 2, 3, four = "not allowed" }
		} )

	testSetLocalDocValue_rejectsDocValue( 'zeroAsKey',
		{
			nested = {
				[ 0 ] = "no negative array indices"
			}
		} )

	testSetLocalDocValue_rejectsDocValue( 'nonIntegerAsKey',
		{
			nested = {
				[ 1.745 ] = "no non-integer indices"
			}
		} )

	testSetLocalDocValue_rejectsDocValue( 'infinityAsKey',
		{
			nested = {
				[ 1 / 0 ] = "to infinity and beyond, but not in doc values"
			}
		} )

	testSetLocalDocValue_rejectsDocValue( 'negativeInfinityAsKey',
		{
			nested = {
				[ -1 / 0 ] = "to negative infinity and beyond, but not in doc values"
			}
		} )

	testSetLocalDocValue_rejectsDocValue( 'infinityAsValue',
		{
			a = 1 / 0, -- infinity not allowed as value (?)
		} )

	testSetLocalDocValue_rejectsDocValue( 'negativeInfinityAsValue',
		{
			a = -1 / 0, -- negative infinity not allowed as value (?)
		} )

	testSetLocalDocValue_rejectsDocValue( 'NaNAsValue',
		{
			a = 0 / 0, -- NaN not allowed as value (?)
		} )

	-- Note that Lua doesn't allow you to construct a table with 0/0 as a key, so
	-- there's no need to explicitly test that case here.

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetLocalDocValue_empty( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		rxTest:expectSingleValue(

			docStore:localDocValue( 'noSuchDoc' ),

			{
				_id = 'noSuchDoc',
				_empty = true,
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetLocalDocValueAccepts_runUntilDone( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local myDocValue = {
			_id = 'testLocalDoc',
			blah = 42,
		}

		local docId = myDocValue._id

		local expectedValue = KSTableUtils.deepCopy( myDocValue )

		-- Set doc value, but don't observe the result.

		docStore:setLocalDocValue( myDocValue )

		-- Read it back.

		rxTest:expectSingleValue( docStore:localDocValue( docId ), expectedValue )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetSetLocalDocValue_noCrossContamination( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Local and syncing docs are in separate namespaces.
		-- Note that we're using both in this test.

		rxTest:expectOnlyCompleted(
			docStore:setLocalDocValue {
				_id = 'no-conflict-please',
				blah = 42,
			} )

		local rev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'no-conflict-please',
				blah = 95,
			} )

		rxTest:expectSingleValue(

			docStore:docValue( 'no-conflict-please' ),

			{
				_changeCounter = 1,
				_revId = rev1Id,
				_id = 'no-conflict-please',
				_localId = 1,
				blah = 95,
			} )

		rxTest:expectSingleValue(

			docStore:localDocValue( 'no-conflict-please' ),

			{
				_id = 'no-conflict-please',
				blah = 42,
			} )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestGetSetLocalDocValue_update( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		rxTest:expectOnlyCompleted(
			docStore:setLocalDocValue {
				_id = 'no-conflict-please',
				blah = 42,
			} )

		rxTest:expectSingleValue(

			docStore:localDocValue( 'no-conflict-please' ),

			{
				_id = 'no-conflict-please',
				blah = 42,
			} )

		rxTest:expectOnlyCompleted(
			docStore:setLocalDocValue {
				_id = 'no-conflict-please',
				blah = 99,
			} )

		rxTest:expectSingleValue(

			docStore:localDocValue( 'no-conflict-please' ),

			{
				_id = 'no-conflict-please',
				blah = 99,
			} )

	end
	
	--------------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestApplyOverlayLocalDocValue( rxTest )
	
		rxTest:setTimeLimit( 100 )

		local docStore = setUpDocStore( rxTest )
		
		rxTest:expectOnlyCompleted(
			docStore:applyOverlayLocalDocValue {
				_id = 'testLocalDoc',
				foo = 'bar',
			} )
			
		rxTest:expectSingleValue(

			docStore:localDocValue( 'testLocalDoc' ),

			{
				_id = 'testLocalDoc',
				foo = 'bar',
			} )

		rxTest:expectOnlyCompleted(
			docStore:setLocalDocValue {
				_id = 'testLocalDoc',
				blah = 42,
			} )

		rxTest:expectSingleValue(

			docStore:localDocValue( 'testLocalDoc' ),

			{
				_id = 'testLocalDoc',
				blah = 42,
			} )
			
		rxTest:expectOnlyCompleted(
			docStore:applyOverlayLocalDocValue {
				_id = 'testLocalDoc',
				foo = 'bar',
			} )
			
		rxTest:expectSingleValue(

			docStore:localDocValue( 'testLocalDoc' ),

			{
				_id = 'testLocalDoc',
				blah = 42,
				foo = 'bar',
			} )
			
		rxTest:expectOnlyCompleted(
			docStore:applyOverlayLocalDocValue {
				_id = 'testLocalDoc',
				foo = { _delete = true },
			} )
			
		rxTest:expectSingleValue(

			docStore:localDocValue( 'testLocalDoc' ),

			{
				_id = 'testLocalDoc',
				blah = 42,
			} )
			
	end

	----------------------------------------------------------------------------

	-- @mark diagnostic log

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSendDiagnosticLogData( rxTest )

		local docStore = setUpDocStore( rxTest )

		docStore:sendDiagnosticLogData()

			-- NOTE: This test doesn't do much aside from verify that the
			-- diagnostic log code doesn't blow up.

	end

	----------------------------------------------------------------------------

	-- @mark miscellaneous

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestPath( rxTest )

		local docStore = setUpDocStore( rxTest )

		local path = docStore:path()

		rxTest:expectEqual( 'string', type( path ) )
		rxTest:expectEqual( true, #path > 0 )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestToString( rxTest )

		local docStore = setUpDocStore( rxTest )

		local docStoreAsString = tostring( docStore )

		rxTest:expectEqual( 'string', type( docStoreAsString ) )
		rxTest:expectEqual( true, #docStoreAsString > 0 )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetDocValue_Compaction( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest  )

		-- Create first revision of document.

		local rev1Id = setDocValue_firstRevTest42( rxTest, docStore )

		-- Create second revision.

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			test = 23,
			_labels = {
				_labelthatdoesntaffectsync = true,
			}
		}

		local rev2Id = setDocValueAndValidateRevId( rxTest, docStore, expectedValueRev2 )

		expectedValueRev2._revId = rev2Id
		expectedValueRev2._changeCounter = 2

		-- rev2 must still be available
		
		rxTest:expectSingleValue(

			docStore:docRevisionHistory( 'testDoc' ),
			{
				{
					revId = rev2Id,
					status = 'available',
				},
				{
					revId = rev1Id,
					status = 'available',
				},
			} )
		
		local expectedValueRev3 = {
			_id = 'testDoc',
			_localId = 1,
			because = "NoSQL",
			_labels = {
				saveme = true,
			}
		}

		-- Create third revision.

		local rev3Id = setDocValueAndValidateRevId( rxTest, docStore, expectedValueRev3 )
		
		expectedValueRev3._revId = rev3Id
		expectedValueRev3._changeCounter = 3
		
		-- Now read back all three revisions. Only the last revision should be available

		rxTest:expectOnlyError(
			Observable.pcall( function()
				return docStore:docValue( 'testDoc', rev1Id )
			end ),
			':ignore:' )

		rxTest:expectOnlyError(
			Observable.pcall( function()
				return docStore:docValue( 'testDoc', rev2Id )
			end ),
			':ignore:' )

		rxTest:expectSingleValue(
			docStore:docValue( 'testDoc', rev3Id ),
			expectedValueRev3 )


		rxTest:expectSingleValue(

			docStore:docRevisionHistory( 'testDoc' ),

			{
				{
					revId = rev3Id,
					status = 'available',
				},
			} )

		rxTest:expectOnlyCompleted( docStore:haltAndDelete() )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetDocValue_CompactionOnlyUnderscoreLabels( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest  )

		-- Create first revision of document.

		local rev1Id = setDocValue_firstRevTest42( rxTest, docStore )

		-- Create second revision.

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			test = 23,
			_labels = {
				_labelthatdoesntaffectsync = true,
			}
		}

		local rev2Id = setDocValueAndValidateRevId( rxTest, docStore, expectedValueRev2 )

		expectedValueRev2._revId = rev2Id
		expectedValueRev2._changeCounter = 2

		-- rev2 must still be available
		
		rxTest:expectSingleValue(

			docStore:docRevisionHistory( 'testDoc' ),
			{
				{
					revId = rev2Id,
					status = 'available',
				},
				{
					revId = rev1Id,
					status = 'available',
				},
			} )
		
		local expectedValueRev3 = {
			_id = 'testDoc',
			_localId = 1,
			because = "NoSQL",
			_labels = {
				_saveme = true,
			}
		}

		-- Create third revision.

		local rev3Id = setDocValueAndValidateRevId( rxTest, docStore, expectedValueRev3 )
		
		expectedValueRev3._revId = rev3Id
		expectedValueRev3._changeCounter = 3
		
		-- Now read back all three revisions. All three hould be avilable

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc', rev1Id ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev1Id,
				_changeCounter = 1,
				test = 42,
			} )

		rxTest:expectSingleValue(
			docStore:docValue( 'testDoc', rev2Id ),
			expectedValueRev2 )

		rxTest:expectSingleValue(
			docStore:docValue( 'testDoc', rev3Id ),
			expectedValueRev3 )

		rxTest:expectSingleValue(

			docStore:docRevisionHistory( 'testDoc' ),

			{
				{
					revId = rev3Id,
					status = 'available',
				},
				{
					revId = rev2Id,
					status = 'available',
				},
				{
					revId = rev1Id,
					status = 'available',
				},
			} )

		rxTest:expectOnlyCompleted( docStore:haltAndDelete() )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestCompact( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		-- This test needs to exercise some code that can't run on in-memory
		-- databases.

		local path = KSPathUtils.child( KSPathUtils.getStandardFilePath 'temp',
										'RxDocStoreForCompactTest' )

		deleteDB( path )

		WFCapabilities.setCapability( 'WFRxDocStore.disableRevisionCleanupForCompact', 'true' )

		rxTest:finally( function()
			WFCapabilities.setCapability( 'WFRxDocStore.disableRevisionCleanupForCompact', nil )
			deleteDB( path )
		end )

		local docStoreClass = require( docStoreClassNamespace )

		local docStore = setUpDocStore( rxTest, docStoreClass( path ) )

		-- Create first revision of document.

		local rev1Id = setDocValue_firstRevTest42( rxTest, docStore )

		-- Create second revision.

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			test = 23,
			_labels = {
				_labelthatdoesntaffectsync = true,
			}
		}

		local rev2Id = setDocValueAndValidateRevId( rxTest, docStore, expectedValueRev2 )

		expectedValueRev2._revId = rev2Id
		expectedValueRev2._changeCounter = 2
		
		local expectedValueRev3 = {
			_id = 'testDoc',
			_localId = 1,
			because = "NoSQL",
			_labels = {
				saveme = true,
			}
		}

		-- Create third revision.

		local rev3Id = setDocValueAndValidateRevId( rxTest, docStore, expectedValueRev3 )
		
		expectedValueRev3._revId = rev3Id
		expectedValueRev3._changeCounter = 3
		
		-- Now read back all three revisions.

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc', rev1Id ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev1Id,
				_changeCounter = 1,
				test = 42,
			} )


		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc', rev2Id ), 
			expectedValueRev2 )
		
		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc', rev3Id ), 
			expectedValueRev3 )
			
		-- Read back document history before compaction.

		rxTest:expectSingleValue(

			docStore:docRevisionHistory( 'testDoc' ),
			{
				{
					revId = rev3Id,
					status = 'available',
				},
				{
					revId = rev2Id,
					status = 'available',
				},
				{
					revId = rev1Id,
					status = 'available',
				},
			} )
		
		-- Compact the DB

		local maybeObservable = docStore:compact()

		if maybeObservable then

			-- if we're running the WFRxDocStoreImpTests, this just returns nil

			local obs = rxTest:observe( maybeObservable )

			obs:expectCompletedWithinTime( TEST_WAIT_TIME / 3 )

		end
		
		-- Now read back all three revisions. Only the third should succeed.

		rxTest:expectOnlyError(
			Observable.pcall( function()
				return docStore:docValue( 'testDoc', rev1Id )
			end ),
			':ignore:' )

		rxTest:expectOnlyError(
			Observable.pcall( function()
				return docStore:docValue( 'testDoc', rev2Id )
			end ),
			':ignore:' )

		rxTest:expectSingleValue(
			docStore:docValue( 'testDoc', rev3Id ),
			expectedValueRev3 )

		-- Read back history. Should only show the remaining rev labelled without an underscore.

		-- NOTE [immisch 2019-04-12]: We don't need the opposite test, i.e. storing the labels in 
		-- a different order. But it's not easy to explain without explaining how revision compaction 
		-- has to work

		rxTest:expectSingleValue(

			docStore:docRevisionHistory( 'testDoc' ),

			{
				{
					revId = rev3Id,
					status = 'available',
				},
			} )

		rxTest:expectOnlyCompleted( docStore:haltAndDelete() )

	end


	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestCompact_runUntilDone( rxTest )

		-- same as above, but don't observe the result. It should still run

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		-- This test needs to exercise some code that can't run on in-memory
		-- databases.

		local path = KSPathUtils.child( KSPathUtils.getStandardFilePath 'temp',
										'RxDocStoreForCompactTest' )

		deleteDB( path )

		WFCapabilities.setCapability( 'WFRxDocStore.disableRevisionCleanupForCompact', 'true' )

		rxTest:finally( function()
			WFCapabilities.setCapability( 'WFRxDocStore.disableRevisionCleanupForCompact', nil )
			deleteDB( path )
		end )

		local docStoreClass = require( docStoreClassNamespace )

		local docStore = setUpDocStore( rxTest, docStoreClass( path ) )

		-- Create first revision of document.

		local rev1Id = setDocValue_firstRevTest42( rxTest, docStore )

		-- Create second revision.

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			test = 23,
			_labels = {
				_labelthatdoesntaffectsync = true,
			}
		}

		local rev2Id = setDocValueAndValidateRevId( rxTest, docStore, expectedValueRev2 )

		expectedValueRev2._revId = rev2Id
		expectedValueRev2._changeCounter = 2
		
		local expectedValueRev3 = {
			_id = 'testDoc',
			_localId = 1,
			because = "NoSQL",
			_labels = {
				saveme = true,
			}
		}

		-- Create third revision.

		local rev3Id = setDocValueAndValidateRevId( rxTest, docStore, expectedValueRev3 )
		
		expectedValueRev3._revId = rev3Id
		expectedValueRev3._changeCounter = 3
		
		-- Now read back all three revisions.

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc', rev1Id ),
			{
				_id = 'testDoc',
				_localId = 1,
				_revId = rev1Id,
				_changeCounter = 1,
				test = 42,
			} )


		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc', rev2Id ), 
			expectedValueRev2 )
		
		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc', rev3Id ), 
			expectedValueRev3 )
			
		-- Read back document history before compaction.

		rxTest:expectSingleValue(

			docStore:docRevisionHistory( 'testDoc' ),
			{
				{
					revId = rev3Id,
					status = 'available',
				},
				{
					revId = rev2Id,
					status = 'available',
				},
				{
					revId = rev1Id,
					status = 'available',
				},
			} )

		-- Compact the DB, but don't observe the result

		docStore:compact()

		-- Now read back all three revisions. Only the third should succeed.

		rxTest:expectOnlyError(
			Observable.pcall( function()
				return docStore:docValue( 'testDoc', rev1Id )
			end ),
			':ignore:' )

		rxTest:expectOnlyError(
			Observable.pcall( function()
				return docStore:docValue( 'testDoc', rev2Id )
			end ),
			':ignore:' )

		rxTest:expectSingleValue(
			docStore:docValue( 'testDoc', rev3Id ),
			expectedValueRev3 )

		-- Read back history. Should only show the remaining rev labelled without an underscore.

		-- NOTE [immisch 2019-04-12]: We don't need the opposite test, i.e. storing the labels in 
		-- a different order. But it's not easy to explain without explaining how revision compaction 
		-- has to work

		rxTest:expectSingleValue(

			docStore:docRevisionHistory( 'testDoc' ),

			{
				{
					revId = rev3Id,
					status = 'available',
				},
			} )

		rxTest:expectOnlyCompleted( docStore:haltAndDelete() )
		
    end
	
	--------------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestCompact_protectsUnlabeledRevisions( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		-- Create first revision of empty document and attach a label to it.

		local rev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				test = 42,
			} )

		-- Replace that with a second (deleted) revision.

		local rev2Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = rev1Id,
				_deleted = true,
			} )

		-- Replace that with third revision.

		local rev3Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = rev2Id,
				because = "NoSQL",
			} )

		-- Now read back all three revisions.

		local expectedValueRev1 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = rev1Id,
			_changeCounter = 1,
			test = 42,
		}

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc', rev1Id ),
			expectedValueRev1 )

		local expectedValueRev2 = {
			_id = 'testDoc',
			_localId = 1,
			_revId = rev2Id,
			_changeCounter = 2,
			_deleted = true,
		}

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc', rev2Id ),
			expectedValueRev2 )

		local expectedValueRev3 = {
			-- This one gets reused later in the test.
			_id = 'testDoc',
			_localId = 1,
			_revId = rev3Id,
			_changeCounter = 3,
			because = "NoSQL",
		}

		rxTest:expectSingleValue(

			docStore:docValue( 'testDoc', rev3Id ),
			expectedValueRev3 )

		-- Read back document history before compaction.

		rxTest:expectSingleValue(

			docStore:docRevisionHistory( 'testDoc' ),
			{
				{
					revId = rev3Id,
					status = 'available',
				},
				{
					revId = rev2Id,
					status = 'deleted',
				},
				{
					revId = rev1Id,
					status = 'available',
				},
			} )

		-- Compact the DB.

		local maybeObservable = docStore:compact()

		if maybeObservable then 
		
			-- if we're running the WFRxDocStoreImpTests, this just returns nil
		
			local obs = rxTest:observe( maybeObservable )

			obs:expectCompletedWithinTime( TEST_WAIT_TIME / 3 )
		
		end

		-- Now read back all three revisions.
		-- The revisions should all be available.

		rxTest:expectSingleValue(
			docStore:docValue( 'testDoc', rev1Id ),
			expectedValueRev1 )

		rxTest:expectSingleValue(
			docStore:docValue( 'testDoc', rev2Id ),
			expectedValueRev2 )

		rxTest:expectSingleValue(
			docStore:docValue( 'testDoc', rev3Id ),
			expectedValueRev3 )

		-- Read back history. Should show 'deleted' revs.

		rxTest:expectSingleValue(

			docStore:docRevisionHistory( 'testDoc' ),

			{
				{
					revId = rev3Id,
					status = 'available',
				},
				{
					revId = rev2Id,
					status = 'deleted',
				},
				{
					revId = rev1Id,
					status = 'available',
				},
			} )

		-- Add a fourth revision and set a label. This will remove the historic revisions

		local rev4Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'testDoc',
				_revId = rev3Id,
				_labels = {
					saveMe = true,
				},
				experience = 3,
			} )

		-- Read back history. Should just show the labelled 'available' rev.

		rxTest:expectSingleValue(

			docStore:docRevisionHistory( 'testDoc' ),

			{
				{
					revId = rev4Id,
					status = 'available',
				}
			} )

	end

	----------------------------------------------------------------------------


	function RxDocStoreTestSuite.rxTestPerformanceInstrumentation( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		rxTest:expectOnlyCompleted( docStore:PERF_collectQueryPerformanceStats() )

		rxTest:expectSingleValue(
			docStore:PERF_generateQueryStatsReport(),
			':ignore:' )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestDeleteSpace( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local spaceId = 'space_1'

		-- Create some documents with space ids.
		
		local rev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				_spaceId = spaceId,
				test = 42,
			} )

		local rev2Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_2',
				_spaceId = spaceId,
				test = 44,
			} )

		local rev3Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_3',
				_spaceId = spaceId,
				test = 48,
			} )

		-- Verify the documents.
		
		rxTest:expectSingleValue(

			docStore:docValue( 'test_1' ),

			{
				_id = 'test_1',
				_spaceId = spaceId,
				_localId = 1,
				_revId = rev1Id,
				_changeCounter = 1,
				test = 42,
			} )
			
		rxTest:expectSingleValue(

			docStore:docValue( 'test_2' ),

			{
				_id = 'test_2',
				_spaceId = spaceId,
				_localId = 2,
				_revId = rev2Id,
				_changeCounter = 2,
				test = 44,
			} )
			
		rxTest:expectSingleValue(

			docStore:docValue( 'test_3' ),

			{
				_id = 'test_3',
				_spaceId = spaceId,
				_localId = 3,
				_revId = rev3Id,
				_changeCounter = 3,
				test = 48,
			} )
			
		local deletedDocs = rxTest:observe( docStore:deletedDocsSinceNamedChangeCounter( "test_deletedDocs" )
			:flatMap( function( deletedDocs )
				return Observable.from( deletedDocs )
			end ) )

		-- Delete the space. This should do the following:
		
		rxTest:expectSingleValue( docStore:deleteSpace( spaceId ), true )
		
		--[[
			- Deletes all revisions for all documents associated with the space.
			- Inserts a new deletion tombstone into the revs table for all documents
				associated with the space.
			- Inserts a new '_dontsendtooz' label for each of the new tombstone revisions 
				to prevent them from syncing to Oz.
			- Updates all documents associated with the space to point at the new tombstone
				revisions.
		]]--
		
		-- Verify that all previous revisions are deleted.
		
		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:docValue( 'test_1', rev1Id )
			end ),

			':ignore:' )
		
		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:docValue( 'test_2', rev2Id )
			end ),

			':ignore:' )
			
		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:docValue( 'test_3', rev3Id )
			end ),

			':ignore:' )

		-- Verify the new deletion tombstone, label, and new revision.
		
		rxTest:expectSingleValue(

			docStore:docValue( 'test_1' ),

			{
				_deleted = true,
				_empty = true,
				_id = "test_1",
				_labels = {
					_dontsendtooz = true,
				},
				_revId = "00000000000000000000000000000000",
			} )
			
		rxTest:expectSingleValue(

			docStore:docValue( 'test_2' ),

			{
				_deleted = true,
				_empty = true,
				_id = "test_2",
				_labels = {
					_dontsendtooz = true,
				},
				_revId = "00000000000000000000000000000000",
			} )
			
		rxTest:expectSingleValue(

			docStore:docValue( 'test_3' ),

			{
				_deleted = true,
				_empty = true,
				_id = "test_3",
				_labels = {
					_dontsendtooz = true,
				},
				_revId = "00000000000000000000000000000000",
			} )
			
			
		deletedDocs:expectData( {
			_id = "test_1",
			_spaceId = spaceId,
			_deleted = true,
			_changeCounter = 4,
		} )
		deletedDocs:expectData( {
			_id = "test_2",
			_spaceId = spaceId,
			_deleted = true,
			_changeCounter = 5,
		} )
		deletedDocs:expectData( {
			_id = "test_3",
			_spaceId = spaceId,
			_deleted = true,
			_changeCounter = 6,
		} )
		
	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestDeleteSpace_ExcludeDocumentIds( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local spaceId = 'space_1'

		-- Create some documents with space ids.
		
		local rev1Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_1',
				_spaceId = spaceId,
				test = 42,
			} )

		local rev2Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_2',
				_spaceId = spaceId,
				test = 44,
			} )

		local rev3Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_3',
				_spaceId = spaceId,
				test = 48,
			} )

		local rev4Id = setDocValueAndValidateRevId( rxTest, docStore,
			{
				_id = 'test_4',
				_spaceId = spaceId,
				test = 50,
			} )

		-- Verify the documents.
		
		rxTest:expectSingleValue(

			docStore:docValue( 'test_1' ),

			{
				_id = 'test_1',
				_spaceId = spaceId,
				_localId = 1,
				_revId = rev1Id,
				_changeCounter = 1,
				test = 42,
			} )
			
		rxTest:expectSingleValue(

			docStore:docValue( 'test_2' ),

			{
				_id = 'test_2',
				_spaceId = spaceId,
				_localId = 2,
				_revId = rev2Id,
				_changeCounter = 2,
				test = 44,
			} )
			
		rxTest:expectSingleValue(

			docStore:docValue( 'test_3' ),

			{
				_id = 'test_3',
				_spaceId = spaceId,
				_localId = 3,
				_revId = rev3Id,
				_changeCounter = 3,
				test = 48,
			} )

		rxTest:expectSingleValue(

			docStore:docValue( 'test_4' ),

			{
				_id = 'test_4',
				_spaceId = spaceId,
				_localId = 4,
				_revId = rev4Id,
				_changeCounter = 4,
				test = 50,
			} )

		-- Delete the space but exclude test_3 & test_4
		
		local excludedDocumentIds = { 'test_3', 'test_4' }
		
		rxTest:expectSingleValue( docStore:deleteSpace( spaceId, excludedDocumentIds ), true )
		
		-- Verify that all previous revisions are deleted except test_3 & test_4.
		
		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:docValue( 'test_1', rev1Id )
			end ),

			':ignore:' )
		
		rxTest:expectOnlyError(

			Observable.pcall( function()
				return docStore:docValue( 'test_2', rev2Id )
			end ),

			':ignore:' )
			
		rxTest:expectSingleValue(

			docStore:docValue( 'test_3', rev3Id ),

			{
				_id = 'test_3',
				_spaceId = spaceId,
				_localId = 3,
				_revId = rev3Id,
				_changeCounter = 3,
				test = 48,
			} )

		rxTest:expectSingleValue(

			docStore:docValue( 'test_4', rev4Id ),

			{
				_id = 'test_4',
				_spaceId = spaceId,
				_localId = 4,
				_revId = rev4Id,
				_changeCounter = 4,
				test = 50,
			} )

		-- Verify the new deletion tombstone, label, and new revision, except for test_3 & test_4.
		
		rxTest:expectSingleValue(

			docStore:docValue( 'test_1' ),

			{
				_deleted = true,
				_empty = true,
				_id = "test_1",
				_labels = {
					_dontsendtooz = true,
				},
				_revId = "00000000000000000000000000000000",
			} )
			
		rxTest:expectSingleValue(

			docStore:docValue( 'test_2' ),

			{
				_deleted = true,
				_empty = true,
				_id = "test_2",
				_labels = {
					_dontsendtooz = true,
				},
				_revId = "00000000000000000000000000000000",
			} )
			
		rxTest:expectSingleValue(

			docStore:docValue( 'test_3' ),

			{
				_id = 'test_3',
				_spaceId = spaceId,
				_localId = 3,
				_revId = rev3Id,
				_changeCounter = 3,
				test = 48,
			} )
		
		rxTest:expectSingleValue(

			docStore:docValue( 'test_4' ),

			{
				_id = 'test_4',
				_spaceId = spaceId,
				_localId = 4,
				_revId = rev4Id,
				_changeCounter = 4,
				test = 50,
			} )
		
	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestSetDocValue_DocumentWithSpaceIdDeletion( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest  )

		local spaceId = 'space_1'
		
		-- Create a document with a space id.
		
		local expectedValueRev1 = { 
			_id = 'testDoc',
			_spaceId = spaceId,
			test = 42,
		}
		
		local rev1Id = setDocValueAndValidateRevId( rxTest, docStore, expectedValueRev1 )

		expectedValueRev1._localId = 1
		expectedValueRev1._revId = rev1Id
		expectedValueRev1._changeCounter = 1

		-- Create second revision.

		local expectedValueRev2 = {
			_id = 'testDoc',
			_spaceId = spaceId,
			_localId = 1,
			_revId = rev1Id,
			test = 23,
		}

		local rev2Id = setDocValueAndValidateRevId( rxTest, docStore, expectedValueRev2 )

		expectedValueRev2._revId = rev2Id
		expectedValueRev2._changeCounter = 2

		local expectedValueRev3 = {
			_id = 'testDoc',
			_spaceId = spaceId,
			_localId = 1,
			_revId = rev2Id,
			test = 99,
		}

		-- Create third revision.

		local rev3Id = setDocValueAndValidateRevId( rxTest, docStore, expectedValueRev3 )

		expectedValueRev3._revId = rev3Id
		expectedValueRev3._changeCounter = 3
		
		-- Now read back all three revisions. All should be available.

		rxTest:expectSingleValue(
			docStore:docValue( 'testDoc', rev1Id ),
			expectedValueRev1 )

		rxTest:expectSingleValue(
			docStore:docValue( 'testDoc', rev2Id ),
			expectedValueRev2 )

		rxTest:expectSingleValue(
			docStore:docValue( 'testDoc', rev3Id ),
			expectedValueRev3 )

		-- Now delete the document. Since it's a space-feed document, it should
		-- clean up the revision history as part of the delete.
		
		local rev4Id
		
		rxTest:expectSingleValue( docStore:deleteDoc( 'testDoc' )
			:map( function( deletedRevId )
			
				rev4Id = deletedRevId
				return deletedRevId
			end ), ':ignore:' )

		-- Now read back all three prior revisions. They should all have their content deleted.

		rxTest:expectSingleValue(
				docStore:docValue( 'testDoc', rev1Id )
					:pluck( 'test' ), nil )

		rxTest:expectSingleValue(
				docStore:docValue( 'testDoc', rev2Id )
					:pluck( 'test' ), nil )

		rxTest:expectSingleValue(
				docStore:docValue( 'testDoc', rev3Id )
					:pluck( 'test' ), nil )

		-- Verify the deletion revision.
		
		rxTest:expectSingleValue(

			docStore:docRevisionHistory( 'testDoc' ),

			{
				{
					revId = rev4Id,
					status = 'deleted',
				},
				{
					revId = rev3Id,
					status = "available",
				},
				{
					revId = rev2Id,
					status = "available",
				},
				{
					revId = rev1Id,
					status = "available",
				},
			} )

		rxTest:expectOnlyCompleted( docStore:haltAndDelete() )

	end


	--------------------------------------------------------------------------------


	function RxDocStoreTestSuite.rxTestGetDocsOfType( rxTest )

		local function selectFirst( a, _ )

			return a

		end

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValues = {}
		
		local allDocIds = {}
		local videoDocIds = {}
		local imageDocIds = {}
		
		for i = 1, 100 do
		
			local docId = 'test_' .. tostring( i )
		
			local docType = 'asset'
			local subType = 'image'
			
			if i % 2 == 0 then
			
				subType = 'video'
				videoDocIds[ #videoDocIds + 1 ] = docId
				
			else
			
				imageDocIds[ #imageDocIds + 1 ] = docId
			
			end
		
			allDocIds[ #allDocIds + 1 ] = docId
		
			docValues[ i ] = {
				_id = docId,
				type = docType,
				subtype = subType,
				foo = 'bar',
				counter = i,
			}
			
		end

		rxTest:expectSingleValue(
			docStore:bulkSetDocValue( docValues ),
			':ignore:' )

		-- Test by type/subtype

		rxTest:expectSingleValue(
			docStore:getDocsOfType( { asset = { '*' } } ):map( selectFirst ),
			allDocIds )

		rxTest:expectSingleValue(
			docStore:getDocsOfType( { asset = { 'image' } } ):map( selectFirst ),
			imageDocIds )
			
		rxTest:expectSingleValue(
			docStore:getDocsOfType( { asset = { 'video' } } ):map( selectFirst ),
			videoDocIds )
			
		rxTest:expectSingleValue(
			docStore:getDocsOfType( { [ '*' ] = { '*' } } ):map( selectFirst ),
			allDocIds )
			
		-- Test with type/subtype and change counter
		
		rxTest:expectSingleValue(
			docStore:getDocsOfType( { asset = { '*' } }, 95 ):map( selectFirst ),
			{ 'test_95', 'test_96', 'test_97', 'test_98', 'test_99', 'test_100' } )

		rxTest:expectSingleValue(
			docStore:getDocsOfType( { asset = { 'image' } }, 95 ):map( selectFirst ),
			{ 'test_95','test_97', 'test_99' } )
			
		rxTest:expectSingleValue(
			docStore:getDocsOfType( { asset = { 'video' } }, 95 ):map( selectFirst ),
			{ 'test_96', 'test_98', 'test_100' } )
			
		rxTest:expectSingleValue(
			docStore:getDocsOfType( { [ '*' ] = { '*' } }, 95 ):map( selectFirst ),
			{ 'test_95', 'test_96', 'test_97', 'test_98', 'test_99', 'test_100' } ) 
			
		-- Misc
		
		rxTest:expectSingleValue(
			docStore:getDocsOfType( { asset = { '*' } }, 0 ):map( selectFirst ),
			allDocIds )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestCountDocsOfType( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValues = {}
		
		local allDocIds = {}
		local videoDocIds = {}
		local imageDocIds = {}
		local albumIds = {}
		
		for i = 1, 101 do
		
			local docId = 'test_' .. tostring( i )
		
			local docType = 'asset'
			local subType = 'image'
			
			if i % 5 == 0 then
			
				docType = 'album'
				subType = 'collection'
				albumIds[ #albumIds + 1 ] = docId
				
			elseif i % 2 == 0 then
			
				subType = 'video'
				videoDocIds[ #videoDocIds + 1 ] = docId
				
			else
			
				imageDocIds[ #imageDocIds + 1 ] = docId
			
			end
		
			allDocIds[ #allDocIds + 1 ] = docId
		
			docValues[ i ] = {
				_id = docId,
				type = docType,
				subtype = subType,
				foo = 'bar',
				counter = i,
			}
			
		end

		rxTest:expectSingleValue(
			docStore:bulkSetDocValue( docValues ),
			':ignore:' )

		rxTest:expectSingleValue(
			docStore:countDocsOfType( { asset = { 'image' } } ), 41 )

		rxTest:expectSingleValue(
			docStore:countDocsOfType( { asset = { 'video' } } ), 40 )

		rxTest:expectSingleValue(
			docStore:countDocsOfType( { album = { 'collection' } } ), 20 )

		rxTest:expectSingleValue(
			docStore:countDocsOfType( { asset = { 'video', 'image' } } ), 81 )

		rxTest:expectSingleValue(
			docStore:countDocsOfType( { asset = { '*' } } ), 81 )

		rxTest:expectSingleValue(
			docStore:countDocsOfType( { asset = { 'video', 'image' }, album = { 'collection' } } ), 101 )

		rxTest:expectSingleValue(
			docStore:countDocsOfType( { asset = { '*' }, album = { '*' } } ), 101 )

		rxTest:expectSingleValue(
			docStore:countDocsOfType( { [ '*' ] = { '*' } } ), 101 )

	end

	----------------------------------------------------------------------------

	function RxDocStoreTestSuite.rxTestCountAllDocs( rxTest )

		rxTest:setTimeLimit( TEST_WAIT_TIME )

		local docStore = setUpDocStore( rxTest )

		local docValues = {}
		
		local allDocIds = {}
		local videoDocIds = {}
		local imageDocIds = {}
		
		for i = 1, 100 do
		
			local docId = 'test_' .. tostring( i )
		
			local docType = 'asset'
			local subType = 'image'
			
			if i % 2 == 0 then
			
				subType = 'video'
				videoDocIds[ #videoDocIds + 1 ] = docId
				
			else
			
				imageDocIds[ #imageDocIds + 1 ] = docId
			
			end
		
			allDocIds[ #allDocIds + 1 ] = docId
		
			docValues[ i ] = {
				_id = docId,
				type = docType,
				subtype = subType,
				foo = 'bar',
				counter = i,
			}
			
		end

		rxTest:expectSingleValue(
			docStore:bulkSetDocValue( docValues ),
			':ignore:' )

		rxTest:expectSingleValue(
			docStore:countAllDocs(), 100 )

		rxTest:expectSingleValue(
			docStore:countDocsOfType( { [ '*' ] = { '*' } } ), 100 )

	end

	----------------------------------------------------------------------------

	return WFRxTestCase.wrapSuite( RxDocStoreTestSuite )

--============================================================================--

end
