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

WFHttpClientTest.lua

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

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

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

	-- WF.core.logging
local WFLogger = require 'WFLogger'

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

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

	-- WF.web.http_client
local WFHttpClient = require 'WFHttpClient'


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

local logger = WFLogger 'WFHttpClientTest'

-- infer how far we are from the server by examing a local time
local localTimezone = KSDate.formatDateTimeAsLocal( 0 )
local multiplier = 1

if localTimezone == "2000-12-31T18:00:00" then
	multiplier = 2
end

local function getTimeout( nearbyTimeout )
	return nearbyTimeout * multiplier
end

local serverAddr = "kibler.corp.adobe.com:8888"

local sAttemptedToSetProxy = false

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

local WFHttpClientTest = {}

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

function WFHttpClientTest.tearDown( coveragePath )

	if coveragePath then
		
		local WFWorkerThread = require "WFWorkerThread"
		local HttpClientEP = WFWorkerThread( "WFRxHttpClientImp.lua" )
		HttpClientEP:requestP( 'DEBUG_dumpCoverageP', coveragePath )
		:runUntilDone()
		HttpClientEP:close()
		
	end

	if sAttemptedToSetProxy then
		WFHttpClient.setProxyCredentialsP( nil, nil )
		:runUntilDone()
		sAttemptedToSetProxy = false
	end
	
end

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

function WFHttpClientTest.testHeaderParser( test )
	
	local WFHttpClientHeaderParser = require 'WFHttpClientHeaderParser'
	assert( WFHttpClientHeaderParser, "Failed to requiredWFHttpClientHeaderParser" )
	local parseHeaderString = WFHttpClientHeaderParser.parseHeaderString
	
	local exampleInput = "Header-Name: Header value\r\nHeader2-Name:Header 2 Value\r\nHeader3-Name:	 	Header 3\r\n gots value\r\n\r\n"
	local headers = parseHeaderString( exampleInput )

	test:log( "Dumping headers:" )
	for k, v in pairs( headers ) do
		test:log( string.format( "%s = %s", k, v ) )
	end
	test:log( "Done." )
	assert( headers[ "Header-Name" ] == "Header value", "Basic space separated one-line header value" )
	assert( headers[ "Header2-Name" ] == "Header 2 Value", "Basic no-whitespace separated one-line header value" )
	assert( headers[ "Header3-Name" ] == "Header 3 gots value", "Multiple whitespaces, including tabs, with a multi-line header value" )
	
end

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

-- needs tests for the promise version

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

function WFHttpClientTest.testGetP( test )

	WFHttpClient.getP(
		'http://' .. serverAddr .. '/http-get-test-data.txt',
		{
			requestMethod = "GET",
			timeout = getTimeout( 10 ),
		})
		:whenResolvedDo( function( status, data )
		
			if status ~= 'data' then
				
				debug.print( "unexpected promise status ", status )
				test:notifyTestDidFail( "testGetP: unexpected promise result: " .. tostring( status ) .. " and data: '" .. tostring( data ) .. "'" )
		
			elseif tostring( data ) == "Hello, Wichita!\n" then

				test:notifyTestDidSucceed()

			else
			
				test:log( "HTTP GET returned incorrect data: '" .. tostring( data )  .. "' expected: 'Hello, Wichita!\n'" )
				debug.print( "HTTP GET returned incorrect data '" .. tostring( data )  .. "' expected: 'Hello, Wichita!\n'" )
				test:notifyTestDidFail("HTTP GET returned incorrect status: '" .. tostring( status ) .. "' and data: '" .. tostring( data ) .. "'" )
			
			end
					
		end )
		:runUntilDone()

	collectgarbage()
	collectgarbage()
	collectgarbage()

	test:waitForTestToFinish( getTimeout( 15 ) )

end

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

function WFHttpClientTest.testPutWithInvalidHeaderInfo( test )

	local resultP = WFHttpClient.putP(
		'http://' .. serverAddr .. '/AgHttpClientTest3.post.cgi',
		"Post Data",
		{
			requestMethod = "POST",
			timeout = getTimeout( 10 ),
			requestHeaders = {
				{ header = { invalid=true }, value = "invalid key, should log bug not fail", },
				{ header = "Header Has Invalid Value Will Log But Won't Fail", value = { invalid=true }, },
				{ header = "X-Unit-Test-Ignore-This", value = "ignore", },
			},
		})
		
		resultP:whenResolvedDo( function( status, data )
		
			if status ~= 'data' then
				test:notifyTestDidFail( "HTTP PUT returned incorrect status: " .. tostring( status ) .. " and data " .. tostring( data ))
			elseif tostring( data ) == "<p>Thanks. You said: Post Data</p>" then
				test:notifyTestDidSucceed()
			else
				test:notifyTestDidFail( "HTTP PUT returned incorrect status: " .. tostring( status ) .. " and data " .. tostring( data ) )
			end
				
		end )
		:runUntilDone()

	test:waitForTestToFinish( getTimeout( 20 ) )

	return resultP
	
