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

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

NOTICE: All information contained herein is, and remain the property of
Adobe Systems Incorporated and its suppliers, if any. The intellectual and
technical concepts contained herein are proprietary to Adobe Systems
Incorporated and its suppliers and may be covered by U.S. and Foreign
Patents, patents in process, and are protected by trade secret or copyright
law.  Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.

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


local KSIdGenerator = require 'KSIdGenerator'
local KSPathUtils = require 'KSPathUtils'
local KSTableUtils = require 'KSTableUtils'
local OzCatalogUrls = require 'OzCatalogUrls'
local WFTableMerge = require 'WFTableMerge'

local WFCatUser = require 'WFCatUser'
local WFCatSpec = require 'WFCatSpec'
local WFCatalog = require 'WFCatalog'
local WFCatTrash = require 'WFCatTrash'
local WFRxTestCase = require 'WFRxTestCase'
local WFPlatformInfo = require 'WFPlatformInfo'
local WFCatMockCatalog = require 'WFCatMockCatalog'

local LrmCatSyncStatus = require 'LrmCatSyncStatus'

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

local kUser1 = string.rep( '1', 32 )
local kCatalogId = string.rep( 'c', 32 )

local LrmCatSyncStatusTest = {}

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

local function makeLocalOnlyWithMaster()

	local localOnly

	if WFPlatformInfo.isiOS then
		localOnly =  {
			files = {
				master = {
					path = '/a/path'
				},
			},
		}
	else
		localOnly = {
			originalFilePath = '/a/path'
		}
	end

	return localOnly
end

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

local function makeLocalOnlyWithMasterAndXmpDevelop()

	local localOnly

	if WFPlatformInfo.isiOS then
		localOnly =  {
			files = {
				master = {
					path = '/a/path'
				},
				xmp_develop = {
					path = "/b/path",
					contentId = "deadbeaf",
				},
			}
		}
	else
		localOnly = {
			originalFilePath = '/a/path',
			files = {
				xmp_develop = {
					path = "/b/path",
					sha256 = "deadbeaf"
				}
			}
		}
	end

	return localOnly
end

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

local function makeLocalOnlyWithPreview()

	local localOnly

	if WFPlatformInfo.isiOS then
		localOnly =  {
			files = {
				preview = {
					path = '/a/path'
				},
			}
		}
	else
		localOnly = {
			preview = {
				filePath = '/a/path',
			}
		}
	end

	return localOnly
end

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

local function makeLocalOnlyWithProxyAndXmpDevelop()

	local localOnly

	if WFPlatformInfo.isiOS then
		localOnly =  {
			files = {
				preview = {
					path = '/a/path'
				},
				proxy = {
					path = '/a/path'
				},
				xmp_develop = {
					path = "/b/path",
					contentId = "deadbeaf",
				},
			}
		}
	else
		localOnly = {
			preview = {
				path = '/a/path'
			},
			proxy = {
				filePath = '/a/path'
			},
			files = {
				xmp_develop = {
					path = "/b/path",
					sha256 = "deadbeaf"
				},
			}
		}
	end

	return localOnly
end

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

local function makeLocalOnlyWithPreviewAndXmpDevelop()

	local localOnly

	if WFPlatformInfo.isiOS then
		localOnly =  {
			files = {
				preview = {
					path = '/a/path'
				},
				xmp_develop = {
					path = "/b/path",
					contentId = "deadbeaf",
				},
			}
		}
	else
		localOnly =  {
			preview = {
				filePath = '/a/path'
			},
			files = {
				xmp_develop = {
					path = "/b/path",
					sha256 = "deadbeaf"
				},
			}
		}
	end

	return localOnly
end

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

local function mockCatalogSetUp( rxTest )

	local catalogOptions = {
		indexDefinitions = 'LrmCatalogIndexDefinitions.lua'
	}

	local wfCatalog = WFCatMockCatalog.createFakeWFCatalog( rxTest, nil, nil, catalogOptions )

	return wfCatalog

end

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

