From d8e683ef0b15e00bba9c241181e7d3247d30539c Mon Sep 17 00:00:00 2001
From: Ruben Pollan <meskio@sindominio.net>
Date: Thu, 22 Nov 2018 20:50:56 -0600
Subject: [PATCH] [test] make bonafide more testable

Get local timezone and http client as configurable things in bonafide,
so tests can set them. Also separate integration tests and unit tests.
---
 .gitlab-ci.yml                          |   1 +
 Makefile                                |   2 +-
 standalone/bonafide.go                  |  20 +++--
 standalone/bonafide_integration_test.go |  58 ++++++++++++
 standalone/bonafide_test.go             |  94 ++++++++++++++++----
 standalone/testdata/cert                |  54 +++++++++++
 standalone/testdata/eip-service.json    | 113 ++++++++++++++++++++++++
 7 files changed, 318 insertions(+), 24 deletions(-)
 create mode 100644 standalone/bonafide_integration_test.go
 create mode 100644 standalone/testdata/cert
 create mode 100644 standalone/testdata/eip-service.json

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f259d112..b3f1861f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -14,6 +14,7 @@ build_test:
     - ln -s "$(pwd)" ${APP_PATH}
     - cd ${APP_PATH}
     - make get
+    - make test
     - make bitmaskd
     - make build
   tags:
diff --git a/Makefile b/Makefile
index 04849143..5d3847b4 100644
--- a/Makefile
+++ b/Makefile
@@ -12,7 +12,7 @@ build:
 	go build -tags $(TAGS) -ldflags "-X main.version=`git describe --tags`"
 
 test:
-	go test -tags $(TAGS) ./...
+	go test -tags "integration $(TAGS)" ./...
 
 build_bitmaskd:
 	go build -tags "$(TAGS) bitmaskd" -ldflags "-X main.version=`git describe --tags`"
diff --git a/standalone/bonafide.go b/standalone/bonafide.go
index b46d9bb9..db9ef7ee 100644
--- a/standalone/bonafide.go
+++ b/standalone/bonafide.go
@@ -20,6 +20,7 @@ import (
 	"crypto/x509"
 	"encoding/json"
 	"fmt"
+	"io"
 	"io/ioutil"
 	"log"
 	"math/rand"
@@ -72,10 +73,14 @@ UN9SaWRlWKSdP4haujnzCoJbM7dU9bjvlGZNyXEekgeT0W2qFeGGp+yyUWw8tNsp
 )
 
 type bonafide struct {
-	client         *http.Client
+	client         httpClient
+	tzOffsetHours  int
 	eip            *eipService
 	defaultGateway string
 }
+type httpClient interface {
+	Post(url, contentType string, body io.Reader) (resp *http.Response, err error)
+}
 
 type eipService struct {
 	Gateways  []gateway
@@ -108,8 +113,15 @@ func newBonafide() *bonafide {
 			},
 		},
 	}
+	_, tzOffsetSeconds := time.Now().Zone()
+	tzOffsetHours := tzOffsetSeconds / secondsPerHour
 
-	return &bonafide{client, nil, ""}
+	return &bonafide{
+		client:         client,
+		tzOffsetHours:  tzOffsetHours,
+		eip:            nil,
+		defaultGateway: "",
+	}
 }
 
 func (b *bonafide) getCertPem() ([]byte, error) {
@@ -195,8 +207,6 @@ func (b *bonafide) sortGateways() {
 	}
 
 	gws := []gatewayDistance{}
-	_, tzOffset := time.Now().Zone()
-	tzOffset = tzOffset / secondsPerHour
 	for _, gw := range b.eip.Gateways {
 		distance := 13
 		if gw.Location == b.defaultGateway {
@@ -206,7 +216,7 @@ func (b *bonafide) sortGateways() {
 			if err != nil {
 				log.Printf("Error sorting gateways: %v", err)
 			} else {
-				distance = tzDistance(tzOffset, gwOffset)
+				distance = tzDistance(b.tzOffsetHours, gwOffset)
 			}
 		}
 		gws = append(gws, gatewayDistance{gw, distance})
diff --git a/standalone/bonafide_integration_test.go b/standalone/bonafide_integration_test.go
new file mode 100644
index 00000000..f162eb56
--- /dev/null
+++ b/standalone/bonafide_integration_test.go
@@ -0,0 +1,58 @@
+// +build integration
+// Copyright (C) 2018 LEAP
+//
+// This program 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.
+//
+// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package bitmask
+
+import (
+	"bytes"
+	"testing"
+)
+
+var (
+	privateKeyHeader = []byte("-----BEGIN RSA PRIVATE KEY-----")
+	certHeader       = []byte("-----BEGIN CERTIFICATE-----")
+)
+
+func TestIntegrationGetCert(t *testing.T) {
+	b := newBonafide()
+	cert, err := b.getCertPem()
+	if err != nil {
+		t.Fatal("getCert returned an error: ", err)
+	}
+
+	if !bytes.Contains(cert, privateKeyHeader) {
+		t.Errorf("No private key present: \n%q", cert)
+	}
+
+	if !bytes.Contains(cert, certHeader) {
+		t.Errorf("No cert present: \n%q", cert)
+	}
+}
+
+func TestGetGateways(t *testing.T) {
+	b := newBonafide()
+	gateways, err := b.getGateways()
+	if err != nil {
+		t.Fatal("getGateways returned an error: ", err)
+	}
+
+	for _, gw := range gateways {
+		if gw.IPAddress == "5.79.86.180" {
+			return
+		}
+	}
+	t.Errorf("5.79.86.180 not in the list")
+}
diff --git a/standalone/bonafide_test.go b/standalone/bonafide_test.go
index 152b1086..a659e46a 100644
--- a/standalone/bonafide_test.go
+++ b/standalone/bonafide_test.go
@@ -1,42 +1,100 @@
+// Copyright (C) 2018 LEAP
+//
+// This program 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.
+//
+// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+
 package bitmask
 
 import (
-	"bytes"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"reflect"
 	"testing"
 )
 
-var (
-	privateKeyHeader = []byte("-----BEGIN RSA PRIVATE KEY-----")
-	certHeader       = []byte("-----BEGIN CERTIFICATE-----")
+const (
+	certPath = "testdata/cert"
+	eipPath  = "testdata/eip-service.json"
 )
 
+type client struct {
+	path string
+}
+
+func (c client) Post(url, contentType string, body io.Reader) (resp *http.Response, err error) {
+	f, err := os.Open(c.path)
+	return &http.Response{
+		Body:       f,
+		StatusCode: 200,
+	}, err
+}
+
 func TestGetCert(t *testing.T) {
-	b := newBonafide()
+	b := bonafide{client: client{certPath}}
 	cert, err := b.getCertPem()
 	if err != nil {
 		t.Fatal("getCert returned an error: ", err)
 	}
 
-	if !bytes.Contains(cert, privateKeyHeader) {
-		t.Errorf("No private key present: \n%q", cert)
+	f, err := os.Open(certPath)
+	if err != nil {
+		t.Fatal("Can't open ", certPath, ": ", err)
 	}
+	defer f.Close()
 
-	if !bytes.Contains(cert, certHeader) {
-		t.Errorf("No cert present: \n%q", cert)
+	certData, err := ioutil.ReadAll(f)
+	if err != nil {
+		t.Fatal("Can't read all: ", err)
+	}
+	if !reflect.DeepEqual(certData, cert) {
+		t.Errorf("cert doesn't match")
 	}
 }
 
-func TestGetGateways(t *testing.T) {
-	b := newBonafide()
-	gateways, err := b.getGateways()
-	if err != nil {
-		t.Fatal("getGateways returned an error: ", err)
+func TestGatewayTzLocation(t *testing.T) {
+	// tzOffset -> location
+	values := map[int]string{
+		-12: "c",
+		-10: "a",
+		-5:  "a",
+		-3:  "a",
+		-1:  "b",
+		0:   "b",
+		2:   "b",
+		5:   "c",
+		8:   "c",
+		12:  "c",
 	}
 
-	for _, gw := range gateways {
-		if gw.IPAddress == "5.79.86.180" {
-			return
+	for tzOffset, location := range values {
+		b := bonafide{
+			client:        client{eipPath},
+			tzOffsetHours: tzOffset,
+		}
+		gateways, err := b.getGateways()
+		if err != nil {
+			t.Errorf("getGateways returned an error: %v", err)
+			continue
+		}
+		if len(gateways) < 4 {
+			t.Errorf("Wrong number of gateways: %d", len(gateways))
+			continue
+
+		}
+		if gateways[0].Location != location {
+			t.Errorf("Wrong location for tz %d: %s, expected: %s", tzOffset, gateways[0].Location, location)
 		}
 	}
-	t.Errorf("5.79.86.180 not in the list")
 }
diff --git a/standalone/testdata/cert b/standalone/testdata/cert
new file mode 100644
index 00000000..4968b3f0
--- /dev/null
+++ b/standalone/testdata/cert
@@ -0,0 +1,54 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA3TQAmZs9U6e1xqQqVWkb132AGXdaO97bsXPOrrKUp63hKeXD
+2OQbmG96H3COi0uubVFQT1cAmpuym2COtgahQlnv42p0u2CYsBqfrCHw3iSK7nf6
+Q8RaG2oUIvlQj5m4DUk1wfRBgG5z0pN2HwFWmgheoT0RnOelTO3vcLCaSJA6PF4M
+Wehg5ScXi9wr0vibKsANpqab3oUxcHEYcNcKfJKRnXryJx6ctLrRp1WPv3JAXLnn
+oUtQ00S0dSHrLED5yPwGFV08q4bkv54qFai2cPO8ITReC6BpvrilBzOjT6fjCmzm
+6MCwBot7aRHYcWJgfp7H2b2S7T2qhnC4c2u6mwIDAQABAoIBAFue83SsOS2SNJdv
+Xd18qLyLzeg+aFCOET8h8YSokSwWuEGLWqBWcxujaNjm3RPTKA89c9848RYY0VTM
+HLBGdLqv183BRVJrQzMGBAbfFA5e4nC9nxo8lPnv6SFHVNf12qceILcSPaM9nJmm
+3HEhM8afGtr8GXR8+hmwH9H0RCMzXIjO1//WrY3nfOP8LRuQpSnnhsfZRyngWhot
+xsJlDP5plFNw7J/PDLtbjnbOXOktv0fhhq7aWR+A+0s0627r7Tidk1YoNwusYJeB
+uKrzNW1+c7Z9xl2yvMQ6+0Wry7A5YUVYP/BakYb/f/skB4ox/zz8vcWeQ4ShZ7m6
+LwPN0ckCgYEA+9wjSBbOlksath3QrJywikD1sQYFOdNrINTBFQnuipKOYKLJNhQM
+OzKHY1OiO7G6FcEvz9gKYMpMyMOs8TsISyKPNOXLpnwpgIUx6WRo6qxgEuyWLpBb
+Q3Kodl1a/q51dw56pPDEATKjSB1CjXXzm717m5FimH5csPKj9SzrGecCgYEA4Nbb
+QML1Jh9cu7TvlK3WqbAFJa4Mx/0+OQ+5xlhbs/ygn3/AZiSsPWdNK11XJ25jgGJw
+AucXr/kHgwJX23kFpCYB3zZE0Vh/hOqk/KlUFmptuADIDOAVst0v8MqBLZpZessN
+TXph5VBT6P51Oz/ZLC67uno02R1vUhDMB5VCyy0CgYEAoriZuuuxUXz4pw0gU0Vw
+8gICOvsuySuFHVMX5GXkTnddsaW65kuRk3WT72KLgJHVLlUAdQKZwesyLMvvonOH
+ajPL3ltRdiDmF3j2xFnxRx1TfSaJ6U+vBya/HKo4Li+9CMy8BHDh0fxLbj4pT4gT
+el2zzNDjqK6LaG976t24j6UCgYEAyTD5uRW7cIWX4Y+i4xQ7hlQwBuucHEkMKNtd
+jZL7XC+vO4qBi+U9CyUo9KjtmCc7emKbgL1xgNICWsT6ATZmSeCIxEg3hG0Ajtu5
+Dy4mRHiv/XsViA/s2sT6ZSmQNlJrx2lzWeUtPJmIvHEWThJwLw0Sh2dbavzf5DuL
+ly2FO3ECgYEA5AKnPQo45I+abEA/zKHsKHCqBPEbIaZpJJTaiInrsLiGJf+WzN4N
+zr/VAzvY+v0X5RgZmROY5ZLPVf2fTeVNzU5WzoB78hHOI67YI2Sbq7jZlatOgX4z
+Ur2BQdT0bW6VINYpDLUvS4goW5p0nQbGItdk69yyef1v3NDbCJ/Sg+Q=
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIEnDCCAoSgAwIBAgIRAJzr5xxPLgDD+drzU+5p8cgwDQYJKoZIhvcNAQELBQAw
+dTEYMBYGA1UECgwPUmlzZXVwIE5ldHdvcmtzMRswGQYDVQQLDBJodHRwczovL3Jp
+c2V1cC5uZXQxPDA6BgNVBAMMM1Jpc2V1cCBOZXR3b3JrcyBSb290IENBIChjbGll
+bnQgY2VydGlmaWNhdGVzIG9ubHkhKTAeFw0xODEwMjMwMDAwMDBaFw0xOTAxMjMw
+MDAwMDBaMC0xKzApBgNVBAMMIlVOTElNSVRFRDVmbzdsMmxiY3g0OWR5ZzR5MWY4
+YXN3YXcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDdNACZmz1Tp7XG
+pCpVaRvXfYAZd1o73tuxc86uspSnreEp5cPY5BuYb3ofcI6LS65tUVBPVwCam7Kb
+YI62BqFCWe/janS7YJiwGp+sIfDeJIrud/pDxFobahQi+VCPmbgNSTXB9EGAbnPS
+k3YfAVaaCF6hPRGc56VM7e9wsJpIkDo8XgxZ6GDlJxeL3CvS+JsqwA2mppvehTFw
+cRhw1wp8kpGdevInHpy0utGnVY+/ckBcueehS1DTRLR1IessQPnI/AYVXTyrhuS/
+nioVqLZw87whNF4LoGm+uKUHM6NPp+MKbObowLAGi3tpEdhxYmB+nsfZvZLtPaqG
+cLhza7qbAgMBAAGjbzBtMB0GA1UdDgQWBBRwXpI96PjilFPrkK+CHUPia++ISTAL
+BgNVHQ8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwIwCQYDVR0TBAIwADAfBgNV
+HSMEGDAWgBQX9BvV5SoBAU1rol02CikJlmWARjANBgkqhkiG9w0BAQsFAAOCAgEA
+ryNFLixuicVRepocY2lTSY0cpG0eRmLuYJGupk9KeiLA5YEFzl4ZfXJLi+9UHoUR
+Bgfe6QYLBb77nO24CoeiMJQw6s593ctMLiMU++fjew31gNp6aA9DmvbLd+fNuLyO
+XObRtGw99M37cyf3ZS2SEbTBr4NBp/r3OCyUYsxPYKOzEkr9kNYa8ZZSI960i7/R
+/aiq2qemQaOQHTlmrhcBuARJoRVVlLnn2zgLSVm6ptbFLNtAk0lWriUT/WlRmn8j
+Cyn/JOuo/1wtrK1dHkaXr8bkEq1oFQzcwMN85hrZKWU0BCehELZtiUg8grqaX/sf
+/jaXD61FEqWjIXeGqY/K6ruosZCw2R8sQYzTuQNHMjxmx+J3pch7dMmJPbmA3HW2
+nA7yVp51SX8iZ26zb40S7GG6RNesU+BZxz05XVLt1GwyLx/uNxS4rFpKAT/+ifWG
+3Y1j1lMqBxx6RbuqiM1TWqU7Xtzu3hf8ytP5qP7kudXn1TyNtpZCIrzbTXbLnYiD
+nH4ZQEWGyAKBOz41eOcG6EXn0TznSGE593ueXBeFnsym7i9MjoOWNGaJ7UbkipfX
+FzxirlY5IRkWnmHCL0wGUg6YGnZ1OQ8VBBGb/dBPRMDwA7zWvoM7+3yDLR3aRaLH
+mTQzNzu3jy6CRdlpIUcPRcgbniySip1jJrHRYBui+9w=
+-----END CERTIFICATE-----
diff --git a/standalone/testdata/eip-service.json b/standalone/testdata/eip-service.json
new file mode 100644
index 00000000..d5f24134
--- /dev/null
+++ b/standalone/testdata/eip-service.json
@@ -0,0 +1,113 @@
+{
+  "gateways": [
+    {
+      "capabilities": {
+        "adblock": false,
+        "filter_dns": false,
+        "limited": false,
+        "ports": [
+          "443"
+        ],
+        "protocols": [
+          "tcp"
+        ],
+        "transport": [
+          "openvpn"
+        ],
+        "user_ips": false
+      },
+      "host": "1.example.com",
+      "ip_address": "1.1.1.1",
+      "location": "a"
+    },
+    {
+      "capabilities": {
+        "adblock": false,
+        "filter_dns": false,
+        "limited": false,
+        "ports": [
+          "443"
+        ],
+        "protocols": [
+          "tcp"
+        ],
+        "transport": [
+          "openvpn"
+        ],
+        "user_ips": false
+      },
+      "host": "2.example.com",
+      "ip_address": "2.2.2.2",
+      "location": "b"
+    },
+    {
+      "capabilities": {
+        "adblock": false,
+        "filter_dns": false,
+        "limited": false,
+        "ports": [
+          "443"
+        ],
+        "protocols": [
+          "tcp"
+        ],
+        "transport": [
+          "openvpn"
+        ],
+        "user_ips": false
+      },
+      "host": "3.example.com",
+      "ip_address": "3.3.3.3",
+      "location": "b"
+    },
+    {
+      "capabilities": {
+        "adblock": false,
+        "filter_dns": false,
+        "limited": false,
+        "ports": [
+          "443"
+        ],
+        "protocols": [
+          "tcp"
+        ],
+        "transport": [
+          "openvpn"
+        ],
+        "user_ips": false
+      },
+      "host": "4.example.com",
+      "ip_address": "4.4.4.4",
+      "location": "c"
+    }
+  ],
+  "locations": {
+    "a": {
+      "country_code": "AA",
+      "hemisphere": "N",
+      "name": "a",
+      "timezone": "-5"
+    },
+    "b": {
+      "country_code": "BB",
+      "hemisphere": "S",
+      "name": "b",
+      "timezone": "+1"
+    },
+    "c": {
+      "country_code": "CC",
+      "hemisphere": "N",
+      "name": "c",
+      "timezone": "+8"
+    }
+  },
+  "openvpn_configuration": {
+    "auth": "SHA1",
+    "cipher": "AES-128-CBC",
+    "keepalive": "10 30",
+    "tls-cipher": "DHE-RSA-AES128-SHA",
+    "tun-ipv6": true
+  },
+  "serial": 1,
+  "version": 1
+}
-- 
GitLab