Skip to content

Commit

Permalink
Experiments with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
qbart committed Oct 1, 2022
1 parent b41743a commit a4174bf
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 36 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ default:

build:
mkdir -p bin/
go build -gcflags='-G=3' -o bin/krab main.go
go build -o bin/krab main.go

install:
cp bin/krab /usr/local/bin

test:
DATABASE_URL="postgres://krab:secret@localhost:5432/krab?sslmode=disable&prefer_simple_protocol=true" go test -gcflags=-G=3 -v ./... && echo "☑️ "
DATABASE_URL="postgres://krab:secret@localhost:5432/krab?sslmode=disable&prefer_simple_protocol=true" go test -v ./... && echo "☑️ "

docker_test:
docker run --rm -e DATABASE_URL="postgres://krab:secret@localhost:5432/krab?sslmode=disable" \
Expand Down
112 changes: 106 additions & 6 deletions krab/cmd_test_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net/http"
"strings"

"github.com/ohkrab/krab/krabdb"
"github.com/ohkrab/krab/krabhcl"
Expand Down Expand Up @@ -65,19 +66,118 @@ func (c *CmdTestRun) Do(ctx context.Context, o CmdOpts) (interface{}, error) {
}
}

// err := c.Connection.Get(func(db krabdb.DB) error {
// resp, err := c.run(ctx, db, o.Inputs)
// result = resp
// return err
// })
err := c.Connection.Get(func(db krabdb.DB) error {
resp, err := c.run(ctx, db, o.Inputs)
result = resp
return err
})

return result, nil
return result, err
}

func (c *CmdTestRun) run(ctx context.Context, db krabdb.DB, inputs Inputs) (ResponseTestRun, error) {
result := ResponseTestRun{}

for _, testCase := range c.Suite.Tests {
fmt.Println(ctc.ForegroundBlue, testCase.Name, ctc.Reset)
for _, it := range testCase.Its {
fmt.Println(" ", ctc.ForegroundBlue, it.Comment, ctc.Reset)

// apply SET parameters
if testCase.Set != nil {
sb := &strings.Builder{}
testCase.Set.ToSQL(sb)
_, err := db.ExecContext(ctx, sb.String())
if err != nil {
panic(fmt.Errorf("SET parameters not set: %w", err))
}
}

// execute `do` from `it` and collect results for further expectations
// fmt.Println(" ", it.Do.SQL)
queryResult := []map[string]interface{}{}
typeResult := map[string]string{}
rows, capturedErr := db.QueryContext(ctx, it.Do.SQL)
capturedErrConsumed := false

if capturedErr == nil {
defer rows.Close()

types, _ := rows.ColumnTypes()
for _, colType := range types {
typeResult[colType.Name()] = colType.DatabaseTypeName()
}
for rows.Next() {
row := map[string]interface{}{}
rows.MapScan(row)
queryResult = append(queryResult, row)
}
} else {
// if query errored we can populate result map for easier processing later
queryResult = append(queryResult, map[string]interface{}{
"error": capturedErr.Error(),
})
}

for _, asserts := range it.RowsAsserts {
for _, expect := range asserts.Expectations {
if expect.Subject == "error" {
capturedErrConsumed = true
fmt.Println(ctc.ForegroundGreen, *expect.Contains, ctc.ForegroundYellow, capturedErr.Error(), ctc.Reset)
}
if expect.Equal == nil {
fmt.Println(" ", ctc.ForegroundGreen, expect.Subject, "= null", ctc.Reset)
} else {
fmt.Println(" ", ctc.ForegroundGreen, expect.Subject, "=", *expect.Equal, ctc.Reset)
}
}
}

if !capturedErrConsumed && capturedErr != nil {
panic(fmt.Errorf("query `%s` failed to execute: %w", it.Do.SQL, capturedErr))
}

for _, rowAssert := range it.RowAsserts {
rowAssert.EachRow(func(i int64) {
query := &strings.Builder{}
query.WriteString("SELECT key, value FROM ( VALUES\n")

rowData := queryResult[i]
for i, expect := range rowAssert.Expectations {
value, ok := rowData[expect.Subject]
if i != 0 {
query.WriteString("\n,")
}
query.WriteString(fmt.Sprintf("(%s, %s)", krabdb.QuoteIdent(expect.Subject), krabdb.Quote(value)))
fmt.Println(value)
if !ok {
panic("Expectation defined for missing column")
}

color := ctc.ForegroundGreen
success := makeAssertion(expect, value)
if !success {
color = ctc.ForegroundRed
}
fmt.Println(" ", color, expect.Subject, value, *expect.Equal, ctc.Reset)
}
query.WriteString("\n) AS t(key, value)")
fmt.Println(" ", ctc.ForegroundGreen, query.String(), ctc.Reset)
})
}
}
}

// _, err := db.ExecContext(ctx, sql)

return result, nil
}

func makeAssertion(expect *Expect, value interface{}) bool {
if expect.Equal == nil {
return value == nil
}
// fmt.Printf("// expect: %T, value: %T\n", *expect.Equal, value)

return *expect.Equal == value
}
19 changes: 19 additions & 0 deletions krab/type_set_runtime_parameters.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package krab

import (
"io"
)

// SetRuntimeParameters
// https://www.postgresql.org/docs/current/sql-set.html
type SetRuntimeParameters struct {
SearchPath *string `hcl:"search_path"`
}

// ToSQL converts set parameters to SQL.
func (d *SetRuntimeParameters) ToSQL(w io.StringWriter) {
if d.SearchPath != nil {
w.WriteString("SET search_path TO ")
w.WriteString(*d.SearchPath)
}
}
24 changes: 19 additions & 5 deletions krab/type_test_example.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package krab

