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

NmbFreemiumUtilsTest.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2020 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.

------------------------------------------------------------------------------]]
	
local Rx = require "Rx"
local WFRxTestCase = require 'WFRxTestCase'
local KSPathUtils = require 'KSPathUtils'
local WFFileUtils = require 'WFFileUtils'
local KSIdGenerator = require 'KSIdGenerator'
local WFSQLiteDatabaseImp = require "WFSQLiteDatabaseImp"

local NmbFreemiumUtils = require "NmbFreemiumUtils"

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

local k1Mb = 1024 * 1024

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

local k5Mb = 5 * k1Mb

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

local k1WeekInSeconds = 60 * 60 * 24 * 7

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

local function justReturn( ... )

	local args, nArgs = { ... }, select( '#', ... )

	return function()

		return unpack( args, 1, nArgs )

	end

end

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

local _freemiumPathForTest = {}

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

local function makeFreemiumPathForTest( rxTest )

	local parentDir =
				KSPathUtils.child( 
					KSPathUtils.getStandardFilePath( 'temp' ), 
					KSIdGenerator.uuidInLowercaseHex() ) 

	WFFileUtils.mkdir( parentDir )

	return KSPathUtils.child( parentDir, rxTest:testName() )

end

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

local function getDocstorePathForTest( rxTest )

	return _freemiumPathForTest[ rxTest ]

end

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

local function makeValidDb( path )

	WFFileUtils.mkdir( KSPathUtils.parent( path ) )

	local db = WFSQLiteDatabaseImp.makeDatabase( path )

	db:exec( "CREATE TABLE t( f )" )

	db:close()

end

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

local function rxTestFirstRun( rxTest )

	local dsPath = getDocstorePathForTest( rxTest )

	rxTest
		:observe(
			NmbFreemiumUtils.maybeBackupDocstoreResultStream( dsPath ) )
		:expectData( 
			false, 
			"new docstore" )

		
end

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

local function rxTestNoBackupNeeded( rxTest )

	local dsPath = getDocstorePathForTest( rxTest )

	makeValidDb( dsPath )

	rxTest
		:observe(
			NmbFreemiumUtils.maybeBackupDocstoreResultStream( dsPath ) )
		:expectData( 
			false, 
			"backup not needed" )
		
end

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

local function addTestOverrideFunction( overrides, overrideFuncName, 
										overrideFunc )

	overrides[ overrideFuncName ] = overrideFunc 

	return overrides

end

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

local function rxTestNoBackupBecauseNoAttr( rxTest )

	local dsPath = getDocstorePathForTest( rxTest )

	makeValidDb( dsPath )

	local overrides = 
			addTestOverrideFunction(
				{},
				"maybeGetFileAttributesImp", 
				justReturn( nil ) )

	rxTest
		:observe(
			NmbFreemiumUtils.privateUnitTestAPI.maybeBackupDocstoreResultStreamUsingTestOverrideFunctions( 
				dsPath, overrides ) )
		:expectError( 
			"Couldn't get orig file attributes" )
		
end

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

local function rxTestDoBackupBecauseNoBackupAttr( rxTest )

	local dsPath = getDocstorePathForTest( rxTest )

	makeValidDb( dsPath )

	local _, backupDSPath = 
		NmbFreemiumUtils.privateUnitTestAPI.getFreemiumBackupPaths( dsPath )

	makeValidDb( backupDSPath )

	-- don't return attributes if backup doesn't have attr
	local overrides = 
			addTestOverrideFunction(
				{},
				"maybeGetFileAttributesImp", 
				function( p )

					local fileSize, fileCreationDate, fileModificationDate

					if p == dsPath then

						fileSize = k5Mb + 1

						fileCreationDate = 0

						fileModificationDate = 0

					end

					return fileSize, fileCreationDate, fileModificationDate

				end )

	local _, backupPath =
			NmbFreemiumUtils.privateUnitTestAPI.getFreemiumBackupPaths( 
				dsPath )

	rxTest
		:observe(
			NmbFreemiumUtils.privateUnitTestAPI.maybeBackupDocstoreResultStreamUsingTestOverrideFunctions( 
				dsPath, overrides ) )
		:expectData( true, backupPath )
		
	rxTest
		:expectEqual(
			WFFileUtils.exists( backupDSPath ),
			"file" )

end

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

