Skip to content

Commit

Permalink
Merge pull request #1 from godogx/init
Browse files Browse the repository at this point in the history
Initial commit
  • Loading branch information
vearutop authored Jan 9, 2022
2 parents 7ffd125 + 3a99cf4 commit 6b37fcf
Show file tree
Hide file tree
Showing 21 changed files with 2,437 additions and 158 deletions.
114 changes: 0 additions & 114 deletions .github/workflows/bench.yml

This file was deleted.

4 changes: 4 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@ linters-settings:
check-exported: false
unparam:
check-exported: true
cyclop:
max-complexity: 15

linters:
enable-all: true
disable:
- gosec
- nilnil
- lll
- maligned
- gochecknoglobals
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2021 bool64
Copyright (c) 2022 Viacheslav Poturaev

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ endif
-include $(DEVGO_PATH)/makefiles/main.mk
-include $(DEVGO_PATH)/makefiles/lint.mk
-include $(DEVGO_PATH)/makefiles/test-unit.mk
-include $(DEVGO_PATH)/makefiles/bench.mk
-include $(DEVGO_PATH)/makefiles/reset-ci.mk

# Add your custom targets here.
Expand Down
165 changes: 153 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,158 @@
# go-template
# Cucumber database steps for Go

[![Build Status](https://github.com/bool64/go-template/workflows/test-unit/badge.svg)](https://github.com/bool64/go-template/actions?query=branch%3Amaster+workflow%3Atest-unit)
[![Coverage Status](https://codecov.io/gh/bool64/go-template/branch/master/graph/badge.svg)](https://codecov.io/gh/bool64/go-template)
[![GoDevDoc](https://img.shields.io/badge/dev-doc-00ADD8?logo=go)](https://pkg.go.dev/github.com/bool64/go-template)
[![Time Tracker](https://wakatime.com/badge/github/bool64/go-template.svg)](https://wakatime.com/badge/github/bool64/go-template)
![Code lines](https://sloc.xyz/github/bool64/go-template/?category=code)
![Comments](https://sloc.xyz/github/bool64/go-template/?category=comments)
[![Build Status](https://github.com/godogx/dbsteps/workflows/test/badge.svg)](https://github.com/godogx/dbsteps/actions?query=branch%3Amaster+workflow%3Atest)
[![Coverage Status](https://codecov.io/gh/bool64/dbsteps/branch/master/graph/badge.svg)](https://codecov.io/gh/bool64/dbsteps)
[![GoDevDoc](https://img.shields.io/badge/dev-doc-00ADD8?logo=go)](https://pkg.go.dev/github.com/godogx/dbsteps)
[![Time Tracker](https://wakatime.com/badge/github/bool64/dbsteps.svg)](https://wakatime.com/badge/github/bool64/dbsteps)
![Code lines](https://sloc.xyz/github/bool64/dbsteps/?category=code)
![Comments](https://sloc.xyz/github/bool64/dbsteps/?category=comments)

<!--- TODO Update README.md -->
This module implements database-related step definitions
for [`github.com/cucumber/godog`](https://github.com/cucumber/godog).

Project template with GitHub actions for Go.
## Database Configuration

## Usage
Databases instances should be configured with `Manager.Instances`.

Create a new repository from this template, check out it and run `./run_me.sh` to replace template name with name of
your repository.
```go
dbm := dbsteps.Manager{}

dbm.Instances = map[string]dbsteps.Instance{
"my_db": {
Storage: storage,
Tables: map[string]interface{}{
"my_table": new(repository.MyRow),
"my_another_table": new(repository.MyAnotherRow),
},
},
}
```

Row types should be structs with `db` field tags, for example:

```go
type MyRow struct {
ID int `db:"id"`
Name string `db:"name"`
}
```

These structures are used to map data between database and `gherkin` tables.

## Table Mapper Configuration

Table mapper allows customizing decoding string values from godog table cells into Go row structures and back.

```go
tableMapper := dbsteps.NewTableMapper()

// Apply JSON decoding to a particular type.
tableMapper.Decoder.RegisterFunc(func(s string) (interface{}, error) {
m := repository.Meta{}
err := json.Unmarshal([]byte(s), &m)
if err != nil {
return nil, err
}
return m, err
}, repository.Meta{})

// Apply string splitting to github.com/lib/pq.StringArray.
tableMapper.Decoder.RegisterFunc(func(s string) (interface{}, error) {
return pq.StringArray(strings.Split(s, ",")), nil
}, pq.StringArray{})

// Create database manager with custom mapper.
dbm := dbsteps.Manager{
TableMapper: tableMapper,
}
```

## Step Definitions

Delete all rows from table.

```gherkin
Given there are no rows in table "my_table" of database "my_db"
```

Populate rows in a database.

```gherkin
And these rows are stored in table "my_table" of database "my_db"
| id | foo | bar | created_at | deleted_at |
| 1 | foo-1 | abc | 2021-01-01T00:00:00Z | NULL |
| 2 | foo-1 | def | 2021-01-02T00:00:00Z | 2021-01-03T00:00:00Z |
| 3 | foo-2 | hij | 2021-01-03T00:00:00Z | 2021-01-03T00:00:00Z |
```

```gherkin
And rows from this file are stored in table "my_table" of database "my_db"
"""
path/to/rows.csv
"""
```

Assert rows existence in a database.

For each row in gherkin table database is queried to find a row with `WHERE` condition that includes provided column
values.

If a column has `NULL` value, it is excluded from `WHERE` condition.

Column can contain variable (any unique string starting with `$` or other prefix configured with `Manager.VarPrefix`).
If variable has not yet been populated, it is excluded from `WHERE` condition and populated with value received from
database. When this variable is used in next steps, it replaces the value of column with value of variable.

Variables can help to assert consistency of dynamic data, for example variable can be populated as ID of one entity and
then checked as foreign key value of another entity. This can be especially helpful in cases of UUIDs.

If column value represents JSON array or object it is excluded from `WHERE` condition, value assertion is done by
comparing Go value mapped from database row field with Go value mapped from gherkin table cell.

```gherkin
Then these rows are available in table "my_table" of database "my_db"
| id | foo | bar | created_at | deleted_at |
| $id1 | foo-1 | abc | 2021-01-01T00:00:00Z | NULL |
| $id2 | foo-1 | def | 2021-01-02T00:00:00Z | 2021-01-03T00:00:00Z |
| $id3 | foo-2 | hij | 2021-01-03T00:00:00Z | 2021-01-03T00:00:00Z |
```

```gherkin
Then rows from this file are available in table "my_table" of database "my_db"
"""
path/to/rows.csv
"""
```

It is possible to check table contents exhaustively by adding "only" to step statement. Such assertion will also make
sure that total number of rows in database table matches number of rows in gherkin table.

```gherkin
Then only these rows are available in table "my_table" of database "my_db"
| id | foo | bar | created_at | deleted_at |
| $id1 | foo-1 | abc | 2021-01-01T00:00:00Z | NULL |
| $id2 | foo-1 | def | 2021-01-02T00:00:00Z | 2021-01-03T00:00:00Z |
| $id3 | foo-2 | hij | 2021-01-03T00:00:00Z | 2021-01-03T00:00:00Z |
```

```gherkin
Then only rows from this file are available in table "my_table" of database "my_db"
"""
path/to/rows.csv
"""
```

Assert no rows exist in a database.

```gherkin
And no rows are available in table "my_another_table" of database "my_db"
```

The name of database instance `of database "my_db"` can be omitted in all steps, in such case `"default"` will be used from database instance name.

## Concurrent Usage

Please note, due to centralized nature of database instance, scenarios that work with same tables would conflict.
In order to avoid conflicts, `dbsteps` locks access to a specific scenario until that scenario is finished.
The lock is per table, so if scenarios are operating on different tables, they will not conflict.
It is safe to use concurrent scenarios.
27 changes: 27 additions & 0 deletions _testdata/Database.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Feature: Database Query

Scenario: Successful Query
Given there are no rows in table "my_table" of database "my_db"

And rows from this file are stored in table "my_table" of database "my_db"
"""
_testdata/rows.csv
"""

And these rows are stored in table "my_table" of database "my_db"
| id | foo | bar | created_at | deleted_at |
| 1 | foo-1 | abc | 2021-01-01T00:00:00Z | NULL |

Then only these rows are available in table "my_table" of database "my_db"
| id | foo | bar | created_at | deleted_at |
| $id1 | $foo1 | abc | 2021-01-01T00:00:00Z | NULL |
| $id2 | $foo1 | def | 2021-01-02T00:00:00Z | 2021-01-03T00:00:00Z |
| $id3 | foo-2 | hij | 2021-01-03T00:00:00Z | 2021-01-03T00:00:00Z |

Then only these rows are available in table "my_table" of database "my_db"
| id | foo | bar | created_at | deleted_at |
| $id1 | $foo1 | abc | 2021-01-01T00:00:00Z | NULL |
| $id2 | $foo1 | def | 2021-01-02T00:00:00Z | 2021-01-03T00:00:00Z |
| $id3 | foo-2 | hij | 2021-01-03T00:00:00Z | 2021-01-03T00:00:00Z |

And no rows are available in table "my_another_table" of database "my_db"
19 changes: 19 additions & 0 deletions _testdata/DatabaseConcurrent.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Feature: No locking for different tables

Scenario: Table 1
Given I sleep
Given I should not be blocked for "db1::t1"
Given there are no rows in table "t1" of database "db1"
And I sleep

Scenario: Table 2
Given I sleep
Given I should not be blocked for "db2::t2"
Given there are no rows in table "t2" of database "db2"
And I sleep

Scenario: Table 3
Given I sleep
Given I should not be blocked for "db3::t3"
Given there are no rows in table "t3" of database "db3"
And I sleep
19 changes: 19 additions & 0 deletions _testdata/DatabaseConcurrentBlocked.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Feature: No locking for different tables

Scenario: Table 1
Given I sleep
Given I should not be blocked for "db1::t1"
Given there are no rows in table "t1" of database "db1"
And I sleep

Scenario: Table 1 again
Given I sleep
Given I should not be blocked for "db1::t1"
Given there are no rows in table "t1" of database "db1"
And I sleep

Scenario: Table 3
Given I sleep
Given I should not be blocked for "db3::t3"
Given there are no rows in table "t3" of database "db3"
And I sleep
Loading

0 comments on commit 6b37fcf

Please sign in to comment.