import (
"fmt"
"strconv"

"github.com/ohkrab/krab/krabhcl"
)

// TestExample represents test runner configuration.
//
type TestExample struct {
TestSuiteRefName string `hcl:"test_suite,label"`
Name string `hcl:"name,label"`
Its []*TestExampleIt `hcl:"it,block"`
TestSuiteRefName string `hcl:"test_suite,label"`
Name string `hcl:"name,label"`
Set *SetRuntimeParameters `hcl:"set,block"`
Its []*TestExampleIt `hcl:"it,block"`
}

func (t *TestExample) Addr() krabhcl.Addr {
Expand Down Expand Up @@ -41,8 +45,18 @@ type AssertRow struct {
Expectations []*Expect `hcl:"expect,block"`
}

// EachRow yields each function for every row defined in the scope.
func (a *AssertRow) EachRow(each func(i int64)) {
i, err := strconv.ParseInt(a.Scope, 10, 64)
if err != nil {
panic(fmt.Sprintf("Failed to parse scope `%s` to rows: %v", a.Scope, err))
}
each(i)
}

// Expect
type Expect struct {
Subject string `hcl:"subject,label"`
Equal *string `hcl:"equal,optional"`
Subject string `hcl:"subject,label"`
Equal *string `hcl:"eq,optional"`
Contains *string `hcl:"contains,optional"`
}
4 changes: 2 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ func main() {

dir, err := krabenv.ConfigDir()
if err != nil {
ui.Error(fmt.Errorf("Can't read config dir: %w", err).Error())
ui.Error(fmt.Errorf("can't read config dir: %w", err).Error())
os.Exit(1)
}

parser := krab.NewParser()
config, err := parser.LoadConfigDir(dir)
if err != nil {
ui.Error(fmt.Errorf("Parsing error: %w", err).Error())
ui.Error(fmt.Errorf("parsing error: %w", err).Error())
os.Exit(1)
}

Expand Down
15 changes: 12 additions & 3 deletions test/fixtures/tests/versions.krab.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ migration "create_version_type" {
major SMALLINT,
minor SMALLINT,
patch SMALLINT
)
);
SQL
}

Expand All @@ -32,10 +32,19 @@ migration "create_version_function" {
_v.major = _v.major + 1;
_v.minor = 0;
_v.patch = 0;
RETURN _v;
WHEN _type = 'minor' THEN
_v.minor = _v.minor + 1;
_v.patch = 0;
WHEN _type = 'patch' THEN
_v.patch = _v.patch + 1;
ELSE
RAISE EXCEPTION 'Failed to increase version using type = % for version %.%.%', _type, _ver.major, _ver.minor, _ver.patch;
END CASE;
RAISE EXCEPTION 'Failed to increase version using type = %s for version %L.%L.%L', _type, _ver.major, _ver.minor, _ver.patch;
RETURN _v;
END;
$$
RETURNS NULL ON NULL INPUT
Expand Down
48 changes: 30 additions & 18 deletions test/fixtures/tests/versions_test.krab.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -16,53 +16,65 @@ test_suite "versions" {
}

test "versions" "version_inc()" {
set {
search_path = "testing"
}

it "increases `major` component by default when no type specified" {
do { sql = "SELECT version_inc('1.1.1') AS ver" }
do {
sql = "SELECT version_inc(row(1,1,1)::sem_version) AS ver"
}

row "0" {
expect "ver" { equal = "2.0.0" }
column "ver" { assert = "(2,0,0)" }
}
}

it "increases `major` component and resets `minor` and `patch`" {
do { sql = "SELECT version_inc('1.1.1', 'major') AS ver" }
do { sql = "SELECT version_inc(row(1,1,1)::sem_version, 'major') AS ver" }

row "0" {
expect "ver" { equal = "2.0.0" }
column "ver" { assert = "ver = row(2,0,0)::sem_version" }
}
}

it "increases `minor` component and resets `patch` leaving `major` untouched" {
do { sql = "SELECT version_inc('1.1.1', 'minor') AS ver" }
describe "increases `minor` component and resets `patch` leaving `major` untouched" {
do { sql = "SELECT version_inc(row(1,1,1)::sem_version, 'minor') AS ver" }

# v1 - set scope
row "0" {
expect "ver" { equal = "1.2.0" }
it "ver" { expect = "ver = row(2,0,0)::sem_version" }
its { expect = "ver = row(2,0,0)::sem_version" }
}

# v2
it "0" "ver" { expect = "ver = row(2,0,0)::sem_version" }
its "0" { expect = "ver = row(2,0,0)::sem_version" }
}

it "increases `patch` component and leaves `major` and `minor` untouched" {
do { sql = "SELECT version_inc('1.1.1', 'patch') AS ver" }
do { sql = "SELECT version_inc(row(1,1,1)::sem_version, 'patch') AS ver" }

row "0" {
expect "ver" { equal = "1.1.2" }
column "ver" { assert = "(1,1,2)" }
}
}

it "raises error when increasing invalid component" {
do { sql = "SELECT version_inc('1.1.1', 'invalid') AS ver" }
/* it "raises error when increasing invalid component" { */
/* do { sql = "SELECT version_inc(row(1,1,1)::sem_version, 'invalid') AS ver" } */

rows {
expect "error" {
equal = "Failed to increase version using type = 'invalid' for version 1.1.1"
}
}
}
/* rows { */
/* raises_error { */
/* message = "Failed to increase version using type = 'invalid' for version 1.1.1" */
/* } */
/* } */
/* } */

it "returns null on null input" {
do { sql = "SELECT version_inc(null) AS ver" }

row "0" {
expect "ver" { equal = null }
column "ver" { assert = null }
}
}
}

0 comments on commit a4174bf

Please sign in to comment.