/**************************************************************************************
 *
 *     Project:    kvmstoreserver
 *     File name:  testCaseRef_4_3_005_2_test.go
 *     Version:    1.0.0
 *     Date:       2018-01-12
 *
 *     Autor:      Bogdanovic Theodor
 *     Contact:    t.bogdanovic@hotmail.com
 *
 *     License:    GPLv3
 *
 **************************************************************************************/
/**************************************************************************************
 * Copyright 2018 Bogdanovic Theodor
 *
 * This file is part of kvmstoreserver.
 *
 * kvmstoreserver is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * kvmstoreserver is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with kvmstoreserver.  If not, see <http://www.gnu.org/licenses/>.
 **************************************************************************************/
/**************************************************************************************
 *  Further explanations:  This source code is under the license specified by the
 *                         Autor above. Core components or other packages are still
 *                         in source code form, under the license specified by the
 *                         respective author.
 **************************************************************************************/

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"testing"
)

// Test4_3_005_2_test contains several individual tests. The detailed
// descriptions of the tests can be found as comments in the tests themselves
func Test4_3_005_2_test(t *testing.T) {

	// Create test struct for the test data
	type innerStruct []struct {
		name      string
		keySeqNbr int
		revNbr    int
		cliAction clientAction
		httpMeth  string
		paramExt  string
	}
	tests := map[int]innerStruct{}
	tests = make(map[int]innerStruct, 0)

	// Create test data
	tests[0] = innerStruct{
		// Insert sequence 1
		{name: "Test 1_1: Try to insert entry first time (Controller /get/ | Method Get)", keySeqNbr: 501, revNbr: 0, cliAction: actionGet, httpMeth: GET, paramExt: "?rev"},
		{name: "Test 1_2: Try to insert entry second time (Controller /get/ | Method Get)", keySeqNbr: 501, revNbr: 1, cliAction: actionGet, httpMeth: GET, paramExt: "?search"},
		{name: "Test 1_3: Try to insert entry third time (Controller /get/ | Method Get)", keySeqNbr: 501, revNbr: 2, cliAction: actionGet, httpMeth: GET, paramExt: "?search=rev_nbr"},
	}
	tests[1] = innerStruct{
		// Insert sequence 2
		{name: "Test 2_1: Try to insert entry first time (Controller /get/ | Method Get)", keySeqNbr: 502, revNbr: 0, cliAction: actionGet, httpMeth: GET, paramExt: "?search&rev"},
		{name: "Test 2_2: Try to insert entry second time (Controller /get/ | Method Get)", keySeqNbr: 502, revNbr: 1, cliAction: actionGet, httpMeth: GET, paramExt: "?rev=1&search=rev_nbr"},
		{name: "Test 2_3: Try to insert entry third time (Controller /get/ | Method POST)", keySeqNbr: 502, revNbr: 2, cliAction: actionGet, httpMeth: GET},
	}
	tests[2] = innerStruct{
		// Insert sequence 3
		{name: "Test 3_1: Try to insert entry first time (Controller /get/ | Method Get)", keySeqNbr: 503, revNbr: 0, cliAction: actionGet, httpMeth: GET, paramExt: "?search_wrong=rev_nbr"},
		{name: "Test 3_2: Try to insert entry second time (Controller /get/ | Method Get)", keySeqNbr: 503, revNbr: 1, cliAction: actionGet, httpMeth: GET},
		{name: "Test 3_3: Try to insert entry third time (Controller /get/ | Method Get)", keySeqNbr: 503, revNbr: 2, cliAction: actionGet, httpMeth: GET},
	}

	// Start a test server in a separate gourutine
	runKvmssTestInstance()

	// Clean up the environment (if there are remnants of the last test)
	testClient(tests[0][0].keySeqNbr, tests[0][0].revNbr, actionDel, DELETE, "")
	testClient(tests[1][0].keySeqNbr, tests[1][0].revNbr, actionDel, DELETE, "")
	testClient(tests[2][0].keySeqNbr, tests[2][0].revNbr, actionDel, DELETE, "")

	// Insert test data into kvmstoreserver
	for _, outer := range tests {
		for _, inner := range outer {
			testClient(inner.keySeqNbr, inner.revNbr, actionAdd, POST, "")
		}
	}

	// Test 1: Try to get the JSON field RevNbr from key 1 revision 2
	// (Expected: Int 2 && Status code 200)
	{
		testLine := &tests[0][2]
		got := testClient(testLine.keySeqNbr, testLine.revNbr, testLine.cliAction, testLine.httpMeth, testLine.paramExt)
		ressult, _, resultStatus := parseBodyRevAndSearch(got)
		if ressult != 2 || resultStatus != 200 {
			t.Errorf("Start testClient() (%v) = %v | Status: %v, want %v | Status: %v", testLine, ressult, resultStatus, 2, 200)
		}
	}

	// Test 2: Try to get the JSON field RevNbr from key 2 revision 1
	// (Expected: Int 1 && Status code 200)
	{
		testLine := &tests[1][1]
		got := testClient(testLine.keySeqNbr, testLine.revNbr, testLine.cliAction, testLine.httpMeth, testLine.paramExt)
		ressult, _, resultStatus := parseBodyRevAndSearch(got)
		if ressult != 1 || resultStatus != 200 {
			t.Errorf("Start testClient() (%v) = %v | Status: %v, want %v | Status: %v", testLine.name, ressult, resultStatus, 1, 200)
		}
	}

	// Test 3: Try to get the JSON field RevNbr from key 3 revision 0
	// The http parameter search is misspelled
	// (This test also applies to the parameter rev {Equivalent Implementation})
	// (Expected: Status code 400)
	{
		testLine := &tests[2][0]
		got := testClient(testLine.keySeqNbr, testLine.revNbr, testLine.cliAction, testLine.httpMeth, testLine.paramExt)
		_, _, resultStatus := parseBodyRevAndSearch(got)
		if resultStatus != 400 {
			t.Errorf("Start testClient() (%v) = %v, want %v", testLine.name, resultStatus, 400)
		}
	}

	// Test 4: Try to get the JSON document from key 1 revision ()
	// The http parameter rev is set but not defined
	// (Expected: The last valid revision of the key {current revision})
	{
		testLine := &tests[0][0]
		got := testClient(testLine.keySeqNbr, testLine.revNbr, testLine.cliAction, testLine.httpMeth, testLine.paramExt)
		_, fullAnswer, resultStatus := parseBodyRevAndSearch(got)
		if resultStatus != 200 && fullAnswer.RevNbr != testLine.revNbr {
			t.Errorf("Start testClient() (%v) = %v, want %v", testLine.name, resultStatus, 400)
		}
	}

	// Test 5: Try to get the field () from the current JSON document
	// The http parameter search is set but not defined
	// (Expected: The last valid revision of the key {current revision})
	{
		testLine := &tests[0][1]
		got := testClient(testLine.keySeqNbr, testLine.revNbr, testLine.cliAction, testLine.httpMeth, testLine.paramExt)
		_, fullAnswer, resultStatus := parseBodyRevAndSearch(got)
		if resultStatus != 200 && fullAnswer.RevNbr != testLine.revNbr {
			t.Errorf("Start testClient() (%v) = %v, want %v", testLine.name, resultStatus, 400)
		}
	}

	// Test 6: Try to get the field () from the revision ()
	// The http parameters search and rev are set but not defined
	// (Expected: The last valid revision of the key {current revision})
	{
		testLine := &tests[1][0]
		got := testClient(testLine.keySeqNbr, testLine.revNbr, testLine.cliAction, testLine.httpMeth, testLine.paramExt)
		_, fullAnswer, resultStatus := parseBodyRevAndSearch(got)
		if resultStatus != 200 && fullAnswer.RevNbr != testLine.revNbr {
			t.Errorf("Start testClient() (%v) = %v, want %v", testLine.name, resultStatus, 400)
		}
	}

	// Post job: Clean up the environment
	testClient(tests[0][0].keySeqNbr, tests[0][0].revNbr, actionDel, DELETE, "")
	testClient(tests[1][0].keySeqNbr, tests[1][0].revNbr, actionDel, DELETE, "")
	testClient(tests[2][0].keySeqNbr, tests[2][0].revNbr, actionDel, DELETE, "")
}

