Initial cloud-services repo - gateway service + pkg modules

This commit is contained in:
Chris Rai
2026-01-30 23:14:52 -05:00
commit fbb820d7b3
1037 changed files with 171318 additions and 0 deletions

92
pkg/testhelper/assert.go Normal file
View File

@@ -0,0 +1,92 @@
package testhelper
import (
"fmt"
"reflect"
"testing"
)
func NoError(t *testing.T, name string, err error) bool {
if err == nil {
return false
}
t.Errorf(TestErrorTemplate, name, nil, err)
return true
}
func Error(t *testing.T, name string, err error) {
if err != nil {
return
}
t.Errorf(TestErrorTemplate, name, "Some error", err)
}
func getLen(object interface{}) (ok bool, length int) {
v := reflect.ValueOf(object)
defer func() {
if e := recover(); e != nil {
ok = false
}
}()
return true, v.Len()
}
func Len(t *testing.T, name string, object interface{}, length int) {
ok, l := getLen(object)
if !ok {
t.Errorf(TestErrorTemplate, name, "could not be applied builtin len", false)
}
if l != length {
t.Errorf(TestErrorTemplate, name, fmt.Sprintf("length of %d", length), l)
}
}
func Equal(t *testing.T, name string, expected interface{}, actual interface{}) {
if expected != actual {
t.Errorf(TestErrorTemplate, name, expected, actual)
}
}
func EqualByteArray(t *testing.T, name string, expected []byte, actual []byte) {
if len(expected) != len(actual) {
t.Errorf(TestErrorTemplate, fmt.Sprintf("%s len", name), len(expected), len(actual))
} else {
for i, b := range actual {
Equal(t, fmt.Sprintf("%s byte %d", name, i), expected[i], b)
}
}
}
func Same(t *testing.T, name string, expected interface{}, actual interface{}) {
if !reflect.DeepEqual(expected, actual) {
t.Errorf(TestErrorTemplate, name, expected, actual)
}
}
func True(t *testing.T, name string, actual bool) {
Equal(t, name, true, actual)
}
func MapKeyExists(t *testing.T, name string, m map[string]interface{}, key string) {
if _, ok := m[key]; !ok {
t.Errorf(TestErrorTemplate, name, nil, key)
}
}
func MapKeyNotExists(t *testing.T, name string, m map[string]interface{}, key string) {
if _, ok := m[key]; ok {
t.Errorf(TestErrorTemplate, name, key, nil)
}
}
func MapHasValue(t *testing.T, name string, m map[string]interface{}, key string, value interface{}) {
if val, ok := m[key]; ok {
if value != val {
t.Errorf(TestErrorTemplate, fmt.Sprintf("%s %s", name, key), value, val)
}
} else {
t.Errorf(TestErrorTemplate, fmt.Sprintf("%s missing %s", name, key), key, nil)
}
}

View File

@@ -0,0 +1,14 @@
package testhelper
import (
"path/filepath"
"runtime"
"strings"
)
func GetSchemaDirPath() string {
_, fn, _, _ := runtime.Caller(0)
c := strings.Split(fn, "/")
p := strings.Join(c[:len(c)-4], "/")
return filepath.Join(p, "cloud/3rdparty/common/schema")
}

View File

@@ -0,0 +1,78 @@
package testhelper
import (
"encoding/json"
"fmt"
)
type JSONComparer struct {
result map[string]interface{}
IgnoreProps []string
}
func (j *JSONComparer) Close() {
j.result = nil
j.IgnoreProps = nil
}
func (j *JSONComparer) GetDiff(reference []byte, compare []byte) (map[string]interface{}, error) {
j.result = map[string]interface{}{}
objRef := map[string]interface{}{}
objComp := map[string]interface{}{}
err := json.Unmarshal(reference, &objRef)
if err != nil {
return j.result, err
}
err = json.Unmarshal(compare, &objComp)
if err != nil {
return j.result, err
}
j.compare("", objRef, objComp)
return j.result, nil
}
func (j *JSONComparer) isMap(obj interface{}) (converted map[string]interface{}, isMap bool) {
converted, isMap = obj.(map[string]interface{})
return
}
func (j *JSONComparer) isIgnored(key string) bool {
for _, ignored := range j.IgnoreProps {
if ignored == key {
return true
}
}
return false
}
func (j JSONComparer) processMap(key string, refVal interface{}, compVal interface{}) bool {
if convRef, isRefMap := j.isMap(refVal); isRefMap {
if convCom, isComMap := j.isMap(compVal); isComMap {
j.compare(fmt.Sprintf("%s.", key), convRef, convCom)
return true
}
}
return false
}
func (j *JSONComparer) compare(prefix string, reference map[string]interface{}, compare map[string]interface{}) {
for key, refVal := range reference {
if j.isIgnored(key) {
continue
}
if comVal, ok := compare[key]; !ok {
j.result[key] = "missing"
} else {
if j.processMap(key, refVal, comVal) {
continue
}
if comVal != refVal {
j.result[prefix+key] = refVal
}
}
}
}

