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

WFRtmfpPartnerSessionTest.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2015 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.rtmfp
local WFRtmfpClient = require 'WFRtmfpClient'
local WFRtmfpPartnerSession = require 'WFRtmfpPartnerSession'

	-- WF.core
local KSJsonDecoder = require 'KSJsonDecoder'
local KSJsonEncoder = require 'KSJsonEncoder'
local KSData = require 'KSData'
local WFRxTestCase = require 'WFRxTestCase'
local WFRxRunUntilDone = require 'WFRxRunUntilDone'


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

local WFRtmfpPartnerSessionTest = {}

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

local testWaitTime = 60
local dataTransitTime = 10 -- Time to wait for a response from the RTMFP Peer

local runUntilDone = WFRxRunUntilDone

-- RTMFP Echo Server. Lives on sj1010122073213.corp.adobe.com

local serverAddr = "sj1010122073213.corp.adobe.com"
local serverPort = 1979
local _rtmfpClient = false

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

function WFRtmfpPartnerSessionTest.setUp()

	if not _rtmfpClient then
		_rtmfpClient = WFRtmfpClient()
	end

end

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

function WFRtmfpPartnerSessionTest.tearDownClass()

	if _rtmfpClient then
		_rtmfpClient:close()
	end

end

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

function WFRtmfpPartnerSessionTest.rxTestOpenSessionWithPartner( rxTest )

	rxTest:setTimeLimit( testWaitTime )

	local _rtmfpPartnerSession = WFRtmfpPartnerSession( _rtmfpClient )

	rxTest:log( "openSessionWithPartner" )

	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:openSessionWithPartner( serverAddr, serverPort ) )

	rxTest:log( "waiting for shutdown" )

	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:shutdown() )

end

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

function WFRtmfpPartnerSessionTest.rxTestOpenSessionVerify( rxTest )

	rxTest:setTimeLimit( testWaitTime )

	local _rtmfpPartnerSession = WFRtmfpPartnerSession( _rtmfpClient )
	local hitMe = false
	
	local verifyResponseFunction = function() 
		hitMe = true	
		return true 
	end
	
	rxTest:log( "openSessionWithPartner" )

	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:openSessionWithPartner( serverAddr, serverPort, nil, verifyResponseFunction ) )

	rxTest:expectEqual( true, hitMe )

	rxTest:log( "waiting for shutdown" )

	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:shutdown() )

end

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

function WFRtmfpPartnerSessionTest.rxTestOpenSessionVerifyFails( rxTest )

	rxTest:setTimeLimit( testWaitTime )

	local _rtmfpPartnerSession = WFRtmfpPartnerSession( _rtmfpClient )
	local hitMe = false
	
	local verifyResponseFunction = function() 
		hitMe = true	
		return false 
	end
	
	rxTest:log( "openSessionWithPartner" )

	rxTest:expectOnlyError( _rtmfpPartnerSession:openSessionWithPartner( serverAddr, serverPort, nil, verifyResponseFunction ), ':ignore:')

	rxTest:expectEqual( true, hitMe )

	rxTest:log( "waiting for shutdown" )

	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:shutdown() )

end

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

function WFRtmfpPartnerSessionTest.rxTestOpenSessionWithPartnerFail( rxTest )

	rxTest:setTimeLimit( testWaitTime )

	local _rtmfpPartnerSession = WFRtmfpPartnerSession( _rtmfpClient )
	
	rxTest:log( "openSessionWithPartner" )
	
	rxTest:expectOnlyError( _rtmfpPartnerSession:openSessionWithPartner( serverAddr, serverPort, 'BAD SECRET' ), ':ignore:')

	rxTest:log( "waiting for shutdown" )

	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:shutdown() )

end


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

local function decodeRequest( rtmfpRequest )

	local json = tostring( rtmfpRequest:getData() )
	return KSJsonDecoder.decodeWebSafeJson( json )

end

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

function WFRtmfpPartnerSessionTest.rxTestOpenReturnSessionWithPartner( rxTest )

	rxTest:setTimeLimit( testWaitTime )

	local _rtmfpPartnerSession = WFRtmfpPartnerSession( _rtmfpClient )
	
	local _flow = _rtmfpClient:openFlow( serverAddr, serverPort )

	local _rtmfpRequest = false

	-- A special message that tells the RTMFP Peer to attempt to
	-- initialize a new partner session with us.

 	local connectCommand = {
		command = "HANDSHAKE_PLEASE",
	}

	local connectToMe = KSJsonEncoder.encodeOzUploadJson( connectCommand )
	
	local clientStream = _rtmfpClient:listen()
	
	local handshakeListener = rxTest:observe( clientStream
	
		:map( function( rtmfpRequest )
			
			local cmdInfo = decodeRequest( rtmfpRequest )
				
			if cmdInfo.command == 'HELLO' then
				_rtmfpRequest = rtmfpRequest
			end
				
			return cmdInfo.command
				
		end ) )
	
	-- Send off the special message. Once it receives this message,
	-- it will attempt to initalize a new session with us by
	-- sending a 'HELLO' message, which is the first part of the
	-- session partner handshake.
	
	_flow:send( connectToMe ):call( runUntilDone )
	
	handshakeListener:expectDataWithinTime( dataTransitTime, 'HELLO' )
	
	local cmdInfo = decodeRequest( _rtmfpRequest )
	
	rxTest:log( "openReturnSessionWithPartner" )
	
	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:openReturnSessionWithPartner( _rtmfpRequest, cmdInfo ) )

	rxTest:log( "waiting for shutdown" )

	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:shutdown() )

