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

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

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

	-- WF.core.os
local WFPlatformInfo = require 'WFPlatformInfo'

-- SPAM [immisch 2019-12-09]: The iOS tests are in LriOzPurgeAgentTest
if not WFPlatformInfo.isAndroid then
       return {}
end

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

	-- WF.core.utils
local KSIdGenerator = require 'KSIdGenerator'
local KSTableUtils = require 'KSTableUtils'
local KSDate = require "KSDate"

	-- WF.core.lua
local KSData = require 'KSData'

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

local LrmConfig = require "LrmConfig"

local LrmCatalogTestHelper = require 'LrmCatalogTestHelper'
local LrmOzPurgeAgent = require 'LrmOzPurgeAgent'

local KSPathUtils = require "KSPathUtils"

local OzFilePaths = require "OzFilePaths"

local WFCatUtils = require 'WFCatUtils'
local WFTableMerge = require 'WFTableMerge'

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

local LrmOzPurgeAgentTest = {}

local purgeAgent, catalog

local TEST_TIMEOUT = 10

local deref = KSTableUtils.derefOpt

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

local function writeTestDataToUniquelyNamedFile( filePath, optData )

	local testData = KSData.make( optData or 'foo' )
	KSData.saveToUniquelyNamedFile( testData, filePath )

	local size = KSData.length( testData )
	testData:close()

	return size

end

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

local function writeTestData( filePath, optData )

	local testData = KSData.make( optData or 'foo' )
	KSData.saveToUniquelyNamedFile( testData, filePath )
	
	local size = KSData.length( testData )
	testData:close()

	return size
	
end
	
--------------------------------------------------------------------------------

local function generateAssetWithLocalOriginal_Uploaded( optData, optEpoch )

	local testFilePath = LrmCatalogTestHelper.makeTestFilePath()	
	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local testDataSize = writeTestDataToUniquelyNamedFile( testFilePath, optData )

	return {
		type = 'asset',
		subtype = 'image',
		_id = assetId,
		importSource = {
			originalWidth = 100,
			originalHeight = 100,
		},
		_localOnly = {
			originalFilePath = testFilePath,
			original = {
				epoch = optEpoch or 1,
				size = testDataSize,
			}
		},
		_serverProvidedMetadata = {
			links = {
				[ '/rels/master' ] = {
					href = assetId,
				}
			}
		}
	}

end

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

local function generateVersionWithLocalXmpForAsset_Uploaded( assetId, optData, optSha256 )

	local versionId = KSIdGenerator.uuidInLowercaseHex()

	local developSha256 = optSha256 or KSIdGenerator.uuidInLowercaseHex()
	
	local settingsFileRelPath, settingsFileAbsPath = OzFilePaths.getXmpDevelopPath( catalog._catalogId, { _id = assetId }, developSha256 )

	local settingsFileSize = writeTestData( settingsFileAbsPath, optData )


	return {
		_id = versionId,
		created = "2018-08-23T20:25:31.964992Z",
		updated = "2018-08-23T20:25:31.964992Z",
		type = "version",
		subtype = "edit",
		name = 'foo',
		develop = {
			sha256 = developSha256
		},
		asset = {
			id = assetId,
		},
		_localOnly = {
			files = {
				xmp_develop = {
					path = settingsFileRelPath,
					fileSize = settingsFileSize,
					sha256 = developSha256
				}
			}
		},
		_serverProvidedMetadata = {
			links = {
				["/rels/xmp/develop"] = {
					fileSize = 26104,
					href = "<<stripped>>",
				}
			}
		}
	}, settingsFileAbsPath, developSha256

end

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

local function generateAlbumAsset( albumId, assetId )

	local albumAssetId = KSIdGenerator.uuidInLowercaseHex()

	return {

		type = 'album_asset',
		_id = albumAssetId,
		album = {
				id = albumId,
			},
			asset = {
				id = assetId,
			}
		}
		
end

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

function LrmOzPurgeAgentTest.setUp()

	catalog = LrmCatalogTestHelper.makeCatalogService()
	
	local options = {
		delay = 0.1
	}
	
	purgeAgent = LrmOzPurgeAgent( catalog, LrmConfig.StrategyPreferMaster, 1, options )
	
	purgeAgent:mayBeStartPurging()
	
end

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

function LrmOzPurgeAgentTest.tearDown()

	if purgeAgent then
	
		purgeAgent:close()
		purgeAgent = nil
		
	end
	
	if catalog then
	
		catalog:close()
		catalog = nil
	
	end
	
end

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

function LrmOzPurgeAgentTest.rxTestPurgeAlbum( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local albumId = KSIdGenerator.uuidInLowercaseHex()

	local asset = generateAssetWithLocalOriginal_Uploaded( 'foo' )
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( asset ),
		':ignore:' )

	local asset2 = generateAssetWithLocalOriginal_Uploaded( 'foo2' )
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( asset2 ),
		':ignore:' )
		
	local version, settingsFileAbsPath = generateVersionWithLocalXmpForAsset_Uploaded( asset._id, 'a' )
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( version ),
		':ignore:' )
		
	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
	
			type = 'album',
			subtype = 'collection',
			_id = albumId,
			name = 'foo',
			_localOnly = {
				useOffline = true,
			}
		},
		':ignore:')
		
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( generateAlbumAsset( albumId, asset._id ) ),
		':ignore:' )

	rxTest:expectSingleValue( catalog:docStore():applyOverlay( generateAlbumAsset( albumId, asset2._id ) ),
		':ignore:' )

	rxTest:expectSingleValue( purgeAgent:purgeForAlbum( albumId, LrmConfig.StrategyPreferMaster ), 0 )
	
	rxTest:expectSingleValue( catalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM cacheReferences ORDER BY size',
			quiesceUntilUpToDate = true 		
		},
		{
			{ version._id },
			{ asset._id },
			{ asset2._id },
		} ) 

	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
			
			type = 'album',
			subtype = 'collection',
			_id = albumId,
			name = 'foo',
			_localOnly = {
				useOffline = false,
			}
		},
		':ignore:' )


	rxTest:expectSingleValue( purgeAgent:purgeForAlbum( albumId, LrmConfig.StrategyPreferMaster ), 3 )

	rxTest:expectSingleValue( catalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM cacheReferences',
			quiesceUntilUpToDate = true 		
		},
		{ } )
	
	rxTest:expectEqual( false, WFFileUtils.exists( settingsFileAbsPath ))
	
