<commit_before>package integration

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/EaseApp/web-backend/src/app/models"
	"github.com/EaseApp/web-backend/src/db"
	"github.com/EaseApp/web-backend/src/server"
	r "github.com/dancannon/gorethink"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

var localDBAddr = "localhost:28015"

type errorResp struct {
	Err string `json:"error"`
}

func TestSignUp(t *testing.T) {
	server, _ := setUpServer(t)
	defer server.Close()

	testcases := []struct {
		input            string
		expectedCode     int
		expectedError    string
		expectedUsername string
	}{
		{
			input:            `{"username": "theuser", "password": "<PASSWORD>"}`,
			expectedCode:     http.StatusOK,
			expectedError:    "",
			expectedUsername: "theuser",
		},
		{
			input:            `{"username": "theuser", "password": "<PASSWORD>"}`,
			expectedCode:     http.StatusBadRequest,
			expectedError:    "A user with that name already exists",
			expectedUsername: "",
		},
		{
			input:            `{"password": "<PASSWORD>"}`,
			expectedCode:     http.StatusBadRequest,
			expectedError:    "Username or password cannot be blank",
			expectedUsername: "",
		},
	}

	for _, testcase := range testcases {
		resp := sendJSON(testcase.input, "", server.URL, "/users/sign_up", "POST", t)

		assert.Equal(t, testcase.expectedCode, resp.StatusCode)

		// Error expected.
		if testcase.expectedError != "" {
			var errStruct errorResp
			json.NewDecoder(resp.Body).Decode(&errStruct)
			assert.Equal(t, testcase.expectedError, errStruct.Err)
		} else { // No error expected.
			var userStruct models.User
			json.NewDecoder(resp.Body).Decode(&userStruct)
			assert.Equal(t, testcase.expectedUsername, userStruct.Username)
		}
	}
}

func TestDeleteApplication(t *testing.T) {
	server, _ := setUpServer(t)
	defer server.Close()

	apiToken := createTestUser(server.URL, t)

	// Create two applications.
	resp := sendJSON("", apiToken, server.URL, "/users/applications/bestappevar", "POST", t)
	require.Equal(t, http.StatusOK, resp.StatusCode)
	resp = sendJSON("", apiToken, server.URL, "/users/applications/lol", "POST", t)
	require.Equal(t, http.StatusOK, resp.StatusCode)

	testcases := []struct {
		token         string
		appToDelete   string
		appNames      []string
		expectedCode  int
		expectedError string
	}{
		// Invalid token.
		{
			token:         "bad<PASSWORD>",
			appToDelete:   "lol",
			appNames:      nil,
			expectedCode:  http.StatusUnauthorized,
			expectedError: "Authorization token does not match.",
		},
		// Invalid app name.
		{
			token:         apiToken,
			appToDelete:   "idontexist",
			appNames:      nil,
			expectedCode:  http.StatusInternalServerError,
			expectedError: "Failed to delete application",
		},
		// Valid token and the app deleted.
		{
			token:         apiToken,
			appToDelete:   "bestappevar",
			appNames:      []string{"lol"},
			expectedCode:  http.StatusOK,
			expectedError: "",
		},
		// Valid token and both apps deleted.
		{
			token:         apiToken,
			appToDelete:   "lol",
			appNames:      []string{},
			expectedCode:  http.StatusOK,
			expectedError: "",
		},
	}

	for _, testcase := range testcases {
		resp := sendJSON("", testcase.token, server.URL, "/users/applications/"+testcase.appToDelete, "DELETE", t)

		assert.Equal(t, testcase.expectedCode, resp.StatusCode)

		// Error expected.
		if testcase.expectedError != "" {
			var errStruct errorResp
			json.NewDecoder(resp.Body).Decode(&errStruct)
			assert.Equal(t, testcase.expectedError, errStruct.Err)
		} else { // No error expected.
			var apps []models.Application
			json.NewDecoder(resp.Body).Decode(&apps)
			for i, app := range apps {
				assert.Equal(t, testcase.appNames[i], app.Name)
				assert.NotEmpty(t, app.AppToken)
			}
		}
	}
}