end

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

function WFRtmfpPartnerSessionTest.rxTestRejectReturnSessionWithPartner( rxTest )

	rxTest:setTimeLimit( testWaitTime )

	local _rtmfpPartnerSession = WFRtmfpPartnerSession( _rtmfpClient )
	
	local _flow = _rtmfpClient:openFlow( serverAddr, serverPort )

	local _rtmfpRequest = false

	-- A special message that tells the RTMFP Peer to attempt to
	-- initialize a new partner session with us.

 	local connectCommand = {
		command = "HANDSHAKE_PLEASE",
	}

	local connectToMe = KSJsonEncoder.encodeOzUploadJson( connectCommand )
	
	local clientStream = _rtmfpClient:listen()
	
	local handshakeListener = rxTest:observe( clientStream
	
		:map( function( rtmfpRequest )
			
			local cmdInfo = decodeRequest( rtmfpRequest )
				
			if cmdInfo.command == 'HELLO' then
				_rtmfpRequest = rtmfpRequest
			end
				
			return cmdInfo.command
				
		end ) )
	
	_flow:send( connectToMe ):call( runUntilDone )
	
	handshakeListener:expectDataWithinTime( dataTransitTime, 'HELLO' )
		
	local cmdInfo = decodeRequest( _rtmfpRequest )
	cmdInfo.secret = 'BAD SECRET'
	
	rxTest:log( "openReturnSessionWithPartner emits error" )
	
	rxTest:expectOnlyError( _rtmfpPartnerSession:openReturnSessionWithPartner( _rtmfpRequest, cmdInfo ), ':ignore:' )

	rxTest:log( "waiting for shutdown" )

	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:shutdown() )

end

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

function WFRtmfpPartnerSessionTest.rxTestSessionResponse( rxTest )

	rxTest:setTimeLimit( testWaitTime )

	local _rtmfpPartnerSession = WFRtmfpPartnerSession( _rtmfpClient )
	
	local sessionStream = _rtmfpPartnerSession:listen()
	
	local sessionListener = rxTest:observe( sessionStream
	
		:map( function( rtmfpRequest )

			return tostring( rtmfpRequest:getData() )

		end ) )
	
	rxTest:log( "openSessionWithPartner" )

	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:openSessionWithPartner( serverAddr, serverPort ) )
	
	_rtmfpPartnerSession:getCommandFlowOnce()
		:flatMap( function( flow )
			return flow:send( rxTest:testName() ):call( runUntilDone )
		end  )
		:call( runUntilDone )
	
	sessionListener:expectDataWithinTime( dataTransitTime, rxTest:testName() )

	rxTest:log( "waiting for shutdown" )

	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:shutdown() )

end

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

function WFRtmfpPartnerSessionTest.rxTestReopenSessionWithPartner_testNightly( rxTest )

	rxTest:setTimeLimit( testWaitTime * 2 )

	local _rtmfpPartnerSession = WFRtmfpPartnerSession( _rtmfpClient )
	
	rxTest:log( "openSessionWithPartner" )
	
	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:openSessionWithPartner( serverAddr, serverPort ) )
	
	-- We shouldn't need to reopen our partner session, yet
	
	rxTest:expectEqual( _rtmfpPartnerSession._sessionNeedsToReestablishFlows, false )
	
	rxTest:log( "closeFlows" )

	rxTest:expectOnlyCompleted( 
		WFRtmfpPartnerSession.unitTestExports.closeFlows( _rtmfpPartnerSession, 
															"unit test" ) )

	-- After a few seconds, we should realize that the session has died
	-- This should cause the system to automatically close all flows
	-- associated with the end point.
	
	rxTest:sleep( 10 )
		
	-- Our Session should now need reinitialization
		
	rxTest:expectEqual( _rtmfpPartnerSession._sessionNeedsToReestablishFlows, true )

	rxTest:log( "reopenSessionWithPartnerIfNeeded" )
	
	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:reopenSessionWithPartnerIfNeeded() )
	
	-- Make sure the session status changed internally
	
	rxTest:expectEqual( _rtmfpPartnerSession._sessionNeedsToReestablishFlows, false )
	rxTest:expectEqual( _rtmfpPartnerSession._newFlowCreationCurrentlyActive, false )
	
	-- Verify the session is open
	
	local sessionStream = _rtmfpPartnerSession:listen()
	
	local sessionListener = rxTest:observe( sessionStream
	
		:map( function( rtmfpRequest )
			
			return tostring( rtmfpRequest:getData() )
			
		end ) )
	
	_rtmfpPartnerSession:getCommandFlowOnce()
		:flatMap( function( flow )
			return flow:send( rxTest:testName() ):call( runUntilDone )
		end  )
		:call( runUntilDone )
	
	rxTest:log( "waiting for send after reopen" )

	sessionListener:expectDataWithinTime( dataTransitTime, rxTest:testName() )
	
	-- Close it down
	
	rxTest:log( "shutdown" )

	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:shutdown() )