end

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

function LrmOzPurgeAgentTest.rxTestPurgeAlbumWhileOffline( rxTest )
	-- Purge should not work when network is offline
	
	rxTest:setTimeLimit( TEST_TIMEOUT )

	local albumId = KSIdGenerator.uuidInLowercaseHex()
	
	purgeAgent:mayBeStopPurging()

	local asset = generateAssetWithLocalOriginal_Uploaded( 'foo' )
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( asset ),
		':ignore:' )

	local asset2 = generateAssetWithLocalOriginal_Uploaded( 'foo2' )
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( asset2 ),
		':ignore:' )
		
	rxTest:expectSingleValue( purgeAgent:purgeForAlbum( albumId, LrmConfig.StrategyPreferMaster ), 0 )
	
	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
	
			type = 'album',
			subtype = 'collection',
			_id = albumId,
			name = 'foo',
			_localOnly = {
				useOffline = true,
			}
		},
		':ignore:')
		
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( generateAlbumAsset( albumId, asset._id ) ),
		':ignore:' )

	rxTest:expectSingleValue( catalog:docStore():applyOverlay( generateAlbumAsset( albumId, asset2._id ) ),
		':ignore:' )

	rxTest:expectSingleValue( purgeAgent:purgeForAlbum( albumId, LrmConfig.StrategyPreferMaster ), 0 )
	
	rxTest:expectSingleValue( catalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM cacheReferences ORDER BY size',
			quiesceUntilUpToDate = true 		
		},
		{
			{ asset._id },
			{ asset2._id },
		} ) 

	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
			
			type = 'album',
			subtype = 'collection',
			_id = albumId,
			name = 'foo',
			_localOnly = {
				useOffline = false,
			}
		},
		':ignore:' )


	rxTest:expectSingleValue( purgeAgent:purgeForAlbum( albumId, LrmConfig.StrategyPreferMaster ), 0)

	rxTest:expectSingleValue( catalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM cacheReferences ORDER BY size',
			quiesceUntilUpToDate = true 		
		},
		{
			{ asset._id },
			{ asset2._id },
		} ) 


	purgeAgent:mayBeStartPurging()

end

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

function LrmOzPurgeAgentTest.rxTestPurgeImmediately1k( rxTest )

	rxTest:setTimeLimit( 120 )

	local albumId = KSIdGenerator.uuidInLowercaseHex()

	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
	
			type = 'album',
			subtype = 'collection',
			_id = albumId,
			name = 'foo',
		},
		':ignore:')

	local assetCount = 0

	-- do chunks of 100 for the bulk documents
	for i=1, 10 do

		local assets = {}
		local albumAssets = {}
		
		for j=1, 100 do

			local asset = generateAssetWithLocalOriginal_Uploaded()
			
			assets[ j ] = asset
			albumAssets[ j ] = generateAlbumAsset( albumId, asset._id ) 
						
		end
		
		rxTest:expectSingleValue( catalog:docStore():bulkApplyOverlay( assets ),
				':ignore:' )

		rxTest:expectSingleValue( catalog:docStore():bulkApplyOverlay( albumAssets ),
				':ignore:' )

		assetCount = assetCount + #assets

	end

	rxTest:expectSingleValue( purgeAgent:purgeImmediately( LrmConfig.StrategyPreferMaster ), assetCount )

	rxTest:expectSingleValue( catalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM cacheReferences',
			quiesceUntilUpToDate = true 		
		},
		{ } )
	
end

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

function LrmOzPurgeAgentTest.rxTestPurgeNoPurgeLimit1k( rxTest )

	rxTest:setTimeLimit( 120 )

	local albumId = KSIdGenerator.uuidInLowercaseHex()

	rxTest:expectSingleValue( catalog:docStore():applyOverlay {

			type = 'album',
			subtype = 'collection',
			_id = albumId,
			name = 'foo',
		},
		':ignore:')

	local assetCount = 0

	-- do chunks of 100 for the bulk documents
	for i=1, 10 do

		local assets = {}
		local albumAssets = {}

		for j=1, 100 do

			local asset = generateAssetWithLocalOriginal_Uploaded()
			asset._localOnly.original.size = 1000000 -- fake a considerable size

			assets[ j ] = asset
			albumAssets[ j ] = generateAlbumAsset( albumId, asset._id ) 

		end

		rxTest:expectSingleValue( catalog:docStore():bulkApplyOverlay( assets ),
				':ignore:' )

		rxTest:expectSingleValue( catalog:docStore():bulkApplyOverlay( albumAssets ),
				':ignore:' )

		assetCount = assetCount + #assets

	end

	rxTest:expectSingleValue( purgeAgent:purgeImmediately( LrmConfig.StrategyPreferMaster, true ), assetCount )

	rxTest:expectSingleValue( catalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM cacheReferences',
			quiesceUntilUpToDate = true
		},
		{ } )

end

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

