initial commit
This commit is contained in:
21
vendor/gopkg.in/testfixtures.v2/LICENSE
generated
vendored
Normal file
21
vendor/gopkg.in/testfixtures.v2/LICENSE
generated
vendored
Normal file
@ -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.
|
368
vendor/gopkg.in/testfixtures.v2/README.md
generated
vendored
Normal file
368
vendor/gopkg.in/testfixtures.v2/README.md
generated
vendored
Normal file
@ -0,0 +1,368 @@
|
||||
# Go Test Fixtures
|
||||
|
||||
[](https://github.com/go-testfixtures/testfixtures/blob/master/LICENSE)
|
||||
[](https://gitter.im/go-testfixtures/testfixtures?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://godoc.org/gopkg.in/testfixtures.v2)
|
||||
[](https://travis-ci.org/go-testfixtures/testfixtures)
|
||||
[](https://goreportcard.com/report/github.com/go-testfixtures/testfixtures)
|
||||
|
||||
> ***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.
|
||||
|
||||
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 ["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, get it:
|
||||
|
||||
```bash
|
||||
go get -u gopkg.in/testfixtures.v2
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Create a folder for the fixture files. Each file should contain data for a
|
||||
single table and have the name `<table-name>.yml`:
|
||||
|
||||
```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: 2016-01-01 12:30:12
|
||||
updated_at: 2016-01-01 12:30:12
|
||||
|
||||
- id: 2
|
||||
post_id: 2
|
||||
content: Another comment...
|
||||
author_name: John Doe
|
||||
author_email: john@doe.com
|
||||
created_at: 2016-01-01 12:30:12
|
||||
updated_at: 2016-01-01 12:30:12
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
An YAML object or array will be converted to JSON. It can 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"
|
||||
"log"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
"gopkg.in/testfixtures.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
db *sql.DB
|
||||
fixtures *testfixtures.Context
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
|
||||
// Open connection with 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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// creating the context that hold the fixtures
|
||||
// see about all compatible databases in this page below
|
||||
fixtures, err = testfixtures.NewFolder(db, &testfixtures.PostgreSQL{}, "testdata/fixtures")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func prepareTestDatabase() {
|
||||
if err := fixtures.Load(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
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 `NewFiles` function, to specify which
|
||||
files you want to load into the database:
|
||||
|
||||
```go
|
||||
fixtures, err := testfixtures.NewFiles(db, &testfixtures.PostgreSQL{},
|
||||
"fixtures/orders.yml",
|
||||
"fixtures/customers.yml",
|
||||
// add as many files you want
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
## 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.SkipDatabaseNameCheck(true)
|
||||
```
|
||||
|
||||
## Sequences
|
||||
|
||||
For PostgreSQL or Oracle, 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.ResetSequencesTo(10000)
|
||||
```
|
||||
|
||||
## Compatible databases
|
||||
|
||||
### PostgreSQL
|
||||
|
||||
This package has two approaches to disable foreign keys while importing fixtures
|
||||
in PostgreSQL databases:
|
||||
|
||||
#### With `DISABLE TRIGGER`
|
||||
|
||||
This is the default approach. For that use:
|
||||
|
||||
```go
|
||||
&testfixtures.PostgreSQL{}
|
||||
```
|
||||
|
||||
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.PostgreSQL{UseAlterConstraint: true}
|
||||
```
|
||||
|
||||
### 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.MySQL{}
|
||||
```
|
||||
|
||||
### 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.SQLite{}
|
||||
```
|
||||
|
||||
### 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.SQLServer{}
|
||||
```
|
||||
|
||||
### Oracle
|
||||
|
||||
Oracle is supported as well. Use:
|
||||
|
||||
```go
|
||||
&testfixtures.Oracle{}
|
||||
```
|
||||
|
||||
## Generation fixtures for a existent database (experimental)
|
||||
|
||||
The following code will generate a YAML file for each table of the database in
|
||||
the given folder. It may be useful to boostrap a test scenario from a sample
|
||||
database of your app.
|
||||
|
||||
```go
|
||||
err := testfixtures.GenerateFixtures(db, &testfixtures.PostgreSQL{}, "testdata/fixtures")
|
||||
if err != nil {
|
||||
log.Fatalf("Error generating fixtures: %v", err)
|
||||
}
|
||||
```
|
||||
|
||||
> This was thought to run in small sample databases. It will likely break
|
||||
if run in a production/big database.
|
||||
|
||||
## Contributing
|
||||
|
||||
Tests were written to ensure everything work as expected. You can run the tests
|
||||
with:
|
||||
|
||||
```bash
|
||||
# running tests for PostgreSQL
|
||||
go test -tags postgresql
|
||||
|
||||
# running test for MySQL
|
||||
go test -tags mysql
|
||||
|
||||
# running tests for SQLite
|
||||
go test -tags sqlite
|
||||
|
||||
# running tests for SQL Server
|
||||
go test -tags sqlserver
|
||||
|
||||
# running tests for Oracle
|
||||
go test -tags oracle
|
||||
|
||||
# running test for multiple databases at once
|
||||
go test -tags 'sqlite postgresql mysql'
|
||||
|
||||
# running tests + benchmark
|
||||
go test -v -bench=. -tags postgresql
|
||||
```
|
||||
|
||||
Travis runs tests for PostgreSQL, MySQL and SQLite.
|
||||
|
||||
To set the connection string of tests for each database, edit the `.env`
|
||||
file, but do not include the changes a in pull request.
|
||||
|
||||
## Changes in v2
|
||||
|
||||
A context was created to allow cache of some SQL statements. See in the
|
||||
documentation above how to use it.
|
||||
|
||||
The helpers were renamed to have a smaller name:
|
||||
|
||||
```go
|
||||
PostgreSQLHelper{} -> PostgreSQL{}
|
||||
MySQLHelper{} -> MySQL{}
|
||||
SQLiteHelper{} -> SQLite{}
|
||||
SQLServerHelper{} -> SQLServer{}
|
||||
OracleHelper{} -> Oracle{}
|
||||
```
|
||||
|
||||
The old functions and helpers are still available for backward compatibility.
|
||||
See the file [deprecated.go](https://github.com/go-testfixtures/testfixtures/blob/master/deprecated.go)
|
||||
|
||||
## 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
|
||||
|
||||
There's also these other implementations of test fixtures for Go:
|
||||
|
||||
- [go-fixtures][gofixtures]: Django style fixtures for Go
|
||||
- [mongofixtures][mongofixtures]: Fixtures for MongoDB
|
||||
- [fixturer][fixturer]: Another fixture loader supporting MySQL
|
||||
|
||||
[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
|
||||
[gofixtures]: https://github.com/AreaHQ/go-fixtures
|
||||
[mongofixtures]: https://github.com/OwlyCode/mongofixtures
|
||||
[fixturer]: https://github.com/44hapa/fixturer
|
||||
[factorygo]: https://github.com/bluele/factory-go
|
||||
[dbcleaner]: https://github.com/khaiql/dbcleaner
|
62
vendor/gopkg.in/testfixtures.v2/Taskfile.yml
generated
vendored
Normal file
62
vendor/gopkg.in/testfixtures.v2/Taskfile.yml
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
# github.com/go-task/task
|
||||
|
||||
dl-deps:
|
||||
desc: Download cli deps
|
||||
cmds:
|
||||
- go get -u github.com/golang/lint/golint
|
||||
- go get -u github.com/go-task/task/cmd/task
|
||||
|
||||
lint:
|
||||
desc: Runs golint
|
||||
cmds:
|
||||
- golint .
|
||||
|
||||
test-free:
|
||||
desc: Test free databases (PG, MySQL and SQLite)
|
||||
cmds:
|
||||
- task: test-pg
|
||||
- task: test-mysql
|
||||
- task: test-sqlite
|
||||
|
||||
test-all:
|
||||
desc: Test all databases (PG, MySQL, SQLite, SQLServer and Oracle)
|
||||
cmds:
|
||||
- task: test-pg
|
||||
- task: test-mysql
|
||||
- task: test-sqlite
|
||||
- task: test-sqlserver
|
||||
- task: test-oracle
|
||||
|
||||
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-oracle:
|
||||
desc: Test Oracle
|
||||
cmds:
|
||||
- task: test-db
|
||||
vars: {DATABASE: oracle}
|
||||
|
||||
test-db:
|
||||
cmds:
|
||||
- go test -v -tags {{.DATABASE}}
|
75
vendor/gopkg.in/testfixtures.v2/deprecated.go
generated
vendored
Normal file
75
vendor/gopkg.in/testfixtures.v2/deprecated.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
package testfixtures
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type (
|
||||
// DataBaseHelper is the helper interface
|
||||
// Deprecated: Use Helper instead
|
||||
DataBaseHelper Helper
|
||||
|
||||
// PostgreSQLHelper is the PostgreSQL helper
|
||||
// Deprecated: Use PostgreSQL{} instead
|
||||
PostgreSQLHelper struct {
|
||||
PostgreSQL
|
||||
UseAlterConstraint bool
|
||||
}
|
||||
|
||||
// MySQLHelper is the MySQL helper
|
||||
// Deprecated: Use MySQL{} instead
|
||||
MySQLHelper struct {
|
||||
MySQL
|
||||
}
|
||||
|
||||
// SQLiteHelper is the SQLite helper
|
||||
// Deprecated: Use SQLite{} instead
|
||||
SQLiteHelper struct {
|
||||
SQLite
|
||||
}
|
||||
|
||||
// SQLServerHelper is the SQLServer helper
|
||||
// Deprecated: Use SQLServer{} instead
|
||||
SQLServerHelper struct {
|
||||
SQLServer
|
||||
}
|
||||
|
||||
// OracleHelper is the Oracle helper
|
||||
// Deprecated: Use Oracle{} instead
|
||||
OracleHelper struct {
|
||||
Oracle
|
||||
}
|
||||
)
|
||||
|
||||
func (h *PostgreSQLHelper) disableReferentialIntegrity(db *sql.DB, loadFn loadFunction) error {
|
||||
h.PostgreSQL.UseAlterConstraint = h.UseAlterConstraint
|
||||
return h.PostgreSQL.disableReferentialIntegrity(db, loadFn)
|
||||
}
|
||||
|
||||
// LoadFixtureFiles load all specified fixtures files into database:
|
||||
// LoadFixtureFiles(db, &PostgreSQL{},
|
||||
// "fixtures/customers.yml", "fixtures/orders.yml")
|
||||
// // add as many files you want
|
||||
//
|
||||
// Deprecated: Use NewFiles() and Load() instead.
|
||||
func LoadFixtureFiles(db *sql.DB, helper Helper, files ...string) error {
|
||||
c, err := NewFiles(db, helper, files...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Load()
|
||||
}
|
||||
|
||||
// LoadFixtures loads all fixtures in a given folder into the database:
|
||||
// LoadFixtures("myfixturesfolder", db, &PostgreSQL{})
|
||||
//
|
||||
// Deprecated: Use NewFolder() and Load() instead.
|
||||
func LoadFixtures(folderName string, db *sql.DB, helper Helper) error {
|
||||
c, err := NewFolder(db, helper, folderName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Load()
|
||||
}
|
41
vendor/gopkg.in/testfixtures.v2/errors.go
generated
vendored
Normal file
41
vendor/gopkg.in/testfixtures.v2/errors.go
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
package testfixtures
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrWrongCastNotAMap is returned when a map is not a map[interface{}]interface{}
|
||||
ErrWrongCastNotAMap = errors.New("Could not cast record: not a map[interface{}]interface{}")
|
||||
|
||||
// ErrFileIsNotSliceOrMap is returned the the fixture file is not a slice or map.
|
||||
ErrFileIsNotSliceOrMap = errors.New("The fixture file is not a slice or map")
|
||||
|
||||
// ErrKeyIsNotString is returned when a record is not of type string
|
||||
ErrKeyIsNotString = errors.New("Record map key is not string")
|
||||
|
||||
// ErrNotTestDatabase is returned when the database name doesn't contains "test"
|
||||
ErrNotTestDatabase = errors.New(`Loading aborted because the database name does not contains "test"`)
|
||||
)
|
||||
|
||||
// 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,
|
||||
)
|
||||
}
|
110
vendor/gopkg.in/testfixtures.v2/generate.go
generated
vendored
Normal file
110
vendor/gopkg.in/testfixtures.v2/generate.go
generated
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
package testfixtures
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"unicode/utf8"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// TableInfo is settings for generating a fixture for table.
|
||||
type TableInfo struct {
|
||||
Name string // Table name
|
||||
Where string // A condition for extracting records. If this value is empty, extracts all records.
|
||||
}
|
||||
|
||||
func (ti *TableInfo) whereClause() string {
|
||||
if ti.Where == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(" WHERE %s", ti.Where)
|
||||
}
|
||||
|
||||
// GenerateFixtures generates fixtures for the current contents of a database, and saves
|
||||
// them to the specified directory
|
||||
func GenerateFixtures(db *sql.DB, helper Helper, dir string) error {
|
||||
tables, err := helper.tableNames(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, table := range tables {
|
||||
filename := path.Join(dir, table+".yml")
|
||||
if err := generateFixturesForTable(db, helper, &TableInfo{Name: table}, filename); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateFixturesForTables generates fixtures for the current contents of specified tables in a database, and saves
|
||||
// them to the specified directory
|
||||
func GenerateFixturesForTables(db *sql.DB, tables []*TableInfo, helper Helper, dir string) error {
|
||||
for _, table := range tables {
|
||||
filename := path.Join(dir, table.Name+".yml")
|
||||
if err := generateFixturesForTable(db, helper, table, filename); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateFixturesForTable(db *sql.DB, h Helper, table *TableInfo, filename string) error {
|
||||
query := fmt.Sprintf("SELECT * FROM %s%s", h.quoteKeyword(table.Name), table.whereClause())
|
||||
rows, err := db.Query(query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
columns, err := rows.Columns()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fixtures := make([]interface{}, 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(map[string]interface{}, len(entries))
|
||||
for i, column := range columns {
|
||||
entryMap[column] = convertValue(entries[i])
|
||||
}
|
||||
fixtures = append(fixtures, entryMap)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
marshaled, err := yaml.Marshal(fixtures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.Write(marshaled)
|
||||
return err
|
||||
}
|
||||
|
||||
func convertValue(value interface{}) interface{} {
|
||||
switch v := value.(type) {
|
||||
case []byte:
|
||||
if utf8.Valid(v) {
|
||||
return string(v)
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
63
vendor/gopkg.in/testfixtures.v2/helper.go
generated
vendored
Normal file
63
vendor/gopkg.in/testfixtures.v2/helper.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
package testfixtures
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
paramTypeDollar = iota + 1
|
||||
paramTypeQuestion
|
||||
paramTypeColon
|
||||
)
|
||||
|
||||
type loadFunction func(tx *sql.Tx) error
|
||||
|
||||
// Helper is the generic interface for the database helper
|
||||
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
|
||||
}
|
||||
|
||||
var (
|
||||
_ Helper = &MySQL{}
|
||||
_ Helper = &PostgreSQL{}
|
||||
_ Helper = &SQLite{}
|
||||
_ Helper = &Oracle{}
|
||||
_ 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
|
||||
}
|
44
vendor/gopkg.in/testfixtures.v2/json.go
generated
vendored
Normal file
44
vendor/gopkg.in/testfixtures.v2/json.go
generated
vendored
Normal file
@ -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
|
||||
}
|
133
vendor/gopkg.in/testfixtures.v2/mysql.go
generated
vendored
Normal file
133
vendor/gopkg.in/testfixtures.v2/mysql.go
generated
vendored
Normal file
@ -0,0 +1,133 @@
|
||||
package testfixtures
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// MySQL is the MySQL helper for this package
|
||||
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=?;
|
||||
`
|
||||
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) {
|
||||
// re-enable after load
|
||||
defer func() {
|
||||
if _, err2 := db.Exec("SET FOREIGN_KEY_CHECKS = 1"); err2 != nil && err == nil {
|
||||
err = err2
|
||||
}
|
||||
}()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if err = loadFn(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
19
vendor/gopkg.in/testfixtures.v2/options.go
generated
vendored
Normal file
19
vendor/gopkg.in/testfixtures.v2/options.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
package testfixtures
|
||||
|
||||
var (
|
||||
skipDatabaseNameCheck bool
|
||||
resetSequencesTo int64 = 10000
|
||||
)
|
||||
|
||||
// SkipDatabaseNameCheck If true, loading fixtures will not check if the database
|
||||
// name constaint "test". Use with caution!
|
||||
func SkipDatabaseNameCheck(value bool) {
|
||||
skipDatabaseNameCheck = value
|
||||
}
|
||||
|
||||
// ResetSequencesTo sets the value the sequences will be reset to.
|
||||
// This is used by PostgreSQL and Oracle.
|
||||
// Defaults to 10000.
|
||||
func ResetSequencesTo(value int64) {
|
||||
resetSequencesTo = value
|
||||
}
|
171
vendor/gopkg.in/testfixtures.v2/oracle.go
generated
vendored
Normal file
171
vendor/gopkg.in/testfixtures.v2/oracle.go
generated
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
package testfixtures
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Oracle is the Oracle database helper for this package
|
||||
type Oracle struct {
|
||||
baseHelper
|
||||
|
||||
enabledConstraints []oracleConstraint
|
||||
sequences []string
|
||||
}
|
||||
|
||||
type oracleConstraint struct {
|
||||
tableName string
|
||||
constraintName string
|
||||
}
|
||||
|
||||
func (h *Oracle) init(db *sql.DB) error {
|
||||
var err error
|
||||
|
||||
h.enabledConstraints, err = h.getEnabledConstraints(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.sequences, err = h.getSequences(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*Oracle) paramType() int {
|
||||
return paramTypeColon
|
||||
}
|
||||
|
||||
func (*Oracle) quoteKeyword(str string) string {
|
||||
return fmt.Sprintf("\"%s\"", strings.ToUpper(str))
|
||||
}
|
||||
|
||||
func (*Oracle) databaseName(q queryable) (string, error) {
|
||||
var dbName string
|
||||
err := q.QueryRow("SELECT user FROM DUAL").Scan(&dbName)
|
||||
return dbName, err
|
||||
}
|
||||
|
||||
func (*Oracle) tableNames(q queryable) ([]string, error) {
|
||||
query := `
|
||||
SELECT TABLE_NAME
|
||||
FROM USER_TABLES
|
||||
`
|
||||
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 (*Oracle) getEnabledConstraints(q queryable) ([]oracleConstraint, error) {
|
||||
var constraints []oracleConstraint
|
||||
rows, err := q.Query(`
|
||||
SELECT table_name, constraint_name
|
||||
FROM user_constraints
|
||||
WHERE constraint_type = 'R'
|
||||
AND status = 'ENABLED'
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var constraint oracleConstraint
|
||||
rows.Scan(&constraint.tableName, &constraint.constraintName)
|
||||
constraints = append(constraints, constraint)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return constraints, nil
|
||||
}
|
||||
|
||||
func (*Oracle) getSequences(q queryable) ([]string, error) {
|
||||
var sequences []string
|
||||
rows, err := q.Query("SELECT sequence_name FROM user_sequences")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
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 (h *Oracle) resetSequences(q queryable) error {
|
||||
for _, sequence := range h.sequences {
|
||||
_, err := q.Exec(fmt.Sprintf("DROP SEQUENCE %s", h.quoteKeyword(sequence)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = q.Exec(fmt.Sprintf("CREATE SEQUENCE %s START WITH %d", h.quoteKeyword(sequence), resetSequencesTo))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Oracle) disableReferentialIntegrity(db *sql.DB, loadFn loadFunction) (err error) {
|
||||
// re-enable after load
|
||||
defer func() {
|
||||
for _, c := range h.enabledConstraints {
|
||||
_, err2 := db.Exec(fmt.Sprintf("ALTER TABLE %s ENABLE CONSTRAINT %s", h.quoteKeyword(c.tableName), h.quoteKeyword(c.constraintName)))
|
||||
if err2 != nil && err == nil {
|
||||
err = err2
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// disable foreign keys
|
||||
for _, c := range h.enabledConstraints {
|
||||
_, err := db.Exec(fmt.Sprintf("ALTER TABLE %s DISABLE CONSTRAINT %s", h.quoteKeyword(c.tableName), h.quoteKeyword(c.constraintName)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
if err = loadFn(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return h.resetSequences(db)
|
||||
}
|
291
vendor/gopkg.in/testfixtures.v2/postgresql.go
generated
vendored
Normal file
291
vendor/gopkg.in/testfixtures.v2/postgresql.go
generated
vendored
Normal file
@ -0,0 +1,291 @@
|
||||
package testfixtures
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PostgreSQL is the PG helper for this package
|
||||
type PostgreSQL struct {
|
||||
baseHelper
|
||||
|
||||
// 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.
|
||||
UseAlterConstraint bool
|
||||
|
||||
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%';
|
||||
`
|
||||
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'
|
||||
`
|
||||
|
||||
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'
|
||||
`
|
||||
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
|
||||
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 {
|
||||
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, ".")
|
||||
}
|
76
vendor/gopkg.in/testfixtures.v2/sqlite.go
generated
vendored
Normal file
76
vendor/gopkg.in/testfixtures.v2/sqlite.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
package testfixtures
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// SQLite is the SQLite Helper for this package
|
||||
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()
|
||||
}
|
122
vendor/gopkg.in/testfixtures.v2/sqlserver.go
generated
vendored
Normal file
122
vendor/gopkg.in/testfixtures.v2/sqlserver.go
generated
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
package testfixtures
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// SQLServer is the helper for SQL Server for this package.
|
||||
// SQL Server >= 2008 is required.
|
||||
type SQLServer struct {
|
||||
baseHelper
|
||||
|
||||
tables []string
|
||||
}
|
||||
|
||||
func (h *SQLServer) init(db *sql.DB) error {
|
||||
var err error
|
||||
|
||||
h.tables, err = h.tableNames(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*SQLServer) paramType() int {
|
||||
return paramTypeQuestion
|
||||
}
|
||||
|
||||
func (*SQLServer) quoteKeyword(str string) string {
|
||||
return fmt.Sprintf("[%s]", str)
|
||||
}
|
||||
|
||||
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_name FROM information_schema.tables")
|
||||
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 (*SQLServer) tableHasIdentityColumn(q queryable, tableName string) bool {
|
||||
sql := `
|
||||
SELECT COUNT(*)
|
||||
FROM SYS.IDENTITY_COLUMNS
|
||||
WHERE OBJECT_NAME(OBJECT_ID) = ?
|
||||
`
|
||||
var count int
|
||||
q.QueryRow(sql, tableName).Scan(&count)
|
||||
return count > 0
|
||||
|
||||
}
|
||||
|
||||
func (h *SQLServer) whileInsertOnTable(tx *sql.Tx, tableName string, fn func() error) (err error) {
|
||||
if h.tableHasIdentityColumn(tx, tableName) {
|
||||
defer func() {
|
||||
_, err2 := tx.Exec(fmt.Sprintf("SET IDENTITY_INSERT %s OFF", h.quoteKeyword(tableName)))
|
||||
if err2 != nil && err == nil {
|
||||
err = err2
|
||||
}
|
||||
}()
|
||||
|
||||
_, err := tx.Exec(fmt.Sprintf("SET IDENTITY_INSERT %s ON", h.quoteKeyword(tableName)))
|
||||
if err != nil {
|
||||
return 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()
|
||||
}
|
294
vendor/gopkg.in/testfixtures.v2/testfixtures.go
generated
vendored
Normal file
294
vendor/gopkg.in/testfixtures.v2/testfixtures.go
generated
vendored
Normal file
@ -0,0 +1,294 @@
|
||||
package testfixtures
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// Context holds the fixtures to be loaded in the database.
|
||||
type Context struct {
|
||||
db *sql.DB
|
||||
helper Helper
|
||||
fixturesFiles []*fixtureFile
|
||||
}
|
||||
|
||||
type fixtureFile struct {
|
||||
path string
|
||||
fileName string
|
||||
content []byte
|
||||
insertSQLs []insertSQL
|
||||
}
|
||||
|
||||
type insertSQL struct {
|
||||
sql string
|
||||
params []interface{}
|
||||
}
|
||||
|
||||
var (
|
||||
dbnameRegexp = regexp.MustCompile("(?i)test")
|
||||
)
|
||||
|
||||
// NewFolder creates a context for all fixtures in a given folder into the database:
|
||||
// NewFolder(db, &PostgreSQL{}, "my/fixtures/folder")
|
||||
func NewFolder(db *sql.DB, helper Helper, folderName string) (*Context, error) {
|
||||
fixtures, err := fixturesFromFolder(folderName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := newContext(db, helper, fixtures)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// NewFiles creates a context for all specified fixtures files into database:
|
||||
// NewFiles(db, &PostgreSQL{},
|
||||
// "fixtures/customers.yml",
|
||||
// "fixtures/orders.yml"
|
||||
// // add as many files you want
|
||||
// )
|
||||
func NewFiles(db *sql.DB, helper Helper, fileNames ...string) (*Context, error) {
|
||||
fixtures, err := fixturesFromFiles(fileNames...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := newContext(db, helper, fixtures)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func newContext(db *sql.DB, helper Helper, fixtures []*fixtureFile) (*Context, error) {
|
||||
c := &Context{
|
||||
db: db,
|
||||
helper: helper,
|
||||
fixturesFiles: fixtures,
|
||||
}
|
||||
|
||||
if err := c.helper.init(c.db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := c.buildInsertSQLs(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Load wipes and after load all fixtures in the database.
|
||||
// if err := fixtures.Load(); err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
func (c *Context) Load() error {
|
||||
if !skipDatabaseNameCheck {
|
||||
dbName, err := c.helper.databaseName(c.db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !dbnameRegexp.MatchString(dbName) {
|
||||
return ErrNotTestDatabase
|
||||
}
|
||||
}
|
||||
|
||||
err := c.helper.disableReferentialIntegrity(c.db, func(tx *sql.Tx) error {
|
||||
for _, file := range c.fixturesFiles {
|
||||
modified, err := c.helper.isTableModified(tx, file.fileNameWithoutExtension())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !modified {
|
||||
continue
|
||||
}
|
||||
if err := file.delete(tx, c.helper); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.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 c.helper.afterLoad(c.db)
|
||||
}
|
||||
|
||||
func (c *Context) buildInsertSQLs() error {
|
||||
for _, f := range c.fixturesFiles {
|
||||
var records interface{}
|
||||
if err := yaml.Unmarshal(f.content, &records); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch records := records.(type) {
|
||||
case []interface{}:
|
||||
for _, record := range records {
|
||||
recordMap, ok := record.(map[interface{}]interface{})
|
||||
if !ok {
|
||||
return ErrWrongCastNotAMap
|
||||
}
|
||||
|
||||
sql, values, err := f.buildInsertSQL(c.helper, recordMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.insertSQLs = append(f.insertSQLs, insertSQL{sql, values})
|
||||
}
|
||||
case map[interface{}]interface{}:
|
||||
for _, record := range records {
|
||||
recordMap, ok := record.(map[interface{}]interface{})
|
||||
if !ok {
|
||||
return ErrWrongCastNotAMap
|
||||
}
|
||||
|
||||
sql, values, err := f.buildInsertSQL(c.helper, recordMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.insertSQLs = append(f.insertSQLs, insertSQL{sql, values})
|
||||
}
|
||||
default:
|
||||
return ErrFileIsNotSliceOrMap
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
_, err := tx.Exec(fmt.Sprintf("DELETE FROM %s", h.quoteKeyword(f.fileNameWithoutExtension())))
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *fixtureFile) buildInsertSQL(h Helper, record map[interface{}]interface{}) (sqlStr string, values []interface{}, err error) {
|
||||
var (
|
||||
sqlColumns []string
|
||||
sqlValues []string
|
||||
i = 1
|
||||
)
|
||||
for key, value := range record {
|
||||
keyStr, ok := key.(string)
|
||||
if !ok {
|
||||
err = ErrKeyIsNotString
|
||||
return
|
||||
}
|
||||
|
||||
sqlColumns = append(sqlColumns, h.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 := tryStrToDate(v); err == nil {
|
||||
value = t
|
||||
}
|
||||
case []interface{}, map[interface{}]interface{}:
|
||||
value = recursiveToJSON(v)
|
||||
}
|
||||
|
||||
switch h.paramType() {
|
||||
case paramTypeDollar:
|
||||
sqlValues = append(sqlValues, fmt.Sprintf("$%d", i))
|
||||
case paramTypeQuestion:
|
||||
sqlValues = append(sqlValues, "?")
|
||||
case paramTypeColon:
|
||||
sqlValues = append(sqlValues, fmt.Sprintf(":%d", i))
|
||||
}
|
||||
|
||||
values = append(values, value)
|
||||
i++
|
||||
}
|
||||
|
||||
sqlStr = fmt.Sprintf(
|
||||
"INSERT INTO %s (%s) VALUES (%s)",
|
||||
h.quoteKeyword(f.fileNameWithoutExtension()),
|
||||
strings.Join(sqlColumns, ", "),
|
||||
strings.Join(sqlValues, ", "),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func fixturesFromFolder(folderName string) ([]*fixtureFile, error) {
|
||||
var files []*fixtureFile
|
||||
fileinfos, err := ioutil.ReadDir(folderName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, fileinfo := range fileinfos {
|
||||
if !fileinfo.IsDir() && filepath.Ext(fileinfo.Name()) == ".yml" {
|
||||
fixture := &fixtureFile{
|
||||
path: path.Join(folderName, fileinfo.Name()),
|
||||
fileName: fileinfo.Name(),
|
||||
}
|
||||
fixture.content, err = ioutil.ReadFile(fixture.path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files = append(files, fixture)
|
||||
}
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func fixturesFromFiles(fileNames ...string) ([]*fixtureFile, error) {
|
||||
var (
|
||||
fixtureFiles []*fixtureFile
|
||||
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, err
|
||||
}
|
||||
fixtureFiles = append(fixtureFiles, fixture)
|
||||
}
|
||||
|
||||
return fixtureFiles, nil
|
||||
}
|
34
vendor/gopkg.in/testfixtures.v2/time.go
generated
vendored
Normal file
34
vendor/gopkg.in/testfixtures.v2/time.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
package testfixtures
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"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",
|
||||
}
|
||||
|
||||
// ErrCouldNotConvertToTime is returns when a string is not a reconizable time format
|
||||
var ErrCouldNotConvertToTime = errors.New("Could not convert string to time")
|
||||
|
||||
func tryStrToDate(s string) (time.Time, error) {
|
||||
for _, f := range timeFormats {
|
||||
t, err := time.ParseInLocation(f, s, time.Local)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
return time.Time{}, ErrCouldNotConvertToTime
|
||||
}
|
Reference in New Issue
Block a user