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

KSJsonEncoderTest.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2010 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 WFRxTestCase = require 'WFRxTestCase'

	-- libs.json
local KSJsonDecoder = require 'KSJsonDecoder'
local KSJsonEncoder = require 'KSJsonEncoder'

	-- libs.table
local WFTableArchiving = require 'WFTableArchiving'


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

local KSJsonEncoderTest = {}

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

-- GRUNGE [scouten 2011-02-22]: Some of this code is duplicated from
-- KSBlondeTest. Opportunity for refactoring code.

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

local function convertTableToStringForDebugIfNeeded( value )

	if type( value ) == 'table' then
		return WFTableArchiving.tableToString( value )
	else
		return value
	end

end

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

local compareValues, deepCompareTables

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

-- Performs a deep comparison of two tables, nested up to 20 levels deep. Returns
-- true if every key-value pair in t1 also appears in t2 and the number of
-- key-value pairs in t1 and t2 are equal. Returns false otherwise.

function deepCompareTables( t1, t2, depth )

    if depth == 0 then
        error( "Tables are too deeply nested to be compared" )
    end
    
    local t1count = 0
    local t2count = 0
    
    for k, v in pairs( t1 ) do
        t1count = t1count + 1
        local equal = compareValues( v, t2[ k ], depth )
        if not equal then return false end
    end
    
    for k, v in pairs( t2 ) do
        t2count = t2count + 1
    end
    
    return t1count == t2count

end

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

function compareValues( v1, v2, level )

    if type( v2 ) == "table" and type( v1 ) == "table" then
        if not level then level = 20 end
        return deepCompareTables( v1, v2, level - 1 )
    else
        return ( v2 == v1 )
    end
        
end

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

local function testWebSafeEncodeThenDecode( luaValue )

	local actualJson = KSJsonEncoder.encodeWebSafeJson( luaValue )

	assert( type( actualJson ) == 'string',
		"KSJsonEncode.encodeWebSafeJson: did not return a string value" )

	assert( actualJson:sub( 1, 13 ) == 'while (1) {}\n',
		"KSJsonEncode.encodeWebSafeJson: did not include exec guard" )

	-- [scouten 2011-03-11]: Ideally, one would want to have an expected
	-- string and simply compare against that. But ... the table keys
	-- are encoded in random order, so we can't do that. Instead we
	-- re-decode the JSON and make sure it matches the original Lua value.
	-- (Note that the JSON decode does work as a standalone test.)
	
	local reconstructedLuaValue = KSJsonDecoder.decodeWebSafeJson( actualJson )
	
	if not compareValues( luaValue, reconstructedLuaValue ) then
		local message = string.format(
					"KSJsonEncoder.encodeWebSafeJson: value did not decode correctly\n\nexpected:\n%s\n\nactual:\n%s\n",
					convertTableToStringForDebugIfNeeded( luaValue ),
					convertTableToStringForDebugIfNeeded( reconstructedLuaValue ) )
		print( message )
		error( message )
	end

end

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

function KSJsonEncoderTest.testBasicString()

	testWebSafeEncodeThenDecode {
		status = 'ok',
		last_event_received = '2011-02-22T21:21:40.711Z',
		events = {
			{
				id = 'bc63f8cecc09469fbc40fe544119251a',
				href = '/v1.0/accounts/7a6b9ad57de3404cbc54305878afbc35/partitions/9a24b25d114c4a008ba051beb54d0ab2/events/bc63f8cecc09469fbc40fe544119251a',
				partition = {
					id = '9a24b25d114c4a008ba051beb54d0ab2',
					href = '/v1.0/accounts/7a6b9ad57de3404cbc54305878afbc35/partitions/9a24b25d114c4a008ba051beb54d0ab2'
				},
				date_created = '2011-02-22T21:21:40.711Z',
				date_received = '2011-02-22T21:21:40.711Z',
				class = 'favorite',
				value = 'true'
			},
			{
				id = '5667d946f3eb4733a0986a72f1d24108',
				href = '/v1.0/accounts/7a6b9ad57de3404cbc54305878afbc35/partitions/d4e259b3dad84a629d2a32e0c074ffb4/events/5667d946f3eb4733a0986a72f1d24108',
				partition = {
					id = 'd4e259b3dad84a629d2a32e0c074ffb4',
					href = '/v1.0/accounts/7a6b9ad57de3404cbc54305878afbc35/partitions/d4e259b3dad84a629d2a32e0c074ffb4'
				},
				date_created = '2011-02-22T21:21:40.711Z',
				date_received = '2011-02-22T21:21:40.711Z',
				class = 'favorite',
				value = 'false'
			},
		},
	}

