Skip to content

Commit

Permalink
Implement Cassandra preset
Browse files Browse the repository at this point in the history
This preset doesn't support setting a custom username and password since
I couldn't make it work with bitnami/cassandra docker image, which
appears to always use `cassandra`/`cassandra` credentials.
  • Loading branch information
orlangure committed Aug 20, 2021
1 parent 6d166e7 commit 76a100e
Show file tree
Hide file tree
Showing 14 changed files with 366 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,17 @@ jobs:
name: Test server
command: go test -race -cover -v ./internal/gnomockd -run TestInfluxDB

test-cassandra:
machine: true
steps:
- setup-for-go-test
- run:
name: Test preset
command: go test -race -cover -v ./preset/cassandra/...
- run:
name: Test server
command: go test -race -cover -v ./internal/gnomockd -run TestCassandra

### preset tests go here

workflows:
Expand All @@ -260,4 +271,5 @@ workflows:
- test-k3s
- test-cockroachdb
- test-influxdb
- test-cassandra
### circleci jobs go here
23 changes: 23 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -436,4 +436,27 @@ jobs:
cat preset-cover.txt server-cover.txt > coverage.txt
bash <(curl -s https://codecov.io/bash)
test-cassandra:
name: "[preset] cassandra"
runs-on: ubuntu-latest
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
steps:
- name: Set up Go 1.16
uses: actions/setup-go@v1
with:
go-version: 1.16
- name: Check out code into the Go module directory
uses: actions/checkout@v1
- name: Get dependencies
run: go get -v -t -d ./...
- name: Test preset
run: go test -race -cover -coverprofile=preset-cover.txt -coverpkg=./... -v ./preset/cassandra/...
- name: Test server
run: go test -race -cover -coverprofile=server-cover.txt -coverpkg=./... -v ./internal/gnomockd -run TestCassandra
- name: Report coverage
run: |
cat preset-cover.txt server-cover.txt > coverage.txt
bash <(curl -s https://codecov.io/bash)
### preset tests go here
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ Elasticsearch | [Go package](https://github.com/orlangure/gnomock/tree/master/pr
Kubernetes | [Go package](https://github.com/orlangure/gnomock/tree/master/preset/k3s) | [Docs](https://app.swaggerhub.com/apis/orlangure/gnomock/1.5.0#/presets/startKubernetes) | [Reference](https://pkg.go.dev/github.com/orlangure/gnomock/preset/k3s?tab=doc) | `v1.19.12`
CockroachDB | [Go package](https://github.com/orlangure/gnomock/tree/master/preset/cockroachdb) | [Docs](https://app.swaggerhub.com/apis/orlangure/gnomock/1.5.0#/presets/startCockroachDB) | [Reference](https://pkg.go.dev/github.com/orlangure/gnomock/preset/cockroachdb?tab=doc) | `v19.2.11`, `v20.1.10`
InfluxDB | [Go package](https://github.com/orlangure/gnomock/tree/master/preset/influxdb) | [Docs](https://app.swaggerhub.com/apis/orlangure/gnomock/1.5.0#/presets/startInfluxDB) | [Reference](https://pkg.go.dev/github.com/orlangure/gnomock/preset/influxdb?tab=doc) | `2.0.4-alpine`
Cassandra | [Go package](https://github.com/orlangure/gnomock/tree/master/preset/cassandra) | [Docs](https://app.swaggerhub.com/apis/orlangure/gnomock/1.4.6#/presets/startCassandra) | [Reference](https://pkg.go.dev/github.com/orlangure/gnomock/preset/cassandra?tab=doc) | `4.0`, `3`
<!-- new presets go here -->

It is possible to use Gnomock [directly from
Expand Down
1 change: 1 addition & 0 deletions cmd/server/presets.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
// all known presets should go right here so that they are available when
// requested over HTTP:
import (
_ "github.com/orlangure/gnomock/preset/cassandra"
_ "github.com/orlangure/gnomock/preset/cockroachdb"
_ "github.com/orlangure/gnomock/preset/elastic"
_ "github.com/orlangure/gnomock/preset/influxdb"
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/go-redis/redis v6.15.9+incompatible
github.com/go-redis/redis/v7 v7.4.1
github.com/go-sql-driver/mysql v1.6.0
github.com/gocql/gocql v0.0.0-20210707082121-9a3953d1826d
github.com/golang/snappy v0.0.4 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.3.0
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,13 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
Expand Down Expand Up @@ -345,6 +348,8 @@ github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWe
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
github.com/gocql/gocql v0.0.0-20210707082121-9a3953d1826d h1:k544nNVphXK4Yt0FTduvOvCfJabEY/DMkdNw0zpCwBE=
github.com/gocql/gocql v0.0.0-20210707082121-9a3953d1826d/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8=
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
Expand Down Expand Up @@ -452,6 +457,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
Expand Down
52 changes: 52 additions & 0 deletions internal/gnomockd/cassandra_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package gnomockd_test

import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"

"github.com/orlangure/gnomock"
"github.com/orlangure/gnomock/internal/gnomockd"
_ "github.com/orlangure/gnomock/preset/cassandra"
"github.com/stretchr/testify/require"
)

func TestCassandra(t *testing.T) {
t.Parallel()

h := gnomockd.Handler()
bs, err := ioutil.ReadFile("./testdata/cassandra.json")
require.NoError(t, err)

buf := bytes.NewBuffer(bs)
w, r := httptest.NewRecorder(), httptest.NewRequest(http.MethodPost, "/start/cassandra", buf)
h.ServeHTTP(w, r)

res := w.Result()

defer func() { require.NoError(t, res.Body.Close()) }()

body, err := ioutil.ReadAll(res.Body)
require.NoError(t, err)

require.Equalf(t, http.StatusOK, res.StatusCode, string(body))

var c *gnomock.Container

err = json.Unmarshal(body, &c)
require.NoError(t, err)
require.NotEmpty(t, c.DefaultAddress())

bs, err = json.Marshal(c)
require.NoError(t, err)

buf = bytes.NewBuffer(bs)
w, r = httptest.NewRecorder(), httptest.NewRequest(http.MethodPost, "/stop", buf)
h.ServeHTTP(w, r)

res = w.Result()
require.Equal(t, http.StatusOK, res.StatusCode)
}
1 change: 1 addition & 0 deletions internal/gnomockd/testdata/cassandra.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"options":{},"preset":{"version":"latest"}}
50 changes: 50 additions & 0 deletions preset/cassandra/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Gnomock Cassandra

Gnomock Cassandra is a [Gnomock](https://github.com/orlangure/gnomock) preset for
running tests against a real Cassandra container, without mocks.

```go
package cassandra_test

func TestPreset(t *testing.T) {
t.Parallel()

for _, version := range []string{"4.0", "3"} {
t.Run(version, testPreset(version))
}
}

func testPreset(version string) func(t *testing.T) {
return func(t *testing.T) {
p := cassandra.Preset(
cassandra.WithVersion(version),
)
container, err := gnomock.Start(p)

defer func() { require.NoError(t, gnomock.Stop(container)) }()

require.NoError(t, err)

addr := container.DefaultAddress()
require.NotEmpty(t, addr)

cluster := gocql.NewCluster(addr)
cluster.Authenticator = gocql.PasswordAuthenticator{
Username: cassandra.DefaultUser,
Password: cassandra.DefaultPassword,
}

session, err := cluster.CreateSession()
require.NoError(t, err)

defer session.Close()

err = session.Query("create keyspace gnomock with replication = {'class':'SimpleStrategy', 'replication_factor' : 1};").Exec()
require.NoError(t, err)

err = session.Query("CREATE TABLE gnomock.test (id UUID, PRIMARY KEY (id));").Exec()
require.NoError(t, err)
}
}
```

12 changes: 12 additions & 0 deletions preset/cassandra/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cassandra

// Option is an optional configuration of this Gnomock preset. Use available
// Options to configure the container.
type Option func(*P)

// WithVersion sets image version.
func WithVersion(version string) Option {
return func(o *P) {
o.Version = version
}
}
94 changes: 94 additions & 0 deletions preset/cassandra/preset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Package cassandra includes Cassandra implementation of Gnomock Preset
// interface. This Preset can be passed to gnomock.Start() function to create a
// configured Cassandra container to use in tests.
//
// Cassandra containers always use cassandra/cassandra username/password pair,
// it is currently not possible to use different values.
package cassandra

import (
"context"
"fmt"

"github.com/gocql/gocql"
"github.com/orlangure/gnomock"
"github.com/orlangure/gnomock/internal/registry"
)

// By default, Cassandra containers will use these values.
const (
DefaultUser = "cassandra"
DefaultPassword = "cassandra"

defaultVersion = "3"
defaultPort = 9042
)

func init() {
registry.Register("cassandra", func() gnomock.Preset { return &P{} })
}

// Preset creates a new Gmomock Cassandra preset. This preset includes a
// Cassandra specific healthcheck function and default Cassandra image and
// port.
//
// Containers created using this preset should be accessed using
// cassandra/cassandra username/password pair.
func Preset(opts ...Option) gnomock.Preset {
p := &P{}

for _, opt := range opts {
opt(p)
}

return p
}

// P is a Gnomock Preset implementation for Cassandra.
type P struct {
Version string `json:"version"`
}

// Image returns an image that should be pulled to create this container.
func (p *P) Image() string {
return fmt.Sprintf("docker.io/bitnami/cassandra:%s", p.Version)
}

// Ports returns ports that should be used to access this container.
func (p *P) Ports() gnomock.NamedPorts {
return gnomock.DefaultTCP(defaultPort)
}

// Options returns a list of options to configure this container.
func (p *P) Options() []gnomock.Option {
p.setDefaults()

opts := []gnomock.Option{
gnomock.WithHealthCheck(p.healthcheck),
}

return opts
}

func (p *P) setDefaults() {
if p.Version == "" {
p.Version = defaultVersion
}
}

func (p *P) healthcheck(ctx context.Context, c *gnomock.Container) error {
cluster := gocql.NewCluster(c.DefaultAddress())
cluster.Authenticator = gocql.PasswordAuthenticator{
Username: DefaultUser,
Password: DefaultPassword,
}

session, err := cluster.CreateSession()
if err != nil {
return fmt.Errorf("failed to create a new session: %w", err)
}

session.Close()

return nil
}
54 changes: 54 additions & 0 deletions preset/cassandra/preset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package cassandra_test

import (
"testing"

"github.com/gocql/gocql"
"github.com/orlangure/gnomock"
"github.com/orlangure/gnomock/preset/cassandra"
"github.com/stretchr/testify/require"
)

func TestPreset(t *testing.T) {
t.Parallel()

for _, version := range []string{"4.0", "3"} {
t.Run(version, testPreset(version))
}
}

func testPreset(version string) func(t *testing.T) {
return func(t *testing.T) {
p := cassandra.Preset(
cassandra.WithVersion(version),
)
container, err := gnomock.Start(p)

defer func() { require.NoError(t, gnomock.Stop(container)) }()

require.NoError(t, err)

addr := container.DefaultAddress()
require.NotEmpty(t, addr)

cluster := gocql.NewCluster(addr)
cluster.Authenticator = gocql.PasswordAuthenticator{
Username: cassandra.DefaultUser,
Password: cassandra.DefaultPassword,
}

session, err := cluster.CreateSession()
require.NoError(t, err)

defer session.Close()

err = session.Query(`
create keyspace gnomock
with replication = {'class':'SimpleStrategy', 'replication_factor' : 1};
`).Exec()
require.NoError(t, err)

err = session.Query("CREATE TABLE gnomock.test (id UUID, PRIMARY KEY (id));").Exec()
require.NoError(t, err)
}
}
Loading

0 comments on commit 76a100e

Please sign in to comment.