local function rxTestNoBackupBecauseRecentBackup( rxTest )

	local dsPath = getDocstorePathForTest( rxTest )

	makeValidDb( dsPath )

	local _, backupDSPath = 
		NmbFreemiumUtils.privateUnitTestAPI.getFreemiumBackupPaths( dsPath )

	makeValidDb( backupDSPath )

	-- make an override that will return backup file attributes that _don't_
	-- trigger must backup logic
	local overrides = 
			addTestOverrideFunction(
				{},
				"maybeGetFileAttributesImp", 
				function( p )

					local fileSize, fileCreationDate, fileModificationDate

					local origSize = 1

					local origModTime = 2 * k1WeekInSeconds

					if p == dsPath then

						fileSize = origSize

						fileCreationDate = 0

						fileModificationDate = origModTime

					end

					if p == backupDSPath then

						fileSize = origSize + k5Mb - 1

						fileCreationDate = origModTime - k1WeekInSeconds + 1

						fileModificationDate = origModTime - k1WeekInSeconds + 1

					end

					return fileSize, fileCreationDate, fileModificationDate

				end )

	rxTest
		:observe(
			NmbFreemiumUtils.privateUnitTestAPI.maybeBackupDocstoreResultStreamUsingTestOverrideFunctions( 
				dsPath, overrides ) )
		:expectData( false, "backup not needed" )
		
end

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

local function rxTestFirstBackupBecauseSize( rxTest )

	local dsPath = getDocstorePathForTest( rxTest )

	makeValidDb( dsPath )

	local _, backupDSPath = 
		NmbFreemiumUtils.privateUnitTestAPI.getFreemiumBackupPaths( dsPath )

	-- make an override that will return backup file attributes that will
	-- trigger must backup logic
	local overrides = 
			addTestOverrideFunction(
				{},
				"maybeGetFileAttributesImp", 
				function( p )

					local fileSize, fileCreationDate, fileModificationDate

					if p == dsPath then

						fileSize = k5Mb

						fileCreationDate = 0

						fileModificationDate = 0

					end

					return fileSize, fileCreationDate, fileModificationDate

				end )

	rxTest
		:observe(
			NmbFreemiumUtils.privateUnitTestAPI.maybeBackupDocstoreResultStreamUsingTestOverrideFunctions( 
				dsPath, overrides ) )
		:expectData( true, backupDSPath )
		
	rxTest
		:expectEqual(
			WFFileUtils.exists( backupDSPath ),
			"file" )
		
end

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

local function rxTestAnotherBackupBecauseSize( rxTest )

	local dsPath = getDocstorePathForTest( rxTest )

	makeValidDb( dsPath )

	local _, backupDSPath = 
		NmbFreemiumUtils.privateUnitTestAPI.getFreemiumBackupPaths( dsPath )

	makeValidDb( backupDSPath )

	-- make an override that will return backup file attributes that will
	-- trigger must backup logic
	local overrides = 
			addTestOverrideFunction(
				{},
				"maybeGetFileAttributesImp", 
				function( p )

					local fileSize, fileCreationDate, fileModificationDate

					local origSize = 1

					local origModTime = 2 * k1WeekInSeconds

					if p == dsPath then

						fileSize = origSize

						fileCreationDate = 0

						fileModificationDate = origModTime

					end

					if p == backupDSPath then

						fileSize = origSize + k5Mb

						fileCreationDate = origModTime - k1WeekInSeconds + 1

						fileModificationDate = origModTime - k1WeekInSeconds + 1

					end 

					return fileSize, fileCreationDate, fileModificationDate

				end )

	rxTest
		:observe(
			NmbFreemiumUtils.privateUnitTestAPI.maybeBackupDocstoreResultStreamUsingTestOverrideFunctions( 
				dsPath, overrides ) )
		:expectData( true, backupDSPath )
		
	rxTest
		:expectEqual(
			WFFileUtils.exists( backupDSPath ),
			"file" )
		
end

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

local function rxTestFirstBackupBecauseTime( rxTest )

	local dsPath = getDocstorePathForTest( rxTest )

	makeValidDb( dsPath )

	local _, backupDSPath = 
		NmbFreemiumUtils.privateUnitTestAPI.getFreemiumBackupPaths( dsPath )

	-- make an override that will return backup file attributes that will
	-- trigger must backup logic
	local overrides = 
			addTestOverrideFunction(
				{},
				"maybeGetFileAttributesImp", 
				function( p )

					local fileSize, fileCreationDate, fileModificationDate

					if p == dsPath then

						fileSize = 1

						fileCreationDate = 0

						fileModificationDate = k1WeekInSeconds

					end


					return fileSize, fileCreationDate, fileModificationDate

				end )

	rxTest
		:observe(
			NmbFreemiumUtils.privateUnitTestAPI.maybeBackupDocstoreResultStreamUsingTestOverrideFunctions( 
				dsPath, overrides ) )
		:expectData( true, backupDSPath )
		
	rxTest
		:expectEqual(
			WFFileUtils.exists( backupDSPath ),
			"file" )
		