View File

@@ -0,0 +1,39 @@
package testhelper_test
import (
"testing"
th "fiskerinc.com/modules/testhelper"
)
func TestJSONCompare(t *testing.T) {
comparer := th.JSONComparer{
IgnoreProps: []string{"prop3"},
}
defer comparer.Close()
ref := []byte(`{"prop1": 1, "prop2": "x", "prop3": true, "prop4": true, "prop5": {"prop1": 1, "prop2": "x", "prop3": true, "prop4": true}, "prop10": 1, "prop11": "x", "prop12": true, "prop13": true, "prop14": {"prop10": 1, "prop11": "x", "prop12": true, "prop13": true}}`)
comp := []byte(`{"prop1": 2, "prop2": "X", "prop3": false, "prop4": false, "prop5": {"prop1": 3, "prop2": "Y", "prop3": false, "prop4": false}, "prop10": 1, "prop11": "x", "prop12": true, "prop13": true, "prop14": {"prop10": 1, "prop11": "x", "prop12": true, "prop13": true}}`)
diff, err := comparer.GetDiff(ref, comp)
if err != nil {
t.Errorf(th.TestErrorTemplate, "JSONComparer.GetDiff error", nil, err)
return
}
th.MapHasValue(t, "prop1 = 1", diff, "prop1", float64(1))
th.MapHasValue(t, "prop2 = X", diff, "prop2", "x")
th.MapKeyNotExists(t, "prop3 ignored", diff, "prop3")
th.MapHasValue(t, "prop4 = true", diff, "prop4", true)
th.MapHasValue(t, "prop5.prop1 = 1", diff, "prop5.prop1", float64(1))
th.MapHasValue(t, "prop5.prop2 = X", diff, "prop5.prop2", "x")
th.MapHasValue(t, "prop5.prop4 = true", diff, "prop5.prop4", true)
th.MapKeyNotExists(t, "no prop10", diff, "prop10")
th.MapKeyNotExists(t, "no prop11", diff, "prop11")
th.MapKeyNotExists(t, "no prop12", diff, "prop12")
th.MapKeyNotExists(t, "no prop13", diff, "prop13")
th.MapKeyNotExists(t, "no prop14", diff, "prop14")
th.MapKeyNotExists(t, "no prop14.prop10", diff, "prop14.prop10")
th.MapKeyNotExists(t, "no prop14.prop11", diff, "prop14.prop11")
th.MapKeyNotExists(t, "no prop14.prop12", diff, "prop14.prop12")
th.MapKeyNotExists(t, "no prop14.prop13", diff, "prop14.prop13")
}

View File

@@ -0,0 +1,61 @@
package testhelper
import (
"reflect"
"testing"
)
func PropsTester(t *testing.T, model interface{}, lookup map[string]interface{}) {
for key, value := range lookup {
val := getField(model, key)
reflectExpected := reflect.ValueOf(value)
Compare(t, key, reflectExpected, val)
}
}
func Compare(t *testing.T, key string, expectedValue, gotValue reflect.Value) bool {
// Check if they have the same type, if not thats an issue
if expectedValue.Type() != gotValue.Type() {
if expectedValue.String() == gotValue.String() {
t.Logf("Key %s val %s had mismatched types but string value matched", key, expectedValue)
return true
} else {
t.Errorf("Key %s was of type %s but got %s", key, expectedValue.Type(), gotValue.Type())
}
return false
}
// If they share the same type, and are a comparable type, so int(123) == int(123)
if gotValue.Comparable() {
if !gotValue.Equal(expectedValue) {
t.Errorf(TestErrorTemplate, key, expectedValue, gotValue)
return false
}
}
// If they are not comparable, we need to look deeper in the object
switch expectedValue.Kind() {
case reflect.Slice:
if expectedValue.Len() != gotValue.Len() {
return false
}
for x := 0; x < expectedValue.Len(); x++ {
Compare(t, key, expectedValue.Index(x), gotValue.Index(x))
}
return true
}
return false
}
// This whole type of testing is incredibly bunk
func getField(v interface{}, field string) reflect.Value {
r := reflect.ValueOf(v)
f := reflect.Indirect(r).FieldByName(field)
return f
}

63
pkg/testhelper/schema.go Normal file
View File