end

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

function WFRtmfpPartnerSessionTest.rxTestClosePartnerSession( rxTest )

	rxTest:setTimeLimit( testWaitTime )

	local _rtmfpPartnerSession = WFRtmfpPartnerSession( _rtmfpClient )

	rxTest:log( "openSessionWithPartner" )

	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:openSessionWithPartner( serverAddr, serverPort ) )

	rxTest:log( "shutdown" )
	
	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:shutdown() )

	rxTest:expectEqual( _rtmfpPartnerSession._incomingStream, nil )
	rxTest:expectEqual( _rtmfpPartnerSession._rtmfpClient, nil )

end

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

function WFRtmfpPartnerSessionTest.rxTestSessionGoodbye( rxTest )

	rxTest:setTimeLimit( testWaitTime )

	local _rtmfpPartnerSession = WFRtmfpPartnerSession( _rtmfpClient )

	rxTest:log( "openSessionWithPartner" )

	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:openSessionWithPartner( serverAddr, serverPort ) )
	
	local sessionListener = 
		rxTest:observe( 

			_rtmfpPartnerSession
				:listen()
				:map( 
				
					function( rtmfpRequest )

						rxTest:log( "saw a request on the partnersession" )
				
						local cmdInfo = decodeRequest( rtmfpRequest )
					
						return cmdInfo.command
			
					end ) 
				:tapOnCompleted(

					function()

						rxTest:log( "partnersession:listen is done" )

					end )

				)
	
	local specialRequestData = KSData.make( 'SEND_GOODBYE' )
	
	_rtmfpPartnerSession._isManagingFlowGUIDs = false
		-- Make the session partner believe the RTMFP peer established the session.
		-- This will cause the GOODBYE message to close down the session.
		
	rxTest:log( "waiting for send goodbye to fire" )

	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:getCommandFlowOnce()
		:flatMap( function( flow )
			return flow:send( specialRequestData )
		end  ) )
	
	rxTest:log( "sessionListener:expectCompleted()" )

	sessionListener:expectCompleted()
	
	rxTest:sleep( dataTransitTime )
	
	-- Make sure the GOODBYE message properly shut down the session
	
	rxTest:expectEqual( _rtmfpPartnerSession._incomingStream, nil )
	rxTest:expectEqual( _rtmfpPartnerSession._rtmfpClient, nil )

	rxTest:log( "shutdown" )

	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:shutdown() )

end

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

--[[

-- SPAM [jandrew 2017-09-18]: 
	
-- This is a remote code exceution test. To run this test, first launch LrCC.
-- Once it's running, uncomment this code and build and run the WF unit test project.
-- Running this specific test will try to connect the the daemon process and
-- execute a os-level command.

	-- WF.core.blonde
local blonde = require 'blonde'

local blonde_encode = blonde.encode

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

local function encodeCommand( namespace, command, ... )

	return blonde_encode( namespace, command, select( '#', ... ), ... )

end

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

function WFRtmfpPartnerSessionTest.rxTest_DaemonRemoteCodeExecution( rxTest )

	rxTest:setTimeLimit( testWaitTime )

	local _rtmfpPartnerSession = WFRtmfpPartnerSession( _rtmfpClient )

	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:openSessionWithPartner( "127.0.0.1", 1929 ) )
		-- Open the session with the daemon process. It's reasonable that someone could reverse engineer
		-- the RTMFP session handshake the achieve this.
	
	local command = encodeCommand( 'os', 'execute', "touch '/tmp/bishopfoxtest'" )
		-- Craft our malicious command. The command is being encoded using BLONDE, though it could
		-- also be encoded using JSON.
		
	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:getCommandFlowOnce():send( command ) )
		-- Execute the malicious command.
		
	rxTest:expectOnlyCompleted( _rtmfpPartnerSession:shutdown() )

end
]]

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

return WFRxTestCase.wrapSuite( WFRtmfpPartnerSessionTest )