function LrmCatSyncStatusTest.rxTestAssetSyncStatus( rxTest )

	local statusList = LrmCatSyncStatus.getAssetSyncStatusList()

	-- just an empty doc, no link section, nothing on Oz yet: 'needsMetadataUpload'

	local docValue = {
		_revId = "b2855707c97dc7ae52e40a7d179636f5"
	}

	rxTest:expectEqual ( statusList.needsMetadataUpload, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- if there is no revision_id at all in the rev list -> non asset type old doc -> consider metadata synced.

	docValue._serverProvidedMetadata = {
			base = "https://photos.adobe.io/v2/catalogs/a4be0605937b4bc5aa3092e51fd90dc4/",
			created = "2017-08-17T11:57:14.343553Z",
			links = {
			},
			revision_ids = {
			},
			updated = "2018-08-29T16:13:13.128931Z",
		}

	rxTest:expectEqual( statusList.needsProxyOrMaster, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- new metadata Oz doesn't yet now about

	docValue._serverProvidedMetadata.revision_ids = {
		"54de3e54e717446c9de71b5639a0ae1a",
		"75171df99897de9e1b9819c2311c4a92",
		"2f87e2ffa1883247d7a5db457a7a3ba5",
	}

	-- metadata upload error conditions

	rxTest:expectEqual( statusList.needsMetadataUpload, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	docValue._localOnly = {
		ozError = {
			subtype = 'StorageExceededError',
		},
	}

	rxTest:expectEqual( statusList.storageLimitExceeded, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	docValue._localOnly.ozError.subtype = 'TrialOrSubscriptionExpiredError'

	rxTest:expectEqual( statusList.trialOrSubscriptionExpired, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	docValue._localOnly.ozError.subtype = 'MultipartCompleteError'

	rxTest:expectEqual( statusList.multipartCompleteError, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- metadata successfully uploaded, revid is in revision list

	docValue = {
		_revId = "b2855707c97dc7ae52e40a7d179636f5",
		_serverProvidedMetadata = {
			base = "https://photos.adobe.io/v2/catalogs/a4be0605937b4bc5aa3092e51fd90dc4/",
			created = "2017-08-17T11:57:14.343553Z",
			links = {
			},
			revision_ids = {
				"b2855707c97dc7ae52e40a7d179636f5",
				"54de3e54e717446c9de71b5639a0ae1a",
				"75171df99897de9e1b9819c2311c4a92",
				"2f87e2ffa1883247d7a5db457a7a3ba5",
			},
			updated = "2018-08-29T16:13:13.128931Z",
		}
	}

	rxTest:expectEqual( statusList.needsProxyOrMaster, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- have master locally, needs to be uploaded to Oz

	docValue._localOnly = makeLocalOnlyWithMaster()

	rxTest:expectEqual( statusList.needsMasterUpload, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- created master link but binary has not yet been uploaded

	local content = {
		content_type = "image/jpeg",
		href = "/v2/catalogs/a4be0605937b4bc",
		invalid = true,
	}

	KSTableUtils.setNested( docValue, content, '_serverProvidedMetadata', 'links', "/rels/master" )

	rxTest:expectEqual( statusList.needsMasterUpload, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- created master link but storage exceeded

	docValue._localOnly.ozError = {
		subtype = 'StorageExceededError',
	}

	rxTest:expectEqual( statusList.storageLimitExceeded, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- created master link but subscription ended

	docValue._localOnly.ozError.subtype = 'TrialOrSubscriptionExpiredError'

	rxTest:expectEqual( statusList.trialOrSubscriptionExpired, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- created master link but received multipart complete error

	docValue._localOnly.ozError.subtype = 'MultipartCompleteError'

	rxTest:expectEqual( statusList.multipartCompleteError, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- master link is now valid, master succesfully uploaded, external develop XMP needs to be uploaded
	-- local external xmp_develop but no link created.

	docValue = {
		_revId = "b2855707c97dc7ae52e40a7d179636f5",
		_localOnly = makeLocalOnlyWithMasterAndXmpDevelop(),
		_serverProvidedMetadata = {
			base = "https://photos.adobe.io/v2/catalogs/a4be0605937b4bc5aa3092e51fd90dc4/",
			created = "2017-08-17T11:57:14.343553Z",
			links = {
				["/rels/master"] = {
					content_type = "image/jpeg",
					href = "/v2/catalogs/a4be0605937b4bc",
				},
			},
			revision_ids = {
				"b2855707c97dc7ae52e40a7d179636f5",
				"54de3e54e717446c9de71b5639a0ae1a",
				"75171df99897de9e1b9819c2311c4a92",
				"2f87e2ffa1883247d7a5db457a7a3ba5",
			},
			updated = "2018-08-29T16:13:13.128931Z",
		},
		develop = {
			xmpCameraRaw = {
				sha256 = "deadbeaf",
			}
		}
	}

	rxTest:expectEqual( statusList.needsDevelopXmpUpload, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- add xmp_develop link but still invalid.

	docValue._serverProvidedMetadata.links = {
		["/rels/master"] = {
			content_type = "image/jpeg",
			href = "/v2/catalogs/a4be0605937b4bc",
		},
		["/rels/xmp/develop"] = {
			href = "/v2/catalogs/a4be0605937b4bc",
			invalid = true,
		},
	}

	rxTest:expectEqual( statusList.needsDevelopXmpUpload, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- created xmp develop link but storage exceeded

	docValue._localOnly.ozError = {
		subtype = 'StorageExceededError',
	}

	rxTest:expectEqual( statusList.storageLimitExceeded, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- created xmp develop link but subscription ended

	docValue._localOnly.ozError.subtype = 'TrialOrSubscriptionExpiredError'

	rxTest:expectEqual( statusList.trialOrSubscriptionExpired, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- created develop xmp link but received multipart complete error

	docValue._localOnly.ozError.subtype = 'MultipartCompleteError'

	-- master link is now valid, master succesfully uploaded, external develop XMP uploaded,
	-- but still need to upload referenced auxiliary objects.

	docValue = {
		_revId = "b2855707c97dc7ae52e40a7d179636f5",
		_localOnly = {
			files = {
				aux = {
					fakeAuxDigest = {
						relativePath = 'fake/path'
					}
				}
			}
		},
		aux = {
			auxId1 = {
				digest = 'fakeAuxDigest'
			}
		},
		_serverProvidedMetadata = {
			base = "https://photos.adobe.io/v2/catalogs/a4be0605937b4bc5aa3092e51fd90dc4/",
			created = "2017-08-17T11:57:14.343553Z",
			links = {
				["/rels/master"] = {
					content_type = "image/jpeg",
					href = "/v2/catalogs/a4be0605937b4bc",
				},
				["/rels/xmp/develop"] = {
					href = "/v2/catalogs/a4be0605937b4bc",
				},
			},
			revision_ids = {
				"b2855707c97dc7ae52e40a7d179636f5",
				"54de3e54e717446c9de71b5639a0ae1a",
				"75171df99897de9e1b9819c2311c4a92",
				"2f87e2ffa1883247d7a5db457a7a3ba5",
			},
			updated = "2018-08-29T16:13:13.128931Z",
		},
		develop = {
			xmpCameraRaw = {
				sha256 = "deadbeaf",
			},
			masks = {
				auxId1 = {
					digest = 'fakeAuxDigest'
				}
			}
		}
	}

	rxTest:expectEqual( statusList.needsDevelopXmpUpload, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- add aux link

	content = {
		href = "/v2/catalogs/a4be0605937b4bc"
	}

	KSTableUtils.setNested( docValue, content, '_serverProvidedMetadata', 'links', "/rels/aux/auxId1" )

	rxTest:expectEqual( statusList.needsProxyOrMaster, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- master link is now valid, master succesfully uploaded, external develop XMP successfully upload

	docValue = {
		_revId = "b2855707c97dc7ae52e40a7d179636f5",
		_localOnly = makeLocalOnlyWithMasterAndXmpDevelop();
		_serverProvidedMetadata = {
			base = "https://photos.adobe.io/v2/catalogs/a4be0605937b4bc5aa3092e51fd90dc4/",
			created = "2017-08-17T11:57:14.343553Z",
			links = {
				["/rels/master"] = {
					content_type = "image/jpeg",
					href = "/v2/catalogs/a4be0605937b4bc",
				},
				["/rels/xmp/develop"] = {
					href = "/v2/catalogs/a4be0605937b4bc",
				},
			},
			revision_ids = {
				"b2855707c97dc7ae52e40a7d179636f5",
				"54de3e54e717446c9de71b5639a0ae1a",
				"75171df99897de9e1b9819c2311c4a92",
				"2f87e2ffa1883247d7a5db457a7a3ba5",
			},
			updated = "2018-08-29T16:13:13.128931Z",
		},
		develop = {
			xmpCameraRaw = {
				sha256 = "deadbeaf",
			}
		}
	}

	rxTest:expectEqual( statusList.complete, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- master and external XMP links are available, no local files

	docValue = {
		_revId = "b2855707c97dc7ae52e40a7d179636f5",
		_serverProvidedMetadata = {
			base = "https://photos.adobe.io/v2/catalogs/a4be0605937b4bc5aa3092e51fd90dc4/",
			created = "2017-08-17T11:57:14.343553Z",
			links = {
				["/rels/master"] = {
					content_type = "image/jpeg",
					href = "/v2/catalogs/a4be0605937b4bc",
				},
				["/rels/xmp/develop"] = {
					href = "/v2/catalogs/a4be0605937b4bc",
				},
			},
			revision_ids = {
				"b2855707c97dc7ae52e40a7d179636f5",
				"54de3e54e717446c9de71b5639a0ae1a",
				"75171df99897de9e1b9819c2311c4a92",
				"2f87e2ffa1883247d7a5db457a7a3ba5",
			},
			updated = "2018-08-29T16:13:13.128931Z",
		},
		develop = {
			xmpCameraRaw = {
				sha256 = "deadbeaf",
			}
		}
	}

	rxTest:expectEqual( statusList.needsProxyOrMaster, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- add local preview

	docValue._localOnly = makeLocalOnlyWithPreview()

	rxTest:expectEqual( statusList.needsDevelopXmp, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	rxTest:expectEqual( false, LrmCatSyncStatus.isAssetCompleted( LrmCatSyncStatus.getAssetSyncStatus( docValue ) ) )

	-- add local develop xmp

	docValue._localOnly = makeLocalOnlyWithPreviewAndXmpDevelop()

	rxTest:expectEqual( statusList.completeNoNegatives, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	rxTest:expectEqual( true, LrmCatSyncStatus.isAssetCompleted( LrmCatSyncStatus.getAssetSyncStatus( docValue ) ) )

	-- add local proxy and xmp

	docValue._localOnly = makeLocalOnlyWithProxyAndXmpDevelop()

	rxTest:expectEqual( statusList.completeNoMaster, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	rxTest:expectEqual( true, LrmCatSyncStatus.isAssetCompleted( LrmCatSyncStatus.getAssetSyncStatus( docValue ) ) )

	-- downloaded master instead of proxy

	docValue._localOnly = makeLocalOnlyWithMasterAndXmpDevelop()

	rxTest:expectEqual( statusList.complete, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	rxTest:expectEqual( true, LrmCatSyncStatus.isAssetCompleted( LrmCatSyncStatus.getAssetSyncStatus( docValue ) ) )

end

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

function LrmCatSyncStatusTest.rxTestAssetSyncStatus_InvalidAux( rxTest )

	local statusList = LrmCatSyncStatus.getAssetSyncStatusList()

	-- master link is now valid, master succesfully uploaded, external develop XMP uploaded,
	-- but still need to upload referenced auxiliary objects.

	local docValue = {
		_revId = "b2855707c97dc7ae52e40a7d179636f5",
		_localOnly = {
			files = {
				aux = {
					fakeAuxDigest = {
						relativePath = 'fake/path'
					}
				}
			}
		},
		aux = {
			auxId1 = {
				digest = 'fakeAuxDigest'
			}
		},
		_serverProvidedMetadata = {
			base = "https://photos.adobe.io/v2/catalogs/a4be0605937b4bc5aa3092e51fd90dc4/",
			created = "2017-08-17T11:57:14.343553Z",
			links = {
				["/rels/master"] = {
					content_type = "image/jpeg",
					href = "/v2/catalogs/a4be0605937b4bc",
				},
				["/rels/xmp/develop"] = {
					href = "/v2/catalogs/a4be0605937b4bc",
				},
			},
			revision_ids = {
				"b2855707c97dc7ae52e40a7d179636f5",
				"54de3e54e717446c9de71b5639a0ae1a",
				"75171df99897de9e1b9819c2311c4a92",
				"2f87e2ffa1883247d7a5db457a7a3ba5",
			},
			updated = "2018-08-29T16:13:13.128931Z",
		},
		develop = {
			xmpCameraRaw = {
				sha256 = "deadbeaf",
			},
			masks = {
				auxId1 = {
					digest = 'fakeAuxDigest'
				}
			}
		}
	}

	rxTest:expectEqual( statusList.needsDevelopXmpUpload, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- Mark the auxiliary as invalid.

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

	rxTest:expectEqual( statusList.invalidAuxError, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

end

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

function LrmCatSyncStatusTest.rxTestAssetSyncStatus_InvalidXmpDevelop( rxTest )

	local statusList = LrmCatSyncStatus.getAssetSyncStatusList()

	local docValue = {
		_revId = "b2855707c97dc7ae52e40a7d179636f5",
		_localOnly = makeLocalOnlyWithMasterAndXmpDevelop(),
		_serverProvidedMetadata = {
			base = "https://photos.adobe.io/v2/catalogs/a4be0605937b4bc5aa3092e51fd90dc4/",
			created = "2017-08-17T11:57:14.343553Z",
			links = {
				["/rels/master"] = {
					content_type = "image/jpeg",
					href = "/v2/catalogs/a4be0605937b4bc",
				},
				["/rels/xmp/develop"] = {
					href = "/v2/catalogs/a4be0605937b4bc",
					invalid = true,
				},
			},
			revision_ids = {
				"b2855707c97dc7ae52e40a7d179636f5",
				"54de3e54e717446c9de71b5639a0ae1a",
				"75171df99897de9e1b9819c2311c4a92",
				"2f87e2ffa1883247d7a5db457a7a3ba5",
			},
			updated = "2018-08-29T16:13:13.128931Z",
		},
		develop = {
			xmpCameraRaw = {
				sha256 = "deadbeaf",
			},
		}
	}

	rxTest:expectEqual( statusList.needsDevelopXmpUpload, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- Mark the xmpDevelop as invalid.

	KSTableUtils.setNested( docValue, true, '_localOnly', 'files', 'xmp_develop', 'error' )

	rxTest:expectEqual( statusList.invalidXmpDevelopError, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

end

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

function LrmCatSyncStatusTest.rxTestAlbumSyncStatus( rxTest )

	local statusList = LrmCatSyncStatus.getAlbumSyncStatusList()

	-- just a basic album doc, nothing on Oz yet

	local docValue = {
		_revId = "b2855707c97dc7ae52e40a7d179636f5",
		name = "some album",
		subtype = "collection",
		type = "album",
	}

	rxTest:expectEqual ( statusList.syncing, LrmCatSyncStatus.getAlbumSyncStatus( docValue ) )

	-- add revision_ids: doc is synced, links section available, consider it complete

	docValue._serverProvidedMetadata = {
		links = {
			["/rels/web_album"] = {
				href = "https://lightroom.adobe.com/libraries/b6308fa8eb45442e943fbd19ca8dc389/albums/b2855707c97dc7ae52e40a7d179636f5",
			},
			["self"] = {
				href = "/v2/catalogs/b6308fa8eb45442e943fbd19ca8dc389/albums/b2855707c97dc7ae52e40a7d179636f5",
			},
		},
		revision_ids = {
			"b2855707c97dc7ae52e40a7d179636f5",
			"37c40404ac2041d999eb60c3319d5f13",
			"6c8bd86df2e746c4a7a96b6b8dba6563",
			"5cdbfc477f9b47bea43d28ce56364b47",
		}
	}

	rxTest:expectEqual ( statusList.synced, LrmCatSyncStatus.getAlbumSyncStatus( docValue ) )

	-- just a basic album doc, nothing on Oz yet

	docValue = {
		_revId = "b2855707c97dc7ae52e40a7d179636f5",
		name = "some album",
		subtype = "collection",
		type = "album",
	}

	-- and mark it for offline use

	docValue._localOnly = {
		useOffline = true
	}

	rxTest:expectEqual ( statusList.syncingForOfflineUse, LrmCatSyncStatus.getAlbumSyncStatus( docValue ) )

	rxTest:expectEqual( false, LrmCatSyncStatus.isAlbumCompleted( LrmCatSyncStatus.getAlbumSyncStatus( docValue ) ) )

	-- add revision_ids: doc is synced, links section available, consider it complete

	docValue._serverProvidedMetadata = {
		links = {
			["/rels/web_album"] = {
				href = "https://lightroom.adobe.com/libraries/b6308fa8eb45442e943fbd19ca8dc389/albums/b2855707c97dc7ae52e40a7d179636f5",
			},
			["self"] = {
				href = "/v2/catalogs/b6308fa8eb45442e943fbd19ca8dc389/albums/b2855707c97dc7ae52e40a7d179636f5",
			},
		},
		revision_ids = {
			"b2855707c97dc7ae52e40a7d179636f5",
			"37c40404ac2041d999eb60c3319d5f13",
			"6c8bd86df2e746c4a7a96b6b8dba6563",
			"5cdbfc477f9b47bea43d28ce56364b47",
		}
	}

	rxTest:expectEqual ( statusList.syncedForOfflineUse, LrmCatSyncStatus.getAlbumSyncStatus( docValue ) )

	rxTest:expectEqual( true, LrmCatSyncStatus.isAlbumCompleted( LrmCatSyncStatus.getAlbumSyncStatus( docValue ) ) )

end

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

function LrmCatSyncStatusTest.rxTestSyncStatusOfAlbumsOfAlbumAssets( rxTest )

	local catalog = mockCatalogSetUp( rxTest )

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

	rxTest:setTimeLimit( 10 )

	-- create asset
	rxTest:expectSingleValue (
		catalog:docStore():applyOverlay {
			type = 'asset',
			subtype = 'image',
			_id = assetId,
			importSource = {
				originalWidth = 30,
				originalHeight = 20,
			},
			captureDate = '2012-08-15T15:22:29',
			_localOnly = makeLocalOnlyWithMaster(),
		},
		':ignore:'
	)

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

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


	local observeCoreInfo = rxTest
		:observe( catalog:indexStore():query{
			sql = [[SELECT docId, syncStatus FROM coreInfo ]],
			quiesceUntilUpToDate = true,
		})

	local observeAlbums = rxTest
		:observe( catalog:indexStore():query{
			sql = [[SELECT docId, syncStatus FROM albums ]],
			quiesceUntilUpToDate = true,
		})

	-- nothing synced yet
	observeCoreInfo:expectData { { assetId, 1 } }

	observeAlbums:expectData { { albumId, 1 } }

	local albumAssetIds = { albumAssetId }

	local observeSyncStatusOfAlbums =
		rxTest:observe( LrmCatSyncStatus.getSyncStatusOfAlbumsOfAlbumAssets( catalog, albumAssetIds ) )

	local expected = {}

	expected[albumId] = false
	observeSyncStatusOfAlbums:expectData( expected )

	-- simulate album meta data synced, links are available
	rxTest:expectSingleValue (
		catalog:docStore():applyOverlay {
			_id = albumId,
			_serverProvidedMetadata = {
				links = {
					["/rels/web_album"] = {
						href = "https://lightroom.adobe.com/...",
					},
					["self"] = {
						href = "/v2/catalogs/...",
					},
				},
				revision_ids = {	-- Makes use of special case - see LrAssetUtils.isLatestMetadataUploaded()
									-- so to not have to guess the new revId.
				}
			}
		},
		':ignore:'
	)

	observeAlbums:expectData { { albumId, 2 } }

	expected[albumId] = true
	observeSyncStatusOfAlbums:expectData( expected )
end

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

function LrmCatSyncStatusTest.rxTestSyncStatusOfAssetsOfAlbumAssets( rxTest )

	local catalog = mockCatalogSetUp( rxTest )

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

	rxTest:setTimeLimit( 60 )

	-- create asset
	rxTest:expectSingleValue (
		catalog:docStore():applyOverlay {
			type = 'asset',
			subtype = 'image',
			_id = assetId,
			importSource = {
				originalWidth = 30,
				originalHeight = 20,
			},
			captureDate = '2012-08-15T15:22:29',
			_localOnly = makeLocalOnlyWithMaster(),
		},
		':ignore:'
	)

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

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

	rxTest:expectSingleValue (
		catalog:docStore():applyOverlay {
			type = 'asset',
			subtype = 'image',
			_id = assetId2,
			importSource = {
				originalWidth = 30,
				originalHeight = 20,
			},
			captureDate = '2012-08-15T15:22:30',
			_localOnly = makeLocalOnlyWithMaster(),
		},
		':ignore:'
	)

	rxTest:expectSingleValue (
		catalog:docStore():applyOverlay {
			type = 'album_asset',
			_id = albumAssetId2,
			album = {
				id = albumId,
			},
			asset = {
				id = assetId2,
			},
		},
		':ignore:'
	)

	local observeCoreInfo = rxTest
		:observe( catalog:indexStore():query{
			sql = [[SELECT docId, syncStatus FROM coreInfo ]],
			quiesceUntilUpToDate = true,
		})

	local observeAlbums = rxTest
		:observe( catalog:indexStore():query{
			sql = [[SELECT docId, syncStatus FROM albums ]],
			quiesceUntilUpToDate = true,
		})

	-- nothing synced yet
	observeCoreInfo:expectData { { assetId, 1 }, { assetId2, 1 } }

	observeAlbums:expectData { { albumId, 1 } }

	local albumAssetIds = { albumAssetId, albumAssetId2 }

	local observeSyncStatusOfAssets =
		rxTest:observe( LrmCatSyncStatus.getSyncStatusOfAssetsOfAlbumAssets( catalog, albumAssetIds ) )

	local expected = {}

	expected[assetId] = false
	expected[assetId2] = false
	observeSyncStatusOfAssets:expectData( expected )

	-- simulate asset meta data synced, links are available
	rxTest:expectSingleValue (
		catalog:docStore():applyOverlay {
			_id = assetId,
			_serverProvidedMetadata = {
				base = "https://photos.adobe.io/v2/catalogs/...",
				created = "2017-08-17T11:57:14.343553Z",
				links = {
					["/rels/master"] = {
						content_type = "image/jpeg",
						href = "/v2/catalogs/...",
					},
				},
				revision_ids = {	-- Makes use of special case - see LrAssetUtils.isLatestMetadataUploaded()
									-- so to not have to guess the new revId.
				},
				updated = "2018-08-29T16:13:13.128931Z",
			},
		},
		':ignore:'
	)

	observeCoreInfo:expectData { { assetId2, 1 }, { assetId, 6 } }	-- note: updated doc is reliably last because
																	-- rowid order is default sorting order

	expected[assetId] = true
	observeSyncStatusOfAssets:expectData( expected )

	-- WFCatTrash.moveAssetsToTrash() can't be easily configured to work
	-- with LriCatalogIndexDefinitions on desktop. Skip rest of test on desktop.
	if not WFPlatformInfo.isDesktop then
		-- simulate assetId2 is trashed
		rxTest:expectOnlyCompleted (
			WFCatTrash.moveAssetsToTrash( catalog, { assetId2 } )
		)

		local trashAssetId2

		rxTest:expectSingleValue (
			catalog:indexStore():query {
				sql = [[SELECT docId FROM trashAssets ]],
				quiesceUntilUpToDate = true,
			}
			:map( function( row )
				trashAssetId2 = row[1] and row[1][1]
				return {}
			end )
			:take(1)
			, {}
		)

		local observeTrash = rxTest
		:observe( catalog:indexStore():query{
			sql = [[SELECT docId, syncStatus FROM trashAssets ]],
			quiesceUntilUpToDate = true,
		})

		observeTrash:expectData { { trashAssetId2, 1 } }

		-- simulate trashAsset meta data synced, links are available
		rxTest:expectSingleValue (
			catalog:docStore():applyOverlay {
				_id = trashAssetId2,
				_serverProvidedMetadata = {
					base = "https://photos.adobe.io/v2/catalogs/...",
					created = "2017-08-17T11:57:14.343553Z",
					links = {
						["/rels/master"] = {
							content_type = "image/jpeg",
							href = "/v2/catalogs/...",
						},
					},
					revision_ids = {	-- Makes use of special case - see LrAssetUtils.isLatestMetadataUploaded()
										-- so to not have to guess the new revId.
					},
					updated = "2018-08-29T16:13:13.128931Z",
				},
			},
			':ignore:'
		)

		observeTrash:expectData { { trashAssetId2, 6 } }

		expected = {}
		expected[assetId] = true
		expected[trashAssetId2] = true
		-- syncStatusOfAssets is calmed, so give it a bit of time.
		observeSyncStatusOfAssets:expectDataWithinTime( 4, expected )

	end -- not desktop

end

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

function LrmCatSyncStatusTest.rxTestSyncStatusOfParentCollectionsOfAlbums( rxTest )

	local catalog = mockCatalogSetUp( rxTest )

	local albumId = KSIdGenerator.uuidInLowercaseHex()
	local albumId2 = KSIdGenerator.uuidInLowercaseHex()

	rxTest:setTimeLimit( 10 )

	-- create albums
	rxTest:expectSingleValue (
		catalog:docStore():applyOverlay {
			type = 'album',
			subtype = 'collection_set',
			name = 'test collection set',
			_id = albumId
		},
		':ignore:'
	)

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

	local observeAlbums = rxTest
		:observe( catalog:indexStore():query{
			sql = [[SELECT docId, syncStatus FROM albums ]],
			quiesceUntilUpToDate = true,
		})

	-- nothing synced yet
	observeAlbums:expectData { { albumId, 1 }, { albumId2, 1 } }

	local albumIds = { albumId2 }

	local observeSyncStatusOfAlbums =
		rxTest:observe( LrmCatSyncStatus.getSyncStatusOfParentCollectionOfAlbums( catalog, albumIds ) )

	local expected = {}

	expected[albumId] = false
	observeSyncStatusOfAlbums:expectData( expected )

	-- simulate album meta data synced, links are available
	rxTest:expectSingleValue (
		catalog:docStore():applyOverlay {
			_id = albumId,
			_serverProvidedMetadata = {
				links = {
					["/rels/web_album"] = {
						href = "https://lightroom.adobe.com/...",
					},
					["self"] = {
						href = "/v2/catalogs/...",
					},
				},
				revision_ids = {	-- Makes use of special case - see LrAssetUtils.isLatestMetadataUploaded()
									-- so to not have to guess the new revId.
				}
			}
		},
		':ignore:'
	)

	observeAlbums:expectData { { albumId2, 1 }, { albumId, 2 } }

	expected[albumId] = true
	observeSyncStatusOfAlbums:expectData( expected )
end

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

function LrmCatSyncStatusTest.rxTestAssetSyncStatus_NeedsAuxDownload( rxTest )

	local statusList = LrmCatSyncStatus.getAssetSyncStatusList()

	-- NeedsDevelopXmpDownload due to missing local auxiliary objects

	local _localOnly = makeLocalOnlyWithMasterAndXmpDevelop()

	local docValue = {
		_revId = "b2855707c97dc7ae52e40a7d179636f5",
		_localOnly = _localOnly,
		aux = {
			auxId1 = {
				digest = 'fakeAuxDigest'
			}
		},
		_serverProvidedMetadata = {
			base = "https://photos.adobe.io/v2/catalogs/a4be0605937b4bc5aa3092e51fd90dc4/",
			created = "2017-08-17T11:57:14.343553Z",
			links = {
				["/rels/master"] = {
					content_type = "image/jpeg",
					href = "/v2/catalogs/a4be0605937b4bc",
				},
				["/rels/xmp/develop"] = {
					href = "/v2/catalogs/a4be0605937b4bc",
				},
				["/rels/aux/auxId1"] = {
					href = "/v2/catalogs/a4be0605937b4bc",
				},
			},
			revision_ids = {
				"b2855707c97dc7ae52e40a7d179636f5",
				"54de3e54e717446c9de71b5639a0ae1a",
				"75171df99897de9e1b9819c2311c4a92",
				"2f87e2ffa1883247d7a5db457a7a3ba5",
			},
			updated = "2018-08-29T16:13:13.128931Z",
		},
		develop = {
			xmpCameraRaw = {
				sha256 = "deadbeaf",
			},
			masks = {
				auxId1 = {
					digest = 'fakeAuxDigest'
				}
			}
		}
	}

	rxTest:expectEqual( statusList.needsDevelopXmp, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- Downloaded Aux, Complete!

	_localOnly = makeLocalOnlyWithMasterAndXmpDevelop()

	WFTableMerge.applyOverlay( _localOnly, {
			files = {
				aux = {
					fakeAuxDigest = {
						relativePath = 'fake/path'
					}
				}
			}
		} )

	docValue._localOnly = _localOnly

	rxTest:expectEqual( statusList.complete, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

end

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

function LrmCatSyncStatusTest.rxTestAssetSyncStatus_PendingCommit( rxTest )

	local statusList = LrmCatSyncStatus.getAssetSyncStatusList()

	-- Pending commit

	local docValue = {
		_id = KSIdGenerator.uuidInLowercaseHex(),
		_labels = {
			[ '_pendingCommit' ] = true
		}
	}

	rxTest:expectEqual( statusList.pendingCommit, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

	-- Not pending commit

	docValue._labels = {}

	rxTest:expectEqual( statusList.needsMetadataUpload, LrmCatSyncStatus.getAssetSyncStatus( docValue ) )

end

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

return WFRxTestCase.wrapSuite( 'LrmCatSyncStatusTest', LrmCatSyncStatusTest )
