Skip to content

Commit

Permalink
Adding unit tests for fake server (#50)
Browse files Browse the repository at this point in the history
  • Loading branch information
aburgel authored Apr 9, 2020
1 parent d6dd547 commit 4115135
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 21 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
*.swo
*.swp
/.idea/**/workspace.xml
Expand Down
17 changes: 9 additions & 8 deletions fakeassignments/fakeassignments.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,29 @@ package fakeassignments
import (
"io/ioutil"
"os"
"os/user"

"github.com/Betterment/testtrack-cli/paths"

"gopkg.in/yaml.v2"
)

// Read reads or creates the assignment file
func Read() (*map[string]string, error) {
user, err := user.Current()
configDir, err := paths.FakeServerConfigDir()
if err != nil {
return nil, err
}
if _, err := os.Stat(user.HomeDir + "/.testtrack/assignments.yml"); os.IsNotExist(err) {
err := os.MkdirAll(user.HomeDir+"/.testtrack", 0755)
if _, err := os.Stat(*configDir + "/assignments.yml"); os.IsNotExist(err) {
err := os.MkdirAll(*configDir, 0755)
if err != nil {
return nil, err
}
err = ioutil.WriteFile(user.HomeDir+"/.testtrack/assignments.yml", []byte("{}"), 0644)
err = ioutil.WriteFile(*configDir+"/assignments.yml", []byte("{}"), 0644)
if err != nil {
return nil, err
}
}
assignmentsBytes, err := ioutil.ReadFile(user.HomeDir + "/.testtrack/assignments.yml")
assignmentsBytes, err := ioutil.ReadFile(*configDir + "/assignments.yml")
if err != nil {
return nil, err
}
Expand All @@ -38,15 +39,15 @@ func Read() (*map[string]string, error) {

// Write dumps the assignment file to disk
func Write(assignments *map[string]string) error {
user, err := user.Current()
configDir, err := paths.FakeServerConfigDir()
if err != nil {
return err
}
bytes, err := yaml.Marshal(assignments)
if err != nil {
return err
}
err = ioutil.WriteFile(user.HomeDir+"/.testtrack/assignments.yml", bytes, 0644)
err = ioutil.WriteFile(*configDir+"/assignments.yml", bytes, 0644)
if err != nil {
return err
}
Expand Down
14 changes: 9 additions & 5 deletions fakeserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ func createCors() *cors.Cors {

// Start the server
func Start(port int) {
handler := createHandler()

listenOn := fmt.Sprintf("127.0.0.1:%d", port)
logger.Printf("testtrack server listening on %s", listenOn)
logger.Fatalf("fatal - %s", http.ListenAndServe(listenOn, handler))
}

func createHandler() http.Handler {
logger = log.New(os.Stdout, "", log.LstdFlags)

r := mux.NewRouter()
Expand All @@ -73,11 +81,7 @@ func Start(port int) {

r.Use(loggingMiddleware)

handler := createCors().Handler(r)

listenOn := fmt.Sprintf("127.0.0.1:%d", port)
logger.Printf("testtrack server listening on %s", listenOn)
logger.Fatalf("fatal - %s", http.ListenAndServe(listenOn, handler))
return createCors().Handler(r)
}

func (s *server) handleGet(pattern string, responseFunc func() (interface{}, error)) {
Expand Down
152 changes: 152 additions & 0 deletions fakeserver/server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package fakeserver

import (
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
"testing"

"github.com/Betterment/testtrack-cli/fakeassignments"

"encoding/json"

"github.com/stretchr/testify/require"
)

var testSchema = `
serializer_version: 1
schema_version: "2020011774023"
splits:
- name: test.test_experiment
weights:
control: 60
treatment: 40
`

func TestMain(m *testing.M) {
current, exists := os.LookupEnv("TESTTRACK_FAKE_SERVER_CONFIG_DIR")

dir, err := ioutil.TempDir("", "testtrack-cli")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(dir) // clean up

schemasDir := filepath.Join(dir, "schemas")
if err := os.MkdirAll(schemasDir, 0755); err != nil {
log.Fatal(err)
}

schemaContent := []byte(testSchema)
if err := ioutil.WriteFile(filepath.Join(schemasDir, "test.yml"), schemaContent, 0644); err != nil {
log.Fatal(err)
}

os.Setenv("TESTTRACK_FAKE_SERVER_CONFIG_DIR", dir)
exitCode := m.Run()
if exists {
os.Setenv("TESTTRACK_FAKE_SERVER_CONFIG_DIR", current)
}
os.Exit(exitCode)
}

func TestSplitRegistry(t *testing.T) {
t.Run("it loads split registry v2", func(t *testing.T) {
w := httptest.NewRecorder()
h := createHandler()

h.ServeHTTP(w, httptest.NewRequest("GET", "/api/v2/split_registry", nil))

require.Equal(t, http.StatusOK, w.Code)

registry := v2SplitRegistry{}
err := json.Unmarshal(w.Body.Bytes(), &registry)
require.Nil(t, err)

require.Equal(t, 1, registry.ExperienceSamplingWeight)
require.Equal(t, 60, registry.Splits["test.test_experiment"].Weights["control"])
require.Equal(t, 40, registry.Splits["test.test_experiment"].Weights["treatment"])
require.Equal(t, false, registry.Splits["test.test_experiment"].FeatureGate)
})

t.Run("it loads split registry v3", func(t *testing.T) {
w := httptest.NewRecorder()
h := createHandler()

h.ServeHTTP(w, httptest.NewRequest("GET", "/api/v3/builds/2020-01-02T03:04:05/split_registry", nil))

require.Equal(t, http.StatusOK, w.Code)

registry := v2SplitRegistry{}
err := json.Unmarshal(w.Body.Bytes(), &registry)
require.Nil(t, err)

require.Equal(t, 1, registry.ExperienceSamplingWeight)
require.Equal(t, 60, registry.Splits["test.test_experiment"].Weights["control"])
require.Equal(t, 40, registry.Splits["test.test_experiment"].Weights["treatment"])
require.Equal(t, false, registry.Splits["test.test_experiment"].FeatureGate)
})
}

func TestCors(t *testing.T) {
os.Setenv("TESTTRACK_ALLOWED_ORIGINS", "allowed.com")

t.Run("it fails cors with an unallowed origin", func(t *testing.T) {
w := httptest.NewRecorder()
h := createHandler()

request := httptest.NewRequest("GET", "/api/v2/split_registry", nil)
request.Header.Add("Origin", "http://www.denied.com")

h.ServeHTTP(w, request)

require.Equal(t, http.StatusOK, w.Code)
require.Equal(t, "", w.HeaderMap.Get("Access-Control-Allow-Origin"))
})

t.Run("it passes cors with an allowed origin", func(t *testing.T) {
w := httptest.NewRecorder()
h := createHandler()

request := httptest.NewRequest("GET", "/api/v2/split_registry", nil)
request.Header.Add("Origin", "http://www.allowed.com")

h.ServeHTTP(w, request)

require.Equal(t, http.StatusOK, w.Code)
require.Equal(t, "http://www.allowed.com", w.HeaderMap.Get("Access-Control-Allow-Origin"))
})

os.Unsetenv("TESTTRACK_ALLOWED_ORIGINS")
}

func TestPersistAssignment(t *testing.T) {
os.Remove("testdata/assignments.yml")

t.Run("it persists assignments to yaml", func(t *testing.T) {
w := httptest.NewRecorder()
h := createHandler()

data := url.Values{}
data.Set("split_name", "test.test_experiment")
data.Set("variant", "control")

request := httptest.NewRequest("POST", "/api/v1/assignment_override", strings.NewReader(data.Encode()))
request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
request.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))

h.ServeHTTP(w, request)

require.Equal(t, http.StatusNoContent, w.Code)

assignments, err := fakeassignments.Read()
require.Nil(t, err)
require.Equal(t, "control", (*assignments)["test.test_experiment"])
})
}
20 changes: 20 additions & 0 deletions paths/paths.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package paths

import (
"os"
"os/user"
)

// FakeServerConfigDir return the fake server's configuration directory
func FakeServerConfigDir() (*string, error) {
user, err := user.Current()
if err != nil {
return nil, err
}
configDir, ok := os.LookupEnv("TESTTRACK_FAKE_SERVER_CONFIG_DIR")
if !ok {
configDir = user.HomeDir + "/.testtrack"
}

return &configDir, nil
}
16 changes: 8 additions & 8 deletions schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import (
"fmt"
"io/ioutil"
"os"
"os/user"
"path"
"path/filepath"
"sort"

"github.com/Betterment/testtrack-cli/migrationloaders"
"github.com/Betterment/testtrack-cli/paths"
"github.com/Betterment/testtrack-cli/serializers"
"github.com/Betterment/testtrack-cli/splits"
"gopkg.in/yaml.v2"
Expand Down Expand Up @@ -69,20 +69,20 @@ func Link(force bool) error {
if _, err := os.Stat("testtrack/schema.yml"); os.IsNotExist(err) {
return errors.New("testtrack/schema.yml does not exist. Are you in your app root dir? If so, call testtrack init_project first")
}
user, err := user.Current()
dir, err := os.Getwd()
if err != nil {
return err
}
dir, err := os.Getwd()
dirname := path.Base(dir)
configDir, err := paths.FakeServerConfigDir()
if err != nil {
return err
}
dirname := path.Base(dir)
err = os.MkdirAll(user.HomeDir+"/.testtrack/schemas", 0755)
err = os.MkdirAll(*configDir+"/schemas", 0755)
if err != nil {
return err
}
path := fmt.Sprintf("%s/.testtrack/schemas/%s.yml", user.HomeDir, dirname)
path := fmt.Sprintf("%s/schemas/%s.yml", *configDir, dirname)
if force {
os.Remove(path) // If this fails it might just not exist, we'll error on the next line if something else is up
}
Expand All @@ -91,11 +91,11 @@ func Link(force bool) error {

// ReadMerged merges schemas linked at ~/testtrack/schemas into a single virtual schema
func ReadMerged() (*serializers.Schema, error) {
user, err := user.Current()
configDir, err := paths.FakeServerConfigDir()
if err != nil {
return nil, err
}
paths, err := filepath.Glob(user.HomeDir + "/.testtrack/schemas/*.yml")
paths, err := filepath.Glob(*configDir + "/schemas/*.yml")
if err != nil {
return nil, err
}
Expand Down

0 comments on commit 4115135

Please sign in to comment.