| /* |
| Copyright 2017 Google Inc. |
| |
| Licensed 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. |
| */ |
| |
| package mock |
| |
| import ( |
| "bytes" |
| "compress/gzip" |
| "crypto/x509" |
| "encoding/json" |
| "encoding/pem" |
| "fmt" |
| "io/ioutil" |
| "net/http" |
| "os" |
| "testing" |
| "time" |
| |
| "io" |
| |
| "github.com/SermoDigital/jose/jws" |
| "github.com/apid/istioApigeeAdapter/common" |
| ) |
| |
| var testMockServer *MockServer |
| |
| func TestMain(m *testing.M) { |
| var err error |
| testMockServer, err = StartMockServer(0) |
| if err != nil { |
| panic(err.Error()) |
| } |
| |
| result := m.Run() |
| testMockServer.Stop() |
| os.Exit(result) |
| } |
| |
| func TestServerSanity(t *testing.T) { |
| if mockKeyPEM == nil { |
| t.Fatal("Expected mock key to be generated") |
| } |
| if mockCertPEM == nil { |
| t.Fatal("Expected mock cert to be generated") |
| } |
| } |
| |
| func TestPublicKey(t *testing.T) { |
| resp, err := http.Get(fmt.Sprintf("http://%s/publicKey", testMockServer.Address())) |
| if err != nil { |
| t.Fatalf("Network error: %s", err) |
| } |
| defer resp.Body.Close() |
| if resp.StatusCode != 200 { |
| t.Fatalf("Got HTTP status code %d", resp.StatusCode) |
| } |
| |
| body, err := ioutil.ReadAll(resp.Body) |
| if err != nil { |
| t.Fatalf("Error reading body: %s", err) |
| } |
| pb, _ := pem.Decode(body) |
| if pb == nil { |
| t.Fatalf("Failed decoding public key body \"%s\"", string(body)) |
| } |
| if pb.Type != "CERTIFICATE" { |
| t.Fatalf("Got back invalid PEM type %s", pb.Type) |
| } |
| |
| cert, err := x509.ParseCertificate(pb.Bytes) |
| if err != nil { |
| t.Fatalf("Error decoding certificate: %s", err) |
| } |
| if cert.Subject.CommonName != "mockserver" { |
| t.Fatalf("Common name does not match \"mockserver\": \"%s\"", cert.Subject.CommonName) |
| } |
| } |
| |
| func TestProducts(t *testing.T) { |
| resp, err := http.Get(fmt.Sprintf("http://%s/products", testMockServer.Address())) |
| if err != nil { |
| t.Fatalf("Network error: %s", err) |
| } |
| defer resp.Body.Close() |
| if resp.StatusCode != 200 { |
| t.Fatalf("Got HTTP status code %d", resp.StatusCode) |
| } |
| if resp.Header.Get("content-type") != "application/json" { |
| t.Fatalf("Invalid content typs %s", resp.Header.Get("content-type")) |
| } |
| |
| dec := json.NewDecoder(resp.Body) |
| var products common.APIProductResponse |
| err = dec.Decode(&products) |
| if err != nil { |
| t.Fatalf("Error decoding response json: %s", err) |
| } |
| } |
| |
| func TestAPIKeyWrongFormat(t *testing.T) { |
| req := common.VerifyAPIKeyRequest{} |
| requestBod, _ := json.Marshal(&req) |
| resp, err := http.DefaultClient.Post( |
| fmt.Sprintf("http://%s/verifyApiKey", testMockServer.Address()), |
| "text/plain", |
| bytes.NewBuffer(requestBod)) |
| if err != nil { |
| t.Fatalf("Network error: %s", err) |
| } |
| defer resp.Body.Close() |
| if resp.StatusCode != 404 { |
| t.Fatalf("Got HTTP status code %d -- expected 404", resp.StatusCode) |
| } |
| } |
| |
| func TestAPIKeyNotJSON(t *testing.T) { |
| resp, err := http.DefaultClient.Post( |
| fmt.Sprintf("http://%s/verifyApiKey", testMockServer.Address()), |
| "application/json", |
| bytes.NewBuffer([]byte("Hello!"))) |
| if err != nil { |
| t.Fatalf("Network error: %s", err) |
| } |
| defer resp.Body.Close() |
| if resp.StatusCode != 500 { |
| t.Fatalf("Got HTTP status code %d -- expected 500", resp.StatusCode) |
| } |
| } |
| |
| func TestAPIKeyNoKey(t *testing.T) { |
| req := common.VerifyAPIKeyRequest{} |
| requestBod, _ := json.Marshal(&req) |
| resp, err := http.DefaultClient.Post( |
| fmt.Sprintf("http://%s/verifyApiKey", testMockServer.Address()), |
| "application/json", |
| bytes.NewBuffer(requestBod)) |
| if err != nil { |
| t.Fatalf("Network error: %s", err) |
| } |
| defer resp.Body.Close() |
| if resp.StatusCode != 401 { |
| t.Fatalf("Got HTTP status code %d -- expected 401", resp.StatusCode) |
| } |
| } |
| |
| func TestAPIKeyBadKey(t *testing.T) { |
| req := common.VerifyAPIKeyRequest{ |
| Key: "foobar", |
| } |
| requestBod, _ := json.Marshal(&req) |
| resp, err := http.DefaultClient.Post( |
| fmt.Sprintf("http://%s/verifyApiKey", testMockServer.Address()), |
| "application/json", |
| bytes.NewBuffer(requestBod)) |
| if err != nil { |
| t.Fatalf("Network error: %s", err) |
| } |
| defer resp.Body.Close() |
| if resp.StatusCode != 401 { |
| t.Fatalf("Got HTTP status code %d -- expected 401", resp.StatusCode) |
| } |
| } |
| |
| func TestAPIKeySuccess(t *testing.T) { |
| req := common.VerifyAPIKeyRequest{ |
| Key: ValidAPIKey1, |
| } |
| requestBod, _ := json.Marshal(&req) |
| resp, err := http.DefaultClient.Post( |
| fmt.Sprintf("http://%s/verifyApiKey", testMockServer.Address()), |
| "application/json", |
| bytes.NewBuffer(requestBod)) |
| if err != nil { |
| t.Fatalf("Network error: %s", err) |
| } |
| defer resp.Body.Close() |
| if resp.StatusCode != 200 { |
| t.Fatalf("Got HTTP status code %d -- expected 200", resp.StatusCode) |
| } |
| |
| var respBody common.VerifyAPIKeyResponse |
| dec := json.NewDecoder(resp.Body) |
| err = dec.Decode(&respBody) |
| if err != nil { |
| t.Fatalf("Error reading JSON response: %s", err) |
| } |
| |
| _, err = jws.ParseJWT([]byte(respBody.Token)) |
| if err != nil { |
| t.Fatalf("Error parsing JWT: %s", err) |
| } |
| } |
| |
| func TestAxPublishBadJSON(t *testing.T) { |
| resp, err := sendAnalytics([]byte("NOTJSON"), "application/json", |
| ValidPublishKey, ValidPublishSecret, false) |
| if err != nil { |
| t.Fatalf("Network error sending request: %s", err) |
| } |
| defer resp.Body.Close() |
| if resp.StatusCode != 500 { |
| t.Fatalf("Got status code %d and expected 500", resp.StatusCode) |
| } |
| } |
| |
| func TestAxPublishNotJSON(t *testing.T) { |
| resp, err := sendAnalytics([]byte("{}"), "text/plain", |
| ValidPublishKey, ValidPublishSecret, false) |
| if err != nil { |
| t.Fatalf("Network error sending request: %s", err) |
| } |
| defer resp.Body.Close() |
| if resp.StatusCode != 200 { |
| t.Fatalf("Got status code %d and expected 200", resp.StatusCode) |
| } |
| |
| var respBody common.AnalyticsResponse |
| dec := json.NewDecoder(resp.Body) |
| err = dec.Decode(&respBody) |
| if err != nil { |
| t.Fatalf("Error reading JSON response: %s", err) |
| } |
| |
| if respBody.Accepted != 0 { |
| t.Fatalf("%d records were accepted", respBody.Accepted) |
| } |
| if respBody.Rejected != 1 { |
| t.Fatalf("%d records were rejected", respBody.Rejected) |
| } |
| } |
| |
| func TestAxPublishEmpty(t *testing.T) { |
| resp, err := sendAnalytics([]byte("{}"), "application/json", |
| ValidPublishKey, ValidPublishSecret, false) |
| if err != nil { |
| t.Fatalf("Network error sending request: %s", err) |
| } |
| defer resp.Body.Close() |
| if resp.StatusCode != 200 { |
| t.Fatalf("Got status code %d and expected 200", resp.StatusCode) |
| } |
| |
| var respBody common.AnalyticsResponse |
| dec := json.NewDecoder(resp.Body) |
| err = dec.Decode(&respBody) |
| if err != nil { |
| t.Fatalf("Error reading JSON response: %s", err) |
| } |
| |
| if respBody.Accepted != 0 { |
| t.Fatalf("%d records were accepted", respBody.Accepted) |
| } |
| if respBody.Rejected != 0 { |
| t.Fatalf("%d records were rejected", respBody.Rejected) |
| } |
| } |
| |
| func TestAxPublishWrongUser(t *testing.T) { |
| resp, err := sendAnalytics([]byte("{}"), "application/json", |
| "NOTVALID", ValidPublishSecret, false) |
| if err != nil { |
| t.Fatalf("Network error sending request: %s", err) |
| } |
| defer resp.Body.Close() |
| if resp.StatusCode != 401 { |
| t.Fatalf("Got status code %d and expected 401", resp.StatusCode) |
| } |
| } |
| |
| func TestAxPublishWrongPW(t *testing.T) { |
| resp, err := sendAnalytics([]byte("{}"), "application/json", |
| ValidPublishKey, "NOPE", false) |
| if err != nil { |
| t.Fatalf("Network error sending request: %s", err) |
| } |
| defer resp.Body.Close() |
| if resp.StatusCode != 401 { |
| t.Fatalf("Got status code %d and expected 401", resp.StatusCode) |
| } |
| } |
| |
| func TestAxPublishNoAuth(t *testing.T) { |
| resp, err := http.Post( |
| fmt.Sprintf("http://%s/edgemicro/axpublisher/organization/foo/environment/test", testMockServer.Address()), |
| "application/json", bytes.NewReader([]byte("{}"))) |
| if err != nil { |
| t.Fatalf("Network error sending request: %s", err) |
| } |
| defer resp.Body.Close() |
| if resp.StatusCode != 401 { |
| t.Fatalf("Got status code %d and expected 401", resp.StatusCode) |
| } |
| } |
| |
| func TestAxPublishCompressed(t *testing.T) { |
| requestBod, err := json.Marshal(makeRequest(10)) |
| if err != nil { |
| t.Fatalf("Error making JSON: %s", err) |
| } |
| resp, err := sendAnalytics(requestBod, "application/json", |
| ValidPublishKey, ValidPublishSecret, true) |
| if err != nil { |
| t.Fatalf("Network error sending request: %s", err) |
| } |
| defer resp.Body.Close() |
| if resp.StatusCode != 200 { |
| t.Fatalf("Got status code %d", resp.StatusCode) |
| } |
| |
| var respBody common.AnalyticsResponse |
| dec := json.NewDecoder(resp.Body) |
| err = dec.Decode(&respBody) |
| if err != nil { |
| t.Fatalf("Error reading JSON response: %s", err) |
| } |
| |
| if respBody.Accepted != 10 { |
| t.Fatalf("Only %d record accepted", respBody.Accepted) |
| } |
| if respBody.Rejected != 0 { |
| t.Fatalf("%d records were rejected", respBody.Rejected) |
| } |
| } |
| |
| func sendAnalytics(body []byte, contentType, user, pw string, compress bool) (*http.Response, error) { |
| var bod io.Reader |
| |
| if compress { |
| bb := &bytes.Buffer{} |
| zw := gzip.NewWriter(bb) |
| zw.Write(body) |
| zw.Close() |
| bod = bb |
| } else { |
| bod = bytes.NewBuffer(body) |
| } |
| |
| req, err := http.NewRequest( |
| "POST", |
| fmt.Sprintf("http://%s/edgemicro/axpublisher/organization/foo/environment/test", testMockServer.Address()), |
| bod) |
| if err != nil { |
| return nil, err |
| } |
| req.Header.Set("content-type", contentType) |
| if compress { |
| req.Header.Set("content-encoding", "gzip") |
| } |
| req.SetBasicAuth(user, pw) |
| return http.DefaultClient.Do(req) |
| } |
| |
| func TestRegionCheck(t *testing.T) { |
| req, err := http.NewRequest( |
| "GET", |
| fmt.Sprintf("http://%s/edgemicro/region/organization/foo/environment/test", testMockServer.Address()), |
| nil) |
| if err != nil { |
| t.Fatal("Can't make request") |
| } |
| req.SetBasicAuth(ValidPublishKey, ValidPublishSecret) |
| resp, err := http.DefaultClient.Do(req) |
| if err != nil { |
| t.Fatalf("Network error sending request: %s", err) |
| } |
| defer resp.Body.Close() |
| if resp.StatusCode != 200 { |
| t.Fatalf("Got status code %d", resp.StatusCode) |
| } |
| |
| var rr common.RegionResponse |
| dec := json.NewDecoder(resp.Body) |
| err = dec.Decode(&rr) |
| if err != nil { |
| t.Fatalf("Error reading JSON response: %s", err) |
| } |
| |
| if rr.Host != testMockServer.Address() { |
| t.Fatalf("Invalid host response %s", rr.Host) |
| } |
| } |
| |
| func TestRegionCheckNoAuth(t *testing.T) { |
| req, err := http.NewRequest( |
| "GET", |
| fmt.Sprintf("http://%s/edgemicro/region/organization/foo/environment/test", testMockServer.Address()), |
| nil) |
| if err != nil { |
| t.Fatal("Can't make request") |
| } |
| resp, err := http.DefaultClient.Do(req) |
| if err != nil { |
| t.Fatalf("Network error sending request: %s", err) |
| } |
| defer resp.Body.Close() |
| if resp.StatusCode != 401 { |
| t.Fatalf("Got status code %d", resp.StatusCode) |
| } |
| } |
| |
| func makeRequest(numRecords int) *common.AnalyticsRequest { |
| var recs []common.AnalyticsRecord |
| |
| for i := 0; i < numRecords; i++ { |
| now := time.Now().UnixNano() / 1000000 |
| nr := common.AnalyticsRecord{ |
| ClientReceivedStartTimestamp: now, |
| ClientReceivedEndTimestamp: now + 1, |
| ClientSentStartTimestamp: now + 10, |
| ClientSentEndTimestamp: now + 11, |
| RecordType: "APIAnalytics", |
| APIProxy: "helloworld", |
| RequestURI: "http://hello/world", |
| RequestPath: "/world", |
| RequestVerb: "GET", |
| ClientIP: "192.168.201.100", |
| UserAgent: "Testing", |
| APIProxyRevision: 1, |
| ResponseStatusCode: 200, |
| } |
| recs = append(recs, nr) |
| } |
| |
| return &common.AnalyticsRequest{ |
| Records: recs, |
| } |
| } |