1
0
mirror of https://github.com/MontFerret/ferret.git synced 2024-12-16 11:37:36 +02:00
ferret/e2e/runner/runner.go
Tim Voronov 9b762d32ee
Feature/#360 default driver params (#372)
* Added default headers and cookies

* wip

* Added tests

* Added default headers and cookies to HTTP driver

* Removed unused struct prop
2019-09-05 11:49:21 -04:00

413 lines
7.5 KiB
Go

package runner
import (
"context"
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
"github.com/MontFerret/ferret/pkg/compiler"
"github.com/MontFerret/ferret/pkg/drivers"
"github.com/MontFerret/ferret/pkg/drivers/cdp"
"github.com/MontFerret/ferret/pkg/drivers/http"
"github.com/MontFerret/ferret/pkg/runtime"
"github.com/gobwas/glob"
"github.com/pkg/errors"
"github.com/rs/zerolog"
)
type (
Settings struct {
StaticServerAddress string
DynamicServerAddress string
CDPAddress string
Dir string
Filter string
}
Result struct {
name string
duration time.Duration
err error
}
Summary struct {
passed int
failed int
duration time.Duration
}
Runner struct {
logger zerolog.Logger
settings Settings
}
)
func New(logger zerolog.Logger, settings Settings) *Runner {
return &Runner{
logger,
settings,
}
}
func (r *Runner) Run(ctx context.Context) error {
ctx = drivers.WithContext(
ctx,
cdp.NewDriver(cdp.WithAddress(r.settings.CDPAddress)),
)
ctx = drivers.WithContext(
ctx,
cdp.NewDriver(cdp.WithAddress(r.settings.CDPAddress),
cdp.WithCustomName("cdp_headers"),
cdp.WithHeader("Single_header", []string{"single_header_value"}),
cdp.WithHeaders(drivers.HTTPHeaders{
"Multi_set_header": []string{"multi_set_header_value"},
"Multi_set_header2": []string{"multi_set_header2_value"},
}),
),
)
ctx = drivers.WithContext(
ctx,
cdp.NewDriver(cdp.WithAddress(r.settings.CDPAddress),
cdp.WithCustomName("cdp_cookies"),
cdp.WithCookie(drivers.HTTPCookie{
Name: "single_cookie",
Value: "single_cookie_value",
Path: "/",
MaxAge: 0,
Secure: false,
HTTPOnly: false,
SameSite: 0,
}),
cdp.WithCookies([]drivers.HTTPCookie{
{
Name: "multi_set_cookie",
Value: "multi_set_cookie_value",
Path: "/",
MaxAge: 0,
Secure: false,
HTTPOnly: false,
SameSite: 0,
},
{
Name: "multi_set_cookie2",
Value: "multi_set_cookie2_value",
Path: "/",
MaxAge: 0,
Secure: false,
HTTPOnly: false,
SameSite: 0,
},
}),
),
)
ctx = drivers.WithContext(
ctx,
http.NewDriver(),
drivers.AsDefault(),
)
ctx = drivers.WithContext(
ctx,
http.NewDriver(
http.WithCustomName("http_headers"),
http.WithHeader("Single_header", []string{"single_header_value"}),
http.WithHeaders(drivers.HTTPHeaders{
"Multi_set_header": []string{"multi_set_header_value"},
"Multi_set_header2": []string{"multi_set_header2_value"},
}),
),
)
ctx = drivers.WithContext(
ctx,
http.NewDriver(
http.WithCustomName("http_cookies"),
http.WithCookie(drivers.HTTPCookie{
Name: "single_cookie",
Value: "single_cookie_value",
Path: "/",
MaxAge: 0,
Secure: false,
HTTPOnly: false,
SameSite: 0,
}),
http.WithCookies([]drivers.HTTPCookie{
{
Name: "multi_set_cookie",
Value: "multi_set_cookie_value",
Path: "/",
MaxAge: 0,
Secure: false,
HTTPOnly: false,
SameSite: 0,
},
{
Name: "multi_set_cookie2",
Value: "multi_set_cookie2_value",
Path: "/",
MaxAge: 0,
Secure: false,
HTTPOnly: false,
SameSite: 0,
},
}),
),
)
results, err := r.runQueries(ctx, r.settings.Dir)
if err != nil {
return err
}
sum := r.report(results)
var event *zerolog.Event
if sum.failed == 0 {
event = r.logger.Info()
} else {
event = r.logger.Error()
}
event.
Timestamp().
Int("passed", sum.passed).
Int("failed", sum.failed).
Str("duration", sum.duration.String()).
Msg("Completed")
if sum.failed > 0 {
return errors.New("failed")
}
return nil
}
func (r *Runner) runQueries(ctx context.Context, dir string) ([]Result, error) {
results := make([]Result, 0, 50)
c := compiler.New()
// backward compatible
if err := Assertions(c); err != nil {
return nil, err
}
ns := c.Namespace("T")
if err := Assertions(ns); err != nil {
return nil, err
}
if err := HTTPHelpers(ns.Namespace("HTTP")); err != nil {
return nil, err
}
var filter glob.Glob
var useFilter bool
if r.settings.Filter != "" {
f, err := glob.Compile(r.settings.Filter)
if err != nil {
return nil, err
}
filter = f
useFilter = true
}
err := r.traverseDir(ctx, dir, func(name string) error {
if useFilter {
if !filter.Match(name) {
return nil
}
}
b, err := ioutil.ReadFile(name)
if err != nil {
results = append(results, Result{
name: name,
err: errors.Wrap(err, "failed to read script file"),
})
return nil
}
r.logger.Info().Timestamp().Str("name", name).Msg("Running test")
select {
case <-ctx.Done():
return context.Canceled
default:
result := r.runQuery(ctx, c, name, string(b))
if result.err == nil {
r.logger.Info().
Timestamp().
Str("file", result.name).
Str("duration", result.duration.String()).
Msg("Test passed")
} else {
r.logger.Error().
Timestamp().
Err(result.err).
Str("file", result.name).
Str("duration", result.duration.String()).
Msg("Test failed")
}
results = append(results, result)
}
return nil
})
if err != nil {
return nil, err
}
return results, nil
}
func (r *Runner) runQuery(ctx context.Context, c *compiler.Compiler, name, script string) Result {
start := time.Now()
p, err := c.Compile(script)
if err != nil {
return Result{
name: name,
duration: time.Duration(0) * time.Millisecond,
err: errors.Wrap(err, "failed to compile query"),
}
}
mustFail := r.mustFail(name)
out, err := p.Run(
ctx,
runtime.WithLog(zerolog.ConsoleWriter{Out: os.Stdout}),
runtime.WithParam("static", r.settings.StaticServerAddress),
runtime.WithParam("dynamic", r.settings.DynamicServerAddress),
)
duration := time.Since(start)
if err != nil {
if mustFail {
return Result{
name: name,
duration: duration,
}
}
return Result{
name: name,
duration: duration,
err: errors.Wrap(err, "failed to execute query"),
}
}
if mustFail {
return Result{
name: name,
duration: duration,
err: errors.New("expected to fail"),
}
}
var result string
if err := json.Unmarshal(out, &result); err != nil {
return Result{
name: name,
duration: duration,
err: err,
}
}
if result == "" {
return Result{
name: name,
duration: duration,
}
}
return Result{
name: name,
duration: duration,
err: errors.New(result),
}
}
func (r *Runner) report(results []Result) Summary {
var failed int
var passed int
var sumDuration time.Duration
for _, res := range results {
if res.err != nil {
failed++
} else {
passed++
}
sumDuration += res.duration
}
return Summary{
passed: passed,
failed: failed,
duration: sumDuration,
}
}
func (r *Runner) traverseDir(ctx context.Context, dir string, iteratee func(name string) error) error {
files, err := ioutil.ReadDir(dir)
if err != nil {
r.logger.Error().
Timestamp().
Err(err).
Str("dir", dir).
Msg("failed to read scripts directory")
return err
}
for _, file := range files {
name := filepath.Join(dir, file.Name())
if file.IsDir() {
if err := r.traverseDir(ctx, name, iteratee); err != nil {
return err
}
continue
}
if err := iteratee(name); err != nil {
return err
}
}
return nil
}
func (r *Runner) mustFail(name string) bool {
return strings.HasSuffix(name, ".fail.fql")
}