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

WFOzDocUtilsTest.lua

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

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

NOTICE: All information contained herein is, and remains the property of
Adobe Systems Incorporated and its suppliers, if any. The intellectual and
technical concepts contained herein are proprietary to Adobe Systems
Incorporated and its suppliers and are protected by all applicable
intellectual property laws, including trade secret and copyright laws.
Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.

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

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

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

	-- WF.oz_client
local WFCatAssetTestUtils = require 'WFCatAssetTestUtils'
local WFCatMockCatalog = require 'WFCatMockCatalog'
local WFCatVersionTestUtils = require 'WFCatVersionTestUtils'
local WFOzDocUtils = require 'WFOzDocUtils'

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

local WFOzDocUtilsTest = {}

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

function WFOzDocUtilsTest.rxTest_escapeUnderscoreKeyword( rxTest )

	local escapeUnderscoreKeyword = require 'WFOzDocUtils'.escapeUnderscoreKeyword
	assert( escapeUnderscoreKeyword, "WFOzDocUtils.escapeUnderscoreKeyword is not nil" )

	assert( escapeUnderscoreKeyword( "normal keyword" ) == "normal keyword", "normal ok" )
	assert( escapeUnderscoreKeyword( "normal_keyword" ) == "normal_keyword", "normal_ ok" )
	assert( escapeUnderscoreKeyword( "_merge" ) ~= "_merge", "reserved keyword mangled" )

end

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

local function createFakeWFCatalog( rxTest )

	local optArgs = {}

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

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

end

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

function WFOzDocUtilsTest.rxTestIsAssetDocFullySyncedWithOz( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local assetId = KSIdGenerator.uuidInLowercaseHex()

	-- Create a new asset doc. By default, the asset doc has a link
	-- to the master.

	local optOverrides = {
		hasMasks = true,
		internalXmpCameraRaw = false
	}

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

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

	-- No Oz label, so this should evaluate to false.

	rxTest:expectEqual( false, WFOzDocUtils.isAssetDocFullySyncedWithOz( assetDoc ) )

	assetDoc._labels = { oz = true }

	-- Now that there's an Oz label, this should be true.

	rxTest:expectEqual( true, WFOzDocUtils.isAssetDocFullySyncedWithOz( assetDoc ) )

	local syncedDoc = KSTableUtils.deepCopy( assetDoc )

	-- Remove the master link. The asset should no longer be considered
	-- as fully synced.

	assetDoc._serverProvidedMetadata = nil

	rxTest:expectEqual( false, WFOzDocUtils.isAssetDocFullySyncedWithOz( assetDoc ) )

	-- Reset the doc

	assetDoc = KSTableUtils.deepCopy( syncedDoc )

	-- Unsync the develop settings.

	KSTableUtils.setNested( assetDoc, nil, '_serverProvidedMetadata', 'links', '/rels/xmp/develop' )

	rxTest:expectEqual( false, WFOzDocUtils.isAssetDocFullySyncedWithOz( assetDoc ) )

	-- Reset the doc

	assetDoc = KSTableUtils.deepCopy( syncedDoc )

	-- Unsync the Auxiliary Objects

	local auxMetadata = KSTableUtils.derefOpt( assetDoc, 'aux' ) or {}

	for auxId in pairs( auxMetadata ) do

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

		KSTableUtils.setNested( assetDoc, nil, '_serverProvidedMetadata', 'links', auxLinkPath )

	end

	rxTest:expectEqual( false, WFOzDocUtils.isAssetDocFullySyncedWithOz( assetDoc ) )

end

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

function WFOzDocUtilsTest.rxTestAreAssetsFullySyncedWithOz( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local optOverrides = {
		internalXmpCameraRaw = true
	}

	local assetId1 = KSIdGenerator.uuidInLowercaseHex()
	local  assetDoc1 = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId1, optOverrides )
	assetDoc1._newRevId = KSIdGenerator.uuidInLowercaseHex()

	local assetId2 = KSIdGenerator.uuidInLowercaseHex()
	local  assetDoc2 = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId2, optOverrides )
	assetDoc2._newRevId = KSIdGenerator.uuidInLowercaseHex()

	local assetId3 = KSIdGenerator.uuidInLowercaseHex()
	local  assetDoc3 = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId3, optOverrides )
	assetDoc3._newRevId = KSIdGenerator.uuidInLowercaseHex()

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

	local assetIds = { assetId1, assetId2, assetId3 }

	-- No assets have Oz labels, so this should be false.

	rxTest:expectSingleValue( WFOzDocUtils.areAssetsFullySyncedWithOz( wfCatalog, assetIds ), false )

	rxTest:waitFor( wfCatalog:docStore():setLabel( assetId1, 'oz', assetDoc1._newRevId ) )

	-- This should still evaluate to false since only asset as the label.

	rxTest:expectSingleValue( WFOzDocUtils.areAssetsFullySyncedWithOz( wfCatalog, assetIds ), false )

	rxTest:waitFor( wfCatalog:docStore():setLabel( assetId2, 'oz', assetDoc2._newRevId ) )
	rxTest:waitFor( wfCatalog:docStore():setLabel( assetId3, 'oz', assetDoc3._newRevId ) )

	-- All assets have an Oz label. This should evaluate to true.

	rxTest:expectSingleValue( WFOzDocUtils.areAssetsFullySyncedWithOz( wfCatalog, assetIds ), true )

