Skip to content

Commit

Permalink
add more tests and another way to register for custom seed name, also…
Browse files Browse the repository at this point in the history
… allow programmatic access to seed execution entry
  • Loading branch information
kristijorgji committed Oct 24, 2020
1 parent d694992 commit 0e85906
Show file tree
Hide file tree
Showing 9 changed files with 292 additions and 27 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ To run the test seeder above you have to run:
go run main.go --gseed --gsenv=test
```

This will run only the tests registered for the env `test`, all other seeds will get ignored (also those without environment known as `common` seeds))

### 4. Run Seeds By Name

When we register a seed like shown in step 2, the seed name is the same as the function name, so our seed is called `categoriesSeeder` because that is the name of the function we register below
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/kristijorgji/goseeder
go 1.15

require (
github.com/DATA-DOG/go-sqlmock v1.5.0
github.com/go-sql-driver/mysql v1.5.0
github.com/stretchr/testify v1.6.1
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
Expand Down
10 changes: 10 additions & 0 deletions goseeder_testdata/db/seeds/data/categories.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"id": "1",
"name": "{\"en\": \"Push\"}"
},
{
"id": "2",
"name": "{\"en\": \"Pull\"}"
}
]
65 changes: 65 additions & 0 deletions helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package goseeder

import (
"github.com/stretchr/testify/require"
"testing"
)

func TestFindString(t *testing.T) {
input := []string{
"dandy",
"trout",
"fish",
"more",
"fish",
"ok",
}

pos, found := findString(input, "fish")
require.Equal(t, 2, pos)
require.Equal(t, true, found)
}

func TestPrepareStatement(t *testing.T) {
table := "categories"
data := map[string]string{
"id": "100",
"name": "common",
}
sb, args := prepareStatement(table, data)

require.Equal(
t,
"insert into categories (id, name) values (?, ?)",
sb.String(),
)

require.Equal(
t,
[]interface{}{
int64(100),
"common"},
args,
)
}

var testCases = []struct {
name string
value string
expected interface{}
}{
{"bool", "true", true},
{"bool", "false", false},
{"int", "12", int64(12)},
{"float", "12.77", 12.770000457763672},
{"string", "justastring", "justastring"},
{"string_with_nr", "12justastring", "12justastring"},
}

func TestParseValue(t *testing.T) {
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.expected, parseValue(tt.value))
})
}
}
40 changes: 32 additions & 8 deletions seeder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package goseeder

import (
"database/sql"
"errors"
"flag"
"fmt"
"log"
"os"
"regexp"
"strings"
"time"
Expand All @@ -17,6 +17,29 @@ type Seeder struct {
context clientSeeder
}

//Registration this allows for custom registration with full options available at once, like specifying custom seed name and env in one go. Then have to finish registration by calling Complete
type Registration struct {
Name string
Env string
completed bool
}

//Complete this finished the registration of this registration instance. If you call a second time for same instance, error will be throw
func (r Registration) Complete(s func(seeder Seeder)) error {
if r.completed {
return errors.New("registration is already completed. You can use one registration only one time")
}

seeders = append(seeders, clientSeeder{
env: r.Env,
name: r.Name,
cb: s,
})
r.completed = true

return nil
}

type clientSeeder struct {
env string
name string
Expand All @@ -41,13 +64,12 @@ func WithSeeder(conProvider func() *sql.DB, clientMain func()) {
return
}

var seeders = make([]string, 0)
var specifiedSeeders = make([]string, 0)
if len(names) > 0 {
seeders = strings.Split(names, ",")
specifiedSeeders = strings.Split(names, ",")
}

execute(conProvider(), env, seeders...)
os.Exit(0)
Execute(conProvider(), env, specifiedSeeders)
}

// Register the given seed function as common to run for all environments
Expand All @@ -62,7 +84,7 @@ func RegisterForTest(seeder func(s Seeder)) {

// RegisterForEnv the given seed function for a specific environment
func RegisterForEnv(env string, seeder func(s Seeder)) {
r := regexp.MustCompile(`.*\.(?P<name>[a-zA-Z]+$)`)
r := regexp.MustCompile(`.*\.(?P<name>[a-zA-Z0-9]+$)`)
match := r.FindStringSubmatch(getFunctionName(seeder))

seeders = append(seeders, clientSeeder{
Expand All @@ -72,8 +94,10 @@ func RegisterForEnv(env string, seeder func(s Seeder)) {
})
}

// Execute will executes the given seeder method
func execute(db *sql.DB, env string, seedMethodNames ...string) {
// Execute use this method for using this lib programmatically and executing
// seeder directly with full flexibility. Be sure first to have registered your
// seeders
func Execute(db *sql.DB, env string, seedMethodNames []string) {
// Execute all seeders if no method name is given
if len(seedMethodNames) == 0 {
if env == "" {
Expand Down
170 changes: 152 additions & 18 deletions seeder_test.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,161 @@
package goseeder

import (
"database/sql"
"flag"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/require"
"os"
"testing"
)

var testCases = []struct {
name string
value string
expected interface{}
}{
{"bool", "true", true},
{"bool", "false", false},
{"int", "12", int64(12)},
{"float", "12.77", 12.770000457763672},
{"string", "justastring", "justastring"},
{"string_with_nr", "12justastring", "12justastring"},
}

func TestParseValue(t *testing.T) {
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.expected, parseValue(tt.value))
})
type mockStatus struct {
callCount int
callParams []interface{}
}

func newFnMock() (func(), *mockStatus) {
mockStatus := mockStatus{
callCount: 0,
callParams: []interface{}{},
}
fn := func() {
mockStatus.callCount++
}

return fn, &mockStatus
}

func newSeedFnMock() (func(s Seeder), *mockStatus) {
mockStatus := mockStatus{
callCount: 0,
callParams: []interface{}{},
}
fnSeeder := func(s Seeder) {
mockStatus.callCount++
}

return fnSeeder, &mockStatus
}

var conProvider = func() *sql.DB {
db, _, _ := sqlmock.New()
return db
}

func TestWithSeeder_not_seeding(t *testing.T) {
clientMain, mockStatus := newFnMock()
WithSeeder(conProvider, clientMain)
require.Equal(t, 1, mockStatus.callCount)

flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
}

func TestWithSeeder_seed_all(t *testing.T) {
clientMain, mockFnStatus := newFnMock()
mockSeedFn, mockSeedStatus := newSeedFnMock()

Registration{
Name: "test_seed",
Env: "",
}.Complete(mockSeedFn)

os.Args = []string{
"",
"--gseed",
}

WithSeeder(conProvider, clientMain)
require.Equal(t, 0, mockFnStatus.callCount)
require.Equal(t, 1, mockSeedStatus.callCount)

flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
}

func TestWithSeeder_seed_named(t *testing.T) {
clientMain, mockFnStatus := newFnMock()
mockSeedFn, mockSeedStatus := newSeedFnMock()
mockSeed2Fn, mockSeed2Status := newSeedFnMock()

Registration{
Name: "test_seed",
Env: "",
}.Complete(mockSeedFn)
Registration{
Name: "another_weird_seed",
Env: "",
}.Complete(mockSeed2Fn)

os.Args = []string{
"",
"--gseed",
"--gsnames=another_weird_seed",
}

WithSeeder(conProvider, clientMain)
require.Equal(t, 0, mockFnStatus.callCount)
require.Equal(t, 0, mockSeedStatus.callCount)
require.Equal(t, 1, mockSeed2Status.callCount)

flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
}

func TestWithSeeder_seed_for_env(t *testing.T) {
clientMain, mockFnStatus := newFnMock()
mockCommonSeedFn, mockCommonSeedStatus := newSeedFnMock()
mockSeedSecretFn, mockSeedSecretStatus := newSeedFnMock()
mockSeedStageFn, mockSeedStageStatus := newSeedFnMock()

Registration{
Name: "common_seed",
Env: "",
}.Complete(mockCommonSeedFn)
Registration{
Name: "secret_env_seed",
Env: "secret",
}.Complete(mockSeedSecretFn)
Registration{
Name: "stage_env_seed",
Env: "stage",
}.Complete(mockSeedStageFn)

os.Args = []string{
"",
"--gseed",
"--gsenv=secret",
}

WithSeeder(conProvider, clientMain)
require.Equal(t, 0, mockFnStatus.callCount)
require.Equal(t, 0, mockCommonSeedStatus.callCount)
require.Equal(t, 1, mockSeedSecretStatus.callCount)
require.Equal(t, 0, mockSeedStageStatus.callCount)

flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
}

func TestRegister(t *testing.T) {
Register(dummySeeder)
a := seeders[len(seeders)-1]
require.Equal(t, "", a.env)
require.Equal(t, "dummySeeder", a.name)
require.NotEmpty(t, a.cb)
}

func TestRegisterForTest(t *testing.T) {
RegisterForTest(dummySeeder)
a := seeders[len(seeders)-1]
require.Equal(t, "test", a.env)
require.Equal(t, "dummySeeder", a.name)
require.NotEmpty(t, a.cb)
}

func TestRegisterForEnv(t *testing.T) {
RegisterForEnv("mySuperEnv", dummySeeder)
a := seeders[len(seeders)-1]
require.Equal(t, "mySuperEnv", a.env)
require.Equal(t, "dummySeeder", a.name)
require.NotEmpty(t, a.cb)
}

func dummySeeder(s Seeder) {}
4 changes: 3 additions & 1 deletion sources.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import (
"log"
)

var dataPath = "db/seeds/data"

//FromJson inserts into a database table with name same as the filename all the json entries
func (s Seeder) FromJson(filename string) {
var folder = ""
if s.context.env != "" {
folder = fmt.Sprintf("%s/", s.context.env)
}
content, err := ioutil.ReadFile(fmt.Sprintf("db/seeds/data/%s%s.json", folder, filename))
content, err := ioutil.ReadFile(fmt.Sprintf("%s/%s%s.json", dataPath, folder, filename))
if err != nil {
log.Fatal(err)
}
Expand Down
Loading

0 comments on commit 0e85906

Please sign in to comment.