end

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

local function rxTestAnotherBackupBecauseTime( rxTest )

	local dsPath = getDocstorePathForTest( rxTest )

	makeValidDb( dsPath )

	local _, backupDSPath = 
		NmbFreemiumUtils.privateUnitTestAPI.getFreemiumBackupPaths( dsPath )

	makeValidDb( backupDSPath )

	-- make an override that will return backup file attributes that will
	-- trigger must backup logic
	local overrides = 
			addTestOverrideFunction(
				{},
				"maybeGetFileAttributesImp", 
				function( p )

					local fileSize, fileCreationDate, fileModificationDate

					local origSize = 1

					local origModTime = 2 * k1WeekInSeconds

					if p == dsPath then

						fileSize = origSize

						fileCreationDate = 0

						fileModificationDate = origModTime

					end

					if p == backupDSPath then

						fileSize = origSize + k5Mb - 1

						fileCreationDate = origModTime - k1WeekInSeconds

						fileModificationDate = origModTime - k1WeekInSeconds

					end 

					return fileSize, fileCreationDate, fileModificationDate

				end )

	rxTest
		:observe(
			NmbFreemiumUtils.privateUnitTestAPI.maybeBackupDocstoreResultStreamUsingTestOverrideFunctions( 
				dsPath, overrides ) )
		:expectData( true, backupDSPath )
		
	rxTest
		:expectEqual(
			WFFileUtils.exists( backupDSPath ),
			"file" )

end

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

local function rxTestBackupFailBecauseCorruptCheckThrows( rxTest )

	local dsPath = getDocstorePathForTest( rxTest )

	makeValidDb( dsPath )

	local _, backupDSPath = 
		NmbFreemiumUtils.privateUnitTestAPI.getFreemiumBackupPaths( dsPath )

	makeValidDb( backupDSPath )

	-- make an override that will return backup file attributes that will
	-- trigger must backup logic
	local overrides = 
			addTestOverrideFunction(
				{},
				"maybeGetFileAttributesImp", 
				function( p )

					local fileSize, fileCreationDate, fileModificationDate

					local origSize = 1

					local origModTime = 2 * k1WeekInSeconds

					if p == dsPath then

						fileSize = origSize

						fileCreationDate = 0

						fileModificationDate = origModTime

					end

					if p == backupDSPath then

						fileSize = origSize + k5Mb - 1

						fileCreationDate = origModTime - k1WeekInSeconds

						fileModificationDate = origModTime - k1WeekInSeconds

					end 

					return fileSize, fileCreationDate, fileModificationDate

				end )

	-- make another check that throws when checking corruption
	overrides =
			addTestOverrideFunction(
				overrides,
				"isDocstoreValidImp",
				function()

					error( "bad db!" )

				end )

	local seenErrStr

	rxTest
		:observe(
			NmbFreemiumUtils.privateUnitTestAPI.maybeBackupDocstoreResultStreamUsingTestOverrideFunctions( 
					dsPath, overrides )
				:tapOnError(
					function( errStr )

						seenErrStr = errStr

					end ) )
		:expectError( ":ignore:" )
		
	rxTest:logf( "check if we got the error we expected. raw error was: %s", seenErrStr )

	rxTest
		:expectEqual(
			string.find( seenErrStr, "docstore check threw error" ) ~= nil,
			true )
		
	rxTest:log( "check to make sure we still have a backup" )

	rxTest
		:expectEqual(
			WFFileUtils.exists( backupDSPath ),
			"file" )

end

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