// Here valid standard parser. Accepts the response of the testClient,
// decodes it and returns the following:
//
// @var int (1) 		= JSON field RevNbr (int)
// @var *fullRespAnswer = In struct decoded JSOn content from kvmstoreserver
// @var int (2)			= The http status code from the response header
func parseBodyRevAndSearch(resp *http.Response) (int, *fullRespAnswer, int) {

	var revNbrResult int
	var fullAnswer fullRespAnswer

	body, errBody := ioutil.ReadAll(resp.Body)
	if errBody != nil {
		errMsg := fmt.Sprintf("Can not read body content (OrigErr)-> %v", errBody)
		panic(errMsg)
	}

	if resp.StatusCode == 200 {
		errJSONOuter := json.Unmarshal(body, &revNbrResult)
		if errJSONOuter != nil {
			errJSONInner := json.Unmarshal(body, &fullAnswer)
			if errJSONInner != nil {
				errMsg := fmt.Sprintf("Can not decode JSON from body (OK Response; oter or inner) (OrigErr)-> %v", errJSONInner)
				panic(errMsg)
			}
			return 0, &fullAnswer, resp.StatusCode
		}
	}

	return revNbrResult, &fullAnswer, resp.StatusCode
}

type fullRespAnswer struct {
	SystemName    string      `json:"system_name"`
	SystemNumber  int         `json:"system_number"`
	SystemVersion float64     `json:"system_version"`
	SystemActive  bool        `json:"system_active"`
	SystemNull    interface{} `json:"system_null"`
	RevNbr        int         `json:"rev_nbr"`
}