end

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

function WFOzDocUtilsTest.rxTestWaitUntilAssetsAreFullySyncedWithOz( rxTest )

	rxTest:setTimeLimit( 10 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local optOverrides = {
		internalXmpCameraRaw = true
	}

	local assetId1 = KSIdGenerator.uuidInLowercaseHex()
	local  assetDoc1 = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId1, optOverrides )
	assetDoc1._newRevId = KSIdGenerator.uuidInLowercaseHex()

	local assetId2 = KSIdGenerator.uuidInLowercaseHex()
	local  assetDoc2 = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId2, optOverrides )
	assetDoc2._newRevId = KSIdGenerator.uuidInLowercaseHex()

	local assetId3 = KSIdGenerator.uuidInLowercaseHex()
	local  assetDoc3 = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId3, optOverrides )
	assetDoc3._newRevId = KSIdGenerator.uuidInLowercaseHex()

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

	local assetIds = { assetId1, assetId2, assetId3 }

	-- Subscribe to the stream that fires when all assetse are fully synced.

	local counter = 0

	local observer = rxTest:observe( 
			WFOzDocUtils.waitUntilAssetsAreFullySyncedWithOzAndHasRendition( wfCatalog, assetIds, true )
			:map( function( areFullySynced )
				counter = counter + 1
				return areFullySynced, counter
			end ) )

	-- No assets have Oz labels, so this should be false.

	observer:expectData( false, 1 )

	-- Make 1 asset fully synced.

	local overlay = {
		_id = assetId1,
		_localOnly = {
			foo = true
		},
		_newLabel = 'oz'
	}

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( overlay ) )

	-- This should still evaluate to false since the other assets are not fully synced.

	observer:expectData( false, 2 )

	-- Make all assets fully synced.

	overlay._id = assetId2
	rxTest:waitFor( wfCatalog:docStore():applyOverlay( overlay ) )

	overlay._id = assetId3
	rxTest:waitFor( wfCatalog:docStore():applyOverlay( overlay ) )

	-- Now waitUntilAssetsAreFullySyncedWithOz should fire with true.

	observer:expectDataWithinTime( 2, true, ':ignore:' )


end

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

local function setOzLinkInvalid( docValue, binaryType, isInvalid )

	local invalid = isInvalid and true or nil

	KSTableUtils.setNested( docValue, invalid, '_serverProvidedMetadata', 'links', binaryType, 'invalid' )

	return docValue

end

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

local function setOzLinkHrefOrExists( docValue, binaryType, urlOrExists )

	if type( urlOrExists ) == 'string' then

		KSTableUtils.setNested( docValue, urlOrExists, '_serverProvidedMetadata', 'links', binaryType, 'href' )
		KSTableUtils.setNested( docValue, nil, '_serverProvidedMetadata', 'links', binaryType, 'exists' )

	elseif type( urlOrExists ) == 'boolean' then

		KSTableUtils.setNested( docValue, nil, '_serverProvidedMetadata', 'links', binaryType, 'href' )
		KSTableUtils.setNested( docValue, urlOrExists, '_serverProvidedMetadata', 'links', binaryType, 'exists' )

	elseif urlOrExists == nil then

		KSTableUtils.setNested( docValue, nil, '_serverProvidedMetadata', 'links', binaryType, 'href' )
		KSTableUtils.setNested( docValue, nil, '_serverProvidedMetadata', 'links', binaryType, 'exists' )

	end

	return docValue

