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

ADOBE CONFIDENTIAL
------------------
Copyright 2022 Adobe
All Rights Reserved.

NOTICE: All information contained herein is, and remains
the property of Adobe and its suppliers, if any. The intellectual
and technical concepts contained herein are proprietary to Adobe
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.

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

local Rx = require 'Rx'

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

	-- WF.core.utils
local KSData = require 'KSData'
local KSPathUtils = require 'KSPathUtils'
local KSTableUtils = require 'KSTableUtils'
local WFAsyncFileUtils = require "WFAsyncFileUtils"
local WFCapabilities = require 'WFCapabilities'
local WFFileUtils = require 'WFFileUtils'
local WFPlatformInfo = require 'WFPlatformInfo'
local WFTableMerge = require 'WFTableMerge'

	-- WF.managed_catalog
local WFCatOzTestSession = require 'WFCatOzTestSession'

	-- WF.oz_client.managed_catalog
local WFCatAssetAux = require 'WFCatAssetAux'
local WFCatAssetTestUtils = require 'WFCatAssetTestUtils'
local WFCatMockCatalog = require 'WFCatMockCatalog'
local WFCatSpecMock = require 'WFCatSpecMock'
local WFCatTestSession = require 'WFCatTestSession'
local WFCatVersions = require 'WFCatVersions'
local WFCatVersionTestUtils = require 'WFCatVersionTestUtils'
local WFCrypto = require "WFCrypto"

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

	-- WF.web.json
local KSJsonEncoder = require "KSJsonEncoder"

local LrmCatalogTestHelper

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

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

local WFCatAssetAuxTest = {}

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

local KSTableUtils_derefOpt = KSTableUtils.derefOpt

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

-- UTILITY FUNCTIONS

local auxCounter = 1

-- build a generator which yields sequentially identifiable aux documents
local function createAuxDoc(overrides)

	local newAux = {
		['type'] = 'mask',
		fileSize = 100,
		digest = "mask-" .. auxCounter .. "-digest",
		sha256 = "mask-" .. auxCounter .. "-sha256"
	}

	-- override default options
	if type(overrides) == 'table' then
		for k,v in pairs(overrides) do newAux[k] = v end
	end

	auxCounter = auxCounter + 1

	return newAux

end

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

local function createAuxDataAndFileInfo( auxType, overrides )

	auxType = auxType or 'mask'
	local valuePrefix = auxType .. '-'

	local digest = valuePrefix .. auxCounter .. "-digest"

	local newAuxData = {
		digest = digest,
	}

	if auxType == 'filter' then

		local groupDigest = valuePrefix .. auxCounter .. "-groupDigest"

		newAuxData = {
			digest = digest,
			groupDigest = groupDigest,
			sourceLongEdge = 6000,
			crsSizeX = 1024,
			crsSizeY = 768
		}

	end

	local _, relativeFilePath = WFCatAssetAux.calculatePathUsingDigest( 'foo', digest )

	local fileInfo = {
		fileSize = 100,
		sha256 = valuePrefix .. auxCounter .. "-sha256",
		relativeFilePath = relativeFilePath,
	}

	-- override default options
	if type(overrides) == 'table' then
		for k,v in pairs(overrides) do
			newAuxData[k] = v
			fileInfo[k] = v 
		end
	end

	local auxDoc = {
		type = auxType,
		digest = newAuxData.digest,
		fileSize = fileInfo.fileSize,
		sha256 = fileInfo.sha256,
	}

	auxCounter = auxCounter + 1

	return newAuxData, fileInfo, auxDoc

end

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

function WFCatAssetAuxTest.rxTestFindAuxByDigest( rxTest )

	local aux1ID, aux1 = 1, createAuxDoc({['type'] = 'mask', digest = "aux1" })
	local aux2ID, aux2 = 2, createAuxDoc({['type'] = 'mask', digest = "aux2" })
	local aux3ID, aux3 = 3, createAuxDoc({['type'] = 'depthMap', digest = "aux3" })

	local auxList = {
		[aux1ID] = aux1,
		[aux2ID] = aux2,
		[aux3ID] = aux3
	}

	local foundID, foundValue = WFCatAssetAux.findAuxByDigest( auxList, "aux1" )
	rxTest:expectEqual( aux1ID, foundID )
	rxTest:expectEqual( aux1, foundValue )

	foundID, foundValue = WFCatAssetAux.findAuxByDigest( auxList, "aux2" )
	rxTest:expectEqual( aux2ID, foundID )
	rxTest:expectEqual( aux2, foundValue )

	foundID, foundValue = WFCatAssetAux.findAuxByDigest( auxList, "aux3" )
	rxTest:expectEqual( aux3ID, foundID )
	rxTest:expectEqual( aux3, foundValue )

	foundID, foundValue = WFCatAssetAux.findAuxByDigest( auxList, "nonsense" )
	rxTest:expectEqual( nil, foundID )
	rxTest:expectEqual( nil, foundValue )

end


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

