runn

package module
v0.8.1 Latest Latest
Warning

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

Go to latest
Published: Mar 15, 2022 License: MIT Imports: 29 Imported by: 6

README

runn

runn ( means "Run N" ) is a package/tool for running operations following a scenario.

Key features of runn are:

  • As a tool for scenario testing.
  • As a test helper package for the Go language.
  • As a tool for automation.

Usage

runn can run a multi-step scenario following a runbook written in YAML format.

As a tool for scenario testing / As a tool for automation.

runn can run one or more runbooks.

$ runn list path/to/**/*.yml
  Desc                     Path
-----------------------------------------------------
  Login and get projects.  path/to/books/login.yml
  Login and logout.        path/to/books/logout.yml
  New project.             path/to/books/new.yml
$ runn run path/to/**/*.yml
Login and get projects. ... ok
Login and logout. ... ok
New project. ... ok

3 scenarios, 0 failures
As a test helper package for the Go language.

runn can also behave as a test helper for the Go language.

Run N runbooks using httptest.Server
func TestRouter(t *testing.T) {
	ctx := context.Background()
	db, err := sql.Open("postgres", "user=root password=root host=localhost dbname=test sslmode=disable")
	if err != nil {
		log.Fatal(err)
	}
	ts := httptest.NewServer(NewRouter(db))
	t.Cleanup(func() {
		ts.Close()
		db.Close()
	})
	o, err := runn.Load("testdata/books/**/*.yml", runn.T(t), runn.Runner("req", ts.URL), runn.DBRunner("db", db))
	if err != nil {
		t.Fatal(err)
	}
	if err := o.RunN(ctx); err != nil {
		t.Fatal(err)
	}
}
Run single runbook using httptest.Server
func TestRouter(t *testing.T) {
	ctx := context.Background()
	db, err := sql.Open("postgres", "user=root password=root host=localhost dbname=test sslmode=disable")
	if err != nil {
		log.Fatal(err)
	}
	ts := httptest.NewServer(NewRouter(db))
	t.Cleanup(func() {
		ts.Close()
		db.Close()
	})
	o, err := runn.New(runn.T(t), runn.Book("testdata/books/login.yml"), runn.Runner("req", ts.URL), runn.DBRunner("db", db))
	if err != nil {
		t.Fatal(err)
	}
	if err := o.Run(ctx); err != nil {
		t.Fatal(err)
	}
}
Run N runbooks with http.Handler
func TestRouter(t *testing.T) {
	ctx := context.Background()
	db, err := sql.Open("postgres", "user=root password=root host=localhost dbname=test sslmode=disable")
	if err != nil {
		log.Fatal(err)
	}
	t.Cleanup(func() {
		db.Close()
	})
	o, err := runn.Load("testdata/books/**/*.yml", runn.T(t), runn.HTTPRunnerWithHandler("req", NewRouter(db)), runn.DBRunner("db", db))
	if err != nil {
		t.Fatal(err)
	}
	if err := o.RunN(ctx); err != nil {
		t.Fatal(err)
	}
}

Runbook

The runbook file has the following format.

step: section accepts array or ordered map.

Array
desc: Login and get projects.
runners:
  req: https://example.com/api/v1
  db: mysql://root:mypass@localhost:3306/testdb
vars:
  username: alice
steps:
  -
    db:
      query: SELECT * FROM users WHERE name = '{{ vars.username }}'
  -
    req:
      /login:
        post:
          body:
            application/json:
              email: "{{ steps[0].rows[0].email }}"
              password: "{{ steps[0].rows[0].password }}"
  -
    test: steps[1].res.status == 200
  -
    req:
      /projects:
        get:
          headers:
            Authorization: "token {{ steps[1].res.body.session_token }}"
          body: null
  -
    test: steps[3].res.status == 200
  -
    test: len(steps[3].res.body.projects) > 0
Map
desc: Login and get projects.
runners:
  req: https://example.com/api/v1
  db: mysql://root:mypass@localhost:3306/testdb
vars:
  username: alice
steps:
  find_user:
    db:
      query: SELECT * FROM users WHERE name = '{{ vars.username }}'
  login:
    req:
      /login:
        post:
          body:
            application/json:
              email: "{{ steps.find_user.rows[0].email }}"
              password: "{{ steps.find_user.rows[0].password }}"
  login_should_be_ok:
    test: steps.login.res.status == 200
  list_projects:
    req:
      /projects:
        get:
          headers:
            Authorization: "token {{ steps.login.res.body.session_token }}"
          body: null
  list_projects_should_be_ok:
    test: steps.list_projects.res.status == 200
  count_projects:
    test: len(steps.list_projects.res.body.projects) > 0

Array

color

Map

color

desc:

Description of runbook.

runners:

Mapping of runners that run steps: of runbook.

In the steps: section, call the runner with the key specified in the runners: section.

Built-in runners such as test runner do not need to be specified in this section.

runners:
  ghapi: ${GITHUB_API_ENDPOINT}
  idp: https://auth.example.com
  db: my:dbuser:${DB_PASS}@hostname:3306/dbname

In the example, each runner can be called by ghapi:, idp: or db: in steps:.

vars:

Mapping of variables available in the steps: of runbook.

vars:
  username: alice@example.com
  token: ${SECRET_TOKEN}

In the example, each variable can be used in {{ vars.username }} or {{ vars.token }} in steps:.

debug:

Enable debug output for runn.

debug: true
steps:

Steps to run in runbook.

The steps are invoked in order from top to bottom.

Any return values are recorded for each step.

When steps: is array, recorded values can be retrieved with {{ steps[*].* }}.

steps:
  -
    db:
      query: SELECT * FROM users WHERE name = '{{ vars.username }}'
  -
    req:
      /users/{{ steps[0].rows[0].id }}:
        get:
          body: null

When steps: is map, recorded values can be retrieved with {{ steps.<key>.* }}.

steps:
  find_user:
    db:
      query: SELECT * FROM users WHERE name = '{{ vars.username }}'
  user_info:
    req:
      /users/{{ steps.find_user.rows[0].id }}:
        get:
          body: null

Runner

HTTP Runner: Do HTTP request

Use https:// or http:// scheme to specify HTTP Runner.

When the step is invoked, it sends the specified HTTP Request and records the response.

runners:
  ghapi: https://api.github.com
DB Runner: Query a database

Use dsn (Data Source Name) to specify DB Runner.

When step is executed, it executes the specified query the database.

If the query is a SELECT clause, it records the selected rows, otherwise it records last_insert_id and rows_affected .

Support Databases

PostgreSQL:

runners:
  mydb: postgres://dbuser:dbpass@hostname:5432/dbname
runners:
  db: pg://dbuser:dbpass@hostname:5432/dbname

MySQL:

runners:
  testdb: mysql://dbuser:dbpass@hostname:3306/dbname
runners:
  db: my://dbuser:dbpass@hostname:3306/dbname

SQLite3:

runners:
  db: sqlite:///path/to/dbname.db
runners:
  local: sq://dbname.db
Exec Runner: execute command

The exec runner is a built-in runner, so there is no need to specify it in the runners: section.

It execute command using command: and stdin:

-
  exec:
    command: grep error
    stdin: '{{ steps[3].res.rawBody }}'
Test Runner: test using recorded values

The test runner is a built-in runner, so there is no need to specify it in the runners: section.

It evaluates the conditional expression using the recorded values.

-
  test: steps[3].res.status == 200
Dump Runner: dump recorded values

The dump runner is a built-in runner, so there is no need to specify it in the runners: section.

It dumps the specified recorded values.

-
  dump: steps[4].rows
Include Runner: include other runbook

The include runner is a built-in runner, so there is no need to specify it in the runners: section.

Include runner reads and runs the runbook in the specified path.

Recorded values are nested.

-
  include: path/to/get_token.yml

Install

As tool

deb:

Use dpkg-i-from-url

$ export RUNN_VERSION=X.X.X
$ curl -L https://git.io/dpkg-i-from-url | bash -s -- https://github.com/k1LoW/runn/releases/download/v$RUNN_VERSION/runn_$RUNN_VERSION-1_amd64.deb

RPM:

$ export RUNN_VERSION=X.X.X
$ yum install https://github.com/k1LoW/runn/releases/download/v$RUNN_VERSION/runn_$RUNN_VERSION-1_amd64.rpm

apk:

Use apk-add-from-url

$ export RUNN_VERSION=X.X.X
$ curl -L https://git.io/apk-add-from-url | sh -s -- https://github.com/k1LoW/runn/releases/download/v$RUNN_VERSION/runn_$RUNN_VERSION-1_amd64.apk

homebrew tap:

$ brew install k1LoW/tap/runn

manually:

Download binary from releases page

docker:

$ docker pull ghcr.io/k1low/runn:latest

go install:

$ go install github.com/k1LoW/runn/cmd/runn@latest
As test helper
$ go get github.com/k1LoW/runn

Alternatives

Documentation

Index

Constants

View Source
const (
	MediaTypeApplicationJSON = "application/json"
)

Variables

View Source
var (
	T       = AsTestHelper
	Runbook = Book
)

Functions

func GetDesc added in v0.4.0

func GetDesc(opt Option) string

func Load added in v0.2.0

func Load(pathp string, opts ...Option) (*operators, error)

func LoadBook

func LoadBook(path string) (*book, error)

func New

func New(opts ...Option) (*operator, error)

func Paths added in v0.4.0

func Paths(pathp string) ([]string, error)

Types

type Option

type Option func(*book) error

func AsTestHelper

func AsTestHelper(t *testing.T) Option

func Book

func Book(path string) Option

func Books added in v0.4.0

func Books(pathp string) ([]Option, error)

func DBRunner

func DBRunner(name string, client *sql.DB) Option

func Debug added in v0.3.0

func Debug(debug bool) Option

func Desc

func Desc(desc string) Option

func HTTPRunner

func HTTPRunner(name, endpoint string, client *http.Client) Option

func HTTPRunnerWithHandler added in v0.6.0

func HTTPRunnerWithHandler(name string, h http.Handler) Option

func Runner

func Runner(name, dsn string) Option

func Var added in v0.4.0

func Var(k string, v interface{}) Option

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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