function LrmOzPurgeAgentTest.rxTestPurgePresetAndBinaries( rxTest )

	local absTestFilePath = LrmCatalogTestHelper.makeTestFilePath()

	local testData = KSData.make( 'foo' )
	local sha256 = testData:digest( 'SHA256' )
	KSData.saveToUniquelyNamedFile( testData, absTestFilePath )

	local testDataSize = KSData.length( testData )
	testData:close()

	local PRESET_CONTENT_TYPE = "application/rdf+xml"

	local presetId = KSIdGenerator.uuidInLowercaseHex()
	local baseDocValue = WFCatUtils.createBaseOverlayWithUserCreatedFields( presetId )

	local overlay = {
		type = 'asset',
		subtype = 'preset',
		name = 'test_preset',
		if_none_match = sha256,
		captureDate = KSDate.timeInIso8601Format( nil, false ),
		importSource = {
			fileSize = testDataSize,
			sha256 = sha256,
			contentType = PRESET_CONTENT_TYPE
		},
		_localOnly = {
			files = {
				presetProfile = {
					sha256 = sha256,
					path = absTestFilePath,
				}
			}
		}
	}

	WFTableMerge.applyOverlay( overlay, baseDocValue )
	rxTest:expectSingleValue(
		catalog:docStore():applyOverlay ( overlay ), ':ignore:' )

	rxTest:expectSingleValue( catalog:indexStore():queryOnce
		{
			sql = [[SELECT docId FROM presetsProfiles WHERE sha256 == ?]],
			sha256,
			quiesceUntilUpToDate = true,
		},
		{
			{ presetId }
		}
	)

	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
		_id = presetId,
		_deleted = true,
	},
		':ignore:' )

	local obs = rxTest:observe( purgeAgent._purgeOnDeleteChain )
	obs:expectData( { presetId } ) -- 1 item should have been removed from the cache

	-- make sure preset file is gone
	rxTest:expectEqual( false, WFFileUtils.exists( absTestFilePath ) )

end

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

function LrmOzPurgeAgentTest.rxTestPurgePresetsWithRelativePaths( rxTest )

	local testData = KSData.make( 'foo' )
	local sha256 = testData:digest( 'SHA256' )
	local documentsPath = KSPathUtils.getStandardFilePath( 'carouselDocuments' )
	local catalogPath = KSPathUtils.child( documentsPath, catalog._catalogId )
	local presetId = KSIdGenerator.uuidInLowercaseHex()
	local relTestFilePath = "some_relative_path/preset_" .. presetId .. ".xmp"
	local absTestFilePath = KSPathUtils.child( catalogPath, relTestFilePath )

	WFFileUtils.mkdir( KSPathUtils.parent( absTestFilePath ) )
	KSData.saveToFile( testData, absTestFilePath )

	rxTest:finally( function()
		WFFileUtils.delete( absTestFilePath )
	end )

	local PRESET_CONTENT_TYPE = "application/rdf+xml"

	local testDataSize = KSData.length( testData )

	local baseDocValue = WFCatUtils.createBaseOverlayWithUserCreatedFields( presetId )

	local overlay = {
		type = 'asset',
		subtype = 'preset',
		name = 'test_preset',
		if_none_match = sha256,
		captureDate = KSDate.timeInIso8601Format( nil, false ),
		importSource = {
			fileSize = testDataSize,
			sha256 = sha256,
			contentType = PRESET_CONTENT_TYPE
		},
		_localOnly = {
			files = {
				presetProfile = {
					sha256 = sha256,
					path = relTestFilePath,
				}
			}
		}
	}

	WFTableMerge.applyOverlay( overlay, baseDocValue )
	rxTest:expectSingleValue(
		catalog:docStore():applyOverlay ( overlay ), ':ignore:' )

	rxTest:expectSingleValue( catalog:indexStore():queryOnce
		{
			sql = [[SELECT docId FROM presetsProfiles WHERE sha256 == ?]],
			sha256,
			quiesceUntilUpToDate = true,
		},
		{
			{ presetId }
		}
	)

	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
		_id = presetId,
		_deleted = true,
	},
		':ignore:' )

	local obs = rxTest:observe( purgeAgent._purgeOnDeleteChain )
	obs:expectData( { presetId } ) -- 1 item should have been removed from the cache

	-- make sure preset file is gone
	rxTest:expectEqual( false, WFFileUtils.exists( absTestFilePath ) )

end

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

function LrmOzPurgeAgentTest.rxTestPurgePresetsWithRelativePathAndCatalogIdPrepended( rxTest )

	-- Basically the same test as rxTestPurgePresetsWithRelativePaths, but the relativePath
	-- as stored in the document has now a catalogId prepended.

	-- Note: this case will not happen in the wild for presets, preset paths on Android should always
	-- be either absolute or relative without catalogId prepended.

	-- This test here is just to see if the LrmPurgeAgent local function absolutePath() works correctly
	-- if the catalogId is prepended to the stored relative path.

	local testData = KSData.make( 'bar' )
	local sha256 = testData:digest( 'SHA256' )
	local documentsPath = KSPathUtils.getStandardFilePath( 'carouselDocuments' )
	local catalogPath = KSPathUtils.child( documentsPath, catalog._catalogId )
	local presetId = KSIdGenerator.uuidInLowercaseHex()
	local relTestFilePath = "some_relative_path/preset_" .. presetId .. ".xmp"
	local absTestFilePath = KSPathUtils.child( catalogPath, relTestFilePath )

	WFFileUtils.mkdir( KSPathUtils.parent( absTestFilePath ) )
	KSData.saveToFile( testData, absTestFilePath )

	rxTest:finally( function()
		WFFileUtils.delete( absTestFilePath )
	end )

	local PRESET_CONTENT_TYPE = "application/rdf+xml"

	local testDataSize = KSData.length( testData )

	local baseDocValue = WFCatUtils.createBaseOverlayWithUserCreatedFields( presetId )

	local overlay = {
		type = 'asset',
		subtype = 'preset',
		name = 'test_preset',
		if_none_match = sha256,
		captureDate = KSDate.timeInIso8601Format( nil, false ),
		importSource = {
			fileSize = testDataSize,
			sha256 = sha256,
			contentType = PRESET_CONTENT_TYPE
		},
		_localOnly = {
			files = {
				presetProfile = {
					sha256 = sha256,
					path = KSPathUtils.child( catalog._catalogId, relTestFilePath ),
				}
			}
		}
	}

	WFTableMerge.applyOverlay( overlay, baseDocValue )
	rxTest:expectSingleValue(
		catalog:docStore():applyOverlay ( overlay ), ':ignore:' )

	rxTest:expectSingleValue( catalog:indexStore():queryOnce
		{
			sql = [[SELECT docId FROM presetsProfiles WHERE sha256 == ?]],
			sha256,
			quiesceUntilUpToDate = true,
		},
		{
			{ presetId }
		}
	)

	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
		_id = presetId,
		_deleted = true,
	},
		':ignore:' )

	local obs = rxTest:observe( purgeAgent._purgeOnDeleteChain )
	obs:expectData( { presetId } ) -- 1 item should have been removed from the cache

	-- make sure preset file is gone
	rxTest:expectEqual( false, WFFileUtils.exists( absTestFilePath ) )

