Skip to content

Commit

Permalink
added integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Kansuler committed Apr 8, 2022
1 parent bff1422 commit a77c1db
Show file tree
Hide file tree
Showing 10 changed files with 457 additions and 22 deletions.
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/terraform
/.github
/.vscode
31 changes: 31 additions & 0 deletions .github/workflows/testing.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Testing
on:
pull_request:
branches: [main]
push:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.18

- name: Vendor deps
run: go mod vendor

- name: Run tests
run: docker compose up --abort-on-container-exit

- name: Report coverage to Codacy
uses: codacy/codacy-coverage-reporter-action@v1
with:
api-token: ${{ secrets.CODACY_TOKEN }}
coverage-reports: coverage.txt
force-coverage-parser: go
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/.idea/*
/.vscode/*
coverage.txt
20 changes: 20 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version: "3.9" # optional since v1.27.0
services:
integration:
image: golang:1.18
command: bash -c "cd /workspace && go install gotest.tools/gotestsum@latest && gotestsum -- -coverprofile=coverage.txt -timeout=10s -v -count=1 -coverpkg=./... -covermode=atomic ./..."
volumes:
- type: bind
source: .
target: /workspace
environment:
- DATABASE_CONNECTION_STRING=postgresql://postgres:mysecretpassword@postgres/development?connect_timeout=10
depends_on:
- postgres
links:
- postgres
postgres:
image: postgres
environment:
- POSTGRES_DB=development
- POSTGRES_PASSWORD=mysecretpassword
21 changes: 16 additions & 5 deletions example/database/products.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,25 @@ import (
"github.com/Kansuler/octobe"
)

// Database interface that implements the databases, with this you can mock Database.
type Database interface {
InsertProduct(*Product) octobe.Handler
UpdateProduct(*Product) octobe.Handler
ProductByID(*Product) octobe.Handler
Products(*[]Product) octobe.Handler
}

// Handler implements the database interface
type Handler struct{}

// Product is an example database model
type Product struct {
ID string `json:"id"`
ID int64 `json:"id"`
Name string `json:"name"`
}

// InsertProduct will take a pointer of a product, and insert it
func InsertProduct(p *Product) octobe.Handler {
func (Handler) InsertProduct(p *Product) octobe.Handler {
return func(scheme *octobe.Scheme) error {
seg := scheme.Segment(`
INSERT INTO
Expand All @@ -29,7 +40,7 @@ func InsertProduct(p *Product) octobe.Handler {
}

// UpdateProduct will take a pointer and update the fields
func UpdateProduct(p *Product) octobe.Handler {
func (Handler) UpdateProduct(p *Product) octobe.Handler {
return func(scheme *octobe.Scheme) error {
seg := scheme.Segment(`
UPDATE
Expand All @@ -48,7 +59,7 @@ func UpdateProduct(p *Product) octobe.Handler {
}

// ProductByID will take an id, and a pointer to scan into
func ProductByID(id string, p *Product) octobe.Handler {
func (Handler) ProductByID(id int64, p *Product) octobe.Handler {
return func(scheme *octobe.Scheme) error {
seg := scheme.Segment(`
SELECT
Expand All @@ -67,7 +78,7 @@ func ProductByID(id string, p *Product) octobe.Handler {
}

// Products will take a pointer to append Products into
func Products(result *[]Product) octobe.Handler {
func (Handler) Products(result *[]Product) octobe.Handler {
return func(scheme *octobe.Scheme) (err error) {
seg := scheme.Segment(`
SELECT
Expand Down
185 changes: 185 additions & 0 deletions example/integration/integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package integration

import (
"context"
"database/sql"
"github.com/Kansuler/octobe"
"github.com/Kansuler/octobe/example/database"
"github.com/stretchr/testify/suite"
"os"
"testing"

// This import only relates to database connection
_ "github.com/jackc/pgx/v4/stdlib"
)

// Open will start a database connection and ping to confirm that it works
func open(con string) (db *sql.DB, err error) {
// The `sql.Open` function opens a new `*sql.DB` instance. We specify the driver name
// and the URI for our database. Here, we're using a Postgres URI
db, err = sql.Open("pgx", con)
if err != nil {
return
}

// To verify the connection to our database instance, we can call the `Ping`
// method. If no error is returned, we can assume a successful connection
err = db.Ping()
return
}

type PostgresTestSuite struct {
suite.Suite
db *sql.DB
}

func (suite *PostgresTestSuite) SetupSuite() {
var err error
suite.db, err = open(os.Getenv("DATABASE_CONNECTION_STRING"))
if err != nil {
suite.T().Fatalf("Failed to open database: %s", err)
}

ob := octobe.New(suite.db)
scheme := ob.Begin(context.Background())
seg := scheme.Segment(`
CREATE TABLE IF NOT EXISTS products (
id serial PRIMARY KEY,
name varchar(255) NOT NULL
);
`)

_, err = seg.Exec()
if err != nil {
suite.T().Fatalf("Failed to create table: %s", err)
}
}

func (suite *PostgresTestSuite) Test() {
suite.T().Run("TestBeginInsert", func(t *testing.T) {
h := database.Handler{}
ob := octobe.New(suite.db)
scheme := ob.Begin(context.Background())
p1 := database.Product{
Name: "Product 1",
}
err := scheme.Handle(h.InsertProduct(&p1))
suite.NoError(err)
suite.NotEqual(0, p1.ID)
suite.Equal("Product 1", p1.Name)

p2 := database.Product{
Name: "Product 2",
}
err = scheme.Handle(h.InsertProduct(&p2))
suite.NoError(err)
suite.NotEqual(0, p2.ID)
suite.Equal("Product 2", p2.Name)
})

suite.T().Run("TestBeginSelect", func(t *testing.T) {
h := database.Handler{}
ob := octobe.New(suite.db)
scheme := ob.Begin(context.Background())

p1 := database.Product{
Name: "Product To Be Selected",
}
err := scheme.Handle(h.InsertProduct(&p1))
suite.NoError(err)
suite.NotEqual(0, p1.ID)
suite.Equal("Product To Be Selected", p1.Name)

var p2 database.Product
err = scheme.Handle(h.ProductByID(p1.ID, &p2))
suite.NoError(err)
suite.Equal(p1.ID, p2.ID)
suite.Equal(p1.Name, p2.Name)
})

suite.T().Run("TestBeginUpdate", func(t *testing.T) {
h := database.Handler{}
ob := octobe.New(suite.db)
scheme := ob.Begin(context.Background())

p1 := database.Product{
Name: "Product Before Update",
}

err := scheme.Handle(h.InsertProduct(&p1))
suite.NoError(err)
suite.NotEqual(0, p1.ID)
suite.Equal("Product Before Update", p1.Name)

p1.Name = "Product After Update"

err = scheme.Handle(h.UpdateProduct(&p1))
suite.NoError(err)
suite.NotEqual(0, p1.ID)
suite.Equal("Product After Update", p1.Name)

var p2 database.Product
err = scheme.Handle(h.ProductByID(p1.ID, &p2))
suite.NoError(err)
suite.Equal(p1.ID, p2.ID)
suite.Equal(p1.Name, p2.Name)
})

suite.T().Run("TestBeginTxInsert", func(t *testing.T) {
h := database.Handler{}
ob := octobe.New(suite.db)
scheme, err := ob.BeginTx(context.Background())
if err != nil {
suite.T().Fatalf("Failed to begin transaction: %s", err)
}

p1 := database.Product{
Name: "Product Before Update",
}

err = scheme.Handle(h.InsertProduct(&p1))
suite.NoError(err)
suite.NotEqual(0, p1.ID)
suite.Equal("Product Before Update", p1.Name)

err = scheme.Commit()
if err != nil {
suite.T().Fatalf("Failed to commit transaction: %s", err)
}

scheme = ob.Begin(context.Background())
var p2 database.Product
err = scheme.Handle(h.ProductByID(p1.ID, &p2))
suite.NoError(err)
suite.Equal(p1.ID, p2.ID)
suite.Equal(p1.Name, p2.Name)
})

suite.T().Run("TestWatchTransaction", func(t *testing.T) {
h := database.Handler{}
ob := octobe.New(suite.db)
err := ob.WatchTransaction(context.Background(), func(scheme *octobe.Scheme) error {
p1 := database.Product{
Name: "Product Before Update",
}

err := scheme.Handle(h.InsertProduct(&p1))
suite.NoError(err)
suite.NotEqual(0, p1.ID)
suite.Equal("Product Before Update", p1.Name)

var p2 database.Product
err = scheme.Handle(h.ProductByID(p1.ID, &p2))
suite.NoError(err)
suite.Equal(p1.ID, p2.ID)
suite.Equal(p1.Name, p2.Name)

return nil
})
suite.NoError(err)
})
}

func TestPostgres(t *testing.T) {
suite.Run(t, new(PostgresTestSuite))
}
12 changes: 8 additions & 4 deletions example/logic.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ func RunTx(ob *octobe.Octobe) error {
Name: "Foo product",
}

err = scheme.Handle(database.InsertProduct(&product))
h := database.Handler{}
err = scheme.Handle(h.InsertProduct(&product))
if err != nil {
return err
}
Expand All @@ -41,7 +42,8 @@ func Run(ob *octobe.Octobe) error {
scheme := ob.Begin(ctx)

var p database.Product
err := scheme.Handle(database.ProductByID("123", &p))
h := database.Handler{}
err := scheme.Handle(h.ProductByID(1, &p))
if err != nil {
return err
}
Expand All @@ -56,7 +58,8 @@ func RunSupress(ob *octobe.Octobe) error {
scheme := ob.Begin(ctx)

var p database.Product
err := scheme.Handle(database.ProductByID("123", &p), octobe.SuppressError(sql.ErrNoRows))
h := database.Handler{}
err := scheme.Handle(h.ProductByID(1, &p), octobe.SuppressError(sql.ErrNoRows))
if err != nil {
return err
}
Expand All @@ -69,9 +72,10 @@ func RunSupress(ob *octobe.Octobe) error {
func RunWatchTransaction(ob *octobe.Octobe) error {
ctx := context.Background()

h := database.Handler{}
// WatchTransaction will start a transaction against database, if func returns
// error it will perform a rollback of transaction. If err is nil it will do commit
return ob.WatchTransaction(ctx, func(scheme *octobe.Scheme) error {
return scheme.Handle(database.InsertProduct(&database.Product{Name: "test"}))
return scheme.Handle(h.InsertProduct(&database.Product{Name: "test"}))
})
}
10 changes: 5 additions & 5 deletions example/logic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func TestRun(t *testing.T) {

defer db.Close()

mock.ExpectQuery("SELECT id, name FROM products").WithArgs("123").WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow("123", "test product"))
mock.ExpectQuery("SELECT id, name FROM products").WithArgs(1).WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow("123", "test product"))

ob := octobe.New(db)
err = example.Run(&ob)
Expand All @@ -34,7 +34,7 @@ func TestRunFail(t *testing.T) {

defer db.Close()

mock.ExpectQuery("SELECT id, name FROM products").WithArgs("123").WillReturnError(errors.New("an error occurred"))
mock.ExpectQuery("SELECT id, name FROM products").WithArgs(1).WillReturnError(errors.New("an error occurred"))

ob := octobe.New(db)
err = example.Run(&ob)
Expand All @@ -49,7 +49,7 @@ func TestRunFailSupress(t *testing.T) {

defer db.Close()

mock.ExpectQuery("SELECT id, name FROM products").WithArgs("123").WillReturnError(sql.ErrNoRows)
mock.ExpectQuery("SELECT id, name FROM products").WithArgs(1).WillReturnError(sql.ErrNoRows)

ob := octobe.New(db)
err = example.RunSupress(&ob)
Expand All @@ -65,7 +65,7 @@ func TestRunTx(t *testing.T) {
defer db.Close()

mock.ExpectBegin()
mock.ExpectQuery("INSERT INTO").WithArgs("Foo product").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("123"))
mock.ExpectQuery("INSERT INTO").WithArgs("Foo product").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1))
mock.ExpectCommit()

ob := octobe.New(db)
Expand Down Expand Up @@ -99,7 +99,7 @@ func TestRunWatchTransaction(t *testing.T) {
defer db.Close()

mock.ExpectBegin()
mock.ExpectQuery("INSERT INTO").WithArgs("test").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("123"))
mock.ExpectQuery("INSERT INTO").WithArgs("test").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1))
mock.ExpectCommit()

ob := octobe.New(db)
Expand Down
Loading

0 comments on commit a77c1db

Please sign in to comment.