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

WFCatCopiesTest.lua

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

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

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

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

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

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

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

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

	-- WF.oz_client.managed_catalog
local WFCatAssetUtils = require 'WFCatAssetUtils'
local WFCatAssetTestUtils = require 'WFCatAssetTestUtils'
local WFCatMockCatalog = require 'WFCatMockCatalog'
local WFCatCopies = require 'WFCatCopies'
local WFCatFileSpec = require "WFCatFileSpec"
local WFCatAlbums = require "WFCatAlbums"

local OzFilePaths

if WFPlatformInfo.isiOS then
	OzFilePaths = require 'OzFilePaths'
end

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

local WFCatMockCatalog_createFakeWFCatalog = WFCatMockCatalog.createFakeWFCatalog

local KSTableUtils_derefOpt = KSTableUtils.derefOpt


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

local WFCatCopiesTest = {}

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

local function addDoc( docStore, docId )
	return docStore:setDocValue {
			_id = docId,
			type = 'asset',
			subtype = 'image',
			_localOnly = {
				files = {
					proxy = {
						pano = {
							relativePath = 'foo',
							volumeGuid = 'bar'
						}
					}
				}
			},
			importSource = {
				filePath = '/path/',
				originalHeight = 150,
				originalWidth = 100,
			}
		}
end

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

local function mapPluckCopyIds( docs )

	local copies = {}
	
	for i, doc in ipairs( docs ) do
		copies[ i ] = doc.copy and doc.copy.copy_id
	end

	return copies

end

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

local function mapArrayHasIdenticalEntriesFn( num )

	return function( array )
	
		if #array ~= num then
			return false
		end
		
		local first = array[ 1 ]
		for _, val in ipairs( array ) do
			if val ~= first then
				return false
			end
		end
		
		return true
	end
	
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 function createAsset( rxTest, wfCatalog, docStore, assetId, fileInfo, optOverrides, spaceId )

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

	rxTest:logf( "Asset: %s", docValue )

	rxTest:expectSingleValue( docStore:setDocValue( docValue ), ':ignore:' )
	
	return assetInfo, docValue
	
end

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

local function absolutePath( catalogId, relativePath )

	local absolutePath 

	if WFPlatformInfo.isiOS then

		if relativePath then

			if string.sub( relativePath, 1, 1 ) == '/' then
			
				absolutePath = relativePath
				
			else
			
				if not OzFilePaths then
					OzFilePaths = require 'OzFilePaths'
				end
			
				local basePath = OzFilePaths.getCatalogBasePath( catalogId )
				absolutePath = KSPathUtils.child( basePath, relativePath )
				
			end
			
		end
	
	elseif WFPlatformInfo.isAndroid then

		local OzCatalogUploadStatus = require 'OzCatalogUploadStatus'

		if relativePath then

			if string.sub( relativePath, 1, 1 ) == '/' then
				absolutePath = relativePath
			else
				-- the relative path might have the catalogid already prepended or not
				if string.sub( relativePath, 1, catalogId:len() ) == catalogId then
					local documentsDir = KSPathUtils.getStandardFilePath( 'carouselDocuments' )
					absolutePath = KSPathUtils.child( documentsDir, relativePath )
				else
					absolutePath = OzFilePaths.getAbsolutePathInCatalog( catalogId, relativePath )
				end
			end

		end

		absolutePath = OzCatalogUploadStatus.checkAndCorrectLocalPath( absolutePath )

	end
	
	return absolutePath

end

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

