github.com/avenga/couper@v1.12.2/server/http_custom_logs_test.go (about) 1 package server_test 2 3 import ( 4 "net/http" 5 "reflect" 6 "testing" 7 "time" 8 9 "github.com/google/go-cmp/cmp" 10 "github.com/sirupsen/logrus" 11 12 "github.com/avenga/couper/internal/test" 13 "github.com/avenga/couper/logging" 14 ) 15 16 func TestCustomLogs_Upstream(t *testing.T) { 17 client := test.NewHTTPClient() 18 19 shutdown, hook := newCouper("testdata/integration/logs/01_couper.hcl", test.New(t)) 20 defer shutdown() 21 22 type testCase struct { 23 path string 24 expAccess logrus.Fields 25 expUpstream logrus.Fields 26 } 27 28 hmacToken := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwic2NvcGUiOiJmb28gYmFyIiwiaWF0IjoxNTE2MjM5MDIyfQ.7wz7Z7IajfEpwYayfshag6tQVS0e0zZJyjAhuFC0L-E" 29 30 for _, tc := range []testCase{ 31 { 32 "/", 33 logrus.Fields{ 34 "api": "couper test-backend", 35 "endpoint": "couper test-backend", 36 "server": "couper test-backend", 37 }, 38 logrus.Fields{ 39 "array": []interface{}{ 40 float64(1), 41 "couper test-backend", 42 []interface{}{ 43 float64(2), 44 "couper test-backend", 45 }, 46 logrus.Fields{"x": "X"}, 47 }, 48 "bool": true, 49 "float": 1.23, 50 "int": float64(123), 51 "object": logrus.Fields{"a": "A", "b": "B", "c": float64(123)}, 52 "req": "GET", 53 "string": "couper test-backend", 54 }, 55 }, 56 { 57 "/backend", 58 logrus.Fields{"api": "couper test-backend", "server": "couper test-backend"}, 59 logrus.Fields{"backend": "couper test-backend"}, 60 }, 61 { 62 "/jwt-valid", 63 logrus.Fields{"jwt_regular": "GET", "server": "couper test-backend"}, 64 logrus.Fields{"backend": "couper test-backend"}, 65 }, 66 } { 67 t.Run(tc.path, func(st *testing.T) { 68 helper := test.New(st) 69 req, err := http.NewRequest(http.MethodGet, "http://localhost:8080"+tc.path, nil) 70 helper.Must(err) 71 72 req.Header.Set("Authorization", "Bearer "+hmacToken) 73 74 hook.Reset() 75 _, err = client.Do(req) 76 helper.Must(err) 77 78 // Wait for logs 79 time.Sleep(100 * time.Millisecond) 80 81 entries := hook.AllEntries() 82 var accessLog, upstreamLog logrus.Fields 83 for _, entry := range entries { 84 request := entry.Data["request"].(logging.Fields) 85 path, _ := request["path"].(string) 86 if entry.Data["type"] == "couper_access" { 87 accessLog = entry.Data 88 } else if entry.Data["type"] == "couper_backend" && path != "/jwks.json" { 89 upstreamLog = entry.Data 90 } 91 } 92 93 if accessLog == nil || upstreamLog == nil { 94 st.Fatalf("expected logs, got access: %p, got upstream: %p", accessLog, upstreamLog) 95 } 96 97 customAccess, ok := accessLog["custom"].(logrus.Fields) 98 if !ok { 99 st.Fatal("expected access log custom field") 100 } 101 102 customUpstream, ok := upstreamLog["custom"].(logrus.Fields) 103 if !ok { 104 st.Fatal("expected upstream log custom field") 105 } 106 107 if !cmp.Equal(tc.expAccess, customAccess) { 108 st.Error(cmp.Diff(tc.expAccess, customAccess)) 109 } 110 111 if !cmp.Equal(tc.expUpstream, customUpstream) { 112 st.Error(cmp.Diff(tc.expUpstream, customUpstream)) 113 } 114 }) 115 } 116 } 117 118 func TestCustomLogs_Local(t *testing.T) { 119 client := newClient() 120 121 shutdown, hook := newCouper("testdata/integration/logs/01_couper.hcl", test.New(t)) 122 defer shutdown() 123 124 type testCase struct { 125 name string 126 path string 127 header test.Header 128 exp logrus.Fields 129 } 130 131 for _, tc := range []testCase{ 132 {"basic-auth", "/secure", nil, logrus.Fields{"error_handler": "GET"}}, 133 {"jwt with error-handler", "/jwt", nil, logrus.Fields{"jwt_error": "GET", "jwt_regular": "GET"}}, 134 {"jwt with * error-handler", "/jwt-wildcard", nil, logrus.Fields{"jwt_error_wildcard": "GET", "jwt_regular": "GET"}}, 135 {"oauth2 error-handler", "/oauth2cb?pkcecv=qerbnr&error=qeuboub", nil, logrus.Fields{"oauth2_error": "GET", "oauth2_regular": "GET"}}, 136 {"oauth2 * error-handler", "/oauth2cb-wildcard?pkcecv=qerbnr&error=qeuboub", nil, logrus.Fields{"oauth2_wildcard_error": "GET", "oauth2_regular": "GET"}}, 137 {"saml with saml2 error-handler", "/saml-saml2/acs", nil, logrus.Fields{"saml_saml2_error": "GET", "saml_regular": "GET"}}, 138 {"saml with saml error-handler", "/saml-saml/acs", nil, logrus.Fields{"saml_saml_error": "GET", "saml_regular": "GET"}}, 139 {"saml with * error-handler", "/saml-wildcard/acs", nil, logrus.Fields{"saml_wildcard_error": "GET", "saml_regular": "GET"}}, 140 {"oidc with error-handler", "/oidc/cb", nil, logrus.Fields{"oidc_error": "GET", "oidc_regular": "GET"}}, 141 {"oidc with * error-handler", "/oidc-wildcard/cb", nil, logrus.Fields{"oidc_wildcard_error": "GET", "oidc_regular": "GET"}}, 142 {"file access", "/file.html", nil, logrus.Fields{"files": "GET"}}, 143 {"spa access", "/spa", nil, logrus.Fields{"spa": "GET"}}, 144 {"endpoint with error-handler", "/error-handler/endpoint", 145 test.Header{"Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.Qf0lkeZKZ3NJrYm3VdgiQiQ6QTrjCvISshD_q9F8GAM"}, 146 logrus.Fields{"error_handler": "GET", "jwt_regular": "GET"}}, 147 {"endpoint standard", "/standard", nil, logrus.Fields{ 148 "server": "couper test-backend", 149 "item-1": "item1", 150 "item-2": "item1", 151 }}, 152 {"endpoint sequence", "/sequence", nil, logrus.Fields{ 153 "server": "couper test-backend", 154 "seq-item-1": "item1", 155 "seq-item-2": "item1", 156 }}, 157 } { 158 t.Run(tc.name, func(st *testing.T) { 159 hook.Reset() 160 161 helper := test.New(st) 162 req, err := http.NewRequest(http.MethodGet, "http://localhost:8080"+tc.path, nil) 163 helper.Must(err) 164 165 for k, v := range tc.header { 166 req.Header.Set(k, v) 167 } 168 169 res, err := client.Do(req) 170 helper.Must(err) 171 172 helper.Must(res.Body.Close()) 173 174 // Wait for logs 175 time.Sleep(time.Second / 5) 176 177 // Access log 178 entries := hook.AllEntries() 179 if len(entries) == 0 { 180 st.Errorf("expected log entries, got none") 181 return 182 } 183 184 var accessLogEntry *logrus.Entry 185 for _, e := range entries { 186 if e.Data["type"] == "couper_access" { 187 accessLogEntry = e 188 break 189 } 190 } 191 if accessLogEntry == nil { 192 st.Fatal("Expected access log entry") 193 } 194 195 got, ok := accessLogEntry.Data["custom"].(logrus.Fields) 196 if !ok { 197 st.Fatal("expected custom log field, got none") 198 } 199 200 if !reflect.DeepEqual(tc.exp, got) { 201 st.Error(cmp.Diff(tc.exp, got)) 202 } 203 }) 204 } 205 } 206 207 func TestCustomLogs_Merge(t *testing.T) { 208 client := newClient() 209 helper := test.New(t) 210 211 shutdown, hook := newCouper("testdata/integration/logs/02_couper.hcl", test.New(t)) 212 defer shutdown() 213 214 req, err := http.NewRequest(http.MethodGet, "http://localhost:8080/", nil) 215 helper.Must(err) 216 217 hook.Reset() 218 _, err = client.Do(req) 219 helper.Must(err) 220 221 // Wait for logs 222 time.Sleep(200 * time.Millisecond) 223 224 exp := logrus.Fields{ 225 "api": true, 226 "endpoint": true, 227 "l1": "endpoint", 228 "l2": []interface{}{"server", "api", "endpoint"}, 229 "l3": []interface{}{"endpoint"}, 230 "server": true, 231 } 232 233 // Access log 234 got, ok := hook.AllEntries()[0].Data["custom"].(logrus.Fields) 235 if !ok { 236 t.Fatalf("expected\n%#v\ngot\n%#v", exp, got) 237 } 238 if !reflect.DeepEqual(exp, got) { 239 t.Errorf("expected\n%#v\ngot\n%#v", exp, got) 240 } 241 } 242 243 func TestCustomLogs_EvalError(t *testing.T) { 244 client := newClient() 245 helper := test.New(t) 246 247 shutdown, hook := newCouper("testdata/integration/logs/04_couper.hcl", test.New(t)) 248 defer shutdown() 249 250 req, err := http.NewRequest(http.MethodGet, "http://localhost:8080/", nil) 251 helper.Must(err) 252 253 hook.Reset() 254 _, err = client.Do(req) 255 helper.Must(err) 256 }