blob: 2a08ee8e284abf14fdb73040ce150109dc766b45 [file] [log] [blame]
package api
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import (
"bytes"
"database/sql"
"encoding/json"
"errors"
"net/http"
"net/url"
"testing"
"github.com/lib/pq"
"github.com/apache/trafficcontrol/lib/go-tc"
)
func TestCamelCase(t *testing.T) {
testStrings := []string{"hello_world", "trailing_underscore_", "w_h_a_t____"}
expected := []string{"helloWorld", "trailingUnderscore", "wHAT"}
for i, str := range testStrings {
if toCamelCase(str) != expected[i] {
t.Errorf("expected: %v error, actual: %v", expected[i], toCamelCase(str))
}
}
}
// TestRespWrittenAfterErrFails tests that a WriteResp called after HandleErr will not be written.
func TestRespWrittenAfterErrFails(t *testing.T) {
w := &MockHTTPResponseWriter{}
r := &http.Request{URL: &url.URL{}}
tx := (*sql.Tx)(nil)
expectedCode := http.StatusUnauthorized
expectedUserErr := errors.New("user unauthorized")
HandleErr(w, r, tx, expectedCode, expectedUserErr, nil)
WriteResp(w, r, "should not be written")
actualCode := w.Code
statusVal := r.Context().Value(tc.StatusKey)
statusInt, ok := statusVal.(int)
if ok {
actualCode = statusInt
}
if actualCode != expectedCode {
t.Errorf("code expected: %+v, actual %+v", expectedCode, actualCode)
}
alerts := tc.Alerts{}
if err := json.Unmarshal(w.Body, &alerts); err != nil {
t.Fatalf("unmarshalling actual body: %v", err)
}
for _, alert := range alerts.Alerts {
if string(alert.Level) != tc.ErrorLevel.String() {
t.Errorf("alert level expected: '%s', actual: '%s'", tc.ErrorLevel.String(), alert.Level)
}
}
}
func TestWriteAlertsObjEmpty(t *testing.T) {
w := &MockHTTPResponseWriter{}
r := &http.Request{URL: &url.URL{}}
a := tc.Alerts{}
code := http.StatusAlreadyReported
WriteAlertsObj(w, r, code, a, code)
resp := struct {
Response interface{} `json:"response"`
}{code}
serialized, _ := json.Marshal(resp)
if !bytes.Equal(append(serialized[:], '\n'), w.Body[:]) {
t.Error("expected response to only include object")
}
writeAlertsCodeTest(t, *w, code)
}
func TestWriteAlertsObj(t *testing.T) {
w := &MockHTTPResponseWriter{}
r := &http.Request{URL: &url.URL{}}
a := tc.CreateAlerts(tc.WarnLevel, "test")
code := http.StatusAlreadyReported
WriteAlertsObj(w, r, code, a, code)
resp := struct {
tc.Alerts
Response interface{} `json:"response"`
}{a, code}
serialized, _ := json.Marshal(resp)
if !bytes.Equal(append(serialized[:], '\n'), w.Body[:]) {
t.Error("expected response to include alert")
}
writeAlertsCodeTest(t, *w, code)
}
func writeAlertsCodeTest(t *testing.T, w MockHTTPResponseWriter, code int) {
if w.Body == nil || len(w.Body) == 0 {
t.Error("expected response body to be written to")
}
if w.Code != code {
t.Errorf("expected response code %v, got %v", code, w.Code)
}
}
func TestWriteResp(t *testing.T) {
apiWriteTest(t, func(w http.ResponseWriter, r *http.Request) {
WriteResp(w, r, "foo")
})
}
func TestWriteRespRaw(t *testing.T) {
apiWriteTest(t, func(w http.ResponseWriter, r *http.Request) {
WriteRespRaw(w, r, "foo")
})
}
func TestWriteRespVals(t *testing.T) {
apiWriteTest(t, func(w http.ResponseWriter, r *http.Request) {
WriteRespVals(w, r, "foo", map[string]interface{}{"a": "b"})
})
}
func TestRespWriter(t *testing.T) {
apiWriteTest(t, func(w http.ResponseWriter, r *http.Request) {
RespWriter(w, r, nil)("foo", nil)
})
}
func TestRespWriterVals(t *testing.T) {
apiWriteTest(t, func(w http.ResponseWriter, r *http.Request) {
RespWriterVals(w, r, nil, map[string]interface{}{"a": "b"})("foo", nil)
})
}
func TestWriteRespAlert(t *testing.T) {
apiWriteTest(t, func(w http.ResponseWriter, r *http.Request) {
WriteRespAlert(w, r, tc.ErrorLevel, "foo error")
})
}
func TestWriteRespAlertObj(t *testing.T) {
apiWriteTest(t, func(w http.ResponseWriter, r *http.Request) {
WriteRespAlertObj(w, r, tc.ErrorLevel, "foo error", "bar")
})
}
// apiWriteTest tests that an API write func succeeds and writes a body and a 200.
func apiWriteTest(t *testing.T, write func(w http.ResponseWriter, r *http.Request)) {
w := &MockHTTPResponseWriter{}
r := &http.Request{URL: &url.URL{}}
write(w, r)
if w.Code == 0 {
w.Code = http.StatusOK // emulate behavior of w.Write
}
actualCode := w.Code
statusVal := r.Context().Value(tc.StatusKey)
statusInt, ok := statusVal.(int)
if ok {
actualCode = statusInt
}
expectedCode := http.StatusOK
if actualCode != expectedCode {
t.Errorf("code expected: %+v, actual %+v", expectedCode, actualCode)
}
if len(w.Body) == 0 {
t.Errorf("body len expected: >0, actual 0")
}
}
type MockHTTPResponseWriter struct {
Code int
Body []byte
}
func (i *MockHTTPResponseWriter) WriteHeader(rc int) {
i.Code = rc
}
func (i *MockHTTPResponseWriter) Write(b []byte) (int, error) {
i.Body = append(i.Body, b...)
return len(b), nil
}
func (i *MockHTTPResponseWriter) Header() http.Header {
return http.Header{}
}
func TestParseRestrictFKConstraint(t *testing.T) {
var testCases = []struct {
description string
storageError pq.Error
expectedReturnCode int
}{
{
description: "FK Constraint Error",
storageError: pq.Error{
Message: "update or delete on table \"foo\" violates foreign key constraint \"fk_foo_bar\" on table \"bar\"",
},
expectedReturnCode: http.StatusBadRequest,
},
{
description: "FK Constraint Error with underscores in table name",
storageError: pq.Error{
Message: "update or delete on table \"foo_ser\" violates foreign key constraint \"fk_foo_bar\" on table \"bar_cap\"",
},
expectedReturnCode: http.StatusBadRequest,
},
{
description: "Non FK Constraint Error",
storageError: pq.Error{
Message: "connection error",
},
expectedReturnCode: http.StatusOK,
},
}
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
t.Log("Starting test scenario: ", tc.description)
_, _, sc := parseRestrictFKConstraint(&tc.storageError)
if sc != tc.expectedReturnCode {
t.Errorf("code expected: %v, actual %v", tc.expectedReturnCode, sc)
}
})
}
}