end

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

local function testPutP( test, putData, offset, length, dataFormat )

	local resultP = WFHttpClient.putP(
		'http://' .. serverAddr .. '/AgHttpClientTest3.post.cgi',
		putData,
		{
			requestMethod = "POST",
			timeout = getTimeout( 10 ),
			offset = offset,
			length = length,
			dataFormat = dataFormat,
		})
		
		resultP:whenResolvedDo( function( status, data )
		
			local expectedData = "<p>Thanks. You said: Post Data</p>"

			if status ~= 'data' then
				
				debug.print( "unexpected promise status ", status )
				test:notifyTestDidFail( "HTTP PUT returned incorrect status: " .. tostring( status ) .. " and data: '" .. tostring( data ) .. "'" )
		
			elseif tostring( data ) == expectedData then

				test:notifyTestDidSucceed()

			else
			
				test:log( "HTTP GET returned incorrect data: '" .. tostring( data )  .. "' expected: '" .. expectedData .. "'" )
				debug.print( "HTTP GET returned incorrect data '" .. tostring( data )  .. "' expected: '" .. expectedData .. "'" )
				test:notifyTestDidFail("HTTP GET returned incorrect status: " .. tostring( status ) .. " and data: '" .. tostring( data ) .. "' expected: '" .. expectedData ..   "'" )
			
			end
				
		end )
		:runUntilDone()

	collectgarbage()
	collectgarbage()
	collectgarbage()

	test:waitForTestToFinish( getTimeout( 20 ) )

	return resultP
	
end

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

function WFHttpClientTest.testStringPutP( test )
	testPutP( test, "Post Data" )
end

function WFHttpClientTest.testKSDataPutP( test )
	local KSData = require 'KSData'
	testPutP( test, KSData.make( "Post Data" ) )
end

-- TODO [arahn 2013-08-05]: Optimize the native impl for win32 and android for these.
-- These tests only pass because of a lua emulation.  Look for supportsFileAndOffset
-- in WFHttpClientImp.lua

function WFHttpClientTest.testStringLengthOffsetPutP( test )
	testPutP( test, "Twenty Ignored CharsPost DataThirty More Ignored Characters", 21, 9 )
end

function WFHttpClientTest.testKSDataLengthOffsetPutP( test )
	local KSData = require 'KSData'
	testPutP( test, KSData.make( "Twenty Ignored CharsPost DataThirty More Ignored Characters" ), 21, 9 )
end

function WFHttpClientTest.testStringOffsetPutP( test )
    testPutP( test, "Twenty Ignored CharsPost Data", 21 )
end

function WFHttpClientTest.testKSDataOffsetPutP( test )
    local KSData = require 'KSData'
    testPutP( test, KSData.make( "Twenty Ignored CharsPost Data" ), 21 )
end

local function testFilenamePutP( test, data, offset, length )

	local testFile = KSPathUtils.child( KSPathUtils.getStandardFilePath( "temp" ), "WFHttpClientTest.testFilenamePutP.txt" )
	testFile = WFFileUtils.ensureFileNameIsUnique( testFile )

	local file = io.open( testFile, "w" )
	file:write( data )
	file:close()
	
	testPutP( test, testFile, offset, length, "filePath" )
		:whenResolvedDo( function()
			WFFileUtils.delete( testFile )
		end )
		:runUntilDone()

end

function WFHttpClientTest.testFilenamePutP( test )
	testFilenamePutP( test, "Post Data" )
end

function WFHttpClientTest.testFilenameLengthOffsetPutP( test )
	testFilenamePutP( test, "Twenty Ignored CharsPost DataThirty More Ignored Characters", 21, 9 )
end


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