@@ -0,0 +1,63 @@
package testhelper
import (
"testing"
"github.com/xeipuuv/gojsonschema"
)
func NewSchemaTestHelper(t *testing.T, schemaFile string) SchemaTestHelper {
schema, err := getSchema(schemaFile)
if err != nil {
t.Fatalf(TestErrorTemplate, "RunSchemaTests", nil, err)
}
return SchemaTestHelper{
schema: schema,
t: t,
}
}
type SchemaTestHelper struct {
schema *gojsonschema.Schema
t *testing.T
}
type SchemaTest struct {
Name string
Object interface{}
}
func (h SchemaTestHelper) RunSchemaTests(tests []SchemaTest) {
for _, test := range tests {
h.runSchemaTest(test)
}
}
func (h SchemaTestHelper) ValidateSchemaObject(testName string, data []byte) {
objectLoader := gojsonschema.NewBytesLoader(data)
h.validate(testName, objectLoader)
}
func (h SchemaTestHelper) runSchemaTest(test SchemaTest) {
objectLoader := gojsonschema.NewGoLoader(test.Object)
h.validate(test.Name, objectLoader)
}
func (h SchemaTestHelper) validate(testName string, objectLoader gojsonschema.JSONLoader) {
result, err := h.schema.Validate(objectLoader)
if err != nil {
h.t.Fatalf(TestErrorTemplate, testName, nil, err)
}
if !result.Valid() {
for _, err := range result.Errors() {
h.t.Errorf(TestErrorTemplate, testName, nil, err)
}
}
}
func getSchema(filePath string) (*gojsonschema.Schema, error) {
schemaLoader := gojsonschema.NewReferenceLoader(filePath)
return gojsonschema.NewSchema(schemaLoader)
}

View File