func TestListApplications(t *testing.T) {
	server, client := setUpServer(t)
	defer server.Close()

	apiToken := createTestUser(server.URL, t)

	// Create two applications.
	resp := sendJSON("", apiToken, server.URL, "/users/applications/bestappevar", "POST", t)
	require.Equal(t, http.StatusOK, resp.StatusCode)
	resp = sendJSON("", apiToken, server.URL, "/users/applications/lol", "POST", t)
	require.Equal(t, http.StatusOK, resp.StatusCode)

	testcases := []struct {
		token         string
		appNames      []string
		expectedCode  int
		expectedError string
	}{
		// Invalid token.
		{
			token:         "badtoken",
			appNames:      nil,
			expectedCode:  http.StatusUnauthorized,
			expectedError: "Authorization token does not match.",
		},
		// Valid token and two apps returned.
		{
			token:         apiToken,
			appNames:      []string{"bestappevar", "lol"},
			expectedCode:  http.StatusOK,
			expectedError: "",
		},
	}

	for _, testcase := range testcases {
		resp := sendJSON("", testcase.token, server.URL, "/users/applications", "GET", t)

		assert.Equal(t, testcase.expectedCode, resp.StatusCode)

		// Error expected.
		if testcase.expectedError != "" {
			var errStruct errorResp
			json.NewDecoder(resp.Body).Decode(&errStruct)
			assert.Equal(t, testcase.expectedError, errStruct.Err)
		} else { // No error expected.
			var apps []models.Application
			json.NewDecoder(resp.Body).Decode(&apps)
			for i, app := range apps {
				assert.Equal(t, testcase.appNames[i], app.Name)
				assert.NotEmpty(t, app.AppToken)
			}
		}
	}

	// Delete the created application tables.
	r.DB("test").TableDrop("ronswanson_bestappevar").RunWrite(client.Session)
	r.DB("test").TableDrop("ronswanson_lol").RunWrite(client.Session)
}

func TestCreateApplication(t *testing.T) {
	server, client := setUpServer(t)
	defer server.Close()

	apiToken := createTestUser(server.URL, t)

	testcases := []struct {
		token         string
		appName       string
		expectedCode  int
		expectedError string
	}{
		// Invalid token.
		{
			token:         "badtoken",
			appName:       "lol",
			expectedCode:  http.StatusUnauthorized,
			expectedError: "Authorization token does not match.",
		},
		// Valid token and created app.
		{
			token:         apiToken,
			appName:       "bestappevar",
			expectedCode:  http.StatusOK,
			expectedError: "",
		},
	}

	for _, testcase := range testcases {
		resp := sendJSON("", testcase.token, server.URL, "/users/applications/"+testcase.appName, "POST", t)

		assert.Equal(t, testcase.expectedCode, resp.StatusCode)

		// Error expected.
		if testcase.expectedError != "" {
			var errStruct errorResp
			json.NewDecoder(resp.Body).Decode(&errStruct)
			assert.Equal(t, testcase.expectedError, errStruct.Err)
		} else { // No error expected.
			var app models.Application
			json.NewDecoder(resp.Body).Decode(&app)
			assert.Equal(t, testcase.appName, app.Name)
			assert.NotEmpty(t, app.AppToken)
		}
	}

	// Delete the created application table.
	r.DB("test").TableDrop("ronswanson_bestappevar").RunWrite(client.Session)
}

func TestSignIn(t *testing.T) {
	server, _ := setUpServer(t)
	defer server.Close()

	userAPIToken := createTestUser(server.URL, t)

	testcases := []struct {
		input            string
		expectedCode     int
		expectedError    string
		expectedUsername string
		expectedAPIToken string
	}{
		// Valid login.
		{
			input:            `{"username": "ronswanson", "password": "<PASSWORD>"}`,
			expectedCode:     http.StatusOK,
			expectedError:    "",
			expectedUsername: "ronswanson",
			expectedAPIToken: userAPIToken,
		},
		// Invalid username.
		{
			input:            `{"username": "anneperkins", "password": "<PASSWORD>"}`,
			expectedCode:     http.StatusUnauthorized,
			expectedError:    "Couldn't find user with that username",
			expectedUsername: "",
			expectedAPIToken: "",
		},
		// Invalid password.
		{
			input:            `{"username": "ronswanson", "password": "<PASSWORD>"}`,
			expectedCode:     http.StatusUnauthorized,
			expectedError:    "Password was invalid",
			expectedUsername: "",
			expectedAPIToken: "",
		},
	}

	for _, testcase := range testcases {
		resp := sendJSON(testcase.input, "", server.URL, "/users/sign_in", "POST", t)

		assert.Equal(t, testcase.expectedCode, resp.StatusCode)

		// Error expected.
		if testcase.expectedError != "" {
			var errStruct errorResp
			json.NewDecoder(resp.Body).Decode(&errStruct)
			assert.Equal(t, testcase.expectedError, errStruct.Err)
		} else { // No error expected.
			var userStruct models.User
			json.NewDecoder(resp.Body).Decode(&userStruct)
			assert.Equal(t, testcase.expectedUsername, userStruct.Username)
			assert.Equal(t, testcase.expectedAPIToken, userStruct.APIToken)
		}
	}

}