end

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

function WFOzDocUtilsTest.rxTestWaitUntilAssetsAreFullySyncedWithOzAndHasRendition( rxTest )

	rxTest:setTimeLimit( 20 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local optOverrides = {
		internalXmpCameraRaw = true,
		noSufficientRenditionFoundOnOz = true,
		noProxyLink = true
	}

	local assetId1 = KSIdGenerator.uuidInLowercaseHex()
	local  assetDoc1 = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId1, optOverrides )
	assetDoc1._newRevId = KSIdGenerator.uuidInLowercaseHex()

	local assetId2 = KSIdGenerator.uuidInLowercaseHex()
	local  assetDoc2 = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId2, optOverrides )
	assetDoc2._newRevId = KSIdGenerator.uuidInLowercaseHex()

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

	local assetIds = { assetId1, assetId2 }

	-- Subscribe to the stream that fires when all assetse are fully synced.

	local counter = 0

	local observer = rxTest:observe( 
			WFOzDocUtils.waitUntilAssetsAreFullySyncedWithOzAndHasRendition( wfCatalog, assetIds, true )
			:map( function( areFullySynced )
				counter = counter + 1
				return areFullySynced, counter
			end ) )

	-- No assets have Oz labels, so this should be false.

	observer:expectData( false, 1 )

	-- Make one of the assets metadata appear fully synced.

	local overlay = {
		_id = assetId1,
		_localOnly = {
			foo = true
		},
		_newLabel = 'oz'
	}

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( overlay ) )

	observer:expectData( false, 2 )

	-- Make the other assets metadata appear fully synced.

	overlay._id = assetId2
	rxTest:waitFor( wfCatalog:docStore():applyOverlay( overlay ) )

	observer:expectData( false, 3 )

	--proxy

	local proxyBinaryType = '/rels/proxy_type/2560'

	overlay = {
		_id = assetId1,
		_serverProvidedMetadata = {
			links = {
				[ proxyBinaryType ] = { href = '/href/bogus/testing', exists = true },
			}
		},
	}

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( overlay ) )

	overlay = {
		_id = assetId2,
		_serverProvidedMetadata = {
			links = {
				[ proxyBinaryType ] = { href = '/href/bogus/testing', exists = true },
			}
		},
	}

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( overlay ) )

	observer:expectData( false, 4 )

	--thumbnail2x

	overlay = {
		_id = assetId1,
		_serverProvidedMetadata = {
			links = {
				[ '/rels/rendition_type/2048' ] = { href = '/href/bogus/testing' },
				[ '/rels/rendition_type/thumbnail2x' ] = { href = '/href/bogus/testing' }
			}
		},
	}

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( overlay ) )

	observer:expectData( false, 5 )

	overlay = {
		_id = assetId2,
		_serverProvidedMetadata = {
			links = {
				[ '/rels/rendition_type/2048' ] = { href = '/href/bogus/testing' },
				[ '/rels/rendition_type/thumbnail2x' ] = { href = '/href/bogus/testing' }
			}
		},
	}

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( overlay ) )

	observer:expectDataWithinTime( 2, true, ':ignore:' )

end

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

