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

WFCatImportDuplicateFilesTest.lua

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

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

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

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

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

	-- WF.oz_client.managed_catalog
local WFCatDelete = require 'WFCatDelete'
local WFCatImportDuplicateFiles = require 'WFCatImportDuplicateFiles'
local WFCatImportConstants = require 'WFCatImportConstants'
local WFCatMockCatalog = require 'WFCatMockCatalog'

	-- WF.core.os
local KSPathUtils = require 'KSPathUtils'
local WFPlatformInfo = require "WFPlatformInfo"

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

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

local createFakeWFCatalog = WFCatMockCatalog.createFakeWFCatalog
local randomDocId = 'd93e8d7a63094d69861ee8983e950850'
local randomDocIdA = 'd93e8d7a63094d69861ee8983e95085a'
local randomDocIdB = 'd93e8d7a63094d69861ee8983e95085b'
local randomDocIdC = 'd93e8d7a63094d69861ee8983e95085c'
local randomDocIdD = 'd93e8d7a63094d69861ee8983e95085d'
local randomDocIdE = 'd93e8d7a63094d69861ee8983e95085e'

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

local WFCatImportDuplicateFilesTest = {}

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

local sCoveragePath

function WFCatImportDuplicateFilesTest.setUp( inCoveragePath )
	sCoveragePath = inCoveragePath
end

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

local localOnlyHash = { 
	files = { 
		original = { 
			master = { 
				relativePath = 'fake/path', 
				volumeGuid = 'bar'
			}
		}
	}
}

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

