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

WFReadOnlyDocStoreTest.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2021 Adobe Inc.
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.

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

local Rx = require "Rx"
local WFLogger = require "WFLogger"

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

local KSPathUtils = require "KSPathUtils"
local KSIdGenerator = require "KSIdGenerator"
local KSTableUtils = require "KSTableUtils"

local WFRxDocStore = require "WFRxDocStore"
local WFRxDocStoreUtils = require "WFRxDocStoreUtils"

local WFReadOnlyDocStore = require "WFReadOnlyDocStore"

local WFRxScheduler = require "WFRxScheduler"

local logger = WFLogger( 'WFReadOnlyDocStoreTest' )

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

local function stripWobblyBits( docValue )

	local v = KSTableUtils.deepCopy( docValue )

	v._revId = nil;

	return v

end

local WFReadOnlyDocStoreTest = {}

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

function WFReadOnlyDocStoreTest.rxTest_a_readDocument( rxTest )

	rxTest:setTimeLimit( 240 )

	local testDataPath = KSPathUtils.getStandardFilePath( "testdata" )

	local dbPath = KSPathUtils.child( testDataPath, rxTest:testName() .. '.mcat' )

	local docStore = WFRxDocStore( dbPath )
	rxTest:finally( function()
		docStore:haltAndDelete()
	end )

	local docId = KSIdGenerator.uuidInLowercaseHex()

	local doc = {
		_id = docId,
		type = 'readonly_test',
		_localOnly = {
			counter = 1
		},
		_labels = {
			c = true,
		},
	}

	rxTest:waitFor( docStore:setDocValue( doc ) )
	
	local expectedDoc = rxTest:getFirstValue( docStore:docValue( docId ) )

	local readOnlyDocStore = WFReadOnlyDocStore.WFReadOnlyDocStore( dbPath )

	local archive = readOnlyDocStore:docArchive( docId )
	local readDoc = WFRxDocStoreUtils.unpackDocValue( archive )
	
	rxTest:expectEqual( expectedDoc, readDoc )

end

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

function WFReadOnlyDocStoreTest.rxTest_a_readEmptyDocument( rxTest )

	rxTest:setTimeLimit( 240 )

	local testDataPath = KSPathUtils.getStandardFilePath( "testdata" )

	local dbPath = KSPathUtils.child( testDataPath, rxTest:testName() .. '.mcat' )

	local docStore = WFRxDocStore( dbPath, { initProgressRequested = true } )
	rxTest:finally( function()
		docStore:haltAndDelete()
	end )

	rxTest:waitFor( docStore:getInitializationProgress() )

	local readOnlyDocStore = WFReadOnlyDocStore.WFReadOnlyDocStore( dbPath )

	local archive = readOnlyDocStore:docArchive( 'does_not_exist' )

	rxTest:expectEqual( { _id = 'does_not_exist', _empty = true }, archive[ 1 ] )

end

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

function WFReadOnlyDocStoreTest.rxTest_applyOverlayReadRepeatedly( rxTest )

	rxTest:setTimeLimit( 240 )

	local testDataPath = KSPathUtils.getStandardFilePath( "testdata" )

	local dbPath = KSPathUtils.child( testDataPath, rxTest:testName() .. '.mcat' )

	local docStore = WFRxDocStore( dbPath )
	rxTest:finally( function()
		docStore:haltAndDelete()
	end )

	local docId = KSIdGenerator.uuidInLowercaseHex()

	local doc = {
		_id = docId,
		type = 'readonly_test',
		_localOnly = {
			counter = 1
		},
		counter = 1
	}

	rxTest:waitFor( docStore:applyOverlay( doc ) )

	local readOnlyDocStore = WFReadOnlyDocStore.WFReadOnlyDocStore( dbPath )

	-- write and read concurrently and make sure that we get consistent documents

	local range = Rx.Observable.range( 2, 10000 )

	local write = range
		:flatMap( function( i )
		
			doc = {
				_id = docId,
				_localOnly = {
					counter = i,
				},
				counter = i,
			}

			logger:debug( string.format("rxTest_applyOverlayReadRepeatedly writing %d", i ) )
			
			return docStore:applyOverlay( doc )
				:map( function()
					return doc
				end )
		
		end )

	local read = range
		:map( function( i )
		
			
			local archive = readOnlyDocStore:docArchive( docId )
			local readDoc = WFRxDocStoreUtils.unpackDocValue( archive )
			
			logger:debug( string.format("rxTest_applyOverlayReadRepeatedly read %d", i ), readDoc.counter )

			rxTest:expectEqual( readDoc.counter, readDoc._localOnly.counter )
		
			return readDoc
			
		end )

	rxTest:waitFor( Rx.Observable.combineLatest( read, write ) )
	
	-- Check the final docValue is the same
	local expectedDoc = rxTest:getFirstValue( docStore:docValue( docId ) )
	local archive = readOnlyDocStore:docArchive( docId )
	local readDoc = WFRxDocStoreUtils.unpackDocValue( archive )
			
	rxTest:expectEqual( expectedDoc, readDoc )	
	
end

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