local function mapAwayPaths( rxTest, wfCatalog, docValue, fileInfo, performFileCheck )
	
	docValue.captureDate = nil
	
	performFileCheck = performFileCheck ~= false 
	
	if WFPlatformInfo.isDesktop then
	
		local copy_originalPath = WFCatAssetUtils.getFullOriginalPathFromDocValue( docValue )
		local copy_proxyPath = WFCatAssetUtils.getFullProxyPathFromDocValue( docValue )
		local copy_externalXmpPath = WFCatAssetUtils.getLocalExternalDevelopSettingsPath( docValue )
			
		rxTest:logf( "copy_originalPath %s", copy_originalPath ) 
		rxTest:logf( "copy_proxyPath %s", copy_proxyPath ) 
		rxTest:logf( "copy_externalXmpPath %s", copy_externalXmpPath ) 
			
		if performFileCheck then
			rxTest:expectEqual( fileInfo.originalFileSize, WFFileUtils.fileAttributes( copy_originalPath ).fileSize )
			rxTest:expectEqual( fileInfo.proxyFileSize, WFFileUtils.fileAttributes( copy_proxyPath ).fileSize )
			rxTest:expectEqual( fileInfo.xmpDevelopSettingsFileSize, WFFileUtils.fileAttributes( copy_externalXmpPath ).fileSize )
		end
		
		-- check the file paths are changed and switch to a known value if they are.

		local copy_xmpDevelopPath = WFCatFileSpec.getXmpDevelopInfoFromDocValue( docValue).path
		local copy_xmpSidecarPath = WFCatFileSpec.getXmpSidecarPathFromDocValue( docValue )
		
		if performFileCheck then
			rxTest:expectEqual( fileInfo.xmpSidecarFileSize, WFFileUtils.fileAttributes( copy_xmpSidecarPath ).fileSize )
			rxTest:expectEqual( fileInfo.xmpDevelopSettingsFileSize, WFFileUtils.fileAttributes( copy_xmpDevelopPath ).fileSize )
		end
		
		if copy_originalPath ~= fileInfo.originalFilePath then
			docValue._localOnly.files.original.master.relativePath = '<newOriginalPath>'
			docValue._localOnly.files.original.master.volumeGuid = '<newOriginalGuid>'
		end
		
		if copy_proxyPath ~= fileInfo.proxyFilePath then
			docValue._localOnly.files.proxy['2560'].relativePath = '<newProxyPath>'
			docValue._localOnly.files.proxy['2560'].volumeGuid = '<newProxyVolumeGuid>'
		end
		
		if copy_xmpSidecarPath ~= fileInfo.xmpSidecarFilePath then
			docValue._localOnly.files.original.xmp.path = '<newXmpSidecarPath>'
		end

		-- xmpDevelopSettingsFilePath currently is shared by all files with same settings 
		-- sha256 hash so it's path doesn't change when copied.

	elseif WFPlatformInfo.isiOS then
	
		local copy_originalRelativePath = WFCatAssetUtils.getOriginalRelativePathFromDocValue( docValue )
		local copy_proxyRelativePath = WFCatAssetUtils.getProxyRelativePathFromDocValue( docValue )
		local copy_externalXmpRelativePath = WFCatAssetUtils.getLocalExternalDevelopSettingsPath( docValue )
			
		local copy_originalPath = absolutePath( wfCatalog:ozCatalogId(), copy_originalRelativePath )
		local copy_proxyPath = absolutePath( wfCatalog:ozCatalogId(), copy_proxyRelativePath )
		local copy_externalXmpPath = absolutePath( wfCatalog:ozCatalogId(), copy_externalXmpRelativePath )
		
		rxTest:logf( "copy_originalPath %s", copy_originalPath ) 
		rxTest:logf( "copy_proxyPath %s", copy_proxyPath ) 
		rxTest:logf( "copy_externalXmpPath %s", copy_externalXmpPath ) 
			
		if performFileCheck then
			rxTest:expectEqual( fileInfo.originalFileSize, WFFileUtils.fileAttributes( copy_originalPath ).fileSize )
			rxTest:expectEqual( fileInfo.proxyFileSize, WFFileUtils.fileAttributes( copy_proxyPath ).fileSize )
			rxTest:expectEqual( fileInfo.xmpDevelopSettingsFileSize, WFFileUtils.fileAttributes( copy_externalXmpPath ).fileSize )
		end
		
		local copy_previewPath = KSTableUtils_derefOpt( docValue, '_localOnly', 'files', 'preview', 'path' )
		local copy_thumbnailPath = KSTableUtils_derefOpt( docValue, '_localOnly', 'files', 'thumbnail2x', 'path' )
		
		if performFileCheck then
			local absolutePreviewPath = absolutePath( wfCatalog:ozCatalogId(), copy_previewPath )
			local copy_previewData = KSData.memoryMappedContentsOfFile( absolutePreviewPath )
			rxTest:expectEqual( fileInfo.previewData, tostring( copy_previewData ) )
			copy_previewData:close()
			
			local absoluteThumbnailPath = absolutePath( wfCatalog:ozCatalogId(), copy_thumbnailPath )
			local copy_thumbnailData = KSData.memoryMappedContentsOfFile( absoluteThumbnailPath )
			rxTest:expectEqual( fileInfo.thumbnailData, tostring( copy_thumbnailData ) )
			copy_thumbnailData:close()
		end
		
		docValue._localOnly.files.preview.path = '<previewPath>'
		docValue._localOnly.files.thumbnail2x.path = '<thumbnailPath>'
		
		if copy_originalPath ~= fileInfo.originalFilePath then
			docValue._localOnly.files.master.path = '<newOriginalPath>'
		end
		
		if copy_proxyPath ~= fileInfo.proxyFilePath then
			docValue._localOnly.files.proxy.path = '<newProxyPath>'
		end
	
		if copy_externalXmpPath ~= fileInfo.externalXmpPath then
			docValue._localOnly.files.xmp_develop.path = '<newExternalXmpPath>'
		end
	
	elseif WFPlatformInfo.isAndroid then
	
		local copy_originalPath = WFCatAssetUtils.getFullOriginalPathFromDocValue( docValue )
		local copy_proxyPath = WFCatAssetUtils.getFullProxyPathFromDocValue( docValue )
		local copy_externalXmpRelativePath = WFCatAssetUtils.getLocalExternalDevelopSettingsPath( docValue )

		local copy_externalXmpPath = absolutePath( wfCatalog:ozCatalogId(), copy_externalXmpRelativePath )
			
		rxTest:logf( "copy_originalPath %s", copy_originalPath ) 
		rxTest:logf( "copy_proxyPath %s", copy_proxyPath ) 
		rxTest:logf( "copy_externalXmpPath %s", copy_externalXmpPath ) 
			
		if performFileCheck then
			rxTest:expectEqual( fileInfo.originalFileSize, WFFileUtils.fileAttributes( copy_originalPath ).fileSize )
			rxTest:expectEqual( fileInfo.proxyFileSize, WFFileUtils.fileAttributes( copy_proxyPath ).fileSize )
			rxTest:expectEqual( fileInfo.xmpDevelopSettingsFileSize, WFFileUtils.fileAttributes( copy_externalXmpPath ).fileSize )
		end
		
		if copy_originalPath ~= fileInfo.originalFilePath then
			docValue._localOnly.originalFilePath = '<newOriginalPath>'
		end
		
		if copy_proxyPath ~= fileInfo.proxyFilePath then
			docValue._localOnly.proxy.filePath = '<newProxyPath>'
		end
	
		if copy_externalXmpPath ~= fileInfo.externalXmpPath then
			docValue._localOnly.files.xmp_develop.path = '<newExternalXmpPath>'
		end
		
		local copy_previewMd5 = KSTableUtils_derefOpt( docValue, '_localOnly', 'preview', 'md5' )
		local copy_thumbnailMd5 = KSTableUtils_derefOpt( docValue, '_localOnly', 'thumbnail2x', 'md5' )
	
		if performFileCheck then
			rxTest:expectEqual( fileInfo.previewMd5, copy_previewMd5 )
			rxTest:expectEqual( fileInfo.thumbMd5, copy_thumbnailMd5 )
		end
		
		docValue._localOnly.preview.md5 = '<previewMd5>'
		docValue._localOnly.thumbnail2x.md5 = '<thumbnailMd5>'
		
		docValue._localOnly.preview.epoch = '<epoch>'
		docValue._localOnly.thumbnail2x.epoch = '<epoch>'
		
		docValue._localOnly.preview.revId = '<revId>'
		docValue._localOnly.thumbnail2x.revId = '<revId>'
	
	end

	return docValue

end

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