end

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

function LrmOzPurgeAgentTest.rxTestPurgeAllOriginal( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	-- pretend we always need disk space
	purgeAgent.private._getDiskSpaceNeeded = function()
	
		return 1234567
	
	end

	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local albumId = KSIdGenerator.uuidInLowercaseHex()
	
	local testFilePath = LrmCatalogTestHelper.makeTestFilePath()
	
	local testData = KSData.make( 'foo' )
	KSData.saveToUniquelyNamedFile( testData, testFilePath )
	
	local testDataSize = KSData.length( testData )
	testData:close()

	rxTest:expectSingleValue( catalog:docStore()
		:applyOverlay {	
			_id = assetId,
			type = 'asset',
			subtype = 'image',
			importSource = {
				originalWidth = 100,
				originalHeight = 100,
			},
			_localOnly = {
				thumbnail2x = {
					md5 = 'foo',
				},
				originalFilePath = testFilePath,
				original = {
					epoch = 1,
					size = testDataSize,
				}
			}
		},
		':ignore:' )
	
	rxTest:expectSingleValue( catalog:indexStore():queryOnce
		{ 
			sql = [[SELECT docId FROM cacheReferences WHERE hasUploaded == 1]],
			quiesceUntilUpToDate = true,
		},
		{}
	)
		
	rxTest:expectSingleValue( catalog:docStore():applyOverlay {			
			_id = assetId,
			_serverProvidedMetadata = {
				links = {
					[ '/rels/master' ] = {
						href = '/v1.0/123..'
					}
				}
			}
		},
		':ignore:' )

	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
			type = 'album',
			subtype = 'collection',
			_id = albumId,
			name = 'foo',
		},
		':ignore:' )

	rxTest:expectSingleValue( purgeAgent:purgeImmediately( LrmConfig.StrategyPreferMaster ), 1 )
	
	local exists = WFFileUtils.exists( testFilePath )	
	rxTest:expectEqual(	exists, false )
		
	rxTest:expectSingleValue( catalog:docStore():docValue( assetId )
			
		:map( function( docValue )
				
			return deref( docValue, '_localOnly', 'original' ),
				   deref( docValue, '_localOnly', 'originalfilePath' )
					
		end ),
		nil, nil )

end

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

function LrmOzPurgeAgentTest.rxTestPurgeAllOriginalOfflineEditing( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local albumId = KSIdGenerator.uuidInLowercaseHex()
	
	local testFilePath = LrmCatalogTestHelper.makeTestFilePath()
	
	local testData = KSData.make( 'foo' )
	KSData.saveToUniquelyNamedFile( testData, testFilePath )
	
	local testDataSize = KSData.length( testData )
	testData:close()

	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
			
			type = 'asset',
			subtype = 'image',
			_id = assetId,
			importSource = {
				originalWidth = 100,
				originalHeight = 100,
			},
			_localOnly = {
				thumbnail2x = {
					md5 = 'foo',
				},
				originalFilePath = testFilePath,
				original = {
					epoch = 1,
					size = testDataSize,
				}
			},
			_serverProvidedMetadata = {
				links = {
					[ '/rels/master' ] = {
						href = '/v1.0/123..'
					}
				}
			}
		}, 
		':ignore:' )
		
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( generateAlbumAsset( albumId, assetId ) ),
		':ignore:' )
	
	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
			
			type = 'album',
			subtype = 'collection',
			_id = albumId,
			name = 'foo',
			_localOnly = {
				useOffline = true,
			}
		},
		':ignore:' )
		
	rxTest:expectSingleValue( purgeAgent:purgeImmediately( LrmConfig.StrategyPreferMaster ), 0 )

	local exists = WFFileUtils.exists( testFilePath )
	rxTest:expectEqual(	'file', exists )		

	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
			
			type = 'album',
			_id = albumId,
			name = 'foo',
			_localOnly = {
				useOffline = false,
			}
		},
		':ignore:')
		
	rxTest:expectSingleValue( purgeAgent:purgeImmediately( LrmConfig.StrategyPreferMaster ), 1 )
		
	exists = WFFileUtils.exists( testFilePath )
		
	rxTest:expectEqual(	false, exists )
		
end

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

function LrmOzPurgeAgentTest.rxTestPurgeAllOfflineEditing( rxTest )
 
	rxTest:setTimeLimit( TEST_TIMEOUT )

	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local albumId = KSIdGenerator.uuidInLowercaseHex()
	local albumAssetId = KSIdGenerator.uuidInLowercaseHex()

	local testFilePath = LrmCatalogTestHelper.makeTestFilePath()
	local proxyFilePath = LrmCatalogTestHelper.makeTestFilePath()
	
	local testData = KSData.make( 'foo' )
	
	KSData.saveToUniquelyNamedFile( testData, testFilePath )
	KSData.saveToUniquelyNamedFile( testData, proxyFilePath )
	
	local testDataSize = KSData.length( testData )
	testData:close()
	
	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
 			
		type = 'asset',
			_id = assetId,
			importSource = {
				originalWidth = 100,
				originalHeight = 100,
			},
			_localOnly = {
				thumbnail2x = {
					md5 = 'foo',
				},
				originalFilePath = testFilePath,
				original = {
					epoch = 1,
					size = testDataSize,
				},
				proxy = {
					filePath = proxyFilePath,
					size = testDataSize,
					epoch = 1,
				}
			},
			_serverProvidedMetadata = {
				links = {
					[ '/rels/master' ] = {
						href = '/v1.0/123..'
					}
				}
			}
		},
		':ignore:' )

 		
 	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
 			
			_id = albumId,
			type = 'album',
			subtype = 'collection',
			name = 'foo',
			_localOnly = {
				useOffline = true,
			}
		},
		':ignore:' )
 			

	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
		
			type = 'album_asset',
			_id = albumAssetId,
			album = {
				id = albumId,
			},
			asset = {
				id = assetId,
			}
		},
		':ignore:' )
 	
	local version, versionFilePath = generateVersionWithLocalXmpForAsset_Uploaded( assetId, 'hi' )
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( version ),
		':ignore:' )
	
	-- StrategyProxy (or StrategyMaster) should not delete any original or proxy in offline albums
	rxTest:expectSingleValue( purgeAgent:purgeImmediately( LrmConfig.StrategyProxy ), 0 )
 		
	local exists = WFFileUtils.exists( testFilePath )

	rxTest:expectEqual(	'file', exists )

	exists = WFFileUtils.exists( proxyFilePath )
 		
	rxTest:expectEqual(	'file', exists )

	exists = WFFileUtils.exists( versionFilePath )
 		
	rxTest:expectEqual(	'file', exists )

 end

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

