Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: additional usage examples #18

Merged
merged 1 commit into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.idea
gocuke.iml
gocuke.iml
coverage.txt
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ linters-settings:
min-confidence: 0
gocyclo:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 10
min-complexity: 20
unused:
# treat code as a program (not a library) and report unused exported identifiers; default is false.
# XXX: if you enable this setting, unused will report a lot of false-positives in text editors:
Expand Down
53 changes: 53 additions & 0 deletions _examples/api/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Example - demonstrates REST API server implementation tests.
package main

import (
"fmt"
"log"
"net/http"

"github.com/regen-network/gocuke"
)

// Server implements http.Server
type Server struct {
*http.Server
}

// NewServer creates a new instance of Server
func NewServer(port int, handler http.Handler) *Server {
addr := fmt.Sprintf(":%d", port)
srv := &http.Server{
Addr: addr,
Handler: handler,
}
return &Server{
Server: srv,
}
}

func getVersion(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
fail(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
data := struct {
Version string `json:"version"`
}{Version: gocuke.Version}
ok(w, data)
}

func main() {
// Define your handler
mux := http.NewServeMux()
mux.HandleFunc("/version", getVersion)

// Create a new server instance
server := NewServer(8080, mux)

// Start the server
fmt.Printf("Server running on port %s...\n", server.Addr)
if err := server.ListenAndServe(); err != nil {
log.Fatalf("Server failed to start: %v", err)
}
}
57 changes: 57 additions & 0 deletions _examples/api/api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package main

import (
"fmt"
"net/http"
"net/http/httptest"
"testing"

"github.com/regen-network/gocuke"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

type suite struct {
// special arguments like TestingT are injected automatically into exported fields
gocuke.TestingT
resp *httptest.ResponseRecorder
}

func TestApi(t *testing.T) {
scope := &suite{TestingT: t, resp: httptest.NewRecorder()}
gocuke.NewRunner(t, scope).
Step(`^I send "(GET|POST|PUT|DELETE)" request to "([^"]*)"$`, scope.ISendRequestTo).
Step(`^the response code should be (\d+)$`, scope.TheResponseCodeShouldBe).
Step(`^the response should match json:$`, scope.TheResponseShouldMatchJson).
Run()
}

func (s *suite) ISendRequestTo(method string, endpoint string) {
req, err := http.NewRequest(method, endpoint, nil)
assert.Nil(s, err)

defer func() {
switch t := recover().(type) {
case string:
err = fmt.Errorf(t)
case error:
err = t
}
}()

switch endpoint {
case "/version":
getVersion(s.resp, req)
default:
err = fmt.Errorf("unknown endpoint: %s", endpoint)
}
assert.Nil(s, err)
}

func (s *suite) TheResponseCodeShouldBe(code int64) {
assert.Equalf(s, code, int64(s.resp.Code), "expected response code to be: %d, but actual is: %d", code, s.resp.Code)
}

func (s *suite) TheResponseShouldMatchJson(body gocuke.DocString) {
require.JSONEq(s, body.Content, s.resp.Body.String())
}
24 changes: 24 additions & 0 deletions _examples/api/features/version.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Feature: get version
In order to know gocuke version
As an API user
I need to be able to request version

Scenario: does not allow POST method
When I send "POST" request to "/version"
Then the response code should be 405
And the response should match json:
"""
{
"error": "Method not allowed"
}
"""

Scenario: should get version number
When I send "GET" request to "/version"
Then the response code should be 200
And the response should match json:
"""
{
"version": "v0.0.0-dev"
}
"""
31 changes: 31 additions & 0 deletions _examples/api/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package main

import (
"encoding/json"
"net/http"
)

// fail writes a json response with error msg and status header
func fail(w http.ResponseWriter, msg string, status int) {
w.WriteHeader(status)

data := struct {
Error string `json:"error"`
}{Error: msg}
resp, _ := json.Marshal(data)

w.Header().Set("Content-Type", "application/json")
w.Write(resp)
}

// ok writes data to response with 200 status
func ok(w http.ResponseWriter, data interface{}) {
resp, err := json.Marshal(data)
if err != nil {
fail(w, "Oops something evil has happened", http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/json")
w.Write(resp)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ Feature: data tables
Then it has 4 rows and 4 columns
And 3 rows as a header table
Then the values add up when accessed as a header table
And the total sum of the x + y + z column is 27
And the total sum of the x + y + z column is 27
18 changes: 10 additions & 8 deletions datatable_test.go → _examples/datatable/datatable_test.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
package gocuke
package datatable

import (
"gotest.tools/v3/assert"
"testing"

"github.com/regen-network/gocuke"
"gotest.tools/v3/assert"
)

func TestDataTable(t *testing.T) {
NewRunner(t, &dataTableSuite{}).
Path("features/datatable.feature").Run()
gocuke.NewRunner(t, &dataTableSuite{}).
Path("datatable.feature").Run()
}

type dataTableSuite struct {
TestingT
datatable DataTable
gocuke.TestingT
datatable gocuke.DataTable
total int64
}

func (d *dataTableSuite) Before(t TestingT) {
func (d *dataTableSuite) Before(t gocuke.TestingT) {
d.TestingT = t
}

func (s *dataTableSuite) ThisDataTable(a DataTable) {
func (s *dataTableSuite) ThisDataTable(a gocuke.DataTable) {
s.datatable = a
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ Feature: simple
Examples:
| x | y | z |
| 5 | 3 | 2 |
| 10 | 2 | 8 |
| 10 | 2 | 8 |
7 changes: 4 additions & 3 deletions custom_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package gocuke_test

import (
"github.com/regen-network/gocuke"
"github.com/stretchr/testify/require"
"regexp"
"testing"

"github.com/regen-network/gocuke"
"github.com/stretchr/testify/require"
)

func TestCustomSteps(t *testing.T) {
gocuke.NewRunner(t, &customStepsSuite{}).
Path("features/simple.feature").
Path("_examples/simple/simple.feature").
Step(`I have (\d+) cukes`, (*customStepsSuite).A).
Step(regexp.MustCompile(`I eat (\d+)`), (*customStepsSuite).B).
Step(`I have (\d+) left`, (*customStepsSuite).C).
Expand Down
3 changes: 1 addition & 2 deletions guess.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,9 @@ func lastChar(x string) byte {
}

func toFirstUpperIdentifier(str string) string {
runes := []rune(str)
var res []rune
isFirst := true
for _, r := range runes {
for _, r := range str {
if isFirst {
if !(unicode.IsLetter(r) || unicode.IsNumber(r)) {
continue
Expand Down
16 changes: 16 additions & 0 deletions mod_version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package gocuke

import (
"runtime/debug"
)

// Version of package - based on Semantic Versioning 2.0.0 http://semver.org/
var Version = "v0.0.0-dev"

func init() {
if info, available := debug.ReadBuildInfo(); available {
if Version == "v0.0.0-dev" && info.Main.Version != "(devel)" {
Version = info.Main.Version
}
}
}
11 changes: 4 additions & 7 deletions run.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,8 @@ func (r *Runner) Run() {
}

for _, file := range files {
f, err := os.Open(file)
f, err := os.Open(file) //nolint:gosec,EXC0010
assert.NilError(r.topLevelT, err)
defer func() {
err := f.Close()
if err != nil {
panic(err)
}
}()

haveTests = true

Expand All @@ -58,6 +52,9 @@ func (r *Runner) Run() {

(newDocRunner(r, doc)).runDoc(t)
})
if err = f.Close(); err != nil {
panic(err)
}
}
}

Expand Down
8 changes: 4 additions & 4 deletions run_step.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ func (r *scenarioRunner) runStep(step *messages.PickleStep, def *stepDef) {
// pickleArg goes last
if hasPickleArg {
i := expectedIn - 1
typ := typ.In(i)
pickleArgType := typ.In(i)
// only one of DataTable or DocString is valid
if typ == dataTableType {
if pickleArgType == dataTableType {
if step.Argument.DataTable == nil {
r.t.Fatalf("expected non-nil DataTable")
}
Expand All @@ -81,7 +81,7 @@ func (r *scenarioRunner) runStep(step *messages.PickleStep, def *stepDef) {
table: step.Argument.DataTable,
}
values[i] = reflect.ValueOf(dataTable)
} else if typ == docStringType {
} else if pickleArgType == docStringType {
if step.Argument.DocString == nil {
r.t.Fatalf("expected non-nil DocString")
}
Expand All @@ -92,7 +92,7 @@ func (r *scenarioRunner) runStep(step *messages.PickleStep, def *stepDef) {
}
values[i] = reflect.ValueOf(docString)
} else {
r.t.Fatalf("unexpected parameter type %v in function %s", typ, def.funcLoc)
r.t.Fatalf("unexpected parameter type %v in function %s", pickleArgType, def.funcLoc)
}
}

Expand Down
4 changes: 2 additions & 2 deletions simple_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
)

func TestSimple(t *testing.T) {
gocuke.NewRunner(t, &simpleSuite{}).Path("features/simple.feature").Run()
gocuke.NewRunner(t, &simpleSuite{}).Path("_examples/simple/simple.feature").Run()
}

type simpleSuite struct {
Expand All @@ -31,7 +31,7 @@ func (s *simpleSuite) IHaveLeft(a int64) {

// test if a struct that doesn't use a pointer and a global var
func TestSimpleNonPointer(t *testing.T) {
gocuke.NewRunner(t, simpleSuiteNP{}).Path("features/simple.feature").Run()
gocuke.NewRunner(t, simpleSuiteNP{}).Path("_examples/simple/simple.feature").Run()
}

var globalCukes int64
Expand Down
9 changes: 5 additions & 4 deletions step_def.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package gocuke

import (
"fmt"
"gotest.tools/v3/assert"
"reflect"
"regexp"
"runtime"
"testing"

"gotest.tools/v3/assert"
)

type stepDef struct {
Expand Down Expand Up @@ -78,15 +79,15 @@ func (r *Runner) newStepDefOrHook(t *testing.T, exp *regexp.Regexp, f reflect.Va
}

for i := 0; i < typ.NumIn(); i++ {
typ := typ.In(i)
getter, ok := r.supportedSpecialArgs[typ]
stepTyp := typ.In(i)
getter, ok := r.supportedSpecialArgs[stepTyp]
if !ok {
// expect remaining args to be step arguments
break
}

def.specialArgs = append(def.specialArgs, &specialArg{
typ: typ,
typ: stepTyp,
getValue: getter,
})
}
Expand Down
2 changes: 1 addition & 1 deletion tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func initGlobalTagExpr() tag.Evaluatable {
globalTagExpr, err = tag.Parse(*flagTags)
if err != nil {
if err != nil {
panic(fmt.Errorf("error parsing tag expression %q: %v\n", *flagTags, err))
panic(fmt.Errorf("error parsing tag expression %q: %v", *flagTags, err))
}
}
}
Expand Down
Loading
Loading