function WFOzDocUtilsTest.rxTestIsOriginalAvailableOnOz( rxTest )

	rxTest:setTimeLimit( 300 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local assetDoc = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog )
	assetDoc._spaceId = KSIdGenerator.uuidInLowercaseHex()

	local masterBinaryType = '/rels/master'

	-- asset with master link

	rxTest:expectEqual( true, WFOzDocUtils.isOriginalAvailableOnOz( assetDoc ) )
	rxTest:expectEqual( true, WFOzDocUtils.isValidOriginalAvailableOnOz( assetDoc ) )

	-- asset with master link, but invalid is true

	assetDoc = setOzLinkInvalid( assetDoc, masterBinaryType, true )

	rxTest:expectEqual( true, WFOzDocUtils.isOriginalAvailableOnOz( assetDoc ) )
	rxTest:expectEqual( false, WFOzDocUtils.isValidOriginalAvailableOnOz( assetDoc ) )

	-- asset without master link, with master exists field

	assetDoc = setOzLinkHrefOrExists( assetDoc, masterBinaryType, true )
	assetDoc = setOzLinkInvalid( assetDoc, masterBinaryType, false )

	rxTest:expectEqual( true, WFOzDocUtils.isOriginalAvailableOnOz( assetDoc ) )
	rxTest:expectEqual( true, WFOzDocUtils.isValidOriginalAvailableOnOz( assetDoc ) )

	-- asset without master link, with master exists field, but invalid is true

	assetDoc = setOzLinkInvalid( assetDoc, masterBinaryType, true )

	rxTest:expectEqual( true, WFOzDocUtils.isOriginalAvailableOnOz( assetDoc ) )
	rxTest:expectEqual( false, WFOzDocUtils.isValidOriginalAvailableOnOz( assetDoc ) )

	-- asset without master link, without exists field

	assetDoc = setOzLinkHrefOrExists( assetDoc, masterBinaryType, nil )
	assetDoc = setOzLinkInvalid( assetDoc, masterBinaryType, false )

	rxTest:expectEqual( false, WFOzDocUtils.isOriginalAvailableOnOz( assetDoc ) )
	rxTest:expectEqual( false, WFOzDocUtils.isValidOriginalAvailableOnOz( assetDoc ) )

end


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

function WFOzDocUtilsTest.rxTestIsProxyAvailableOnOz( rxTest )

	rxTest:setTimeLimit( 300 )

	local wfCatalog = createFakeWFCatalog( rxTest )

	local overrides = {
		noFullsizeProxyLink = true
	}

	local assetDoc = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, nil, overrides )
	assetDoc._spaceId = KSIdGenerator.uuidInLowercaseHex()

	local proxyBinaryType = '/rels/proxy_type/2560'

	-- asset with proxy link

	rxTest:expectEqual( true, WFOzDocUtils.isProxyAvailableOnOz( assetDoc ) )
	rxTest:expectEqual( true, WFOzDocUtils.isValidProxyAvailableOnOz( assetDoc ) )

	-- asset with proxy link, but invalid is true

	assetDoc = setOzLinkInvalid( assetDoc, proxyBinaryType, true )

	rxTest:expectEqual( true, WFOzDocUtils.isProxyAvailableOnOz( assetDoc ) )
	rxTest:expectEqual( false, WFOzDocUtils.isValidProxyAvailableOnOz( assetDoc ) )

	-- asset without proxy link, with proxy exists field

	assetDoc = setOzLinkHrefOrExists( assetDoc, proxyBinaryType, true )
	assetDoc = setOzLinkInvalid( assetDoc, proxyBinaryType, false )

	rxTest:expectEqual( true, WFOzDocUtils.isProxyAvailableOnOz( assetDoc ) )
	rxTest:expectEqual( true, WFOzDocUtils.isValidProxyAvailableOnOz( assetDoc ) )

	-- asset without proxy link, with proxy exists field, but invalid is true

	assetDoc = setOzLinkInvalid( assetDoc, proxyBinaryType, true )

	rxTest:expectEqual( true, WFOzDocUtils.isProxyAvailableOnOz( assetDoc ) )
	rxTest:expectEqual( false, WFOzDocUtils.isValidProxyAvailableOnOz( assetDoc ) )

	-- asset without proxy link, without exists field

	assetDoc = setOzLinkHrefOrExists( assetDoc, proxyBinaryType, nil )
	assetDoc = setOzLinkInvalid( assetDoc, proxyBinaryType, false )

	rxTest:expectEqual( false, WFOzDocUtils.isProxyAvailableOnOz( assetDoc ) )
	rxTest:expectEqual( false, WFOzDocUtils.isValidProxyAvailableOnOz( assetDoc ) )

end

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