var testUserUsername = "ronswanson"
var testUserPass = "<PASSWORD>"

// createTestUser creates a test user and returns its api token.
func createTestUser(url string, t *testing.T) string {
	resp := sendJSON(`{"username":"ronswanson","password":"<PASSWORD>"}`,
		"", url, "/users/sign_up", "POST", t)
	require.Equal(t, http.StatusOK, resp.StatusCode)
	var user models.User
	err := json.NewDecoder(resp.Body).Decode(&user)
	require.NoError(t, err)
	return user.APIToken
}

func sendJSON(jsonInput, token, url, path, method string, t *testing.T) *http.Response {
	var jsonStr = []byte(jsonInput)
	req, err := http.NewRequest(method, url+path, bytes.NewBuffer(jsonStr))<commit_msg>CRUD integration tests woooork! :D<commit_after>package integration

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/EaseApp/web-backend/src/app/models"
	"github.com/EaseApp/web-backend/src/db"
	"github.com/EaseApp/web-backend/src/server"
	r "github.com/dancannon/gorethink"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

var localDBAddr = "localhost:28015"

type errorResp struct {
	Err string `json:"error"`
}

func TestSignUp(t *testing.T) {
	server, _ := setUpServer(t)
	defer server.Close()

	testcases := []struct {
		input            string
		expectedCode     int
		expectedError    string
		expectedUsername string
	}{
		{
			input:            `{"username": "theuser", "password": "<PASSWORD>"}`,
			expectedCode:     http.StatusOK,
			expectedError:    "",
			expectedUsername: "theuser",
		},
		{
			input:            `{"username": "theuser", "password": "<PASSWORD>"}`,
			expectedCode:     http.StatusBadRequest,
			expectedError:    "A user with that name already exists",
			expectedUsername: "",
		},
		{
			input:            `{"password": "<PASSWORD>"}`,
			expectedCode:     http.StatusBadRequest,
			expectedError:    "Username or password cannot be blank",
			expectedUsername: "",
		},
	}

	for _, testcase := range testcases {
		resp := sendJSON(testcase.input, "", server.URL, "/users/sign_up", "POST", t)

		assert.Equal(t, testcase.expectedCode, resp.StatusCode)

		// Error expected.
		if testcase.expectedError != "" {
			var errStruct errorResp
			json.NewDecoder(resp.Body).Decode(&errStruct)
			assert.Equal(t, testcase.expectedError, errStruct.Err)
		} else { // No error expected.
			var userStruct models.User
			json.NewDecoder(resp.Body).Decode(&userStruct)
			assert.Equal(t, testcase.expectedUsername, userStruct.Username)
		}
	}
}

func TestDeleteApplication(t *testing.T) {
	server, _ := setUpServer(t)
	defer server.Close()

	apiToken := createTestUser(server.URL, t)

	// Create two applications.
	resp := sendJSON("", apiToken, server.URL, "/users/applications/bestappevar", "POST", t)
	require.Equal(t, http.StatusOK, resp.StatusCode)
	resp = sendJSON("", apiToken, server.URL, "/users/applications/lol", "POST", t)
	require.Equal(t, http.StatusOK, resp.StatusCode)

	testcases := []struct {
		token         string
		appToDelete   string
		appNames      []string
		expectedCode  int
		expectedError string
	}{
		// Invalid token.
		{
			token:         "bad<PASSWORD>",
			appToDelete:   "lol",
			appNames:      nil,
			expectedCode:  http.StatusUnauthorized,
			expectedError: "Authorization token does not match.",
		},
		// Invalid app name.
		{
			token:         apiToken,
			appToDelete:   "idontexist",
			appNames:      nil,
			expectedCode:  http.StatusInternalServerError,
			expectedError: "Failed to delete application",
		},
		// Valid token and the app deleted.
		{
			token:         apiToken,
			appToDelete:   "bestappevar",
			appNames:      []string{"lol"},
			expectedCode:  http.StatusOK,
			expectedError: "",
		},
		// Valid token and both apps deleted.
		{
			token:         apiToken,
			appToDelete:   "lol",
			appNames:      []string{},
			expectedCode:  http.StatusOK,
			expectedError: "",
		},
	}

	for _, testcase := range testcases {
		resp := sendJSON("", testcase.token, server.URL, "/users/applications/"+testcase.appToDelete, "DELETE", t)

		assert.Equal(t, testcase.expectedCode, resp.StatusCode)

		// Error expected.
		if testcase.expectedError != "" {
			var errStruct errorResp
			json.NewDecoder(resp.Body).Decode(&errStruct)
			assert.Equal(t, testcase.expectedError, errStruct.Err)
		} else { // No error expected.
			var apps []models.Application
			json.NewDecoder(resp.Body).Decode(&apps)
			for i, app := range apps {
				assert.Equal(t, testcase.appNames[i], app.Name)
				assert.NotEmpty(t, app.AppToken)
			}
		}
	}
}

func TestListApplications(t *testing.T) {
	server, client := setUpServer(t)
	defer server.Close()

	apiToken := createTestUser(server.URL, t)

	// Create two applications.
	resp := sendJSON("", apiToken, server.URL, "/users/applications/bestappevar", "POST", t)
	require.Equal(t, http.StatusOK, resp.StatusCode)
	resp = sendJSON("", apiToken, server.URL, "/users/applications/lol", "POST", t)
	require.Equal(t, http.StatusOK, resp.StatusCode)

	testcases := []struct {
		token         string
		appNames      []string
		expectedCode  int
		expectedError string
	}{
		// Invalid token.
		{
			token:         "badtoken",
			appNames:      nil,
			expectedCode:  http.StatusUnauthorized,
			expectedError: "Authorization token does not match.",
		},
		// Valid token and two apps returned.
		{
			token:         apiToken,
			appNames:      []string{"bestappevar", "lol"},
			expectedCode:  http.StatusOK,
			expectedError: "",
		},
	}

	for _, testcase := range testcases {
		resp := sendJSON("", testcase.token, server.URL, "/users/applications", "GET", t)

		assert.Equal(t, testcase.expectedCode, resp.StatusCode)

		// Error expected.
		if testcase.expectedError != "" {
			var errStruct errorResp
			json.NewDecoder(resp.Body).Decode(&errStruct)
			assert.Equal(t, testcase.expectedError, errStruct.Err)
		} else { // No error expected.
			var apps []models.Application
			json.NewDecoder(resp.Body).Decode(&apps)
			for i, app := range apps {
				assert.Equal(t, testcase.appNames[i], app.Name)
				assert.NotEmpty(t, app.AppToken)
			}
		}
	}

	// Delete the created application tables.
	r.DB("test").TableDrop("ronswanson_bestappevar").RunWrite(client.Session)
	r.DB("test").TableDrop("ronswanson_lol").RunWrite(client.Session)
}

func TestCreateApplication(t *testing.T) {
	server, client := setUpServer(t)
	defer server.Close()

	apiToken := createTestUser(server.URL, t)

	testcases := []struct {
		token         string
		appName       string
		expectedCode  int
		expectedError string
	}{
		// Invalid token.
		{
			token:         "bad<PASSWORD>",
			appName:       "lol",
			expectedCode:  http.StatusUnauthorized,
			expectedError: "Authorization token does not match.",
		},
		// Valid token and created app.
		{
			token:         apiToken,
			appName:       "bestappevar",
			expectedCode:  http.StatusOK,
			expectedError: "",
		},
	}

	for _, testcase := range testcases {
		resp := sendJSON("", testcase.token, server.URL, "/users/applications/"+testcase.appName, "POST", t)

		assert.Equal(t, testcase.expectedCode, resp.StatusCode)

		// Error expected.
		if testcase.expectedError != "" {
			var errStruct errorResp
			json.NewDecoder(resp.Body).Decode(&errStruct)
			assert.Equal(t, testcase.expectedError, errStruct.Err)
		} else { // No error expected.
			var app models.Application
			json.NewDecoder(resp.Body).Decode(&app)
			assert.Equal(t, testcase.appName, app.Name)
			assert.NotEmpty(t, app.AppToken)
		}
	}

	// Delete the created application table.
	r.DB("test").TableDrop("ronswanson_bestappevar").RunWrite(client.Session)
}

func TestSignIn(t *testing.T) {
	server, _ := setUpServer(t)
	defer server.Close()

	userAPIToken := createTestUser(server.URL, t)

	testcases := []struct {
		input            string
		expectedCode     int
		expectedError    string
		expectedUsername string
		expectedAPIToken string
	}{
		// Valid login.
		{
			input:            `{"username": "ronswanson", "password": "<PASSWORD>"}`,
			expectedCode:     http.StatusOK,
			expectedError:    "",
			expectedUsername: "ronswanson",
			expectedAPIToken: userAPIToken,
		},
		// Invalid username.
		{
			input:            `{"username": "anneperkins", "password": "<PASSWORD>"}`,
			expectedCode:     http.StatusUnauthorized,
			expectedError:    "Couldn't find user with that username",
			expectedUsername: "",
			expectedAPIToken: "",
		},
		// Invalid password.
		{
			input:            `{"username": "ronswanson", "password": "<PASSWORD>"}`,
			expectedCode:     http.StatusUnauthorized,
			expectedError:    "Password was invalid",
			expectedUsername: "",
			expectedAPIToken: "",
		},
	}

	for _, testcase := range testcases {
		resp := sendJSON(testcase.input, "", server.URL, "/users/sign_in", "POST", t)

		assert.Equal(t, testcase.expectedCode, resp.StatusCode)

		// Error expected.
		if testcase.expectedError != "" {
			var errStruct errorResp
			json.NewDecoder(resp.Body).Decode(&errStruct)
			assert.Equal(t, testcase.expectedError, errStruct.Err)
		} else { // No error expected.
			var userStruct models.User
			json.NewDecoder(resp.Body).Decode(&userStruct)
			assert.Equal(t, testcase.expectedUsername, userStruct.Username)
			assert.Equal(t, testcase.expectedAPIToken, userStruct.APIToken)
		}
	}

}

func TestSaveReadAndDeleteAppDataEndpoints(t *testing.T) {
	// This test isn't as extensive as some of the other ones because these components are already
	// tested well in models/application_test.

	server, client := setUpServer(t)
	defer server.Close()

	appToken := createTestApplication(server.URL, t)

	resp := sendJSON(`{"path":"/hello", "data":{"nested":"objects", "yes": 1}}`,
		appToken, server.URL, "/api/data/ronswanson/bestappevar", "POST", t)
	assert.Equal(t, http.StatusOK, resp.StatusCode)

	resp = sendJSON(`{"path":"/hello"}`,
		appToken, server.URL, "/api/data/ronswanson/bestappevar", "GET", t)
	assert.Equal(t, http.StatusOK, resp.StatusCode)

	var data1 interface{}
	err := json.NewDecoder(resp.Body).Decode(&data1)
	assert.NoError(t, err)
	assert.Equal(t, interface{}(map[string]interface{}{"nested": "objects", "yes": float64(1)}), data1)

	resp = sendJSON(`{"path":"/hello/yes"}`,
		appToken, server.URL, "/api/data/ronswanson/bestappevar", "DELETE", t)
	assert.Equal(t, http.StatusOK, resp.StatusCode)

	resp = sendJSON(`{"path":"/hello"}`,
		appToken, server.URL, "/api/data/ronswanson/bestappevar", "GET", t)
	assert.Equal(t, http.StatusOK, resp.StatusCode)

	var data2 interface{}
	err = json.NewDecoder(resp.Body).Decode(&data2)
	assert.NoError(t, err)
	assert.Equal(t, interface{}(map[string]interface{}{"nested": "objects"}), data2)

	// Delete the created application table.
	r.DB("test").TableDrop("ronswanson_bestappevar").RunWrite(client.Session)
}

var testUserUsername = "ronswanson"
var testUserPass = "<PASSWORD>"
var testAppName = "bestappevar"

// createTestApplication creates a test application (under a new user) and returns its app token.
func createTestApplication(url string, t *testing.T) string {
	apiToken := createTestUser(url, t)

	resp := sendJSON("", apiToken, url, "/users/applications/bestappevar", "POST", t)
	require.Equal(t, http.StatusOK, resp.StatusCode)

	var app models.Application
	err := json.NewDecoder(resp.Body).Decode(&app)
	require.NoError(t, err)
	return app.AppToken
}

// createTestUser creates a test user and returns its api token.
func createTestUser(url string, t *testing.T) string {
	resp := sendJSON(`{"username":"ronswanson","password":"<PASSWORD>"}`,
		"", url, "/users/sign_up", "POST", t)
	require.Equal(t, http.StatusOK, resp.StatusCode)
	var user models.User
	err := json.NewDecoder(resp.Body).Decode(&user)
	require.NoError(t, err)
	return user.APIToken
}

func sendJSON(jsonInput, token, url, path, method string, t *testing.T) *http.Response {
	var jsonStr = []byte(jsonInput)
	req, err := http.NewRequest(method, url+path, bytes.NewBuffer(jsonStr))
{'fraction_non_alphanumeric': 0.14317051769523254, 'fraction_numerical': 0.0009749439407234084, 'mean_word_length': 3.6246618575293055, 'pattern_counts': {'":': 36, '<': 22, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6797404', 'n_tokens_mistral': 7337, 'n_tokens_neox': 6678, 'n_words': 1440}