local function rxTestBackupFailBecauseCorruptCheckFindsCorruption( rxTest )

	local dsPath = getDocstorePathForTest( rxTest )

	makeValidDb( dsPath )

	local _, backupDSPath = 
		NmbFreemiumUtils.privateUnitTestAPI.getFreemiumBackupPaths( dsPath )

	makeValidDb( backupDSPath )

	-- make an override that will return backup file attributes that will
	-- trigger must backup logic
	local overrides = 
			addTestOverrideFunction(
				{},
				"maybeGetFileAttributesImp", 
				function( p )

					local fileSize, fileCreationDate, fileModificationDate

					local origSize = 1

					local origModTime = 2 * k1WeekInSeconds

					if p == dsPath then

						fileSize = origSize

						fileCreationDate = 0

						fileModificationDate = origModTime

					end

					if p == backupDSPath then

						fileSize = origSize + k5Mb - 1

						fileCreationDate = origModTime - k1WeekInSeconds

						fileModificationDate = origModTime - k1WeekInSeconds

					end 

					return fileSize, fileCreationDate, fileModificationDate

				end )

	-- make another check that throws when checking corruption
	overrides =
			addTestOverrideFunction(
				overrides,
				"isDocstoreValidImp",
				function()

					return { "no ok!" }

				end )

	local seenErrStr

	rxTest
		:observe(
			NmbFreemiumUtils.privateUnitTestAPI.maybeBackupDocstoreResultStreamUsingTestOverrideFunctions( 
					dsPath, overrides )
				:tapOnError(
					function( errStr )

						seenErrStr = errStr

					end ) )
		:expectError( ":ignore:" )

	rxTest:logf( "check if we got the error we expected. raw error was: %s", seenErrStr )

	rxTest
		:expectEqual(
			string.find( seenErrStr, "corrupt error seen" ) ~= nil,
			true )
		
	rxTest:log( "check to make sure we still have a backup" )

	rxTest
		:expectEqual(
			WFFileUtils.exists( backupDSPath ),
			"file" )
		
end

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

local function rxTestNoBackupBecauseCantCreateBackupFolder( rxTest )

	local dsPath = getDocstorePathForTest( rxTest )

	makeValidDb( dsPath )

	-- make the file look big enough to backup
	local overrides = 
			addTestOverrideFunction(
				{},
				"maybeGetFileAttributesImp", 
				function( p )

					local fileSize, fileCreationDate, fileModificationDate

					if p == dsPath then

						fileSize = k5Mb + 1

						fileCreationDate = 0

						fileModificationDate = 0

					end

					return fileSize, fileCreationDate, fileModificationDate

				end )

	-- fake fail to create backup dir
	overrides = 
			addTestOverrideFunction(
				overrides,
				"WFFileUtils.mkdir", 
				function() error( "fake fail mkdir" ) end )

	local seenErrStr

	rxTest
		:observe(
			NmbFreemiumUtils.privateUnitTestAPI.maybeBackupDocstoreResultStreamUsingTestOverrideFunctions( 
					dsPath, overrides )
				:tapOnError(
					function( errStr )

						seenErrStr = errStr

					end ) )
		:expectError( ":ignore:" )

	rxTest
		:expectEqual(
			string.find( seenErrStr, "Can't create backup folder" ) ~= nil,
			true )
		
end

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

local function rxTestBackupFailBecauseCantDeleteExistingBackup( rxTest )

	local dsPath = getDocstorePathForTest( rxTest )

	makeValidDb( dsPath )

	local _, backupDSPath = 
		NmbFreemiumUtils.privateUnitTestAPI.getFreemiumBackupPaths( dsPath )

	makeValidDb( backupDSPath )

	-- make an override that will return backup file attributes that will
	-- trigger must backup logic
	local overrides = 
			addTestOverrideFunction(
				{},
				"maybeGetFileAttributesImp", 
				function( p )

					local fileSize, fileCreationDate, fileModificationDate

					local origSize = 1

					local origModTime = 2 * k1WeekInSeconds

					if p == dsPath then

						fileSize = origSize

						fileCreationDate = 0

						fileModificationDate = origModTime

					end

					if p == backupDSPath then

						fileSize = origSize + k5Mb - 1

						fileCreationDate = origModTime - k1WeekInSeconds

						fileModificationDate = origModTime - k1WeekInSeconds

					end 

					return fileSize, fileCreationDate, fileModificationDate

				end )

	overrides = 
			addTestOverrideFunction(
				overrides,
				"WFFileUtils.delete", 
				function() error( "fake file deletion error!" ) end )

	local seenErrStr

	rxTest
		:observe(
			NmbFreemiumUtils.privateUnitTestAPI.maybeBackupDocstoreResultStreamUsingTestOverrideFunctions( 
					dsPath, overrides )
				:tapOnError(
					function( errStr )

						seenErrStr = errStr

					end ) )
		:expectError( ":ignore:" )

		
	rxTest:logf( "check if we got the error we expected. raw error was: %s", seenErrStr )

	rxTest
		:expectEqual(
			string.find( seenErrStr, "Failed to delete existing" ) ~= nil,
			true )