function WFOzDocUtilsTest.rxTestMaybeLocalXmpDevelopSupersedesOzXmpDevelop( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Create a new asset doc with no external develop settings.

	local optOverrides = {
		internalXmpCameraRaw = true
	}

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

	rxTest:expectEqual( false, WFOzDocUtils.maybeLocalXmpDevelopSupersedesOzXmpDevelop( assetDoc ) )

	-- Create a new asset doc without sha256 in the develop settings link. This is a legacy doc
	-- that synced before atomic develop settings feature. If document is fully synced with Oz,
	-- then this is just an older document with nothing to sync.

	optOverrides = {
		ozDevelopSettingsWithoutSha256 = true
	}

	local assetId = KSIdGenerator.uuidInLowercaseHex()

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

	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc ) )
	assetDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( assetId ) )

	rxTest:expectEqual( false, WFOzDocUtils.maybeLocalXmpDevelopSupersedesOzXmpDevelop( assetDoc ) )

	-- If the xmp_develop link does not have a sha256 and there is some unsynced local change,
	-- then must assume that there is an xmp_develop binary to sync for the moment. If this is not
	-- the case, this will resolve when the latest changes are synced back from Oz with the sha256
	-- information in the xmp_develop link.

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( {
		_id = assetId,
		_newRevId = KSIdGenerator.uuidInLowercaseHex(),
		foo = 'bar',
	} ) )

	assetDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( assetId ) )

	rxTest:expectEqual( true, WFOzDocUtils.maybeLocalXmpDevelopSupersedesOzXmpDevelop( assetDoc ) )

	-- Create a new asset doc. By default, the asset doc has external xmp which is fully synced.

	assetId = KSIdGenerator.uuidInLowercaseHex()

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

	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc ) )
	assetDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( assetId ) )

	rxTest:expectEqual( false, WFOzDocUtils.maybeLocalXmpDevelopSupersedesOzXmpDevelop( assetDoc ) )

	-- If the sha256 in the xmp_develop link does not match the value in the payload,
	-- then an atomic develop settings upload is necessary.

	local sha256 = assetDoc._serverProvidedMetadata.links[ '/rels/xmp/develop' ].sha256

	local overlay = {
		_id = assetId,
		_serverProvidedMetadata = {
			links = {
				[ '/rels/xmp/develop' ] = { sha256 = KSIdGenerator.uuidInLowercaseHex() .. KSIdGenerator.uuidInLowercaseHex()  }
			}
		}
	}

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( overlay) )
	assetDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( assetId ) )

	rxTest:expectEqual( true, WFOzDocUtils.maybeLocalXmpDevelopSupersedesOzXmpDevelop( assetDoc ) )

	-- switch the sha256 back for the next test

	overlay._serverProvidedMetadata.links[ '/rels/xmp/develop' ] = { sha256 = sha256 }

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( overlay) )

	-- Apply an unsynced revision to this document. Since the sha256 in the payload matches the
	-- sha256 in the xmp_develop link, it will still think xmp_develop binary upload is not necessary.

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( {
		_id = assetId,
		_newRevId = KSIdGenerator.uuidInLowercaseHex(),
		foo = 'bar',
	} ) )

	assetDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( assetId ) )

	rxTest:expectEqual( false, WFOzDocUtils.maybeLocalXmpDevelopSupersedesOzXmpDevelop( assetDoc ) )

	-- If no /rels/xmp/develop link is present at all, return false (nothing to supersede)

	assetId = KSIdGenerator.uuidInLowercaseHex()

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

	assetDoc._serverProvidedMetadata.links[ '/rels/xmp/develop' ] = nil

	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc ) )
	assetDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( assetId ) )

	rxTest:expectEqual( false, WFOzDocUtils.maybeLocalXmpDevelopSupersedesOzXmpDevelop( assetDoc ) )

	-- Now make it look like the synced xmp is invalid.

	overlay = {
		_id = assetId,
		_serverProvidedMetadata = {
			links = {
				[ '/rels/xmp/develop' ] = { href = 'foo', invalid = true, },
			}
		}
	}

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( overlay) )
	assetDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( assetId ) )

	rxTest:expectEqual( true, WFOzDocUtils.maybeLocalXmpDevelopSupersedesOzXmpDevelop( assetDoc ) )

end

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