end

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

function KSJsonEncoderTest.rxTestWebSafeEncode( rxTest )
	
	local expected = [[while (1) {}
{"status":"ok"}]]
	
	rxTest:expectEqual( expected, KSJsonEncoder.encodeWebSafeJson( { status = 'ok' } ) )
	
end

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

function KSJsonEncoderTest.rxTestNullAtEndOfArray( rxTest )
	
	local expected = '["something",null]'
	
	local json = KSJsonEncoder.encodeOzUploadJson( { "something", KSJsonEncoder.json_null } )
	
	rxTest:expectEqual( expected, json )
	
end

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

function KSJsonEncoderTest.rxTestEncodeEmptyArray( rxTest )
	
	-- empty table should end up as an empty JSON object ({}) and not an empty JSON array ([])
	-- Oz does not really ever want an empty [] with the current data model, so we prefer a 
	-- default of {} for an empty lua table. 
	
	-- Note that lua-RapidJSON would allow us to control this more specifically with metatables
	-- which can be used to force one or the other but this can mess with serialization so we
	-- do not currently use this as a method of control
	local expected = "{}"
	
	rxTest:expectEqual( expected, KSJsonEncoder.encodeOzUploadJson( {} ) )

end

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

function KSJsonEncoderTest.rxTestVariousKeysAndValues( rxTest )
		
	local original = { 
		{	'float',
			1.5,
			'integer',
			5,
			'string',
			'hello',
			'empty',
			{},
			'false',
			false,
			'custom',
			'custom json serializer',
			'ostr_key',
			{ key = 'value' },
			'obool_key',
			{ ['true'] = true },
			'onull_key',
			{ ['null'] = KSJsonDecoder.json_null }
		},
		10,
		10.3,
		10.3,
		'a string',
		KSJsonDecoder.json_null,
		false,
		true
	}
	
	local expected = '[["float",1.5,"integer",5,"string","hello","empty",{},"false",false,"custom","custom json serializer","ostr_key",{"key":"value"},"obool_key",{"true":true},"onull_key",{"null":null}],10,10.3,10.3,"a string",null,false,true]' --]]

	local encoded = KSJsonEncoder.encodeOzUploadJson( original )
	
	rxTest:expectEqual( expected, encoded )
	
	
end

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