function WFCatAssetAuxTest.rxTestAddAuxOverlay( rxTest )

	local auxData1, fileInfo1, auxDoc1 = createAuxDataAndFileInfo()
	local auxData2, fileInfo2, auxDoc2 = createAuxDataAndFileInfo()
	local auxData3, fileInfo3, auxDoc3 = createAuxDataAndFileInfo()

	local auxDataList = {auxData1, auxData2, auxData3}

	local auxFileInfoByDigest = {
		[ auxData1.digest ] = fileInfo1,
		[ auxData2.digest ] = fileInfo2,
		[ auxData3.digest ] = fileInfo3,
	}

	local auxDocs = { auxDoc1, auxDoc2, auxDoc3 }

	local mockAsset = {
		base = "https://photos.adobe.io/v2/catalogs/fake-catalog/",
	    id = "Mock id",
		created = "2012-01-03T04:54:32.884643Z",
		updated = "2012-01-03T04:54:32.884643Z",
		type = "asset",
		subtype = "image",
		develop = {
			masks = {},
			filters = {}
		},
		aux = {}
	}

	local auxIds = {}
	local auxIdIndex = 0
	local currentAuxId = ''
	local function auxIDGenerator()
		auxIdIndex = auxIdIndex + 1
		currentAuxId = auxIdIndex
		auxIds[#auxIds + 1] = currentAuxId
		return currentAuxId
	end

	local testOverlay, auxIdByDigest = WFCatAssetAux.createAuxOverlay(mockAsset.aux, auxDataList, auxFileInfoByDigest, { auxIDGenerator = auxIDGenerator })
	WFCatAssetAux.addDevelopToOverlay( testOverlay, mockAsset.develop.filters, auxDataList, auxIdByDigest )

	-- Test that all auxDocs were added to the mask subsection, the aux section

	for _, maybeMaskDoc in ipairs(testOverlay.develop.masks) do

		-- ignore the _delete = true value in the overlay

		if type( maybeMaskDoc ) == 'table' then

			local maskCorrectlyOverlayed = not not KSTableUtils.findInTable(auxDocs, function(_, auxDoc)

				return auxDoc.digest == maybeMaskDoc.digest

			end)

			rxTest:expectEqual( true, maskCorrectlyOverlayed )
		end

	end

	rxTest:expectEqual( true, testOverlay.develop.masks._replace )

	rxTest:expectEqual( auxDocs, testOverlay.aux )

	local function auxInAuxDocs( doc )

		for _, aux in ipairs(auxDocs) do
			if (doc['type'] == aux['type']) and
			   (doc['fileSize'] == aux['fileSize']) and
			   (doc['digest'] == aux['digest']) and
			   (doc['sha256'] == aux['sha256']) then
			   	return true
			  end
		end

		return false
	end

	local function auxInPayloadDevelopMasks( id, doc )

		for auxID, miniAux in pairs(testOverlay.develop.masks) do
			if id == auxID and doc['digest'] == miniAux['digest'] then
				return true
			end
		end

		return false
	end

	for id, aux in pairs(mockAsset.aux) do

		-- check that the aux inserted was not modified by the insertion
		rxTest:expectEqual( true, auxInAuxDocs( aux ) )

		-- check that the aux is properly inserted into the payload.develop.mask subsection
		rxTest:expectEqual( true, auxInPayloadDevelopMasks( id, aux ) )

	end

	-- Test that edits count as adds - setup an "edit" of the first aux document

	local auxData4, fileInfo4 = createAuxDataAndFileInfo()
	local auxData5, fileInfo5, auxDoc5 = createAuxDataAndFileInfo( 'filter' )

	auxFileInfoByDigest = {
		[ auxData4.digest ] = fileInfo4,
		[ auxData5.digest ] = fileInfo5
	}

	-- build an overlay and pipe through the test id generator

	local editOverlay, editAuxIdByDigest = WFCatAssetAux.createAuxOverlay(testOverlay.aux, {auxData4, auxData5},
												auxFileInfoByDigest, { auxIDGenerator = auxIDGenerator })
	WFCatAssetAux.addDevelopToOverlay( editOverlay, mockAsset.develop.filters, {auxData4, auxData5}, editAuxIdByDigest )

	-- Merge both overlays to check behaviour in a real way
	WFTableMerge.applyOverlay( testOverlay, editOverlay )

	-- test that a new aux has been added
	rxTest:expectEqual(#auxDocs + 2, #KSTableUtils.extractKeys( testOverlay.aux ))

	-- check that the first aux is unmodified
	rxTest:expectEqual(auxDoc1.fileSize, testOverlay.aux[auxIds[1]].fileSize)

	-- check that the "edit" has been inserted as a new aux document
	local auxId5 = currentAuxId
	rxTest:expectEqual(auxDoc5.fileSize, testOverlay.aux[auxId5].fileSize)

	-- Check that the develop section has been overwriten
	rxTest:expectEqual(1, #KSTableUtils.extractKeys(testOverlay.develop.masks))

	rxTest:expectEqual( KSTableUtils.derefOpt( testOverlay, 'develop', 'filters' ),
		{
			[ auxData5.groupDigest ] = {
				[ auxId5 ] = {
					digest = auxData5.digest,
					sourceLongEdge = auxData5.sourceLongEdge,
					crsSizeX = 1024,
					crsSizeY = 768
				}
			}
		})

	-- Test that if filter aux digest already exists in the group, 
	-- ignore applying the new aux

	local auxData6, fileInfo6 = createAuxDataAndFileInfo( 'filter', 
															{
																groupDigest = auxData5.groupDigest,
																digest =  auxData5.digest 
															} )

	auxFileInfoByDigest = {
		[ auxData6.digest ] = fileInfo6
	}

	-- build an overlay and pipe through the test id generator

	local existingDigestEditOverlay, existingDigestEditAuxIdByDigest = WFCatAssetAux.createAuxOverlay(testOverlay.aux, {auxData6},
												auxFileInfoByDigest, { auxIDGenerator = auxIDGenerator })
	existingDigestEditAuxIdByDigest[ auxData6.digest ] = currentAuxId
	WFCatAssetAux.addDevelopToOverlay( existingDigestEditOverlay, testOverlay.develop.filters, {auxData6}, existingDigestEditAuxIdByDigest )

	-- Merge both overlays to check behaviour in a real way
	WFTableMerge.applyOverlay( testOverlay, existingDigestEditOverlay )

	-- develop.filters should remain the same, new auxData6 is not added.
	-- Because the same digest auxData5 already exists in the same group
	rxTest:expectEqual( KSTableUtils.derefOpt( testOverlay, 'develop', 'filters' ),
		{
			[ auxData5.groupDigest ] = {
				[ auxId5 ] = {
					digest = auxData5.digest,
					sourceLongEdge = auxData5.sourceLongEdge,
					crsSizeX = 1024,
					crsSizeY = 768
				}
			}
		})

	-- Test the clearing of masks

	local resetOverlay = WFCatAssetAux.createAuxOverlay(testOverlay.aux, {}, {}, { auxIDGenerator = auxIDGenerator })
	WFCatAssetAux.addDevelopToOverlay( resetOverlay, mockAsset.filters, {}, {} )

	-- Merge both overlays to check behaviour in a real way
	WFTableMerge.applyOverlay( testOverlay, resetOverlay )

	-- test that no aux has been added
	rxTest:expectEqual(#auxDocs + 2, #KSTableUtils.extractKeys( testOverlay.aux ))

	-- Check that the develop section has been overwriten
	rxTest:expectEqual(nil, testOverlay.develop.masks)
	rxTest:expectEqual(nil, testOverlay.develop.filters)

end

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

function WFCatAssetAuxTest.rxTestGetDownloadLink( rxTest )

	local maskID = "i-am-a-mask"
	local maskDigest = "i-am-a-mask-digest"
	local mask = createAuxDoc({ type = 'mask', digest = maskDigest })
	local maskDownloadLink = "i-am-a-mask-download-link"

	local depthMapID = "i-am-a-depthMap"
	local depthMapDigest = "i-am-a-depthMap-digest"
	local depthMap = createAuxDoc({ type = 'depthMap', digest = depthMapDigest })
	local depthMapDownloadLink = "i-am-a-depthMap-download-link"

	local docValue = {
		_serverProvidedMetadata = {
			links = {
				["/rels/aux/i-am-a-mask"] = {
					href = maskDownloadLink
				},
				["/rels/aux/i-am-a-depthMap"] = {
					href = depthMapDownloadLink
				},
			}
		},
		aux = {
			[maskID] = mask,
			[depthMapID] = depthMap
		}
	}


	rxTest:expectEqual( maskDownloadLink, WFCatAssetAux.getDownloadLink( docValue, maskDigest ) )
	rxTest:expectEqual( depthMapDownloadLink, WFCatAssetAux.getDownloadLink( docValue, depthMapDigest ) )

	rxTest:expectEqual( nil, WFCatAssetAux.getDownloadLink( docValue, "nonsense" ) )
	rxTest:expectEqual( nil, WFCatAssetAux.getDownloadLink( {}, maskDigest ) )
end

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

function WFCatAssetAuxTest.rxTestCalculatePathUsingDigest( rxTest )

	local baseBinariesPath = '/this/is/a/fake/path'

	local digest = '123fakedigest'

	local path = WFCatAssetAux.calculatePathUsingDigest( baseBinariesPath, digest )

	local expectedPath = '/this/is/a/fake/path/auxiliary/123/fakedigest'

	path = path:replace( '\\', '/' )

	rxTest:expectEqual( expectedPath, path )

end

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

function WFCatAssetAuxTest.rxTestCreateAuxLocalOnlyOverlay( rxTest )

	local testDigest = "i-am-an-aux-digest"
	local filePath = "on/ur/hard/drive"

	local expectedOverlay = {
		[ "_localOnly" ] = {
			files = {
				aux = {
					[ testDigest ] = {
						relativePath = filePath
					}
				}
			}
		}
	}

	rxTest:expectEqual( expectedOverlay, WFCatAssetAux.createAuxLocalOnlyOverlay( testDigest, filePath ) )

	-- Existing Overlay

	local testDigest2 = "i-am-an-aux-digest2"
	local filePath2 = "on/ur/hard/drive2"

	local existingOverlay = KSTableUtils.deepCopy( expectedOverlay )

	expectedOverlay = {
		[ "_localOnly" ] = {
			files = {
				aux = {
					[ testDigest ] = {
						relativePath = filePath
					},
					[ testDigest2 ] = {
						relativePath = filePath2
					}
				}
			}
		}
	}

	rxTest:expectEqual( expectedOverlay, WFCatAssetAux.createAuxLocalOnlyOverlay( testDigest2, filePath2, existingOverlay ) )

end

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

local function createFakeWFCatalog( rxTest )

	if WFPlatformInfo.isAndroid then

		local catalogService, wfCatalog = LrmCatalogTestHelper.makeCatalogService( rxTest )
		local dataCache = rxTest:getFirstValue( catalogService:getDataCache() )

		return wfCatalog, dataCache

	else

		local optArgs = {}

		if WFPlatformInfo.isiOS then
			optArgs.indexDefinitions = 'LrmCatalogIndexDefinitions.lua'
		end

		return WFCatMockCatalog.createFakeWFCatalog( rxTest , nil, nil, optArgs )

	end

end

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

local function auxFromPriorVersion( auxType )
	local auxId = KSIdGenerator.uuidInLowercaseHex()
	local auxData = {
			type = auxType,
			fileSize = 123,
			digest = KSIdGenerator.uuidInLowercaseHex(),
			sha256 = KSIdGenerator.uuidInLowercaseHex(),
		}
	return auxId, auxData
end

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

local function addAuxEntriesFromPriorVersionToDocValue( docValue )
	local verAuxId1, verAuxData1 = auxFromPriorVersion( 'mask' )
	local verAuxId2, verAuxData2 = auxFromPriorVersion( 'filter' )

	-- Modify the document to add some aux entries for prior
	-- versions of the doc that are not longer used by the
	-- current document and do not appear in the develop.filters
	-- or develop.masks tables.

	local aux = docValue.aux
	aux[ verAuxId1 ] = verAuxData1
	aux[ verAuxId2 ] = verAuxData2
end

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

 local function setUpTestCatalog( rxTest )

	local maybeIndexDefinitionsFile = WFPlatformInfo.isMobile() and 'LrmCatalogIndexDefinitions.lua' or nil

	local catalogTestSession = WFCatTestSession( rxTest, nil, maybeIndexDefinitionsFile )

	local catalog = rxTest:getFirstValue( catalogTestSession:catalog() )

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

	return catalog

end

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

local function addAuxToAsset( catalog, assetDoc, auxBinary, auxType )

	auxType = auxType or 'filter'

	local auxId = KSIdGenerator.uuidInLowercaseHex()

	-- Create and save aux binary

	local auxDigest = string.upper( WFCrypto.MD5.digest( KSIdGenerator.uuidInLowercaseHex() ) )

	local auxGroupDigest = string.upper( WFCrypto.MD5.digest( KSIdGenerator.uuidInLowercaseHex() ) )

	local baseAuxPath = WFCatAssetAux.getBasePath( catalog )

	local auxFilePath, auxRelativeFilePath = WFCatAssetAux.calculatePathUsingDigest( baseAuxPath, auxDigest )

	local auxParentPath = KSPathUtils.parent( auxFilePath )

	WFFileUtils.mkdir( auxParentPath )

	KSData.saveToFile( auxBinary, auxFilePath )

	auxBinary:close()

	local auxFileSize = WFFileUtils.fileAttributes( auxFilePath ).fileSize

	local auxSha256 = WFCrypto.digestFile( auxFilePath, 'SHA256' )

	-- Adds file to localonly section and sets up the develop section

	local isFilter = ( auxType == 'filter' )

	local auxParamsList = { {
		type = auxType,
		id = auxId,
		sha256 = auxSha256,
		filePath = auxRelativeFilePath,
		fileSize = auxFileSize,
		digest = auxDigest,
		groupDigest = isFilter and auxGroupDigest or nil,
		sourceLongEdge = isFilter and 6000 or nil,
		crsSizeX = isFilter and 1024 or nil,
		crsSizeY = isFilter and 768 or nil
	} }

	assetDoc = WFCatAssetTestUtils.addAuxiliaryMetadataReference( assetDoc, assetDoc._id, auxParamsList )

	return assetDoc, auxId, auxDigest, auxFilePath, auxGroupDigest

end

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

function WFCatAssetAuxTest.rxTestExtractDevelopAuxDigestsFromAsset( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local optArguments = {
		hasDevelopAux = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	addAuxEntriesFromPriorVersionToDocValue( docValue )

	local digestsByAuxId = WFCatAssetAux.extractDevelopAuxDigestsFromDocValue( docValue )

	local expectedDigests = {
		[ expectedData.mask1AuxId ] = expectedData.mask1Fingerprint,
		[ expectedData.mask2AuxId ] = expectedData.mask2Fingerprint,
		[ expectedData.filter1AuxId ] = expectedData.filter1Fingerprint,
		[ expectedData.filter2AuxId ] = expectedData.filter2Fingerprint,
	}

	rxTest:expectEqual( expectedDigests, digestsByAuxId )

end

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

function WFCatAssetAuxTest.rxTestExtractDevelopAuxDigestsFromVersion( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local optArguments = {
		hasDevelopAux = true
	}

	local docValue, expectedData = WFCatVersionTestUtils.createVersionDocument( rxTest, wfCatalog, nil, optArguments )

	local digestsByAuxId = WFCatAssetAux.extractDevelopAuxDigestsFromDocValue( docValue )

	local expectedDigests = {
		[ expectedData.mask1AuxId ] = expectedData.mask1Fingerprint,
		[ expectedData.mask2AuxId ] = expectedData.mask2Fingerprint,
		[ expectedData.filter1AuxId ] = expectedData.filter1Fingerprint,
		[ expectedData.filter2AuxId ] = expectedData.filter2Fingerprint
	}

	rxTest:expectEqual( expectedDigests, digestsByAuxId )

end

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

function WFCatAssetAuxTest.rxTestExtractLongestDevelopAuxDigestsFromAsset( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local optArguments = {
		hasDevelopAux = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	addAuxEntriesFromPriorVersionToDocValue( docValue )

	local digestsByAuxId = WFCatAssetAux.extractLongestDevelopAuxDigestsFromDocValue( docValue )

	-- Note filter2AuxId should not be included as it's a proxy in the same group as filter1AuxId

	local expectedDigests = {
		[ expectedData.mask1AuxId ] = expectedData.mask1Fingerprint,
		[ expectedData.mask2AuxId ] = expectedData.mask2Fingerprint,
		[ expectedData.filter1AuxId ] = expectedData.filter1Fingerprint,
	}

	rxTest:expectEqual( expectedDigests, digestsByAuxId )

end

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

function WFCatAssetAuxTest.rxTestExtractLongestDevelopAuxDigestsFromVersion( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local optArguments = {
		hasDevelopAux = true
	}

	local docValue, expectedData = WFCatVersionTestUtils.createVersionDocument( rxTest, wfCatalog, nil, optArguments )

	local digestsByAuxId = WFCatAssetAux.extractLongestDevelopAuxDigestsFromDocValue( docValue )

	-- Note filter2AuxId should not be included as it's a proxy in the same group as filter1AuxId

	local expectedDigests = {
		[ expectedData.mask1AuxId ] = expectedData.mask1Fingerprint,
		[ expectedData.mask2AuxId ] = expectedData.mask2Fingerprint,
		[ expectedData.filter1AuxId ] = expectedData.filter1Fingerprint,
	}

	rxTest:expectEqual( expectedDigests, digestsByAuxId )

end

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

function WFCatAssetAuxTest.rxTestIsVersionComplete( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Masks on Oz

	-- Test the most basic case

	local optArguments = {}

	local assetDocValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local versionDocValue = {
		type = 'version',
		subtype = 'edit',
		name = 'some new version',
		createdManually = true,
		asset = {
			id = assetDocValue._id
		}
	}

	local isComplete = WFCatAssetAux.isVersionComplete( assetDocValue, versionDocValue )

	rxTest:expectEqual( true, isComplete )

	-- Test when masks are present on asset and not the version

	optArguments = {
		hasMasks = true
	}

	assetDocValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	versionDocValue = {
		type = 'version',
		subtype = 'edit',
		name = 'some new version',
		createdManually = true,
		asset = {
			id = assetDocValue._id
		}
	}

	isComplete = WFCatAssetAux.isVersionComplete( assetDocValue, versionDocValue )

	rxTest:expectEqual( true, isComplete )


	-- Masks not on Oz, no version masks

	optArguments = {
		hasMasks = true,
		noMaskLinks = true
	}

	assetDocValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	isComplete = WFCatAssetAux.isVersionComplete( assetDocValue, versionDocValue )

	rxTest:expectEqual( true, isComplete )

	-- Masks on Oz, XMP sidecar not uploaded

	optArguments = {
		hasMasks = true,
		noMaskLinks = true,
		noXmpSidecarLink = true,
	}

	assetDocValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	isComplete = WFCatAssetAux.isVersionComplete( assetDocValue, versionDocValue )

	rxTest:expectEqual( true, isComplete )

        -- Masks on version (complete set)

	optArguments = {
		hasMasks = true,
	}

	assetDocValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	KSTableUtils.setNested(versionDocValue, assetDocValue.develop.masks, 'develop', 'masks')

	isComplete = WFCatAssetAux.isVersionComplete( assetDocValue, versionDocValue )

	rxTest:expectEqual( true, isComplete )

	-- Version Masks != Asset Masks but Asset Masks are local

	local deleteFirst = true
	for auxId, _ in pairs(versionDocValue.develop.masks) do

		if deleteFirst then
			versionDocValue.develop.masks[ auxId ] = nil
		end

		deleteFirst = false

	end

	isComplete = WFCatAssetAux.isVersionComplete( assetDocValue, versionDocValue )

	rxTest:expectEqual( true, isComplete )

end

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

function WFCatAssetAuxTest.rxTestIsVersionComplete_DifferentOptImageTypes( rxTest )

local wfCatalog = createFakeWFCatalog( rxTest )

--CASE 1: Both original and proxy on Oz, only original binary is local
--versionIsLocal should be true for both "original" and "proxy-2560" because we would always use original when available locally

	local optOverrides = {
		hasFilters = true
	}

	local assetDocValue, localOnlySettings = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optOverrides )

	WFFileUtils.delete( localOnlySettings.filter2FilePath )

	rxTest:expectEqual( false, WFFileUtils.exists( localOnlySettings.filter2FilePath ) )

	local filters =  KSTableUtils.derefOpt( assetDocValue, 'develop', 'filters' )

	local originalDigest = localOnlySettings.filter1Fingerprint

	local versionDocValue = {
		type = 'version',
		subtype = 'edit',
		name = 'some new version',
		createdManually = true,
		asset = {
			id = assetDocValue._id
		},
		develop = {
			filters = filters,
		},
		_localOnly = {
			files = {
				aux = {
					[ originalDigest ] = {
						relativePath = localOnlySettings.filter1FilePath
					}
				}
			}
		}
	}

	-- optImageType = original

	local optImageType = 'original'

	local _, versionIsLocal = WFCatAssetAux.isVersionComplete( assetDocValue, versionDocValue, optImageType )

	rxTest:expectEqual( true, versionIsLocal )

	-- optImageType = proxy-2560

	optImageType = 'proxy-2560'

	_, versionIsLocal = WFCatAssetAux.isVersionComplete( assetDocValue, versionDocValue, optImageType )

	rxTest:expectEqual( true, versionIsLocal )

	--CASE 2: Both on Oz, only proxy is local

	optOverrides = {
		hasFilters = true
	}

	assetDocValue, localOnlySettings = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optOverrides )

	WFFileUtils.delete( localOnlySettings.filter1FilePath )

	rxTest:expectEqual( false, WFFileUtils.exists( localOnlySettings.filter1FilePath ) )

	filters =  KSTableUtils.derefOpt( assetDocValue, 'develop', 'filters' )

	local proxyDigest = localOnlySettings.filter2Fingerprint

	versionDocValue = {
		type = 'version',
		subtype = 'edit',
		name = 'some new version',
		createdManually = true,
		asset = {
			id = assetDocValue._id
		},
		develop = {
			filters = filters,
		},
		_localOnly = {
			files = {
				aux = {
					[ proxyDigest ] = {
						relativePath = localOnlySettings.filter2FilePath
					}
				}
			}
		}
	}

	-- optImageType = original

	optImageType = 'original'

	_, versionIsLocal = WFCatAssetAux.isVersionComplete( assetDocValue, versionDocValue, optImageType )

	rxTest:expectEqual( false, versionIsLocal )

	-- optImageType = proxy-2560, should be false as the proxy is small enough to not need to resample

	optImageType = 'proxy-2560'

	_, versionIsLocal = WFCatAssetAux.isVersionComplete( assetDocValue, versionDocValue, optImageType )

	rxTest:expectEqual( false, versionIsLocal )

	-- optImageType = proxy-2560, should be true as we increase the proxy size

	KSTableUtils.setNested( assetDocValue, 1024*1024, 'aux', localOnlySettings.filter1AuxId, 'fileSize' )

	_, versionIsLocal = WFCatAssetAux.isVersionComplete( assetDocValue, versionDocValue, optImageType )

	rxTest:expectEqual( true, versionIsLocal )

	-- CASE 3: Both on Oz, both not local

	optOverrides = {
		hasFilters = true
	}

	assetDocValue, localOnlySettings = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optOverrides )

	WFFileUtils.delete( localOnlySettings.filter1FilePath )
	WFFileUtils.delete( localOnlySettings.filter2FilePath )

	rxTest:expectEqual( false, WFFileUtils.exists( localOnlySettings.filter1FilePath ) )
	rxTest:expectEqual( false, WFFileUtils.exists( localOnlySettings.filter2FilePath ) )

	versionDocValue = {
			type = 'version',
			subtype = 'edit',
			name = 'some new version',
			createdManually = true,
			asset = {
				id = assetDocValue._id
			},
			develop = {
				filters = filters,
			}
		}

	-- optImageType = original

	optImageType = 'original'

	_, versionIsLocal = WFCatAssetAux.isVersionComplete( assetDocValue, versionDocValue, optImageType )

	rxTest:expectEqual( false, versionIsLocal )

	-- optImageType = proxy-2560, should be false as the proxy is small enough to not need to resample

	optImageType = 'proxy-2560'

	_, versionIsLocal = WFCatAssetAux.isVersionComplete( assetDocValue, versionDocValue, optImageType )

	rxTest:expectEqual( false, versionIsLocal )


	-- CASE 4: Only original instance exists on Oz

	optOverrides = {
		hasSourceOnlyFilters = true,
		noLocalFilters = true
	}

	assetDocValue, localOnlySettings = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optOverrides )

	filters =  KSTableUtils.derefOpt( assetDocValue, 'develop', 'filters' )

	versionDocValue = {
			type = 'version',
			subtype = 'edit',
			name = 'some new version',
			createdManually = true,
			asset = {
				id = assetDocValue._id
			},
			develop = {
				filters = filters,
			}
		}

	-- optImageType = original

	optImageType = 'original'

	_, versionIsLocal = WFCatAssetAux.isVersionComplete( assetDocValue, versionDocValue, optImageType )

	rxTest:expectEqual( false, versionIsLocal )

	-- optImageType = proxy-2560,

	optImageType = 'proxy-2560'

	_, versionIsLocal = WFCatAssetAux.isVersionComplete( assetDocValue, versionDocValue, optImageType )

	rxTest:expectEqual( false, versionIsLocal )

	-- CASE 5: Only original instance on Oz, and local

	optOverrides = {
		hasSourceOnlyFilters = true
	}

	assetDocValue, localOnlySettings = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optOverrides )

	filters =  KSTableUtils.derefOpt( assetDocValue, 'develop', 'filters' )

	originalDigest = localOnlySettings.filter1Fingerprint

	versionDocValue = {
		type = 'version',
		subtype = 'edit',
		name = 'some new version',
		createdManually = true,
		asset = {
			id = assetDocValue._id
		},
		develop = {
			filters = filters,
		},
		_localOnly = {
			files = {
				aux = {
					[ originalDigest ] = {
						relativePath = localOnlySettings.filter1FilePath
					}
				}
			}
		}
	}

	-- optImageType = original

	optImageType = 'original'

	_, versionIsLocal = WFCatAssetAux.isVersionComplete( assetDocValue, versionDocValue, optImageType )

	rxTest:expectEqual( true, versionIsLocal )

	-- optImageType = proxy-2560,

	optImageType = 'proxy-2560'

	_, versionIsLocal = WFCatAssetAux.isVersionComplete( assetDocValue, versionDocValue, optImageType )

	rxTest:expectEqual( true, versionIsLocal )

end

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

function WFCatAssetAuxTest.rxTestIsAuxObjectOnOz( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Masks on Oz

	local optArguments = {
		hasMasks = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local isOnOz = WFCatAssetAux.isAuxObjectOnOz( docValue, expectedData.mask1AuxId )

	rxTest:expectEqual( true, isOnOz )

	isOnOz = WFCatAssetAux.isAuxObjectOnOz( docValue, expectedData.mask2AuxId )

	rxTest:expectEqual( true, isOnOz )

	-- Masks not on Oz

	optArguments = {
		hasMasks = true,
		noMaskLinks = true
	}

	docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	isOnOz = WFCatAssetAux.isAuxObjectOnOz( docValue, expectedData.mask1AuxId )

	rxTest:expectEqual( false, isOnOz )

	isOnOz = WFCatAssetAux.isAuxObjectOnOz( docValue, expectedData.mask2AuxId )

	rxTest:expectEqual( false, isOnOz )

	-- Space Masks

	optArguments = {
		hasMasks = true,
		noMaskLinks = true
	}

	docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )
	docValue._spaceId = KSIdGenerator.uuidInLowercaseHex()

	local auxRels = string.format( "/rels/aux/%s", expectedData.mask1AuxId )
	KSTableUtils.setNested( docValue, true, '_serverProvidedMetadata', 'links', auxRels, 'exists' )

	isOnOz = WFCatAssetAux.isAuxObjectOnOz( docValue, expectedData.mask1AuxId )

	rxTest:expectEqual( true, isOnOz )

	isOnOz = WFCatAssetAux.isAuxObjectOnOz( docValue, expectedData.mask2AuxId )

	rxTest:expectEqual( false, isOnOz )

end

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

function WFCatAssetAuxTest.rxTestIsAuxiliaryAccessible( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Masks on Oz

	local optArguments = {
		hasMasks = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local isAccessible = WFCatAssetAux.isAuxiliaryAccessible( wfCatalog, docValue, expectedData.mask1AuxId )

	rxTest:expectEqual( true, isAccessible )

	isAccessible = WFCatAssetAux.isAuxiliaryAccessible( wfCatalog, docValue, expectedData.mask2AuxId )

	rxTest:expectEqual( true, isAccessible )

	-- Masks not on Oz, but exists locally

	optArguments = {
		hasMasks = true,
		noMaskLinks = true
	}

	docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	isAccessible = WFCatAssetAux.isAuxiliaryAccessible( wfCatalog, docValue, expectedData.mask1AuxId )

	rxTest:expectEqual( true, isAccessible )

	isAccessible = WFCatAssetAux.isAuxiliaryAccessible( wfCatalog, docValue, expectedData.mask2AuxId )

	rxTest:expectEqual( true, isAccessible )

	-- Not on Oz, Not Local

	optArguments = {
		hasMasks = true,
		noMaskLinks = true,
		noLocalMasks = true
	}

	docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	isAccessible = WFCatAssetAux.isAuxiliaryAccessible( wfCatalog, docValue, expectedData.mask1AuxId )

	rxTest:expectEqual( false, isAccessible )

	isAccessible = WFCatAssetAux.isAuxiliaryAccessible( wfCatalog, docValue, expectedData.mask2AuxId )

	rxTest:expectEqual( false, isAccessible )

end

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

function WFCatAssetAuxTest.rxTestGetAuxIdForDigestAsset( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local optArguments = {
		hasMasks = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local actual = WFCatAssetAux.getAuxIdForDigest( docValue, expectedData.mask1Fingerprint )

	local expected = expectedData.mask1AuxId

	rxTest:expectEqual( expected, actual )

	actual = WFCatAssetAux.getAuxIdForDigest( docValue, expectedData.mask2Fingerprint )

	expected = expectedData.mask2AuxId

	rxTest:expectEqual( expected, actual )

end

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

function WFCatAssetAuxTest.rxTestGetAuxIdForDigestVersion( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local optArguments = {
		hasMasks = true
	}

	local docValue, expectedData = WFCatVersionTestUtils.createVersionDocument( rxTest, wfCatalog, nil, optArguments )

	local actual = WFCatAssetAux.getAuxIdForDigest( docValue, expectedData.mask1Fingerprint )

	local expected = expectedData.mask1AuxId

	rxTest:expectEqual( expected, actual )

	actual = WFCatAssetAux.getAuxIdForDigest( docValue, expectedData.mask2Fingerprint )

	expected = expectedData.mask2AuxId

	rxTest:expectEqual( expected, actual )

end

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

function WFCatAssetAuxTest.rxTestGetDigestForAuxIdAsset( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local optArguments = {
		hasMasks = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local actual = WFCatAssetAux.getDigestForAuxId( docValue, expectedData.mask1AuxId )

	local expected = expectedData.mask1Fingerprint

	rxTest:expectEqual( expected, actual )

	actual = WFCatAssetAux.getDigestForAuxId( docValue, expectedData.mask2AuxId )

	expected = expectedData.mask2Fingerprint

	rxTest:expectEqual( expected, actual )

end

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

function WFCatAssetAuxTest.rxTestMaybeGetGroupDigestForDigest( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local optArguments = {
		hasFilters = true,
		hasMasks = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local actual = WFCatAssetAux.maybeGetGroupDigestForDigest( docValue, expectedData.filter1Fingerprint )

	rxTest:expectEqual( expectedData.filterGroupFingerprint, actual )

	actual = WFCatAssetAux.maybeGetGroupDigestForDigest( docValue, expectedData.filter2Fingerprint )

	rxTest:expectEqual( expectedData.filterGroupFingerprint, actual )

	actual = WFCatAssetAux.maybeGetGroupDigestForDigest( docValue, expectedData.mask1Fingerprint )

	rxTest:expectEqual( nil, actual )

end

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

function WFCatAssetAuxTest.rxTestGetDigestForAuxIdVersion( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local optArguments = {
		hasMasks = true
	}

	local docValue, expectedData = WFCatVersionTestUtils.createVersionDocument( rxTest, wfCatalog, nil, optArguments )

	local actual = WFCatAssetAux.getDigestForAuxId( docValue, expectedData.mask1AuxId )

	local expected = expectedData.mask1Fingerprint

	rxTest:expectEqual( expected, actual )

	actual = WFCatAssetAux.getDigestForAuxId( docValue, expectedData.mask2AuxId )

	expected = expectedData.mask2Fingerprint

	rxTest:expectEqual( expected, actual )

end

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

function WFCatAssetAuxTest.rxTestUploadsRequired_Required( rxTest )

	local auxIdNeedUpload = "fake-aux"
	local auxId = "more-fake-aux"

	local auxNeedUpload = {
		["type"] = "mask",
		fileSize = 1,
		digest = auxIdNeedUpload ..  "-digest",
		sha256 = auxIdNeedUpload .. "-sha256"
	}

	local assetWithAuxNeedsUpload = {
		_id = "fake-asset",
		_serverProvidedMetadata = {
			links = {
				[ "/rels/aux/" .. auxId ] = {
					href = "I-am-a-real-href"
				}
			}
		},
		aux = {
			[ auxIdNeedUpload ] = auxNeedUpload,

			[ auxId ] = {
				[ "type" ] = "mask",
				fileSize = 1,
				digest = auxId ..  "-digest",
				sha256 = auxId .. "-sha256"
				}
			}
		}

	local uploadsRequired, auxToUpload = WFCatAssetAux.uploadsRequired( assetWithAuxNeedsUpload )

	rxTest:expectEqual( true, uploadsRequired )

	rxTest:expectEqual( {
		[ auxIdNeedUpload ] = auxNeedUpload
	}, auxToUpload )

end

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

function WFCatAssetAuxTest.rxTestUploadsRequired_NotRequired( rxTest )

	local auxId = "more-fake-aux"

	local assetWithAuxNeedsUpload = {
		_id = "fake-asset",
		_serverProvidedMetadata = {
			links = {
				[ "/rels/aux/" .. auxId ] = {
					href = "I-am-a-real-href"
				}
			}
		},
		aux = {
			[ auxId ] = {
				[ "type" ] = "mask",
				fileSize = 1,
				digest = auxId ..  "-digest",
				sha256 = auxId .. "-sha256"
			}
		}
	}

	local uploadsRequired, auxToUpload = WFCatAssetAux.uploadsRequired( assetWithAuxNeedsUpload )

	rxTest:expectEqual( false, uploadsRequired )

	rxTest:expectEqual( {}, auxToUpload )

end

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

function WFCatAssetAuxTest.rxTestUploadsRequired_SpaceAsset_Required( rxTest )

	local auxIdNeedUpload = "fake-aux"
	local auxId = "more-fake-aux"

	local auxNeedUpload = {
		["type"] = "mask",
		fileSize = 1,
		digest = auxIdNeedUpload ..  "-digest",
		sha256 = auxIdNeedUpload .. "-sha256"
	}

	local assetWithAuxNeedsUpload = {
		_id = "fake-asset",
		_spaceId = "fake-space-id",
		_serverProvidedMetadata = {
			links = {
				[ "/rels/aux/" .. auxId ] = {
					exists = "I-am-a-real-href"
				}
			}
		},
		aux = {
			[ auxIdNeedUpload ] = auxNeedUpload,

			[ auxId ] = {
				[ "type" ] = "mask",
				fileSize = 1,
				digest = auxId ..  "-digest",
				sha256 = auxId .. "-sha256"
			}
		}
	}

	local uploadsRequired, auxToUpload = WFCatAssetAux.uploadsRequired( assetWithAuxNeedsUpload )

	rxTest:expectEqual( true, uploadsRequired )

	rxTest:expectEqual( {
		[ auxIdNeedUpload ] = auxNeedUpload
	}, auxToUpload )

end

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

function WFCatAssetAuxTest.rxTestUploadsRequired_SpaceAsset_NotRequired( rxTest )

	local auxId = "more-fake-aux"

	local assetWithAuxNeedsUpload = {
		_id = "fake-asset",
		_spaceId = "fake-space-id",
		_serverProvidedMetadata = {
			links = {
				[ "/rels/aux/" .. auxId ] = {
					exists = "I-am-a-real-href"
				}
			}
		},
		aux = {
			[ auxId ] = {
				[ "type" ] = "mask",
				fileSize = 1,
				digest = auxId ..  "-digest",
				sha256 = auxId .. "-sha256"
			}
		}
	}

	local uploadsRequired, auxToUpload = WFCatAssetAux.uploadsRequired( assetWithAuxNeedsUpload )

	rxTest:expectEqual( false, uploadsRequired )

	rxTest:expectEqual( {}, auxToUpload )

end

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

function WFCatAssetAuxTest.rxTestUploadsRequired_onlyConsiderDevelopMasks( rxTest )

	local auxIdNeedUploadMask = "fake-aux-mask"
	local auxIdNeedUploadFilter = "fake-aux-filter"
	local auxId = "more-fake-aux"
	local filterGroupDigest = auxIdNeedUploadFilter .. "-groupDigest"


	local auxNeedUploadMask = {
		["type"] = "mask",
		fileSize = 1,
		digest = auxIdNeedUploadMask ..  "-digest",
		sha256 = auxIdNeedUploadMask .. "-sha256"
	}

	local auxNeedUploadFilter = {
		["type"] = "filter",
		fileSize = 1,
		digest = auxIdNeedUploadFilter ..  "-digest",
		sha256 = auxIdNeedUploadFilter .. "-sha256"
	}

	local assetWithAuxNeedsUpload = {
		_id = "fake-asset",
		_serverProvidedMetadata = {
			links = {
				[ "/rels/aux/" .. auxId ] = {
					href = "I-am-a-real-href"
				}
			}
		},
		aux = {
			[ auxIdNeedUploadMask ] = auxNeedUploadMask,

			[ auxId ] = {
				[ "type" ] = "mask",
				fileSize = 1,
				digest = auxId ..  "-digest",
				sha256 = auxId .. "-sha256"
				}
			}
		}

	local uploadsRequired, auxToUpload = WFCatAssetAux.uploadsRequired( assetWithAuxNeedsUpload, true )

	rxTest:expectEqual( false, uploadsRequired )

	rxTest:expectEqual( {}, auxToUpload )

	----

	assetWithAuxNeedsUpload = {
		_id = "fake-asset",
		_serverProvidedMetadata = {
			links = {
				[ "/rels/aux/" .. auxId ] = {
					href = "I-am-a-real-href"
				}
			}
		},
		develop = {
			masks = {
				[ auxIdNeedUploadMask ] = {
					digest = auxNeedUploadMask.digest
				},
				[ auxId ] = {
					digest = auxId ..  "-digest"
				},
			},
			filters = {
				[ filterGroupDigest ] = {
					[ auxIdNeedUploadFilter ] = {
						digest = auxNeedUploadFilter.digest,
						sourceLongEdge = 6000,
						crsSizeX = 1024,
						crsSizeY = 768
					}
				}
			}
		},
		aux = {
			[ auxIdNeedUploadMask ] = auxNeedUploadMask,
			[ auxIdNeedUploadFilter ] = auxNeedUploadFilter,

			[ auxId ] = {
				[ "type" ] = "mask",
				fileSize = 1,
				digest = auxId ..  "-digest",
				sha256 = auxId .. "-sha256"
				}
			}
		}

	uploadsRequired, auxToUpload = WFCatAssetAux.uploadsRequired( assetWithAuxNeedsUpload, true )

	rxTest:expectEqual( true, uploadsRequired )

	rxTest:expectEqual( {
		[ auxIdNeedUploadMask ] = {
			digest = auxNeedUploadMask.digest
		},
		[ auxIdNeedUploadFilter ] = {
			digest = auxNeedUploadFilter.digest,
			groupDigest = filterGroupDigest,
			sourceLongEdge = 6000,
			crsSizeX = 1024,
			crsSizeY = 768
		}
	}, auxToUpload )

end

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

local function getCatalogDir( wfCatalog )

	local catalogId = wfCatalog:ozCatalogSpec():catalogId()

	local baseDir = WFCatAssetAux.getBasePath( wfCatalog )

	return baseDir, catalogId

end

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

function WFCatAssetAuxTest.rxTestGetAuxOverlay( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest )

	local assetId = KSIdGenerator.uuidInLowercaseHex()

	local optArgs = {
		hasDevelopAux = true
	}

	local assetDocValue, assetInfo = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId, optArgs )

	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDocValue ) )

	-- Empty auxInfo, so current develop settings contain no masks.
	-- Overlay should remove develop.masks.

	local auxInfo = {}

	local expectedOverlay = {
		_id = assetId,
		develop = {
			masks = {
				_delete = true
			},
			filters = {
				_delete = true
			}
		}
	}

	local catalogDir = getCatalogDir( wfCatalog )

	rxTest:expectSingleValue(
			WFCatAssetAux.getAuxOverlay( wfCatalog, assetId, auxInfo )
				:flatMap( function( overlay )

					overlay._id = assetId

					return wfCatalog:docStore():applyOverlay( overlay )
						:map( function()
							return overlay
						end )

				end ),
			expectedOverlay )

	local expectedDevelopMasks = nil
	local expectedDevelopFilters = nil

	rxTest:expectSingleValue(
			wfCatalog:docStore():docValue( assetId ):pluck( 'develop', 'masks' ),
			expectedDevelopMasks )

	rxTest:expectSingleValue(
			wfCatalog:docStore():docValue( assetId ):pluck( 'develop', 'filters' ),
			expectedDevelopFilters )

	-- Adding a new auxiliary object.

	local auxIdIndex = 0

	local function auxIDGenerator()
		auxIdIndex = auxIdIndex + 1
		return string.format( "auxId:%d", auxIdIndex )
	end

	local overrideArgs = { auxIDGenerator = auxIDGenerator }

	local auxDigest1 = KSIdGenerator.uuidInLowercaseHex()

	auxInfo = {
		{ digest = auxDigest1 }
	}

	-- Make sure the File Already Exists on Disk at the correct place

	local absoluteFilePath, relativePath = WFCatAssetAux.calculatePathUsingDigest( catalogDir, auxDigest1 )

	local parentDir = KSPathUtils.parent( absoluteFilePath )
	WFFileUtils.mkdir( parentDir )
	WFFileUtils.copy( assetInfo.mask1FilePath, absoluteFilePath )

	expectedOverlay = {
		_id = assetId,
		_localOnly = {
			files = {
				aux = {
					[ auxDigest1 ] = {
						relativePath = relativePath,
						error = {
							_delete = true,
						},
					}
				}
			}
		},
		aux = {
			[ "auxId:1" ] = {
				digest = auxDigest1,
				fileSize = assetInfo.mask1FileSize,
				sha256 = assetInfo.mask1Sha256,
				type = "mask",
			}
		},
		develop = {
			masks = {
				[ "auxId:1" ] = {
					digest = auxDigest1,
				},
				_replace = true,
			},
			filters = {
				_delete = true
			}
		}
	}

	rxTest:expectSingleValue(
			WFCatAssetAux.getAuxOverlay( wfCatalog, assetId, auxInfo, overrideArgs )
				:flatMap( function( overlay )

					overlay._id = assetId

					return wfCatalog:docStore():applyOverlay( overlay )
						:map( function()
							return overlay
						end )

				end ),
			expectedOverlay )

	expectedDevelopMasks = {
		[ "auxId:1" ] = {
			digest = auxDigest1
		}
	}

	rxTest:expectSingleValue(
			wfCatalog:docStore():docValue( assetId ):pluck( 'develop', 'masks' ),
			expectedDevelopMasks )

	rxTest:expectSingleValue(
			wfCatalog:docStore():docValue( assetId ):pluck( 'develop', 'filters' ),
			expectedDevelopFilters )

	-- Adding more auxiliary objects.

	local auxDigest2 = KSIdGenerator.uuidInLowercaseHex()
	local auxDigest3 = KSIdGenerator.uuidInLowercaseHex()
	local auxDigest4 = KSIdGenerator.uuidInLowercaseHex()
	local groupDigest = KSIdGenerator.uuidInLowercaseHex()

	auxInfo = {
		{ digest = auxDigest1 },
		{ digest = auxDigest2 },
		{
			digest = auxDigest3,
			groupDigest = groupDigest,
			sourceLongEdge = 6000,
			crsSizeX = 1024,
			crsSizeY = 768
		},
		{
			digest = auxDigest4,
			groupDigest = groupDigest,
			targetLongEdge = 2560,
			crsSizeX = 437,
			crsSizeY = 328
		}
	}

	-- Make sure the File Already Exists on Disk at the correct place

	local absoluteFilePath2, relativePath2 = WFCatAssetAux.calculatePathUsingDigest( catalogDir, auxDigest2 )

	local parentDir2 = KSPathUtils.parent( absoluteFilePath2 )
	WFFileUtils.mkdir( parentDir2 )
	WFFileUtils.copy( assetInfo.mask2FilePath, absoluteFilePath2 )

	local absoluteFilePath3, relativePath3 = WFCatAssetAux.calculatePathUsingDigest( catalogDir, auxDigest3 )

	local parentDir3 = KSPathUtils.parent( absoluteFilePath3 )
	WFFileUtils.mkdir( parentDir3 )
	WFFileUtils.copy( assetInfo.filter1FilePath, absoluteFilePath3 )

	local absoluteFilePath4, relativePath4 = WFCatAssetAux.calculatePathUsingDigest( catalogDir, auxDigest4 )

	local parentDir4 = KSPathUtils.parent( absoluteFilePath4 )
	WFFileUtils.mkdir( parentDir4 )
	WFFileUtils.copy( assetInfo.filter2FilePath, absoluteFilePath4 )

	expectedOverlay = {
		_id = assetId,
		_localOnly = {
			files = {
				aux = {
					[ auxDigest1 ] = {
						relativePath = relativePath,
						error = {
							_delete = true,
						},
					},
					[ auxDigest2 ] = {
						relativePath = relativePath2,
						error = {
							_delete = true,
						},
					},
					[ auxDigest3 ] = {
						relativePath = relativePath3,
						error = {
							_delete = true,
						},
					},
					[ auxDigest4 ] = {
						relativePath = relativePath4,
						error = {
							_delete = true,
						},
						groupDigest = {
							_delete = true,
						},
						crsSizeX = {
							_delete = true,
						},
						crsSizeY = {
							_delete = true,
						},
						fileSize = {
							_delete = true,
						},
						targetLongEdge = {
							_delete = true,
						},
					}
				}
			}
		},
		aux = {
			[ "auxId:1" ] = {
				digest = auxDigest1,
				fileSize = assetInfo.mask1FileSize,
				sha256 = assetInfo.mask1Sha256,
				type = "mask",
			},
			[ "auxId:2" ] = {
				digest = auxDigest2,
				fileSize = assetInfo.mask2FileSize,
				sha256 = assetInfo.mask2Sha256,
				type = "mask",
			},
			[ "auxId:3" ] = {
				digest = auxDigest3,
				fileSize = assetInfo.filter1FileSize,
				sha256 = assetInfo.filter1Sha256,
				type = "filter",
			},
			[ "auxId:4" ] = {
				digest = auxDigest4,
				fileSize = assetInfo.filter2FileSize,
				sha256 = assetInfo.filter2Sha256,
				type = "filter",
			}
		},
		develop = {
			masks = {
				[ "auxId:1" ] = {
					digest = auxDigest1,
				},
				[ "auxId:2" ] = {
					digest = auxDigest2,
				},
				_replace = true,
			},
			filters = {
				[ groupDigest ] = {
					[ "auxId:3" ] = {
						digest = auxDigest3,
						sourceLongEdge = 6000,
						crsSizeX = 1024,
						crsSizeY = 768
					},
					[ "auxId:4" ] = {
						digest = auxDigest4,
						targetLongEdge = 2560,
						crsSizeX = 437,
						crsSizeY = 328
					},
				},
				_replace = true,
			}
		}
	}

	rxTest:expectSingleValue(
			WFCatAssetAux.getAuxOverlay( wfCatalog, assetId, auxInfo, overrideArgs )
				:flatMap( function( overlay )

					overlay._id = assetId

					return wfCatalog:docStore():applyOverlay( overlay )
						:map( function()
							return overlay
						end )

				end ),
			expectedOverlay )

	expectedDevelopMasks = {
		[ "auxId:1" ] = {
			digest = auxDigest1
		},
		[ "auxId:2" ] = {
			digest = auxDigest2,
		}
	}

	rxTest:expectSingleValue(
			wfCatalog:docStore():docValue( assetId ):pluck( 'develop', 'masks' ),
			expectedDevelopMasks )

	expectedDevelopFilters = {
		[ groupDigest ] = {
			[ "auxId:3" ] = {
				digest = auxDigest3,
				sourceLongEdge = 6000,
				crsSizeX = 1024,
				crsSizeY = 768
			},
			[ "auxId:4" ] = {
				digest = auxDigest4,
				targetLongEdge = 2560,
				crsSizeX = 437,
				crsSizeY = 328
			}
		}
	}

	rxTest:expectSingleValue(
			wfCatalog:docStore():docValue( assetId ):pluck( 'develop', 'filters' ),
			expectedDevelopFilters )

	-- Remove the first mask and filters

	auxInfo = {
		{ digest = auxDigest2 }
	}

	expectedOverlay = {
		_id = assetId,
		_localOnly = {
			files = {
				aux = {
					[ auxDigest2 ] = {
						relativePath = relativePath2,
						error = {
							_delete = true,
						},
					}
				}
			}
		},
		aux = {
			[ "auxId:2" ] = {
				digest = auxDigest2,
				fileSize = assetInfo.mask2FileSize,
				sha256 = assetInfo.mask2Sha256,
				type = "mask",
			}
		},
		develop = {
			masks = {
				[ "auxId:2" ] = {
					digest = auxDigest2,
				},
				_replace = true,
			},
			filters = {
				_delete =true
			}
		}
	}

	rxTest:expectSingleValue(
			WFCatAssetAux.getAuxOverlay( wfCatalog, assetId, auxInfo, overrideArgs )
				:flatMap( function( overlay )

					overlay._id = assetId

					return wfCatalog:docStore():applyOverlay( overlay )
						:map( function()
							return overlay
						end )

				end ),
			expectedOverlay )

	expectedDevelopMasks = {
		[ "auxId:2" ] = {
			digest = auxDigest2,
		}
	}

	rxTest:expectSingleValue(
			wfCatalog:docStore():docValue( assetId ):pluck( 'develop', 'masks' ),
			expectedDevelopMasks )

	expectedDevelopFilters = nil

	rxTest:expectSingleValue(
			wfCatalog:docStore():docValue( assetId ):pluck( 'develop', 'filters' ),
			expectedDevelopFilters )

end

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

function WFCatAssetAuxTest.rxTestGetC2paOverlay( rxTest )

	rxTest:setTimeLimit( 10 )

	local optArgs = {
		hasC2pa = true
	}

	local fakeWFCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest )
	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local assetDocValue, assetInfo = WFCatAssetTestUtils.createAssetDocument( rxTest, fakeWFCatalog, assetId, optArgs )
	rxTest:waitFor( fakeWFCatalog:docStore():setDocValue( assetDocValue ) )

	local catalogDir = getCatalogDir( fakeWFCatalog )
	local auxDigest1 = KSIdGenerator.uuidInLowercaseHex()
	local absoluteFilePath = WFCatAssetAux.calculatePathUsingDigest( catalogDir, auxDigest1 )

	local parentDir = KSPathUtils.parent( absoluteFilePath )
	WFFileUtils.mkdir( parentDir )
	WFFileUtils.copy( assetInfo.c2paFilePath, absoluteFilePath )

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

	local c2paIngredients = {
			digest = auxDigest1,
			state = "Signed_Valid",
			version = 1,
	}

	rxTest:expectSingleValue(
			WFCatAssetAux.getC2paOverlay( fakeWFCatalog, c2paIngredients )
				:map( function( overlay )
						local actC2pa = {
							state = KSTableUtils.derefOpt( overlay, 'importSource', 'c2paIngredients', 'state' ),
							version = KSTableUtils.derefOpt( overlay, 'importSource', 'c2paIngredients', 'version' )
						}
						local auxId = KSTableUtils.derefOpt( overlay, 'importSource', 'c2paIngredients', 'aux_id' )
						local actAux = {
							digest = KSTableUtils.derefOpt( overlay, 'aux', auxId, 'digest' ),
							fileSize = KSTableUtils.derefOpt( overlay, 'aux', auxId, 'fileSize' ),
							sha256 = KSTableUtils.derefOpt( overlay, 'aux', auxId, 'sha256' ),
							type = KSTableUtils.derefOpt( overlay, 'aux', auxId, 'type' ),
						}
						return { actC2pa, actAux }
					end ),

					{
						{
							state = "Signed_Valid",
							version = 1,

						},
						{
							digest = auxDigest1,
							fileSize = assetInfo.c2paFileSize,
							sha256 = assetInfo.c2paSha256,
							type = 'c2pa',
						}
					}
					)


end

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

function WFCatAssetAuxTest.rxTestGetAuxOverlayForVersion( rxTest )

	rxTest:setTimeLimit( 10 )

	local optArgs = {
		hasDevelopAux = true,
		noLocalDevelopAux = true
	}

	local fakeWFCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest )

	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local assetDocValue, assetInfo = WFCatAssetTestUtils.createAssetDocument( rxTest, fakeWFCatalog, assetId, optArgs )

	rxTest:waitFor( fakeWFCatalog:docStore():setDocValue( assetDocValue ) )

	-- auxDataList when provides only one instance for filter group,
	-- WF would fetch all resolution from asset docValue for the filter group

	local versionId = KSIdGenerator.uuidInLowercaseHex()
	local auxDataList = {
		{
			digest = assetInfo.mask1Fingerprint
		},
		{
			digest = assetInfo.mask2Fingerprint
		},
		{
			digest = assetInfo.filter1Fingerprint,
			groupDigest = assetInfo.filterGroupFingerprint,
			sourceLongEdge = 6000,
			crsSizeX = 1024,
			crsSizeY = 768
		}
	}

	rxTest:expectSingleValue( WFCatAssetAux.getAuxOverlayForVersion( fakeWFCatalog, versionId, auxDataList, assetDocValue ),
		{
			develop = {
				filters = {
					[ assetInfo.filterGroupFingerprint ] = {
						[ assetInfo.filter1AuxId ] = {
							digest = assetInfo.filter1Fingerprint,
							sourceLongEdge = 6000,
							crsSizeX = 1024,
							crsSizeY = 768,
						},
						[ assetInfo.filter2AuxId ] = {
							digest = assetInfo.filter2Fingerprint,
							targetLongEdge = 2560,
							crsSizeX = 437,
							crsSizeY = 328,
						},
					},
					_replace = true,
				},
				masks = {
					[ assetInfo.mask1AuxId ] = {
						digest = assetInfo.mask1Fingerprint,
					},
					[ assetInfo.mask2AuxId ] = {
						digest = assetInfo.mask2Fingerprint,
					},
					_replace = true,
				}
			},
			_localOnly = {
				files = {
					aux = {
						[ assetInfo.mask1Fingerprint ] = {
							error = {
								_delete = true,
							},
						},
						[ assetInfo.mask2Fingerprint ] = {
							error = {
								_delete = true,
							},
						},
						[ assetInfo.filter1Fingerprint ] = {
							error = {
								_delete = true,
							},
						}
					}
				}
			}
		} )

end

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

function WFCatAssetAuxTest.rxTestGetAuxOverlayForLocalOnlyResampledAux( rxTest )

	rxTest:setTimeLimit( 10 )

	local optArgs = {
		hasSourceOnlyFilters = true,
		noLocalFilters = true,
		hasLocalResampledFilter  = true
	}

	local fakeWFCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest )

	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local assetDocValue, assetInfo = WFCatAssetTestUtils.createAssetDocument( rxTest, fakeWFCatalog, assetId, optArgs )

	-- Make sure resampled aux doesn't exist in aux section
	rxTest:expectEqual( nil, WFCatAssetAux.getAuxIdForDigest( assetDocValue, assetInfo.filter2Fingerprint ) )

	local auxDataList = {
		{
			digest = assetInfo.filter2Fingerprint,
			groupDigest = assetInfo.filterGroupFingerprint,
			targetLongEdge = 2560,
			crsSizeX = 437,
			crsSizeY = 328,
		}
	}

	rxTest:expectSingleValue( WFCatAssetAux.getAuxOverlayForLocalOnlyResampledAux( fakeWFCatalog, assetId, auxDataList, assetDocValue )
		:map( function( auxOverlay )
		
			local fixAux = {}
			local fixFilters = {}

			for _, auxItem in pairs( auxOverlay.aux ) do
				fixAux[ #fixAux + 1 ] = auxItem
			end

			for _, filterGroupItem in pairs( auxOverlay.develop.filters[ assetInfo.filterGroupFingerprint ] ) do
				fixFilters[ #fixFilters + 1 ] = filterGroupItem
			end

			return {
				aux = fixAux,
				develop = {
					filters = {
						[ assetInfo.filterGroupFingerprint ] = fixFilters
					}
				}
			}

		end ),
		{
			aux = {
				{
					type = 'filter',
					fileSize = assetInfo.filter2FileSize,
					sha256 = assetInfo.filter2Sha256,
					digest = assetInfo.filter2Fingerprint
				}
			},
			develop = {
				filters = {
					[ assetInfo.filterGroupFingerprint ] = {
						{
							digest = assetInfo.filter2Fingerprint,
							targetLongEdge = 2560,
							crsSizeX = 437,
							crsSizeY = 328,
						},
					}
				}
			}
		} )

end

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

function WFCatAssetAuxTest.rxTestGetAuxOverlay_NoAuxAtExpectedPath( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest )

	local assetId = KSIdGenerator.uuidInLowercaseHex()

	local optArgs = {
		hasMasks = true
	}

	local assetDocValue, _ = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId, optArgs )

	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDocValue ) )

	-- Try to add a new auxiliary object whose file doesn't exist on disk.

	local auxIdIndex = 0

	local function auxIDGenerator()
		auxIdIndex = auxIdIndex + 1
		return string.format( "auxId:%d", auxIdIndex )
	end

	local overrideArgs = { auxIDGenerator = auxIDGenerator }

	local auxDigest1 = KSIdGenerator.uuidInLowercaseHex()

	local auxInfo = {
		{ digest = auxDigest1 }
	}

	rxTest:expectOnlyError(
			WFCatAssetAux.getAuxOverlay( wfCatalog, assetId, auxInfo, overrideArgs ),
			"AuxNotFound",
			auxDigest1,
			":ignore:"
			)

end

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

function WFCatAssetAuxTest.rxTestGetAuxOverlay_NoAuxAtExpectedPathButAuxEntryExists( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest )

	local assetId = KSIdGenerator.uuidInLowercaseHex()

	local optArgs = {
		hasMasks = true
	}

	local assetDocValue, assetInfo = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId, optArgs )

	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDocValue ) )

	-- Create the auxInfo parameter

	local auxIdIndex = 0

	local function auxIDGenerator()
		auxIdIndex = auxIdIndex + 1
		return string.format( "auxId:%d", auxIdIndex )
	end

	local overrideArgs = { auxIDGenerator = auxIDGenerator }

	local auxDigest1 = KSIdGenerator.uuidInLowercaseHex()

	local auxInfo = {
		{ digest = auxDigest1 }
	}

	-- Make sure the aux file exists on disk at the correct place

	local catalogDir = getCatalogDir( wfCatalog )

	local absoluteFilePath, relativePath = WFCatAssetAux.calculatePathUsingDigest( catalogDir, auxDigest1 )

	local parentDir = KSPathUtils.parent( absoluteFilePath )
	WFFileUtils.mkdir( parentDir )
	WFFileUtils.copy( assetInfo.mask1FilePath, absoluteFilePath )

	-- Verify getAuxOverlay

	local expectedOverlay = {
		_id = assetId,
		_localOnly = {
			files = {
				aux = {
					[ auxDigest1 ] = {
						relativePath = relativePath,
						error = {
							_delete = true,
						},
					}
				}
			}
		},
		aux = {
			[ "auxId:1" ] = {
				digest = auxDigest1,
				fileSize = assetInfo.mask1FileSize,
				sha256 = assetInfo.mask1Sha256,
				type = "mask",
			}
		},
		develop = {
			masks = {
				[ "auxId:1" ] = {
					digest = auxDigest1,
				},
				_replace = true,
			},
			filters = {
				_delete = true
			}
		}
	}

	rxTest:expectSingleValue(
			WFCatAssetAux.getAuxOverlay( wfCatalog, assetId, auxInfo, overrideArgs )
				:flatMap( function( overlay )

					overlay._id = assetId

					return wfCatalog:docStore():applyOverlay( overlay )
						:map( function()
							return overlay
						end )

				end ),
			expectedOverlay )

	local expectedDevelopMasks = {
		[ "auxId:1" ] = {
			digest = auxDigest1
		}
	}

	rxTest:expectSingleValue(
			wfCatalog:docStore():docValue( assetId ):pluck( 'develop', 'masks' ),
			expectedDevelopMasks )

	-- Now delete the aux file from disk.

	WFFileUtils.deleteFile( absoluteFilePath )

	-- Construct a new auxInfo parameter that contains the a new auxiliary.

	local auxDigest2 = KSIdGenerator.uuidInLowercaseHex()

	auxInfo = {
		{ digest = auxDigest1 },
		{ digest = auxDigest2 }
	}

	-- Make sure this new auxiliary file is on disk.

	local absoluteFilePath2, relativePath2 = WFCatAssetAux.calculatePathUsingDigest( catalogDir, auxDigest2 )

	local parentDir2 = KSPathUtils.parent( absoluteFilePath2 )
	WFFileUtils.mkdir( parentDir2 )
	WFFileUtils.copy( assetInfo.mask2FilePath, absoluteFilePath2 )

	-- Verify getAuxOverlay

	expectedOverlay = {
		_id = assetId,
		_localOnly = {
			files = {
				aux = {
					[ auxDigest1 ] = {
						error = {
							_delete = true,
						},
					},
					[ auxDigest2 ] = {
						relativePath = relativePath2,
						error = {
							_delete = true,
						},
					}
				}
			}
		},
		aux = {
			[ "auxId:1" ] = {
				digest = auxDigest1,
				fileSize = assetInfo.mask1FileSize,
				sha256 = assetInfo.mask1Sha256,
				type = "mask",
			},
			[ "auxId:2" ] = {
				digest = auxDigest2,
				fileSize = assetInfo.mask2FileSize,
				sha256 = assetInfo.mask2Sha256,
				type = "mask",
			}
		},
		develop = {
			masks = {
				[ "auxId:1" ] = {
					digest = auxDigest1,
				},
				[ "auxId:2" ] = {
					digest = auxDigest2,
				},
				_replace = true,
			},
			filters = {
				_delete = true
			}
		}
	}

	rxTest:expectSingleValue(
			WFCatAssetAux.getAuxOverlay( wfCatalog, assetId, auxInfo, overrideArgs )
				:flatMap( function( overlay )

					overlay._id = assetId

					return wfCatalog:docStore():applyOverlay( overlay )
						:map( function()
							return overlay
						end )

				end ),
			expectedOverlay )

	expectedDevelopMasks = {
		[ "auxId:1" ] = {
			digest = auxDigest1
		},
		[ "auxId:2" ] = {
			digest = auxDigest2,
		}
	}

	rxTest:expectSingleValue(
			wfCatalog:docStore():docValue( assetId ):pluck( 'develop', 'masks' ),
			expectedDevelopMasks )

end

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

function WFCatAssetAuxTest.rxTestMergeAuxOverlay( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest )

	local assetId = KSIdGenerator.uuidInLowercaseHex()

	local optArgs = {
		hasMasks = true
	}

	local assetDocValue, assetInfo = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId, optArgs )

	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDocValue ) )

	-- Empty auxInfo, so current develop settings contain no masks.
	-- Overlay should remove develop.masks.

	local auxInfo = {}

	local expectedOverlay = {
		_id = assetId,
		develop = {
			masks = {
				_delete = true
			},
			filters = {
				_delete = true
			}
		}
	}

	local catalogDir = getCatalogDir( wfCatalog )

	rxTest:expectSingleValue(
			WFCatAssetAux.getAuxOverlay( wfCatalog, assetId, auxInfo )
				:flatMap( function( overlay )

					overlay._id = assetId

					return wfCatalog:docStore():applyOverlay( overlay )
						:map( function()
							return overlay
						end )

				end ),
			expectedOverlay )

	local expectedDevelopMasks = nil

	rxTest:expectSingleValue(
			wfCatalog:docStore():docValue( assetId ):pluck( 'develop', 'masks' ),
			expectedDevelopMasks )

	-- Generate an overlay that adds a mask.

	local auxIdIndex = 0

	local function auxIDGenerator()
		auxIdIndex = auxIdIndex + 1
		return string.format( "auxId:%d", auxIdIndex )
	end

	local overrideArgs = { auxIDGenerator = auxIDGenerator }

	local auxDigest1 = KSIdGenerator.uuidInLowercaseHex()

	auxInfo = {
		{ digest = auxDigest1 }
	}

	local absoluteFilePath, relativePath = WFCatAssetAux.calculatePathUsingDigest( catalogDir, auxDigest1 )

	local parentDir = KSPathUtils.parent( absoluteFilePath )
	WFFileUtils.mkdir( parentDir )
	WFFileUtils.copy( assetInfo.mask1FilePath, absoluteFilePath )

	expectedOverlay = {
		_localOnly = {
			files = {
				aux = {
					[ auxDigest1 ] = {
						relativePath = relativePath,
						error = {
							_delete = true,
						},
					}
				}
			}
		},
		aux = {
			[ "auxId:1" ] = {
				digest = auxDigest1,
				fileSize = assetInfo.mask1FileSize,
				sha256 = assetInfo.mask1Sha256,
				type = "mask",
			}
		},
		develop = {
			masks = {
				[ "auxId:1" ] = {
					digest = auxDigest1,
				},
				_replace = true,
			},
			filters = {
				_delete = true
			}
		}
	}

	local auxOverlay = rxTest:getFirstValue(
			WFCatAssetAux.getAuxOverlay( wfCatalog, assetId, auxInfo, overrideArgs ) )

	rxTest:expectEqual( expectedOverlay, auxOverlay )

	local overlay = {
		_localOnly = {
			files = {
				aux = {
					[ 'foo' ] = {
						bar = 'baz'
					}
				}
			}
		},
		aux = {
			[ "foo" ] = {
				digest = 'foo',
				fileSize = 'foo',
				sha256 = 'foo',
				type = "mask",
			}
		},
		develop = {
			masks = {
				[ "foo" ] = {
					digest = 'baz',
				},
				_replace = true,
			}
		}
	}

	local expectedMergedOverlay = {
		_localOnly = {
			files = {
				aux = {
					[ auxDigest1 ] = {
						relativePath = relativePath,
						error = {
							_delete = true,
						},
					},
					foo = {
						bar = "baz",
					},
				},
			},
		},
		aux = { -- This section should be replace.
			[ "auxId:1" ] = {
				digest = auxDigest1,
				fileSize = assetInfo.mask1FileSize,
				sha256 = assetInfo.mask1Sha256,
				type = "mask",
			}
		},
		develop = { -- This section should be replaced
			masks = {
				_replace = true, -- This key should remain intact.
				[ "auxId:1" ] = {
					digest = auxDigest1,
				}
			},
			filters = {
				_delete =true
			}
		}
	}

	local mergedOverlay = WFCatAssetAux.mergeAuxOverlay( overlay, auxOverlay )

	rxTest:expectEqual(
			expectedMergedOverlay,
			mergedOverlay )

	-- OLD MERGE LOGIC THAT CAUSED A BUG:

	local badMergedOverlay = WFTableMerge.applyOverlay( overlay, auxOverlay )

	rxTest:expectNotEqual( badMergedOverlay, mergedOverlay )

end

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

function WFCatAssetAuxTest.rxTestGetAuxInfoBySha256( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local optArguments = {
		hasMasks = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local auxInfo = WFCatAssetAux.getAuxInfoBySha256( docValue, expectedData.mask1Sha256 )

	local expectedAuxInfo = {
		digest = expectedData.mask1Fingerprint,
		fileSize = expectedData.mask1FileSize,
		sha256 = expectedData.mask1Sha256,
		type = "mask",
	}

	rxTest:expectEqual( expectedAuxInfo, auxInfo )

	auxInfo = WFCatAssetAux.getAuxInfoBySha256( docValue, expectedData.mask2Sha256 )

	expectedAuxInfo = {
		digest = expectedData.mask2Fingerprint,
		fileSize = expectedData.mask2FileSize,
		sha256 = expectedData.mask2Sha256,
		type = "mask",
	}

	rxTest:expectEqual( expectedAuxInfo, auxInfo )

end

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

function WFCatAssetAuxTest.rxTestGetFileSize( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local optArguments = {
		hasMasks = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local fileSize = WFCatAssetAux.getFileSize( docValue, expectedData.mask1Fingerprint )

	rxTest:expectEqual( expectedData.mask1FileSize, fileSize )

	fileSize = WFCatAssetAux.getFileSize( docValue, expectedData.mask2Fingerprint )

	rxTest:expectEqual( expectedData.mask2FileSize, fileSize )

end

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

function WFCatAssetAuxTest.rxTestHasSomeLocalAuxFiles( rxTest )

	rxTest:setTimeLimit( 5 )

	local wfCatalog = createFakeWFCatalog( rxTest )
	local docValue, localOnlySettings = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, { hasMasks = true } )

	rxTest:expectEqual( true, WFCatAssetAux.hasSomeLocalAuxFiles( docValue ) )

	KSTableUtils.setNested( docValue, nil, '_localOnly', 'files', 'aux', localOnlySettings.mask1Fingerprint )

	rxTest:expectEqual( true, WFCatAssetAux.hasSomeLocalAuxFiles( docValue ) )

	KSTableUtils.setNested( docValue, nil, '_localOnly', 'files', 'aux', localOnlySettings.mask2Fingerprint )

	rxTest:expectEqual( false, WFCatAssetAux.hasSomeLocalAuxFiles( docValue ) )

	KSTableUtils.setNested( docValue, nil, '_localOnly', 'files', 'aux' )

	rxTest:expectEqual( false, WFCatAssetAux.hasSomeLocalAuxFiles( docValue ) )

end

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

function WFCatAssetAuxTest.rxTestGetSourceAssetLinkForAuxFromDocValue( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Create doc

	local optArguments = {
		hasMasks = true,
		sharedMasks = true
	}

	local docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	-- Create another doc that does not have any mask links on Oz:

	optArguments = {
		hasMasks = true,
		sharedMasks = true,
		noMaskLinks = true
	}

	local docValue2 = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	-- Copy the mask links from the first document

	docValue2._localOnly.sourceAssetLinkForAux = require 'WFCatFileSpec'.getSelfLinkFromDocValue( docValue )

	local link = WFCatAssetAux.getSourceAssetLinkForAuxFromDocValue( docValue2 )

	rxTest:expectEqual( "/href/bogus/self", link )

end

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

function WFCatAssetAuxTest.rxTestGetListOfAuxThatAreNotLocal( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Local Masks and Filters

	local optArguments = {
		hasAllAuxType = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local auxThatAreNotLocal = WFCatAssetAux.getListOfAuxThatAreNotLocal( docValue )

	rxTest:expectEqual( {}, auxThatAreNotLocal )

	-- No Local Masks

	optArguments = {
		hasAllAuxType = true,
		noLocalMasks = true
	}

	docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	auxThatAreNotLocal = WFCatAssetAux.getListOfAuxThatAreNotLocal( docValue )

	local function sortResult( tbl )

		table.sort( tbl, function( a, b )

			return a.auxId > b.auxId

		end )

		return tbl

	end

	local expected = {
		{
			auxId = expectedData.mask1AuxId,
			digest = expectedData.mask1Fingerprint
		},
		{
			auxId = expectedData.mask2AuxId,
			digest = expectedData.mask2Fingerprint
		}
	}

	rxTest:expectEqual( sortResult( expected ), sortResult( auxThatAreNotLocal ) )

	-- No Local Filters. Only original instance filter would be included

	optArguments = {
		hasAllAuxType = true,
		noLocalFilters = true
	}

	docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	auxThatAreNotLocal = WFCatAssetAux.getListOfAuxThatAreNotLocal( docValue )

	expected = {
		{
			auxId = expectedData.filter1AuxId,
			digest = expectedData.filter1Fingerprint,
			groupDigest = expectedData.filterGroupFingerprint,
			sourceLongEdge = 6000,
			crsSizeX = 1024,
			crsSizeY = 768
		}
	}

	rxTest:expectEqual( sortResult( expected ), sortResult( auxThatAreNotLocal ) )

	-- No Local c2pa

	optArguments = {
		hasAllAuxType = true,
		noLocalC2pa = true
	}

	docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	auxThatAreNotLocal = WFCatAssetAux.getListOfAuxThatAreNotLocal( docValue )

	expected = {
		{
			auxId = expectedData.c2paAuxId,
			digest = expectedData.c2paFingerprint,
			type = "c2pa",
			fileSize = expectedData.c2paFileSize,
			sha256 = expectedData.c2paSha256,
		},
	}

	rxTest:expectEqual( sortResult( expected ), sortResult( auxThatAreNotLocal ) )

end

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

function WFCatAssetAuxTest.rxTestFilterAuxThatAreNotUploaded( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Masks do have Oz links

	local optArguments = {
		hasMasks = true,
		noMaskLinks = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	rxTest:waitFor( wfCatalog:docStore():setDocValue( docValue ) )

	local digests = {
		{ digest = expectedData.mask1Fingerprint },
		{ digest = expectedData.mask2Fingerprint }
	}

	local notUploadedAuxList = rxTest:getFirstValue(
				WFCatAssetAux.filterAuxThatAreNotUploaded( wfCatalog, docValue._id, digests ) )

	local expected = {
		{
			digest = expectedData.mask1Fingerprint,
			filePath = expectedData.mask1FilePath
		},
		{
			digest = expectedData.mask2Fingerprint,
			filePath = expectedData.mask2FilePath
		}
	}

	local function sortResult( tbl )

		table.sort( tbl, function( a, b )

			return a.digest > b.digest

		end )

		return tbl

	end

	rxTest:expectEqual( sortResult( expected ), sortResult( notUploadedAuxList ) )

	-- Masks has Oz links

	optArguments = {
		hasMasks = true,
	}

	docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	rxTest:waitFor( wfCatalog:docStore():setDocValue( docValue ) )

	digests = {
		{ digest = expectedData.mask1Fingerprint },
		{ digest = expectedData.mask2Fingerprint }
	}

	notUploadedAuxList = rxTest:getFirstValue(
				WFCatAssetAux.filterAuxThatAreNotUploaded( wfCatalog, docValue._id, digests ) )

	rxTest:expectEqual( {}, notUploadedAuxList )

end

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

function WFCatAssetAuxTest.rxTestHasLocalAuxError( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local optArguments = {
		hasMasks = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	KSTableUtils.setNested( docValue, true, '_localOnly', 'files', 'aux', expectedData.mask1Fingerprint, 'error' )

	local hasError = WFCatAssetAux.hasLocalAuxError( docValue, expectedData.mask1AuxId )

	rxTest:expectEqual( true, hasError )

	hasError = WFCatAssetAux.hasLocalAuxError( docValue, expectedData.mask2AuxId )

	rxTest:expectEqual( false, hasError )

end

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

function WFCatAssetAuxTest.rxTestHasLocalAuxErrorForCurrentDevelopSettings( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local optArguments = {
		hasDevelopAux = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local hasError = WFCatAssetAux.hasLocalAuxErrorForCurrentDevelopSettings( docValue )

	rxTest:expectEqual( false, hasError )

	KSTableUtils.setNested( docValue, true, '_localOnly', 'files', 'aux', expectedData.mask1Fingerprint, 'error' )

	hasError = WFCatAssetAux.hasLocalAuxErrorForCurrentDevelopSettings( docValue )

	rxTest:expectEqual( true, hasError )

	KSTableUtils.setNested( docValue, nil, '_localOnly', 'files', 'aux', expectedData.mask1Fingerprint, 'error' )
	KSTableUtils.setNested( docValue, true, '_localOnly', 'files', 'aux', expectedData.filter1Fingerprint, 'error' )

	hasError = WFCatAssetAux.hasLocalAuxErrorForCurrentDevelopSettings( docValue )

	rxTest:expectEqual( true, hasError )

end

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

function WFCatAssetAuxTest.rxTestHasAuxValues( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Mask
	for _, hasMasksValue in pairs( { true, false } ) do
		for _, noMaskLinksValue in pairs( { true, false } ) do
			for _, noLocalMasksValue in pairs( { true, false } ) do

				local optArguments = {}

				if hasMasksValue then

					optArguments.hasMasks = true

				end

				if noMaskLinksValue then

					optArguments.noMaskLinks = true

				end

				if noLocalMasksValue then

					optArguments.noLocalMasks = true

				end

				local docValue = WFCatAssetTestUtils.createAssetDocument(
									rxTest, wfCatalog, nil, optArguments )

				rxTest:logf( "Checking hasMasks( ... ) hasMasks = %s  noMaskLinks = %s and noLocalMasks = %s",
								hasMasksValue, noMaskLinksValue,
								noLocalMasksValue )

				local docValueHasMasks = WFCatAssetAux.hasAuxValues( docValue )

				rxTest:expectEqual( hasMasksValue, docValueHasMasks )

				rxTest:logf( "Checking getAuxInfoList( ... ) hasMasks = %s  noMaskLinks = %s and noLocalMasks = %s",
								hasMasksValue, noMaskLinksValue,
								noLocalMasksValue )

				local auxInfoList = WFCatAssetAux.getAuxInfoList( docValue )

				rxTest:expectEqual( hasMasksValue, next( auxInfoList ) ~= nil )

				rxTest:logf( "Checking getAuxInfoList( ... ) has digests for all masks hasMasks = %s  noMaskLinks = %s and noLocalMasks = %s",
								hasMasksValue, noMaskLinksValue,
								noLocalMasksValue )

				for _, auxInfo in pairs( auxInfoList ) do

					rxTest:expectEqual( true, type( auxInfo.digest ) == "string" and
												string.len( auxInfo.digest ) > 0 )

				end

			end

		end

	end

	-- Filter
	for _, hasFiltersValue in pairs( { true, false } ) do
		for _, noFilterLinksValue in pairs( { true, false } ) do
			for _, noLocalFiltersValue in pairs( { true, false } ) do

				local optArguments = {}

				if hasFiltersValue then

					optArguments.hasFilters = true

				end

				if noFilterLinksValue then

					optArguments.noFilterLinks = true

				end

				if noLocalFiltersValue then

					optArguments.noLocalFilters = true

				end

				local docValue = WFCatAssetTestUtils.createAssetDocument(
									rxTest, wfCatalog, nil, optArguments )

				rxTest:logf( "Checking hasMasks( ... ) hasMasks = %s  noMaskLinks = %s and noLocalMasks = %s",
								hasFiltersValue, noFilterLinksValue,
								noLocalFiltersValue )

				local docValueHasMasks = WFCatAssetAux.hasAuxValues( docValue )

				rxTest:expectEqual( hasFiltersValue, docValueHasMasks )

				rxTest:logf( "Checking getAuxInfoList( ... ) hasMasks = %s  noMaskLinks = %s and noLocalMasks = %s",
								hasFiltersValue, noFilterLinksValue,
								noLocalFiltersValue )

				local auxInfoList = WFCatAssetAux.getAuxInfoList( docValue )

				rxTest:expectEqual( hasFiltersValue, next( auxInfoList ) ~= nil )

				rxTest:logf( "Checking getAuxInfoList( ... ) has digests for all masks hasMasks = %s  noMaskLinks = %s and noLocalMasks = %s",
								hasFiltersValue, noFilterLinksValue,
								noLocalFiltersValue )

				for _, auxInfo in pairs( auxInfoList ) do

					rxTest:expectEqual( true, type( auxInfo.digest ) == "string"
												and string.len( auxInfo.digest ) > 0 )

				end

			end

		end

	end

end

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

function WFCatAssetAuxTest.rxTestHasUnsyncedAuxObjectsLocally( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Masks on Oz & Local

	local optArguments = {
		hasMasks = true
	}

	local docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local hasUnsyncedAuxObjectsLocally = WFCatAssetAux.hasUnsyncedAuxObjectsLocally( docValue )

	rxTest:expectEqual( false, hasUnsyncedAuxObjectsLocally )

	-- Masks not on Oz

	optArguments = {
		hasMasks = true,
		noMaskLinks = true
	}

	docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	hasUnsyncedAuxObjectsLocally = WFCatAssetAux.hasUnsyncedAuxObjectsLocally( docValue )

	rxTest:expectEqual( true, hasUnsyncedAuxObjectsLocally )

	-- Masks not on Oz, Not Referenced by Develop Settings

	optArguments = {
		hasMasks = true,
		noMaskLinks = true
	}

	docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )
	docValue.develop.masks = nil

	hasUnsyncedAuxObjectsLocally = WFCatAssetAux.hasUnsyncedAuxObjectsLocally( docValue )

	rxTest:expectEqual( true, hasUnsyncedAuxObjectsLocally )

end

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

function WFCatAssetAuxTest.rxTestHasUnsyncedDevelopAuxLocally( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Masks on Oz & Local

	local optArguments = {
		hasDevelopAux = true
	}

	local docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local hasUnsyncedAuxObjectsLocally = WFCatAssetAux.hasUnsyncedDevelopAuxLocally( docValue )

	rxTest:expectEqual( false, hasUnsyncedAuxObjectsLocally )

	-- Masks not on Oz

	optArguments = {
		hasMasks = true,
		noMaskLinks = true
	}

	docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	hasUnsyncedAuxObjectsLocally = WFCatAssetAux.hasUnsyncedDevelopAuxLocally( docValue )

	rxTest:expectEqual( true, hasUnsyncedAuxObjectsLocally )

	-- Filters not on Oz

	optArguments = {
		hasFilters = true,
		noFilterLinks = true
	}

	docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	hasUnsyncedAuxObjectsLocally = WFCatAssetAux.hasUnsyncedDevelopAuxLocally( docValue )

	rxTest:expectEqual( true, hasUnsyncedAuxObjectsLocally )

	-- Masks not on Oz, Not Referenced by Develop Settings

	optArguments = {
		hasDevelopAux = true,
		noDevelopAuxLinks = true
	}

	docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )
	docValue.develop.masks = nil
	docValue.develop.filters = nil

	hasUnsyncedAuxObjectsLocally = WFCatAssetAux.hasUnsyncedDevelopAuxLocally( docValue )

	rxTest:expectEqual( false, hasUnsyncedAuxObjectsLocally )

end

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

function WFCatAssetAuxTest.rxTestNeedsAuxDownloadForDevelop( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Masks on Oz & Local

	local optArguments = {
		hasDevelopAux = true
	}

	local docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local needsAuxDownload = WFCatAssetAux.needsAuxDownloadForDevelop( docValue )

	rxTest:expectEqual( false, needsAuxDownload )

	-- Masks and filters not on Oz

	optArguments = {
		hasDevelopAux = true,
		noDevelopAuxLinks = true
	}

	docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	needsAuxDownload = WFCatAssetAux.needsAuxDownloadForDevelop( docValue )

	rxTest:expectEqual( false, needsAuxDownload )

	-- Masks on Oz, Not Local

	optArguments = {
		hasMasks = true,
		noLocalMasks = true
	}

	docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local docValueOrig = KSTableUtils.deepCopy( docValue )

	rxTest:expectEqual( docValueOrig, docValue )

	needsAuxDownload = WFCatAssetAux.needsAuxDownloadForDevelop( docValue )

	rxTest:expectEqual( true, needsAuxDownload )

	rxTest:log( "Check that needsAuxDownloadForDevelop didn't modify docValue" )

	rxTest:expectEqual( docValueOrig, docValue )

	-- Filters on Oz, Not Local

	optArguments = {
		hasFilters = true,
		noLocalFilters = true
	}

	docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	docValueOrig = KSTableUtils.deepCopy( docValue )

	rxTest:expectEqual( docValueOrig, docValue )

	needsAuxDownload = WFCatAssetAux.needsAuxDownloadForDevelop( docValue )

	rxTest:expectEqual( true, needsAuxDownload )

	rxTest:log( "Check that needsAuxDownloadForDevelop didn't modify docValue" )

	rxTest:expectEqual( docValueOrig, docValue )

	-- Masks and filters not on Oz, Not Local

	optArguments = {
		hasDevelopAux = true,
		noDevelopAuxLinks = true,
		noLocalDevelopAux = true
	}

	docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	needsAuxDownload = WFCatAssetAux.needsAuxDownloadForDevelop( docValue )

	rxTest:expectEqual( false, needsAuxDownload )

end

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

function WFCatAssetAuxTest.rxTestNeedsAuxDownloadForDevelopAndC2pa( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Masks on Oz & Local

	local optArguments = {
		hasMasks = true
	}

	local docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local needsAuxDownload = WFCatAssetAux.needsAuxDownloadForDevelop( docValue )
	local needsC2paDownload = WFCatAssetAux.needsAuxDownloadForC2pa( docValue )
	local needsAuxOrC2paDownload = WFCatAssetAux._TEST_needsAuxDownloadForDevelopAndC2pa( docValue )

	rxTest:expectEqual( false, needsAuxDownload )
	rxTest:expectEqual( false, needsC2paDownload )
	rxTest:expectEqual( false, needsAuxOrC2paDownload )

	-- Masks not on Oz

	optArguments = {
		hasMasks = true,
		noMaskLinks = true
	}

	docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	needsAuxDownload = WFCatAssetAux.needsAuxDownloadForDevelop( docValue )
	needsC2paDownload = WFCatAssetAux.needsAuxDownloadForC2pa( docValue )
	needsAuxOrC2paDownload = WFCatAssetAux._TEST_needsAuxDownloadForDevelopAndC2pa( docValue )

	rxTest:expectEqual( false, needsAuxDownload )
	rxTest:expectEqual( false, needsC2paDownload )
	rxTest:expectEqual( false, needsAuxOrC2paDownload )

	-- Masks on Oz, Not Local

	optArguments = {
		hasMasks = true,
		noLocalMasks = true
	}

	docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local docValueOrig = KSTableUtils.deepCopy( docValue )

	rxTest:expectEqual( docValueOrig, docValue )

	needsAuxDownload = WFCatAssetAux.needsAuxDownloadForDevelop( docValue )
	needsC2paDownload = WFCatAssetAux.needsAuxDownloadForC2pa( docValue )
	needsAuxOrC2paDownload = WFCatAssetAux._TEST_needsAuxDownloadForDevelopAndC2pa( docValue )

	rxTest:expectEqual( true, needsAuxDownload )
	rxTest:expectEqual( false, needsC2paDownload )
	rxTest:expectEqual( true, needsAuxOrC2paDownload )

	optArguments = {
		hasMasks = true,
		hasC2pa = true,
		noLocalC2pa = true,
	}

	docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	docValueOrig = KSTableUtils.deepCopy( docValue )

	rxTest:expectEqual( docValueOrig, docValue )

	needsAuxDownload = WFCatAssetAux.needsAuxDownloadForDevelop( docValue )
	needsC2paDownload = WFCatAssetAux.needsAuxDownloadForC2pa( docValue )
	needsAuxOrC2paDownload = WFCatAssetAux._TEST_needsAuxDownloadForDevelopAndC2pa( docValue )

	rxTest:expectEqual( false, needsAuxDownload )
	rxTest:expectEqual( true, needsC2paDownload )
	rxTest:expectEqual( true, needsAuxOrC2paDownload )
end

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

function WFCatAssetAuxTest.rxTestNeedsAuxDownloadForDevelop_externalAuxModule_testSerially( rxTest )

	WFCapabilities.setCapability( 'WFCatAssetAux.externalAuxModule', 'WFCatAssetAuxTest_externalAuxModule' )

	rxTest:finally( function()
		WFCapabilities.setCapability( 'WFCatAssetAux.externalAuxModule', nil )
	end )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Masks on Oz, Not localOnly, but externalAuxModule check has them

	local optArguments = {
		hasMasks = true,
		noLocalMasks = true
	}

	local docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local performFileExistenceCheck = true
	local needsAuxDownload = WFCatAssetAux._TEST_needsAuxDownloadForDevelop( docValue, performFileExistenceCheck )

	rxTest:expectEqual( false, needsAuxDownload )

end

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

function WFCatAssetAuxTest.rxTestDownloadAuxAndSaveToDisk_Exists( rxTest )

	local catalogSpec = WFCatSpecMock(
			KSIdGenerator.uuidInLowercaseHex(),
			KSIdGenerator.uuidInLowercaseHex(),
			rxTest, {} )

	local wfCatalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	-- Attempt to Download Aux Files

	local optArguments = {
		hasDevelopAux = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	-- Now attempt to download a mask. There should be no download, since it already
	-- exists.

	local maskDigest = expectedData.mask1Fingerprint

	local observable = WFCatAssetAux._TEST_downloadAuxAndSaveToDisk( wfCatalog, docValue, maskDigest )

	-- Verify the response

	rxTest:expectSingleValue( observable, maskDigest, expectedData.mask1RelativeFilePath )

	-- Make sure the file exists

	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.mask1FilePath ) )

	-- Attempt to download a filter. There should be no download, since it already
	-- exists.

	local filterDigest = expectedData.filter1Fingerprint

	observable = WFCatAssetAux._TEST_downloadAuxAndSaveToDisk( wfCatalog, docValue, filterDigest )

	-- Verify the response

	rxTest:expectSingleValue( observable, filterDigest, expectedData.filter1RelativeFilePath )

	-- Make sure the file exists

	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.filter1FilePath ) )

end

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

local function testDownloadAuxAndSaveToDisk( rxTest, optAssetArgs )

	local requestOptions = {
		requestHeaders = {
			{
				header = "Content-Type",
				value = "application/octet-stream",
			},
		},
	}

	local auxContent = KSIdGenerator.uuidInLowercaseHex()

	local responses = {
		{
			url = "/href/bogus/aux1",
			response = KSData.make( auxContent ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		}
	}

	local catalogSpec = WFCatSpecMock(
			KSIdGenerator.uuidInLowercaseHex(),
			KSIdGenerator.uuidInLowercaseHex(),
			rxTest, responses )

	local wfCatalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	-- Attempt to Download Aux Files

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optAssetArgs )

	-- Check the names are correct

	local digest

	if optAssetArgs.hasMasks then

		digest = expectedData.mask1Fingerprint

	elseif optAssetArgs.hasFilters then

		digest = expectedData.filter1Fingerprint

	elseif optAssetArgs.hasC2pa then

		digest = expectedData.c2paFingerprint

	end

	local observable = WFCatAssetAux._TEST_downloadAuxAndSaveToDisk( wfCatalog, docValue, digest, nil )

	-- Verify the response

	local catalogDir = getCatalogDir( wfCatalog )

	local absoluteFilePath, relativeFilePath = WFCatAssetAux.calculatePathUsingDigest( catalogDir, digest )

	rxTest:expectSingleValue( observable, digest, relativeFilePath )

	-- Make sure the file was downloaded to disk

	rxTest:expectEqual( 'file', WFFileUtils.exists( absoluteFilePath ) )

	-- Verify file contents

	local contents = KSData.memoryMappedContentsOfFile( absoluteFilePath )

	local contentsStr = tostring( contents )

	rxTest:expectEqual( auxContent, contentsStr )

	contents:close()

end

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

function WFCatAssetAuxTest.rxTestDownloadAuxAndSaveToDisk_mask( rxTest )

	local optAssetArgs = {
		hasMasks = true,
		noLocalMasks = true
	}

	testDownloadAuxAndSaveToDisk( rxTest, optAssetArgs )

end

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

function WFCatAssetAuxTest.rxTestDownloadAuxAndSaveToDisk_filter( rxTest )

	local optAssetArgs = {
		hasFilters = true,
		noLocalFilters = true
	}

	testDownloadAuxAndSaveToDisk( rxTest, optAssetArgs )

end

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

function WFCatAssetAuxTest.rxTestDownloadAuxAndSaveToDisk_c2pa( rxTest )

	local optAssetArgs = {
		hasC2pa = true,
		noLocalC2pa = true
	}

	testDownloadAuxAndSaveToDisk( rxTest, optAssetArgs )

end

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

local ensureAuxFilesExistLocally_success = {
	success = true
}

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

function WFCatAssetAuxTest.rxTestEnsureAuxFilesExistLocally( rxTest )

	rxTest:setTimeLimit( 10 )

	local requestOptions = {
		requestHeaders = {
			{
				header = "Content-Type",
				value = "application/octet-stream",
			},
		},
	}

	local responses = {
		{
			url = "/href/bogus/aux1",
			response = KSData.make( 'foo' ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		},
		{
			url = "/href/bogus/aux1",
			response = KSData.make( 'bar' ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		},
		{
			url = "/href/bogus/aux1",
			response = KSData.make( 'baz' ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		},
		{
			url = "/href/bogus/aux1",
			response = KSData.make( 'qux' ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		}
	}

	local catalogSpec = WFCatSpecMock(
			KSIdGenerator.uuidInLowercaseHex(),
			KSIdGenerator.uuidInLowercaseHex(),
			rxTest, responses )

	local wfCatalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	-- Attempt to Download Aux Files

	local optArguments = {
		hasAllAuxType = true,
		noLocalAllAuxType = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local observable = WFCatAssetAux.ensureAuxFilesExistLocally( wfCatalog, docValue )
			:map( function( event )

				if event.success == true and #event.selectedDigests == 1 then
					return ensureAuxFilesExistLocally_success
				end

			end )

	rxTest:expectSingleValue( observable, ensureAuxFilesExistLocally_success )

	-- Verify the Document Has Local Only Aux Annotations

	local function sortAux( auxiliaries )
		local sorted = {}

		for _, aux in pairs( auxiliaries ) do
			sorted[ #sorted + 1 ] = aux
		end

		table.sort( sorted, function( a, b )
				return a.relativePath < b.relativePath
			end )

		return sorted

	end

	local _, mask1RelativePath = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData.mask1Fingerprint )
	local _, mask2RelativePath = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData.mask2Fingerprint )
	local _, filter1RelativePath = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData.filter1Fingerprint )
	local _, c2paRelativePath = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData.c2paFingerprint )

	local expected = {
		[ expectedData.mask1Fingerprint ] = {
			relativePath = mask1RelativePath
		},
		[ expectedData.mask2Fingerprint ] = {
			relativePath = mask2RelativePath
		},
		[ expectedData.filter1Fingerprint ] = {
			relativePath = filter1RelativePath
		},
		[ expectedData.c2paFingerprint ] = {
			relativePath = c2paRelativePath
		}
	}

	rxTest:expectSingleValue( wfCatalog:docStore():docValue( docValue._id )
		:pluck( '_localOnly', 'files', 'aux' )
		:map( sortAux ), sortAux( expected ) )

	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.mask1FilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.mask2FilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.filter1FilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.c2paFilePath ) )

end

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

function WFCatAssetAuxTest.rxTestEnsureAuxFilesExistLocally_DownloadError( rxTest )

	rxTest:setTimeLimit( 5 )

	local requestOptions = {
		requestHeaders = {
			{
				header = "Content-Type",
				value = "application/octet-stream",
			},
		},
	}

	local responses = {
		{
			url = "/href/bogus/aux1",
			fail = {
				'AHHH',
				{
					status_code = '404',
				},
			},
			options = requestOptions
		},
		{
			url = "/href/bogus/aux1",
			fail = {
				'AHHH',
				{
					status_code = '404',
				},
			},
			options = requestOptions
		},
		{
			url = "/href/bogus/aux1",
			fail = {
				'AHHH',
				{
					status_code = '404',
				},
			},
			options = requestOptions
		},
		{
			url = "/href/bogus/aux1",
			fail = {
				'AHHH',
				{
					status_code = '404',
				},
			},
			options = requestOptions
		}
	}

	local catalogSpec = WFCatSpecMock(
			KSIdGenerator.uuidInLowercaseHex(),
			KSIdGenerator.uuidInLowercaseHex(),
			rxTest, responses )

	local wfCatalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	-- Attempt to Download Aux Files

	local optArguments = {
		hasAllAuxType = true,
		noLocalAllAuxType = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local observable = WFCatAssetAux.ensureAuxFilesExistLocally( wfCatalog, docValue )
		:map( function( event )

			-- There should be 1 selected digest returned
			-- Return value for selectedDigests is checked in resampled tests
			if #event.selectedDigests == 1 then

				event.selectedDigests = nil
				return event

			end

		end )

	rxTest:expectSingleValue( observable, {
			success = false,
			failedToDownloadDigests = {
				[ expectedData.mask1Fingerprint ] = true,
				[ expectedData.mask2Fingerprint ] = true,
				[ expectedData.filter1Fingerprint ] = true,
				[ expectedData.c2paFingerprint ] = true,
			}
		} )

end

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

function WFCatAssetAuxTest.rxTestEnsureAuxFilesExistLocally_NothingToDownload( rxTest )

	rxTest:setTimeLimit( 5 )

	local catalogSpec = WFCatSpecMock(
			KSIdGenerator.uuidInLowercaseHex(),
			KSIdGenerator.uuidInLowercaseHex(),
			rxTest, {} )

	local wfCatalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	-- Attempt to Download Aux Files

	local optArguments = {
		hasAllAuxType = true
	}

	local docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local observable = WFCatAssetAux.ensureAuxFilesExistLocally( wfCatalog, docValue )
		:map( function( event )

			-- There should be 1 selected digest returned
			-- Return value for selectedDigests is checked in resampled tests
			if #event.selectedDigests == 1 then

				event.selectedDigests = nil
				return event

			end

		end )

	rxTest:expectSingleValue( observable, ensureAuxFilesExistLocally_success )

end

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

function WFCatAssetAuxTest.rxTestEnsureAuxFilesExistLocally_AnnotationsButNoLocalFiles( rxTest )

	rxTest:setTimeLimit( 5 )

	local requestOptions = {
		requestHeaders = {
			{
				header = "Content-Type",
				value = "application/octet-stream",
			},
		},
	}

	local responses = {
		{
			url = "/href/bogus/aux1",
			response = KSData.make( 'foo' ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		},
		{
			url = "/href/bogus/aux1",
			response = KSData.make( 'bar' ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		},
		{
			url = "/href/bogus/aux1",
			response = KSData.make( 'baz' ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		}
	}

	local catalogSpec = WFCatSpecMock(
			KSIdGenerator.uuidInLowercaseHex(),
			KSIdGenerator.uuidInLowercaseHex(),
			rxTest, responses )

	local wfCatalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	-- Attempt to Download Aux Files

	local optArguments = {
		hasAllAuxType = true
	}

	-- Create a document that has local only annotation for mask 2, but no local file.

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )
	KSTableUtils.setNested( docValue, nil, '_localOnly', 'files', 'aux', expectedData.filter2Fingerprint )

	rxTest:waitFor( wfCatalog:docStore():setDocValue( docValue ) )

	WFFileUtils.delete( expectedData.mask2FilePath )
	WFFileUtils.delete( expectedData.filter1FilePath )
	WFFileUtils.delete( expectedData.c2paFilePath )

	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.mask1FilePath ) )
	rxTest:expectEqual( false, WFFileUtils.exists( expectedData.mask2FilePath ) )
	rxTest:expectEqual( false, WFFileUtils.exists( expectedData.filter1FilePath ) )
	rxTest:expectEqual( false, WFFileUtils.exists( expectedData.c2paFilePath ) )

	local observable = WFCatAssetAux.ensureAuxFilesExistLocally( wfCatalog, docValue )
		:map( function( event )

			-- There should be 1 selected digest returned
			-- Return value for selectedDigests is checked in resampled tests
			if #event.selectedDigests == 1 then

				event.selectedDigests = nil
				return event

			end

		end )

	rxTest:expectSingleValue( observable, ensureAuxFilesExistLocally_success )

	-- Verify the Document Has Local Only Aux Annotations

	local function sortAux( auxiliaries )

		local sorted = {}

		for _, aux in pairs( auxiliaries ) do
			sorted[ #sorted + 1 ] = aux
		end

		table.sort( sorted, function( a, b )
				return a.relativePath < b.relativePath
			end )

		return sorted

	end

	local _, mask1RelativePath = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData.mask1Fingerprint )
	local _, mask2RelativePath = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData.mask2Fingerprint )
	local _, filter1RelativePath = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData.filter1Fingerprint )
	local _, c2paRelativePath = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData.c2paFingerprint )


	local expected = {
		[ expectedData.mask1Fingerprint ] = {
			relativePath = mask1RelativePath
		},
		[ expectedData.mask2Fingerprint ] = {
			relativePath = mask2RelativePath
		},
		[ expectedData.filter1Fingerprint ] = {
			relativePath = filter1RelativePath
		},
		[ expectedData.c2paFingerprint ] = {
			relativePath = c2paRelativePath
		}
	}

	rxTest:expectSingleValue( wfCatalog:docStore():docValue( docValue._id )
		:pluck( '_localOnly', 'files', 'aux' )
		:map( sortAux ), sortAux( expected ) )

	-- Make sure the files were downloaded, even though the annotations existed.

	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.mask1FilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.mask2FilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.filter1FilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.c2paFilePath ) )

end


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

function WFCatAssetAuxTest.rxTestEnsureAuxFilesExistLocally_Version( rxTest )

	rxTest:setTimeLimit( 5 )

	local requestOptions = {
		requestHeaders = {
			{
				header = "Content-Type",
				value = "application/octet-stream",
			},
		},
	}

	local responses = {
		{
			url = "/href/bogus/aux1",
			response = KSData.make( 'foo' ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		},
		{
			url = "/href/bogus/aux1",
			response = KSData.make( 'bar' ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		},
		{
			url = "/href/bogus/aux1",
			response = KSData.make( 'baz' ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		}
	}

	local catalogSpec = WFCatSpecMock(
			KSIdGenerator.uuidInLowercaseHex(),
			KSIdGenerator.uuidInLowercaseHex(),
			rxTest, responses )

	local wfCatalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	-- Create an asset document that contains the auxiliary links

	local optArguments = {
		hasMasks = true,
		noLocalMasks = true,
		sharedMasks = true,
		hasFilters = true,
		noLocalFilters = true,
		sharedFilters = true,
	}

	local assetDocValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDocValue ) )

	-- Create a version document that contains no auxiliary links, whose parent is the asset.

	optArguments = {
		hasMasks = true,
		noLocalMasks = true,
		noLocalDevelopSettings = true,
		sharedMasks = true,
		hasFilters = true,
		noLocalFilters = true,
		sharedFilters = true
	}

	local versionDocValue, expectedData = WFCatVersionTestUtils.createVersionDocument( rxTest, wfCatalog, nil, optArguments )
	versionDocValue.asset.id = assetDocValue._id

	local versionId = versionDocValue._id

	rxTest:waitFor( wfCatalog:docStore():setDocValue( versionDocValue ) )

	-- Now try to download the auxiliaries associated with the Version document

	local observable = WFCatAssetAux.ensureAuxFilesExistLocally( wfCatalog, versionDocValue )
		:map( function( event )

			-- There should be 1 selected digest returned
			-- Return value for selectedDigests is checked in resampled tests
			if #event.selectedDigests == 1 then

				event.selectedDigests = nil
				return event

			end

		end )

	rxTest:expectSingleValue( observable, ensureAuxFilesExistLocally_success )

	-- Verify the version document has Local Only Aux Annotations

	local function sortAux( auxiliaries )

		local sorted = {}

		for _, aux in pairs( auxiliaries ) do
			sorted[ #sorted + 1 ] = aux
		end

		table.sort( sorted, function( a, b )
				return a.relativePath < b.relativePath
			end )

		return sorted

	end

	local _, mask1RelativePath = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData.mask1Fingerprint )
	local _, mask2RelativePath = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData.mask2Fingerprint )
	local _, filter1RelativePath = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData.filter1Fingerprint )
	local _, filter2RelativePath = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData.filter2Fingerprint )


	local expected = {
		[ expectedData.mask1Fingerprint ] = {
			relativePath = mask1RelativePath
		},
		[ expectedData.mask2Fingerprint ] = {
			relativePath = mask2RelativePath
		},
		[ expectedData.filter1Fingerprint ] = {
			relativePath = filter1RelativePath
		},
		[ expectedData.filter2Fingerprint ] = {
			relativePath = filter2RelativePath
		}
	}

	rxTest:expectSingleValue( wfCatalog:docStore():docValue( versionId )
		:pluck( '_localOnly', 'files', 'aux' )
		:map( sortAux ), sortAux( expected ) )

end

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

function WFCatAssetAuxTest.rxTestEnsureAuxFilesExistLocally_ResampleExistingDigest( rxTest )

	rxTest:setTimeLimit( 10 )

	local catalogId = KSIdGenerator.uuidInLowercaseHex()
	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local filterAuxId = '444' .. string.rep( 'd', 29 )
	local resampledDigest = string.rep( 'E', 32 )

	local requestOptions = {
		requestHeaders = {
			{
				header = 'Content-Type',
				value = 'application/json',
			}
		},
		requestMethod = 'POST',
		returnHttpErrorsAsData = true,
	}

	local requestData = KSJsonEncoder.encodeOzUploadJson {
			source_image_type = 'original',
			target_image_type = 'proxy-2560',
	}

	local responses = {
		{
			url = string.format( "/v2/catalogs/%s/assets/%s/aux/%s/resample", catalogId, assetId, filterAuxId ),
			data = requestData,
			response = KSData.make( 'foo' ),
			headers = {
				status_code = '200',
				[ 'X-Aux-Digest' ] = resampledDigest,
				[ 'X-Aux-Width' ] = 437,
				[ 'X-Aux-Height' ] = 328
			},
			options = requestOptions
		},
	}

	local catalogSpec = WFCatSpecMock(
			catalogId,
			KSIdGenerator.uuidInLowercaseHex(),
			rxTest, responses )

	local wfCatalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	-- Attempt to resample Aux File

	local optArguments = {
		hasFilters = true,
		noLocalFilters = true,
		sharedFilters = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId, optArguments )
	KSTableUtils.setNested( docValue, 1024*1024, 'aux', expectedData.filter1AuxId, 'fileSize' )
	KSTableUtils.setNested( docValue, nil, 'develop', 'filters', expectedData.filterGroupFingerprint, expectedData.filter2AuxId )

	rxTest:waitFor( wfCatalog:docStore():setDocValue( docValue ) )

	local baseAuxPath = WFCatAssetAux.getBasePath( wfCatalog )
	local resampledAbsolutePath = WFCatAssetAux.calculatePathUsingDigest( baseAuxPath, resampledDigest )

	-- resampled aux should have existing resampled filter

	local imageType = 'proxy-2560'

	rxTest:expectSingleValue( WFCatAssetAux.ensureAuxFilesExistLocally( wfCatalog, docValue, imageType )
		:map( function( event )

			-- There should be 1 selected digest returned.
			-- This is the result of resampled aux
			if #event.selectedDigests == 1 then
				return event.selectedDigests[ 1 ]
			end

		end ),
		{
			groupDigest = expectedData.filterGroupFingerprint,
			digest = expectedData.filter2Fingerprint,
			crsSizeX = 437,
			crsSizeY = 328,
			targetLongEdge = 2560,
			resampled = true
		} )

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

end

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

function WFCatAssetAuxTest.rxTestEnsureAuxFilesExistLocally_ResampleExistingDigest_Version( rxTest )

	rxTest:setTimeLimit( 10 )

	local catalogId = KSIdGenerator.uuidInLowercaseHex()
	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local versionId = KSIdGenerator.uuidInLowercaseHex()
	local filterAuxId = '444' .. string.rep( 'd', 29 )
	local resampledDigest = string.rep( 'E', 32 )

	local requestOptions = {
		requestHeaders = {
			{
				header = 'Content-Type',
				value = 'application/json',
			}
		},
		requestMethod = 'POST',
		returnHttpErrorsAsData = true,
	}

	local requestData = KSJsonEncoder.encodeOzUploadJson {
			source_image_type = 'original',
			target_image_type = 'proxy-2560',
	}

	local responses = {
		{
			url = string.format( "/v2/catalogs/%s/assets/%s/aux/%s/resample?version_id=%s", catalogId, assetId, filterAuxId, versionId ),
			data = requestData,
			response = KSData.make( 'foo' ),
			headers = {
				status_code = '200',
				[ 'X-Aux-Digest' ] = resampledDigest,
				[ 'X-Aux-Width' ] = 437,
				[ 'X-Aux-Height' ] = 328
			},
			options = requestOptions
		},
	}

	local catalogSpec = WFCatSpecMock(
			catalogId,
			KSIdGenerator.uuidInLowercaseHex(),
			rxTest, responses )

	local wfCatalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	-- Attempt to resample Aux File

	local optArguments = {
		hasFilters = true,
		noLocalFilters = true,
		sharedFilters = true
	}

	local assetDocValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId, optArguments )
	KSTableUtils.setNested( assetDocValue, 1024*1024, 'aux', expectedData.filter1AuxId, 'fileSize' )
	KSTableUtils.setNested( assetDocValue, nil, 'develop', 'filters', expectedData.filterGroupFingerprint, expectedData.filter2AuxId )

	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDocValue ) )

	-- version docValue

	local versionDocValue = {
		_id = versionId,
		type = 'version',
		subtype = 'edit',
		name = 'some new version',
		createdManually = true,
		asset = {
			id = assetDocValue._id
		},
		develop = assetDocValue.develop
	}

	rxTest:waitFor( wfCatalog:docStore():setDocValue( versionDocValue ) )

	local baseAuxPath = WFCatAssetAux.getBasePath( wfCatalog )
	local resampledAbsolutePath = WFCatAssetAux.calculatePathUsingDigest( baseAuxPath, resampledDigest )

	-- resampled aux should have existing resampled filter

	local imageType = 'proxy-2560'

	rxTest:expectSingleValue( WFCatAssetAux.ensureAuxFilesExistLocally( wfCatalog, versionDocValue, imageType )
		:map( function( event )

			-- There should be 1 selected digest returned.
			-- This is the result of resampled aux
			if #event.selectedDigests == 1 then
				return event.selectedDigests[ 1 ]
			end

		end ),
		{
			groupDigest = expectedData.filterGroupFingerprint,
			digest = expectedData.filter2Fingerprint,
			crsSizeX = 437,
			crsSizeY = 328,
			targetLongEdge = 2560,
			resampled = true
		} )

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

end

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

function WFCatAssetAuxTest.rxTestEnsureAuxFilesExistLocally_Resample_space( rxTest )

	rxTest:setTimeLimit( 10 )

	local catalogId = KSIdGenerator.uuidInLowercaseHex()
	local spaceId = KSIdGenerator.uuidInLowercaseHex()
	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local catalogDocId = KSIdGenerator.uuidInLowercaseHex()
	local filterAuxId = '444' .. string.rep( 'd', 29 )
	local resampledDigest = string.rep( 'E', 32 )

	local requestOptions = {
		requestHeaders = {
			{
				header = 'Content-Type',
				value = 'application/json',
			}
		},
		requestMethod = 'POST',
		returnHttpErrorsAsData = true,
	}

	local requestData = KSJsonEncoder.encodeOzUploadJson {
			source_image_type = 'original',
			target_image_type = 'proxy-2560',
	}

	local responses = {
		{
			url = string.format( "/v2/spaces/%s/assets/%s/aux/%s/resample", spaceId, catalogDocId, filterAuxId ),
			data = requestData,
			response = KSData.make( 'foo' ),
			headers = {
				status_code = '200',
				[ 'X-Aux-Digest' ] = resampledDigest,
				[ 'X-Aux-Width' ] = 437,
				[ 'X-Aux-Height' ] = 328
			},
			options = requestOptions
		},
	}

	local catalogSpec = WFCatSpecMock(
			catalogId,
			KSIdGenerator.uuidInLowercaseHex(),
			rxTest, responses )

	local wfCatalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	-- Attempt to resample Aux File

	local optArguments = {
		isSpaceAsset = true,
		spaceId = spaceId,
		hasFilters = true,
		noLocalFilters = true,
		sharedFilters = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId, optArguments )
	KSTableUtils.setNested( docValue, catalogDocId, '_serverProvidedMetadata', 'catalog_doc', 'id' )
	KSTableUtils.setNested( docValue, 1024*1024, 'aux', expectedData.filter1AuxId, 'fileSize' )
	KSTableUtils.setNested( docValue, nil, 'develop', 'filters', expectedData.filterGroupFingerprint, expectedData.filter2AuxId )

	rxTest:waitFor( wfCatalog:docStore():setDocValue( docValue ) )

	local baseAuxPath = WFCatAssetAux.getBasePath( wfCatalog )
	local resampledAbsolutePath = WFCatAssetAux.calculatePathUsingDigest( baseAuxPath, resampledDigest )

	-- resampled aux should have existing filter2AuxId

	local imageType = 'proxy-2560'

	rxTest:expectSingleValue( WFCatAssetAux.ensureAuxFilesExistLocally( wfCatalog, docValue, imageType )
		:map( function( event )

			-- There should be 1 selected digest returned.
			-- This is the result of resampled aux
			if #event.selectedDigests == 1 then
				return event.selectedDigests[ 1 ]
			end

		end ),
		{
			groupDigest = expectedData.filterGroupFingerprint,
			digest = expectedData.filter2Fingerprint,
			crsSizeX = 437,
			crsSizeY = 328,
			targetLongEdge = 2560,
			resampled = true
		} )

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

end

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

function WFCatAssetAuxTest.rxTestEnsureAuxFilesExistLocally_Resample_testNightly( rxTest )

	rxTest:setTimeLimit( 100 )

	-- Setup testSession

	local testSession, catalog, _, pusher, puller, uploader, _ = WFCatOzTestSession.setupOzTest( rxTest )

	assert( testSession )

	local assetId = KSIdGenerator.uuidInLowercaseHex()

	local catalogId = catalog:ozCatalogSpec():catalogId()

	local fileName = 'IMG_7763.JPG'

	local revisionId = KSIdGenerator.uuidInLowercaseHex()

	local userId = KSIdGenerator.uuidInLowercaseHex()

	-- Add asset to Oz

	local tempTestFolder = WFTestFileUtils.createTestFolder( rxTest )

	local originalFilePath = WFCatAssetTestUtils.getTestDataOriginalFilePath( rxTest, tempTestFolder, fileName )

	local sha256 = WFCrypto.digestFile( originalFilePath, 'SHA256' )

	local assetDocValue = WFCatAssetTestUtils.createBaseAssetDocument( catalogId, assetId, revisionId, sha256, originalFilePath, userId )

	assetDocValue = WFCatAssetTestUtils.addOriginalPathToDocument( assetDocValue, sha256, originalFilePath )

	rxTest:waitFor( catalog:docStore():setDocValue( assetDocValue ) )

	-- Add test digest to asset

	local testDataPath = KSPathUtils.child( KSPathUtils.getStandardFilePath( 'testdata' ), "assets" )

	local testDenoiseBinaryPath = KSPathUtils.child( testDataPath, "denoise_filter_aux.dat" )

	local filterBinary = rxTest:getFirstValue( WFAsyncFileUtils.readFile( testDenoiseBinaryPath ) )

	local updatedAssetDocument, auxId, auxDigest, auxAbsoluteFilePath = addAuxToAsset( catalog, assetDocValue, filterBinary )

	-- Add external xmp develop

	local carouselDocuments = KSPathUtils.getStandardFilePath( 'carouselDocuments' )

	local xmpDevelopFilePath = KSPathUtils.child( carouselDocuments, 'small.XMP' )

	WFCatAssetTestUtils.addXmpDevelopToDocument( rxTest, catalog, assetId, xmpDevelopFilePath )

	-- Apply overlay

	rxTest:waitFor( catalog:docStore():applyOverlay( updatedAssetDocument ) )

	rxTest:waitFor( pusher:sendUpdatesContinuously() )

	puller:getUpdatesContinuously()

	uploader:startContinuousUploads()

	-- Send update to Oz

	local observeAuxOnOz = rxTest:observe( catalog:docStore():ongoingDocValue( assetId )
			:map( function( docValue )

				local auxLinkPath = string.format( '/rels/aux/%s', auxId )

				local maybeAux = KSTableUtils_derefOpt( docValue, '_serverProvidedMetadata', 'links', auxLinkPath, 'href' )

				local masterOnOz = KSTableUtils_derefOpt( docValue, '_serverProvidedMetadata', 'links', '/rels/master', 'href') ~= nil

				local receivedUpdateFromOz = maybeAux ~= nil and masterOnOz

				return receivedUpdateFromOz

			end
		)
	)

	observeAuxOnOz:expectDataWithinTime( 40, true )

	-- Delete full-res aux from local database

	WFFileUtils.deleteFile( auxAbsoluteFilePath )

	rxTest:waitFor( catalog:docStore():applyOverlay {
		_id = assetId,
		_localOnly = {
			files = {
				aux = {
					[ auxDigest ] = {
						_delete = true
					}
				}
			}
		}
	} )

	-- Request a resample of filter aux, should get resampled.

	local optImageType = 'proxy-2560'

	local resampledAuxDigest, resampledAuxFilePath

	-- Running two ensureAuxFilesExistLocally at the same time should give same result
	-- This is to test that they are not having race condition

	local ensureAuxFilesExistLocallyObs1 = catalog:docStore():docValue( assetId )
		:flatMap( function( docValue )
			return WFCatAssetAux.ensureAuxFilesExistLocally( catalog, docValue, optImageType )
		end )

	local ensureAuxFilesExistLocallyObs2 = catalog:docStore():docValue( assetId )
		:flatMap( function( docValue )
			return WFCatAssetAux.ensureAuxFilesExistLocally( catalog, docValue, optImageType )
		end )

	rxTest:expectSingleValue( Rx.Observable.combineLatest( ensureAuxFilesExistLocallyObs1, ensureAuxFilesExistLocallyObs2 )
		:map( function ( event1, event2 )

			local sameResults =  KSTableUtils.deepEquals( event1, event2 )

			-- Check that all response fields are present

			if event1.selectedDigests then

				local resampledDigest = event1.selectedDigests[1]
				resampledAuxDigest = resampledDigest.digest

				local baseAuxPath = WFCatAssetAux.getBasePath( catalog )
				resampledAuxFilePath = WFCatAssetAux.calculatePathUsingDigest( baseAuxPath, resampledAuxDigest )

				return sameResults
					and resampledAuxDigest and resampledDigest.groupDigest
					and resampledDigest.crsSizeX and resampledDigest.crsSizeY
					and resampledDigest.targetLongEdge == 2560
					and resampledDigest.resampled
					and WFFileUtils.exists( resampledAuxFilePath ) == 'file'
					and true

			end

		end), true )

	-- Since resample has been requested before, resample should not be triggered.

	rxTest:expectSingleValue( catalog:docStore():docValue( assetId )
		:flatMap( function( docValue )

			return WFCatAssetAux.ensureAuxFilesExistLocally( catalog, docValue, optImageType )

		end )
		:map( function ( event )

			-- Check that all response fields are present

			if event.selectedDigests then

				local resampledDigest = event.selectedDigests[1]

				return resampledDigest.digest == resampledAuxDigest
					and resampledDigest.groupDigest
					and resampledDigest.crsSizeX and resampledDigest.crsSizeY
					and resampledDigest.targetLongEdge == 2560
					and resampledDigest.resampled
					and WFFileUtils.exists( resampledAuxFilePath ) == 'file'
					and true

			end

		end), true )

	-- Check that the docValue has been updated properly

	-- Close connections to oz

	observeAuxOnOz:dispose()
	puller:stopContinuousUpdates()
	uploader:stopContinuousUploads()

	if resampledAuxFilePath then
		WFFileUtils.deleteFile( resampledAuxFilePath )
	end

end


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

function WFCatAssetAuxTest.rxTestEnsureAuxFilesExistLocally_Resample_Version_testNightly( rxTest )

	rxTest:setTimeLimit( 100 )

	-- Setup testSession

	local testSession, catalog, _, pusher, puller, uploader, _ = WFCatOzTestSession.setupOzTest( rxTest )

	assert( testSession )

	local assetId = KSIdGenerator.uuidInLowercaseHex()

	local catalogId = catalog:ozCatalogSpec():catalogId()

	local fileName = 'IMG_7763.JPG'

	local revisionId = KSIdGenerator.uuidInLowercaseHex()

	local userId = KSIdGenerator.uuidInLowercaseHex()

	-- Add asset to Oz

	local tempTestFolder = WFTestFileUtils.createTestFolder( rxTest )

	local originalFilePath = WFCatAssetTestUtils.getTestDataOriginalFilePath( rxTest, tempTestFolder, fileName )

	local sha256 = WFCrypto.digestFile( originalFilePath, 'SHA256' )

	local assetDocValue = WFCatAssetTestUtils.createBaseAssetDocument( catalogId, assetId, revisionId, sha256, originalFilePath, userId )

	assetDocValue = WFCatAssetTestUtils.addOriginalPathToDocument( assetDocValue, sha256, originalFilePath )

	rxTest:waitFor( catalog:docStore():setDocValue( assetDocValue ) )

	-- Add test digest to asset

	local testDataPath = KSPathUtils.child( KSPathUtils.getStandardFilePath( 'testdata' ), "assets" )

	local testDenoiseBinaryPath = KSPathUtils.child( testDataPath, "denoise_filter_aux.dat" )

	local filterBinary = rxTest:getFirstValue( WFAsyncFileUtils.readFile( testDenoiseBinaryPath ) )

	local updatedAssetDocument, auxId, auxDigest, auxAbsoluteFilePath, auxGroupDigest = addAuxToAsset( catalog, assetDocValue, filterBinary )

	-- Add external xmp develop

	local carouselDocuments = KSPathUtils.getStandardFilePath( 'carouselDocuments' )

	local xmpDevelopFilePath = KSPathUtils.child( carouselDocuments, 'small.XMP' )

	local _, xmpDevelopSha256, _, storedXmpDevelopFilePath, xmpDevelopFileSize = WFCatAssetTestUtils.addXmpDevelopToDocument( rxTest, catalog, assetId, xmpDevelopFilePath )

	-- Apply overlay
	
	rxTest:waitFor( catalog:docStore():applyOverlay( updatedAssetDocument ) )
	
	-- Create a version from asset current edit

	local versionName = "axuResampleTest"
	local developSettings = {
		cameraRawXmpFilePath = storedXmpDevelopFilePath,
		cameraRawXmpSha256 =xmpDevelopSha256,
		cameraRawXmpSize = xmpDevelopFileSize,
		auxInfo = {
			{
				digest = auxDigest,
				groupDigest = auxGroupDigest,
				sourceLongEdge = 6000,
				crsSizeX = 1024,
				crsSizeY = 768
			}
		}
	}

	local versionId = rxTest:getFirstValue(
			WFCatVersions.createVersionForAssetWithExternalSettings(
				catalog, assetId, true, versionName, developSettings ) )

	-- Start syncing

	rxTest:waitFor( pusher:sendUpdatesContinuously() )

	puller:getUpdatesContinuously()

	uploader:startContinuousUploads()

	-- Send update to Oz

	local observeAuxOnOz = rxTest:observe( catalog:docStore():ongoingDocValue( assetId )
			:map( function( docValue )

				local auxLinkPath = string.format( '/rels/aux/%s', auxId )

				local maybeAux = KSTableUtils_derefOpt( docValue, '_serverProvidedMetadata', 'links', auxLinkPath, 'href' )

				local masterOnOz = KSTableUtils_derefOpt( docValue, '_serverProvidedMetadata', 'links', '/rels/master', 'href') ~= nil

				local receivedUpdateFromOz = maybeAux ~= nil and masterOnOz

				return receivedUpdateFromOz

			end
		)
	)

	observeAuxOnOz:expectDataWithinTime( 40, true )

	local observeVersionOnOz = rxTest:observe( catalog:docStore():ongoingDocValue( versionId )
			:map( function( versionDocValue )

				local xmpDevelopOnOz = KSTableUtils_derefOpt( versionDocValue, '_serverProvidedMetadata', 'links', '/rels/xmp/develop', 'href') ~= nil

				return xmpDevelopOnOz

			end
		)
	)

	observeVersionOnOz:expectDataWithinTime( 40, true )

	-- Delete full-res aux from local database

	WFFileUtils.deleteFile( auxAbsoluteFilePath )

	rxTest:waitFor( catalog:docStore():bulkApplyOverlay {
		{
			_id = assetId,
			_localOnly = {
				files = {
					aux = {
						[ auxDigest ] = {
							_delete = true
						}
					}
				}
			}
		},
		{
			_id = versionId,
			_localOnly = {
				files = {
					aux = {
						[ auxDigest ] = {
							_delete = true
						}
					}
				}
			}
		},
	} )

	-- Request a resample of filter aux, should get resampled.

	local optImageType = 'proxy-2560'
	local resampledAuxDigest, resampledAuxFilePath

	rxTest:expectSingleValue( catalog:docStore():docValue( versionId )
		:flatMap( function( versionDocValue )
			return WFCatAssetAux.ensureAuxFilesExistLocally( catalog, versionDocValue, optImageType )
		end )
		:map( function ( event )

			-- Check that all response fields are present

			if event.selectedDigests then

				local resampledDigest = event.selectedDigests[1]
				resampledAuxDigest = resampledDigest.digest

				local baseAuxPath = WFCatAssetAux.getBasePath( catalog )
				resampledAuxFilePath = WFCatAssetAux.calculatePathUsingDigest( baseAuxPath, resampledAuxDigest )

				return resampledAuxDigest and resampledDigest.groupDigest
					and resampledDigest.crsSizeX and resampledDigest.crsSizeY
					and resampledDigest.targetLongEdge == 2560
					and resampledDigest.resampled
					and WFFileUtils.exists( resampledAuxFilePath ) == 'file'
					and true

			end

		end), true )

	-- Close connections to oz

	observeAuxOnOz:dispose()
	puller:stopContinuousUpdates()
	uploader:stopContinuousUploads()

	if resampledAuxFilePath then
		WFFileUtils.deleteFile( resampledAuxFilePath )
	end

end

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

function WFCatAssetAuxTest.rxTestBulkDownloadAuxiliariesForOfflineUse( rxTest )

	local requestOptions = {
		requestHeaders = {
			{
				header = "Content-Type",
				value = "application/octet-stream",
			},
		},
	}

	local responses = {
		{
			url = "/href/bogus/aux1",
			response = KSData.make( 'foo' ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		},
		{
			url = "/href/bogus/aux1",
			response = KSData.make( 'bar' ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		},
		{
			url = "/href/bogus/aux1",
			response = KSData.make( 'baz' ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		},
		{
			url = "/href/bogus/aux1",
			response = KSData.make( 'qux' ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		},
		{
			url = "/href/bogus/aux1",
			response = KSData.make( 'quux' ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		},
		{
			url = "/href/bogus/aux1",
			response = KSData.make( 'foo' ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		},
		{
			url = "/href/bogus/aux1",
			response = KSData.make( 'bar' ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		},
		{
			url = "/href/bogus/aux1",
			response = KSData.make( 'quux' ),
			headers = {
				status_code = '200',
			},
			options = requestOptions
		}
	}

	local catalogSpec = WFCatSpecMock(
			KSIdGenerator.uuidInLowercaseHex(),
			KSIdGenerator.uuidInLowercaseHex(),
			rxTest, responses )

	local wfCatalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	-- Attempt to Download Aux Files

	local optArguments = {
		hasAllAuxType = true,
		noLocalAllAuxType = true
	}

	local docValue, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )
	local docValue2, expectedData2 = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local docIds = { docValue._id, docValue2._id }

	rxTest:waitFor( wfCatalog:docStore():bulkSetDocValue( { docValue, docValue2 } ) )

	local observable = WFCatAssetAux.bulkDownloadAuxiliariesForOfflineUse( wfCatalog, docIds )

	rxTest:expectSingleValue( observable, true )

	-- Verify the documents have Local Only Aux Annotations

	local function sortAux( auxiliaries )

		local sorted = {}

		for _, aux in pairs( auxiliaries or {} ) do
			sorted[ #sorted + 1 ] = aux
		end

		table.sort( sorted, function( a, b )
			return a.relativePath < b.relativePath
		end )

		return sorted

	end

	local _, mask1RelativePath = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData.mask1Fingerprint )
	local _, mask2RelativePath = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData.mask2Fingerprint )
	local _, filter1RelativePath = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData.filter1Fingerprint )
	local _, c2paRelativePath = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData.c2paFingerprint )


	local expected = {
		[ expectedData.mask1Fingerprint ] = {
			relativePath = mask1RelativePath
		},
		[ expectedData.mask2Fingerprint ] = {
			relativePath = mask2RelativePath
		},
		[ expectedData.filter1Fingerprint ] = {
			relativePath = filter1RelativePath
		},
		[ expectedData.c2paFingerprint ] = {
			relativePath = c2paRelativePath
		}
	}

	rxTest:expectSingleValue( wfCatalog:docStore():docValue( docValue._id )
		:pluck( '_localOnly', 'files', 'aux' )
		:map( sortAux ), sortAux( expected ) )

	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.mask1FilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.mask2FilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.filter1FilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData.c2paFilePath ) )

	local _, mask1RelativePath2 = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData2.mask1Fingerprint )
	local _, mask2RelativePath2 = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData2.mask2Fingerprint )
	local _, filter1RelativePath2 = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData2.filter1Fingerprint )
	local _, c2paRelativePath2 = WFCatAssetAux.calculatePathUsingDigest( 'foo', expectedData2.c2paFingerprint )


	expected = {
		[ expectedData2.mask1Fingerprint ] = {
			relativePath = mask1RelativePath2
		},
		[ expectedData2.mask2Fingerprint ] = {
			relativePath = mask2RelativePath2
		},
		[ expectedData2.filter1Fingerprint ] = {
			relativePath = filter1RelativePath2
		},
		[ expectedData2.c2paFingerprint ] = {
			relativePath = c2paRelativePath2
		}
	}

	rxTest:expectSingleValue( wfCatalog:docStore():docValue( docValue2._id )
		:pluck( '_localOnly', 'files', 'aux' )
		:map( sortAux ), sortAux( expected ) )

	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData2.mask1FilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData2.mask2FilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData2.filter1FilePath ) )
	rxTest:expectEqual( 'file', WFFileUtils.exists( expectedData2.c2paFilePath ) )

end

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

function WFCatAssetAuxTest.rxTestBulkDownloadAuxiliariesForOfflineUse_NothingToDownload( rxTest )

	local catalogSpec = WFCatSpecMock(
			KSIdGenerator.uuidInLowercaseHex(),
			KSIdGenerator.uuidInLowercaseHex(),
			rxTest, {} )

	local wfCatalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	-- Attempt to Download Aux Files

	local optArguments = {
		hasAllAuxType = true,
		noLocalAllAuxType = true,
		noAllAuxTypeLinks = true
	}

	local docValue = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )
	local docValue2 = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )

	local docIds = { docValue._id, docValue2._id }

	rxTest:waitFor( wfCatalog:docStore():bulkSetDocValue( { docValue, docValue2 } ) )

	local observable = WFCatAssetAux.bulkDownloadAuxiliariesForOfflineUse( wfCatalog, docIds )

	rxTest:expectSingleValue( observable, true )

end

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

function WFCatAssetAuxTest.rxTestGetAuxIdByDigestAndExistingFiltersFromRelatedDocuments( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Create an Asset Document that refers to two auxiliaries
	-- (two is the default for createAssetDocument)

	local optArguments = {
		hasDevelopAux = true
	}

	local assetDoc, expectedData = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )
	local assetId = assetDoc._id

	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc ) )

	-- Create a Version Document that refers to different auxiliaries.
	-- Note: Both auxilaries need to be reference by the parent asset.

	local versionName = "Foo"

	local developSettings = {
		cameraRawXmpFilePath = "Foo",
		cameraRawXmpSha256 = KSIdGenerator.uuidInLowercaseHex(),
		cameraRawXmpSize = 100,
		auxInfo = {
			{
				digest = expectedData.mask1Fingerprint
			},
			{
				digest = expectedData.filter1Fingerprint,
				groupDigest = expectedData.filterGroupFingerprint,
				sourceLongEdge = 6000,
				crsSizeX = 1024,
				crsSizeY = 768,
			}
		}
	}

	local versionId = rxTest:getFirstValue(
			WFCatVersions.createVersionForAssetWithExternalSettings(
					wfCatalog, assetId, true, versionName, developSettings ) )

	local version1Doc = rxTest:getFirstValue( wfCatalog:docStore():docValue( versionId ) )
	local version1Masks = KSTableUtils.derefOpt( version1Doc, 'develop', 'masks' )
	local version1DigestToAuxIdMap = {}

	for auxId, mask in pairs( version1Masks ) do

		version1DigestToAuxIdMap[ mask.digest ] = auxId

	end

	-- Create a Second Version Document that refers a *different* auxiliary

	local version2Name = "Foo2"

	developSettings = {
		cameraRawXmpFilePath = "Foo2",
		cameraRawXmpSha256 = KSIdGenerator.uuidInLowercaseHex(),
		cameraRawXmpSize = 100,
		auxInfo = {
			{
				digest = expectedData.mask2Fingerprint
			}
		}
	}

	local version2Id = rxTest:getFirstValue(
			WFCatVersions.createVersionForAssetWithExternalSettings(
					wfCatalog, assetId, true, version2Name, developSettings ) )

	local version2Doc = rxTest:getFirstValue( wfCatalog:docStore():docValue( version2Id ) )
	local version2Masks = KSTableUtils.derefOpt( version2Doc, 'develop', 'masks' )
	local version2DigestToAuxIdMap = {}

	for auxId, mask in pairs( version2Masks ) do

		version2DigestToAuxIdMap[ mask.digest ] = auxId

	end

	-- Verification of Aux from Asset Related Documents.
	-- Should be all aux from aset document.

	local expectedFromAsset = {
		[ expectedData.mask1Fingerprint ] = expectedData.mask1AuxId,
		[ expectedData.mask2Fingerprint ] = expectedData.mask2AuxId,
		[ expectedData.filter1Fingerprint ] = expectedData.filter1AuxId,
		[ expectedData.filter2Fingerprint ] = expectedData.filter2AuxId
	}

	local expectedFromVersions = KSTableUtils.merge(
			{}, version1DigestToAuxIdMap, version2DigestToAuxIdMap )

	local totalExpected = KSTableUtils.merge(
			{}, expectedFromAsset, expectedFromVersions )

	local existingFiltersExpected = KSTableUtils.derefOpt( assetDoc, 'develop', 'filters' )

	rxTest:expectSingleValue( WFCatAssetAux._TEST_getAuxIdByDigestAndExistingFiltersFromRelatedDocuments(
			wfCatalog, assetId ),
			totalExpected, existingFiltersExpected )

	-- Verification of Aux from Version Related Documents using parent Asset Id
	
	rxTest:expectSingleValue( WFCatAssetAux._TEST_getAuxIdByDigestAndExistingFiltersFromRelatedDocuments(
			wfCatalog, versionId, assetId ),
			totalExpected, existingFiltersExpected )

	rxTest:expectSingleValue( WFCatAssetAux._TEST_getAuxIdByDigestAndExistingFiltersFromRelatedDocuments(
			wfCatalog, version2Id, assetId ),
			totalExpected, existingFiltersExpected )

	-- Verification of Aux from Version Related Documents using parent doc value

	rxTest:expectSingleValue( WFCatAssetAux._TEST_getAuxIdByDigestAndExistingFiltersFromRelatedDocuments(
			wfCatalog, versionId, nil, assetDoc ),
			totalExpected, existingFiltersExpected )

	rxTest:expectSingleValue( WFCatAssetAux._TEST_getAuxIdByDigestAndExistingFiltersFromRelatedDocuments(
			wfCatalog, version2Id, nil, assetDoc ),
			totalExpected, existingFiltersExpected )

	-- Delete filters section

	rxTest:waitFor( wfCatalog:docStore():applyOverlay{
		_id = assetId,
		develop = {
			filters = {
				_delete = true
			}
		}
	} )

	-- Even we create version with only full-size resolution, the whold group would be added from parentDocument
	
	rxTest:expectSingleValue( WFCatAssetAux._TEST_getAuxIdByDigestAndExistingFiltersFromRelatedDocuments(
			wfCatalog, assetId ),
			totalExpected, existingFiltersExpected )