local function getExpectedCopiedDocValue( fileInfo, sourceAssetId, copiedAssetId, copiedAssetRevId, copyId )

	local docValue = {
		_changeCounter = '<changeCounter>',
		_id = copiedAssetId,
		_localId = '<localId>',
		_revId = copiedAssetRevId,
		copy = {
			copy_id = copyId,
		},
		develop = {
			croppedHeight = 1000,
			croppedWidth = 1000,
			userOrientation = 1,
			xmpCameraRaw = {
				sha256 = fileInfo.developSettingsSha256,
			},
		},
		importSource = {
			sha256 = fileInfo.originalSha256,
		},
		subtype = "image",
		type = "asset",
	}

	if WFPlatformInfo.isDesktop then

		docValue._localOnly = {
			files = {
				original = {
					master = {
						size = fileInfo.originalFileSize,
						relativePath = '<newOriginalPath>',
						volumeGuid = '<newOriginalGuid>',
					},
					xmp = {
						path = '<newXmpSidecarPath>',
						fileSize = fileInfo.xmpSidecarFileSize,
					},
				},
				proxy = {
					[ '2560' ] = {
						relativePath = '<newProxyPath>',
						volumeGuid = '<newProxyVolumeGuid>',
						size = fileInfo.proxyFileSize,
					}
				},					
				xmp_develop = {
					fileSize = fileInfo.xmpDevelopSettingsFileSize,
					path = fileInfo.xmpDevelopSettingsFilePath,
					sha256 = fileInfo.developSettingsSha256
				},
			},
			sourceAssetId = sourceAssetId,
			sourceOriginalLink = "/href/bogus/testing",
			sourceProxyLink = "/href/bogus/proxy",
			sourceXmpLink = "/href/bogus/xmp",
			xmpSidecarExists = true,
		}

	elseif WFPlatformInfo.isiOS then
	
		docValue._localOnly = {
			files = {
				master = {
					path = '<newOriginalPath>',
					contentId = fileInfo.originalContentId,
					fileSize = fileInfo.originalFileSize,
				},
				proxy = {
					path = '<newProxyPath>',
					contentId = fileInfo.proxyContentId,
					fileSize = fileInfo.proxyFileSize,
				},
				preview = {
					path = '<previewPath>',
					contentId = fileInfo.previewContentId,
					fileSize = fileInfo.previewFileSize,
				},
				thumbnail2x = {
					path = '<thumbnailPath>',
					contentId = fileInfo.thumbnailContentId,
					fileSize = fileInfo.thumbnailFileSize,
				},
				xmp_develop = {
					path = '<newExternalXmpPath>',
					fileSize = fileInfo.xmpDevelopSettingsFileSize,
					contentId = fileInfo.developSettingsSha256
				},
			},
			sourceAssetId = sourceAssetId,
			sourceOriginalLink = "/href/bogus/testing",
			sourceProxyLink = "/href/bogus/proxy",
			sourceXmpLink = "/href/bogus/xmp",
			xmpSidecarExists = true,
		}
	
	elseif WFPlatformInfo.isAndroid then
	
		docValue._localOnly = {
			originalFilePath = '<newOriginalPath>',
			original = {
				size = fileInfo.originalFileSize,
			},
			proxy = {
				filePath = '<newProxyPath>',
				size = fileInfo.proxyFileSize,
			},
			preview = {
				revId = '<revId>',
				epoch = '<epoch>',
				md5 = '<previewMd5>',
				referenceHeight = 960,
				referenceWidth = 1280,
				size = fileInfo.previewFileSize,
			},
			thumbnail2x = {
				revId = '<revId>',
				epoch = '<epoch>',
				md5 = '<thumbnailMd5>',
				referenceHeight = 960,
				referenceWidth = 1280,
				size = fileInfo.thumbnailFileSize,
			},
			files = {
				xmp_develop = {
					path = '<newExternalXmpPath>',
					sha256 = fileInfo.developSettingsSha256
				},
			},
			sourceAssetId = sourceAssetId,
			sourceOriginalLink = "/href/bogus/testing",
			sourceProxyLink = "/href/bogus/proxy",
			sourceXmpLink = "/href/bogus/xmp",
			xmpSidecarExists = true,
		}
	
	end
	
	return docValue

end


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