end

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

local function rxTestBackupFailBecauseDidntCopy( rxTest )

	local dsPath = getDocstorePathForTest( rxTest )

	makeValidDb( dsPath )

	local _, backupDSPath = 
		NmbFreemiumUtils.privateUnitTestAPI.getFreemiumBackupPaths( dsPath )

	makeValidDb( backupDSPath )

	-- make an override that will return backup file attributes that will
	-- trigger must backup logic
	local overrides = 
			addTestOverrideFunction(
				{},
				"maybeGetFileAttributesImp", 
				function( p )

					local fileSize, fileCreationDate, fileModificationDate

					local origSize = 1

					local origModTime = 2 * k1WeekInSeconds

					if p == dsPath then

						fileSize = origSize

						fileCreationDate = 0

						fileModificationDate = origModTime

					end

					if p == backupDSPath then

						fileSize = origSize + k5Mb - 1

						fileCreationDate = origModTime - k1WeekInSeconds

						fileModificationDate = origModTime - k1WeekInSeconds

					end 

					return fileSize, fileCreationDate, fileModificationDate

				end )

	overrides = 
			addTestOverrideFunction(
				overrides,
				"WFAsyncFileUtils.copyToUniquelyNamedFile", 
				function() return Rx.Observable.throw( "fake copy error1", "fake copy error 2" ) end )

	local seenErrStr

	rxTest
		:observe(
			NmbFreemiumUtils.privateUnitTestAPI.maybeBackupDocstoreResultStreamUsingTestOverrideFunctions( 
					dsPath, overrides ) )
		:expectError( ":ignore:", ":ignore:" )

	rxTest
		:expectEqual(
			WFFileUtils.exists( backupDSPath ),
			false )

end

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

local testSuite = {
	
	rxTestFirstRun = rxTestFirstRun,

	rxTestNoBackupNeeded = rxTestNoBackupNeeded,

	rxTestNoBackupBecauseNoAttr = rxTestNoBackupBecauseNoAttr,

	rxTestDoBackupBecauseNoBackupAttr = rxTestDoBackupBecauseNoBackupAttr,

	rxTestNoBackupBecauseRecentBackup = rxTestNoBackupBecauseRecentBackup,

	rxTestFirstBackupBecauseSize = rxTestFirstBackupBecauseSize,

	rxTestAnotherBackupBecauseSize = rxTestAnotherBackupBecauseSize,

	rxTestFirstBackupBecauseTime = rxTestFirstBackupBecauseTime,

	rxTestAnotherBackupBecauseTime = rxTestAnotherBackupBecauseTime,

	rxTestBackupFailBecauseCorruptCheckThrows = 
		rxTestBackupFailBecauseCorruptCheckThrows,

	rxTestBackupFailBecauseCorruptCheckFindsCorruption =
		rxTestBackupFailBecauseCorruptCheckFindsCorruption,

	rxTestNoBackupBecauseCantCreateBackupFolder = 
		rxTestNoBackupBecauseCantCreateBackupFolder,

	rxTestBackupFailBecauseDidntCopy = rxTestBackupFailBecauseDidntCopy,
	
}

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

local function wrapInFreemiumPathSupport( f )

	return function( rxTest )

		_freemiumPathForTest[ rxTest ] = makeFreemiumPathForTest( rxTest )

		rxTest:finally(
			function()

				local freemiumDocStorePath = getDocstorePathForTest( rxTest )
		
				_freemiumPathForTest[ rxTest ] = nil

				local freemiumDocStoreBackupFolder, freemiumDocStoreBackupPath =
						NmbFreemiumUtils.privateUnitTestAPI.getFreemiumBackupPaths( 
							freemiumDocStorePath )

				pcall( WFFileUtils.delete, freemiumDocStorePath )

				pcall( WFFileUtils.delete, freemiumDocStoreBackupPath )

				pcall( WFFileUtils.delete, freemiumDocStoreBackupFolder )


			end )

		return f( rxTest )

	end

end

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

for testName, testFunc in pairs( testSuite ) do

	testSuite[ testName ] = wrapInFreemiumPathSupport( testFunc ) 
			
end

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

return WFRxTestCase.wrapSuite( 'NmbFreemiumUtilsTest', testSuite )