function LrmOzPurgeAgentTest.rxTestPurgeContinuously( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )
	
	-- pretend we always need disk space
	purgeAgent.private._getDiskSpaceNeeded = function()
	
		return 1234567
	
	end

	local sum = 0
	
	local obs = rxTest:observe( purgeAgent:startContinuousPurge( LrmConfig.StrategyPreferMaster )
		:map( function( count )
			sum = sum + count
			return sum
		end ) )

	local asset = generateAssetWithLocalOriginal_Uploaded( 'foo' )
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( asset ),
		':ignore:' )

	-- an asset with a higher epoch
	local asset2 = generateAssetWithLocalOriginal_Uploaded( 'foo', 2 )
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( asset2 ),
		':ignore:' )

	local asset3 = generateAssetWithLocalOriginal_Uploaded( 'foo', 3 )
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( asset3 ),
		':ignore:' )

			
	obs:expectDataWithinTime( TEST_TIMEOUT / 2, 2 )
	
	rxTest:expectSingleValue( catalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM cacheReferences ORDER BY size',
			quiesceUntilUpToDate = true
		},
		{
			{ asset3._id },
		} ) 
	
end
 
--------------------------------------------------------------------------------

function LrmOzPurgeAgentTest.rxTestPurgeContinuouslyRestarted( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	-- pretend we always need disk space
	purgeAgent.private._getDiskSpaceNeeded = function()

		return 1234567

	end

	local sum = 0

	purgeAgent:mayBeStopPurging()

	local obs = rxTest:observe( purgeAgent:startContinuousPurge( LrmConfig.StrategyPreferMaster )
		:map( function( count )
			sum = sum + count
			return sum
		end ) )

	local asset = generateAssetWithLocalOriginal_Uploaded( 'foo' )
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( asset ),
		':ignore:' )

	local asset2 = generateAssetWithLocalOriginal_Uploaded( 'fooo', 2 )
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( asset2 ),
		':ignore:' )

	local asset3 = generateAssetWithLocalOriginal_Uploaded( 'foooo', 3 )
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( asset3 ),
		':ignore:' )

	obs:expectDataWithinTime( TEST_TIMEOUT / 2, 0 )

	purgeAgent:mayBeStartPurging()

	obs:expectDataWithinTime( TEST_TIMEOUT / 2, 2 )

	rxTest:expectSingleValue( catalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM cacheReferences ORDER BY size',
			quiesceUntilUpToDate = true
		},
		{
			{ asset3._id },
		} )

end

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

function LrmOzPurgeAgentTest.rxTestPurgeContinuouslyWithFileLock( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT * 2 )

	-- pretend we always need disk space
	purgeAgent.private._getDiskSpaceNeeded = function()

		return 1234567

	end

	local asset = generateAssetWithLocalOriginal_Uploaded( 'foo' )
	rxTest:waitFor( catalog:docStore():applyOverlay( asset ) )

	-- an asset with a higher epoch
	local asset2 = generateAssetWithLocalOriginal_Uploaded( 'fooo', 2 )
	rxTest:waitFor( catalog:docStore():applyOverlay( asset2 ) )

	local asset3 = generateAssetWithLocalOriginal_Uploaded( 'foooo', 3 )
	rxTest:waitFor( catalog:docStore():applyOverlay( asset3 ) )

	purgeAgent:lockAssetForPurge( asset._id )

	local totalPurgedDocIds = {}
	local sum = 0
	
	local obs = rxTest:observe( purgeAgent:startContinuousPurge( LrmConfig.StrategyPreferMaster )
	
			:map( function( _, _, purgedDocIds )
			
				for _, docId in ipairs( purgedDocIds ) do
				
					if not totalPurgedDocIds[ docId ] then
						sum = sum + 1
					end
				
					totalPurgedDocIds[ docId ] = true
										
				end
				
				return sum
				
			end ) )

	obs:expectDataWithinTime( TEST_TIMEOUT / 2, 1 )

	rxTest:expectSingleValue( catalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM cacheReferences ORDER BY size',
			quiesceUntilUpToDate = true
		},
		{
			{ asset._id },
			{ asset3._id },
		} )

	purgeAgent:unlockAssetForPurge( asset._id )

	-- unlockAssetForPurge will not kick off another purge cycle
	-- we need to add another asset to reliably get the purge query to fire again 
	local asset4 = generateAssetWithLocalOriginal_Uploaded( 'fooooo', 4 )
	rxTest:waitFor( catalog:docStore():applyOverlay( asset4 ) )

	obs:expectDataWithinTime( TEST_TIMEOUT, 3 )

	rxTest:expectSingleValue( catalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM cacheReferences ORDER BY size',
			quiesceUntilUpToDate = true
		},
		{
			{ asset4._id },
		} )

end

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

function LrmOzPurgeAgentTest.rxTestPurgeDeletedDocument( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local testFilePath = LrmCatalogTestHelper.makeTestFilePath()
	
	local thumbnail2x = KSData.make( 'foo' )
	KSData.saveToUniquelyNamedFile( thumbnail2x, testFilePath )

	rxTest:finally( function()
		WFFileUtils.delete( testFilePath )
	end )

	local xmp = KSData.make( '<xmp />' )
	local xmpSha256 = xmp:digest( 'SHA256' )
	local xmpPath, xmpAbsPath = OzFilePaths.getXmpDevelopPath( catalog._catalogId, { _id  = assetId }, xmpSha256, true )	
	
	KSData.saveToFile( xmp, xmpAbsPath )

	rxTest:finally( function()
		WFFileUtils.delete( xmpAbsPath )
	end )

	rxTest:expectSingleValue( catalog:getDataCache()
		:flatMap( function( dataCache )
			return dataCache:put( 'foo', thumbnail2x )
		end )
		:map( tostring ),
		'foo' )

	-- create an asset with a thumbnail and external develop settings
	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
			
			type = 'asset',
			subtype = 'image',
			_id = assetId,
			importSource = {
				originalWidth = 100,
				originalHeight = 100,
			},
			_localOnly = {
				thumbnail2x = {
					md5 = 'foo',
				},
				files = {
					xmp_develop = {
						path = xmpPath,
						sha256 = xmpSha256
					}
				},
			},
		}, 
		':ignore:' )

	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
			_id = assetId,
			_deleted = true,
		},
		':ignore:' )

	local obs = rxTest:observe( purgeAgent._purgeOnDeleteChain )
	obs:expectData( { assetId } ) -- 1 item should have been removed from the cache

	-- make sure external develop is gone
	rxTest:expectEqual( false, WFFileUtils.exists( xmpAbsPath ) )

	-- make sure thumbnail is gone
	rxTest:expectSingleValue( catalog:getDataCache()
		:flatMap( function( dataCache )
			return dataCache:get( 'foo' )
		end ),
		nil )

end

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

function LrmOzPurgeAgentTest.rxTestPurgeAlbumAssets( rxTest )
 
	rxTest:setTimeLimit( TEST_TIMEOUT )

	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local albumId = KSIdGenerator.uuidInLowercaseHex()
	local albumAssetId = KSIdGenerator.uuidInLowercaseHex()

	local testFilePath = LrmCatalogTestHelper.makeTestFilePath()
	local proxyFilePath = LrmCatalogTestHelper.makeTestFilePath()
	
	local testData = KSData.make( 'foo' )
	
	KSData.saveToUniquelyNamedFile( testData, testFilePath )
	KSData.saveToUniquelyNamedFile( testData, proxyFilePath )
	
	local testDataSize = KSData.length( testData )
	testData:close()
	
	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
 			
		type = 'asset',
			_id = assetId,
			importSource = {
				originalWidth = 100,
				originalHeight = 100,
			},
			_localOnly = {
				thumbnail2x = {
					md5 = 'foo',
				},
				originalFilePath = testFilePath,
				original = {
					epoch = 1,
					size = testDataSize,
				},
				proxy = {
					filePath = proxyFilePath,
					size = testDataSize,
					epoch = 1,
				}
			},
			_serverProvidedMetadata = {
				links = {
					[ '/rels/master' ] = {
						href = '/v1.0/123..'
					}
				}
			}
		},
		':ignore:' )

 		
 	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
 			
			_id = albumId,
			type = 'album',
			subtype = 'collection',
			name = 'foo',
			_localOnly = {
				useOffline = true,
			}
		},
		':ignore:' )
 			

	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
		
			type = 'album_asset',
			_id = albumAssetId,
			album = {
				id = albumId,
			},
			asset = {
				id = assetId,
			}
		},
		':ignore:' )
 	
	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
			_id = albumId,
			_deleted = true,
		},
		':ignore:' )

	local obs = rxTest:observe( catalog:indexStore():query{
			sql = 'SELECT docId FROM cacheReferences WHERE docId = ?',
			quiesceUntilUpToDate = true,
			albumId
		} )
	
	obs:expectDataWithinTime( 10, {} )
	
	obs:dispose()
	
	obs = rxTest:observe( catalog:indexStore():query{
			sql = 'SELECT COUNT(docId) FROM album_asset WHERE albumId = ?',
			quiesceUntilUpToDate = true,
			albumId
		} )
		
	obs:expectDataWithinTime( 10, { { 0 } } )
	
	obs:dispose()

 end

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

function LrmOzPurgeAgentTest.rxTestPurgeSpaceAssets( rxTest )
 
	rxTest:setTimeLimit( TEST_TIMEOUT )

	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local spaceId = KSIdGenerator.uuidInLowercaseHex()
	local spaceFavoriteId = KSIdGenerator.uuidInLowercaseHex()

	local testFilePath = LrmCatalogTestHelper.makeTestFilePath()
	local proxyFilePath = LrmCatalogTestHelper.makeTestFilePath()
	
	local testData = KSData.make( 'foo' )
	
	KSData.saveToUniquelyNamedFile( testData, testFilePath )
	KSData.saveToUniquelyNamedFile( testData, proxyFilePath )
	
	local testDataSize = KSData.length( testData )
	testData:close()
	
	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
 			
		type = 'asset',
			_id = assetId,
			importSource = {
				originalWidth = 100,
				originalHeight = 100,
			},
			_localOnly = {
				thumbnail2x = {
					md5 = 'foo',
				},
				originalFilePath = testFilePath,
				original = {
					epoch = 1,
					size = testDataSize,
				},
				proxy = {
					filePath = proxyFilePath,
					size = testDataSize,
					epoch = 1,
				}
			},
			_serverProvidedMetadata = {
				links = {
					[ '/rels/master' ] = {
						href = '/v1.0/123..'
					}
				}
			}
		},
		':ignore:' )

 		
 	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
 			
			_id = spaceId,
			type = 'space',
			subtype = 'shared',
			download = false,
			private = false,
			location = false,
			metadata = false,
		},
		':ignore:' )


	for i = 1, 101 do

		local spaceCommentId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue( catalog:docStore():applyOverlay {
			
				type = 'comment',
				subtype = 'user',
				_id = spaceCommentId,
				space = {
					id = spaceId,
				},
				asset = {
					id = assetId,
				},
				body = string.format( '/* no comment %d */', i ),
				_localOnly = {
					created = KSDate.timeInIso8601Format(),
				}
			},
			':ignore:' )
	end

	rxTest:expectSingleValue( catalog:indexStore():queryOnce{
			sql = 'SELECT COUNT(docId) FROM comment WHERE spaceId = ?',
			quiesceUntilUpToDate = true,
			spaceId
		},
		{ { 101 } } )
	
	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
		
			type = 'favorite',
			subtype = 'user',
			_id = spaceFavoriteId,
			space = {
				id = spaceId,
			},
			asset = {
				id = assetId,
			},
			_localOnly = {
				created = "2016-05-11T20:23:22.018642Z",
			}
		},
		':ignore:' )

	rxTest:expectSingleValue( catalog:indexStore():queryOnce{
			sql = 'SELECT COUNT(docId) FROM spacefavorite WHERE spaceId = ?',
			quiesceUntilUpToDate = true,
			spaceId
		},
		{ { 1 } } )

 	
	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
			_id = spaceId,
			_deleted = true,
		},
		':ignore:' )

	local obs = rxTest:observe( catalog:indexStore():query{
			sql = 'SELECT COUNT(docId) FROM comment WHERE spaceId = ?',
			quiesceUntilUpToDate = true,
			spaceId
		} )
	
	obs:expectDataWithinTime( 10, { { 0 } } )
	
	obs:dispose()
	
	obs = rxTest:observe( catalog:indexStore():query{
			sql = 'SELECT COUNT(docId) FROM spacefavorite WHERE spaceId = ?',
			quiesceUntilUpToDate = true,
			spaceId
		} )
		
	obs:expectDataWithinTime( 10, { { 0 } } )
	
	obs:dispose()
	
 end
 
 --------------------------------------------------------------------------------

function LrmOzPurgeAgentTest.rxTestAssetDeletionWithLocalNewId( rxTest )
 
	rxTest:setTimeLimit( TEST_TIMEOUT )

	local assetId = KSIdGenerator.uuidInLowercaseHex()
	
	local testFilePath = LrmCatalogTestHelper.makeTestFilePath()
	local proxyFilePath = LrmCatalogTestHelper.makeTestFilePath()
	
	local testData = KSData.make( 'foo' )
	
	KSData.saveToUniquelyNamedFile( testData, testFilePath )
	KSData.saveToUniquelyNamedFile( testData, proxyFilePath )
	
	local testDataSize = KSData.length( testData )
	testData:close()

	local thumbnail2x = KSData.make( 'foo' )
	KSData.saveToUniquelyNamedFile( thumbnail2x, testFilePath )

	local xmp = KSData.make( '<xmp />' )
	local xmpSha256 = xmp:digest( 'SHA256' )
	local xmpRelPath, xmpAbsPath = OzFilePaths.getXmpDevelopPath( catalog._catalogId, { _id = assetId }, xmpSha256, true)
	
	KSData.saveToFile( xmp, xmpAbsPath )

	rxTest:finally( function()
		WFFileUtils.delete( xmpAbsPath )
	end )

	rxTest:expectSingleValue( catalog:getDataCache()
		:flatMap( function( dataCache )
			return dataCache:put( 'foo', thumbnail2x )
		end )
		:map( tostring ),
		'foo' )


	rxTest:expectSingleValue( catalog:docStore()
		:applyOverlay {	
			_id = assetId,
			type = 'asset',
			subtype = 'image',
			importSource = {
				originalWidth = 100,
				originalHeight = 100,
			},
			_localOnly = {
				thumbnail2x = {
					md5 = 'foo',
				},
				files = {
					xmp_develop = {
						path = xmpRelPath,
						sha256 = xmpSha256
					}
				},
				originalFilePath = testFilePath,
				original = {
					epoch = 1,
					size = testDataSize,
				},
				proxy = {
					filePath = proxyFilePath,
					size = testDataSize,
					epoch = 1,
				}
			}
		},
		':ignore:' )
		
	-- Delete asset with new_id from Oz
	rxTest:expectSingleValue( catalog:docStore()
		:applyOverlay {
			_id = assetId,
			_deleted = true,
			_newId = KSIdGenerator.uuidInLowercaseHex()
		}, 
		':ignore:' )	
	
	local obs = rxTest:observe( purgeAgent._purgeOnDeleteChain )
	obs:expectData( { assetId } ) 
	
	-- make sure original and proxy are not purged	
	rxTest:expectEqual(	'file', WFFileUtils.exists( testFilePath ) )
	rxTest:expectEqual(	'file', WFFileUtils.exists( proxyFilePath ) )
	
	-- make sure external develop is not purged
	rxTest:expectEqual( 'file', WFFileUtils.exists( xmpAbsPath ) )

	-- make sure thumbnail is not purged
	rxTest:expectSingleValue( catalog:getDataCache()
		:flatMap( function( dataCache )
			return dataCache:get( 'foo' )
		end )
		:map( tostring ),
		'foo' )
	
end

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

