// Copyright © 2020 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http/httptest"
	"net/url"
	"strings"
	"sync"
	"testing"
)

func init() {
	for i := 0; i < 10; i++ {
		u := fmt.Sprintf("permanent%d", i)
		permanentRelays = append(permanentRelays, &relay{URL: u})
	}

	knownRelays = []*relay{
		{URL: "known1"},
		{URL: "known2"},
		{URL: "known3"},
	}

	mut = new(sync.RWMutex)
}

// Regression test: handleGetRequest should not modify permanentRelays.
func TestHandleGetRequest(t *testing.T) {
	needcap := len(permanentRelays) + len(knownRelays)
	if needcap > cap(permanentRelays) {
		t.Fatalf("test setup failed: need cap(permanentRelays) >= %d, have %d",
			needcap, cap(permanentRelays))
	}

	w := httptest.NewRecorder()
	w.Body = new(bytes.Buffer)
	handleEndpointFull(w, httptest.NewRequest("GET", "/", nil))

	result := make(map[string][]*relay)
	err := json.NewDecoder(w.Body).Decode(&result)
	if err != nil {
		t.Fatalf("invalid JSON: %v", err)
	}

	relays := result["relays"]
	expect, actual := len(knownRelays)+len(permanentRelays), len(relays)
	if actual != expect {
		t.Errorf("expected %d relays, got %d", expect, actual)
	}

	// Check for changes in permanentRelays.
	for i, r := range permanentRelays {
		switch {
		case !strings.HasPrefix(r.URL, "permanent"):
			t.Errorf("relay %q among permanent relays", r.URL)
		case r.URL != fmt.Sprintf("permanent%d", i):
			t.Error("order of permanent relays changed")
		}
	}
}

func TestCanonicalizeQueryValues(t *testing.T) {
	// This just demonstrates and validates the uri.Parse/String stuff in
	// regards to query strings.

	in := "http://example.com/?some weird= query^value"
	exp := "http://example.com/?some+weird=+query%5Evalue"

	uri, err := url.Parse(in)
	if err != nil {
		t.Fatal(err)
	}

	str := uri.String()
	if str != in {
		// Just re-encoding the URL doesn't sanitize the query string.
		t.Errorf("expected %q, got %q", in, str)
	}

	uri.RawQuery = uri.Query().Encode()
	str = uri.String()
	if str != exp {
		// The query string is now in correct format.
		t.Errorf("expected %q, got %q", exp, str)
	}
}

func TestSlimURL(t *testing.T) {
	cases := []struct {
		in, out string
	}{
		{"http://example.com/", "http://example.com/"},
		{"relay://192.0.2.42:22067/?globalLimitBps=0&id=EIC6B3M-EIC6B3M-EIC6B3M-EIC6B3M-EIC6B3M-EIC6B3M-EIC6B3M-EIC6B3M&networkTimeout=2m0s&pingInterval=1m0s&providedBy=Test&sessionLimitBps=0&statusAddr=%3A22070", "relay://192.0.2.42:22067/?id=EIC6B3M-EIC6B3M-EIC6B3M-EIC6B3M-EIC6B3M-EIC6B3M-EIC6B3M-EIC6B3M"},
	}

	for _, c := range cases {
		if got := slimURL(c.in); got != c.out {
			t.Errorf("expected %q, got %q", c.out, got)
		}
	}
}