function WFOzDocUtilsTest.rxTestDoesDocumentNeedXmpDevelopSettingsUploaded( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Create a new asset doc with no external develop settings.

	local optOverrides = {
		internalXmpCameraRaw = true
	}

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

	rxTest:expectEqual( false, WFOzDocUtils.doesDocumentNeedXmpDevelopSettingsUploaded( assetDoc ) )

	-- Create a new asset doc without sha256 in the develop settings link. This is a legacy doc
	-- that synced before atomic develop settings feature. If document is fully synced with Oz,
	-- then this is just an older document with nothing to sync.

	optOverrides = {
		ozDevelopSettingsWithoutSha256 = true
	}

	local assetId = KSIdGenerator.uuidInLowercaseHex()

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

	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc ) )
	assetDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( assetId ) )

	rxTest:expectEqual( false, WFOzDocUtils.doesDocumentNeedXmpDevelopSettingsUploaded( assetDoc ) )

	-- If the xmp_develop link does not have a sha256 and there is some unsynced local change,
	-- then must assume that there is an xmp_develop binary to sync for the moment. If this is not
	-- the case, this will resolve when the latest changes are synced back from Oz with the sha256
	-- information in the xmp_develop link.

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( {
		_id = assetId,
		_newRevId = KSIdGenerator.uuidInLowercaseHex(),
		foo = 'bar',
	} ) )

	assetDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( assetId ) )

	rxTest:expectEqual( true, WFOzDocUtils.doesDocumentNeedXmpDevelopSettingsUploaded( assetDoc ) )

	-- Create a new asset doc. By default, the asset doc has external xmp which is fully synced.

	assetId = KSIdGenerator.uuidInLowercaseHex()

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

	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc ) )
	assetDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( assetId ) )

	rxTest:expectEqual( false, WFOzDocUtils.doesDocumentNeedXmpDevelopSettingsUploaded( assetDoc ) )

	-- If the sha256 in the xmp_develop link does not match the value in the payload,
	-- then an atomic develop settings upload is necessary.

	local sha256 = assetDoc._serverProvidedMetadata.links[ '/rels/xmp/develop' ].sha256

	local overlay = {
		_id = assetId,
		_serverProvidedMetadata = {
			links = {
				[ '/rels/xmp/develop' ] = { sha256 = KSIdGenerator.uuidInLowercaseHex() .. KSIdGenerator.uuidInLowercaseHex()  }
			}
		}
	}

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( overlay) )
	assetDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( assetId ) )

	rxTest:expectEqual( true, WFOzDocUtils.doesDocumentNeedXmpDevelopSettingsUploaded( assetDoc ) )

	-- switch the sha256 back for the next test

	overlay._serverProvidedMetadata.links[ '/rels/xmp/develop' ] = { sha256 = sha256 }

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( overlay) )

	-- Apply an unsynced revision to this document. Since the sha256 in the payload matches the
	-- sha256 in the xmp_develop link, it will still think xmp_develop binary upload is not necessary.

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( {
		_id = assetId,
		_newRevId = KSIdGenerator.uuidInLowercaseHex(),
		foo = 'bar',
	} ) )

	assetDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( assetId ) )

	rxTest:expectEqual( false, WFOzDocUtils.doesDocumentNeedXmpDevelopSettingsUploaded( assetDoc ) )

	-- Make it look like the external xmp is not synced.

	assetId = KSIdGenerator.uuidInLowercaseHex()

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

	assetDoc._serverProvidedMetadata.links[ '/rels/xmp/develop' ] = nil

	rxTest:waitFor( wfCatalog:docStore():setDocValue( assetDoc ) )
	assetDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( assetId ) )

	rxTest:expectEqual( true, WFOzDocUtils.doesDocumentNeedXmpDevelopSettingsUploaded( assetDoc ) )

	-- Now make it look like the synced xmp is invalid.

	overlay = {
		_id = assetId,
		_serverProvidedMetadata = {
			links = {
				[ '/rels/xmp/develop' ] = { href = 'foo', invalid = true, },
			}
		}
	}

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( overlay) )
	assetDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( assetId ) )

	rxTest:expectEqual( true, WFOzDocUtils.doesDocumentNeedXmpDevelopSettingsUploaded( assetDoc ) )

	-- Shared With You / space tests for when curent user is a contributorwho may need
	-- to upload xmp_develop to a contributed asset. (Editors can see the xmp_develop
	-- href so do not need special logic for exists = true case)

	overlay._spaceId = KSIdGenerator.uuidInLowercaseHex()
		-- in addition, by NOT setting assetdoc._serverProvidedMetadata.editable = true
		-- this indicates the asset is NOT editable by the current user.

	-- asset has no xmp develop uploaded to Oz.

	overlay._serverProvidedMetadata.links[ '/rels/xmp/develop' ] = { _delete = true }

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( overlay ) )
	assetDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( assetId ) )

	rxTest:expectEqual( true, WFOzDocUtils.doesDocumentNeedXmpDevelopSettingsUploaded( assetDoc ) )

	-- asset has valid xmp develop uploaded to Oz.

	overlay._serverProvidedMetadata.links[ '/rels/xmp/develop' ] = { exists = true }

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( overlay) )
	assetDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( assetId ) )

	rxTest:expectEqual( false, WFOzDocUtils.doesDocumentNeedXmpDevelopSettingsUploaded( assetDoc ) )

	-- asset has invalid xmp develop uploaded to Oz.

	overlay._serverProvidedMetadata.links[ '/rels/xmp/develop' ] = { invalid = true }

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( overlay) )
	assetDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( assetId ) )

	rxTest:expectEqual( true, WFOzDocUtils.doesDocumentNeedXmpDevelopSettingsUploaded( assetDoc ) )