end

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

function WFCatAssetAuxTest.rxTestMaybeResampledDigestAndAuxInfoLocally( rxTest )

	local docValue = {}
	local digest = 'aux-digest'
	local groupDigest = 'aux-groupDigest'

	rxTest:expectEqual( nil, WFCatAssetAux.maybeResampledDigestAndAuxInfoLocally( docValue, groupDigest ) )

	local auxFileInfo = {
		relativePath = 'auxiliary/aux/-digest',
	}

	KSTableUtils.setNested( docValue, auxFileInfo, '_localOnly', 'files', 'aux', digest )

	local resampledDigest, resampledAuxInfo = WFCatAssetAux.maybeResampledDigestAndAuxInfoLocally( docValue, groupDigest )
	rxTest:expectEqual( nil, resampledDigest )
	rxTest:expectEqual( nil, resampledAuxInfo )

	-- resampled aux with groupDigest exits in localOnly section

	auxFileInfo = {
		relativePath = 'auxiliary/aux/-digest',
		groupDigest = groupDigest,
		targetLongEdge = 2560,
		crsSizeX = 437,
		crsSizeY = 328
	}

	KSTableUtils.setNested( docValue, auxFileInfo, '_localOnly', 'files', 'aux', digest )

	resampledDigest, resampledAuxInfo = WFCatAssetAux.maybeResampledDigestAndAuxInfoLocally( docValue, groupDigest )
	rxTest:expectEqual( digest, resampledDigest )
	rxTest:expectEqual( {
		groupDigest = groupDigest,
		targetLongEdge = 2560,
		crsSizeX = 437,
		crsSizeY = 328
	}, resampledAuxInfo )

