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

feat: support for generating testcontainers-go tests for DB drivers #271

Merged
merged 22 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e6cf794
chore: add testcontainers advanced feature flag
mdelapenya Jul 3, 2024
baa7d6a
docs: add new advanced feature to the docs
mdelapenya Jul 3, 2024
3c8757d
chore: add testcontainers to the advanced steps
mdelapenya Jul 3, 2024
72a9781
chore: bump go version to v1.22.4
mdelapenya Jul 3, 2024
09e227a
feat: generate testscontainers tests for the database layer
mdelapenya Jul 3, 2024
3d8da48
docs: document testcontainers tests
mdelapenya Jul 3, 2024
c091327
chore: add CI workflow running the generated tests for each DB driver
mdelapenya Jul 4, 2024
b43cf4b
fix: pass a framework in order to actually build the app
mdelapenya Jul 4, 2024
a7e3339
chore: run integration tests, only
mdelapenya Jul 4, 2024
3d6ce43
fix: remove none driver from the list
mdelapenya Jul 4, 2024
08239b2
chore: run tests from makefile
mdelapenya Jul 4, 2024
668f105
fix: do not generate tests for sqlite
mdelapenya Jul 4, 2024
5154dea
Merge branch 'main' into testcontainers-integration-testing
mdelapenya Jul 15, 2024
c4ea4b3
fix: update module APIs
mdelapenya Jul 16, 2024
abc8447
chore: move integration tests to the core
mdelapenya Jul 16, 2024
7f447a7
chore: do not add "make itest" goal to sqlite
mdelapenya Jul 16, 2024
83ac79f
fix: remove entry from docs
mdelapenya Jul 16, 2024
d764ba0
chore: use a generic name for integration tests
mdelapenya Jul 16, 2024
e23f599
chore: remove unused variable
mdelapenya Jul 16, 2024
ca8b484
fix: include git flag in CI
mdelapenya Jul 16, 2024
f70c16f
chore: use x version for Go 1.22
mdelapenya Jul 23, 2024
bc23877
Delete docs/docs/advanced-flag/testcontainers.md
Ujstor Jul 24, 2024
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
34 changes: 34 additions & 0 deletions .github/workflows/testcontainers.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Integrations Test for the Generated Blueprints

on:
pull_request: {}
workflow_dispatch: {}

jobs:
itests_matrix:
strategy:
matrix:
driver:
[mysql, postgres, mongo, redis]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.22.2'
Ujstor marked this conversation as resolved.
Show resolved Hide resolved
mdelapenya marked this conversation as resolved.
Show resolved Hide resolved

- name: Commit report
run: |
git config --global user.name 'testname'
git config --global user.email 'testemail@users.noreply.github.com'

- name: build ${{ matrix.driver }} template
run: script -q /dev/null -c "go run main.go create -n ${{ matrix.driver }} -g commit -f fiber -d ${{matrix.driver}}" /dev/null

- name: run ${{ matrix.driver }} integration tests
working-directory: ${{ matrix.driver }}
run: make itest