@@ -0,0 +1,173 @@
package testhelper
import (
"bytes"
"context"
"encoding/json"
"encoding/xml"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"fiskerinc.com/modules/common/dbbasemodel"
"github.com/julienschmidt/httprouter"
)
var Now = time.Now()
var Timestamp = dbbasemodel.DBModelBase{
CreatedAt: &Now,
UpdatedAt: &Now,
}
type BasicHttpTest struct {
Name string
Request *http.Request
ExpectedStatus int
ExpectedResponse string
}
// TestErrorTemplate test failure message
const TestErrorTemplate string = "%s test.\nExpected '%v'.\nGot '%v'"
const TestErrorTemplate2 string = "%s test.\nExpected '%v':'%v'.\nGot '%v'"
// ExecHTTPHandler passes request to handler and returns test response recorder
func ExecHTTPHandler(handler http.HandlerFunc, request *http.Request) *httptest.ResponseRecorder {
recorder := httptest.NewRecorder()
handler(recorder, request)
return recorder
}
func ExecHTTPRouterHandler(handler http.HandlerFunc, routePath string, request *http.Request) *httptest.ResponseRecorder {
recorder := httptest.NewRecorder()
router := httprouter.New()
router.HandlerFunc(request.Method, routePath, handler)
router.ServeHTTP(recorder, request)
return recorder
}
func MakeTestRequest(method string, path string, body interface{}) *http.Request {
data, _ := json.Marshal(body)
req, _ := http.NewRequest(method, path, bytes.NewBuffer(data))
req.RequestURI = path
return req
}
func MakeTestRequestRawBody(method string, path string, data []byte) *http.Request {
req, _ := http.NewRequest(method, path, bytes.NewBuffer(data))
req.RequestURI = path
return req
}
func MakeXMLTestRequest(method string, path string, body interface{}) *http.Request {
data, _ := xml.Marshal(body)
req, _ := http.NewRequest(method, path, bytes.NewBuffer(data))
req.RequestURI = path
return req
}
func AddValueToContext(r *http.Request, valkey interface{}, value interface{}) *http.Request {
ctx := context.WithValue(r.Context(), valkey, value)
return r.WithContext(ctx)
}
func AddParamsToContext(r *http.Request, params httprouter.Params) *http.Request {
ctx := context.WithValue(r.Context(), httprouter.ParamsKey, params)
return r.WithContext(ctx)
}
func MakeTestRequestWithHeaders(method string, path string, headers map[string]string, body interface{}) *http.Request {
request := MakeTestRequest(method, path, body)
for key, value := range headers {
request.Header.Add(key, value)
}
return request
}
func MakeTestMultipartRequest(path string, form string, boundary string, headers map[string]string) *http.Request {
request, _ := http.NewRequest(http.MethodPost, path, ioutil.NopCloser(strings.NewReader(form)))
request.Header.Add("Content-Type", fmt.Sprintf("multipart/form-data; boundary=%s", boundary))
for key, value := range headers {
request.Header.Add(key, value)
}
return request
}
func MakeTestRequestFakeAuthHeader(method string, path string, body interface{}) *http.Request {
const token = "eyJraWQiOiJzUFVaTlZOMnZXSUhrOVd2N3FUeXROYTY5Q3NaK3JBSFloTDVtRm9QV0p3PSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJhN2Y1OTUxMi0xZWM3LTQ4OGUtYmIxYy01ZWMwMjYwNzc3NGEiLCJldmVudF9pZCI6IjdkM2Y4OWQ5LTA3ZjQtNDVlNC05ZDE4LTc4OWEwOTQzNjIwMiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE2MjU2MjA1MjYsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC51cy13ZXN0LTIuYW1hem9uYXdzLmNvbVwvdXMtd2VzdC0yX2M3UXU5MW0zSiIsImV4cCI6MTYyNTcwNjkyNiwiaWF0IjoxNjI1NjIwNTI2LCJqdGkiOiIxOTkxNWIyMC1hNWY3LTQwM2YtOWEzMi03NTUyNWRkZGNlMTQiLCJjbGllbnRfaWQiOiIyODJuOWhmZ2NoaG12cGhwamVvMGpicDkxZyIsInVzZXJuYW1lIjoiZjY4NzczOGMtNmZkNC00ZGFjLWI3MDAtMDg1OWI2OWQxODQ4In0.KD54niJe71TiVZTbiK384WTTulKWk89bdlwE-0_ldznr1nQqxinYjG8Omg1zqKvfidoD2OIpCujUhE5K6T2zMynHGxUfxPhcaux4TQw5luV1A8EMzo3bFTLnOzoo3KdtJLx7_5i3RmOSaxaXtNBau-DWWjuruN9EDgYILrwptMNaUV9MFFDrCqJxkftz0hrmce4v9xoI7F28FIqlfilvDK5YH_VuDFfNTKm8-EsXu3tt4RmRwDgSnxb9-u-bfKSy5ROOPHRb0o2-vKhP0Kk3Muf8SB8K479Ts4xxP0l1LHAKM26aAf0jQO2o1yFzAJPja5RIkLMLv_3pA7Tn5rx97A"
req := MakeTestRequestWithHeaders(method, path, map[string]string{
"Authorization": strings.Join([]string{"Bearer", token}, " "),
}, body)
identity := map[string]interface{}{
"sub": "162e57ce-39c4-40f1-a232-cea5d3fe50e4",
"email_verified": true,
"iss": "https://cognito-idp.us-west-2.amazonaws.com/us-west-2_SEgxwcFms",
"phone_number_verified": false,
"username": "162e57ce-39c4-40f1-a232-cea5d3fe50e4",
"firstname": "First",
"aud": "4jj6pokr1ajuab5t4o2i2g069l",
"event_id": "ee6eae01-eba5-48a6-92f8-e019f8a32541",
"token_use": "id",
"auth_time": 1622221405,
"phonenumber": "+17601234567",
"exp": 1622307805,
"iat": 1622221405,
"lastname": "Last",
"email": "test@fiskerinc.com",
}
ctx := req.Context()
ctx = context.WithValue(ctx, "identity", identity)
return req.WithContext(ctx)
}
func RunBasicHttpTest(t *testing.T, test BasicHttpTest, handler http.HandlerFunc) *httptest.ResponseRecorder {
w := ExecHTTPHandler(handler, test.Request)
if w.Result().StatusCode != test.ExpectedStatus {
t.Errorf(TestErrorTemplate, test.Name, test.ExpectedStatus, w.Result().StatusCode)
}
if w.Body.String() != test.ExpectedResponse {
t.Errorf(TestErrorTemplate, test.Name, test.ExpectedResponse, w.Body.String())
}
return w
}
func RunBasicHttpTests(t *testing.T, tests []BasicHttpTest, handler http.HandlerFunc) {
for _, test := range tests {
RunBasicHttpTest(t, test, handler)
}
}
func RunParamHttpTest(t *testing.T, test BasicHttpTest, handler http.HandlerFunc, routePath string) *httptest.ResponseRecorder {
w := ExecHTTPRouterHandler(handler, routePath, test.Request)
if w.Result().StatusCode != test.ExpectedStatus {
t.Errorf(TestErrorTemplate, test.Name, test.ExpectedStatus, w.Result().StatusCode)
}
if w.Body.String() != test.ExpectedResponse {
t.Errorf(TestErrorTemplate, test.Name, test.ExpectedResponse, w.Body.String())
}
return w
}
func RunParamHttpTests(t *testing.T, tests []BasicHttpTest, handler http.HandlerFunc, routePath string) {
for _, test := range tests {
RunParamHttpTest(t, test, handler, routePath)
}
}