end

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

local function multiResMissingAndSelectedResult( localOnlySettings, missingIndex, selectedIndex, resampleNeeded )

	missingIndex = missingIndex or {}
	selectedIndex =  selectedIndex or {}

	local auxMissing = {}
	local auxSelected = {}

	local groupDigest = localOnlySettings.filterGroupFingerprint

	local fullsizeAuxId = localOnlySettings.filter1AuxId
	local fullsizeAuxDigest = localOnlySettings.filter1Fingerprint

	local proxyAuxId = localOnlySettings.filter2AuxId
	local proxyAuxDigest = localOnlySettings.filter2Fingerprint

	local fullsizeAuxInfo = {
		auxId = fullsizeAuxId,
		groupDigest = groupDigest,
		digest = fullsizeAuxDigest,
		sourceLongEdge = 6000,
		crsSizeX = 1024,
		crsSizeY = 768
	}

	local proxyAuxInfo = {
		auxId = proxyAuxId,
		groupDigest = groupDigest,
		digest = proxyAuxDigest,
		targetLongEdge = 2560,
		crsSizeX = 437,
		crsSizeY = 328
	}

	if missingIndex[ 1 ] then
		auxMissing[ #auxMissing + 1 ] = fullsizeAuxInfo
	end

	if missingIndex[ 2 ] then
		auxMissing[ #auxMissing + 1 ] = proxyAuxInfo
	end

	if selectedIndex[ 1 ] then
		auxSelected[ #auxSelected + 1 ] = fullsizeAuxInfo
	end

	if selectedIndex[ 2 ] and resampleNeeded then
		auxSelected[ #auxSelected + 1 ] = {
			groupDigest = groupDigest,
			digest = proxyAuxDigest,
			targetLongEdge = 2560,
			crsSizeX = 437,
			crsSizeY = 328,
			fileSize = 32,
			resampled = true
		}
	elseif selectedIndex[ 2 ] then
		auxSelected[ #auxSelected + 1 ] = proxyAuxInfo
	elseif resampleNeeded then
		local copyFullsizeAuxInfo = KSTableUtils.deepCopy( fullsizeAuxInfo )
		copyFullsizeAuxInfo.resample = true
		copyFullsizeAuxInfo.estimatedFileSize = 42949.67296
		auxMissing[ #auxMissing + 1 ] = copyFullsizeAuxInfo
	end

	return { auxMissing, auxSelected }

end

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

function WFCatAssetAuxTest.rxTestCheckMultiResAuxMissingAndSelectedFromDevice( rxTest )

	rxTest:setTimeLimit( 5 )

	local wfCatalog = createFakeWFCatalog( rxTest )
	local basePath = WFCatAssetAux.getBasePath( wfCatalog )

	local maybeParentDoc = nil
	local performFileExistenceCheck = true
	local ignoreOnOzCheck = false

	-- Both full-size and proxy exist locally. Original instance fileSize is small

	local optArguments = {
		hasFilters = true
	}

	local assetDocValue, localOnlySettings = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )
	local filterSection = KSTableUtils_derefOpt( assetDocValue, 'develop', 'filters' ) or {}

	rxTest:expectEqual(
		WFCatAssetAux._TEST_checkMultiResAuxMissingAndSelectedFromDevice( assetDocValue,
								filterSection, maybeParentDoc, 'original',
								ignoreOnOzCheck, performFileExistenceCheck, basePath ),
		multiResMissingAndSelectedResult( localOnlySettings, {}, { true } ) )

	rxTest:expectEqual(
		WFCatAssetAux._TEST_checkMultiResAuxMissingAndSelectedFromDevice( assetDocValue,
								filterSection, maybeParentDoc, 'proxy-2560',
								ignoreOnOzCheck, performFileExistenceCheck, basePath ),
		multiResMissingAndSelectedResult( localOnlySettings, {}, { true } ) )

	-- Both full-size and proxy exist locally. Original instance fileSize is large
	-- Still select original instance

	local fullsizeAuxId = localOnlySettings.filter1AuxId
	KSTableUtils.setNested( assetDocValue, 1024*1024, 'aux', fullsizeAuxId, 'fileSize' )

	rxTest:expectEqual(
		WFCatAssetAux._TEST_checkMultiResAuxMissingAndSelectedFromDevice( assetDocValue,
								filterSection, maybeParentDoc, 'original',
								ignoreOnOzCheck, performFileExistenceCheck, basePath ),
		multiResMissingAndSelectedResult( localOnlySettings, {}, { true } ) )

	rxTest:expectEqual(
		WFCatAssetAux._TEST_checkMultiResAuxMissingAndSelectedFromDevice( assetDocValue,
								filterSection, maybeParentDoc, 'proxy-2560',
								ignoreOnOzCheck, performFileExistenceCheck, basePath ),
		multiResMissingAndSelectedResult( localOnlySettings, {}, { true } ) )

	-- Only proxy exists locally

	local fullsizeAuxDigest = localOnlySettings.filter1Fingerprint
	KSTableUtils.setNested( assetDocValue, nil, '_localOnly', 'files', 'aux', fullsizeAuxDigest, 'relativePath' )

	rxTest:expectEqual(
		WFCatAssetAux._TEST_checkMultiResAuxMissingAndSelectedFromDevice( assetDocValue,
								filterSection, maybeParentDoc, 'original',
								ignoreOnOzCheck, performFileExistenceCheck, basePath ),
		multiResMissingAndSelectedResult( localOnlySettings, { true }, { true } ) )

	rxTest:expectEqual(
		WFCatAssetAux._TEST_checkMultiResAuxMissingAndSelectedFromDevice( assetDocValue,
								filterSection, maybeParentDoc, 'proxy-2560',
								ignoreOnOzCheck, performFileExistenceCheck, basePath ),
		multiResMissingAndSelectedResult( localOnlySettings, {}, { false, true } ) )

	-- Both full-size and proxy are on Oz, and not local
	-- Shoulld select original instance since it's small enough

	optArguments = {
		hasFilters = true,
		noLocalFilters = true
	}

	assetDocValue, localOnlySettings = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )
	filterSection = KSTableUtils_derefOpt( assetDocValue, 'develop', 'filters' ) or {}

	rxTest:expectEqual(
		WFCatAssetAux._TEST_checkMultiResAuxMissingAndSelectedFromDevice( assetDocValue,
								filterSection, maybeParentDoc, 'original',
								ignoreOnOzCheck, performFileExistenceCheck, basePath ),
		multiResMissingAndSelectedResult( localOnlySettings, { true }, { true } ) )

	rxTest:expectEqual(
		WFCatAssetAux._TEST_checkMultiResAuxMissingAndSelectedFromDevice( assetDocValue,
								filterSection, maybeParentDoc, 'proxy-2560',
								ignoreOnOzCheck, performFileExistenceCheck, basePath ),
		multiResMissingAndSelectedResult( localOnlySettings, { true }, { true } ) )

	-- Increase full-size aux fileSize
	-- If optImageType is proxy, then we should select proxy size

	fullsizeAuxId = localOnlySettings.filter1AuxId
	KSTableUtils.setNested( assetDocValue, 1024*1024, 'aux', fullsizeAuxId, 'fileSize' )

	rxTest:expectEqual(
		WFCatAssetAux._TEST_checkMultiResAuxMissingAndSelectedFromDevice( assetDocValue,
								filterSection, maybeParentDoc, 'original',
								ignoreOnOzCheck, performFileExistenceCheck, basePath ),
		multiResMissingAndSelectedResult( localOnlySettings, { true }, { true } ) )

	rxTest:expectEqual(
		WFCatAssetAux._TEST_checkMultiResAuxMissingAndSelectedFromDevice( assetDocValue,
								filterSection, maybeParentDoc, 'proxy-2560',
								ignoreOnOzCheck, performFileExistenceCheck, basePath ),
		multiResMissingAndSelectedResult( localOnlySettings, { false, true }, { false, true } ) )

	-- Only original instance exists on Oz, and fileSize is large

	optArguments = {
		hasSourceOnlyFilters = true,
		noLocalFilters = true
	}

	assetDocValue, localOnlySettings = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )
	filterSection = KSTableUtils_derefOpt( assetDocValue, 'develop', 'filters' ) or {}

	fullsizeAuxId = localOnlySettings.filter1AuxId
	KSTableUtils.setNested( assetDocValue, 1024*1024, 'aux', fullsizeAuxId, 'fileSize' )

	rxTest:expectEqual(
		WFCatAssetAux._TEST_checkMultiResAuxMissingAndSelectedFromDevice( assetDocValue,
								filterSection, maybeParentDoc, 'original',
								ignoreOnOzCheck, performFileExistenceCheck, basePath ),
		multiResMissingAndSelectedResult( localOnlySettings, { true }, { true } ) )

	rxTest:expectEqual(
		WFCatAssetAux._TEST_checkMultiResAuxMissingAndSelectedFromDevice( assetDocValue,
								filterSection, maybeParentDoc, 'proxy-2560',
								ignoreOnOzCheck, performFileExistenceCheck, basePath ),
		multiResMissingAndSelectedResult( localOnlySettings, {}, {}, true ) )


	-- Only original instance exists on Oz, and fileSize is large
	-- But resampled instance exists locally

	optArguments = {
		hasSourceOnlyFilters = true,
		noLocalFilters = true,
		hasLocalResampledFilter  = true
	}

	assetDocValue, localOnlySettings = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, optArguments )
	filterSection = KSTableUtils_derefOpt( assetDocValue, 'develop', 'filters' ) or {}

	fullsizeAuxId = localOnlySettings.filter1AuxId
	KSTableUtils.setNested( assetDocValue, 1024*1024, 'aux', fullsizeAuxId, 'fileSize' )

	rxTest:expectEqual(
		WFCatAssetAux._TEST_checkMultiResAuxMissingAndSelectedFromDevice( assetDocValue,
								filterSection, maybeParentDoc, 'original',
								ignoreOnOzCheck, performFileExistenceCheck, basePath ),
		multiResMissingAndSelectedResult( localOnlySettings, { true }, { true } ) )

	rxTest:expectEqual(
		WFCatAssetAux._TEST_checkMultiResAuxMissingAndSelectedFromDevice( assetDocValue,
								filterSection, maybeParentDoc, 'proxy-2560',
								ignoreOnOzCheck, performFileExistenceCheck, basePath ),
		multiResMissingAndSelectedResult( localOnlySettings, {}, { false, true }, true ) )

end

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

function WFCatAssetAuxTest.rxTestResampleAux_testNightly( rxTest )

	rxTest:setTimeLimit( 60 )

	-- Setup testSession

	local testSession, catalog, _, pusher, puller, uploader, _ = WFCatOzTestSession.setupOzTest( rxTest )

	assert( testSession )

	local assetId = KSIdGenerator.uuidInLowercaseHex()

	local catalogId = catalog:ozCatalogSpec():catalogId()

	local fileName = 'IMG_7763.JPG'

	local revisionId = KSIdGenerator.uuidInLowercaseHex()

	local userId = KSIdGenerator.uuidInLowercaseHex()

	-- Add asset to Oz

	local tempTestFolder = WFTestFileUtils.createTestFolder( rxTest )

	local originalFilePath = WFCatAssetTestUtils.getTestDataOriginalFilePath( rxTest, tempTestFolder, fileName )

	local sha256 = WFCrypto.digestFile( originalFilePath, 'SHA256' )

	local assetDocValue = WFCatAssetTestUtils.createBaseAssetDocument( catalogId, assetId, revisionId, sha256, originalFilePath, userId )

	assetDocValue = WFCatAssetTestUtils.addOriginalPathToDocument( assetDocValue, sha256, originalFilePath )

	rxTest:waitFor( catalog:docStore():setDocValue( assetDocValue ) )

	-- Add test digest to asset

	local testDataPath = KSPathUtils.child( KSPathUtils.getStandardFilePath( 'testdata' ), "assets" )

	local _testDenoiseBinaryPath = KSPathUtils.child( testDataPath, "denoise_filter_aux.dat" )

	local filterBinary = rxTest:getFirstValue( WFAsyncFileUtils.readFile( _testDenoiseBinaryPath ) )

	local updatedAssetDocument, auxId = addAuxToAsset( catalog, assetDocValue, filterBinary )

	-- Add external xmp develop

	local carouselDocuments = KSPathUtils.getStandardFilePath( 'carouselDocuments' )

	local xmpDevelopFilePath = KSPathUtils.child( carouselDocuments, 'small.XMP' )

	WFCatAssetTestUtils.addXmpDevelopToDocument( rxTest, catalog, assetId, xmpDevelopFilePath )

	-- Apply overlay

	rxTest:waitFor( catalog:docStore():applyOverlay( updatedAssetDocument ) )

	rxTest:waitFor( pusher:sendUpdatesContinuously() )

	puller:getUpdatesContinuously()

	uploader:startContinuousUploads()

	-- Send update to Oz

	local observeAuxOnOz = rxTest:observe( catalog:docStore():ongoingDocValue( assetId )
			:map( function( docValue )

				local auxLinkPath = string.format( '/rels/aux/%s', auxId )

				local maybeAux = KSTableUtils_derefOpt( docValue, '_serverProvidedMetadata', 'links', auxLinkPath, 'href' )

				local masterOnOz = KSTableUtils_derefOpt( docValue, '_serverProvidedMetadata', 'links', '/rels/master', 'href') ~= nil

				local receivedUpdateFromOz = maybeAux ~= nil and masterOnOz

				return receivedUpdateFromOz

			end
		)
	)

	observeAuxOnOz:expectDataWithinTime( 40, true )

	-- Request a resample of filter aux

	local resampleAux = WFCatAssetAux._TEST_resampleAux( catalog, assetId, auxId )

	local filepath

	local obs = rxTest:observe( resampleAux
		:map( function ( event )

			-- Check that all response fields are present
			if event.filepath and event.crsSizeX and event.crsSizeY
				and event.digest then

				local baseAuxPath = WFCatAssetAux.getBasePath( catalog )
				local resampledAuxFilePath = WFCatAssetAux.calculatePathUsingDigest( baseAuxPath, event.digest )

				if WFFileUtils.exists( resampledAuxFilePath ) then
					return true
				end

			end

			return false

		end) )

	obs:expectDataWithinTime( 20, true )

	-- Close connections to oz

	observeAuxOnOz:dispose()

	puller:stopContinuousUpdates()

	uploader:stopContinuousUploads()

	if filepath then
		WFFileUtils.deleteFile( filepath )
	end

end

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

function WFCatAssetAuxTest.rxTestResampleAux_failAndRetry( rxTest )

	rxTest:setTimeLimit( 10 )

	local catalogId = KSIdGenerator.uuidInLowercaseHex()
	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local filterAuxId = '444' .. string.rep( 'd', 29 )
	local resampledDigest = KSIdGenerator.uuidInLowercaseHex()

	local requestOptions = {
		requestHeaders = {
			{
				header = 'Content-Type',
				value = 'application/json',
			}
		},
		requestMethod = 'POST',
		returnHttpErrorsAsData = true,
	}

	local requestData = KSJsonEncoder.encodeOzUploadJson {
			source_image_type = 'original',
			target_image_type = 'proxy-2560',
	}

	local responses = {
		{
			url = string.format( "/v2/catalogs/%s/assets/%s/aux/%s/resample", catalogId, assetId, filterAuxId ),
			data = requestData,
			response = "error",
			headers = {
				status_code = '404',
			},
			options = requestOptions
		},
		{
			url = string.format( "/v2/catalogs/%s/assets/%s/aux/%s/resample", catalogId, assetId, filterAuxId ),
			data = requestData,
			response = "error",
			headers = {
				status_code = '404',
			},
			options = requestOptions
		},
		{
			url = string.format( "/v2/catalogs/%s/assets/%s/aux/%s/resample", catalogId, assetId, filterAuxId ),
			data = requestData,
			response = KSData.make( 'foo' ),
			headers = {
				status_code = '200',
				[ 'X-Aux-Digest' ] = resampledDigest,
				[ 'X-Aux-Width' ] = 437,
				[ 'X-Aux-Height' ] = 328
			},
			options = requestOptions
		},
	}

	local catalogSpec = WFCatSpecMock(
			catalogId,
			KSIdGenerator.uuidInLowercaseHex(),
			rxTest, responses )

	local wfCatalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	-- Attempt to resample Aux File

	local optArguments = {
		hasSourceOnlyFilters = true,
		noLocalFilters = true,
		sharedFilters = true
	}

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

	rxTest:waitFor( wfCatalog:docStore():setDocValue( docValue ) )

	local baseAuxPath = WFCatAssetAux.getBasePath( wfCatalog )
	local resampledAbsolutePath, resampledRelativePath = WFCatAssetAux.calculatePathUsingDigest( baseAuxPath, resampledDigest )

	rxTest:expectSingleValue( WFCatAssetAux._TEST_resampleAux( wfCatalog, assetId, filterAuxId ),
		{
			filepath = resampledRelativePath,
			crsSizeX = 437,
			crsSizeY = 328,
			digest = resampledDigest
		} )

	rxTest:expectEqual( 'file', WFFileUtils.exists( resampledAbsolutePath ) )

	local resampledContent = KSData.memoryMappedContentsOfFile( resampledAbsolutePath )
	local resampledContentStr = tostring( resampledContent )

	rxTest:expectEqual( 'foo', resampledContentStr )

	resampledContent:close()

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

end

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

function WFCatAssetAuxTest.rxTestResampleAux_failAllRetries( rxTest )

	rxTest:setTimeLimit( 10 )
	
	local catalogId = KSIdGenerator.uuidInLowercaseHex()
	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local filterAuxId = '444' .. string.rep( 'd', 29 )
	local groupDigest = KSIdGenerator.uuidInLowercaseHex()

	local requestOptions = {
		requestHeaders = {
			{
				header = 'Content-Type',
				value = 'application/json',
			}
		},
		requestMethod = 'POST',
		returnHttpErrorsAsData = true,
	}

	local requestData = KSJsonEncoder.encodeOzUploadJson {
			source_image_type = 'original',
			target_image_type = 'proxy-2560',
	}

	local responses = {
		{
			url = string.format( "/v2/catalogs/%s/assets/%s/aux/%s/resample", catalogId, assetId, filterAuxId ),
			data = requestData,
			response = "error",
			headers = {
				status_code = '404',
			},
			options = requestOptions
		},
		{
			url = string.format( "/v2/catalogs/%s/assets/%s/aux/%s/resample", catalogId, assetId, filterAuxId ),
			data = requestData,
			response = "error",
			headers = {
				status_code = '404',
			},
			options = requestOptions
		},
		{
			url = string.format( "/v2/catalogs/%s/assets/%s/aux/%s/resample", catalogId, assetId, filterAuxId ),
			data = requestData,
			response = "error",
			headers = {
				status_code = '404',
			},
			options = requestOptions
		}
	}

	local catalogSpec = WFCatSpecMock(
			catalogId,
			KSIdGenerator.uuidInLowercaseHex(),
			rxTest, responses )

	local wfCatalog = WFCatMockCatalog.createWFCatalogForTestWithSpec( rxTest, catalogSpec )

	-- Attempt to resample Aux File

	local optArguments = {
		hasSourceOnlyFilters = true,
		noLocalFilters = true,
		sharedFilters = true
	}

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

	rxTest:waitFor( wfCatalog:docStore():setDocValue( docValue ) )
	
	local errorMessage = string.format( "Resample fail for asset %s, auxId %s, versionId %s, spaceId %s, groupDigest %s, targetImageType %s", 
											assetId, filterAuxId, nil, nil, groupDigest, 'proxy-2560' )

	local versionId = nil
	local spaceId = nil
	rxTest:observe( WFCatAssetAux._TEST_invokeResampleAux ( wfCatalog, assetId, filterAuxId, versionId, spaceId, groupDigest ) )
		:expectErrorWithinTime( 10, errorMessage )

end

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

function WFCatAssetAuxTest.rxTestInvokeResampleAux_Failure_testNightly( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = setUpTestCatalog( rxTest )

	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local auxId = KSIdGenerator.uuidInLowercaseHex()

	rxTest:observe( WFCatAssetAux._TEST_resampleAux( wfCatalog, assetId, auxId ) )
		:expectErrorWithinTime( 10, ':ignore:' )

end

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

return WFRxTestCase.wrapSuite( 'WFCatAssetAuxTest', WFCatAssetAuxTest )