- name: remove ${{ matrix.driver }} template
run: rm -rf ${{ matrix.driver }}
13 changes: 13 additions & 0 deletions cmd/program/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ type Templater interface {
type DBDriverTemplater interface {
Service() []byte
Env() []byte
Tests() []byte
}

type DockerTemplater interface {
Expand Down Expand Up @@ -287,6 +288,15 @@ func (p *Project) CreateMainFile() error {
cobra.CheckErr(err)
return err
}

if p.DBDriver != "sqlite" {
err = p.CreateFileWithInjection(internalDatabasePath, projectPath, "database_test.go", "integration-tests")
if err != nil {
log.Printf("Error injecting database_test.go file: %v", err)
cobra.CheckErr(err)
return err
}
}
}

// Create correct docker compose for the selected driver
Expand Down Expand Up @@ -708,6 +718,9 @@ func (p *Project) CreateFileWithInjection(pathToCreate string, projectPath strin
case "db-docker":
createdTemplate := template.Must(template.New(fileName).Parse(string(p.DockerMap[p.Docker].templater.Docker())))
err = createdTemplate.Execute(createdFile, p)
case "integration-tests":
createdTemplate := template.Must(template.New(fileName).Parse(string(p.DBDriverMap[p.DBDriver].templater.Tests())))
err = createdTemplate.Execute(createdFile, p)
case "tests":
createdTemplate := template.Must(template.New(fileName).Parse(string(p.FrameworkMap[p.ProjectType].templater.TestHandler())))
err = createdTemplate.Execute(createdFile, p)
Expand Down
61 changes: 61 additions & 0 deletions cmd/template/dbdriver/files/tests/mongo.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package database

import (
"context"
"log"
"testing"

"github.com/testcontainers/testcontainers-go/modules/mongodb"
)

func mustStartMongoContainer() (func(context.Context) error, error) {
dbContainer, err := mongodb.Run(context.Background(), "mongo:latest")
if err != nil {
return nil, err
}

dbHost, err := dbContainer.Host(context.Background())
if err != nil {
return dbContainer.Terminate, err
}

dbPort, err := dbContainer.MappedPort(context.Background(), "27017/tcp")
if err != nil {
return dbContainer.Terminate, err
}

host = dbHost
port = dbPort.Port()

return dbContainer.Terminate, err
}

func TestMain(m *testing.M) {
teardown, err := mustStartMongoContainer()
if err != nil {
log.Fatalf("could not start postgres container: %v", err)
}

m.Run()

if teardown != nil && teardown(context.Background()) != nil {
log.Fatalf("could not teardown postgres container: %v", err)
}
}

func TestNew(t *testing.T) {
srv := New()
if srv == nil {
t.Fatal("New() returned nil")
}
}

func TestHealth(t *testing.T) {
srv := New()

stats := srv.Health()

if stats["message"] != "It's healthy" {
t.Fatalf("expected message to be 'It's healthy', got %s", stats["message"])
}
}
96 changes: 96 additions & 0 deletions cmd/template/dbdriver/files/tests/mysql.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package database

import (
"context"
"log"
"testing"
"time"

"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/mysql"
"github.com/testcontainers/testcontainers-go/wait"
)

func mustStartMySQLContainer() (func(context.Context) error, error) {
var (
dbName = "database"
dbPwd = "password"
dbUser = "user"
)

dbContainer, err := mysql.Run(context.Background(),
"mysql:8.0.36",
mysql.WithDatabase(dbName),
mysql.WithUsername(dbUser),
mysql.WithPassword(dbPwd),
testcontainers.WithWaitStrategy(wait.ForLog("port: 3306 MySQL Community Server - GPL").WithStartupTimeout(30*time.Second)),
)
if err != nil {
return nil, err
}

dbname = dbName
password = dbPwd
username = dbUser

dbHost, err := dbContainer.Host(context.Background())
if err != nil {
return dbContainer.Terminate, err
}

dbPort, err := dbContainer.MappedPort(context.Background(), "3306/tcp")
if err != nil {
return dbContainer.Terminate, err
}

host = dbHost
port = dbPort.Port()

return dbContainer.Terminate, err
}

func TestMain(m *testing.M) {
teardown, err := mustStartMySQLContainer()
if err != nil {
log.Fatalf("could not start mysql container: %v", err)
}

m.Run()

if teardown != nil && teardown(context.Background()) != nil {
log.Fatalf("could not teardown mysql container: %v", err)
}
}

func TestNew(t *testing.T) {
srv := New()
if srv == nil {
t.Fatal("New() returned nil")
}
}

func TestHealth(t *testing.T) {
srv := New()

stats := srv.Health()

if stats["status"] != "up" {
t.Fatalf("expected status to be up, got %s", stats["status"])
}

if _, ok := stats["error"]; ok {
t.Fatalf("expected error not to be present")
}

if stats["message"] != "It's healthy" {
t.Fatalf("expected message to be 'It's healthy', got %s", stats["message"])
}
}

func TestClose(t *testing.T) {
srv := New()

if srv.Close() != nil {
t.Fatalf("expected Close() to return nil")
}
}
100 changes: 100 additions & 0 deletions cmd/template/dbdriver/files/tests/postgres.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package database

import (
"context"
"log"
"testing"
"time"

"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/postgres"
"github.com/testcontainers/testcontainers-go/wait"
)

func mustStartPostgresContainer() (func(context.Context) error, error) {
var (
dbName = "database"
dbPwd = "password"
dbUser = "user"
)

dbContainer, err := postgres.Run(
context.Background(),
"postgres:latest",
postgres.WithDatabase(dbName),
postgres.WithUsername(dbUser),
postgres.WithPassword(dbPwd),
testcontainers.WithWaitStrategy(
wait.ForLog("database system is ready to accept connections").
WithOccurrence(2).
WithStartupTimeout(5*time.Second)),
)
if err != nil {
return nil, err
}

database = dbName
password = dbPwd
username = dbUser

dbHost, err := dbContainer.Host(context.Background())
if err != nil {
return dbContainer.Terminate, err
}

dbPort, err := dbContainer.MappedPort(context.Background(), "5432/tcp")
if err != nil {
return dbContainer.Terminate, err
}

host = dbHost
port = dbPort.Port()

return dbContainer.Terminate, err
}

func TestMain(m *testing.M) {
teardown, err := mustStartPostgresContainer()
if err != nil {
log.Fatalf("could not start postgres container: %v", err)
}

m.Run()

if teardown != nil && teardown(context.Background()) != nil {
log.Fatalf("could not teardown postgres container: %v", err)
}
}

func TestNew(t *testing.T) {
srv := New()
if srv == nil {
t.Fatal("New() returned nil")
}
}

func TestHealth(t *testing.T) {
srv := New()

stats := srv.Health()

if stats["status"] != "up" {
t.Fatalf("expected status to be up, got %s", stats["status"])
}

if _, ok := stats["error"]; ok {
t.Fatalf("expected error not to be present")
}

if stats["message"] != "It's healthy" {
t.Fatalf("expected message to be 'It's healthy', got %s", stats["message"])
}
}

func TestClose(t *testing.T) {
srv := New()

if srv.Close() != nil {
t.Fatalf("expected Close() to return nil")
}
}
Loading