summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/go-testfixtures
diff options
context:
space:
mode:
authortechknowlogick <techknowlogick@gitea.io>2020-06-17 15:07:58 -0400
committerGitHub <noreply@github.com>2020-06-17 22:07:58 +0300
commit9e6a79bea9d7911c81b86a6d3715d340fc19032a (patch)
tree3c5982227a3ed7481786185f5fdf5a8c1107f279 /vendor/github.com/go-testfixtures
parent1645d4a5d8def3cc5451e068aa0a321e028a889b (diff)
downloadgitea-9e6a79bea9d7911c81b86a6d3715d340fc19032a.tar.gz
gitea-9e6a79bea9d7911c81b86a6d3715d340fc19032a.zip
upgrade to use testfixtures v3 (#11904)
* upgrade to use testfixtures v3 * simplify logic * make vendor * update per @lunny * Update templates/repo/empty.tmpl * Update templates/repo/empty.tmpl Co-authored-by: Lauris BH <lauris@nix.lv>
Diffstat (limited to 'vendor/github.com/go-testfixtures')
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/.editorconfig15
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/.gitattributes1
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/.gitignore29
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/.goreleaser.yml41
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/.sample.env4
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/CHANGELOG.md93
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/Dockerfile9
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/LICENSE21
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/README.md483
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/Taskfile.yml59
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/docker-compose.yml37
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/dump.go165
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/go.mod14
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/go.sum26
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/helper.go71
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/json.go44
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/mysql.go131
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/postgresql.go296
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/sqlite.go75
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/sqlserver.go153
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/testfixtures.go599
-rw-r--r--vendor/github.com/go-testfixtures/testfixtures/v3/time.go43
22 files changed, 2409 insertions, 0 deletions
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/.editorconfig b/vendor/github.com/go-testfixtures/testfixtures/v3/.editorconfig
new file mode 100644
index 0000000000..05f44c7500
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/.editorconfig
@@ -0,0 +1,15 @@
+# http://editorconfig.org/
+
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+charset = utf-8
+trim_trailing_whitespace = true
+indent_style = tab
+indent_size = 8
+
+[*.{yml,md}]
+indent_style = space
+indent_size = 2
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/.gitattributes b/vendor/github.com/go-testfixtures/testfixtures/v3/.gitattributes
new file mode 100644
index 0000000000..fcadb2cf97
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/.gitattributes
@@ -0,0 +1 @@
+* text eol=lf
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/.gitignore b/vendor/github.com/go-testfixtures/testfixtures/v3/.gitignore
new file mode 100644
index 0000000000..8772d9ba0c
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/.gitignore
@@ -0,0 +1,29 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
+
+*.sqlite3
+.env
+/testfixtures
+/dist
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/.goreleaser.yml b/vendor/github.com/go-testfixtures/testfixtures/v3/.goreleaser.yml
new file mode 100644
index 0000000000..cab247177d
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/.goreleaser.yml
@@ -0,0 +1,41 @@
+build:
+ binary: testfixtures
+ main: ./cmd/testfixtures
+ goos:
+ - windows
+ - darwin
+ - linux
+ goarch:
+ - 386
+ - amd64
+ ignore:
+ - goos: darwin
+ goarch: 386
+ flags:
+ - -tags=sqlite
+
+archives:
+ - name_template: "{{.Binary}}_{{.Os}}_{{.Arch}}"
+ format_overrides:
+ - goos: windows
+ format: zip
+
+release:
+ draft: true
+
+snapshot:
+ name_template: "{{.Tag}}"
+
+checksum:
+ name_template: "testfixtures_checksums.txt"
+
+nfpms:
+ - vendor: testfixtures
+ homepage: https://github.com/go-testfixtures/testfixtures
+ maintainer: Andrey Nering <andrey.nering@gmail.com>
+ description: Ruby on Rails like test fixtures for Go.
+ license: MIT
+ formats:
+ - deb
+ - rpm
+ file_name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/.sample.env b/vendor/github.com/go-testfixtures/testfixtures/v3/.sample.env
new file mode 100644
index 0000000000..fcc7cd0c74
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/.sample.env
@@ -0,0 +1,4 @@
+PG_CONN_STRING="user=postgres dbname=testfixtures_test sslmode=disable"
+MYSQL_CONN_STRING="root:@/testfixtures_test?multiStatements=true"
+SQLITE_CONN_STRING="testdb.sqlite3"
+SQLSERVER_CONN_STRING="server=localhost\SQLExpress;database=testfixtures_test;user id=sa;password=sqlserver;encrypt=disable"
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/CHANGELOG.md b/vendor/github.com/go-testfixtures/testfixtures/v3/CHANGELOG.md
new file mode 100644
index 0000000000..2b8aa4b70a
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/CHANGELOG.md
@@ -0,0 +1,93 @@
+# Changelog
+
+## v3.2.0 - 2020-05-10
+
+- Add support for loading multiple files and directories
+ ([#65](https://github.com/go-testfixtures/testfixtures/pull/65)).
+
+## v3.1.2 - 2020-04-26
+
+- Dump: Fix column order in generated YAML files
+ ([#62](https://github.com/go-testfixtures/testfixtures/pull/62)).
+
+## v3.1.1 - 2020-01-11
+
+- testfixtures now work with both `mssql` and `sqlserver` drivers.
+ Note that [the `mssql` one is deprecated](https://github.com/denisenkom/go-mssqldb#deprecated),
+ though. So try to migrate to `sqlserver` once possible.
+
+## v3.1.0 - 2020-01-09
+
+- Using `sqlserver` driver instead of the deprecated `mssql`
+ ([#58](https://github.com/go-testfixtures/testfixtures/pull/58)).
+
+## v3.0.0 - 2019-12-26
+
+### Breaking changes
+
+- The import path changed from `gopkg.in/testfixtures.v2` to
+ `github.com/go-testfixtures/testfixtures/v3`.
+- This package no longer support Oracle databases. This decision was
+ taken because too few people actually used this package with Oracle and it
+ was the most difficult to test (we didn't run on CI due the lack of an
+ official Docker image, etc).
+- The public API was totally rewritten to be more flexible and ideomatic.
+ It now uses functional options. It differs from v2, but should be easy
+ enough to upgrade.
+- Some deprecated APIs from v2 were removed as well.
+- This now requires Go >= 1.13.
+
+### New features
+
+- We now have a CLI so you can easily use testfixtures to load a sample
+ database from fixtures if you want.
+- Templating via [text/template](https://golang.org/pkg/text/template/)
+ is now available. This allows some fancier use cases like generating data
+ or specific columns dynamically.
+- It's now possible to choose which time zone to use when parsing timestamps
+ from fixtures. The default is the same as before, whatever is set on
+ `time.Local`.
+- Errors now use the new `%w` verb only available on Go >= 1.13.
+
+### MISC
+
+- Travis and AppVeyor are gone. We're using GitHub Actions exclusively now.
+ The whole suite is ran inside Docker (with help of Docker Compose), so it's
+ easy to run tests locally as well.
+
+Check the new README for some examples!
+
+## v2.6.0 - 2019-10-24
+
+- Add support for TimescaleDB
+ ([#53](https://github.com/go-testfixtures/testfixtures/pull/53)).
+
+## v2.5.3 - 2018-12-15
+
+- Fixes related to use of foreign key pragmas on MySQL (#43).
+
+## v2.5.2 - 2018-11-25
+
+- This library now supports [Go Modules](https://github.com/golang/go/wiki/Modules);
+- Also allow `.yaml` (as an alternative to `.yml`) as the file extension (#42).
+
+## v2.5.1 - 2018-11-04
+
+- Allowing disabling reset of PostgreSQL sequences (#38).
+
+## v2.5.0 - 2018-09-07
+
+- Add public function DetectTestDatabase (#35, #36).
+
+## v2.4.5 - 2018-07-07
+
+- Fix for MySQL/MariaDB: ignoring views on operations that should be run only on tables (#33).
+
+## v2.4.4 - 2018-07-02
+
+- Fix for multiple schemas on Microsoft SQL Server (#29 and #30);
+- Configuring AppVeyor CI to also test for Microsoft SQL Server.
+
+---
+
+Sorry, we don't have changelog for older releases 😢.
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/Dockerfile b/vendor/github.com/go-testfixtures/testfixtures/v3/Dockerfile
new file mode 100644
index 0000000000..e33dafb07b
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/Dockerfile
@@ -0,0 +1,9 @@
+FROM golang:1.14-alpine
+
+RUN apk update
+RUN apk add alpine-sdk
+
+WORKDIR /testfixtures
+COPY . .
+
+RUN go mod download
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/LICENSE b/vendor/github.com/go-testfixtures/testfixtures/v3/LICENSE
new file mode 100644
index 0000000000..894ee77ed2
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Andrey Nering
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/README.md b/vendor/github.com/go-testfixtures/testfixtures/v3/README.md
new file mode 100644
index 0000000000..56ca8aa455
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/README.md
@@ -0,0 +1,483 @@
+# testfixtures
+
+[![GoDoc](https://godoc.org/github.com/go-testfixtures/testfixtures?status.svg)][doc]
+
+> ***Warning***: this package will wipe the database data before loading the
+fixtures! It is supposed to be used on a test database. Please, double check
+if you are running it against the correct database.
+
+> **TIP**: There are options not described in this README page. It's
+> recommended that you also check [the documentation][doc].
+
+Writing tests is hard, even more when you have to deal with an SQL database.
+This package aims to make writing functional tests for web apps written in
+Go easier.
+
+Basically this package mimics the ["Ruby on Rails' way"][railstests] of writing tests
+for database applications, where sample data is kept in fixtures files. Before
+the execution of every test, the test database is cleaned and the fixture data
+is loaded into the database.
+
+The idea is running tests against a real database, instead of relying in mocks,
+which is boring to setup and may lead to production bugs not being caught in
+the tests.
+
+## Installation
+
+First, import it like this:
+
+```go
+import (
+ "github.com/go-testfixtures/testfixtures/v3"
+)
+```
+
+## Usage
+
+Create a folder for the fixture files. Each file should contain data for a
+single table and have the name `<table_name>.yml`:
+
+```
+myapp/
+ myapp.go
+ myapp_test.go
+ ...
+ fixtures/
+ posts.yml
+ comments.yml
+ tags.yml
+ posts_tags.yml
+ ...
+```
+
+The file would look like this (it can have as many record you want):
+
+```yml
+# comments.yml
+- id: 1
+ post_id: 1
+ content: A comment...
+ author_name: John Doe
+ author_email: john@doe.com
+ created_at: 2020-12-31 23:59:59
+ updated_at: 2020-12-31 23:59:59
+
+- id: 2
+ post_id: 2
+ content: Another comment...
+ author_name: John Doe
+ author_email: john@doe.com
+ created_at: 2020-12-31 23:59:59
+ updated_at: 2020-12-31 23:59:59
+
+# ...
+```
+
+An YAML object or array will be converted to JSON. It will be stored on a native
+JSON type like JSONB on PostgreSQL or as a TEXT or VARCHAR column on other
+databases.
+
+```yml
+- id: 1
+ post_attributes:
+ author: John Due
+ author_email: john@due.com
+ title: "..."
+ tags:
+ - programming
+ - go
+ - testing
+ post: "..."
+```
+
+If you need to write raw SQL, probably to call a function, prefix the value
+of the column with `RAW=`:
+
+```yml
+- id: 1
+ uuid_column: RAW=uuid_generate_v4()
+ postgis_type_column: RAW=ST_GeomFromText('params...')
+ created_at: RAW=NOW()
+ updated_at: RAW=NOW()
+```
+
+Your tests would look like this:
+
+```go
+package myapp
+
+import (
+ "database/sql"
+
+ _ "github.com/lib/pq"
+ "github.com/go-testfixtures/testfixtures/v3"
+)
+
+var (
+ db *sql.DB
+ fixtures *testfixtures.Loader
+)
+
+func TestMain(m *testing.M) {
+ var err error
+
+ // Open connection to the test database.
+ // Do NOT import fixtures in a production database!
+ // Existing data would be deleted.
+ db, err = sql.Open("postgres", "dbname=myapp_test")
+ if err != nil {
+ ...
+ }
+
+ fixtures, err := testfixtures.New(
+ testfixtures.Database(db), // You database connection
+ testfixtures.Dialect("postgres"), // Available: "postgresql", "timescaledb", "mysql", "mariadb", "sqlite" and "sqlserver"
+ testfixtures.Directory("testdata/fixtures"), // the directory containing the YAML files
+ )
+ if err != nil {
+ ...
+ }
+
+ os.Exit(m.Run())
+}
+
+func prepareTestDatabase() {
+ if err := fixtures.Load(); err != nil {
+ ...
+ }
+}
+
+func TestX(t *testing.T) {
+ prepareTestDatabase()
+
+ // Your test here ...
+}
+
+func TestY(t *testing.T) {
+ prepareTestDatabase()
+
+ // Your test here ...
+}
+
+func TestZ(t *testing.T) {
+ prepareTestDatabase()
+
+ // Your test here ...
+}
+```
+
+Alternatively, you can use the `Files` option, to specify which
+files you want to load into the database:
+
+```go
+fixtures, err := testfixtures.New(
+ testfixtures.Database(db),
+ testfixtures.Dialect("postgres"),
+ testfixtures.Files(
+ "fixtures/orders.yml",
+ "fixtures/customers.yml",
+ ),
+)
+if err != nil {
+ ...
+}
+
+fixtures, err := testfixtures.NewFiles(db, &testfixtures.PostgreSQL{},
+ "fixtures/orders.yml",
+ "fixtures/customers.yml",
+ // add as many files you want
+)
+if err != nil {
+ ...
+}
+```
+
+With `Paths` option, you can specify the paths that fixtures will load
+from. Path can be directory or file. If directory, we will search YAML files
+in it.
+
+```go
+fixtures, err := testfixtures.New(
+ testfixtures.Database(db),
+ testfixtures.Dialect("postgres"),
+ testfixtures.Paths(
+ "fixtures/orders.yml",
+ "fixtures/customers.yml",
+ "common_fixtures/users"
+ ),
+)
+if err != nil {
+ ...
+}
+```
+
+## Security check
+
+In order to prevent you from accidentally wiping the wrong database, this
+package will refuse to load fixtures if the database name (or database
+filename for SQLite) doesn't contains "test". If you want to disable this
+check, use:
+
+```go
+testfixtures.New(
+ ...
+ testfixtures.DangerousSkipTestDatabaseCheck(),
+)
+```
+
+## Sequences
+
+For PostgreSQL, this package also resets all sequences to a high
+number to prevent duplicated primary keys while running the tests.
+The default is 10000, but you can change that with:
+
+```go
+testfixtures.New(
+ ...
+ testfixtures.ResetSequencesTo(10000),
+)
+```
+
+Or, if you want to skip the reset of sequences entirely:
+
+```go
+testfixtures.New(
+ ...
+ testfixtures.SkipResetSequences(),
+)
+```
+
+## Compatible databases
+
+### PostgreSQL / TimescaleDB
+
+This package has two approaches to disable foreign keys while importing fixtures
+for PostgreSQL databases:
+
+#### With `DISABLE TRIGGER`
+
+This is the default approach. For that use:
+
+```go
+testfixtures.New(
+ ...
+ testfixtures.Dialect("postgres"), // or "timescaledb"
+)
+```
+
+With the above snippet this package will use `DISABLE TRIGGER` to temporarily
+disabling foreign key constraints while loading fixtures. This work with any
+version of PostgreSQL, but it is **required** to be connected in the database
+as a SUPERUSER. You can make a PostgreSQL user a SUPERUSER with:
+
+```sql
+ALTER USER your_user SUPERUSER;
+```
+
+#### With `ALTER CONSTRAINT`
+
+This approach don't require to be connected as a SUPERUSER, but only work with
+PostgreSQL versions >= 9.4. Try this if you are getting foreign key violation
+errors with the previous approach. It is as simple as using:
+
+```go
+testfixtures.New(
+ ...
+ testfixtures.Dialect("postgres"),
+ testfixtures.UseAlterConstraint(),
+)
+```
+
+Tested using the [github.com/lib/pq](https://github.com/lib/pq) driver.
+
+### MySQL / MariaDB
+
+Just make sure the connection string have
+[the multistatement parameter](https://github.com/go-sql-driver/mysql#multistatements)
+set to true, and use:
+
+```go
+testfixtures.New(
+ ...
+ testfixtures.Dialect("mysql"), // or "mariadb"
+)
+```
+
+Tested using the [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) driver.
+
+### SQLite
+
+SQLite is also supported. It is recommended to create foreign keys as
+`DEFERRABLE` (the default) to prevent problems. See more
+[on the SQLite documentation](https://www.sqlite.org/foreignkeys.html#fk_deferred).
+(Foreign key constraints are no-op by default on SQLite, but enabling it is
+recommended).
+
+```go
+testfixtures.New(
+ ...
+ testfixtures.Dialect("sqlite"),
+)
+```
+
+Tested using the [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) driver.
+
+### Microsoft SQL Server
+
+SQL Server support requires SQL Server >= 2008. Inserting on `IDENTITY` columns
+are handled as well. Just make sure you are logged in with a user with
+`ALTER TABLE` permission.
+
+```go
+testfixtures.New(
+ ...
+ testfixtures.Dialect("sqlserver"),
+)
+```
+
+Tested using the `mssql` and `sqlserver` drivers from the
+[github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb) lib.
+
+## Templating
+
+Testfixtures supports templating, but it's disabled by default. Most people
+won't need it, but it may be useful to dynamically generate data.
+
+Enable it by doing:
+
+```go
+testfixtures.New(
+ ...
+ testfixtures.Template(),
+
+ // the above options are optional
+ TemplateFuncs(...),
+ TemplateDelims("{{", "}}"),
+ TemplateOptions("missingkey=zero"),
+ TemplateData(...),
+)
+```
+
+The YAML file could look like this:
+
+```yaml
+# It's possible generate values...
+- id: {{sha256 "my-awesome-post}}
+ title: My Awesome Post
+ text: {{randomText}}
+
+# ... or records
+{{range $post := $.Posts}}
+- id: {{$post.Id}}
+ title: {{$post.Title}}
+ text: {{$post.Text}}
+{{end}}
+```
+
+## Generating fixtures for a existing database
+
+The following code will generate a YAML file for each table of the database
+into a given folder. It may be useful to boostrap a test scenario from a sample
+database of your app.
+
+```go
+dumper, err := testfixtures.NewDumper(
+ testfixtures.DumpDatabase(db),
+ testfixtures.DumpDialect("postgres"), // or your database of choice
+ testfixtures.DumpDirectory("tmp/fixtures"),
+ textfixtures.DumpTables( // optional, will dump all table if not given
+ "posts",
+ "comments",
+ "tags",
+ )
+)
+if err != nil {
+ ...
+}
+if err := dumper.Dump(); err != nil {
+ ...
+}
+```
+
+> This was intended to run in small sample databases. It will likely break
+if run in a production/big database.
+
+## Gotchas
+
+### Parallel testing
+
+This library doesn't yet support running tests in parallel! Running tests
+in parallel can result in random data being present in the database, which
+will likely cause tests to randomly/intermittently fail.
+
+This is specially tricky since it's not immediately clear that `go test ./...`
+run tests for each package in parallel. If more than one package use this
+library, you can face this issue. Please, use `go test -p 1 ./...` or run tests
+for each package in separated commands to fix this issue.
+
+If you're looking into being able to run tests in parallel you can try using
+testfixtures together with the [txdb][gotxdb] package, which allows wrapping
+each test run in a transaction.
+
+## CLI
+
+We also have a CLI to load fixtures in a given database.
+Grab it from the [releases page](https://github.com/go-testfixtures/testfixtures/releases)
+and use it like:
+
+```bash
+testfixtures -d postgres -c "postgres://user:password@localhost/database" -D testdata/fixtures
+```
+
+The connection string changes for each database driver.
+
+Use `--help` for all flags.
+
+## Contributing
+
+We recommend you to [install Task](https://taskfile.dev/#/installation) and
+Docker before contributing to this package, since some stuff is automated
+using these tools.
+
+It's recommended to use Docker Compose to run tests, since it runs tests for
+all supported databases once. To do that you just need to run:
+
+```bash
+task docker
+```
+
+But if you want to run tests locally, copy the `.sample.env` file as `.env`
+and edit it according to your database setup. You'll need to create a database
+(likely names `testfixtures_test`) before continuing. Then run the command
+for the database you want to run tests against:
+
+```bash
+task test:pg # PostgreSQL
+task test:mysql # MySQL
+task test:sqlite # SQLite
+task test:sqlserver # Microsoft SQL Server
+```
+
+GitHub Actions (CI) runs the same Docker setup available locally.
+
+## Alternatives
+
+If you don't think using fixtures is a good idea, you can try one of these
+packages instead:
+
+- [factory-go][factorygo]: Factory for Go. Inspired by Python's Factory Boy
+and Ruby's Factory Girl
+- [go-txdb (Single transaction SQL driver for Go)][gotxdb]: Use a single
+database transaction for each functional test, so you can rollback to
+previous state between tests to have the same database state in all tests
+- [go-sqlmock][gosqlmock]: A mock for the sql.DB interface. This allow you to
+unit test database code without having to connect to a real database
+- [dbcleaner][dbcleaner] - Clean database for testing, inspired by
+database_cleaner for Ruby
+
+[doc]: https://pkg.go.dev/github.com/go-testfixtures/testfixtures/v3?tab=doc
+[railstests]: http://guides.rubyonrails.org/testing.html#the-test-database
+[gotxdb]: https://github.com/DATA-DOG/go-txdb
+[gosqlmock]: https://github.com/DATA-DOG/go-sqlmock
+[factorygo]: https://github.com/bluele/factory-go
+[dbcleaner]: https://github.com/khaiql/dbcleaner
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/Taskfile.yml b/vendor/github.com/go-testfixtures/testfixtures/v3/Taskfile.yml
new file mode 100644
index 0000000000..a4ae1c899e
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/Taskfile.yml
@@ -0,0 +1,59 @@
+# https://taskfile.org
+
+version: '2'
+
+tasks:
+ build:
+ cmds:
+ - go build -v -tags sqlite -o ./testfixtures{{exeExt}} ./cmd/testfixtures
+
+ test-cli:
+ cmds:
+ - ./testfixtures -d sqlite -c testdb.sqlite3 -D testdata/fixtures
+
+ test:pg:
+ desc: Test PostgreSQL
+ cmds:
+ - task: test-db
+ vars: {DATABASE: postgresql}
+
+ test:mysql:
+ desc: Test MySQL
+ cmds:
+ - task: test:db
+ vars: {DATABASE: mysql}
+
+ test:sqlite:
+ desc: Test SQLite
+ cmds:
+ - task: test-db
+ vars: {DATABASE: sqlite}
+
+ test:sqlserver:
+ desc: Test SQLServer
+ cmds:
+ - task: test-db
+ vars: {DATABASE: sqlserver}
+
+ test-db:
+ cmds:
+ - go test -v -tags {{.DATABASE}}
+
+ goreleaser:test:
+ desc: Tests release process without publishing
+ cmds:
+ - goreleaser --snapshot --rm-dist
+
+ docker:
+ cmds:
+ - task: docker:build
+ - task: docker:test
+
+ docker:build:
+ cmds:
+ - docker build -t testfixtures .
+
+ docker:test:
+ cmds:
+ - docker-compose down -v
+ - docker-compose run testfixtures go test -v -tags 'postgresql sqlite mysql sqlserver'
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/docker-compose.yml b/vendor/github.com/go-testfixtures/testfixtures/v3/docker-compose.yml
new file mode 100644
index 0000000000..44afd56e1c
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/docker-compose.yml
@@ -0,0 +1,37 @@
+version: '3'
+
+services:
+ testfixtures:
+ image: testfixtures
+ depends_on:
+ - postgresql
+ - mysql
+ - sqlserver
+ environment:
+ PGPASSWORD: postgres
+ PG_CONN_STRING: host=postgresql user=postgres dbname=testfixtures_test port=5432 sslmode=disable
+
+ MYSQL_CONN_STRING: root:mysql@tcp(mysql)/testfixtures_test?multiStatements=true
+
+ SQLITE_CONN_STRING: testfixtures_test.sqlite3
+
+ SQLSERVER_CONN_STRING: server=sqlserver;database=master;user id=sa;password=SQL@1server;encrypt=disable
+
+ postgresql:
+ image: postgres:12.1-alpine
+ environment:
+ POSTGRES_DB: testfixtures_test
+ POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: postgres
+
+ mysql:
+ image: mysql:8.0
+ environment:
+ MYSQL_DATABASE: testfixtures_test
+ MYSQL_ROOT_PASSWORD: mysql
+
+ sqlserver:
+ image: mcr.microsoft.com/mssql/server:2019-latest
+ environment:
+ ACCEPT_EULA: 'Y'
+ SA_PASSWORD: SQL@1server
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/dump.go b/vendor/github.com/go-testfixtures/testfixtures/v3/dump.go
new file mode 100644
index 0000000000..fe6f63350f
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/dump.go
@@ -0,0 +1,165 @@
+package testfixtures
+
+import (
+ "database/sql"
+ "fmt"
+ "os"
+ "path/filepath"
+ "unicode/utf8"
+
+ "gopkg.in/yaml.v2"
+)
+
+// Dumper is resposible for dumping fixtures from the database into a
+// directory.
+type Dumper struct {
+ db *sql.DB
+ helper helper
+ dir string
+
+ tables []string
+}
+
+// NewDumper creates a new dumper with the given options.
+//
+// The "DumpDatabase", "DumpDialect" and "DumpDirectory" options are required.
+func NewDumper(options ...func(*Dumper) error) (*Dumper, error) {
+ d := &Dumper{}
+
+ for _, option := range options {
+ if err := option(d); err != nil {
+ return nil, err
+ }
+ }
+
+ return d, nil
+}
+
+// DumpDatabase sets the database to be dumped.
+func DumpDatabase(db *sql.DB) func(*Dumper) error {
+ return func(d *Dumper) error {
+ d.db = db
+ return nil
+ }
+}
+
+// DumpDialect informs Loader about which database dialect you're using.
+//
+// Possible options are "postgresql", "timescaledb", "mysql", "mariadb",
+// "sqlite" and "sqlserver".
+func DumpDialect(dialect string) func(*Dumper) error {
+ return func(d *Dumper) error {
+ h, err := helperForDialect(dialect)
+ if err != nil {
+ return err
+ }
+ d.helper = h
+ return nil
+ }
+}
+
+// DumpDirectory sets the directory where the fixtures files will be created.
+func DumpDirectory(dir string) func(*Dumper) error {
+ return func(d *Dumper) error {
+ d.dir = dir
+ return nil
+ }
+}
+
+// DumpTables allows you to choose which tables you want to dump.
+//
+// If not informed, Dumper will dump all tables by default.
+func DumpTables(tables ...string) func(*Dumper) error {
+ return func(d *Dumper) error {
+ d.tables = tables
+ return nil
+ }
+}
+
+// Dump dumps the databases as YAML fixtures.
+func (d *Dumper) Dump() error {
+ tables := d.tables
+ if len(tables) == 0 {
+ var err error
+ tables, err = d.helper.tableNames(d.db)
+ if err != nil {
+ return err
+ }
+ }
+
+ for _, table := range tables {
+ if err := d.dumpTable(table); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (d *Dumper) dumpTable(table string) error {
+ query := fmt.Sprintf("SELECT * FROM %s", d.helper.quoteKeyword(table))
+
+ stmt, err := d.db.Prepare(query)
+ if err != nil {
+ return err
+ }
+ defer stmt.Close()
+
+ rows, err := stmt.Query()
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ columns, err := rows.Columns()
+ if err != nil {
+ return err
+ }
+
+ fixtures := make([]yaml.MapSlice, 0, 10)
+ for rows.Next() {
+ entries := make([]interface{}, len(columns))
+ entryPtrs := make([]interface{}, len(entries))
+ for i := range entries {
+ entryPtrs[i] = &entries[i]
+ }
+ if err := rows.Scan(entryPtrs...); err != nil {
+ return err
+ }
+
+ entryMap := make([]yaml.MapItem, len(entries))
+ for i, column := range columns {
+ entryMap[i] = yaml.MapItem{
+ Key: column,
+ Value: convertValue(entries[i]),
+ }
+ }
+ fixtures = append(fixtures, entryMap)
+ }
+ if err = rows.Err(); err != nil {
+ return err
+ }
+
+ filePath := filepath.Join(d.dir, table+".yml")
+ f, err := os.Create(filePath)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ data, err := yaml.Marshal(fixtures)
+ if err != nil {
+ return err
+ }
+ _, err = f.Write(data)
+ return err
+}
+
+func convertValue(value interface{}) interface{} {
+ switch v := value.(type) {
+ case []byte:
+ if utf8.Valid(v) {
+ return string(v)
+ }
+ }
+ return value
+}
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/go.mod b/vendor/github.com/go-testfixtures/testfixtures/v3/go.mod
new file mode 100644
index 0000000000..b5f0ae0eea
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/go.mod
@@ -0,0 +1,14 @@
+module github.com/go-testfixtures/testfixtures/v3
+
+require (
+ github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73
+ github.com/go-sql-driver/mysql v1.4.1
+ github.com/joho/godotenv v1.3.0
+ github.com/lib/pq v1.3.0
+ github.com/mattn/go-sqlite3 v2.0.2+incompatible
+ github.com/spf13/pflag v1.0.5
+ google.golang.org/appengine v1.3.0 // indirect
+ gopkg.in/yaml.v2 v2.2.7
+)
+
+go 1.13
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/go.sum b/vendor/github.com/go-testfixtures/testfixtures/v3/go.sum
new file mode 100644
index 0000000000..38616db809
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/go.sum
@@ -0,0 +1,26 @@
+github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73 h1:OGNva6WhsKst5OZf7eZOklDztV3hwtTHovdrLHV+MsA=
+github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
+github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
+github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
+github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
+github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/mattn/go-sqlite3 v2.0.2+incompatible h1:qzw9c2GNT8UFrgWNDhCTqRqYUSmu/Dav/9Z58LGpk7U=
+github.com/mattn/go-sqlite3 v2.0.2+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
+golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk=
+google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
+gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/helper.go b/vendor/github.com/go-testfixtures/testfixtures/v3/helper.go
new file mode 100644
index 0000000000..06ba5b24e3
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/helper.go
@@ -0,0 +1,71 @@
+package testfixtures
+
+import (
+ "database/sql"
+ "fmt"
+)
+
+const (
+ paramTypeDollar = iota + 1
+ paramTypeQuestion
+ paramTypeAtSign
+)
+
+type loadFunction func(tx *sql.Tx) error
+
+type helper interface {
+ init(*sql.DB) error
+ disableReferentialIntegrity(*sql.DB, loadFunction) error
+ paramType() int
+ databaseName(queryable) (string, error)
+ tableNames(queryable) ([]string, error)
+ isTableModified(queryable, string) (bool, error)
+ afterLoad(queryable) error
+ quoteKeyword(string) string
+ whileInsertOnTable(*sql.Tx, string, func() error) error
+}
+
+type queryable interface {
+ Exec(string, ...interface{}) (sql.Result, error)
+ Query(string, ...interface{}) (*sql.Rows, error)
+ QueryRow(string, ...interface{}) *sql.Row
+}
+
+// batchSplitter is an interface with method which returns byte slice for
+// splitting SQL batches. This need to split sql statements and run its
+// separately.
+//
+// For Microsoft SQL Server batch splitter is "GO". For details see
+// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/sql-server-utilities-statements-go
+type batchSplitter interface {
+ splitter() []byte
+}
+
+var (
+ _ helper = &mySQL{}
+ _ helper = &postgreSQL{}
+ _ helper = &sqlite{}
+ _ helper = &sqlserver{}
+)
+
+type baseHelper struct{}
+
+func (baseHelper) init(_ *sql.DB) error {
+ return nil
+}
+
+func (baseHelper) quoteKeyword(str string) string {
+ return fmt.Sprintf(`"%s"`, str)
+}
+
+func (baseHelper) whileInsertOnTable(_ *sql.Tx, _ string, fn func() error) error {
+ return fn()
+}
+
+func (baseHelper) isTableModified(_ queryable, _ string) (bool, error) {
+ return true, nil
+}
+
+func (baseHelper) afterLoad(_ queryable) error {
+ return nil
+}
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/json.go b/vendor/github.com/go-testfixtures/testfixtures/v3/json.go
new file mode 100644
index 0000000000..f954a17a7f
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/json.go
@@ -0,0 +1,44 @@
+package testfixtures
+
+import (
+ "database/sql/driver"
+ "encoding/json"
+)
+
+var (
+ _ driver.Valuer = jsonArray{}
+ _ driver.Valuer = jsonMap{}
+)
+
+type jsonArray []interface{}
+
+func (a jsonArray) Value() (driver.Value, error) {
+ return json.Marshal(a)
+}
+
+type jsonMap map[string]interface{}
+
+func (m jsonMap) Value() (driver.Value, error) {
+ return json.Marshal(m)
+}
+
+// Go refuses to convert map[interface{}]interface{} to JSON because JSON only support string keys
+// So it's necessary to recursively convert all map[interface]interface{} to map[string]interface{}
+func recursiveToJSON(v interface{}) (r interface{}) {
+ switch v := v.(type) {
+ case []interface{}:
+ for i, e := range v {
+ v[i] = recursiveToJSON(e)
+ }
+ r = jsonArray(v)
+ case map[interface{}]interface{}:
+ newMap := make(map[string]interface{}, len(v))
+ for k, e := range v {
+ newMap[k.(string)] = recursiveToJSON(e)
+ }
+ r = jsonMap(newMap)
+ default:
+ r = v
+ }
+ return
+}
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/mysql.go b/vendor/github.com/go-testfixtures/testfixtures/v3/mysql.go
new file mode 100644
index 0000000000..2d1d890207
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/mysql.go
@@ -0,0 +1,131 @@
+package testfixtures
+
+import (
+ "database/sql"
+ "fmt"
+)
+
+type mySQL struct {
+ baseHelper
+ tables []string
+ tablesChecksum map[string]int64
+}
+
+func (h *mySQL) init(db *sql.DB) error {
+ var err error
+ h.tables, err = h.tableNames(db)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (*mySQL) paramType() int {
+ return paramTypeQuestion
+}
+
+func (*mySQL) quoteKeyword(str string) string {
+ return fmt.Sprintf("`%s`", str)
+}
+
+func (*mySQL) databaseName(q queryable) (string, error) {
+ var dbName string
+ err := q.QueryRow("SELECT DATABASE()").Scan(&dbName)
+ return dbName, err
+}
+
+func (h *mySQL) tableNames(q queryable) ([]string, error) {
+ query := `
+ SELECT table_name
+ FROM information_schema.tables
+ WHERE table_schema = ?
+ AND table_type = 'BASE TABLE';
+ `
+ dbName, err := h.databaseName(q)
+ if err != nil {
+ return nil, err
+ }
+
+ rows, err := q.Query(query, dbName)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ var tables []string
+ for rows.Next() {
+ var table string
+ if err = rows.Scan(&table); err != nil {
+ return nil, err
+ }
+ tables = append(tables, table)
+ }
+ if err = rows.Err(); err != nil {
+ return nil, err
+ }
+ return tables, nil
+
+}
+
+func (h *mySQL) disableReferentialIntegrity(db *sql.DB, loadFn loadFunction) (err error) {
+ tx, err := db.Begin()
+ if err != nil {
+ return err
+ }
+ defer tx.Rollback()
+
+ if _, err = tx.Exec("SET FOREIGN_KEY_CHECKS = 0"); err != nil {
+ return err
+ }
+
+ err = loadFn(tx)
+ _, err2 := tx.Exec("SET FOREIGN_KEY_CHECKS = 1")
+ if err != nil {
+ return err
+ }
+ if err2 != nil {
+ return err2
+ }
+
+ return tx.Commit()
+}
+
+func (h *mySQL) isTableModified(q queryable, tableName string) (bool, error) {
+ checksum, err := h.getChecksum(q, tableName)
+ if err != nil {
+ return true, err
+ }
+
+ oldChecksum := h.tablesChecksum[tableName]
+
+ return oldChecksum == 0 || checksum != oldChecksum, nil
+}
+
+func (h *mySQL) afterLoad(q queryable) error {
+ if h.tablesChecksum != nil {
+ return nil
+ }
+
+ h.tablesChecksum = make(map[string]int64, len(h.tables))
+ for _, t := range h.tables {
+ checksum, err := h.getChecksum(q, t)
+ if err != nil {
+ return err
+ }
+ h.tablesChecksum[t] = checksum
+ }
+ return nil
+}
+
+func (h *mySQL) getChecksum(q queryable, tableName string) (int64, error) {
+ sql := fmt.Sprintf("CHECKSUM TABLE %s", h.quoteKeyword(tableName))
+ var (
+ table string
+ checksum int64
+ )
+ if err := q.QueryRow(sql).Scan(&table, &checksum); err != nil {
+ return 0, err
+ }
+ return checksum, nil
+}
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/postgresql.go b/vendor/github.com/go-testfixtures/testfixtures/v3/postgresql.go
new file mode 100644
index 0000000000..a8ccd1d949
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/postgresql.go
@@ -0,0 +1,296 @@
+package testfixtures
+
+import (
+ "database/sql"
+ "fmt"
+ "strings"
+)
+
+type postgreSQL struct {
+ baseHelper
+
+ useAlterConstraint bool
+ skipResetSequences bool
+ resetSequencesTo int64
+
+ tables []string
+ sequences []string
+ nonDeferrableConstraints []pgConstraint
+ tablesChecksum map[string]string
+}
+
+type pgConstraint struct {
+ tableName string
+ constraintName string
+}
+
+func (h *postgreSQL) init(db *sql.DB) error {
+ var err error
+
+ h.tables, err = h.tableNames(db)
+ if err != nil {
+ return err
+ }
+
+ h.sequences, err = h.getSequences(db)
+ if err != nil {
+ return err
+ }
+
+ h.nonDeferrableConstraints, err = h.getNonDeferrableConstraints(db)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (*postgreSQL) paramType() int {
+ return paramTypeDollar
+}
+
+func (*postgreSQL) databaseName(q queryable) (string, error) {
+ var dbName string
+ err := q.QueryRow("SELECT current_database()").Scan(&dbName)
+ return dbName, err
+}
+
+func (h *postgreSQL) tableNames(q queryable) ([]string, error) {
+ var tables []string
+
+ sql := `
+ SELECT pg_namespace.nspname || '.' || pg_class.relname
+ FROM pg_class
+ INNER JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
+ WHERE pg_class.relkind = 'r'
+ AND pg_namespace.nspname NOT IN ('pg_catalog', 'information_schema')
+ AND pg_namespace.nspname NOT LIKE 'pg_toast%'
+ AND pg_namespace.nspname NOT LIKE '\_timescaledb%';
+ `
+ rows, err := q.Query(sql)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ for rows.Next() {
+ var table string
+ if err = rows.Scan(&table); err != nil {
+ return nil, err
+ }
+ tables = append(tables, table)
+ }
+ if err = rows.Err(); err != nil {
+ return nil, err
+ }
+ return tables, nil
+}
+
+func (h *postgreSQL) getSequences(q queryable) ([]string, error) {
+ const sql = `
+ SELECT pg_namespace.nspname || '.' || pg_class.relname AS sequence_name
+ FROM pg_class
+ INNER JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
+ WHERE pg_class.relkind = 'S'
+ AND pg_namespace.nspname NOT LIKE '\_timescaledb%'
+ `
+
+ rows, err := q.Query(sql)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ var sequences []string
+ for rows.Next() {
+ var sequence string
+ if err = rows.Scan(&sequence); err != nil {
+ return nil, err
+ }
+ sequences = append(sequences, sequence)
+ }
+ if err = rows.Err(); err != nil {
+ return nil, err
+ }
+ return sequences, nil
+}
+
+func (*postgreSQL) getNonDeferrableConstraints(q queryable) ([]pgConstraint, error) {
+ var constraints []pgConstraint
+
+ sql := `
+ SELECT table_schema || '.' || table_name, constraint_name
+ FROM information_schema.table_constraints
+ WHERE constraint_type = 'FOREIGN KEY'
+ AND is_deferrable = 'NO'
+ AND table_schema NOT LIKE '\_timescaledb%'
+ `
+ rows, err := q.Query(sql)
+ if err != nil {
+ return nil, err
+ }
+
+ defer rows.Close()
+ for rows.Next() {
+ var constraint pgConstraint
+ if err = rows.Scan(&constraint.tableName, &constraint.constraintName); err != nil {
+ return nil, err
+ }
+ constraints = append(constraints, constraint)
+ }
+ if err = rows.Err(); err != nil {
+ return nil, err
+ }
+ return constraints, nil
+}
+
+func (h *postgreSQL) disableTriggers(db *sql.DB, loadFn loadFunction) (err error) {
+ defer func() {
+ // re-enable triggers after load
+ var sql string
+ for _, table := range h.tables {
+ sql += fmt.Sprintf("ALTER TABLE %s ENABLE TRIGGER ALL;", h.quoteKeyword(table))
+ }
+ if _, err2 := db.Exec(sql); err2 != nil && err == nil {
+ err = err2
+ }
+ }()
+
+ tx, err := db.Begin()
+ if err != nil {
+ return err
+ }
+
+ var sql string
+ for _, table := range h.tables {
+ sql += fmt.Sprintf("ALTER TABLE %s DISABLE TRIGGER ALL;", h.quoteKeyword(table))
+ }
+ if _, err = tx.Exec(sql); err != nil {
+ return err
+ }
+
+ if err = loadFn(tx); err != nil {
+ tx.Rollback()
+ return err
+ }
+
+ return tx.Commit()
+}
+
+func (h *postgreSQL) makeConstraintsDeferrable(db *sql.DB, loadFn loadFunction) (err error) {
+ defer func() {
+ // ensure constraint being not deferrable again after load
+ var sql string
+ for _, constraint := range h.nonDeferrableConstraints {
+ sql += fmt.Sprintf("ALTER TABLE %s ALTER CONSTRAINT %s NOT DEFERRABLE;", h.quoteKeyword(constraint.tableName), h.quoteKeyword(constraint.constraintName))
+ }
+ if _, err2 := db.Exec(sql); err2 != nil && err == nil {
+ err = err2
+ }
+ }()
+
+ var sql string
+ for _, constraint := range h.nonDeferrableConstraints {
+ sql += fmt.Sprintf("ALTER TABLE %s ALTER CONSTRAINT %s DEFERRABLE;", h.quoteKeyword(constraint.tableName), h.quoteKeyword(constraint.constraintName))
+ }
+ if _, err := db.Exec(sql); err != nil {
+ return err
+ }
+
+ tx, err := db.Begin()
+ if err != nil {
+ return err
+ }
+ defer tx.Rollback()
+
+ if _, err = tx.Exec("SET CONSTRAINTS ALL DEFERRED"); err != nil {
+ return err
+ }
+
+ if err = loadFn(tx); err != nil {
+ return err
+ }
+
+ return tx.Commit()
+}
+
+func (h *postgreSQL) disableReferentialIntegrity(db *sql.DB, loadFn loadFunction) (err error) {
+ // ensure sequences being reset after load
+ if !h.skipResetSequences {
+ defer func() {
+ if err2 := h.resetSequences(db); err2 != nil && err == nil {
+ err = err2
+ }
+ }()
+ }
+
+ if h.useAlterConstraint {
+ return h.makeConstraintsDeferrable(db, loadFn)
+ }
+ return h.disableTriggers(db, loadFn)
+}
+
+func (h *postgreSQL) resetSequences(db *sql.DB) error {
+ resetSequencesTo := h.resetSequencesTo
+ if resetSequencesTo == 0 {
+ resetSequencesTo = 10000
+ }
+
+ for _, sequence := range h.sequences {
+ _, err := db.Exec(fmt.Sprintf("SELECT SETVAL('%s', %d)", sequence, resetSequencesTo))
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (h *postgreSQL) isTableModified(q queryable, tableName string) (bool, error) {
+ checksum, err := h.getChecksum(q, tableName)
+ if err != nil {
+ return false, err
+ }
+
+ oldChecksum := h.tablesChecksum[tableName]
+
+ return oldChecksum == "" || checksum != oldChecksum, nil
+}
+
+func (h *postgreSQL) afterLoad(q queryable) error {
+ if h.tablesChecksum != nil {
+ return nil
+ }
+
+ h.tablesChecksum = make(map[string]string, len(h.tables))
+ for _, t := range h.tables {
+ checksum, err := h.getChecksum(q, t)
+ if err != nil {
+ return err
+ }
+ h.tablesChecksum[t] = checksum
+ }
+ return nil
+}
+
+func (h *postgreSQL) getChecksum(q queryable, tableName string) (string, error) {
+ sqlStr := fmt.Sprintf(`
+ SELECT md5(CAST((array_agg(t.*)) AS TEXT))
+ FROM %s AS t
+ `,
+ h.quoteKeyword(tableName),
+ )
+
+ var checksum sql.NullString
+ if err := q.QueryRow(sqlStr).Scan(&checksum); err != nil {
+ return "", err
+ }
+ return checksum.String, nil
+}
+
+func (*postgreSQL) quoteKeyword(s string) string {
+ parts := strings.Split(s, ".")
+ for i, p := range parts {
+ parts[i] = fmt.Sprintf(`"%s"`, p)
+ }
+ return strings.Join(parts, ".")
+}
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/sqlite.go b/vendor/github.com/go-testfixtures/testfixtures/v3/sqlite.go
new file mode 100644
index 0000000000..d3d3153415
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/sqlite.go
@@ -0,0 +1,75 @@
+package testfixtures
+
+import (
+ "database/sql"
+ "path/filepath"
+)
+
+type sqlite struct {
+ baseHelper
+}
+
+func (*sqlite) paramType() int {
+ return paramTypeQuestion
+}
+
+func (*sqlite) databaseName(q queryable) (string, error) {
+ var seq int
+ var main, dbName string
+ err := q.QueryRow("PRAGMA database_list").Scan(&seq, &main, &dbName)
+ if err != nil {
+ return "", err
+ }
+ dbName = filepath.Base(dbName)
+ return dbName, nil
+}
+
+func (*sqlite) tableNames(q queryable) ([]string, error) {
+ query := `
+ SELECT name
+ FROM sqlite_master
+ WHERE type = 'table';
+ `
+ rows, err := q.Query(query)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ var tables []string
+ for rows.Next() {
+ var table string
+ if err = rows.Scan(&table); err != nil {
+ return nil, err
+ }
+ tables = append(tables, table)
+ }
+ if err = rows.Err(); err != nil {
+ return nil, err
+ }
+ return tables, nil
+}
+
+func (*sqlite) disableReferentialIntegrity(db *sql.DB, loadFn loadFunction) (err error) {
+ defer func() {
+ if _, err2 := db.Exec("PRAGMA defer_foreign_keys = OFF"); err2 != nil && err == nil {
+ err = err2
+ }
+ }()
+
+ if _, err = db.Exec("PRAGMA defer_foreign_keys = ON"); err != nil {
+ return err
+ }
+
+ tx, err := db.Begin()
+ if err != nil {
+ return err
+ }
+ defer tx.Rollback()
+
+ if err = loadFn(tx); err != nil {
+ return err
+ }
+
+ return tx.Commit()
+}
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/sqlserver.go b/vendor/github.com/go-testfixtures/testfixtures/v3/sqlserver.go
new file mode 100644
index 0000000000..c862a3b62b
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/sqlserver.go
@@ -0,0 +1,153 @@
+package testfixtures
+
+import (
+ "database/sql"
+ "fmt"
+ "strings"
+)
+
+type sqlserver struct {
+ baseHelper
+
+ paramTypeCache int
+ tables []string
+}
+
+func (h *sqlserver) init(db *sql.DB) error {
+ var err error
+
+ // NOTE(@andreynering): The SQL Server lib (github.com/denisenkom/go-mssqldb)
+ // supports both the "?" style (when using the deprecated "mssql" driver)
+ // and the "@p1" style (when using the new "sqlserver" driver).
+ //
+ // Since we don't have a way to know which driver it's been used,
+ // this is a small hack to detect the allowed param style.
+ var v int
+ if err := db.QueryRow("SELECT ?", 1).Scan(&v); err == nil && v == 1 {
+ h.paramTypeCache = paramTypeQuestion
+ } else {
+ h.paramTypeCache = paramTypeAtSign
+ }
+
+ h.tables, err = h.tableNames(db)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (h *sqlserver) paramType() int {
+ return h.paramTypeCache
+}
+
+func (*sqlserver) quoteKeyword(s string) string {
+ parts := strings.Split(s, ".")
+ for i, p := range parts {
+ parts[i] = fmt.Sprintf(`[%s]`, p)
+ }
+ return strings.Join(parts, ".")
+}
+
+func (*sqlserver) databaseName(q queryable) (string, error) {
+ var dbName string
+ err := q.QueryRow("SELECT DB_NAME()").Scan(&dbName)
+ return dbName, err
+}
+
+func (*sqlserver) tableNames(q queryable) ([]string, error) {
+ rows, err := q.Query("SELECT table_schema + '.' + table_name FROM information_schema.tables WHERE table_name <> 'spt_values'")
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ var tables []string
+ for rows.Next() {
+ var table string
+ if err = rows.Scan(&table); err != nil {
+ return nil, err
+ }
+ tables = append(tables, table)
+ }
+ if err = rows.Err(); err != nil {
+ return nil, err
+ }
+ return tables, nil
+}
+
+func (h *sqlserver) tableHasIdentityColumn(q queryable, tableName string) (bool, error) {
+ sql := fmt.Sprintf(`
+ SELECT COUNT(*)
+ FROM SYS.IDENTITY_COLUMNS
+ WHERE OBJECT_ID = OBJECT_ID('%s')
+ `, tableName)
+ var count int
+ if err := q.QueryRow(sql).Scan(&count); err != nil {
+ return false, err
+ }
+ return count > 0, nil
+
+}
+
+func (h *sqlserver) whileInsertOnTable(tx *sql.Tx, tableName string, fn func() error) (err error) {
+ hasIdentityColumn, err := h.tableHasIdentityColumn(tx, tableName)
+ if err != nil {
+ return err
+ }
+ if hasIdentityColumn {
+ defer func() {
+ _, err2 := tx.Exec(fmt.Sprintf("SET IDENTITY_INSERT %s OFF", h.quoteKeyword(tableName)))
+ if err2 != nil && err == nil {
+ err = fmt.Errorf("testfixtures: could not disable identity insert: %w", err2)
+ }
+ }()
+
+ _, err := tx.Exec(fmt.Sprintf("SET IDENTITY_INSERT %s ON", h.quoteKeyword(tableName)))
+ if err != nil {
+ return fmt.Errorf("testfixtures: could not enable identity insert: %w", err)
+ }
+ }
+ return fn()
+}
+
+func (h *sqlserver) disableReferentialIntegrity(db *sql.DB, loadFn loadFunction) (err error) {
+ // ensure the triggers are re-enable after all
+ defer func() {
+ var sql string
+ for _, table := range h.tables {
+ sql += fmt.Sprintf("ALTER TABLE %s WITH CHECK CHECK CONSTRAINT ALL;", h.quoteKeyword(table))
+ }
+ if _, err2 := db.Exec(sql); err2 != nil && err == nil {
+ err = err2
+ }
+ }()
+
+ var sql string
+ for _, table := range h.tables {
+ sql += fmt.Sprintf("ALTER TABLE %s NOCHECK CONSTRAINT ALL;", h.quoteKeyword(table))
+ }
+ if _, err := db.Exec(sql); err != nil {
+ return err
+ }
+
+ tx, err := db.Begin()
+ if err != nil {
+ return err
+ }
+ defer tx.Rollback()
+
+ if err = loadFn(tx); err != nil {
+ return err
+ }
+
+ return tx.Commit()
+}
+
+// splitter is a batchSplitter interface implementation. We need it for
+// SQL Server because commands like a `CREATE SCHEMA...` and a `CREATE TABLE...`
+// could not be executed in the same batch.
+// See https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2008-r2/ms175502(v=sql.105)#rules-for-using-batches
+func (*sqlserver) splitter() []byte {
+ return []byte("GO\n")
+}
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/testfixtures.go b/vendor/github.com/go-testfixtures/testfixtures/v3/testfixtures.go
new file mode 100644
index 0000000000..7125b38c1d
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/testfixtures.go
@@ -0,0 +1,599 @@
+package testfixtures // import "github.com/go-testfixtures/testfixtures/v3"
+
+import (
+ "bytes"
+ "database/sql"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+ "path/filepath"
+ "regexp"
+ "strings"
+ "text/template"
+ "time"
+
+ "gopkg.in/yaml.v2"
+)
+
+// Loader is the responsible to loading fixtures.
+type Loader struct {
+ db *sql.DB
+ helper helper
+ fixturesFiles []*fixtureFile
+
+ skipTestDatabaseCheck bool
+ location *time.Location
+
+ template bool
+ templateFuncs template.FuncMap
+ templateLeftDelim string
+ templateRightDelim string
+ templateOptions []string
+ templateData interface{}
+}
+
+type fixtureFile struct {
+ path string
+ fileName string
+ content []byte
+ insertSQLs []insertSQL
+}
+
+type insertSQL struct {
+ sql string
+ params []interface{}
+}
+
+var (
+ testDatabaseRegexp = regexp.MustCompile("(?i)test")
+
+ errDatabaseIsRequired = fmt.Errorf("testfixtures: database is required")
+ errDialectIsRequired = fmt.Errorf("testfixtures: dialect is required")
+)
+
+// New instantiates a new Loader instance. The "Database" and "Driver"
+// options are required.
+func New(options ...func(*Loader) error) (*Loader, error) {
+ l := &Loader{
+ templateLeftDelim: "{{",
+ templateRightDelim: "}}",
+ templateOptions: []string{"missingkey=zero"},
+ }
+
+ for _, option := range options {
+ if err := option(l); err != nil {
+ return nil, err
+ }
+ }
+
+ if l.db == nil {
+ return nil, errDatabaseIsRequired
+ }
+ if l.helper == nil {
+ return nil, errDialectIsRequired
+ }
+
+ if err := l.helper.init(l.db); err != nil {
+ return nil, err
+ }
+ if err := l.buildInsertSQLs(); err != nil {
+ return nil, err
+ }
+
+ return l, nil
+}
+
+// Database sets an existing sql.DB instant to Loader.
+func Database(db *sql.DB) func(*Loader) error {
+ return func(l *Loader) error {
+ l.db = db
+ return nil
+ }
+}
+
+// Dialect informs Loader about which database dialect you're using.
+//
+// Possible options are "postgresql", "timescaledb", "mysql", "mariadb",
+// "sqlite" and "sqlserver".
+func Dialect(dialect string) func(*Loader) error {
+ return func(l *Loader) error {
+ h, err := helperForDialect(dialect)
+ if err != nil {
+ return err
+ }
+ l.helper = h
+ return nil
+ }
+}
+
+func helperForDialect(dialect string) (helper, error) {
+ switch dialect {
+ case "postgres", "postgresql", "timescaledb":
+ return &postgreSQL{}, nil
+ case "mysql", "mariadb":
+ return &mySQL{}, nil
+ case "sqlite", "sqlite3":
+ return &sqlite{}, nil
+ case "mssql", "sqlserver":
+ return &sqlserver{}, nil
+ default:
+ return nil, fmt.Errorf(`testfixtures: unrecognized dialect "%s"`, dialect)
+ }
+}
+
+// UseAlterConstraint If true, the contraint disabling will do
+// using ALTER CONTRAINT sintax, only allowed in PG >= 9.4.
+// If false, the constraint disabling will use DISABLE TRIGGER ALL,
+// which requires SUPERUSER privileges.
+//
+// Only valid for PostgreSQL. Returns an error otherwise.
+func UseAlterConstraint() func(*Loader) error {
+ return func(l *Loader) error {
+ pgHelper, ok := l.helper.(*postgreSQL)
+ if !ok {
+ return fmt.Errorf("testfixtures: UseAlterConstraint is only valid for PostgreSQL databases")
+ }
+ pgHelper.useAlterConstraint = true
+ return nil
+ }
+}
+
+// SkipResetSequences prevents Loader from reseting sequences after loading
+// fixtures.
+//
+// Only valid for PostgreSQL. Returns an error otherwise.
+func SkipResetSequences() func(*Loader) error {
+ return func(l *Loader) error {
+ pgHelper, ok := l.helper.(*postgreSQL)
+ if !ok {
+ return fmt.Errorf("testfixtures: SkipResetSequences is only valid for PostgreSQL databases")
+ }
+ pgHelper.skipResetSequences = true
+ return nil
+ }
+}
+
+// ResetSequencesTo sets the value the sequences will be reset to.
+//
+// Defaults to 10000.
+//
+// Only valid for PostgreSQL. Returns an error otherwise.
+func ResetSequencesTo(value int64) func(*Loader) error {
+ return func(l *Loader) error {
+ pgHelper, ok := l.helper.(*postgreSQL)
+ if !ok {
+ return fmt.Errorf("testfixtures: ResetSequencesTo is only valid for PostgreSQL databases")
+ }
+ pgHelper.resetSequencesTo = value
+ return nil
+ }
+}
+
+// DangerousSkipTestDatabaseCheck will make Loader not check if the database
+// name contains "test". Use with caution!
+func DangerousSkipTestDatabaseCheck() func(*Loader) error {
+ return func(l *Loader) error {
+ l.skipTestDatabaseCheck = true
+ return nil
+ }
+}
+
+// Directory informs Loader to load YAML files from a given directory.
+func Directory(dir string) func(*Loader) error {
+ return func(l *Loader) error {
+ fixtures, err := l.fixturesFromDir(dir)
+ if err != nil {
+ return err
+ }
+ l.fixturesFiles = append(l.fixturesFiles, fixtures...)
+ return nil
+ }
+}
+
+// Files informs Loader to load a given set of YAML files.
+func Files(files ...string) func(*Loader) error {
+ return func(l *Loader) error {
+ fixtures, err := l.fixturesFromFiles(files...)
+ if err != nil {
+ return err
+ }
+ l.fixturesFiles = append(l.fixturesFiles, fixtures...)
+ return nil
+ }
+}
+
+// Paths inform Loader to load a given set of YAML files and directories.
+func Paths(paths ...string) func(*Loader) error {
+ return func(l *Loader) error {
+ fixtures, err := l.fixturesFromPaths(paths...)
+ if err != nil {
+ return err
+ }
+ l.fixturesFiles = append(l.fixturesFiles, fixtures...)
+ return nil
+ }
+}
+
+// Location makes Loader use the given location by default when parsing
+// dates. If not given, by default it uses the value of time.Local.
+func Location(location *time.Location) func(*Loader) error {
+ return func(l *Loader) error {
+ l.location = location
+ return nil
+ }
+}
+
+// Template makes loader process each YAML file as an template using the
+// text/template package.
+//
+// For more information on how templates work in Go please read:
+// https://golang.org/pkg/text/template/
+//
+// If not given the YAML files are parsed as is.
+func Template() func(*Loader) error {
+ return func(l *Loader) error {
+ l.template = true
+ return nil
+ }
+}
+
+// TemplateFuncs allow choosing which functions will be available
+// when processing templates.
+//
+// For more information see: https://golang.org/pkg/text/template/#Template.Funcs
+func TemplateFuncs(funcs template.FuncMap) func(*Loader) error {
+ return func(l *Loader) error {
+ if !l.template {
+ return fmt.Errorf(`testfixtures: the Template() options is required in order to use the TemplateFuns() option`)
+ }
+
+ l.templateFuncs = funcs
+ return nil
+ }
+}
+
+// TemplateDelims allow choosing which delimiters will be used for templating.
+// This defaults to "{{" and "}}".
+//
+// For more information see https://golang.org/pkg/text/template/#Template.Delims
+func TemplateDelims(left, right string) func(*Loader) error {
+ return func(l *Loader) error {
+ if !l.template {
+ return fmt.Errorf(`testfixtures: the Template() options is required in order to use the TemplateDelims() option`)
+ }
+
+ l.templateLeftDelim = left
+ l.templateRightDelim = right
+ return nil
+ }
+}
+
+// TemplateOptions allows you to specific which text/template options will
+// be enabled when processing templates.
+//
+// This defaults to "missingkey=zero". Check the available options here:
+// https://golang.org/pkg/text/template/#Template.Option
+func TemplateOptions(options ...string) func(*Loader) error {
+ return func(l *Loader) error {
+ if !l.template {
+ return fmt.Errorf(`testfixtures: the Template() options is required in order to use the TemplateOptions() option`)
+ }
+
+ l.templateOptions = options
+ return nil
+ }
+}
+
+// TemplateData allows you to specify which data will be available
+// when processing templates. Data is accesible by prefixing it with a "."
+// like {{.MyKey}}.
+func TemplateData(data interface{}) func(*Loader) error {
+ return func(l *Loader) error {
+ if !l.template {
+ return fmt.Errorf(`testfixtures: the Template() options is required in order to use the TemplateData() option`)
+ }
+
+ l.templateData = data
+ return nil
+ }
+}
+
+// EnsureTestDatabase returns an error if the database name does not contains
+// "test".
+func (l *Loader) EnsureTestDatabase() error {
+ dbName, err := l.helper.databaseName(l.db)
+ if err != nil {
+ return err
+ }
+ if !testDatabaseRegexp.MatchString(dbName) {
+ return fmt.Errorf(`testfixtures: database "%s" does not appear to be a test database`, dbName)
+ }
+ return nil
+}
+
+// Load wipes and after load all fixtures in the database.
+// if err := fixtures.Load(); err != nil {
+// ...
+// }
+func (l *Loader) Load() error {
+ if !l.skipTestDatabaseCheck {
+ if err := l.EnsureTestDatabase(); err != nil {
+ return err
+ }
+ }
+
+ err := l.helper.disableReferentialIntegrity(l.db, func(tx *sql.Tx) error {
+ for _, file := range l.fixturesFiles {
+ modified, err := l.helper.isTableModified(tx, file.fileNameWithoutExtension())
+ if err != nil {
+ return err
+ }
+ if !modified {
+ continue
+ }
+ if err := file.delete(tx, l.helper); err != nil {
+ return err
+ }
+
+ err = l.helper.whileInsertOnTable(tx, file.fileNameWithoutExtension(), func() error {
+ for j, i := range file.insertSQLs {
+ if _, err := tx.Exec(i.sql, i.params...); err != nil {
+ return &InsertError{
+ Err: err,
+ File: file.fileName,
+ Index: j,
+ SQL: i.sql,
+ Params: i.params,
+ }
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ return err
+ }
+ return l.helper.afterLoad(l.db)
+}
+
+// InsertError will be returned if any error happens on database while
+// inserting the record.
+type InsertError struct {
+ Err error
+ File string
+ Index int
+ SQL string
+ Params []interface{}
+}
+
+func (e *InsertError) Error() string {
+ return fmt.Sprintf(
+ "testfixtures: error inserting record: %v, on file: %s, index: %d, sql: %s, params: %v",
+ e.Err,
+ e.File,
+ e.Index,
+ e.SQL,
+ e.Params,
+ )
+}
+
+func (l *Loader) buildInsertSQLs() error {
+ for _, f := range l.fixturesFiles {
+ var records interface{}
+ if err := yaml.Unmarshal(f.content, &records); err != nil {
+ return fmt.Errorf("testfixtures: could not unmarshal YAML: %w", err)
+ }
+
+ switch records := records.(type) {
+ case []interface{}:
+ f.insertSQLs = make([]insertSQL, 0, len(records))
+
+ for _, record := range records {
+ recordMap, ok := record.(map[interface{}]interface{})
+ if !ok {
+ return fmt.Errorf("testfixtures: could not cast record: not a map[interface{}]interface{}")
+ }
+
+ sql, values, err := l.buildInsertSQL(f, recordMap)
+ if err != nil {
+ return err
+ }
+
+ f.insertSQLs = append(f.insertSQLs, insertSQL{sql, values})
+ }
+ case map[interface{}]interface{}:
+ f.insertSQLs = make([]insertSQL, 0, len(records))
+
+ for _, record := range records {
+ recordMap, ok := record.(map[interface{}]interface{})
+ if !ok {
+ return fmt.Errorf("testfixtures: could not cast record: not a map[interface{}]interface{}")
+ }
+
+ sql, values, err := l.buildInsertSQL(f, recordMap)
+ if err != nil {
+ return err
+ }
+
+ f.insertSQLs = append(f.insertSQLs, insertSQL{sql, values})
+ }
+ default:
+ return fmt.Errorf("testfixtures: fixture is not a slice or map")
+ }
+ }
+
+ return nil
+}
+
+func (f *fixtureFile) fileNameWithoutExtension() string {
+ return strings.Replace(f.fileName, filepath.Ext(f.fileName), "", 1)
+}
+
+func (f *fixtureFile) delete(tx *sql.Tx, h helper) error {
+ if _, err := tx.Exec(fmt.Sprintf("DELETE FROM %s", h.quoteKeyword(f.fileNameWithoutExtension()))); err != nil {
+ return fmt.Errorf(`testfixtures: could not clean table "%s": %w`, f.fileNameWithoutExtension(), err)
+ }
+ return nil
+}
+
+func (l *Loader) buildInsertSQL(f *fixtureFile, record map[interface{}]interface{}) (sqlStr string, values []interface{}, err error) {
+ var (
+ sqlColumns = make([]string, 0, len(record))
+ sqlValues = make([]string, 0, len(record))
+ i = 1
+ )
+ for key, value := range record {
+ keyStr, ok := key.(string)
+ if !ok {
+ err = fmt.Errorf("testfixtures: record map key is not a string")
+ return
+ }
+
+ sqlColumns = append(sqlColumns, l.helper.quoteKeyword(keyStr))
+
+ // if string, try convert to SQL or time
+ // if map or array, convert to json
+ switch v := value.(type) {
+ case string:
+ if strings.HasPrefix(v, "RAW=") {
+ sqlValues = append(sqlValues, strings.TrimPrefix(v, "RAW="))
+ continue
+ }
+
+ if t, err := l.tryStrToDate(v); err == nil {
+ value = t
+ }
+ case []interface{}, map[interface{}]interface{}:
+ value = recursiveToJSON(v)
+ }
+
+ switch l.helper.paramType() {
+ case paramTypeDollar:
+ sqlValues = append(sqlValues, fmt.Sprintf("$%d", i))
+ case paramTypeQuestion:
+ sqlValues = append(sqlValues, "?")
+ case paramTypeAtSign:
+ sqlValues = append(sqlValues, fmt.Sprintf("@p%d", i))
+ }
+
+ values = append(values, value)
+ i++
+ }
+
+ sqlStr = fmt.Sprintf(
+ "INSERT INTO %s (%s) VALUES (%s)",
+ l.helper.quoteKeyword(f.fileNameWithoutExtension()),
+ strings.Join(sqlColumns, ", "),
+ strings.Join(sqlValues, ", "),
+ )
+ return
+}
+
+func (l *Loader) fixturesFromDir(dir string) ([]*fixtureFile, error) {
+ fileinfos, err := ioutil.ReadDir(dir)
+ if err != nil {
+ return nil, fmt.Errorf(`testfixtures: could not stat directory "%s": %w`, dir, err)
+ }
+
+ files := make([]*fixtureFile, 0, len(fileinfos))
+
+ for _, fileinfo := range fileinfos {
+ fileExt := filepath.Ext(fileinfo.Name())
+ if !fileinfo.IsDir() && (fileExt == ".yml" || fileExt == ".yaml") {
+ fixture := &fixtureFile{
+ path: path.Join(dir, fileinfo.Name()),
+ fileName: fileinfo.Name(),
+ }
+ fixture.content, err = ioutil.ReadFile(fixture.path)
+ if err != nil {
+ return nil, fmt.Errorf(`testfixtures: could not read file "%s": %w`, fixture.path, err)
+ }
+ if err := l.processFileTemplate(fixture); err != nil {
+ return nil, err
+ }
+ files = append(files, fixture)
+ }
+ }
+ return files, nil
+}
+
+func (l *Loader) fixturesFromFiles(fileNames ...string) ([]*fixtureFile, error) {
+ var (
+ fixtureFiles = make([]*fixtureFile, 0, len(fileNames))
+ err error
+ )
+
+ for _, f := range fileNames {
+ fixture := &fixtureFile{
+ path: f,
+ fileName: filepath.Base(f),
+ }
+ fixture.content, err = ioutil.ReadFile(fixture.path)
+ if err != nil {
+ return nil, fmt.Errorf(`testfixtures: could not read file "%s": %w`, fixture.path, err)
+ }
+ if err := l.processFileTemplate(fixture); err != nil {
+ return nil, err
+ }
+ fixtureFiles = append(fixtureFiles, fixture)
+ }
+
+ return fixtureFiles, nil
+}
+
+func (l *Loader) fixturesFromPaths(paths ...string) ([]*fixtureFile, error) {
+ fixtureExtractor := func(p string, isDir bool) ([]*fixtureFile, error) {
+ if isDir {
+ return l.fixturesFromDir(p)
+ }
+
+ return l.fixturesFromFiles(p)
+ }
+
+ var fixtureFiles []*fixtureFile
+
+ for _, p := range paths {
+ f, err := os.Stat(p)
+ if err != nil {
+ return nil, fmt.Errorf(`testfixtures: could not stat path "%s": %w`, p, err)
+ }
+
+ fixtures, err := fixtureExtractor(p, f.IsDir())
+ if err != nil {
+ return nil, err
+ }
+
+ fixtureFiles = append(fixtureFiles, fixtures...)
+ }
+
+ return fixtureFiles, nil
+}
+
+func (l *Loader) processFileTemplate(f *fixtureFile) error {
+ if !l.template {
+ return nil
+ }
+
+ t := template.New("").
+ Funcs(l.templateFuncs).
+ Delims(l.templateLeftDelim, l.templateRightDelim).
+ Option(l.templateOptions...)
+ t, err := t.Parse(string(f.content))
+ if err != nil {
+ return fmt.Errorf(`textfixtures: error on parsing template in %s: %w`, f.fileName, err)
+ }
+
+ var buffer bytes.Buffer
+ if err := t.Execute(&buffer, l.templateData); err != nil {
+ return fmt.Errorf(`textfixtures: error on executing template in %s: %w`, f.fileName, err)
+ }
+
+ f.content = buffer.Bytes()
+ return nil
+}
diff --git a/vendor/github.com/go-testfixtures/testfixtures/v3/time.go b/vendor/github.com/go-testfixtures/testfixtures/v3/time.go
new file mode 100644
index 0000000000..3d50a98cc9
--- /dev/null
+++ b/vendor/github.com/go-testfixtures/testfixtures/v3/time.go
@@ -0,0 +1,43 @@
+package testfixtures
+
+import (
+ "fmt"
+ "time"
+)
+
+var timeFormats = [...]string{
+ "2006-01-02",
+ "2006-01-02 15:04",
+ "2006-01-02 15:04:05",
+ "20060102",
+ "20060102 15:04",
+ "20060102 15:04:05",
+ "02/01/2006",
+ "02/01/2006 15:04",
+ "02/01/2006 15:04:05",
+ "2006-01-02T15:04-07:00",
+ "2006-01-02T15:04:05-07:00",
+ "2006-01-02T15:04:05Z07:00",
+ "2006-01-02 15:04:05Z07:00",
+ "2006-01-02T15:04:05Z0700",
+ "2006-01-02 15:04:05Z0700",
+ "2006-01-02T15:04:05Z07",
+ "2006-01-02 15:04:05Z07",
+ "2006-01-02 15:04:05 MST",
+}
+
+func (l *Loader) tryStrToDate(s string) (time.Time, error) {
+ loc := l.location
+ if loc == nil {
+ loc = time.Local
+ }
+
+ for _, f := range timeFormats {
+ t, err := time.ParseInLocation(f, s, loc)
+ if err != nil {
+ continue
+ }
+ return t, nil
+ }
+ return time.Time{}, fmt.Errorf(`testfixtures: could not convert string "%s" to time`, s)
+}