function WFReadOnlyDocStoreTest.rxTest_bulkSetDocValueReadRepeatedly( rxTest )

	rxTest:setTimeLimit( 240 )

	local testDataPath = KSPathUtils.getStandardFilePath( "testdata" )

	local dbPath = KSPathUtils.child( testDataPath, rxTest:testName() .. '.mcat')

	local docStore = WFRxDocStore( dbPath )
	rxTest:finally( function()
		docStore:haltAndDelete()
	end )

	local docId = KSIdGenerator.uuidInLowercaseHex()

	local doc = {
		_id = docId,
		type = 'readonly_test',
		_localOnly = {
			counter = 1
		},
		_labels = {
			['1'] = true,
		},
		counter = 1
	}

	rxTest:waitFor( docStore:setDocValue( doc ) )

	local readOnlyDocStore = WFReadOnlyDocStore.WFReadOnlyDocStore( dbPath )

	-- write and read concurrently and make sure that we get consistent documents

	local docs = {}

	local nUpdates = 1000

	for i = 2, nUpdates do
		doc = {
			_id = docId,
			_localOnly = {
				counter = i,
			},
			_labels = {
				[tostring(i)] = true,
			},
			counter = i,
		}

		docs[ #docs + 1 ] = doc

	end

	local read = Rx.Observable.interval( 0.001, WFRxScheduler.sharedScheduler() )
		:map( function( i )

			local archive = readOnlyDocStore:docArchive( docId )
			local readDoc = WFRxDocStoreUtils.unpackDocValue( archive )

			rxTest:expectEqual( readDoc.counter, readDoc._localOnly.counter )
			rxTest:expectEqual( true, readDoc._labels[ tostring(readDoc.counter ) ] )
			
			return readDoc.counter
		end )
		:takeWhile( function( counter )
			return counter < nUpdates
		end )
		
	rxTest:waitFor( Rx.Observable.combineLatest( docStore:bulkSetDocValue( docs ), read ) )

end

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

function WFReadOnlyDocStoreTest.rxTest_setDocValueReadRepeatedly( rxTest )

	rxTest:setTimeLimit( 240 )

	local testDataPath = KSPathUtils.getStandardFilePath( "testdata" )

	local dbPath = KSPathUtils.child( testDataPath, rxTest:testName() .. '.mcat' )

	local docStore = WFRxDocStore( dbPath )
	rxTest:finally( function()
		docStore:haltAndDelete()
	end )

	local docId = KSIdGenerator.uuidInLowercaseHex()

	local doc = {
		_id = docId,
		type = 'readonly_test',
		_localOnly = {
			counter = 1
		},
		_labels = {
			['1'] = true,
		},
		counter = 1
	}

	rxTest:waitFor( docStore:setDocValue( doc ) )

	local readOnlyDocStore = WFReadOnlyDocStore.WFReadOnlyDocStore( dbPath )

	-- write and read concurrently and make sure that we get consistent documents

	local range = Rx.Observable.range( 2, 10000 )

	local write = range
		:flatMap( function( i )
			local docId = KSIdGenerator.uuidInLowercaseHex()
			doc = {
				_id = docId,
				_localOnly = {
					counter = i,
				},
				_labels = {
					[tostring(i)] = true,
				},
				counter = i,
			}

			logger:debug( string.format("rxTest_setDocValueReadRepeatedly writing %d", i ) )

			return docStore:setDocValue( doc )
				:map( function()
					return doc
				end )
		
		end )

	local read = range
		:map( function( i )
			
			local archive = readOnlyDocStore:docArchive( docId )
			local readDoc = WFRxDocStoreUtils.unpackDocValue( archive )

			logger:debug( string.format("rxTest_setDocValueReadRepeatedly read %d", i, readDoc.counter) )
			
			rxTest:expectEqual( readDoc.counter, readDoc._localOnly.counter )
			
			return readDoc
		end )

	rxTest:waitFor( Rx.Observable.combineLatest( read, write ) )

end

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

function WFReadOnlyDocStoreTest.rxTest_readRepeatedly_testPerformance( rxTest )

	rxTest:setTimeLimit( 240 )

	local testDataPath = KSPathUtils.getStandardFilePath( "testdata" )

	local dbPath = KSPathUtils.child( testDataPath, rxTest:testName() .. '.mcat' )

	local docStore = WFRxDocStore( dbPath )
	rxTest:finally( function()
		docStore:haltAndDelete()
	end )

	local docId = KSIdGenerator.uuidInLowercaseHex()

	local doc = {
		_id = docId,
		type = 'readonly_test',
		_localOnly = {
			counter = 1
		},
		counter = 1
	}

	rxTest:waitFor( docStore:applyOverlay( doc ) )

	local readOnlyDocStore = WFReadOnlyDocStore.WFReadOnlyDocStore( dbPath )

	rxTest:measurePerformance( function()

		for i = 2, 10000 do
		
			local expectedDoc = rxTest:getFirstValue( docStore:docValue( docId ) )
			
			local archive = readOnlyDocStore:docArchive( docId )
			local readDoc = WFRxDocStoreUtils.unpackDocValue( archive )

			rxTest:expectEqual( readDoc.counter, readDoc._localOnly.counter )
			rxTest:expectEqual( expectedDoc, readDoc )
			
		end

	end )

end


return WFRxTestCase.wrapSuite( 'WFReadOnlyDocStoreTest', WFReadOnlyDocStoreTest )