end

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

function WFOzDocUtilsTest.rxTestDoesDocumentNeedXmpDevelopSettingsUploaded_forVersions( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Create a new asset doc with no external develop settings.

	local optOverrides = {
		internalXmpCameraRaw = true
	}

	local versionDoc = WFCatVersionTestUtils.createVersionDocument( rxTest, wfCatalog, KSIdGenerator.uuidInLowercaseHex(), optOverrides )

	rxTest:expectEqual( false, WFOzDocUtils.doesDocumentNeedXmpDevelopSettingsUploaded( versionDoc ) )

	-- Create a new version doc. By default, the version doc has external xmp which is fully synced.

	local versionId = KSIdGenerator.uuidInLowercaseHex()

	versionDoc = WFCatVersionTestUtils.createVersionDocument( rxTest, wfCatalog, versionId )

	rxTest:waitFor( wfCatalog:docStore():setDocValue( versionDoc ) )
	versionDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( versionId ) )

	rxTest:expectEqual( false, WFOzDocUtils.doesDocumentNeedXmpDevelopSettingsUploaded( versionDoc ) )

	-- having an unsynced local change should not impact upload status for a version

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( {
		_id = versionId,
		_newRevId = KSIdGenerator.uuidInLowercaseHex(),
		foo = 'bar',
	} ) )

	versionDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( versionId ) )

	rxTest:expectEqual( false, WFOzDocUtils.doesDocumentNeedXmpDevelopSettingsUploaded( versionDoc ) )

	-- Make it look like the external xmp is not synced.

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( {
		_id = versionId,
		_serverProvidedMetadata = {
			links = {
				[ '/rels/xmp/develop' ] = { _delete = true }
			}
		}
	} ) )

	versionDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( versionId ) )

	rxTest:expectEqual( true, WFOzDocUtils.doesDocumentNeedXmpDevelopSettingsUploaded( versionDoc ) )

	-- In space context may see redacted version and should never upload to one of these

	rxTest:waitFor( wfCatalog:docStore():applyOverlay( {
		_id = versionId,
		_serverProvidedMetadata = {
			redacted = true,
			links = { _delete = true },
		},
	} ) )

	versionDoc = rxTest:getFirstValue( wfCatalog:docStore():docValue( versionId ) )

	rxTest:expectEqual( false, WFOzDocUtils.doesDocumentNeedXmpDevelopSettingsUploaded( versionDoc ) )

end

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

-- SPAM [jandrew 2021-08-23]: This test only runs on Desktop because mobile doesn't
-- support uploading xmp sidecars, AFAIK.

if WFPlatformInfo.isDesktop then

