Skip to content

Commit

Permalink
Run the examples as part of the end to end test (#452)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dominic Wong authored Jun 9, 2021
1 parent ddc4178 commit bfd1c07
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 14 deletions.
2 changes: 1 addition & 1 deletion endtoend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ Checks can be triggered manually by hitting the `RunCheck` endpoint
micro endtoend RunCheck
```

and you can check the logs or the usual alert channels for success/failure.
and you can check the logs or the usual alert channels for success/failure.
177 changes: 168 additions & 9 deletions endtoend/handler/endtoend.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
package handler

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"regexp"
"strings"
"time"

balancepb "github.com/m3o/services/balance/proto"
"github.com/micro/micro/v3/service"

alertpb "github.com/m3o/services/alert/proto/alert"
custpb "github.com/m3o/services/customers/proto"
endtoend "github.com/m3o/services/endtoend/proto"
onpb "github.com/m3o/services/onboarding/proto"
pubpb "github.com/m3o/services/publicapi/proto"
"github.com/micro/micro/v3/service/client"
mconfig "github.com/micro/micro/v3/service/config"
"github.com/micro/micro/v3/service/errors"
merrors "github.com/micro/micro/v3/service/errors"
log "github.com/micro/micro/v3/service/logger"
mstore "github.com/micro/micro/v3/service/store"

Expand Down Expand Up @@ -51,6 +56,8 @@ func NewEndToEnd(srv *service.Service) *Endtoend {
email: email,
custSvc: custpb.NewCustomersService("customers", srv.Client()),
alertSvc: alertpb.NewAlertService("alert", srv.Client()),
balSvc: balancepb.NewBalanceService("balance", srv.Client()),
pubSvc: pubpb.NewPublicapiService("publicapi", srv.Client()),
}
}

Expand Down Expand Up @@ -102,14 +109,14 @@ func (e *Endtoend) Check(ctx context.Context, request *endtoend.Request, respons
log.Info("Received Endtoend.Check request")
recs, err := mstore.Read(keyCheckResult)
if err != nil {
return errors.InternalServerError("endtoend.check.store", "Failed to load last result %s", err)
return merrors.InternalServerError("endtoend.check.store", "Failed to load last result %s", err)
}
if len(recs) == 0 {
return errors.InternalServerError("endtoend.check.noresults", "Failed to load last result, no results found")
return merrors.InternalServerError("endtoend.check.noresults", "Failed to load last result, no results found")
}
cr := checkResult{}
if err := json.Unmarshal(recs[0].Value, &cr); err != nil {
return errors.InternalServerError("endtoend.check.unmarshal", "Failed to unmarshal last result %s", err)
return merrors.InternalServerError("endtoend.check.unmarshal", "Failed to unmarshal last result %s", err)
}
if cr.Passed && time.Now().Add(-checkBuffer).Unix() < cr.Time {
response.StatusCode = 200
Expand All @@ -120,7 +127,7 @@ func (e *Endtoend) Check(ctx context.Context, request *endtoend.Request, respons
if len(response.Body) == 0 {
response.Body = "No recent successful check"
}
return errors.New("endtoend.chack.failed", response.Body, response.StatusCode)
return merrors.New("endtoend.chack.failed", response.Body, response.StatusCode)

}

Expand Down Expand Up @@ -183,7 +190,7 @@ func (e *Endtoend) signup() error {
delErr = nil
break
}
merr, ok := err.(*errors.Error)
merr, ok := err.(*merrors.Error)
if ok && (merr.Code == 404 || strings.Contains(merr.Detail, "not found")) {
delErr = nil
break
Expand Down Expand Up @@ -247,10 +254,12 @@ func (e *Endtoend) signup() error {
return fmt.Errorf("error completing signup %s", err)
}
defer rsp.Body.Close()
b, _ := ioutil.ReadAll(rsp.Body)
if rsp.StatusCode != 200 {
b, _ := ioutil.ReadAll(rsp.Body)
return fmt.Errorf("error completing signup %s %s", rsp.Status, string(b))
}
var srsp onpb.CompleteSignupResponse
json.Unmarshal(b, &srsp)

var custErr error
loopStart = time.Now()
Expand All @@ -273,9 +282,159 @@ func (e *Endtoend) signup() error {
return custErr
}

// TODO check balance for intro credit
// TODO run some apis
loopStart = time.Now()
currBal := int64(0)
for time.Now().Sub(loopStart) < 1*time.Minute {
// check balance for intro credit
balRsp, err := e.balSvc.Current(context.Background(), &balancepb.CurrentRequest{CustomerId: srsp.CustomerID}, client.WithAuthToken())
if err == nil && balRsp.CurrentBalance > 0 {
currBal = balRsp.CurrentBalance
break
}
log.Errorf("balance response %s", err)
}
log.Infof("Balance is %d", currBal)
if currBal == 0 {
return fmt.Errorf("no intro credit was applied to customer")
}

// generate a key
req, err := http.NewRequest("POST", "https://api.m3o.com/v1/api/keys/generate", strings.NewReader(`{"description":"test key", "scopes": ["*"]}`))
if err != nil {
return fmt.Errorf("error generating key request %s", err)
}
req.Header.Set("Authorization", "Bearer "+srsp.AuthToken.AccessToken)
req.Header.Set("Content-Type", "application/json")
rsp, err = http.DefaultClient.Do(req)
if err != nil {
return fmt.Errorf("error generating key %s", err)
}
defer rsp.Body.Close()
b, _ = ioutil.ReadAll(rsp.Body)
if rsp.StatusCode != 200 {
return fmt.Errorf("error generating key %s %s", rsp.Status, string(b))
}
keyRsp := struct {
ApiKey string `json:"api_key"`
}{}
if err := json.Unmarshal(b, &keyRsp); err != nil {
return fmt.Errorf("error generating key, failed to unmarshal response %s", err)
}

// run some apis
pubrsp, err := e.pubSvc.List(context.Background(), &pubpb.ListRequest{}, client.WithAuthToken())
if err != nil {
return fmt.Errorf("error listing apis %s", err)
}

exampleErrs := []string{}
for _, api := range pubrsp.Apis {
var examples apiExamples
if len(api.ExamplesJson) == 0 {
log.Infof("No examples, skipping %s", api.Name)
continue
}

keys, err := objectKeys(api.ExamplesJson)
if err != nil {
log.Errorf("Failed to find keys for %s %s", api.Name, err)
exampleErrs = append(exampleErrs, fmt.Sprintf("Failed to find keys for %s %s", api.Name, err))
continue
}
if err := json.Unmarshal([]byte(api.ExamplesJson), &examples); err != nil {
log.Errorf("Failed to unmarshal examples for %s %s", api.Name, err)
exampleErrs = append(exampleErrs, fmt.Sprintf("Failed to unmarshal examples for %s %s", api.Name, err))
continue
}

// process the examples in json order to respect implicit dependencies
for _, endpointName := range keys {
exs := examples[endpointName]
for _, ex := range exs {
b, err := json.Marshal(ex.Request)
if err != nil {
log.Errorf("Failed to marshal example request for %s %s %s %s", api.Name, endpointName, ex.Title, err)
exampleErrs = append(exampleErrs, fmt.Sprintf("Failed to marshal example request for %s %s %s %s", api.Name, endpointName, ex.Title, err))
continue
}

req, err := http.NewRequest("POST", fmt.Sprintf("https://api.m3o.com/v1/%s/%s", api.Name, endpointName), bytes.NewReader(b))
if err != nil {
log.Errorf("Error generating example request %s %s %s %s", api.Name, endpointName, ex.Title, err)
exampleErrs = append(exampleErrs, fmt.Sprintf("Error generating example request %s %s %s %s", api.Name, endpointName, ex.Title, err))
continue
}
req.Header.Set("Authorization", "Bearer "+keyRsp.ApiKey)
req.Header.Set("Content-Type", "application/json")
rsp, err = http.DefaultClient.Do(req)
if err != nil {
log.Errorf("Error running example %s %s %s %s", api.Name, endpointName, ex.Title, err)
exampleErrs = append(exampleErrs, fmt.Sprintf("Error running example %s %s %s %s", api.Name, endpointName, ex.Title, err))
continue
}
defer rsp.Body.Close()
b, _ = ioutil.ReadAll(rsp.Body)
if rsp.StatusCode != 200 {
log.Errorf("Error running example %s %s %s %s %s", api.Name, endpointName, ex.Title, rsp.Status, string(b))
exampleErrs = append(exampleErrs, fmt.Sprintf("Error running example %s %s %s %s %s", api.Name, endpointName, ex.Title, rsp.Status, string(b)))
continue
}
log.Infof("API response for example %s %s %s %s %s", api.Name, endpointName, ex.Title, rsp.Status, string(b))
// TODO test response against expected response with fuzzy matching

}
}
}
// TODO add credit via stripe
if len(exampleErrs) > 0 {
return fmt.Errorf("Errors running api examples. %s", strings.Join(exampleErrs, ". "))
}
return nil
}

func objectKeys(s string) ([]string, error) {
d := json.NewDecoder(strings.NewReader(s))
t, err := d.Token()
if err != nil {
return nil, err
}
if t != json.Delim('{') {
return nil, errors.New("expected start of object")
}
var keys []string
for {
t, err := d.Token()
if err != nil {
return nil, err
}
if t == json.Delim('}') {
return keys, nil
}
keys = append(keys, t.(string))
if err := skipValue(d); err != nil {
return nil, err
}
}
}
func skipValue(d *json.Decoder) error {
t, err := d.Token()
if err != nil {
return err
}
switch t {
case json.Delim('['), json.Delim('{'):
for {
if err := skipValue(d); err != nil {
if err == end {
break
}
return err
}
}
case json.Delim(']'), json.Delim('}'):
return end
}
return nil
}

var end = errors.New("invalid end of array or object")
13 changes: 13 additions & 0 deletions endtoend/handler/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ package handler

import (
alertpb "github.com/m3o/services/alert/proto/alert"
balancepb "github.com/m3o/services/balance/proto"
custpb "github.com/m3o/services/customers/proto"
pubpb "github.com/m3o/services/publicapi/proto"
)

type Endtoend struct {
custSvc custpb.CustomersService
alertSvc alertpb.AlertService
balSvc balancepb.BalanceService
pubSvc pubpb.PublicapiService
email string
}

Expand All @@ -30,3 +34,12 @@ type checkResult struct {
}

type MailinResponse struct{}

type apiExamples map[string][]apiExample // map of endpoint name to list of examples

type apiExample struct {
Title string
Description string
Request interface{}
Response interface{}
}
4 changes: 2 additions & 2 deletions pkg/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ func VerifyMicroCustomer(ctx context.Context, method string) (*auth.Account, err
}
if err := verifyMicroCustomer(acc, method); err != nil {
// are they admin?
if err := verifyMicroAdmin(acc, method); err != nil {
return nil, err
if err := verifyMicroAdmin(acc, method); err == nil {
return acc, nil
}
return nil, err
}
Expand Down
3 changes: 1 addition & 2 deletions pkg/events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ func processEvents(ch <-chan mevents.Event, handler func(ev mevents.Event) error
}
case <-t.C:
// safety net in case we stop receiving messages for some reason
logger.Infof("No messages received for last 60 minutes retrying connection")
return
logger.Fatalf("No messages received for last 60 minutes retrying connection")
}
t.Reset(timeout)
if err := handler(ev); err != nil {
Expand Down

0 comments on commit bfd1c07

Please sign in to comment.