function WFHttpClientTest.testGetPConverts404ToError( test )

	WFAsyncTestCase( test, getTimeout( 15 ) )

		:waitFor( function()
		
			return WFHttpClient.getP(
				'http://' .. serverAddr .. '/http-get-test-data.txtx',
				{
					timeout = getTimeout( 10 ),
					maxAttempts = 1,
				})
			
		end )
	
		:whenResolvedDo( function( status, data, headers )
		
			if status ~= 'error' then
				error( "Unexpected status " .. tostring( status ) )
			end
			
			if not( headers and headers.status_code == '404' ) then
				error( "HTTP GET returned unexpected status" .. tostring( data ) .. tostring( headers ) )
			end
			
		end )
end

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

function WFHttpClientTest.testGetPDisableErrorConversion( test )

	WFAsyncTestCase( test, getTimeout( 15 ) )

		:waitFor( function()
		
			return WFHttpClient.getP(
				'http://' .. serverAddr .. '/http-get-test-data.txtx',
				{
					timeout = getTimeout( 10 ),
					maxAttempts = 1,
					returnHttpErrorsAsData = true,
				})
		
		end )
		
		:whenResolvedDo( function( status, data, headers )
		
			if status ~= 'data' then
				error( "Unexpected status " .. tostring( status ) )
			end
			
			if not( headers and headers.status_code == '404' ) then
				error( "HTTP GET returned unexpected status" .. data .. headers )
			end
			
		end )

end

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

function WFHttpClientTest.testGetPWithRequestHeader( test )

	WFAsyncTestCase( test, getTimeout( 15 ) )
	
		:waitFor( function()
		
			return WFHttpClient.getP(
				'http://' .. serverAddr .. '/http-get-test-data.txt',
				{
					timeout = getTimeout( 10 ),
					requestHeaders = {
						{ header = 'Accept', value = 'application/json' },
					}
				} )
			
		end )
			
		:whenResolvedDo( function( status, data, headers )
		
			if status ~= 'data' then
				error( "Unexpected status " .. tostring( status ) )
			end
			
			assert( tostring( data ) == "Hello, Wichita!\n",
					"HTTP GET returned incorrect data: '" .. tostring( data ) .. "' expected: 'Hello, Wichita!\n'" )
		
		end )

end

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

function WFHttpClientTest.testGetPWithReadResponseHeaders( test )

	WFAsyncTestCase( test, getTimeout( 15 ) )
	
		:waitFor( function()
		
			return WFHttpClient.getP(
				'http://' .. serverAddr .. '/http-get-test-data.txt',
				{
					timeout = getTimeout( 10 ),
				})
		
		end )
		:whenResolvedDo( function( status, data, headers )
			
			if status ~= 'data' then
				error( "Unexpected status " .. tostring( status ) )
			end
			
			logger:debugf( '%s', headers )

			if headers[ 'Content-Type' ] ~= "text/plain" then
				error( "Unexpected Content-Type: " .. headers[ 'Content-Type' ] )
			end
			
			if headers.status_code ~= '200' then
				error( "Unexpected status_code: " .. headers.status_code )
			end
			
			assert( tostring( data ) == "Hello, Wichita!\n",
					"HTTP GET returned incorrect data: '" .. tostring( data ) .. "' expected: 'Hello, Wichita!\n'" )
		
		end )

end

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

function WFHttpClientTest.testSetProxyCredentials( test )

	WFAsyncTestCase( test, getTimeout( 15 ) )
	
		:waitFor( function()

			sAttemptedToSetProxy = true

			return WFHttpClient.setProxyCredentialsP( "joe", "p" )

		end )
		:whenResolvedDo( function( status, data )
			
			if status ~= 'data' then
				error( "Unexpected status " .. tostring( status ) )
			end

			assert( data )

		end )

end

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

function WFHttpClientTest.test_responseMatchesContentType( test )
	local f = WFHttpClient.responseMatchesContentType
	local k = "Content-Type"
	assert( f( { [ k ] = "video/quicktime" }, "video/quicktime" ) == true )
	assert( f( { [ k ] = "application/binary;params=params33" }, "application/binary" ) == true )
	assert( f( { [ k ] = "application/binaryJosh" }, "application/binary" ) == false )
	assert( f( { }, "application/binary" ) == false )
	assert( f( nil, "application/binary" ) == false )
end

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

function WFHttpClientTest.test_responseContentTypeIsJson( test )
	local f = WFHttpClient.responseContentTypeIsJson
	local k = "Content-Type"
	assert( f( { [ k ] = "application/json" } ) == true )
	assert( f( { [ k ] = "application/json;params=params33" } ) == true )
	assert( f( { [ k ] = "application/jsonHaHaJustKiddingNoImNotJSON" } ) == false )
	assert( f( { } ) == false )
	assert( f( nil ) == false )
end

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

return WFHttpClientTest
