Skip to content

Commit

Permalink
Merge pull request #12 from k1LoW/include
Browse files Browse the repository at this point in the history
Support `include:` in steps
  • Loading branch information
k1LoW authored Mar 11, 2022
2 parents 4e059e5 + 1106a7c commit cc13434
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 35 deletions.
2 changes: 2 additions & 0 deletions book.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type book struct {
Vars map[string]interface{} `yaml:"vars,omitempty"`
Steps []map[string]interface{} `yaml:"steps,omitempty"`
Debug bool `yaml:"debug,omitempty"`
path string
httpRunners map[string]*httpRunner
dbRunners map[string]*dbRunner
t *testing.T
Expand Down Expand Up @@ -68,6 +69,7 @@ func LoadBook(path string) (*book, error) {
_ = f.Close()
return nil, err
}
bk.path = path
if err := f.Close(); err != nil {
return nil, err
}
Expand Down
52 changes: 52 additions & 0 deletions include.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package runn

import (
"context"
"path/filepath"
)

const includeRunnerKey = "include"

type includeRunner struct {
operator *operator
}

func newIncludeRunner(o *operator) (*includeRunner, error) {
return &includeRunner{
operator: o,
}, nil
}

func (rnr *includeRunner) Run(ctx context.Context, path string) error {
oo, err := rnr.operator.newNestedOperator(Book(filepath.Join(rnr.operator.root, path)))
if err != nil {
return err
}
if err := oo.Run(ctx); err != nil {
return err
}
rnr.operator.store.steps = append(rnr.operator.store.steps, map[string]interface{}{
"steps": oo.store.steps,
})
return nil
}

func (o *operator) newNestedOperator(opts ...Option) (*operator, error) {
for k, r := range o.httpRunners {
opts = append(opts, HTTPRunner(k, r.endpoint.String(), r.client))
}
for k, r := range o.dbRunners {
opts = append(opts, DBRunner(k, r.client))
}
for k, v := range o.store.vars {
opts = append(opts, Var(k, v))
}
opts = append(opts, Var("parent", o.store.steps))
opts = append(opts, Debug(o.debug))
oo, err := New(opts...)
if err != nil {
return nil, err
}
oo.t = o.t
return oo, nil
}
49 changes: 49 additions & 0 deletions include_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package runn

import (
"context"
"fmt"
"os"
"testing"
)

func TestIncludeRunnerRun(t *testing.T) {
tests := []struct {
book string
want int
}{
{"testdata/book/db.yml", 8},
}
ctx := context.Background()
for _, tt := range tests {
db, err := os.CreateTemp("", "tmp")
if err != nil {
t.Fatal(err)
}
defer os.Remove(db.Name())
o, err := New(Runner("db", fmt.Sprintf("sqlite://%s", db.Name())))
if err != nil {
t.Fatal(err)
}
r, err := newIncludeRunner(o)
if err != nil {
t.Fatal(err)
}
if err := r.Run(ctx, tt.book); err != nil {
t.Fatal(err)
}

{
got := len(r.operator.store.steps)
if want := 1; got != want {
t.Errorf("got %v\nwant %v", got, want)
}
}
{
got := len(r.operator.store.steps[0]["steps"].([]map[string]interface{}))
if got != tt.want {
t.Errorf("got %v\nwant %v", got, tt.want)
}
}
}
}
63 changes: 48 additions & 15 deletions operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
Expand All @@ -17,15 +18,17 @@ import (
var expandRe = regexp.MustCompile(`"?{{\s*([^}]+)\s*}}"?`)

type step struct {
httpRunner *httpRunner
httpRequest map[string]interface{}
dbRunner *dbRunner
dbQuery map[string]interface{}
testRunner *testRunner
testCond string
dumpRunner *dumpRunner
dumpCond string
debug bool
httpRunner *httpRunner
httpRequest map[string]interface{}
dbRunner *dbRunner
dbQuery map[string]interface{}
testRunner *testRunner
testCond string
dumpRunner *dumpRunner
dumpCond string
includeRunner *includeRunner
includePath string
debug bool
}

type store struct {
Expand All @@ -40,6 +43,7 @@ type operator struct {
store store
desc string
debug bool
root string
t *testing.T
}

Expand All @@ -50,6 +54,7 @@ func New(opts ...Option) (*operator, error) {
return nil, err
}
}

o := &operator{
httpRunners: map[string]*httpRunner{},
dbRunners: map[string]*dbRunner{},
Expand All @@ -62,12 +67,20 @@ func New(opts ...Option) (*operator, error) {
t: bk.t,
}

if bk.path != "" {
o.root = filepath.Dir(bk.path)
} else {
wd, err := os.Getwd()
if err != nil {
return nil, err
}
o.root = wd
}

for k, v := range bk.Runners {
switch {
case k == testRunnerKey:
return nil, fmt.Errorf("runners[%s] is reserved as test runner", testRunnerKey)
case k == dumpRunnerKey:
return nil, fmt.Errorf("runners[%s] is reserved as dump runner", dumpRunnerKey)
case k == includeRunnerKey || k == testRunnerKey || k == dumpRunnerKey:
return nil, fmt.Errorf("runner name '%s' is reserved for built-in runner", k)
case strings.Index(v, "https://") == 0 || strings.Index(v, "http://") == 0:
hc, err := newHTTPRunner(k, v, o)
if err != nil {
Expand Down Expand Up @@ -135,6 +148,19 @@ func (o *operator) AppendStep(s map[string]interface{}) error {
step.dumpCond = vv
continue
}
if k == includeRunnerKey {
ir, err := newIncludeRunner(o)
if err != nil {
return err
}
step.includeRunner = ir
vv, ok := v.(string)
if !ok {
return fmt.Errorf("invalid include path: %v", v)
}
step.includePath = vv
continue
}
h, ok := o.httpRunners[k]
if ok {
step.httpRunner = h
Expand Down Expand Up @@ -221,14 +247,21 @@ func (o *operator) run(ctx context.Context) error {
_, _ = fmt.Fprintf(os.Stderr, "Run '%s' on steps[%d]\n", testRunnerKey, i)
}
if err := s.testRunner.Run(ctx, s.testCond); err != nil {
return fmt.Errorf("test failed on steps[%d]: %s", i, s.testCond)
return fmt.Errorf("test failed on steps[%d]: %v", i, err)
}
case s.dumpRunner != nil && s.dumpCond != "":
if o.debug {
_, _ = fmt.Fprintf(os.Stderr, "Run '%s' on steps[%d]\n", dumpRunnerKey, i)
}
if err := s.dumpRunner.Run(ctx, s.dumpCond); err != nil {
return fmt.Errorf("dump failed on steps[%d]: %s", i, s.dumpCond)
return fmt.Errorf("dump failed on steps[%d]: %v", i, err)
}
case s.includeRunner != nil && s.includePath != "":
if o.debug {
_, _ = fmt.Fprintf(os.Stderr, "Run '%s' on steps[%d]\n", includeRunnerKey, i)
}
if err := s.includeRunner.Run(ctx, s.includePath); err != nil {
return fmt.Errorf("include failed on steps[%d]: %v", i, err)
}
default:
return fmt.Errorf("invalid steps[%d]: %v", i, s)
Expand Down
4 changes: 2 additions & 2 deletions operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ func TestLoad(t *testing.T) {
path string
want int
}{
{"testdata/book/*", 3},
{"testdata/**/*", 3},
{"testdata/book/*", 4},
{"testdata/**/*", 4},
}
for _, tt := range tests {
ops, err := Load(tt.path, Runner("req", "https://api.github.com"), Runner("db", "sqlite://path/to/test.db"))
Expand Down
1 change: 1 addition & 0 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func Book(path string) Option {
}
bk.Steps = loaded.Steps
bk.Debug = loaded.Debug
bk.path = loaded.path
return nil
}
}
Expand Down
24 changes: 6 additions & 18 deletions testdata/book/db.yml
Original file line number Diff line number Diff line change
@@ -1,34 +1,22 @@
desc: Test using SQLite3
steps:
-
db:
query: |
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
created NUMERIC NOT NULL,
updated NUMERIC
)
-
db:
query: INSERT INTO users (username, password, email, created) VALUES ('alice', 'passw0rd', '[email protected]', datetime('2017-12-05'))
include: initdb.yml
-
db:
query: SELECT * FROM users;
-
test: 'steps[2].rows[0].username == "alice"'
test: 'steps[1].rows[0].username == "alice"'
-
db:
query: INSERT INTO users (username, password, email, created) VALUES ('bob', 'passw0rd', 'bob@example.com', datetime('2022-02-22'))
query: INSERT INTO users (username, password, email, created) VALUES ('charlie', 'passw0rd', 'charlie@example.com', datetime('2022-02-22'))
-
db:
query: SELECT * FROM users WHERE id = {{ steps[4].last_insert_id }}
query: SELECT * FROM users WHERE id = {{ steps[3].last_insert_id }}
-
test: 'steps[5].rows[0].username == "bob"'
test: 'steps[4].rows[0].username == "charlie"'
-
db:
query: SELECT COUNT(*) AS c FROM users
-
test: 'steps[7].rows[0].c == 2'
test: 'steps[6].rows[0].c == 3'
19 changes: 19 additions & 0 deletions testdata/book/initdb.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
desc: Initialize SQLite3
steps:
-
db:
query: |
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
created NUMERIC NOT NULL,
updated NUMERIC
)
-
db:
query: INSERT INTO users (username, password, email, created) VALUES ('alice', 'passw0rd', '[email protected]', datetime('2017-12-05'))
-
db:
query: INSERT INTO users (username, password, email, created) VALUES ('bob', 'passw0rd', '[email protected]', datetime('2022-02-22'))

0 comments on commit cc13434

Please sign in to comment.