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

WFCatSyncAutoDeleterTest.lua

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

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

NOTICE: All information contained herein is, and remains 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 are protected by all applicable
intellectual property laws, including trade secret and copyright laws.
Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.

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

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

	-- WF.core.utils
local _ = require 'WFArguments_uuid'
local WFOzTestUtils = require 'WFOzTestUtils'
local WFPlatformInfo = require 'WFPlatformInfo'
local KSIdGenerator = require 'KSIdGenerator'
local KSDate = require 'KSDate'
local WFFileUtils = require 'WFFileUtils'
local KSData = require 'KSData'

	-- WF.oz_client.managed_catalog
local WFCatAlbums = require 'WFCatAlbums'
local WFCatAssetTestUtils = require 'WFCatAssetTestUtils'
local WFCatMockCatalog = require 'WFCatMockCatalog'
local WFCatSyncAutoDeleter = require 'WFCatSyncAutoDeleter'
local WFCatVersions = require 'WFCatVersions'
local LrmCatalogTestHelper

	-- WF.lrmobile.modules.source
local LrmOzPurgeAgentUtils

if WFPlatformInfo.isMobile() then
	LrmCatalogTestHelper = require 'LrmCatalogTestHelper'
	LrmOzPurgeAgentUtils = require 'LrmOzPurgeAgentUtils'
end

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

local WFCatMockCatalog_createFakeWFCatalog = WFCatMockCatalog.createFakeWFCatalog

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

local albumAssetTableName = 'album_asset_v2'
local assetTableName = 'assets'
local assetTableAssetIdColumnName = 'assetId'
local albumTableAlbumIdColumnName = 'albumId'

if WFPlatformInfo.isMobile() then
	albumAssetTableName = 'album_asset'
	assetTableName = 'coreInfo'
	assetTableAssetIdColumnName = 'docId'
	albumTableAlbumIdColumnName = 'docId'
end

local function createFakeWFCatalog( rxTest )

	local optArgs = {}
	
	if WFPlatformInfo.isMobile() then
		optArgs.indexDefinitions = 'LrmCatalogIndexDefinitions.lua'
	end

	return WFCatMockCatalog_createFakeWFCatalog( rxTest, nil, nil, optArgs )
	
end


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

local WFCatSyncAutoDeleterTest = {}

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

local function makeAdderOfAlbum( rxTest, wfCatalog )

	local counter = 9

	return function( parentId )

		local albumName = string.format( "Album %d", counter )

		local docId = KSIdGenerator.uuidInLowercaseHex()

		local albumDoc = {

			_id = docId,
			type = 'album',
			subtype = 'collection',
			name = albumName,
			parent = parentId and {
				id = parentId
			}

		}
	
		rxTest:expectSingleValue( wfCatalog:docStore():setDocValue( albumDoc ), ':ignore:' )

		counter = counter - 1

		return docId

	end

end

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

