How can I use TOML fixtures to seed my database in a development environment using the Go Buffalo framework?

159 views Asked by At

I am trying to seed my development database in the Go Buffalo framework using TOML fixtures. However, I am having trouble finding a clear example or documentation on how to do this.

2

There are 2 answers

1
saurori On BEST ANSWER

To seed a database, you can use grifts. When creating a new application, there should be a dummy placeholder grift generated at grifts/db.go that looks like:

package grifts

import "github.com/gobuffalo/grift/grift"

var _ = grift.Namespace("db", func() {

    grift.Desc("seed", "Seeds a database")
    grift.Add("seed", func(c *grift.Context) error {
        // Add DB seeding stuff here
        return nil
    })

})

I attempted to use model.LoadFixture("seed widgets") in a grift, but there is some panic because it is not in a test environment. I think DB seeding from toml fixtures is not supported directly but that would be a useful feature. However looking at the code from LoadFixture, we can construct our own fixture loading:

Assuming you have a fixture with a scenario named name = "seed widgets":

package grifts

import (
    "fmt"
    "os"
    "strings"

    "github.com/gobuffalo/grift/grift"
    "github.com/gobuffalo/suite/v4"
    "github.com/gobuffalo/suite/v4/fix"
)

var _ = grift.Namespace("db", func() {

    grift.Desc("seed", "Seeds a database")
    grift.Add("seed", func(c *grift.Context) error {
        // The DB connection will connect to the environment set in `GO_ENV` (defaults to `test`)
        // Set this environment variable in your `.env` file to `development`

        // NOTE: it may be better to put seed fixtures in a different directory
        //       to seperate concerns
        model, err := suite.NewModelWithFixtures(os.DirFS("./fixtures"))
        if err != nil {
            return err
        }

        sc, err := fix.Find("seed widgets")
        if err != nil {
            return err
        }

        for _, table := range sc.Tables {
            for _, row := range table.Row {
                q := "insert into " + table.Name
                keys := []string{}
                skeys := []string{}
                for k := range row {
                    keys = append(keys, k)
                    skeys = append(skeys, ":"+k)
                }

                q = q + fmt.Sprintf(" (%s) values (%s)", strings.Join(keys, ","), strings.Join(skeys, ","))
                if _, err = model.DB.Store.NamedExec(q, row); err != nil {
                    return err
                }
            }
        }

        return nil
    })

})
3
saurori On

You can find all the information you need about testing and fixtures here: https://github.com/gobuffalo/suite

Here is a basic example of actions and model tests:

actions/actions_test.go

package actions

import (
    "os"
    "testing"

    "github.com/gobuffalo/suite/v4"
)

type ActionSuite struct {
    *suite.Action
}

func Test_ActionSuite(t *testing.T) {
    action, err := suite.NewActionWithFixtures(App(), os.DirFS("../fixtures"))
    if err != nil {
        t.Fatal(err)
    }

    as := &ActionSuite{
        Action: action,
    }
    suite.Run(t, as)
}

models/models_test.go

package models

import (
    "os"
    "testing"

    "github.com/gobuffalo/suite/v4"
)

type ModelSuite struct {
    *suite.Model
}

func Test_ModelSuite(t *testing.T) {
    model, err := suite.NewModelWithFixtures(os.DirFS("../fixtures"))
    if err != nil {
        t.Fatal(err)
    }

    as := &ModelSuite{
        Model:  model,
    }
    suite.Run(t, as)
}

fixtures/widgets.toml

[[scenario]]
name = "lots of widgets"

  [[scenario.table]]
    name = "widgets"

    [[scenario.table.row]]
      id = "<%= uuidNamed("widget") %>"
      name = "This is widget #1"
      body = "some widget body"
      created_at = "<%= now() %>"
      updated_at = "<%= now() %>"

    [[scenario.table.row]]
      id = "<%= uuid() %>"
      name = "This is widget #2"
      body = "some widget body"
      created_at = "<%= now() %>"
      updated_at = "<%= now() %>"

  [[scenario.table]]
    name = "users"

    [[scenario.table.row]]
      id = "<%= uuid() %>"
      name = "Mark Bates"
      admin = true
      price = 19.99
      widget_id = "<%= uuidNamed("widget") %>"
      created_at = "<%= now() %>"
      updated_at = "<%= now() %>"

actions/widgets_test.go

func (as *ActionSuite) Test_WidgetsResource_List() {
    as.LoadFixture("lots of widgets")
    res := as.HTML("/widgets").Get()

    body := res.Body.String()
    as.Contains(body, "widget #1")
    as.Contains(body, "widget #2")
}

models/widget_test.go

func (ms *ModelSuite) Test_Widget_SomeModelMethod() {
    ms.LoadFixture("lots of widgets")

    var widgets []Widget
    if err := DB.All(&widgets); err != nil {
        ms.Fail(err.Error())
    }

    for _, widget := range widgets {
        ms.Equal("some value", widget.SomeModelMethod())
    }
}

database.yml

test:
  url: {{envOr "TEST_DATABASE_URL" "postgres://[email protected]:5432/medicare_65_quote_test?sslmode=disable"}}