function WFOzDocUtilsTest.rxTestDoesDocumentNeedXmpSidecarUploaded( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Create a new asset doc

	local assetDoc = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, KSIdGenerator.uuidInLowercaseHex() )

	rxTest:expectEqual( false, WFOzDocUtils.doesDocumentNeedXmpSidecarUploaded( assetDoc ) )

	-- locally imported, no local xmp sidecar

	local optOverrides = {
		localXmpSidecarDoesNotExist = true
	}

	assetDoc = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, KSIdGenerator.uuidInLowercaseHex(), optOverrides )

	rxTest:expectEqual( false, WFOzDocUtils.doesDocumentNeedXmpSidecarUploaded( assetDoc ) )

	-- local xmp sidecar, no sidecar on Oz

	optOverrides = {
		noXmpSidecarLink = true
	}

	assetDoc = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, KSIdGenerator.uuidInLowercaseHex(), optOverrides )

	rxTest:expectEqual( true, WFOzDocUtils.doesDocumentNeedXmpSidecarUploaded( assetDoc ) )

end

end

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

function WFOzDocUtilsTest.rxTestDoesDocumentNeedOriginalOrProxyUploaded( rxTest )

	local wfCatalog = createFakeWFCatalog( rxTest )

	-- Create a new asset doc. By default, the asset doc has a link
	-- to the master and the proxy.

	local assetDoc = WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, KSIdGenerator.uuidInLowercaseHex() )

	rxTest:expectEqual( false, WFOzDocUtils.doesDocumentNeedOriginalOrProxyUploaded( assetDoc ) )

	-- An asset with an invalid original needs an upload

	KSTableUtils.setNested( assetDoc, true, "_serverProvidedMetadata", "links", '/rels/master', 'invalid' )

	rxTest:expectEqual( true, WFOzDocUtils.doesDocumentNeedOriginalOrProxyUploaded( assetDoc ) )

	-- An asset with no original, but a valid proxy is okay

	KSTableUtils.setNested( assetDoc, nil, "_serverProvidedMetadata", "links", '/rels/master' )

	rxTest:expectEqual( false, WFOzDocUtils.doesDocumentNeedOriginalOrProxyUploaded( assetDoc ) )

	-- But an asset with no original, and an invalid proxy, needs an upload.

	KSTableUtils.setNested( assetDoc, true, "_serverProvidedMetadata", "links", '/rels/proxy_type/2560', 'invalid' )

	rxTest:expectEqual( true, WFOzDocUtils.doesDocumentNeedOriginalOrProxyUploaded( assetDoc ) )

	-- An asset with no original and no proxy needs an upload.

	KSTableUtils.setNested( assetDoc, nil, "_serverProvidedMetadata", "links" )

	rxTest:expectEqual( true, WFOzDocUtils.doesDocumentNeedOriginalOrProxyUploaded( assetDoc ) )

	-- original with exists = true will be considered needs upoad when not in a space (this should not happen)

	KSTableUtils.setNested( assetDoc, true, "_serverProvidedMetadata", "links", "/rels/master", "exists" )

	rxTest:expectEqual( true, WFOzDocUtils.doesDocumentNeedOriginalOrProxyUploaded( assetDoc ) )

	-- original with exists = true in a space should be considered does not need upload

	assetDoc._spaceId = KSIdGenerator.uuidInLowercaseHex()

	rxTest:expectEqual( false, WFOzDocUtils.doesDocumentNeedOriginalOrProxyUploaded( assetDoc ) )

end

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

-- brief: Test isValidDocId function. This tests gives several invalid docId 
-- scenarios and a valid docId at the end.
function WFOzDocUtilsTest.rxTestIsValidDocId( rxTest )

	rxTest:setTimeLimit( 5 )
	
	local invalidDocIdWithUnderscore = '_123456789'
	local invalidDocIdWithInvalidCharacters = '!234?56;'
	local invalidDocIdWithInvalidStringLength = '123'
	local validDocId = KSIdGenerator.uuidInLowercaseHex()
	
	rxTest:expectEqual( WFOzDocUtils.isValidDocId( invalidDocIdWithUnderscore ), false )
	
	rxTest:expectEqual( WFOzDocUtils.isValidDocId( invalidDocIdWithInvalidCharacters ), false )
	
	rxTest:expectEqual( WFOzDocUtils.isValidDocId( invalidDocIdWithInvalidStringLength ), false )
	
	rxTest:expectEqual( WFOzDocUtils.isValidDocId( validDocId ), true )

end

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

return WFRxTestCase.wrapSuite( 'WFOzDocUtilsTest', WFOzDocUtilsTest )