function WFCatSyncAutoDeleterTest.rxTestAutoDeleter_DeleteAlbumAssets( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local docStore = wfCatalog:docStore()
	local indexStore = wfCatalog:indexStore()

	local options = {
		delay = 0.1
	}

	local autoDeleter = WFCatSyncAutoDeleter( wfCatalog, options )

	local initialCounter = 0
	
	rxTest:expectSingleValue( docStore:localDocValue( 'WFCatSyncAutoDeleter-changeCounter' )
		:tap( function( docValue )
			initialCounter = docValue.changeCounter or 0
		end ), ':ignore:' )

	local addAlbumFn = makeAdderOfAlbum( rxTest, wfCatalog )
	
	local addAssetFn = WFOzTestUtils.v2.makeAdderOfAsset( rxTest, docStore )

	local observer = rxTest:observe( indexStore:query {
			sql = [[SELECT assetId FROM ]] .. albumAssetTableName,
			quiesceUntilUpToDate = true,
		} )
		:expectDataWithinTime( 5, {} )

	observer:dispose()

	local docAlbumId = addAlbumFn()
	
	local docId1 = addAssetFn()
	local docId2 = addAssetFn()
	local docId3 = addAssetFn()

	local albumAssetDoc1 = WFCatAlbums.createAlbumAssetDocument( docAlbumId, docId1 )
	rxTest:expectSingleValue( docStore:setDocValue( albumAssetDoc1 ), ':ignore:' )
	
	local albumAssetDoc2 = WFCatAlbums.createAlbumAssetDocument( docAlbumId, docId2 )
	rxTest:expectSingleValue( docStore:setDocValue( albumAssetDoc2 ), ':ignore:' )
	
	local albumAssetDoc3 = WFCatAlbums.createAlbumAssetDocument( docAlbumId, docId3 )
	rxTest:expectSingleValue( docStore:setDocValue( albumAssetDoc3 ), ':ignore:' )

	-- Delete an asset.

	local overlay = {
		_id = docId1,
		_deleted = true
	}

	rxTest:expectOnlyCompleted(
		docStore:applyOverlay( overlay )
			:takeLast( 0 )
	)

	-- Make sure the asset deletion caused the album asset to get removed.

	observer = rxTest:observe( indexStore:query {
			sql = [[SELECT assetId FROM ]] .. albumAssetTableName .. [[ WHERE albumId IN (?)]],
			quiesceUntilUpToDate = true,
			docAlbumId
		} )
		:expectDataWithinTime( 5, { { docId2 }, { docId3 } } )
	
	observer:dispose()
	
	-- Delete the album

	overlay = {
		_id = docAlbumId,
		_deleted = true
	}

	rxTest:expectOnlyCompleted(
		docStore:applyOverlay( overlay )
			:takeLast( 0 )
	)

	-- Make sure the album deletion caused the album assets to get removed.

	observer = rxTest:observe( indexStore:query {
			sql = [[SELECT docId FROM ]] .. albumAssetTableName,
			quiesceUntilUpToDate = true,
		} )
		:expectDataWithinTime( 5, {} )

	observer:dispose()

	rxTest:finally( function()
		autoDeleter:close()
	end )

	-- Verify task change counter has incremented.
	
	observer = rxTest:observe( docStore:ongoingLocalDocValue( 'WFCatSyncAutoDeleter-changeCounter' )
		:map( function( docValue )
			local currentChangeCounter = docValue.changeCounter or 0
			return currentChangeCounter > initialCounter
		end ) )
		:expectDataWithinTime( 5, true )

end

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

function WFCatSyncAutoDeleterTest.rxTestAutoDeleter_Bulk( rxTest )

	rxTest:setTimeLimit( 20 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local docStore = wfCatalog:docStore()
	local indexStore = wfCatalog:indexStore()

	local options = {
		delay = 0.1
	}

	local autoDeleter = WFCatSyncAutoDeleter( wfCatalog, options )

	local addAlbumFn = makeAdderOfAlbum( rxTest, wfCatalog )
	
	local addAssetFn = WFOzTestUtils.v2.makeAdderOfAsset( rxTest, docStore, 
			function( baseDocValue, date)

				-- set capture date to what we want
				baseDocValue.captureDate = date

				return baseDocValue

			end )

	-- Add a bunch of assets/albums
	
	local allAssetIds = {}
	
	for i=1, 3, 1 do

		local albumId = addAlbumFn( KSDate.timeInIso8601Format() )
		
		local docIds = {}
		local albumAssetDocValues = {}
		
		for j=57,1,-1 do

			local docId = addAssetFn()
			table.insert( docIds, docId )
			table.insert( allAssetIds, docId )

			local albumAssetDoc = WFCatAlbums.createAlbumAssetDocument( albumId, docId )
		
			table.insert( albumAssetDocValues, albumAssetDoc )
			
		end

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

	end

	-- Delete All Assets.

	local overlays = {}
	
	for i, assetId in ipairs( allAssetIds ) do
	
		local overlay = {
			_id = assetId,
			_deleted = true
		}
		
		table.insert( overlays, overlay )
		
	end
	
	rxTest:expectOnlyCompleted(
			docStore:bulkApplyOverlay( overlays )
				:takeLast( 0 )
		)

	-- Make sure the asset deletion caused the album asset to get removed.

	local observer = rxTest:observe( indexStore:query {
			sql = [[SELECT docId FROM ]] .. assetTableName,
			quiesceUntilUpToDate = true,
		} ):expectDataWithinTime( 20, {} )

	observer:dispose()

	observer = rxTest:observe( indexStore:query {
			sql = [[SELECT assetId FROM ]] .. albumAssetTableName,
			quiesceUntilUpToDate = true,
		} ):expectDataWithinTime( 20, {} )

	observer:dispose()

	autoDeleter:close()

end

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

function WFCatSyncAutoDeleterTest.rxTestAutoDelete_DeleteAllSpaceFeedDocsWhenSpaceDeleted( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local docStore = wfCatalog:docStore()
	local indexStore = wfCatalog:indexStore()

	local options = {
		delay = 0.1
	}

	local autoDeleter = WFCatSyncAutoDeleter( wfCatalog, options )

	local addAlbumFn = makeAdderOfAlbum( rxTest, wfCatalog )
	
	local addAssetFn = WFOzTestUtils.v2.makeAdderOfAsset( rxTest, docStore )

	-- Add a bunch of documents into a specific space.
	
	local docAlbumId = addAlbumFn()
	local docId1 = addAssetFn()
	local docId2 = addAssetFn()
	local docId3 = addAssetFn()

	local docIds = {
		docAlbumId,
		docId1,
		docId2,
		docId3
	}
	
	local spaceId = KSIdGenerator.uuidInLowercaseHex()
	
	rxTest:expectSingleValue( docStore:setDocValue( {
		_id = spaceId,
		type = 'space',
		subtype = 'shared'
	} ), ':ignore:' )
	
	for _, docId in ipairs( docIds ) do
	
		local overlay = {
			_id = docId,
			_spaceId = spaceId,
			name = "Foo"
		}
	
		rxTest:expectSingleValue( docStore:applyOverlay( overlay ), ':ignore:' )
	
	end

	local albumAssetDoc1 = WFCatAlbums.createAlbumAssetDocument( docAlbumId, docId1 )
	rxTest:expectSingleValue( docStore:setDocValue( albumAssetDoc1 ), ':ignore:' )
	
	local albumAssetDoc2 = WFCatAlbums.createAlbumAssetDocument( docAlbumId, docId2 )
	rxTest:expectSingleValue( docStore:setDocValue( albumAssetDoc2 ), ':ignore:' )
	
	local albumAssetDoc3 = WFCatAlbums.createAlbumAssetDocument( docAlbumId, docId3 )
	rxTest:expectSingleValue( docStore:setDocValue( albumAssetDoc3 ), ':ignore:' )

	local observer = rxTest:observe( indexStore:query {
			sql = [[SELECT ]] .. assetTableAssetIdColumnName .. [[, spaceId FROM ]] .. assetTableName,
			quiesceUntilUpToDate = true,
		} )
		:expectDataWithinTime( 5, {
			{ docId1, spaceId },
			{ docId2, spaceId },
			{ docId3, spaceId }
		} )
	
	observer:dispose()
	
	observer = rxTest:observe( indexStore:query {
			sql = [[SELECT ]] .. albumTableAlbumIdColumnName .. [[, spaceId FROM albums]],
			quiesceUntilUpToDate = true,
		} )
		:expectDataWithinTime( 5, { { docAlbumId, spaceId } } )

	observer:dispose()

	-- Signal that the space is deleted. This should cause all the space-related assets.
	-- to be deleted.
	
	rxTest:expectSingleValue( docStore:deleteDoc( spaceId ), ':ignore:' )

	-- Make sure the space deletion caused the album assets to get removed.

	observer = rxTest:observe( indexStore:query {
			sql = [[SELECT ]] .. assetTableAssetIdColumnName .. [[, spaceId FROM ]] .. assetTableName,
			quiesceUntilUpToDate = true,
		} )
		:expectDataWithinTime( 5, {} )
	
	observer:dispose()
	
	observer = rxTest:observe( indexStore:query {
			sql = [[SELECT ]] .. albumTableAlbumIdColumnName .. [[, spaceId FROM albums]],
			quiesceUntilUpToDate = true,
		} )
		:expectDataWithinTime( 5, {} )

	observer:dispose()

	rxTest:finally( function()
		autoDeleter:close()
	end )

end

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

function WFCatSyncAutoDeleterTest.rxTestAutoDeleter_DeleteCommentsWhenAssetDeleted( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local docStore = wfCatalog:docStore()
	local indexStore = wfCatalog:indexStore()

	local options = {
		delay = 0.1
	}

	local autoDeleter = WFCatSyncAutoDeleter( wfCatalog, options )

	-- Create an asset document.
	
	local addAssetFn = WFOzTestUtils.v2.makeAdderOfAsset( rxTest, docStore )
	
	local spaceId = KSIdGenerator.uuidInLowercaseHex()
	
	local assetId = addAssetFn()

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

	-- Add some comments to the space for the asset.
	
	for i = 1, 101 do

		local spaceCommentId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue( 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

	-- Count the comments.
	
	rxTest:expectSingleValue( indexStore:query{
			sql = 'SELECT COUNT(docId) FROM comment WHERE spaceId = ?',
			quiesceUntilUpToDate = true,
			spaceId
		}
		:take( 1 ),
		{ { 101 } } )
	
	-- Delete the asset.

	local overlay = {
		_id = assetId,
		_deleted = true
	}

	rxTest:expectOnlyCompleted(
		docStore:applyOverlay( overlay )
			:takeLast( 0 )
	)

	-- Make sure the asset deletion caused the comments to get removed.

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

	rxTest:finally( function()
		autoDeleter:close()
	end )

end

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

function WFCatSyncAutoDeleterTest.rxTestAutoDeleter_DeleteCommentsWhenSpaceDeleted( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local docStore = wfCatalog:docStore()
	local indexStore = wfCatalog:indexStore()

	local options = {
		delay = 0.1
	}

	local autoDeleter = WFCatSyncAutoDeleter( wfCatalog, options )

	-- Create an asset document.
	
	local addAssetFn = WFOzTestUtils.v2.makeAdderOfAsset( rxTest, docStore )
	
	local spaceId = KSIdGenerator.uuidInLowercaseHex()
	
	local assetId = addAssetFn()

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

	-- Add some comments to the space for the asset.
	
	for i = 1, 101 do

		local spaceCommentId = KSIdGenerator.uuidInLowercaseHex()

		rxTest:expectSingleValue( 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

	-- Count the comments.
	
	rxTest:expectSingleValue( indexStore:query{
			sql = 'SELECT COUNT(docId) FROM comment WHERE spaceId = ?',
			quiesceUntilUpToDate = true,
			spaceId
		}
		:take( 1 ),
		{ { 101 } } )
	
	-- Delete the space.

	local overlay = {
		_id = spaceId,
		_deleted = true
	}

	rxTest:expectOnlyCompleted(
		docStore:applyOverlay( overlay )
			:takeLast( 0 )
	)

	-- Make sure the space deletion caused the comments to get removed.

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

	rxTest:finally( function()
		autoDeleter:close()
	end )

end

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

function WFCatSyncAutoDeleterTest.rxTestAutoDeleter_DeleteFavoritesWhenAssetDeleted( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local docStore = wfCatalog:docStore()
	local indexStore = wfCatalog:indexStore()

	local options = {
		delay = 0.1
	}

	local autoDeleter = WFCatSyncAutoDeleter( wfCatalog, options )

	-- Create an asset document.
	
	local addAssetFn = WFOzTestUtils.v2.makeAdderOfAsset( rxTest, docStore )
	
	local spaceId = KSIdGenerator.uuidInLowercaseHex()
	local spaceFavoriteId = KSIdGenerator.uuidInLowercaseHex()
	local assetId = addAssetFn()

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

	-- Favorite the asset in the space.
	
	rxTest:expectSingleValue( 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( indexStore:query{
			sql = 'SELECT COUNT(docId) FROM spacefavorite WHERE spaceId = ?',
			quiesceUntilUpToDate = true,
			spaceId
		}
		:take( 1 ),
		{ { 1 } } )
	
	-- Delete the asset.

	local overlay = {
		_id = assetId,
		_deleted = true
	}

	rxTest:expectOnlyCompleted(
		docStore:applyOverlay( overlay )
			:takeLast( 0 )
	)

	-- Make sure the asset deletion caused the favorites to get removed.

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

	rxTest:finally( function()
		autoDeleter:close()
	end )

end

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

function WFCatSyncAutoDeleterTest.rxTestAutoDeleter_DeleteFavoritesWhenSpaceDeleted( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local docStore = wfCatalog:docStore()
	local indexStore = wfCatalog:indexStore()

	local options = {
		delay = 0.1
	}

	local autoDeleter = WFCatSyncAutoDeleter( wfCatalog, options )

	-- Create an asset document.
	
	local addAssetFn = WFOzTestUtils.v2.makeAdderOfAsset( rxTest, docStore )
	
	local spaceId = KSIdGenerator.uuidInLowercaseHex()
	local spaceFavoriteId = KSIdGenerator.uuidInLowercaseHex()
	local assetId = addAssetFn()

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

	-- Favorite the asset in the space.
	
	rxTest:expectSingleValue( 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( indexStore:query{
			sql = 'SELECT COUNT(docId) FROM spacefavorite WHERE spaceId = ?',
			quiesceUntilUpToDate = true,
			spaceId
		}
		:take( 1 ),
		{ { 1 } } )
	
	-- Delete the space.

	local overlay = {
		_id = spaceId,
		_deleted = true
	}

	rxTest:expectOnlyCompleted(
		docStore:applyOverlay( overlay )
			:takeLast( 0 )
	)

	-- Make sure the space deletion caused the favorites to get removed.

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

	rxTest:finally( function()
		autoDeleter:close()
	end )

end

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

function WFCatSyncAutoDeleterTest.rxTestAutoDeleter_DeleteVersionsWhenAssetDeleted( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local docStore = wfCatalog:docStore()
	local indexStore = wfCatalog:indexStore()

	local options = {
		delay = 0.1
	}

	local autoDeleter = WFCatSyncAutoDeleter( wfCatalog, options )

	-- Create an asset document.
	
	local assetId = KSIdGenerator.uuidInLowercaseHex()
	
	local optOverrides = {
		internalXmpCameraRaw = true
	}
	
	local assetDoc = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId, optOverrides )
	
	rxTest:expectSingleValue( wfCatalog:docStore():setDocValue( assetDoc ), ':ignore:' )

	-- Create a version
	
	local versionName = "Foo"
	local versionId
	
	rxTest:waitFor( WFCatVersions.createVersionForAsset( wfCatalog, assetId, false, versionName )
		:map( function( newVersionId )
		
			versionId = newVersionId
			
			return true
		
		end ) )

	rxTest:expectSingleValue( indexStore:query{
			sql = 'SELECT COUNT(docId) FROM versions WHERE assetId = ?',
			quiesceUntilUpToDate = true,
			assetId
		}
		:take( 1 ),
		{ { 1 } } )
	
	-- Delete the asset.

	local overlay = {
		_id = assetId,
		_deleted = true
	}

	rxTest:expectOnlyCompleted(
		docStore:applyOverlay( overlay )
			:takeLast( 0 )
	)

	-- Make sure the asset deletion caused the version to get removed.

	local obs = rxTest:observe( indexStore:query{
			sql = 'SELECT COUNT(docId) FROM versions WHERE assetId = ?',
			quiesceUntilUpToDate = true,
			assetId
		} )
	
	obs:expectDataWithinTime( 5, { { 0 } } )
	
	obs:dispose()

	rxTest:finally( function()
		autoDeleter:close()
	end )

end

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

function WFCatSyncAutoDeleterTest.rxTestAutoDeleter_DeleteVersionsWhenSpaceDeleted( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local docStore = wfCatalog:docStore()
	local indexStore = wfCatalog:indexStore()

	local options = {
		delay = 0.1
	}

	local autoDeleter = WFCatSyncAutoDeleter( wfCatalog, options )

	-- Create an asset document.
	
	local assetId = KSIdGenerator.uuidInLowercaseHex()
	
	local optOverrides = {
		internalXmpCameraRaw = true
	}
	
	local spaceId = KSIdGenerator.uuidInLowercaseHex()
	local spaceUserId = KSIdGenerator.uuidInLowercaseHex()
	
	local assetDoc = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId, optOverrides )
	assetDoc._spaceId = spaceId
	
	rxTest:expectSingleValue( wfCatalog:docStore():setDocValue( assetDoc ), ':ignore:' )

 	-- Create a space and a spaceUser document.
	
 	rxTest:expectSingleValue( docStore:applyOverlay {
 			
			_id = spaceId,
			type = 'space',
			subtype = 'shared',
			download = false,
			private = false,
			location = false,
			metadata = false,
			_serverProvidedMetadata = {
				owner = {
					id = KSIdGenerator.uuidInLowercaseHex()
				},
				this_space_user = {
					id = spaceUserId
				}
			}
		},
		':ignore:' )
		
	rxTest:expectSingleValue( docStore:applyOverlay {
	
			_id = spaceUserId,
			type = 'space_user',
			space = { id = spaceId },
			user = { id = KSIdGenerator.uuidInLowercaseHex() },
			role = 'editor'
		},
		':ignore:' )
	
	-- Create a version for the asset.

	local versionName = "Foo"
	local versionId
	
	rxTest:waitFor( WFCatVersions.createVersionForAsset( wfCatalog, assetDoc._id, false, versionName )
		:map( function( newVersionId )
		
			versionId = newVersionId
			
			return true
		
		end ) )

	rxTest:expectSingleValue( indexStore:query{
			sql = 'SELECT COUNT(docId) FROM versions WHERE spaceId = ?',
			quiesceUntilUpToDate = true,
			spaceId
		}
		:take( 1 ),
		{ { 1 } } )
	
	-- Delete the space.

	local overlay = {
		_id = spaceId,
		_deleted = true
	}

	rxTest:expectOnlyCompleted(
		docStore:applyOverlay( overlay )
			:takeLast( 0 )
	)

	-- Make sure the space deletion caused the version to get removed.

	local obs = rxTest:observe( indexStore:query{
			sql = 'SELECT COUNT(docId) FROM versions WHERE spaceId = ?',
			quiesceUntilUpToDate = true,
			spaceId
		} )
	
	obs:expectDataWithinTime( 5, { { 0 } } )
	
	obs:dispose()

	rxTest:finally( function()
		autoDeleter:close()
	end )

end

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

function WFCatSyncAutoDeleterTest.rxTestAutoDeleter_DeleteAssetsWhenSpaceFeedAlbumDeleted( rxTest )

	rxTest:setTimeLimit( 100 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local docStore = wfCatalog:docStore()
	local indexStore = wfCatalog:indexStore()

	local options = {
		delay = 0.1
	}

	local autoDeleter = WFCatSyncAutoDeleter( wfCatalog, options )

	rxTest:finally( function()
		autoDeleter:close()
	end )

	local initialCounter = 0
	
	rxTest:expectSingleValue( docStore:localDocValue( 'WFCatSyncAutoDeleter-changeCounter' )
		:tap( function( docValue )
			initialCounter = docValue.changeCounter or 0
		end ), ':ignore:' )

	local addAlbumFn = makeAdderOfAlbum( rxTest, wfCatalog )
	
	local addAssetFn = WFOzTestUtils.v2.makeAdderOfAsset( rxTest, docStore )

	-- Add a bunch of documents into a specific space.
	
	local spaceId = KSIdGenerator.uuidInLowercaseHex()
	
	local docAlbumId = addAlbumFn()
	local docAlbumId2 = addAlbumFn()
	
	local docId1 = addAssetFn()
	local docId2 = addAssetFn()
	local docId3 = addAssetFn()
	local docId4 = addAssetFn()
	local docId5 = addAssetFn()
	
	local docIds = {
		docAlbumId,
		docAlbumId2,
		docId1,
		docId2,
		docId3,
		docId4,
		docId5
	}
	
	rxTest:expectSingleValue( docStore:setDocValue( {
		_id = spaceId,
		type = 'space',
		subtype = 'shared'
	} ), ':ignore:' )
	
	for _, docId in ipairs( docIds ) do
	
		local overlay = {
			_id = docId,
			_spaceId = spaceId,
			name = "Foo"
		}
	
		rxTest:expectSingleValue( docStore:applyOverlay( overlay ), ':ignore:' )
	
	end

	-- Add all five assets into the first album.
	
	local albumAssetDoc1 = WFCatAlbums.createAlbumAssetDocument( docAlbumId, docId1 )
	rxTest:expectSingleValue( docStore:setDocValue( albumAssetDoc1 ), ':ignore:' )
	
	local albumAssetDoc2 = WFCatAlbums.createAlbumAssetDocument( docAlbumId, docId2 )
	rxTest:expectSingleValue( docStore:setDocValue( albumAssetDoc2 ), ':ignore:' )
	
	local albumAssetDoc3 = WFCatAlbums.createAlbumAssetDocument( docAlbumId, docId3 )
	rxTest:expectSingleValue( docStore:setDocValue( albumAssetDoc3 ), ':ignore:' )

	local albumAssetDoc4 = WFCatAlbums.createAlbumAssetDocument( docAlbumId, docId4 )
	rxTest:expectSingleValue( docStore:setDocValue( albumAssetDoc4 ), ':ignore:' )
	
	local albumAssetDoc5 = WFCatAlbums.createAlbumAssetDocument( docAlbumId, docId5 )
	rxTest:expectSingleValue( docStore:setDocValue( albumAssetDoc5 ), ':ignore:' )

	-- Add assets 4 and 5 into the second album.
	
	local album2AssetDoc4 = WFCatAlbums.createAlbumAssetDocument( docAlbumId2, docId4 )
	rxTest:expectSingleValue( docStore:setDocValue( album2AssetDoc4 ), ':ignore:' )
	
	local album2AssetDoc5 = WFCatAlbums.createAlbumAssetDocument( docAlbumId2, docId5 )
	rxTest:expectSingleValue( docStore:setDocValue( album2AssetDoc5 ), ':ignore:' )

	-- Verify the assets exist in the space.
	
	local observer = rxTest:observe( indexStore:query {
			sql = "SELECT " .. assetTableAssetIdColumnName .. ", spaceId FROM " .. assetTableName,
			quiesceUntilUpToDate = true,
		} )
		:expectDataWithinTime( 5, {
			{ docId1, spaceId },
			{ docId2, spaceId },
			{ docId3, spaceId },
			{ docId4, spaceId },
			{ docId5, spaceId }
		} )
	
	observer:dispose()
	
	-- Verify the albums exists in the space.
	
	observer = rxTest:observe( indexStore:query {
			sql = [[SELECT ]] .. albumTableAlbumIdColumnName .. [[, spaceId FROM albums]],
			quiesceUntilUpToDate = true,
		} )
		:expectDataWithinTime( 5, { 
			{ docAlbumId, spaceId },
			{ docAlbumId2, spaceId }
		} )

	observer:dispose()
	
	-- Delete Album 1

	rxTest:expectOnlyCompleted(
		docStore:deleteDoc( docAlbumId )
			:takeLast( 0 )
	)

	-- Make sure the space-feed album deletion caused the assets that are not also
	-- in album 2 to get removed.
	
	observer = rxTest:observe( indexStore:query {
			sql = [[SELECT ]] .. assetTableAssetIdColumnName .. [[, spaceId FROM ]] .. assetTableName,
			quiesceUntilUpToDate = true,
		} )
		:expectDataWithinTime( 5, {
			{ docId4, spaceId },
			{ docId5, spaceId },
		} )
	
	observer:dispose()

	-- Verify task change counter has incremented.
	
	observer = rxTest:observe( docStore:ongoingLocalDocValue( 'WFCatSyncAutoDeleter-changeCounter' )
		:map( function( docValue )
			local currentChangeCounter = docValue.changeCounter or 0
			return currentChangeCounter > initialCounter
		end ) )
		:expectDataWithinTime( 5, true )

	observer:dispose()

end

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

local function createSpaceAlbumWithOneAsset( rxTest, wfCatalog )

	-- create space and asset
	
	local spaceId = KSIdGenerator.uuidInLowercaseHex()
	
	local spaceDoc = {	
		_id = spaceId,
		type = 'space',
		subtype = 'shared',
		download = false,
		private = false,
		location = false,
		metadata = false,
	}
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( spaceDoc ) )
		
	local assetId = KSIdGenerator.uuidInLowercaseHex()
	
	local assetDocValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId )
	assetDocValue._spaceId = spaceId
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDocValue ) )
	
	-- Add asset into space album
	
	local albumId = KSIdGenerator.uuidInLowercaseHex()
	
	local albumDoc = {
		_id = albumId,
		_spaceId = spaceId,
		type = 'album',
		subtype = 'collection',
		name = 'foo',

	}

	rxTest:waitFor( wfCatalog:docStore():setDocValue( albumDoc ) )
	
	local albumAssetId = KSIdGenerator.uuidInLowercaseHex()
	
	local albumAssetDoc = {
		_id = albumAssetId,
		_spaceId = spaceId,
		type = 'album_asset',
		asset = {
			id = assetId
		},
		album = {
			id = albumId
		}
	}
	
	rxTest:waitFor( wfCatalog:docStore():setDocValue( albumAssetDoc ) )

	rxTest:observe( wfCatalog:indexStore():query {
			sql = [[SELECT ]] .. assetTableAssetIdColumnName .. [[, spaceId FROM ]] .. assetTableName,
			quiesceUntilUpToDate = true,
		} )
		:expectDataWithinTime( 5, {{ assetId, spaceId }} )
		:dispose()
		
	return spaceId, albumId, assetId, expectedData

end

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

local function testDeleteAndPurgeAssetWhenSpaceDeleted_desktop( rxTest, wfCatalog )

	local spaceId, _, _, expectedData = createSpaceAlbumWithOneAsset( rxTest, wfCatalog )
	
	-- check if all files exist
	-- xmp_develop purging on desktop would be handled by PixelProviderFileCachePurgeWorker
	
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.originalFilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.xmpSidecarFilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.proxyFilePath ) )

	-- Delete album
	
	rxTest:expectOnlyCompleted(
		wfCatalog:docStore():deleteDoc( spaceId )
			:takeLast( 0 )
	)
	
	-- check if assetDod is deleted
	
	rxTest:observe( wfCatalog:indexStore():query {
			sql = [[SELECT ]] .. assetTableAssetIdColumnName .. [[, spaceId FROM ]] .. assetTableName,
			quiesceUntilUpToDate = true,
		} )
		:expectDataWithinTime( 5, {} )
		:dispose()
		
	-- check if files are purged
	
	rxTest:expectEqual( false, WFFileUtils.exists( expectedData.originalFilePath ) )
	rxTest:expectEqual( false, WFFileUtils.exists( expectedData.xmpSidecarFilePath ) )
	rxTest:expectEqual( false, WFFileUtils.exists( expectedData.proxyFilePath ) )

end 

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

local function testDeleteAndPurgeAssetWhenSpaceDeleted_iOS( rxTest, wfCatalog )

	local spaceId, _, _, expectedData = createSpaceAlbumWithOneAsset( rxTest, wfCatalog )
	
	-- check if all files exist
	
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.originalFilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.proxyFilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.previewFilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.thumbnailFilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.xmpDevelopSettingsFilePath ) )
	
	-- Delete album
	
	rxTest:expectOnlyCompleted(
		wfCatalog:docStore():deleteDoc( spaceId )
			:takeLast( 0 )
	)
	
	-- check if assetDod is deleted
	
	rxTest:observe( wfCatalog:indexStore():query {
			sql = [[SELECT ]] .. assetTableAssetIdColumnName .. [[, spaceId FROM ]] .. assetTableName,
			quiesceUntilUpToDate = true,
		} )
		:expectDataWithinTime( 5, {} )
		:dispose()
	
	-- check if files are purged
	
	rxTest:expectEqual( false, WFFileUtils.exists( expectedData.originalFilePath ) )
	rxTest:expectEqual( false, WFFileUtils.exists( expectedData.proxyFilePath ) )
	rxTest:expectEqual( false, WFFileUtils.exists( expectedData.xmpDevelopSettingsFilePath ) )
	rxTest:expectEqual( false, WFFileUtils.exists( expectedData.previewFilePath ) )
	rxTest:expectEqual( false, WFFileUtils.exists( expectedData.thumbnailFilePath ) )

end 

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

local function testDeleteAndPurgeAssetWhenSpaceDeleted_android( rxTest, wfCatalog, catalogService, catalogDataCache )

	local spaceId, _, _, expectedData = createSpaceAlbumWithOneAsset( rxTest, wfCatalog )

	-- add preview and thumbnail2xData into dataCache
	
	local previewData = KSData.memoryMappedContentsOfFile( expectedData.previewFilePath )
	local thumbnail2xData = KSData.memoryMappedContentsOfFile( expectedData.thumbnailFilePath )

	local previewMd5 = previewData:digest( 'MD5' )
	local thumbMd5 = thumbnail2xData:digest( 'MD5' )
	
	rxTest:expectSingleValue( catalogService:getDataCache()
		:flatMap( function( dataCache )
			return dataCache:put( previewMd5, previewData )
		end )
		:map( tostring ),
		tostring( previewData ) )
		
	rxTest:expectSingleValue( catalogService:getDataCache()
		:flatMap( function( dataCache )
			return dataCache:put( thumbMd5, thumbnail2xData )
		end )
		:map( tostring ),
		tostring( thumbnail2xData ) )
		
	-- check if all files exist
	
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.originalFilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.proxyFilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.xmpDevelopSettingsFilePath ) )

	rxTest:expectSingleValue( catalogService:getDataCache()
		:flatMap( function( dataCache )
			return dataCache:get( previewMd5 )
		end )
		:map( tostring ),
		tostring( previewData ) )
		
	rxTest:expectSingleValue( catalogService:getDataCache()
		:flatMap( function( dataCache )
			return dataCache:get( thumbMd5 )
		end )
		:map( tostring ),
		tostring( thumbnail2xData ) )
	
	-- Delete album
	
	rxTest:expectOnlyCompleted(
		wfCatalog:docStore():deleteDoc( spaceId )
			:takeLast( 0 )
	)
	
	-- check if assetDod is deleted
	
	rxTest:observe( wfCatalog:indexStore():query {
			sql = [[SELECT ]] .. assetTableAssetIdColumnName .. [[, spaceId FROM ]] .. assetTableName,
			quiesceUntilUpToDate = true,
		} )
		:expectDataWithinTime( 5, {} )
		:dispose()
	
	-- check if files are purged
	
	rxTest:expectEqual( false, WFFileUtils.exists( expectedData.originalFilePath ) )
	rxTest:expectEqual( false, WFFileUtils.exists( expectedData.proxyFilePath ) )
	rxTest:expectEqual( false, WFFileUtils.exists( expectedData.xmpDevelopSettingsFilePath ) )
	
	rxTest:expectSingleValue( catalogService:getDataCache()
		:flatMap( function( dataCache )
			return dataCache:get( previewMd5 )
		end ),
		nil )
		
	rxTest:expectSingleValue( catalogService:getDataCache()
		:flatMap( function( dataCache )
			return dataCache:get( thumbMd5 )
		end ),
		nil )

	previewData:close()
	thumbnail2xData:close()

end 


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

function WFCatSyncAutoDeleterTest.rxTestAutoDeleter_DeleteAndPurgeAssetWhenSpaceDeleted( rxTest )

	rxTest:setTimeLimit( 15 )

	local wfCatalog, catalogService, catalogDataCache
	
	if WFPlatformInfo.isDesktop or WFPlatformInfo.isiOS then
	
		wfCatalog = createFakeWFCatalog( rxTest )
		
	elseif WFPlatformInfo.isAndroid then
	
		catalogService, wfCatalog = LrmCatalogTestHelper.makeCatalogService( rxTest )
		
		rxTest:expectSingleValue( catalogService:getDataCache()
				:map( function( dataCache )
					catalogDataCache = dataCache
					return true
				end )
			, true )
	
	end

	local options = {
		delay = 0.1
	}

	local autoDeleter = WFCatSyncAutoDeleter( wfCatalog, options, catalogDataCache )

	rxTest:finally( function()
		autoDeleter:close()
	end )

	local initialCounter = 0
	
	rxTest:expectSingleValue( wfCatalog:docStore():localDocValue( 'WFCatSyncAutoDeleter-changeCounter' )
		:tap( function( docValue )
			initialCounter = docValue.changeCounter or 0
		end ), ':ignore:' )

	-- Check if assetDoc is deleted and binary purged after space album is deleted
	
	if WFPlatformInfo.isDesktop then
	
		testDeleteAndPurgeAssetWhenSpaceDeleted_desktop( rxTest, wfCatalog )
		
	elseif WFPlatformInfo.isiOS then
	
		testDeleteAndPurgeAssetWhenSpaceDeleted_iOS( rxTest, wfCatalog )
		
	elseif WFPlatformInfo.isAndroid then
	
		testDeleteAndPurgeAssetWhenSpaceDeleted_android( rxTest, wfCatalog, catalogService, catalogDataCache )
		
	end
	
	-- Verify task change counter has incremented.
	
	rxTest:observe( wfCatalog:docStore():ongoingLocalDocValue( 'WFCatSyncAutoDeleter-changeCounter' )
		:map( function( docValue )
			local currentChangeCounter = docValue.changeCounter or 0
			return currentChangeCounter > initialCounter
		end ) )
		:expectDataWithinTime( 5, true )
		:dispose()

end

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

function WFCatSyncAutoDeleterTest.rxTestAutoDeleter_DeleteSpaceAlbumWhenAlbumDeleted( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local docStore = wfCatalog:docStore()
	local indexStore = wfCatalog:indexStore()

	local options = {
		delay = 0.1
	}

	local autoDeleter = WFCatSyncAutoDeleter( wfCatalog, options )

	-- Create an album document.
	
	local addAlbumFn = makeAdderOfAlbum( rxTest, wfCatalog )
	
	local albumId = addAlbumFn()
	
	-- Create a space document.
	
	local spaceId = KSIdGenerator.uuidInLowercaseHex()
	
	rxTest:expectSingleValue( docStore:setDocValue( {
		_id = spaceId,
		type = 'space',
		subtype = 'shared'
	} ), ':ignore:' )
	
 	-- Create a space_album document.
	
	local spaceAlbumId = KSIdGenerator.uuidInLowercaseHex()
	
 	rxTest:expectSingleValue( docStore:setDocValue {
			_id = spaceAlbumId,
			type = 'space_album',
			space = { id = spaceId },
			album = { id = albumId }
		},
		':ignore:' )

	-- Make sure the space exists.

	local obs = rxTest:observe( indexStore:query{
			sql = 'SELECT COUNT(docId) FROM space WHERE isDeleted == 0',
			quiesceUntilUpToDate = true,
			albumId
		} )

	obs:expectDataWithinTime( 5, { { 1 } } )

	obs:dispose()

	-- Delete the album.

	local overlay = {
		_id = albumId,
		_deleted = true
	}

	rxTest:expectOnlyCompleted(
		docStore:applyOverlay( overlay )
			:takeLast( 0 )
	)

	-- Make sure the album deletion caused the space to be deleted.

	obs = rxTest:observe( indexStore:query{
			sql = 'SELECT COUNT(docId) FROM space WHERE isDeleted == 0',
			quiesceUntilUpToDate = true,
			albumId
		} )
	
	obs:expectDataWithinTime( 5, { { 0 } } )
	
	obs:dispose()

	rxTest:finally( function()
		autoDeleter:close()
	end )

end

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

function WFCatSyncAutoDeleterTest.rxTestAutoDeleter_DeleteSpaceWhenAlbumDeleted( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local docStore = wfCatalog:docStore()
	local indexStore = wfCatalog:indexStore()

	local options = {
		delay = 0.1
	}

	local autoDeleter = WFCatSyncAutoDeleter( wfCatalog, options )

	-- Create an album document.
	
	local addAlbumFn = makeAdderOfAlbum( rxTest, wfCatalog )
	
	local albumId = addAlbumFn()
	
 	-- Create a space_album document.
	
	local spaceId = KSIdGenerator.uuidInLowercaseHex()
	local spaceAlbumId = KSIdGenerator.uuidInLowercaseHex()
	
 	rxTest:expectSingleValue( docStore:setDocValue {
			_id = spaceAlbumId,
			type = 'space_album',
			space = { id = spaceId },
			album = { id = albumId }
		},
		':ignore:' )

	-- Make sure it all exists.

	local obs = rxTest:observe( indexStore:query{
			sql = 'SELECT COUNT(docId) FROM space_album WHERE albumId = ?',
			quiesceUntilUpToDate = true,
			albumId
		} )

	obs:expectDataWithinTime( 5, { { 1 } } )

	obs:dispose()

	-- Delete the album.

	local overlay = {
		_id = albumId,
		_deleted = true
	}

	rxTest:expectOnlyCompleted(
		docStore:applyOverlay( overlay )
			:takeLast( 0 )
	)

	-- Make sure the album deletion caused the space_album to be deleted.

	obs = rxTest:observe( indexStore:query{
			sql = 'SELECT COUNT(docId) FROM space_album WHERE albumId = ?',
			quiesceUntilUpToDate = true,
			albumId
		} )
	
	obs:expectDataWithinTime( 5, { { 0 } } )
	
	obs:dispose()

	rxTest:finally( function()
		autoDeleter:close()
	end )

end

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

function WFCatSyncAutoDeleterTest.rxTestAutoDeleter_DeleteSpaceAlbumWhenSpaceDeleted( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local docStore = wfCatalog:docStore()
	local indexStore = wfCatalog:indexStore()

	local options = {
		delay = 0.1
	}

	local autoDeleter = WFCatSyncAutoDeleter( wfCatalog, options )

	local spaceId = KSIdGenerator.uuidInLowercaseHex()

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

	-- Create a space album document
	
	local spaceAlbumId = KSIdGenerator.uuidInLowercaseHex()
	local albumId = KSIdGenerator.uuidInLowercaseHex()
	
	rxTest:expectSingleValue( docStore:applyOverlay {
		_id = spaceAlbumId,
		type = 'space_album',
		album = {
			id = albumId,
		},
		space = {
			id = spaceId,
		},
		
	}, ':ignore:' )
	
	local obs = rxTest:observe( indexStore:query{
			sql = 'SELECT COUNT(docId) FROM space_album WHERE spaceId = ?',
			quiesceUntilUpToDate = true,
			spaceId
		} )
	
	obs:expectDataWithinTime( 5, { { 1 } } )
	
	-- Delete the space.

	local overlay = {
		_id = spaceId,
		_deleted = true
	}

	rxTest:expectOnlyCompleted(
		docStore:applyOverlay( overlay )
			:takeLast( 0 )
	)

	-- Make sure the space deletion caused the space album doc to get removed.
	
	obs:expectDataWithinTime( 5, { { 0 } } )
	
	obs:dispose()

	rxTest:finally( function()
		autoDeleter:close()
	end )

end

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

local function createVersionDoc( assetId )

	local versionName = "foo"
	
	local docId = KSIdGenerator.uuidInLowercaseHex()

	local versionDoc = {

		_id = docId,
		type = 'version',
		subtype = 'edit',
		name = versionName,
		createdManually = false,
		asset = {
			id = assetId
		}

	}

	return versionDoc

end

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

function WFCatSyncAutoDeleterTest.rxTestUpdateVersionOnAssetTrashOperation( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local docStore = wfCatalog:docStore()
	local indexStore = wfCatalog:indexStore()

	local options = {
		delay = 0.1
	}

	local autoDeleter = WFCatSyncAutoDeleter( wfCatalog, options )

	-- Create an asset document.
	
	local assetId = KSIdGenerator.uuidInLowercaseHex()
	
	local optOverrides = {
		internalXmpCameraRaw = true
	}
	
	local assetDoc = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId, optOverrides )
	
	rxTest:expectSingleValue( wfCatalog:docStore():setDocValue( assetDoc ), ':ignore:' )

	-- Create a version
	
	local versionName = "Foo"
	local versionId
	
	rxTest:waitFor( WFCatVersions.createVersionForAsset( wfCatalog, assetId, false, versionName )
		:map( function( newVersionId )
		
			versionId = newVersionId
			
			return true
		
		end ) )

	rxTest:expectSingleValue( indexStore:query{
			sql = 'SELECT COUNT(docId) FROM versions WHERE assetId = ?',
			quiesceUntilUpToDate = true,
			assetId
		}
		:take( 1 ),
		{ { 1 } } )
	
	-- Move the asset to the trash.

	local new_id = KSIdGenerator.uuidInLowercaseHex()

	local overlay = {
		_id = assetId,
		_deleted = true,
		_serverProvidedMetadata = {
			new_id = new_id
		}
	}

	rxTest:expectOnlyCompleted(
		docStore:applyOverlay( overlay )
			:takeLast( 0 )
	)

	-- Make sure the asset deletion caused the version to get updated.

	local obs = rxTest:observe( indexStore:query{
			sql = 'SELECT docId FROM versions WHERE assetId = ?',
			quiesceUntilUpToDate = true,
			new_id
		} )
	
	obs:expectDataWithinTime( 5, { { versionId } } )
	
	obs:dispose()

	rxTest:finally( function()
		autoDeleter:close()
	end )

end

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

function WFCatSyncAutoDeleterTest.rxTestDeleteVersionOnTrashOperationWithEmpty_WF1235( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local docStore = wfCatalog:docStore()
	local indexStore = wfCatalog:indexStore()

	local options = {
		delay = 1
	}

	local autoDeleter = WFCatSyncAutoDeleter( wfCatalog, options )

	-- Create an asset document.
	
	local assetId = KSIdGenerator.uuidInLowercaseHex()
	
	local optOverrides = {
		internalXmpCameraRaw = true
	}
	
	local assetDoc = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId, optOverrides )
	
	rxTest:expectSingleValue( wfCatalog:docStore():setDocValue( assetDoc ), ':ignore:' )

	-- Create a version
	
	local versionName = "Foo"
	local versionId
	
	rxTest:waitFor( WFCatVersions.createVersionForAsset( wfCatalog, assetId, false, versionName )
		:map( function( newVersionId )
		
			versionId = newVersionId
			
			return true
		
		end ) )

	rxTest:expectSingleValue( indexStore:query{
			sql = 'SELECT COUNT(docId) FROM versions WHERE assetId = ?',
			quiesceUntilUpToDate = true,
			assetId
		}
		:take( 1 ),
		{ { 1 } } )
	
	-- Move the asset to the trash, create a new asset, then delete that.

	local new_id = KSIdGenerator.uuidInLowercaseHex()

	rxTest:expectSingleValue( docStore:bulkApplyOverlay( 
		{
			{
				type = 'asset',
				_id = new_id,
				subtype = 'deleted_image',
				userCreated = '2012-01-15T11:12:15',
				userUpdated = '2012-09-14T15:22:29',
				_localOnly = {
					localUrl = "file://someUrl.jpg"
				}
			},
			{
				_id = assetId,
				_deleted = true,
				_serverProvidedMetadata = {
					new_id = new_id
				}
			}
		} ),
		':ignore:' )

	rxTest:expectOnlyCompleted(
		docStore:applyOverlay( 
			{
				_id = new_id,
				_deleted = true,
			} )
			:takeLast( 0 )
	)

	-- Make sure the asset deletions + version update allowed the version
	-- to be correclty updated and then deleted.

	local obs = rxTest:observe( indexStore:query{
			sql = 'SELECT count( * ) FROM versions WHERE assetId = ?',
			quiesceUntilUpToDate = true,
			new_id
		} )
	
	obs:expectDataWithinTime( 5, { { 1 } } )
	
	obs:dispose()

	obs = rxTest:observe( indexStore:query{
			sql = 'SELECT count( * ) FROM versions',
			quiesceUntilUpToDate = true,
			assetId
		} )
	
	obs:expectDataWithinTime( 5, { { 0 } } )
	
	obs:dispose()

	rxTest:finally( function()
		autoDeleter:close()
	end )

end

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

function WFCatSyncAutoDeleterTest.rxTestUpdateVersionOnAssetTrashOperation_Bulk( rxTest )

	rxTest:setTimeLimit( 20 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local docStore = wfCatalog:docStore()
	local indexStore = wfCatalog:indexStore()

	local options = {
		delay = 0.1
	}

	local autoDeleter = WFCatSyncAutoDeleter( wfCatalog, options )

	-- Add a few assets, each of which has many versions
	
	local docValues = {}
	local allAssetIds = {}
	
	local versionCountPerAsset = 50
	
	for i=3,1,-1 do

		local assetId = KSIdGenerator.uuidInLowercaseHex()
		
		allAssetIds[ #allAssetIds + 1 ] = assetId
		
		local optOverrides = {
			internalXmpCameraRaw = true
		}
		
		local assetDoc = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId, optOverrides )
		
		docValues[ #docValues + 1 ] = assetDoc
		
		for j=versionCountPerAsset,1,-1 do

			local versionDoc = createVersionDoc( assetId )
			docValues[ #docValues + 1 ] = versionDoc
			
		end
		
	end

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

	-- Add all the asset into the trash.

	local overlays = {}
	
	local newIdsByAssetId = {}
	
	for i, assetId in ipairs( allAssetIds ) do
	
		local new_id = KSIdGenerator.uuidInLowercaseHex()
		newIdsByAssetId[ assetId ] = new_id
	
		local overlay = {
			_id = assetId,
			_deleted = true,
			_serverProvidedMetadata = {
				new_id = new_id
			}
		}
		
		table.insert( overlays, overlay )
		
	end
	
	rxTest:expectOnlyCompleted(
			docStore:bulkApplyOverlay( overlays )
				:takeLast( 0 )
		)

	-- Make sure the asset trash operation caused the versions to be updated.

	for assetId, new_id in pairs( newIdsByAssetId ) do
	
		local obs = rxTest:observe( indexStore:query{
			sql = 'SELECT count( docId ) FROM versions WHERE assetId = ?',
			quiesceUntilUpToDate = true,
			new_id
		} )
		
		obs:expectDataWithinTime( 5, { { versionCountPerAsset } } )
		
		obs:dispose()
	
	end

	rxTest:finally( function()
		autoDeleter:close()
	end )

end

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

function WFCatSyncAutoDeleterTest.rxTestUpdateTaskChangeCounterNoDeletion( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local docStore = wfCatalog:docStore()
	local indexStore = wfCatalog:indexStore()

	-- Create an asset document.

	local assetId = KSIdGenerator.uuidInLowercaseHex()

	local optOverrides = {
		internalXmpCameraRaw = true
	}

	local assetDoc = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId, optOverrides )

	rxTest:expectSingleValue( wfCatalog:docStore():setDocValue( assetDoc ), ':ignore:' )

	local overlay = {
		_id = assetId,
		develop = {
			croppedHeight = 500,
			croppedWidth = 500,
		},
	}

	rxTest:expectOnlyCompleted(
		docStore:applyOverlay( overlay )
			:takeLast( 0 )
	)

	local observer = rxTest:observe( indexStore:query {
			sql = [[SELECT count(*) FROM ]] .. assetTableName,
			quiesceUntilUpToDate = true,
		} )
		:expectDataWithinTime( 5, { { 1 } } )

	observer:dispose()

	local options = {
		delay = 0.1
	}

	local initialCounter

	rxTest:expectSingleValue( docStore:localDocValue( 'WFCatSyncAutoDeleter-changeCounter' )
		:tap( function( docValue )
			initialCounter = docValue.changeCounter or 0
		end ), ':ignore:' )

	local autoDeleter = WFCatSyncAutoDeleter( wfCatalog, options )

	-- Verify task change counter has incremented.

	observer = rxTest:observe( docStore:ongoingLocalDocValue( 'WFCatSyncAutoDeleter-changeCounter' )
		:map( function( docValue )
			local currentChangeCounter = docValue.changeCounter or 0
			return currentChangeCounter == initialCounter + 3
		end ) )
		:expectDataWithinTime( 5, true )

	rxTest:finally( function()
		autoDeleter:close()
	end )

end

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

return WFRxTestCase.wrapSuite( 'WFCatSyncAutoDeleterTest', WFCatSyncAutoDeleterTest )
