package raven

import (
	"net/http"
	"net/url"
	"reflect"
	"testing"
)

type Testcase struct {
	request *http.Request
	*Http
}

func newBaseRequest() *http.Request {
	u, _ := url.Parse("http://example.com/")
	header := make(http.Header)
	header.Add("Foo", "bar")

	req := &http.Request{
		Method:     "GET",
		URL:        u,
		Proto:      "HTTP/1.1",
		ProtoMajor: 1,
		ProtoMinor: 1,
		Header:     header,
		Host:       u.Host,
		RemoteAddr: "127.0.0.1:8000",
	}
	return req
}

func newBaseHttp() *Http {
	h := &Http{
		Method:  "GET",
		Cookies: "",
		Query:   "",
		URL:     "http://example.com/",
		Headers: map[string]string{"Foo": "bar"},
		Env:     map[string]string{"REMOTE_ADDR": "127.0.0.1", "REMOTE_PORT": "8000"},
	}
	h.Headers["Host"] = "example.com"
	return h
}

func NewRequest() Testcase {
	return Testcase{newBaseRequest(), newBaseHttp()}
}

func NewRequestIPV6() Testcase {
	req := newBaseRequest()
	req.RemoteAddr = "[:1]:8000"

	h := newBaseHttp()
	h.Env = map[string]string{"REMOTE_ADDR": ":1", "REMOTE_PORT": "8000"}
	return Testcase{req, h}
}

func NewRequestMultipleHeaders() Testcase {
	req := newBaseRequest()
	req.Header.Add("Foo", "baz")

	h := newBaseHttp()
	h.Headers["Foo"] = "bar,baz"
	return Testcase{req, h}
}

func NewSecureRequest() Testcase {
	req := newBaseRequest()
	req.Header.Add("X-Forwarded-Proto", "https")

	h := newBaseHttp()
	h.URL = "https://example.com/"
	h.Headers["X-Forwarded-Proto"] = "https"
	return Testcase{req, h}
}

func NewCookiesRequest() Testcase {
	val := "foo=bar; bar=baz"
	req := newBaseRequest()
	req.Header.Add("Cookie", val)

	h := newBaseHttp()
	h.Cookies = val
	h.Headers["Cookie"] = val
	return Testcase{req, h}
}

var newHttpTests = []Testcase{
	NewRequest(),
	NewRequestIPV6(),
	NewRequestMultipleHeaders(),
	NewSecureRequest(),
	NewCookiesRequest(),
}

func TestNewHttp(t *testing.T) {
	for _, test := range newHttpTests {
		actual := NewHttp(test.request)
		if actual.Method != test.Method {
			t.Errorf("incorrect Method: got %s, want %s", actual.Method, test.Method)
		}
		if actual.Cookies != test.Cookies {
			t.Errorf("incorrect Cookies: got %s, want %s", actual.Cookies, test.Cookies)
		}
		if actual.Query != test.Query {
			t.Errorf("incorrect Query: got %s, want %s", actual.Query, test.Query)
		}
		if actual.URL != test.URL {
			t.Errorf("incorrect URL: got %s, want %s", actual.URL, test.URL)
		}
		if !reflect.DeepEqual(actual.Headers, test.Headers) {
			t.Errorf("incorrect Headers: got %+v, want %+v", actual.Headers, test.Headers)
		}
		if !reflect.DeepEqual(actual.Env, test.Env) {
			t.Errorf("incorrect Env: got %+v, want %+v", actual.Env, test.Env)
		}
		if !reflect.DeepEqual(actual.Data, test.Data) {
			t.Errorf("incorrect Data: got %+v, want %+v", actual.Data, test.Data)
		}
	}
}

var sanitizeQueryTests = []struct {
	input, output string
}{
	{"foo=bar", "foo=bar"},
	{"password=foo", "password=********"},
	{"passphrase=foo", "passphrase=********"},
	{"passwd=foo", "passwd=********"},
	{"secret=foo", "secret=********"},
	{"secretstuff=foo", "secretstuff=********"},
	{"foo=bar&secret=foo", "foo=bar&secret=********"},
	{"secret=foo&secret=bar", "secret=********"},
}

func parseQuery(q string) url.Values {
	r, _ := url.ParseQuery(q)
	return r
}

func TestSanitizeQuery(t *testing.T) {
	for _, test := range sanitizeQueryTests {
		actual := sanitizeQuery(parseQuery(test.input))
		expected := parseQuery(test.output)
		if !reflect.DeepEqual(actual, expected) {
			t.Errorf("incorrect sanitization: got %+v, want %+v", actual, expected)
		}
	}
}