function KSJsonEncoderTest.rxTestWeirdNumbers( rxTest )
	
	-- SPAM [matthewj 2018-07-23]: As of this comment, this test mostly validates 
	-- the behavior of the current code, I do not believe we are relying on the
	-- way this works in WF or the communication with Oz as we do not transit 
	-- data that looks like this.
	
	-- -0 is converted to 0 (this means we can't roundtrip -0)
	rxTest:expectEqual( "[0]", KSJsonEncoder.encodeOzUploadJson( { -0 } ) )
	
	-- pretty large number ok
	rxTest:expectEqual( '[1.55e308]', KSJsonEncoder.encodeOzUploadJson( { 1.55e308 } ) )
	rxTest:expectEqual( '[-1.55e308]', KSJsonEncoder.encodeOzUploadJson( { -1.55e308 } ) ) 
	
	-- if it overflows a double, we currently can't encode it! No roundtripping
	rxTest:expectError( KSJsonEncoder.encodeOzUploadJson, { 1.55e309 } )
	
	-- tiny number.  table comparison doesn't like these much so...turn to string and see how it goes
	rxTest:expectEqual( '[1.55e-300]', KSJsonEncoder.encodeOzUploadJson( { 1.55e-300 } ) )
	rxTest:expectEqual( '[-1.55e-300]', KSJsonEncoder.encodeOzUploadJson( { -1.55e-300 } ) )
	
	-- basically 0, again note that these won't roundtrip the encode/decode cycle
	rxTest:expectEqual( '[0]', KSJsonEncoder.encodeOzUploadJson( { 1.55e-500 } ) )
	rxTest:expectEqual( '[0]', KSJsonEncoder.encodeOzUploadJson( {-1.55e-500} ) )


end

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

function KSJsonEncoderTest.rxTestNumberInStringKey( rxTest )
	
	-- Thanks to agentzh for this bug report!
	
	rxTest:expectEqual( '{"2":"two"}', KSJsonEncoder.encodeOzUploadJson( { [ "2" ] = "two" } ) )
	
end

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

function KSJsonEncoderTest.rxTestDeeplyNested( rxTest )

       local original = {
               {
                       action = 'update',
                       id = 'a33d186fecb94991b768bee611918849',
                       if_match = 'b4fdc8a687d049ce96c16f78b895d031',
                       payload = {
                               xmp = {
                                       ['mwg-rs'] = {
                                               Regions = {
                                                       RegionList = {
                                                               ['d484b264f2ce85dd30e1abc129e01a829'] = {
                                                                       Extensions = {
                                                                               ['adobe-fi'] = {
                                                                                       People = {
                                                                                               ['1cbb45c4e8af4dd1eaa5dd9e7d61618e51'] = KSJsonEncoder.json_null,
                                                                                               ['1cbb45c4e8af4dd1eaa5dd9e7d61618e52'] = KSJsonEncoder.json_null,
                                                                                               ['1cbb45c4e8af4dd1eaa5dd9e7d61618e53'] = KSJsonEncoder.json_null,
                                                                                               ['1cbb45c4e8af4dd1eaa5dd9e7d61618e54'] = KSJsonEncoder.json_null,
                                                                                               ['1cbb45c4e8af4dd1eaa5dd9e7d61618e55'] = KSJsonEncoder.json_null,
                                                                                               ['444b45c4e8af4dd1eaa5dd9e7d61618440'] = {
                                                                                                       Confirmed = true,
                                                                                                       Rejected = false,
                                                                                               },
                                                                                       }
                                                                               }
                                                                       }
                                                               }
                                                       }
                                               }
                                       }
                               }
                       },
                       revision_id = 'f82a10e07eb6491fb05173e9c16f4a9c',
                       type = 'asset'
               }
       }


       local encoded = KSJsonEncoder.encodeOzUploadJson( original )

       local expected = '[{"if_match":"b4fdc8a687d049ce96c16f78b895d031","action":"update","payload":{"xmp":{"mwg-rs":{"Regions":{"RegionList":{"d484b264f2ce85dd30e1abc129e01a829":{"Extensions":{"adobe-fi":{"People":{"1cbb45c4e8af4dd1eaa5dd9e7d61618e52":null,"444b45c4e8af4dd1eaa5dd9e7d61618440":{"Rejected":false,"Confirmed":true},"1cbb45c4e8af4dd1eaa5dd9e7d61618e51":null,"1cbb45c4e8af4dd1eaa5dd9e7d61618e55":null,"1cbb45c4e8af4dd1eaa5dd9e7d61618e53":null,"1cbb45c4e8af4dd1eaa5dd9e7d61618e54":null}}}}}}}}},"id":"a33d186fecb94991b768bee611918849","type":"asset","revision_id":"f82a10e07eb6491fb05173e9c16f4a9c"}]'

       rxTest:expectEqual( encoded, expected )

end

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

function KSJsonEncoderTest.rxTestFormattingOptions( rxTest )

local KSJsonEncoder = require 'KSJsonEncoder'

local sampleJsonTable = {
		one = 1,
		two = "two",
		three = 3,
		four = "four",
		five = 5,
		level_1_1 = "Depth 1",
		level_2_1 = { level_2_2 = "Depth 2" },
		level_3_1 = { level_3_2 = {level_3_3 = "Depth 3" }},
		level_4_1 = { level_4_2 = {level_4_3 = { level_4_4 = "Depth 4"}}},
		level_5_1 = { level_5_2 = {level_5_3 = { level_5_4 = { level_5_5 = "Depth 5"}}}},
		multiLevel_1 = {
			level_1_1 = "MultiLevel 1",
			level_2_1 = { level_2_2 = "MultiLevel 2" },
			level_3_1 = { level_3_2 = {level_3_3 = "MultiLevel 3" }},
			level_4_1 = { level_4_2 = {level_4_3 = { level_4_4 = "MultiLevel 4"}}},
			level_5_1 = { level_5_2 = {level_5_3 = { level_5_4 = { level_5_5 = "MultiLevel 5"}}}},
		},
		emptyTable = {}
		
	}
	
	local expectedJsonTable = {
		pretty_true = {
			sort_key_true = {
				empty_table_as_array_true = {
					max_depth_1 = "error",
					max_depth_6 = 
[[{
    "emptyTable": [],
    "five": 5,
    "four": "four",
    "level_1_1": "Depth 1",
    "level_2_1": {
        "level_2_2": "Depth 2"
    },
    "level_3_1": {
        "level_3_2": {
            "level_3_3": "Depth 3"
        }
    },
    "level_4_1": {
        "level_4_2": {
            "level_4_3": {
                "level_4_4": "Depth 4"
            }
        }
    },
    "level_5_1": {
        "level_5_2": {
            "level_5_3": {
                "level_5_4": {
                    "level_5_5": "Depth 5"
                }
            }
        }
    },
    "multiLevel_1": {
        "level_1_1": "MultiLevel 1",
        "level_2_1": {
            "level_2_2": "MultiLevel 2"
        },
        "level_3_1": {
            "level_3_2": {
                "level_3_3": "MultiLevel 3"
            }
        },
        "level_4_1": {
            "level_4_2": {
                "level_4_3": {
                    "level_4_4": "MultiLevel 4"
                }
            }
        },
        "level_5_1": {
            "level_5_2": {
                "level_5_3": {
                    "level_5_4": {
                        "level_5_5": "MultiLevel 5"
                    }
                }
            }
        }
    },
    "one": 1,
    "three": 3,
    "two": "two"
}]]
				},
				empty_table_as_array_false = {
					max_depth_1 = "error",
					max_depth_6 = 
[[{
    "emptyTable": {},
    "five": 5,
    "four": "four",
    "level_1_1": "Depth 1",
    "level_2_1": {
        "level_2_2": "Depth 2"
    },
    "level_3_1": {
        "level_3_2": {
            "level_3_3": "Depth 3"
        }
    },
    "level_4_1": {
        "level_4_2": {
            "level_4_3": {
                "level_4_4": "Depth 4"
            }
        }
    },
    "level_5_1": {
        "level_5_2": {
            "level_5_3": {
                "level_5_4": {
                    "level_5_5": "Depth 5"
                }
            }
        }
    },
    "multiLevel_1": {
        "level_1_1": "MultiLevel 1",
        "level_2_1": {
            "level_2_2": "MultiLevel 2"
        },
        "level_3_1": {
            "level_3_2": {
                "level_3_3": "MultiLevel 3"
            }
        },
        "level_4_1": {
            "level_4_2": {
                "level_4_3": {
                    "level_4_4": "MultiLevel 4"
                }
            }
        },
        "level_5_1": {
            "level_5_2": {
                "level_5_3": {
                    "level_5_4": {
                        "level_5_5": "MultiLevel 5"
                    }
                }
            }
        }
    },
    "one": 1,
    "three": 3,
    "two": "two"
}]]
				},
			},
			sort_key_false = {
				empty_table_as_array_true = {
					max_depth_1 = "error",
					max_depth_6 = 
[[{
    "four": "four",
    "level_3_1": {
        "level_3_2": {
            "level_3_3": "Depth 3"
        }
    },
    "multiLevel_1": {
        "level_2_1": {
            "level_2_2": "MultiLevel 2"
        },
        "level_4_1": {
            "level_4_2": {
                "level_4_3": {
                    "level_4_4": "MultiLevel 4"
                }
            }
        },
        "level_1_1": "MultiLevel 1",
        "level_3_1": {
            "level_3_2": {
                "level_3_3": "MultiLevel 3"
            }
        },
        "level_5_1": {
            "level_5_2": {
                "level_5_3": {
                    "level_5_4": {
                        "level_5_5": "MultiLevel 5"
                    }
                }
            }
        }
    },
    "level_5_1": {
        "level_5_2": {
            "level_5_3": {
                "level_5_4": {
                    "level_5_5": "Depth 5"
                }
            }
        }
    },
    "level_2_1": {
        "level_2_2": "Depth 2"
    },
    "emptyTable": [],
    "level_4_1": {
        "level_4_2": {
            "level_4_3": {
                "level_4_4": "Depth 4"
            }
        }
    },
    "two": "two",
    "one": 1,
    "level_1_1": "Depth 1",
    "five": 5,
    "three": 3
}]]
				},
				empty_table_as_array_false = {
					max_depth_1 = "error",
					max_depth_6 =
[[{
    "four": "four",
    "level_3_1": {
        "level_3_2": {
            "level_3_3": "Depth 3"
        }
    },
    "multiLevel_1": {
        "level_2_1": {
            "level_2_2": "MultiLevel 2"
        },
        "level_4_1": {
            "level_4_2": {
                "level_4_3": {
                    "level_4_4": "MultiLevel 4"
                }
            }
        },
        "level_1_1": "MultiLevel 1",
        "level_3_1": {
            "level_3_2": {
                "level_3_3": "MultiLevel 3"
            }
        },
        "level_5_1": {
            "level_5_2": {
                "level_5_3": {
                    "level_5_4": {
                        "level_5_5": "MultiLevel 5"
                    }
                }
            }
        }
    },
    "level_5_1": {
        "level_5_2": {
            "level_5_3": {
                "level_5_4": {
                    "level_5_5": "Depth 5"
                }
            }
        }
    },
    "level_2_1": {
        "level_2_2": "Depth 2"
    },
    "emptyTable": {},
    "level_4_1": {
        "level_4_2": {
            "level_4_3": {
                "level_4_4": "Depth 4"
            }
        }
    },
    "two": "two",
    "one": 1,
    "level_1_1": "Depth 1",
    "five": 5,
    "three": 3
}]]
				},
			},
		},
		pretty_false = {
			sort_key_true = {
				empty_table_as_array_true = {
					max_depth_1 = "error",
					max_depth_6 = [[{"emptyTable":[],"five":5,"four":"four","level_1_1":"Depth 1","level_2_1":{"level_2_2":"Depth 2"},"level_3_1":{"level_3_2":{"level_3_3":"Depth 3"}},"level_4_1":{"level_4_2":{"level_4_3":{"level_4_4":"Depth 4"}}},"level_5_1":{"level_5_2":{"level_5_3":{"level_5_4":{"level_5_5":"Depth 5"}}}},"multiLevel_1":{"level_1_1":"MultiLevel 1","level_2_1":{"level_2_2":"MultiLevel 2"},"level_3_1":{"level_3_2":{"level_3_3":"MultiLevel 3"}},"level_4_1":{"level_4_2":{"level_4_3":{"level_4_4":"MultiLevel 4"}}},"level_5_1":{"level_5_2":{"level_5_3":{"level_5_4":{"level_5_5":"MultiLevel 5"}}}}},"one":1,"three":3,"two":"two"}]]
				},
				empty_table_as_array_false = {
					max_depth_1 = "error",
					max_depth_6 = [[{"emptyTable":{},"five":5,"four":"four","level_1_1":"Depth 1","level_2_1":{"level_2_2":"Depth 2"},"level_3_1":{"level_3_2":{"level_3_3":"Depth 3"}},"level_4_1":{"level_4_2":{"level_4_3":{"level_4_4":"Depth 4"}}},"level_5_1":{"level_5_2":{"level_5_3":{"level_5_4":{"level_5_5":"Depth 5"}}}},"multiLevel_1":{"level_1_1":"MultiLevel 1","level_2_1":{"level_2_2":"MultiLevel 2"},"level_3_1":{"level_3_2":{"level_3_3":"MultiLevel 3"}},"level_4_1":{"level_4_2":{"level_4_3":{"level_4_4":"MultiLevel 4"}}},"level_5_1":{"level_5_2":{"level_5_3":{"level_5_4":{"level_5_5":"MultiLevel 5"}}}}},"one":1,"three":3,"two":"two"}]]
				},
			},
			sort_key_false = {
				empty_table_as_array_true = {
					max_depth_1 = "error",
					max_depth_6 = [[{"four":"four","level_3_1":{"level_3_2":{"level_3_3":"Depth 3"}},"multiLevel_1":{"level_2_1":{"level_2_2":"MultiLevel 2"},"level_4_1":{"level_4_2":{"level_4_3":{"level_4_4":"MultiLevel 4"}}},"level_1_1":"MultiLevel 1","level_3_1":{"level_3_2":{"level_3_3":"MultiLevel 3"}},"level_5_1":{"level_5_2":{"level_5_3":{"level_5_4":{"level_5_5":"MultiLevel 5"}}}}},"level_5_1":{"level_5_2":{"level_5_3":{"level_5_4":{"level_5_5":"Depth 5"}}}},"level_2_1":{"level_2_2":"Depth 2"},"emptyTable":[],"level_4_1":{"level_4_2":{"level_4_3":{"level_4_4":"Depth 4"}}},"two":"two","one":1,"level_1_1":"Depth 1","five":5,"three":3}]]
				},
				empty_table_as_array_false = {
					max_depth_1 = "error",
					max_depth_6 = [[{"four":"four","level_3_1":{"level_3_2":{"level_3_3":"Depth 3"}},"multiLevel_1":{"level_2_1":{"level_2_2":"MultiLevel 2"},"level_4_1":{"level_4_2":{"level_4_3":{"level_4_4":"MultiLevel 4"}}},"level_1_1":"MultiLevel 1","level_3_1":{"level_3_2":{"level_3_3":"MultiLevel 3"}},"level_5_1":{"level_5_2":{"level_5_3":{"level_5_4":{"level_5_5":"MultiLevel 5"}}}}},"level_5_1":{"level_5_2":{"level_5_3":{"level_5_4":{"level_5_5":"Depth 5"}}}},"level_2_1":{"level_2_2":"Depth 2"},"emptyTable":{},"level_4_1":{"level_4_2":{"level_4_3":{"level_4_4":"Depth 4"}}},"two":"two","one":1,"level_1_1":"Depth 1","five":5,"three":3}]]
				},
			},
		}
	}
	
	for _, pretty in ipairs( {true, false} ) do
		local pretty_key = string.format( "pretty_%s", pretty )
		for _, sort_key in ipairs( {true, false} ) do
			local sort_key_key = string.format( "sort_key_%s", sort_key )
			for _, empty_table_as_array in ipairs( {true, false} ) do
				local empty_table_as_array_key = string.format( "empty_table_as_array_%s", empty_table_as_array )
				for _, max_depth in ipairs( {1,6} ) do
					local max_depth_key = string.format( "max_depth_%s", max_depth )
					
					local jsonEncodingOptions = {
						pretty = pretty, 
						sort_keys = sort_key, 
						empty_table_as_array = empty_table_as_array, 
						max_depth = max_depth
					}
					local expectedEncodedJson = expectedJsonTable[pretty_key][sort_key_key][empty_table_as_array_key][max_depth_key]
					local ok, output = pcall(KSJsonEncoder.encodeOzUploadJson, sampleJsonTable, jsonEncodingOptions )
					
					if not ok then
						rxTest:expectEqual( expectedEncodedJson, "error" )
					else
						rxTest:expectEqual( expectedEncodedJson, output)
					end
				
				end
			end
		end
	end
	
	
end

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

return WFRxTestCase.wrapSuite( 'KSJsonEncoderTest', KSJsonEncoderTest )