function WFCatCopiesTest.rxTestSetAssetsAsCopies( rxTest )

	rxTest:setTimeLimit( 50 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()
	
	-- Test making assets are copies ( none of them are previously copies )
	local docIds = {
		'doc1',
		'doc2',
		'doc3',
	}
	rxTest:expectSingleValue( addDoc( docStore, docIds[ 1 ] ), ':ignore:' )
	rxTest:expectSingleValue( addDoc( docStore, docIds[ 2 ] ), ':ignore:' )
	rxTest:expectSingleValue( addDoc( docStore, docIds[ 3 ] ), ':ignore:' )

	rxTest:expectSingleValue( WFCatCopies.setAssetsAsCopies( fakeWFCatalog, docIds ), ':ignore:' )

	rxTest:expectSingleValue(
		docStore:bulkDocValue( docIds )
			:map( mapPluckCopyIds )
			:map( mapArrayHasIdenticalEntriesFn( 3 ) ), true )
			
			
	-- Test making assets are copies ( one of them is already a copy - propagate the copy_id )
	local additionalDocIds = {
		'doc3',
		'doc4',
		'doc5',
	}
	rxTest:expectSingleValue( addDoc( docStore, additionalDocIds[ 2 ] ), ':ignore:' )
	rxTest:expectSingleValue( addDoc( docStore, additionalDocIds[ 3 ] ), ':ignore:' )
	
	rxTest:expectSingleValue( WFCatCopies.setAssetsAsCopies( fakeWFCatalog, additionalDocIds ), ':ignore:' )
	
	
	local allDocIds = {
		'doc1',
		'doc2',
		'doc3',
		'doc4',
		'doc5',
	}
	
	rxTest:expectSingleValue(
		docStore:bulkDocValue( allDocIds )
			:map( mapPluckCopyIds )
			:map( mapArrayHasIdenticalEntriesFn( 5 ) ), true )
			
	
	-- Test making assets are copies ( error: input assets already belong to different copy sets )
	local secondSetDocIds = {
		'doc10',
		'doc11',
	}
	rxTest:expectSingleValue( addDoc( docStore, secondSetDocIds[ 1 ] ), ':ignore:' )
	rxTest:expectSingleValue( addDoc( docStore, secondSetDocIds[ 2 ] ), ':ignore:' )
	
	rxTest:expectSingleValue( WFCatCopies.setAssetsAsCopies( fakeWFCatalog, secondSetDocIds ), ':ignore:' )
	
	-- Test error if we are trying to make 
	local secondSetAdditionalDocIds = {
		'doc1',
		'doc2',
		'doc10',
	}
	
	local obs = rxTest:observe( WFCatCopies.setAssetsAsCopies( fakeWFCatalog, secondSetAdditionalDocIds ) )
	
	obs:expectErrorWithinTime( 2, "Trying to change the copy-id of an asset" )

end

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

function WFCatCopiesTest.rxTestIgnoresSetAssetsAsCopiesWithOneOrZeroDocs( rxTest )

	rxTest:setTimeLimit( 50 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()
	
	local docIds = { 'doc1' }

	rxTest:expectSingleValue( addDoc( docStore, docIds[ 1 ] ), ':ignore:' )

	rxTest:expectOnlyCompleted( WFCatCopies.setAssetsAsCopies( fakeWFCatalog, docIds ) )

	rxTest:expectOnlyCompleted( WFCatCopies.setAssetsAsCopies( fakeWFCatalog, {} ) )

end

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

function WFCatCopiesTest.rxTestCreateCopiesOfAssetsOne( rxTest )

	rxTest:setTimeLimit( 50 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	local assetId1 = 'e0be4f816aa149a7a3e8b148145d5135'

	local assetInfo = createAsset( rxTest, fakeWFCatalog, docStore, assetId1 )

	-- Copy a single asset.
	local copiedAssetId
	
	rxTest:expectSingleValue( WFCatCopies.createCopiesOfAssets( fakeWFCatalog, { assetId1 } )
		:map( function( copiedAssetIds )
			copiedAssetId = copiedAssetIds[ 1 ]
			return copiedAssetIds and #copiedAssetIds == 1
		end ),
	true )

	-- get some info from the copied asset.
	
	local copiedAssetRevId, copyId
	
	rxTest:expectSingleValue( fakeWFCatalog:docStore():docValue( copiedAssetId )
		:map( function( docValue )
			copiedAssetRevId = docValue._revId
			copyId = docValue.copy.copy_id
			return true
		end ), true )

	-- Compare the new copied doc value (ignoring the _id and _revId values fished out above)
	-- This also verifies that there is no _serverProvidedMetadata in the copied asset.
	
	rxTest:expectSingleValue( 
		fakeWFCatalog:docStore():docValue( copiedAssetId )
			:map( function( docValue )

				docValue._changeCounter = "<changeCounter>"
				docValue._localId = "<localId>"
				
				return mapAwayPaths( rxTest, fakeWFCatalog, docValue, assetInfo )

			end ), getExpectedCopiedDocValue( assetInfo, assetId1, copiedAssetId, copiedAssetRevId, copyId ) )

	-- compare the original doc value to see that it has the copy_id set now as well
	
	rxTest:expectSingleValue( 
		fakeWFCatalog:docStore():docValue( assetId1 )
			:map( function( docValue )
				return docValue.copy
			end ), { copy_id = copyId } )
	
end

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

function WFCatCopiesTest.rxTestCreateCopiesOfAssetsInAlbum( rxTest )

	rxTest:setTimeLimit( 50 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	local assetId1 = 'e0be4f816aa149a7a3e8b148145d5135'
	
	local assetInfo = createAsset( rxTest, fakeWFCatalog, docStore, assetId1 )
	
	local localAssetId = rxTest:getFirstValue( fakeWFCatalog:docStore():docIdToLocalId( assetId1 ) )
	
	local albumObservable = WFCatAlbums.createAlbum( fakeWFCatalog, 'test album' )

	local _, albumId = rxTest:getFirstValue( albumObservable )

	rxTest:getFirstValue( fakeWFCatalog:docStore():setDocValue {
		_id = KSIdGenerator.uuidInLowercaseHex(),
		type = 'album_asset',
		album = {
			id = albumId,
		},
		asset = {
			id = assetId1,
		},
	} )
	
	-- Copy a single asset.
	local copiedAssetId
	
	local platformAssetId
	if WFPlatformInfo.isDesktop then
		platformAssetId = localAssetId
	else
		platformAssetId = assetId1
	end
	
	rxTest:expectSingleValue( WFCatCopies.createCopiesOfAssets( fakeWFCatalog, { platformAssetId }, { albumId } )
		:map( function( copiedAssetIds )
			copiedAssetId = copiedAssetIds[ 1 ]
			return copiedAssetIds and #copiedAssetIds == 1
		end ),
	true )

	-- get some info from the copied asset.
	
	local copiedAssetRevId, copyId
	
	rxTest:expectSingleValue( fakeWFCatalog:docStore():docValue( copiedAssetId )
		:map( function( docValue )
			copiedAssetRevId = docValue._revId
			copyId = docValue.copy.copy_id
			return true
		end ), true )

	-- Compare the new copied doc value (ignoring the _id and _revId values fished out above)
	-- This also verifies that there is no _serverProvidedMetadata in the copied asset.
	
	rxTest:expectSingleValue( 
		fakeWFCatalog:docStore():docValue( copiedAssetId )
			:map( function( docValue )

				docValue._changeCounter = "<changeCounter>"
				docValue._localId = "<localId>"
				
				return mapAwayPaths( rxTest, fakeWFCatalog, docValue, assetInfo )

			end ), getExpectedCopiedDocValue( assetInfo, assetId1, copiedAssetId, copiedAssetRevId, copyId ) )


	local albumAssetTableName
	if WFPlatformInfo.isDesktop then
		albumAssetTableName = 'album_asset_v2'
	else
		albumAssetTableName = 'album_asset'
	end
		
	rxTest:expectSingleValue(
		fakeWFCatalog:indexStore():queryOnce {
			sql = 'SELECT COUNT( docId ) FROM ' .. albumAssetTableName .. ' WHERE albumId == ?',
			quiesceUntilUpToDate = true,
			albumId }, { { 2 } } )
	
end

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

function WFCatCopiesTest.rxTestCreateCopiesOfAssetsNoLocalProxyOrOriginal( rxTest )

	rxTest:setTimeLimit( 50 )

	local mockThumb = KSData.make( 'proxy_data' )
	
	local testFileName = "IMG_7706.PNG"

	local tempFolder = WFTestFileUtils.createTestFolder( rxTest )

	local filePath = KSPathUtils.child( tempFolder, testFileName )
	KSData.saveToFile( mockThumb, filePath )

	rxTest:finally( function()
		WFFileUtils.delete( filePath )
		mockThumb:close()
	end )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	local assetId1 = 'e0be4f816aa149a7a3e8b148145d5135'

	local revId = KSIdGenerator.uuidInLowercaseHex()

	local srcDocValue = {
			_id = assetId1,
			type = 'asset',
			subtype = 'image',
			_newRevId = revId,
			_serverProvidedMetadata = {
				links = {
					[ '/rels/master' ] = { href = '/href/bogus/testing' },
					[ '/rels/xmp' ] = { href = '/href/bogus/testing' }
				}
			},
		}

	-- Create an asset with no local files
	
	rxTest:expectSingleValue( docStore:setDocValue( srcDocValue ), ':ignore:' )

	-- Copy a thumbnail to the proper location.
	
	local NmbLocalRenditionDigest, srcThumbnailPath, thumbnailContentId, thumbnailFileSize
	local srcThumbnailMd5
	
	if WFPlatformInfo.isDesktop then
	
		NmbLocalRenditionDigest = require 'NmbLocalRenditionDigest'
		local md5XmpCameraRawSrc = NmbLocalRenditionDigest.maybeComputeFromDocValue( srcDocValue)
		srcThumbnailPath = fakeWFCatalog:fileSpec():pathForPreviewDigestAndLongEdge( srcDocValue._id, md5XmpCameraRawSrc, 320, true )
		KSData.saveToFile( mockThumb, srcThumbnailPath )
		
		rxTest:finally( function()
			WFFileUtils.delete( srcThumbnailPath )
		end )
		
	elseif WFPlatformInfo.isiOS then
	
	
		thumbnailContentId = KSIdGenerator.uuidInLowercaseHex()
		
		srcThumbnailPath = KSPathUtils.child( tempFolder, KSIdGenerator.uuidInLowercaseHex() )
		KSData.saveToFile( mockThumb, srcThumbnailPath )
		
		rxTest:finally( function()
			WFFileUtils.delete( srcThumbnailPath )
		end )
		
		thumbnailFileSize = WFFileUtils.fileAttributes( srcThumbnailPath ).fileSize
		
		rxTest:expectSingleValue( docStore:applyOverlay( {
		
			_id = assetId1,
		
			_localOnly = {
				files = {
					thumbnail2x = {
						path = srcThumbnailPath,
						contentId = thumbnailContentId,
						fileSize = thumbnailFileSize,
					}
				}
			}
		
		} ), ':ignore:' )
	
	elseif WFPlatformInfo.isAndroid then
	
		srcThumbnailPath = KSPathUtils.child( tempFolder, KSIdGenerator.uuidInLowercaseHex() )
		KSData.saveToFile( mockThumb, srcThumbnailPath )
		
		rxTest:finally( function()
			WFFileUtils.delete( srcThumbnailPath )
		end )
		
		thumbnailFileSize = WFFileUtils.fileAttributes( srcThumbnailPath ).fileSize
		
		srcThumbnailMd5 = mockThumb:digest( 'MD5' )
		
		rxTest:expectSingleValue( docStore:applyOverlay( {
		
			_id = assetId1,
		
			_localOnly = {
				thumbnail2x = {
					md5 = srcThumbnailMd5,
					revId = revId,
					size = thumbnailFileSize,
				}
			}
		
		} ), ':ignore:' )
	
	end
	
	-- Copy a single asset.
	
	local copiedAssetId
	
	rxTest:expectSingleValue( WFCatCopies.createCopiesOfAssets( fakeWFCatalog, { assetId1 } )
		:map( function( copiedAssetIds )
			copiedAssetId = copiedAssetIds[ 1 ]
			return copiedAssetIds and #copiedAssetIds == 1
		end ),
	true )

	-- get some info from the copied asset.
	
	local copiedAssetRevId, copyId
	
	if WFPlatformInfo.isDesktop then
	
		rxTest:expectSingleValue( fakeWFCatalog:docStore():docValue( copiedAssetId )
			:map( function( copyDocValue )
				copiedAssetRevId = copyDocValue._revId
				copyId = copyDocValue.copy.copy_id

				local md5XmpCameraRawCpy = NmbLocalRenditionDigest.maybeComputeFromDocValue( copyDocValue )
				local cpyThumbnailPath = fakeWFCatalog:fileSpec():pathForPreviewDigestAndLongEdge( copyDocValue._id, md5XmpCameraRawCpy, 320 )

				-- Make sure the thumbnail file was copied to the correct location.
				rxTest:expectNotEqual( srcThumbnailPath, cpyThumbnailPath )
				rxTest:expectEqual( 'file', WFFileUtils.exists( cpyThumbnailPath ) )

				return true
			end ), true )

		-- Compare the new copied doc value (ignoring the _id and _revId values fished out above)
		-- This also verifies that there is no _serverProvidedMetadata in the copied asset.
		rxTest:expectSingleValue(
			fakeWFCatalog:docStore():docValue( copiedAssetId ),
			{
				_changeCounter = 3,
				_id = copiedAssetId,
				_localId = 2,
				_localOnly = {
					sourceAssetId = assetId1,
					sourceOriginalLink = "/href/bogus/testing",
					sourceXmpLink = "/href/bogus/testing",
					xmpSidecarExists = true,
				},
				_revId = copiedAssetRevId,
				copy = {
					copy_id = copyId,
				},
				subtype = "image",
				type = "asset",
			}
		)

	elseif WFPlatformInfo.isiOS then
	
		rxTest:expectSingleValue( fakeWFCatalog:docStore():docValue( copiedAssetId )
			:map( function( copyDocValue )
			
				copiedAssetRevId = copyDocValue._revId
				copyId = copyDocValue.copy.copy_id

				local cpyThumbnail = KSTableUtils_derefOpt( copyDocValue, '_localOnly', 'files', 'thumbnail2x', 'path' )
				
				local basePath = OzFilePaths.getCatalogBasePath( fakeWFCatalog:ozCatalogId() )
				local absolutePath = KSPathUtils.child( basePath, cpyThumbnail )
				
				-- Make sure the thumbnail data was copied
				rxTest:expectEqual( 'file', WFFileUtils.exists( absolutePath ) )

				return true
				
			end ), true )

		-- Compare the new copied doc value (ignoring the _id and _revId values fished out above)
		-- This also verifies that there is no _serverProvidedMetadata in the copied asset.
		rxTest:expectSingleValue(
			fakeWFCatalog:docStore():docValue( copiedAssetId )
				:map( function( copyDocValue )
			
					copyDocValue._localOnly.files.thumbnail2x.path = '<newThumbnailPath>'
					
					return copyDocValue
					
				end ),
			{
				_changeCounter = 4,
				_id = copiedAssetId,
				_localId = 2,
				_localOnly = {
					files = {
						thumbnail2x = {
							path = '<newThumbnailPath>',
							contentId = thumbnailContentId,
							fileSize = thumbnailFileSize,
						}
					},
					sourceAssetId = assetId1,
					sourceOriginalLink = "/href/bogus/testing",
					sourceXmpLink = "/href/bogus/testing",
					xmpSidecarExists = true,
				},
				_revId = copiedAssetRevId,
				copy = {
					copy_id = copyId,
				},
				subtype = "image",
				type = "asset",
			}
		)

	elseif WFPlatformInfo.isAndroid then

		rxTest:expectSingleValue( fakeWFCatalog:docStore():docValue( copiedAssetId )
			:map( function( copyDocValue )
			
				copiedAssetRevId = copyDocValue._revId
				copyId = copyDocValue.copy.copy_id

				local cpyThumbnailMd5 = KSTableUtils_derefOpt( copyDocValue, '_localOnly', 'thumbnail2x', 'md5' )
				
				return cpyThumbnailMd5
				
			end ), srcThumbnailMd5 )

		-- Compare the new copied doc value (ignoring the _id and _revId values fished out above)
		-- This also verifies that there is no _serverProvidedMetadata in the copied asset.
		
		local expectedDocValue = {
			_changeCounter = 4,
			_id = copiedAssetId,
			_localId = 2,
			_localOnly = {
				thumbnail2x = {
					md5 = srcThumbnailMd5,
					revId = copiedAssetRevId,
					size = thumbnailFileSize,
				},
				sourceAssetId = assetId1,
				sourceOriginalLink = "/href/bogus/testing",
				sourceXmpLink = "/href/bogus/testing",
				xmpSidecarExists = true,
			},
			_revId = copiedAssetRevId,
			copy = {
				copy_id = copyId,
			},
			subtype = "image",
			type = "asset",
		}
		
		rxTest:logf( "expectedDocValue %s", expectedDocValue ) 
		
		rxTest:expectSingleValue(
			fakeWFCatalog:docStore():docValue( copiedAssetId )
				:map( function( copyDocValue )
				
					rxTest:logf( "copyDocValue %s", copyDocValue ) 
				
					return copyDocValue
					
				end ), expectedDocValue
		)

	end

	-- compare the original doc value to see that it has the copy_id set now as well
	
	rxTest:expectSingleValue( 
		fakeWFCatalog:docStore():docValue( assetId1 )
			:map( function( docValue )
				return docValue.copy
			end ), { copy_id = copyId } )

end

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

function WFCatCopiesTest.reportAllChanges( scanState, docValue )
	return scanState, docValue
end

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

function WFCatCopiesTest.rxTestCreateCopiesOfAssetsMultiple( rxTest )

	rxTest:setTimeLimit( 300 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	local assetId1 = '11114f816aa149a7a3e8b148145d1111'
	local assetId2 = '22224f816aa149a7a3e8b148145d2222'

	local assetIdsToCopy = { assetId1, assetId2 }
	local copyId1
	local copyId2
	local numCopiesAsset1 = 0
	local numCopiesAsset2 = 0
	local numDocUpdates = 0
	local asset1UpdateCount = 0
	local asset2UpdateCount = 0
	
	local changedDocsObserver = rxTest:observe(
		fakeWFCatalog:docStore():scannedFilteredMappedChangesFeedSince( "WFCatCopiesTest" , "reportAllChanges", {}, 1 )
		:map( function( doc )

			numDocUpdates = numDocUpdates + 1

			if doc._id == assetId1 then
				asset1UpdateCount = asset1UpdateCount + 1
			elseif doc._id == assetId2 then
				asset2UpdateCount = asset2UpdateCount + 1
			else
				table.insert( assetIdsToCopy, doc._id )
			end

			if doc.copy then
				if copyId1 == nil then
					copyId1 = doc.copy.copy_id
				end

				if doc.copy.copy_id ~= copyId1 then
					if copyId2 == nil then
						copyId2 = doc.copy.copy_id
					end
				end

				if doc.copy.copy_id == copyId1 then
					numCopiesAsset1 = numCopiesAsset1 + 1
				end

				if doc.copy.copy_id == copyId2 then
					numCopiesAsset2 = numCopiesAsset2 + 1
				end

			end

			return numDocUpdates, numCopiesAsset1, numCopiesAsset2

		end )
	)
	
	-- Create an asset with no local files
	rxTest:expectSingleValue( docStore:setDocValue {
			_id = assetId1,
			type = 'asset',
			subtype = 'image',
			_serverProvidedMetadata = {
				links = {
					[ '/rels/master' ] = { href = '/href/bogus/testing' }
				}
			},
		}, ':ignore:' )

	-- Create another asset with no local files
	rxTest:expectSingleValue( docStore:setDocValue {
			_id = assetId2,
			type = 'asset',
			subtype = 'image',
			_serverProvidedMetadata = {
				links = {
					[ '/rels/master' ] = { href = '/href/bogus/testing2' }
				}
			},
		}, ':ignore:' )


	-- Wait for the two assets to show up, there will be no copies. 
	changedDocsObserver:expectDataWithinTime( 45, 2, 0, 0 )

	-- reset numDocUpdates to ignore the doc updates made to create the assets
 
	numDocUpdates = 0

	local numDocUpdatesExpected = 4

	while #assetIdsToCopy < 256 do

		rxTest:expectSingleValue( WFCatCopies.createCopiesOfAssets( fakeWFCatalog, assetIdsToCopy )
			:map( function( copiedAssetIds )
				return #copiedAssetIds
			end )
		, #assetIdsToCopy )

		changedDocsObserver:expectDataWithinTime( 45, numDocUpdatesExpected, numDocUpdatesExpected / 2, numDocUpdatesExpected / 2 )

		numDocUpdatesExpected = numDocUpdatesExpected * 2

	end

	rxTest:expectEqual( #assetIdsToCopy, 256 )

end

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

function WFCatCopiesTest.rxTestCopiedDocModifierFn( rxTest )

	rxTest:setTimeLimit( 50 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	local assetId1 = '11114f816aa149a7a3e8b148145d1111'

	rxTest:expectSingleValue( docStore:setDocValue {
			_id = assetId1,
			type = 'asset',
			subtype = 'image',
			_serverProvidedMetadata = {
				links = {
					[ '/rels/master' ] = { href = '/href/bogus/testing' }
				}
			},
		}, ':ignore:' )

	local spaceId = KSIdGenerator.uuidInLowercaseHex()

	local copiedDocModifierFn = function( copiedDocValue )
	
		copiedDocValue.subtype = 'video'
		copiedDocValue._localOnly = {
			foo = true
		}
		copiedDocValue.spaceId = spaceId
		return copiedDocValue
	
	end

	local copiedAssetId

	rxTest:expectSingleValue( WFCatCopies.createCopiesOfAssets( fakeWFCatalog, { assetId1 }, nil, nil, copiedDocModifierFn )
		:map( function( copiedAssetIds )
		
			copiedAssetId = copiedAssetIds[ 1 ]

			return #copiedAssetIds == 1 
					and type( copiedAssetId ) == 'string' 
					and copiedAssetId ~= assetId1
					
		end ), true )

	rxTest:expectSingleValue( fakeWFCatalog:docStore()
			:docValue( copiedAssetId )
			:map( function( docValue )
				docValue._changeCounter = nil
				docValue._localId = nil
				docValue._revId = nil
				docValue.copy.copy_id = "<copy_id>"
				return docValue
			end ),
			{
				_id = copiedAssetId,
				_localOnly = {
					foo = true,
				},
				spaceId = spaceId,
				copy = {
					copy_id = "<copy_id>",
				},
				subtype = "video",
				type = "asset",
			} )

end

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

function WFCatCopiesTest.rxTestCalculateCopyPathsAndUpdateDocValue( rxTest )

	rxTest:setTimeLimit( 50 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	local assetId1 = '123e4f816aa149a7a3e8b148145d5135'
	local assetId2 = '234e4f816aa149a7a3e8b148145d5135'
	
	local assetInfo, srcDocValue = createAsset( rxTest, fakeWFCatalog, docStore, assetId1 )
	
	local _, dstDocValue = createAsset( rxTest, fakeWFCatalog, docStore, assetId2 )
	
	if WFPlatformInfo.isDesktop then
		KSTableUtils.setNested( dstDocValue, nil, '_localOnly', 'files', 'original', 'master', 'sha256' )
		KSTableUtils.setNested( dstDocValue, nil, '_localOnly', 'files', 'proxy', '2560', 'sha256' )
	elseif WFPlatformInfo.isiOS then
		KSTableUtils.setNested( dstDocValue, nil, '_localOnly', 'files', 'latestMaster' )
	elseif WFPlatformInfo.isAndroid then
		KSTableUtils.setNested( dstDocValue, nil, '_localOnly', 'original', 'epoch' )
		KSTableUtils.setNested( dstDocValue, nil, '_localOnly', 'proxy', 'epoch' )
		KSTableUtils.setNested( dstDocValue, nil, '_localOnly', 'latestOriginal' )
	end
	
	local expectedDstDocValue = getExpectedCopiedDocValue( assetInfo, assetId1, assetId2, nil, nil )
	expectedDstDocValue.importSource = nil
	expectedDstDocValue._changeCounter = nil
	expectedDstDocValue._localId = nil
	expectedDstDocValue._serverProvidedMetadata = nil
	expectedDstDocValue._revId = nil
	expectedDstDocValue.copy = nil
	expectedDstDocValue._localOnly.sourceAssetId = nil	
	expectedDstDocValue._localOnly.sourceOriginalLink = nil
	expectedDstDocValue._localOnly.sourceProxyLink = nil
	expectedDstDocValue._localOnly.sourceXmpLink = nil
	
	local expectedFilesToCopy = {}
	
	if WFPlatformInfo.isDesktop then
	
		expectedFilesToCopy = {
				{
					srcFilePath = assetInfo.thumbnailFilePath,
					dstFilePath = '<newPath>',
				},
				{
					srcFilePath = assetInfo.previewFilePath,
					dstFilePath = '<newPath>',
				},
				{
					srcFilePath = assetInfo.originalFilePath,
					dstFilePath = '<newPath>',
				},
				{
					srcFilePath = assetInfo.proxyFilePath,
					dstFilePath = '<newPath>',
				},
				{
					srcFilePath = assetInfo.xmpSidecarFilePath,
					dstFilePath = '<newPath>',
				}
			}
			
	elseif WFPlatformInfo.isiOS then
		expectedDstDocValue._localOnly.xmpSidecarExists = nil
		
		expectedFilesToCopy = {
				{
					srcFilePath = assetInfo.thumbnailFilePath,
					dstFilePath = '<newPath>',
				},
				{
					srcFilePath = assetInfo.previewFilePath,
					dstFilePath = '<newPath>',
				},
				{
					srcFilePath = assetInfo.originalFilePath,
					dstFilePath = '<newPath>',
				},
				{
					srcFilePath = assetInfo.proxyFilePath,
					dstFilePath = '<newPath>',
				},
				{
					srcFilePath = assetInfo.xmpDevelopSettingsFilePath,
					dstFilePath = '<newPath>',
				},
			}
		
	elseif WFPlatformInfo.isAndroid then
		expectedDstDocValue._localOnly.xmpSidecarExists = nil
		
		expectedFilesToCopy = {
				{
					srcFilePath = assetInfo.originalFilePath,
					dstFilePath = '<newPath>',
				},
				{
					srcFilePath = assetInfo.proxyFilePath,
					dstFilePath = '<newPath>',
				}
			}
	
	end
	
	rxTest:expectSingleValue( Observable.just(
			WFCatCopies.calculateCopyPathsAndUpdateDocValue( fakeWFCatalog, srcDocValue, dstDocValue ) )
				:map( function( newDstDocValue, filesToCopy )
				
					for _, fileToCopy in ipairs( filesToCopy ) do
					
						local srcFilePath = fileToCopy.srcFilePath
						local dstFilePath = fileToCopy.dstFilePath
						
						rxTest:expectNotEqual( srcFilePath, dstFilePath )
						
						fileToCopy.dstFilePath = '<newPath>'
					
					end

					newDstDocValue.importSource = nil
					newDstDocValue._serverProvidedMetadata = nil
				
					return mapAwayPaths( rxTest, fakeWFCatalog, newDstDocValue, assetInfo, false ), filesToCopy
				
				end ), 
			expectedDstDocValue,
			expectedFilesToCopy )
	
end

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

function WFCatCopiesTest.rxTestCopyAsContributor( rxTest )

	rxTest:setTimeLimit( 50 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	-- Test 1: Original And Proxy on Oz.
	
	local assetId1 = KSIdGenerator.uuidInLowercaseHex()

	createAsset( rxTest, fakeWFCatalog, docStore, assetId1 )

	local copiedAssetId
	
	local optArgs = {
		isContributorCopyingToSharedAlbum = true
			-- if isContributorCopyingToSharedAlbum is set to true, the original/proxy
			-- should only be copied if the source asset hasn't been uploaded or if
			-- there are no renditions. This is because contributors should not have
			-- access to proxies or originals, even for those images that they
			-- contributed.
	}
	
	rxTest:expectSingleValue( WFCatCopies.createCopiesOfAssets( 
			fakeWFCatalog, { assetId1 }, nil, nil, nil, nil, nil, optArgs )
		:map( function( copiedAssetIds )
			copiedAssetId = copiedAssetIds[ 1 ]
			return copiedAssetIds and #copiedAssetIds == 1
		end ),
	true )

	-- Compare the source asset to the copied asset, verifying the binaries were not copied.
	
	rxTest:expectSingleValue( 
		fakeWFCatalog:docStore():docValue( copiedAssetId )
			:map( function( docValue )

				local copy_originalPath = WFCatAssetUtils.getFullOriginalPathFromDocValue( docValue )
				local copy_proxyPath = WFCatAssetUtils.getFullProxyPathFromDocValue( docValue )

				return not not copy_originalPath, not not copy_proxyPath

			end ), false, false )
	
	-- Test 2: No Original or Proxy on Oz
	
	local assetId2 = KSIdGenerator.uuidInLowercaseHex()

	local optOverrides = {
		noMasterLink = true
	}

	createAsset( rxTest, fakeWFCatalog, docStore, assetId2, nil, optOverrides )
	
	rxTest:expectSingleValue( WFCatCopies.createCopiesOfAssets( 
			fakeWFCatalog, { assetId2 }, nil, nil, nil, nil, nil, optArgs )
		:map( function( copiedAssetIds )
			copiedAssetId = copiedAssetIds[ 1 ]
			return copiedAssetIds and #copiedAssetIds == 1
		end ),
	true )

	-- Compare the source asset to the copied asset, verifying the binaries were copied.
	
	rxTest:expectSingleValue( 
		fakeWFCatalog:docStore():docValue( copiedAssetId )
			:map( function( docValue )

				local copy_originalPath = WFCatAssetUtils.getFullOriginalPathFromDocValue( docValue )
				local copy_proxyPath = WFCatAssetUtils.getFullProxyPathFromDocValue( docValue )

				return not not copy_originalPath, not not copy_proxyPath

			end ), true, true )
end

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

if WFPlatformInfo.isMobile() then

function WFCatCopiesTest.rxTestCopyXmpDevelopToNewPathArg( rxTest )

	rxTest:setTimeLimit( 240 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	-- Test 1: Original And Proxy on Oz.
	
	local assetId1 = KSIdGenerator.uuidInLowercaseHex()

	local assetInfo = createAsset( rxTest, fakeWFCatalog, docStore, assetId1 )

	local copiedAssetId
	
	local optArgs = {
		isXmpDevelopCopyingToNewPath = true
			-- if isXmpDevelopCopyingToNewPath is set to true, the xmp_develop file
			-- should be copied to a new path
	}
	
	local spaceId = KSIdGenerator.uuidInLowercaseHex()
	
	local copiedDocModifierFn = function( copiedDocValue )
		copiedDocValue._spaceId = spaceId
		return copiedDocValue
	end
	
	rxTest:expectSingleValue( WFCatCopies.createCopiesOfAssets( 
			fakeWFCatalog, { assetId1 }, nil, nil, copiedDocModifierFn, nil, nil, optArgs )
		:map( function( copiedAssetIds )
			copiedAssetId = copiedAssetIds[ 1 ]
			return copiedAssetIds and #copiedAssetIds == 1
		end ),
	true )

	-- Compare the source asset to the copied asset, verifying the xmp file is copied
	
	rxTest:expectSingleValue( 
		fakeWFCatalog:docStore():docValue( copiedAssetId )
			:map( function( docValue )

				local xmpPath = WFCatAssetUtils.getLocalExternalDevelopSettingsPath( docValue )

				return xmpPath ~= assetInfo.xmpDevelopSettingsFilePath

			end ), true )
	
end

end

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

function WFCatCopiesTest.rxTestCopyAsset_willNotUploadMaster( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Insert 3 assets into the doc store
	-- assetDoc1: willNotUploadMaster = true, no master link, or local master
	-- assetDoc2: willNotUploadMaster = true, master link, no local master
	-- assetDoc3: willNotUploadMaster = true, no master link, local master

	local function buildDocValue( rxTest, wfCatalog, assetId, overrides )

		assetId = assetId or KSIdGenerator.uuidInLowercaseHex()
		return WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId, overrides )

	end

	local assetDoc1 = buildDocValue( rxTest, wfCatalog, nil, { noLocalOriginal = true, noMasterLink = true } )
	KSTableUtils.setNested( assetDoc1, true, 'importSource', 'willNotUploadMaster' )

	local assetDoc2 = buildDocValue( rxTest, wfCatalog, nil, { noLocalOriginal = true }  )	
	KSTableUtils.setNested( assetDoc2, true, 'importSource', 'willNotUploadMaster' )

	local assetDoc3 = buildDocValue( rxTest, wfCatalog, nil, { noMasterLink = true }  )
	KSTableUtils.setNested( assetDoc3, true, 'importSource', 'willNotUploadMaster' )

	local assetIds = {}
	table.insert( assetIds, assetDoc1._id )
	table.insert( assetIds, assetDoc2._id )
	table.insert( assetIds, assetDoc3._id )

	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc1 ) )
	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc2 ) )
	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc3 ) )

	-- Copy the assets.

	local copiedAssetIds = 
			rxTest:getFirstValue( WFCatCopies.createCopiesOfAssets( wfCatalog, { assetDoc1._id, assetDoc2._id, assetDoc3._id } ) )

	local copiedDoc1FromDocStore = rxTest:getFirstValue( wfCatalog:docStore():docValue( copiedAssetIds[ 1 ] ) )
		
	local copiedDoc2FromDocStore = rxTest:getFirstValue( wfCatalog:docStore():docValue( copiedAssetIds[ 2 ] ) )
	
	local copiedDoc3FromDocStore = rxTest:getFirstValue( wfCatalog:docStore():docValue( copiedAssetIds[ 3 ] ) )

	assert( KSTableUtils.derefOpt( copiedDoc1FromDocStore, 'importSource', 'willNotUploadMaster' ), "copied doc 1 should be willNotUploadMaster = true" )
	assert( KSTableUtils.derefOpt( copiedDoc2FromDocStore, 'importSource', 'willNotUploadMaster' ) == nil, "copied doc 2 should be willNotUploadMaster nil" )
	assert( KSTableUtils.derefOpt( copiedDoc3FromDocStore, 'importSource', 'willNotUploadMaster' ) == nil, "copied doc 3 should be willNotUploadMaster nil" )

end

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

return WFRxTestCase.wrapSuite( WFCatCopiesTest )