function LrmOzPurgeAgentTest.rxTestAssetDeletionWithNewIdFromOz( rxTest )
 
	rxTest:setTimeLimit( TEST_TIMEOUT )

	local assetId = KSIdGenerator.uuidInLowercaseHex()
	
	local testFilePath = LrmCatalogTestHelper.makeTestFilePath()
	local proxyFilePath = LrmCatalogTestHelper.makeTestFilePath()
	
	local testData = KSData.make( 'foo' )
	
	KSData.saveToUniquelyNamedFile( testData, testFilePath )
	KSData.saveToUniquelyNamedFile( testData, proxyFilePath )
	
	local testDataSize = KSData.length( testData )
	testData:close()


	rxTest:expectSingleValue( catalog:docStore()
		:applyOverlay {	
			_id = assetId,
			type = 'asset',
			subtype = 'image',
			importSource = {
				originalWidth = 100,
				originalHeight = 100,
			},
			_localOnly = {
				originalFilePath = testFilePath,
				original = {
					epoch = 1,
					size = testDataSize,
				},
				proxy = {
					filePath = proxyFilePath,
					size = testDataSize,
					epoch = 1,
				}
			}
		},
		':ignore:' )
		
	local new_id = KSIdGenerator.uuidInLowercaseHex()
	
	-- Delete asset with new_id from Oz
	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
	
			_id = assetId,
			_deleted = true,
			
			_serverProvidedMetadata = {
				new_id = new_id
			}
		}, 
		':ignore:' )
	
	local obs = rxTest:observe( purgeAgent._purgeOnDeleteChain )
	obs:expectData( { assetId } ) 
	
	-- make sure new asset is with master info
	rxTest:observe( catalog:docStore():ongoingDocValue( new_id ) 
			:map( function( docValue ) 
				return deref( docValue, '_localOnly', 'originalFilePath' ) == testFilePath
					and deref( docValue, '_localOnly', 'original', 'size' ) == testDataSize
					and deref( docValue, '_localOnly', 'proxy', 'filePath' ) == proxyFilePath
					and deref( docValue, '_localOnly', 'proxy', 'size' ) == testDataSize
			end )
		)
		:expectData( true )
		:dispose()
	
	-- make sure files are not purged
	-- make sure original and proxy are not purged	
	rxTest:expectEqual(	'file', WFFileUtils.exists( testFilePath ) )
	rxTest:expectEqual(	'file', WFFileUtils.exists( proxyFilePath ) )
	
end

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

function LrmOzPurgeAgentTest.rxTestPurgeDevelopXmpRefCounting( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	-- Create first album containing one asset that has a version.
	
	local albumId = KSIdGenerator.uuidInLowercaseHex()

	local asset = generateAssetWithLocalOriginal_Uploaded( 'foo' )
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( asset ),
		':ignore:' )
		
	local version, settingsFileAbsPath, settingsSha256 
			= generateVersionWithLocalXmpForAsset_Uploaded( asset._id, 'a' )
			
	version._id = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
			
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( version ),
		':ignore:' )
		
	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
	
			type = 'album',
			subtype = 'collection',
			_id = albumId,
			name = 'foo',
			_localOnly = {
				useOffline = false,
			}
		},
		':ignore:')
		
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( generateAlbumAsset( albumId, asset._id ) ),
		':ignore:' )
		
	-- Now create a second asset that contains a version. This version's external XMP
	-- should match that of the first versions..

	local asset2 = generateAssetWithLocalOriginal_Uploaded( 'foo2' )
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( asset2 ),
		':ignore:' )
		
	local version2 = generateVersionWithLocalXmpForAsset_Uploaded( asset2._id, 'a', settingsSha256 )
	
	version2._id = 'zzz8398fffb345db82fcb8f0ba688d3e'
	
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( version2 ),
		':ignore:' )
		
	rxTest:expectSingleValue( catalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM cacheReferences ORDER BY size, docId ASC',
			quiesceUntilUpToDate = true 		
		},
		{
			{ version._id },
			{ version2._id },
			{ asset._id },
			{ asset2._id },
		} ) 

	-- Purge the album. This should *not* remove the develop settings file since it's 
	-- also being used by the version document.
		
	rxTest:expectSingleValue( purgeAgent:purgeForAlbum( albumId, LrmConfig.StrategyPreferMaster ), 2 )
		
	rxTest:expectSingleValue( catalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM cacheReferences ORDER BY size',
			quiesceUntilUpToDate = true 		
		},
		{ 
			{ version2._id },
			{ asset2._id },
		} )
		
	rxTest:expectEqual(	'file', WFFileUtils.exists( settingsFileAbsPath ) )
	
	-- Now remove the version file. This should cause the develop settings file to be removed
	-- because it's no longer referenced.
	
	rxTest:waitFor( catalog:docStore():deleteDoc( version2._id ) )
	
	local obs = rxTest:observe( purgeAgent._purgeOnDeleteChain )
	rxTest:finally( function() 
		obs:dispose()
	end )
	
	obs:expectData( { version2._id } ) -- 1 item should have been removed from the cache

	rxTest:expectSingleValue( catalog:indexStore():queryOnce{
			sql = 'SELECT docId FROM cacheReferences ORDER BY size',
			quiesceUntilUpToDate = true 		
		},
		{ 
			{ asset2._id }
		} )
	
	rxTest:expectEqual(	false, WFFileUtils.exists( settingsFileAbsPath ) )
	
end

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

function LrmOzPurgeAgentTest.rxTestPreparePurgeImp( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )

	local asset = generateAssetWithLocalOriginal_Uploaded( 'foo' )
	local id1 = asset._id
	
	local asset2 = generateAssetWithLocalOriginal_Uploaded( 'foo2' )
	
	KSTableUtils.setNested( asset2, "does not match",
				'importSource',
				'status',
				'error',
				'all',
				'reason',
				'sha256' )
	
	local id2 = asset2._id
	
	local spaceNeeded = 1000000
	
	local docValuesMap = {}
	docValuesMap[ id1 ] = asset
	docValuesMap[ id2 ] = asset2
	
	local victims = {
		{
			id1,
			id1,
			'original',
			"2021-11-15T18:58:31.754Z",
			50000
		},
		{
			id2,
			id2,
			'original',
			"2021-11-15T18:58:31.754Z",
			10000
		}
	}
	
	local expected = {
		debugDocIds = {
			id1,
		},
		developSettingsRelPathsToDelete = {},
		docs = {
			[ id1 ] = {
				_id = id1,
				_localOnly = {
					original = {
						_delete = true,
					},
					originalFilePath = {
						_delete = true,
					},
				},
			},
		},
		purged = 1,
		removeCache = {},
		removeFiles = {
			"2021-11-15T18:58:31.754Z",
		},
		spaceReclaimed = 50000,
	}
	
	rxTest:expectEqual( expected, purgeAgent.private._preparePurgeImp(
			victims, spaceNeeded, {}, docValuesMap ) )
	
end

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

return WFRxTestCase.wrapSuite( LrmOzPurgeAgentTest )