function WFCatImportDuplicateFilesTest.rxTestBasic( rxTest )

	rxTest:setTimeLimit( 5 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()
	rxTest:expectSingleValue(

		docStore:setDocValue {
				_id = randomDocId,
				type = 'asset',
				subtype = 'image',
				captureDate = '2012-08-15T15:22:29',
				_localOnly = localOnlyHash,
				importSource = {
					importTimestamp = '2012-08-15T15:22:29Z',
					fileName = "Image.jpg",
					originalHeight = 150,
					originalWidth = 100,
					fileSize = 12345,
				}
			},

		':ignore:' )
	
	rxTest:expectSingleValue(
		WFCatImportDuplicateFiles.findExistingFiles(fakeWFCatalog, { {
			id_local = 'id1',
			fileName = 'Image.jpg',
			fileSize = 12345,
			
		} } ),
		{
			id1 = WFCatImportConstants.kMaybeMatchesExistingFile
		}
	)

	rxTest:expectSingleValue(
		WFCatImportDuplicateFiles.findExistingFiles(fakeWFCatalog, { {
			id_local = 'id1',
			fileName = 'missing file',
			fileSize = 12345
			
		} } ),
		{
			id1 = WFCatImportConstants.kNotSimilarToAnyExistingFile
		}
	)

end

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

function WFCatImportDuplicateFilesTest.rxTestIsDuplicate( rxTest )

	local possibleMatches = {
			['file1.jpg'] = { [123] = true },
			['file2.jpg'] = { [234] = true, [345] = true },
		}

	assert( WFCatImportDuplicateFiles.isDuplicateFile( {
		fileName = 'file1.jpg',
		fileSize = 123,
	}, possibleMatches ) == true )

	assert( WFCatImportDuplicateFiles.isDuplicateFile( {
		fileName = 'file2.jpg',
		fileSize = 345,
	}, possibleMatches ) == true )

	assert( WFCatImportDuplicateFiles.isDuplicateFile( {
		fileName = 'file2.jpg',
		fileSize = 123,
	}, possibleMatches ) == false )

	assert( WFCatImportDuplicateFiles.isDuplicateFile( {
		fileName = 'missing.jpg',
		fileSize = 123,
	}, possibleMatches ) == false )

	assert( WFCatImportDuplicateFiles.isDuplicateFile( {
		fileName = 'file1.jpg',
		fileSize = 123,
	}, nil ) == false )

end

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

function WFCatImportDuplicateFilesTest.rxTestFindExistingFileBySha256( rxTest )

	rxTest:setTimeLimit( 5 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	rxTest:finally( function()
		fakeWFCatalog:indexStore():DEBUG_dumpCoverage( sCoveragePath )
			-- Need to dump coverage here in order to get coverage of the
			-- function WFCatImportDuplicateFiles.generateIndexSchema.
	end )

	rxTest:expectSingleValue(
		docStore:setDocValue {
				_id = randomDocId,
				type = 'asset',
				subtype = 'image',
				captureDate = '2012-08-15T15:22:29',
				_localOnly = localOnlyHash,
				importSource = {
					importTimestamp = '2012-08-15T15:22:29Z',
					sha256 = '12345678',
					fileName = "Image.jpg",
					originalHeight = 150,
					originalWidth = 100,
				}
			},
		':ignore:' )

	rxTest:expectSingleValue(
		WFCatImportDuplicateFiles.findExistingFileBySha256( fakeWFCatalog:docStore(), fakeWFCatalog:indexStore(), '12345678' ),
		randomDocId, nil
	)

	rxTest:expectSingleValue(
		WFCatImportDuplicateFiles.findExistingFileBySha256( fakeWFCatalog:docStore(), fakeWFCatalog:indexStore(), '11111111' ),
		nil, nil
	)

end

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

local function sawSha256DeletionUpdateFor( assetId )

	return WFCatImportDuplicateFiles._testOnly_getSha256ChangesFeed()
			:filter( 
				function( changeType, docId ) 

					return changeType == "deleted" and docId == assetId

				end )
			:take( 1 )
			:map( function() return true end )

end

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

if WFPlatformInfo.isDesktop then

-- This test uses WFCatDelete which is a desktop specific file.

function WFCatImportDuplicateFilesTest.rxTestFindExistingFileBySha256HandlesDeleteOfDuplicate( rxTest )

	-- This test creates two assets that have the same sha256 value.  Currently
	-- Oz always allows duplicates but Nimbus tries it's best to not allow them.

	-- It then verifies that you can still detect there is an asset with the
	-- sha256 hash value after deleting one of them.

	rxTest:setTimeLimit( 5 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	local assetIdA = randomDocIdA
	local assetIdB = randomDocIdB

	rxTest:expectSingleValue(
		docStore:setDocValue {
				_id = assetIdA,
				type = 'asset',
				subtype = 'image',
				captureDate = '2012-08-15T15:22:29',
				_localOnly = localOnlyHash,
				importSource = {
					importTimestamp = '2012-08-15T15:22:29Z',
					sha256 = '12345678',
					fileName = "Image.jpg",
					originalHeight = 150,
					originalWidth = 100,
				}
			},
		':ignore:' )

	-- create another asset that has an image with the same sha256

	rxTest:expectSingleValue(
		docStore:setDocValue {
				_id = assetIdB,
				type = 'asset',
				subtype = 'image',
				captureDate = '2012-08-15T15:22:29',
				_localOnly = localOnlyHash,
				importSource = {
					importTimestamp = '2012-08-15T15:22:29Z',
					sha256 = '12345678',
					fileName = "Image2.jpg",
					originalHeight = 250,
					originalWidth = 200,
				}
			},
		':ignore:' )

	rxTest:expectSingleValue(
		WFCatImportDuplicateFiles.findExistingFileBySha256( fakeWFCatalog:docStore(), fakeWFCatalog:indexStore(), '12345678' ),
		assetIdB, nil
	)

	rxTest:expectOnlyCompleted( WFCatDelete.deleteAssets( fakeWFCatalog, { assetIdB } ) )

	rxTest:expectSingleValue( sawSha256DeletionUpdateFor( assetIdB ), true )

	rxTest:expectSingleValue(
		WFCatImportDuplicateFiles.findExistingFileBySha256( fakeWFCatalog:docStore(), fakeWFCatalog:indexStore(), '12345678' ),
		assetIdA, nil
	)

	rxTest:expectOnlyCompleted( WFCatDelete.deleteAssets( fakeWFCatalog, { assetIdA } ) )

	rxTest:expectSingleValue( sawSha256DeletionUpdateFor( assetIdA ), true )

	rxTest:expectSingleValue(
		WFCatImportDuplicateFiles.findExistingFileBySha256( fakeWFCatalog:docStore(), fakeWFCatalog:indexStore(), '12345678' ),
		nil, nil
	)

end

end

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

function WFCatImportDuplicateFilesTest.rxTestFindExistingFileBySha256Recent( rxTest )

	rxTest:setTimeLimit( 5 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	rxTest:expectSingleValue(
		WFCatImportDuplicateFiles.findExistingFileBySha256( fakeWFCatalog:docStore(), fakeWFCatalog:indexStore(), '12345678' ),
		nil, nil
	)

	WFCatImportDuplicateFiles.addRecentSha256( fakeWFCatalog:docStore(), fakeWFCatalog:indexStore(), '12345678', randomDocId )

	rxTest:expectSingleValue(
		WFCatImportDuplicateFiles.findExistingFileBySha256( fakeWFCatalog:docStore(), fakeWFCatalog:indexStore(), '12345678' ),
		randomDocId, nil
	)

end

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

function WFCatImportDuplicateFilesTest.rxTestFindExistingFilesBySha256( rxTest )

	rxTest:setTimeLimit( 15 )

	local testDir = KSPathUtils.child( KSPathUtils.getStandardFilePath( 'testdata' ), 'assets' )
	local testFile = KSPathUtils.child( testDir, '1.5MB.jpg' )

	local expectedSHA256 = WFCrypto.digestFile( testFile, 'SHA256' )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	rxTest:expectSingleValue(
		docStore:setDocValue {
				_id = randomDocIdA,
				type = 'asset',
				subtype = 'image',
				captureDate = '2012-08-15T15:22:29',
				_localOnly = localOnlyHash,
				importSource = {
					importTimestamp = '2012-08-15T15:22:29Z',
					sha256 = '12345678',
					fileName = "Image.jpg",
					originalHeight = 150,
					originalWidth = 100,
				}
			},
		':ignore:' )

	rxTest:expectSingleValue(
		docStore:setDocValue {
				_id = randomDocIdB,
				type = 'asset',
				subtype = 'image',
				captureDate = '2012-08-15T15:22:29',
				_localOnly = localOnlyHash,
				importSource = {
					importTimestamp = '2012-08-15T15:22:29Z',
					sha256 = expectedSHA256,
					fileName = "Image.jpg",
					originalHeight = 150,
					originalWidth = 100,
				}
			},
		':ignore:' )

	rxTest:expectSingleValue(
		docStore:setDocValue {
				_id = randomDocIdC,
				type = 'asset',
				subtype = 'image',
				captureDate = '2012-08-15T15:22:29',
				_localOnly = localOnlyHash,
				_spaceId = 'space_id',
				importSource = {
					importTimestamp = '2012-08-15T15:22:29Z',
					sha256 = '10101010',
					fileName = "Image.jpg",
					originalHeight = 150,
					originalWidth = 100,
				}
			},
		':ignore:' )

	rxTest:expectSingleValue(
		WFCatImportDuplicateFiles.findExistingFilesBySha256( fakeWFCatalog, {
			{ id_local = 1, sha256 = '12345678' },
			{ id_local = 2, sha256 = 'missing sha' },
			{ id_local = 3, filePath = testFile, sha256 = expectedSHA256 },
			{ id_local = 4, filePath = testFile, sha256 = 'missing sha' },
			{ id_local = 5, filePath = testFile },
			{ id_local = 6, sha256 = '10101010' },
		} )
		:map( function( arr )
			-- Make a predictable arrangement of the array
			local ret = {}
			for _, val in ipairs( arr ) do
				ret[ val.id_local ] = val
			end
			
			return ret
		end ),
		{
			{ id_local = 1, isDuplicate = WFCatImportConstants.kMatchesExistingFile, sha256 = '12345678' },
			{ id_local = 2, isDuplicate = WFCatImportConstants.kNotSimilarToAnyExistingFile, sha256 = 'missing sha' },
			{ id_local = 3, isDuplicate = WFCatImportConstants.kMatchesExistingFile, sha256 = expectedSHA256 },
			{ id_local = 4, isDuplicate = WFCatImportConstants.kNotSimilarToAnyExistingFile, sha256 = 'missing sha' },
			{ id_local = 5, isDuplicate = WFCatImportConstants.kMatchesExistingFile, sha256 = expectedSHA256 },
			{ id_local = 6, isDuplicate = WFCatImportConstants.kNotSimilarToAnyExistingFile, sha256 = '10101010' },
		}
	)

end

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

function WFCatImportDuplicateFilesTest.rxTestAssetListInfo( rxTest )

	rxTest:setTimeLimit( 5 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()
	rxTest:expectSingleValue(

		docStore:setDocValue {
				_id = randomDocIdA,
				type = 'asset',
				subtype = 'image',
				captureDate = '2012-08-01T15:22:29',
				_localOnly = localOnlyHash,
				importSource = {
					importTimestamp = '2012-08-15T15:22:29Z',
					fileName = "Image1.jpg",
					originalHeight = 150,
					originalWidth = 100,
					fileSize = 123,
				}
			},

		':ignore:' )

	rxTest:expectSingleValue(

		docStore:setDocValue {
				_id = randomDocIdB,
				type = 'asset',
				subtype = 'image',
				captureDate = '2012-08-02T15:22:29',
				_localOnly = localOnlyHash,
				importSource = {
					importTimestamp = '2012-08-15T15:22:29Z',
					fileName = "Image1.jpg",
					originalHeight = 150,
					originalWidth = 100,
					fileSize = 234,
				}
			},

		':ignore:' )

	rxTest:expectSingleValue(

		docStore:setDocValue {
				_id = randomDocIdC,
				type = 'asset',
				subtype = 'image',
				captureDate = '2012-08-03T15:22:29',
				_localOnly = localOnlyHash,
				importSource = {
					importTimestamp = '2012-08-15T15:22:29Z',
					fileName = "Image1.jpg",
					originalHeight = 150,
					originalWidth = 100,
					fileSize = 234,
				}
			},

		':ignore:' )

	rxTest:expectSingleValue(

		docStore:setDocValue {
				_id = randomDocIdD,
				type = 'asset',
				subtype = 'image',
				captureDate = '2012-08-04T15:22:29',
				_localOnly = localOnlyHash,
				importSource = {
					importTimestamp = '2012-08-15T15:22:29Z',
					fileName = "Image2.jpg",
					originalHeight = 150,
					originalWidth = 100,
					fileSize = 345,
				}
			},

		':ignore:' )

	rxTest:expectSingleValue(

		docStore:setDocValue {
				_id = randomDocIdE,
				type = 'asset',
				subtype = 'image',
				captureDate = '2012-08-05T15:22:29',
				_localOnly = { files = { proxy = { ['2560'] = { relativePath = 'dummy', volumeGuid = 'foo' } } } } ,
				importSource = {
					importTimestamp = '2012-08-15T15:22:29Z',
					fileName = "Image2.jpg",
					originalHeight = 150,
					originalWidth = 100,
					fileSize = 456,
					willNotUploadMaster = true,
				}
			},

		':ignore:' )

	rxTest:observe( WFCatImportDuplicateFiles.assetListInfo( fakeWFCatalog ) )
		:expectData( {
			['Image1.jpg'] = {
				[ 123 ] = {
					[ '2012-08-01T15:22:29' ] = true,
				},
				
				[ 234 ] = {
					[ '2012-08-02T15:22:29' ] = true,
					[ '2012-08-03T15:22:29' ] = true,
				},
			},
			
			['Image2.jpg'] = {
				[ 345 ] = {
					[ '2012-08-04T15:22:29' ] = true,
				},
			},
		} )

end

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

function WFCatImportDuplicateFilesTest.rxTestIndexingToleratesNilNameAndPath( rxTest )

	rxTest:setTimeLimit( 5 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()

	rxTest:expectSingleValue(
		docStore:setDocValue {
				_id = randomDocId,
				type = 'asset',
				subtype = 'image',
				captureDate = '2012-08-15T15:22:29',
				_localOnly = localOnlyHash,
				importSource = {
					importTimestamp = '2012-08-15T15:22:29Z',
					sha256 = '12345678',
					originalHeight = 150,
					originalWidth = 100,
				}
			},
		':ignore:' )

	rxTest:expectSingleValue(
		WFCatImportDuplicateFiles.findExistingFileBySha256( fakeWFCatalog:docStore(), fakeWFCatalog:indexStore(), '12345678' ),
		randomDocId, nil
	)

end

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

function WFCatImportDuplicateFilesTest.rxTestFindUploadedAssetWithSha256( rxTest )

	rxTest:setTimeLimit( 500 )

	local fakeWFCatalog = createFakeWFCatalog( rxTest )

	local docStore = fakeWFCatalog:docStore()
	local indexStore = fakeWFCatalog:indexStore()

	local assetId1 = '11118d7a63094d69861ee8983e951111'
	local assetId2 = '22228d7a63094d69861ee8983e952222'
	local assetId3 = '33338d7a63094d69861ee8983e953333'
	local assetId4 = '44448d7a63094d69861ee8983e954444'

	rxTest:expectSingleValue(
		docStore:setDocValue {
				_id = assetId1,
				type = 'asset',
				subtype = 'image',
				captureDate = '2012-08-15T15:22:29',
				_localOnly = localOnlyHash,
				importSource = {
					fileName = "Image2.jpg",
					importTimestamp = '2012-08-15T15:22:29Z',
					sha256 = '11111111',
					originalHeight = 150,
					originalWidth = 100,
				}
			},
		':ignore:' )

	local assetsInImportSourceIndex = rxTest:observe(
		indexStore:query { sql = [[SELECT assetId FROM importSource]] }
	)

	assetsInImportSourceIndex:expectData { { assetId1 } }

	-- hint not AssetId1 which is the doc we just added.
	rxTest:expectSingleValue(
		WFCatImportDuplicateFiles.findUploadedAssetWithSha256HintNotDocId( fakeWFCatalog, '11111111', assetId1 ),
		nil
	)

	-- Now try without the hint, since the links are not provided we should still get nil back
	rxTest:expectSingleValue(
		WFCatImportDuplicateFiles.findUploadedAssetWithSha256HintNotDocId( fakeWFCatalog, '11111111', nil ),
		nil
	)

	-- Add asset2 with links to make it look like it has uploaded assets

	rxTest:expectSingleValue(
		docStore:setDocValue {
				_id = assetId2,
				type = 'asset',
				subtype = 'image',
				captureDate = '2012-08-15T15:22:30',
				_localOnly = localOnlyHash,
				_serverProvidedMetadata = {
					links = {
						[ '/rels/master' ] = { href = '/href/bogus/master' },
						[ '/rels/proxy_type/2560' ] = { href = '/href/bogus/proxy' },
						[ '/rels/rendition_type/640' ] = { href = '/href/bogus/rendition' },
						[ '/rels/xmp' ] = { href = '/href/bogus/xmp' },
					}
				},
				importSource = {
					fileName = "Image3.jpg",
					fileSize = 333,
					importTimestamp = '2012-08-15T15:22:30Z',
					sha256 = '22222222',
					originalHeight = 150,
					originalWidth = 100,
				}
			},
		':ignore:' )

	assetsInImportSourceIndex:expectData { { assetId1 }, { assetId2 } }

	-- Should find assetId2 as it has links and is uploaded, proxy link indicator should be false as it has a master link and xmp link.

	rxTest:expectSingleValue(
		WFCatImportDuplicateFiles.findUploadedAssetWithSha256HintNotDocId( fakeWFCatalog, '22222222', assetId1 ),
		assetId2, false
	)

	-- Not a typical pattern that will happen, but test it anyways, as binary uploader won't call this for already uploaded assets.

	rxTest:expectSingleValue(
		WFCatImportDuplicateFiles.findUploadedAssetWithSha256HintNotDocId( fakeWFCatalog, '22222222', assetId2 ),
		nil
	)

	-- Add asset3 with only a proxy links to make it look like it was uploaded from Lightroom Desktop.

	rxTest:expectSingleValue(
		docStore:setDocValue {
				_id = assetId3,
				type = 'asset',
				subtype = 'image',
				captureDate = '2012-08-15T15:22:33',
				_localOnly = localOnlyHash,
				_serverProvidedMetadata = {
					links = {
						[ '/rels/proxy_type/2560' ] = { href = '/href/bogus/testing4' },
						[ '/rels/rendition_type/640' ] = { href = '/href/bogus/rendition' },
						[ '/rels/xmp' ] = { href = '/href/bogus/xmp' },
					}
				},
				importSource = {
					fileName = "Image3.jpg",
					importTimestamp = '2012-08-15T15:22:33Z',
					sha256 = '33333333',
					originalHeight = 150,
					originalWidth = 100,
				}
			},
		':ignore:' )

	assetsInImportSourceIndex:expectData { { assetId1 }, { assetId2 }, { assetId3 } }

	-- Should find assetId3 as it has a proxy link, proxy link indicator should be true as there is no master link.

	rxTest:expectSingleValue(
		WFCatImportDuplicateFiles.findUploadedAssetWithSha256HintNotDocId( fakeWFCatalog, '33333333', assetId1 ),
		assetId3, true
	)

	-- Add asset4 without an xmp or rendition link to validate we don't think it is uploaded yet.

	rxTest:expectSingleValue(
		docStore:setDocValue {
				_id = assetId4,
				type = 'asset',
				subtype = 'image',
				captureDate = '2012-08-15T15:22:33',
				_localOnly = localOnlyHash,
				_serverProvidedMetadata = {
					links = {
						[ '/rels/master' ] = { href = '/href/bogus/master' },
						[ '/rels/proxy_type/2560' ] = { href = '/href/bogus/testing4' }
					}
				},
				importSource = {
					fileName = "Image3.jpg",
					importTimestamp = '2012-08-15T15:22:33Z',
					sha256 = '44444444',
					originalHeight = 150,
					originalWidth = 100,
				}
			},
		':ignore:' )

	assetsInImportSourceIndex:expectData { { assetId1 }, { assetId2 }, { assetId3 }, { assetId4 } }

	-- Should not find assetId4 as it doesn't have an xmp or rendition link yet.

	rxTest:expectSingleValue(
		WFCatImportDuplicateFiles.findUploadedAssetWithSha256HintNotDocId( fakeWFCatalog, '44444444', assetId1 ),
		nil
	)

end

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

return WFRxTestCase.wrapSuite( WFCatImportDuplicateFilesTest )
