endly

package module
v0.8.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 26, 2018 License: Apache-2.0 Imports: 18 Imported by: 0

README

Declarative end to end functional testing (endly)

Declarative funtional testing for Go. GoDoc

This library is compatible with Go 1.8+

Please refer to CHANGELOG.md if you encounter breaking changes.

Motivation

This library was developed in go lang to enable simple automated declarative end to end functional testing for web application developed in any language.

It addresses all aspect of testing automation namely:

  • Local or remote system preparation including all services required by the application.
  • Checking out the application code
  • Building and deploying the application as a separate process, or in the container.
  • Data preparation including RDBMS, or key/value store
  • Test use cases with HTTP, REST or selenium runner.
  • Verification of responses, data in datastores or log produced.

Installation

  1. Download latest binary

     tar -xvzf endly_xxx.tar.gz
     cp endly /usr/local/bin
     endly -h
     endly -v
    
    
  2. Build from source a) install go 1.9+ b) run the following commands:

    mkdir -p ~/go
    export GOPATH=~/go
    go get -u  github.com/viant/endly
    go get -u  github.com/viant/endly/endly
    cd $GOPATH/src/github.com/viant/endly/endly
    go build endly.go
    cp endly /usr/local/bin
    
  3. Custom build, in case you need additional drivers, dependencies or UDF with additional imports:

@endly.go


package main

//import your udf package  or other dependencies here

import "github.com/viant/endly/bootstrap"

func main() {
	bootstrap.Bootstrap()
}

Introduction

Endly as a comprehensive testing framework automate the following step:

  1. System preparation
    1. Local or remote on cloud
    2. System services initialization. (RDBM, NoSQL, caching or 3rd party API, dockerized services)
    3. Application container. (Docker, Application server, i,e, tomcat, glassfish)
  2. Application build and deployment
    1. Application code checkout.
    2. Application build
    3. Application deployment
  3. Testing
    1. Preparing test data
    2. Actual application testing
      1. Http runner
      2. Reset runner
      3. Selenium runner
    3. Application output verification
    4. Application persisted data verification
    5. Application produced log verification
  4. Cleanup
    1. Data cleanup
    2. Application shutdown
    3. Application system services shutdown

Getting Started

Endly automate sequence of actions into reusable tasks and workflows.

a) System preparation

For instance: the following define inline workflowto prepare app system services:

@system.yaml

tasks: $tasks
defaults:
  target: $serviceTarget
pipeline:
  destroy:
    stop-images:
      action: docker:stop-images
      images:
        - mysql
        - aerospike
  init:
    services:
      mysql:
        workflow: "service/mysql:start"
        name: mydb3
        version: $mysqlVersion
        credentials: $mysqlCredentials
        config: config/my.cnf
      aerospike:
        workflow: "service/aerospike:start"
        name: mydb4
        config: config/aerospike.conf

b) Application build and deployment

For instance: the following define inline workflowto build and deploy a test app: (you can easily build an app for standalone mode or in and for docker container)

@app.yaml

tasks: $tasks
defaults:
  app: myApp
  sdk: go:1.8

pipeline:

  build:
    workflow: app/build
    origin:
      URL: ./../
    commands:
      - cd $buildPath/app
      - go get -u .
      - go build -o $app
      - chmod +x $app
    download:
      /$buildPath/app/${app}: $releasePath
      /$buildPath/endly/config/config.json: $releasePath

  deploy:
    workflow: app/deploy
    init:
      - mkdir -p $appPath
      - mkdir -p $appPath/config
      - chown -R ${os.user} $appPath
    upload:
      ${releasePath}/${app}: $appPath
      ${releasePath}/config.json: $appPath
    commands:
      - echo 'deployed'

  stop:
    action: process:stop-all
    input: ${app}

  start:
    action: process:start
    directory: $appPath
    immuneToHangups: true
    command: ./${app}
    arguments:
      - "-config"
      - "config.json"

c) Datastore creation

For instance: the following define inline workflowto create/populare mysql and aerospike database/dataset:

@datastore.yaml

pipeline:
  create-db:
    db3:
      action: dsunit:init
      scripts:
        - URL: datastore/db3/schema.ddl
      datastore: db3
      recreate: true
      config:
        driverName: mysql
        descriptor: "[username]:[password]@tcp(127.0.0.1:3306)/[dbname]?parseTime=true"
        credentials: $mysqlCredentials
      admin:
        datastore: mysql
        config:
          driverName: mysql
          descriptor: "[username]:[password]@tcp(127.0.0.1:3306)/[dbname]?parseTime=true"
          credentials: $mysqlCredentials
    db4:
      action: dsunit:init
      datastore: db4
      recreate: true
      config:
        driverName: aerospike
        descriptor: "tcp([host]:3000)/[namespace]"
        parameters:
          dbname: db4
          namespace: db4
          host: $serviceHost
          port: 3000
  populate:
    db3:
      action: dsunit:prepare
      datastore: db3
      URL: datastore/db3/dictionary
    db4:
      action: dsunit:prepare
      datastore: db4
      URL: datastore/db4/data

d) Testing

For instance: the following define inline workflowto run test with selenium runner:

@test.yaml

defaults:
  target:
     URL: ssh://127.0.0.1/
     credentials: localhost
pipeline:
  init:
    action: selenium:start
    version: 3.4.0
    port: 8085
    sdk: jdk
    sdkVersion: 1.8
  test:
    action: selenium:run
    browser: firefox
    remoteSelenium:
      URL: http://127.0.0.1:8085
    commands:
      - get(http://play.golang.org/?simple=1)
      - (#code).clear
      - (#code).sendKeys(package main

          import "fmt"

          func main() {
              fmt.Println("Hello Endly!")
          }
        )
      - (#run).click
      - command: output = (#output).text
        exit: $output.Text:/Endly/
        sleepTimeMs: 1000
        repeat: 10
      - close
    expect:
      output:
        Text: /Hello Endly!/

To see Endly in action, try to generate an e2e workflow from scratch.

In addition a few examples of fully functioning applications are included. You can build, deploy and test them end to end all with endly.

  1. Web Service
    • Reporter - a pivot table report builder.
      • Test with Rest Runner
      • Data Preparation and Validation (mysql)
  2. User Interface
    • SSO - user registration and login application.
      • Test with Selenium Runner
      • Data Preparation and Validation (aersopike)
      • Web Content validation
  3. Extract, Transform and Load (ETL)
    • Transformer - datastore to datastore transformer (i.e. aerospike to mysql)
      • Test with Rest Runner
      • Data Preparation and Validation (aersopike, mysql)
  4. Runtime - simple http request event logger
    • Logger
      • Test with HTTP Runner
      • Log Validation
  5. Automation - simple 3rd party echo app
    • Echo
      • Build 3rd party application binary in docker container
      • Build application docker image
      • Optionally publish app image to the docker registry
      • Deploy app to docker container
      • Test an app with REST and HTTP runner

Documentation

@run.yaml

target:
  URL: "ssh://127.0.0.1/"
  credentials: localhost
systemPaths:
  - /usr/local/go/bin
commands:
  - go version
  - echo $GOPATH

External resources

License

The source code is made available under the terms of the Apache License, Version 2, as stated in the file LICENSE.

Individual files may be made available under their own specific license, all compatible with Apache License, Version 2. Please see individual files for details.

Credits and Acknowledgements

Library Author: Adrian Witas

Documentation

Index

Constants

View Source
const AppName = "endly"

AppName represents endly application name

View Source
const Namespace = "github.com/viant/endly/"

Namespace represents endly namespace

Variables

View Source
var Registry = &registry

Registry global service provider registry

View Source
var UdfRegistry = make(map[string]func(source interface{}, state data.Map) (interface{}, error))

UdfRegistry represents a udf registry

Functions

func GetVersion

func GetVersion() string

GetVersion returns endly version

func NewDefaultState

func NewDefaultState() data.Map

NewDefaultState returns a new default state. It comes with the following registered keys:

  • rand - random int64
  • date - current date formatted as yyyy-MM-dd
  • time - current time formatted as yyyy-MM-dd hh:mm:ss
  • ts - current timestamp formatted as yyyyMMddhhmmSSS
  • timestamp.yesterday - timestamp in ms
  • timestamp.now - timestamp in ms
  • timestamp.tomorrow - timestamp in ms
  • tmpDir - temp directory
  • uuid.next - generate unique id
  • uuid.Get - returns previously generated unique id, or generate new *.end.XXX where XXX is the ID of the env variable to return
  • all UFD registry functions

func NewError

func NewError(service, action string, err error) error

NewError returns new workflow exception or update path

func Run

func Run(context *Context, request, result interface{}) error

Run runs action for supplied context request and response. Response has to be pointer or nil

func Services

func Services(mgr interface{}) map[string]Service

Services returns manager serviceByID or error

Types

type AbstractService

type AbstractService struct {
	Service
	*sync.RWMutex
	// contains filtered or unexported fields
}

AbstractService represenst an abstract service.

func NewAbstractService

func NewAbstractService(id string) *AbstractService

NewAbstractService creates a new abstract service.

func (*AbstractService) Actions

func (s *AbstractService) Actions() []string

Actions returns service actions

func (*AbstractService) Begin

func (s *AbstractService) Begin(context *Context, value interface{}) msg.Event

Begin add starting event

func (*AbstractService) End

func (s *AbstractService) End(context *Context) func(startEvent msg.Event, value interface{}) msg.Event

End adds finishing event.

func (*AbstractService) GetHostAndSSHPort

func (s *AbstractService) GetHostAndSSHPort(target *url.Resource) (string, int)

GetHostAndSSHPort return host and ssh port

func (*AbstractService) ID

func (s *AbstractService) ID() string

ID returns this service id.

func (*AbstractService) Mutex

func (s *AbstractService) Mutex() *sync.RWMutex

Mutex returns a mutex.

func (*AbstractService) Register

func (s *AbstractService) Register(routes ...*Route)

Register register action routes

func (*AbstractService) Route

func (s *AbstractService) Route(action string) (*Route, error)

Route returns a service action route for supplied action

func (*AbstractService) Run

func (s *AbstractService) Run(context *Context, request interface{}) (response *ServiceResponse)

Run returns a service action for supplied action

func (*AbstractService) Sleep

func (s *AbstractService) Sleep(context *Context, sleepTimeMs int)

Sleep sleeps for provided time in ms

func (*AbstractService) State

func (s *AbstractService) State() data.Map

State returns this service state map.

type ActionInfo

type ActionInfo struct {
	Description string
	Examples    []*UseCase
}

ActionInfo represent an action info

type Context

type Context struct {
	SessionID       string
	CLIEnabled      bool
	HasLogger       bool
	AsyncUnsafeKeys map[interface{}]bool
	Secrets         *secret.Service
	Wait            *sync.WaitGroup
	Listener        msg.Listener
	Source          *url.Resource

	toolbox.Context
	// contains filtered or unexported fields
}

Context represents a workflow session context/state

func (*Context) AsRequest

func (c *Context) AsRequest(serviceName, action string, source map[string]interface{}) (request interface{}, err error)

AsRequest converts a source map into request for provided service and action.

func (*Context) Clone

func (c *Context) Clone() *Context

Clone clones the context.

func (*Context) Close

func (c *Context) Close()

Close closes this context, it executes all deferred function and set closed flag.

func (*Context) Deffer

func (c *Context) Deffer(functions ...func()) []func()

Deffer add function to be executed if context closes. If returns currently registered functions.

func (*Context) Expand

func (c *Context) Expand(text string) string

Expand substitute $ expression if present in the text and state map.

func (*Context) ExpandResource

func (c *Context) ExpandResource(resource *url.Resource) (*url.Resource, error)

ExpandResource substitutes any $ expression with the key value from the state map if it is present.

func (*Context) IsClosed

func (c *Context) IsClosed() bool

IsClosed returns true if it is closed.

func (*Context) MakeAsyncSafe

func (c *Context) MakeAsyncSafe() *msg.Events

func (*Context) Manager

func (c *Context) Manager() (Manager, error)

Manager returns workflow manager or error

func (*Context) NewRequest

func (c *Context) NewRequest(serviceName, action string) (interface{}, error)

NewRequest creates a new request for service and action

func (*Context) Publish

func (c *Context) Publish(value interface{}) msg.Event

Publish publishes event to listeners, it updates current run details like activity workflow name etc ...

func (*Context) PublishAndRestore added in v0.8.0

func (s *Context) PublishAndRestore(values map[string]interface{}) func()

PublishAndRestore sets supplied value and returns func restoring original values

func (*Context) PublishWithStartEvent

func (c *Context) PublishWithStartEvent(value interface{}, init msg.Event) msg.Event

PublishWithStartEvent publishes event to listeners, it updates current run details like activity workflow name etc ...

func (*Context) Service

func (c *Context) Service(name string) (Service, error)

Service returns a service fo provided id or error.

func (*Context) SetListener

func (c *Context) SetListener(listener msg.Listener)

SetListener sets context event Listener

func (*Context) SetState

func (c *Context) SetState(state data.Map)

SetState sets a new state map

func (*Context) State

func (c *Context) State() data.Map

State returns a context state map.

type Error

type Error struct {
	Path []string
	// contains filtered or unexported fields
}

Error represents an workflow execution error

func (*Error) Error

func (e *Error) Error() string

Error returns en error

func (*Error) Unshift

func (e *Error) Unshift(pathFragments ...string)

Unshift appends supplied pathFragments at the beginning

type Initializer

type Initializer interface {
	Init() error
}

Initializer represents generic initializer

type Manager

type Manager interface {
	//Name returns an application ID
	Name() string

	//Version returns an application version
	Version() string

	//Service return a workflow service for provided ID, request,  or error
	Service(input interface{}) (Service, error)

	//Register register service in this manager
	Register(service Service)

	//NewContext returns new workflow context.
	NewContext(context toolbox.Context) *Context

	//Run run requests
	Run(context *Context, request interface{}) (interface{}, error)
}

Manager represents a endly service manager

func New

func New() Manager

New returns a new manager.

type NopRequest

type NopRequest struct {
	In interface{}
}

NopRequest represent no operation to be deprecated

type Route

type Route struct {
	Action           string
	RequestInfo      *ActionInfo
	ResponseInfo     *ActionInfo
	RequestProvider  func() interface{}
	ResponseProvider func() interface{}
	Handler          func(context *Context, request interface{}) (interface{}, error)
}

Route represents service action route

type Service

type Service interface {
	//service id
	ID() string

	//service state map
	State() data.Map

	//Run service action for supported request types.
	Run(context *Context, request interface{}) *ServiceResponse

	//Route returns service action route
	Route(action string) (*Route, error)

	Mutex() *sync.RWMutex

	Actions() []string
}

Service represents an endly service

type ServiceProvider

type ServiceProvider func() Service

ServiceProvider represents a service provider

type ServiceRegistry

type ServiceRegistry []ServiceProvider

ServiceRegistry represents a service registry

func (*ServiceRegistry) Register

func (r *ServiceRegistry) Register(serviceProvider ServiceProvider) error

Register register service provider.

type ServiceResponse

type ServiceResponse struct {
	Status   string
	Error    string
	Response interface{}
	Err      error
}

ServiceResponse service response

type UseCase

type UseCase struct {
	Description string
	Data        string
}

Description represents example use case

type Validator

type Validator interface {
	Validate() error
}

Validator represents generic validator

Directories

Path Synopsis
cloud
ec2
gce
deployment
sdk
vc
example
gen
app
web
notify
system
test
proto
Package gmetric is a generated protocol buffer package.
Package gmetric is a generated protocol buffer package.
testing
log

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL