diff --git a/.gitignore b/.gitignore index a8d0e64c672d2..b420fb1ebdaf3 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ _testmain.go coverage.out *.db +*.sqlite3 *.log /gitea diff --git a/cmd/web.go b/cmd/web.go index 36425b823d986..ce855cb38f9f5 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -105,7 +105,7 @@ go get -u %[1]s`, c.ImportPath, c.Version(), c.Expected) } // newMacaron initializes Macaron instance. -func newMacaron() *macaron.Macaron { +func NewMacaron() *macaron.Macaron { m := macaron.New() if !setting.DisableRouterLog { m.Use(macaron.Logger()) @@ -183,17 +183,6 @@ func newMacaron() *macaron.Macaron { }, })) m.Use(context.Contexter()) - return m -} - -func runWeb(ctx *cli.Context) error { - if ctx.IsSet("config") { - setting.CustomConf = ctx.String("config") - } - routers.GlobalInit() - checkVersion() - - m := newMacaron() reqSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: true}) ignSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: setting.Service.RequireSignInView}) @@ -625,6 +614,18 @@ func runWeb(ctx *cli.Context) error { // Not found handler. m.NotFound(routers.NotFound) + return m +} + +func runWeb(ctx *cli.Context) error { + if ctx.IsSet("config") { + setting.CustomConf = ctx.String("config") + } + routers.GlobalInit() + checkVersion() + + m := NewMacaron() + // Flag for port number in case first time run conflict. if ctx.IsSet("port") { setting.AppUrl = strings.Replace(setting.AppUrl, setting.HTTPPort, ctx.String("port"), 1) diff --git a/models/models.go b/models/models.go index d6c30e1013acc..45b54edc8aa72 100644 --- a/models/models.go +++ b/models/models.go @@ -57,6 +57,10 @@ var ( EnableTiDB bool ) +func Database() *sql.DB { + return x.DB().DB +} + func init() { tables = append(tables, new(User), new(PublicKey), new(AccessToken), diff --git a/models/repo.go b/models/repo.go index edf5de932980b..513c74b032233 100644 --- a/models/repo.go +++ b/models/repo.go @@ -144,7 +144,11 @@ func NewRepoContext() { log.Fatal(4, "Fail to execute 'git config --global core.quotepath false': %s", stderr) } - RemoveAllWithNotice("Clean up repository temporary data", filepath.Join(setting.AppDataPath, "tmp")) + // FIXME: skipping this for tests by now, because Gogs is not connected to a + // database at this point while running tests, so create notice will fail. + if setting.AppRunMode != setting.RUN_MODE_TEST { + RemoveAllWithNotice("Clean up repository temporary data", filepath.Join(setting.AppDataPath, "tmp")) + } } // Repository represents a git repository. diff --git a/modules/context/context.go b/modules/context/context.go index 9395880d02294..342e7d3254672 100644 --- a/modules/context/context.go +++ b/modules/context/context.go @@ -160,8 +160,17 @@ func Contexter() macaron.Handler { ctx.Data["PageStartTime"] = time.Now() - // Get user from session if logined. - ctx.User, ctx.IsBasicAuth = auth.SignedInUser(ctx.Context, ctx.Session) + // just for unit tests, find user with cookie + if setting.AppRunMode == setting.RUN_MODE_TEST { + user, err := models.GetUserByID(ctx.GetCookieInt64("user_id")) + if err == nil { + ctx.User = user + } + ctx.IsBasicAuth = false + } else { + // Get user from session if logined. + ctx.User, ctx.IsBasicAuth = auth.SignedInUser(ctx.Context, ctx.Session) + } if ctx.User != nil { ctx.IsSigned = true diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 9aade4e8348db..e9f29b5684b64 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -37,6 +37,10 @@ const ( HTTPS Scheme = "https" FCGI Scheme = "fcgi" UNIX_SOCKET Scheme = "unix" + + RUN_MODE_PROD = "prod" + RUN_MODE_DEV = "dev" + RUN_MODE_TEST = "test" ) type LandingPage string @@ -60,6 +64,8 @@ var ( AppPath string AppDataPath string + AppRunMode string + // Server settings Protocol Scheme Domain string @@ -263,6 +269,10 @@ var ( HasRobotsTxt bool ) +func GiteaPath() string { + return filepath.Join(os.Getenv("GOPATH"), "src/github.com/go-gitea/gitea") +} + // DateLang transforms standard language locale name to corresponding value in datetime plugin. func DateLang(lang string) string { name, ok := dateLangs[lang] @@ -361,7 +371,6 @@ func NewContext() { please consider changing to GITEA_CUSTOM`) } } - if len(CustomConf) == 0 { CustomConf = CustomPath + "/conf/app.ini" } @@ -390,6 +399,7 @@ please consider changing to GITEA_CUSTOM`) if AppUrl[len(AppUrl)-1] != '/' { AppUrl += "/" } + AppRunMode = Cfg.Section("").Key("RUN_MODE").In(RUN_MODE_PROD, []string{RUN_MODE_PROD, RUN_MODE_DEV, RUN_MODE_TEST}) // Check if has app suburl. url, err := url.Parse(AppUrl) @@ -498,8 +508,8 @@ please consider changing to GITEA_CUSTOM`) }[Cfg.Section("time").Key("FORMAT").MustString("RFC1123")] RunUser = Cfg.Section("").Key("RUN_USER").String() - // Does not check run user when the install lock is off. - if InstallLock { + // Does not check run user when the install lock is off or is running tests + if InstallLock && AppRunMode != RUN_MODE_TEST { currentUser, match := IsRunUserMatchCurrentUser(RunUser) if !match { log.Fatal(4, "Expect user '%s' but current user is: %s", RunUser, currentUser) diff --git a/routers/api/v1/repo/issue_test.go b/routers/api/v1/repo/issue_test.go new file mode 100644 index 0000000000000..4bff0a992099f --- /dev/null +++ b/routers/api/v1/repo/issue_test.go @@ -0,0 +1,75 @@ +package repo_test + +import ( + "encoding/json" + "net/http" + "os" + "testing" + + "github.com/go-gitea/gitea/models" + "github.com/go-gitea/gitea/testutil" + api "github.com/go-gitea/go-sdk/gitea" + + "github.com/stretchr/testify/assert" +) + +func TestMain(m *testing.M) { + testutil.TestGlobalInit() + + os.Exit(m.Run()) +} + +func TestIssueIndex(t *testing.T) { + testutil.PrepareTestDatabase() + + w, r := testutil.NewTestContext("GET", "/api/v1/repos/user1/foo/issues", "", nil, "1") + testutil.ServeHTTP(w, r) + assert.Equal(t, http.StatusOK, w.Code) +} + +func TestIssueShow(t *testing.T) { + testutil.PrepareTestDatabase() + + w, r := testutil.NewTestContext("GET", "/api/v1/repos/user1/foo/issues/1", "", nil, "1") + testutil.ServeHTTP(w, r) + assert.Equal(t, http.StatusOK, w.Code) + + issue := new(api.Issue) + err := json.Unmarshal(w.Body.Bytes(), &issue) + assert.NoError(t, err) + assert.Equal(t, "Title", issue.Title) + assert.Equal(t, "Content", issue.Body) + assert.Equal(t, "user1", issue.Poster.UserName) +} + +func TestCreate(t *testing.T) { + testutil.PrepareTestDatabase() + + bytes, _ := json.Marshal(api.Issue{ + Title: "A issue title", + Body: "Please fix", + }) + count := testutil.TableCount("issue") + w, r := testutil.NewTestContext("POST", "/api/v1/repos/user1/foo/issues", testutil.CONTENT_TYPE_JSON, bytes, "1") + testutil.ServeHTTP(w, r) + assert.Equal(t, http.StatusCreated, w.Code) + assert.Equal(t, count+1, testutil.TableCount("issue")) + issue, _ := models.GetIssueByID(testutil.LastId("issue")) + assert.Equal(t, "A issue title", issue.Title) + assert.Equal(t, "Please fix", issue.Content) +} + +func TestEdit(t *testing.T) { + testutil.PrepareTestDatabase() + + bytes, _ := json.Marshal(api.Issue{ + Title: "Edited title", + Body: "Edited content", + }) + w, r := testutil.NewTestContext("PATCH", "/api/v1/repos/user1/foo/issues/1", testutil.CONTENT_TYPE_JSON, bytes, "1") + testutil.ServeHTTP(w, r) + assert.Equal(t, http.StatusCreated, w.Code) + issue, _ := models.GetIssueByID(1) + assert.Equal(t, "Edited title", issue.Title) + assert.Equal(t, "Edited content", issue.Content) +} diff --git a/testdata/app_test.ini b/testdata/app_test.ini new file mode 100644 index 0000000000000..16169f2b6df1d --- /dev/null +++ b/testdata/app_test.ini @@ -0,0 +1,31 @@ +# THIS FILE EXISTS TO BE USED TO UNIT TESTS +# DON'T CHANGE IT. THIS IS NOT THE FILE GOGS WILL USE IN PRODUCTION + +APP_NAME = Gogs: Go Git Service +RUN_MODE = test + +[database] +DB_TYPE = sqlite3 +PATH = testdata/gitea_test.sqlite3 + +[repository] +ROOT = testdata/gitea-test-repositories + +[mailer] +ENABLED = false + +[service] + +[picture] +DISABLE_GRAVATAR = true + +[session] +PROVIDER = memory + +[log] +MODE = file +LEVEL = Info + +[security] +INSTALL_LOCK = true +SECRET_KEY = foobar diff --git a/testdata/fixtures/issue.yml b/testdata/fixtures/issue.yml new file mode 100644 index 0000000000000..0aed12c7e18c0 --- /dev/null +++ b/testdata/fixtures/issue.yml @@ -0,0 +1,19 @@ +- + id: 1 + repo_id: 1 + index: 1 + name: Title + poster_id: 1 + content: Content + is_pull: false + is_closed: false + +- + id: 2 + repo_id: 1 + index: 2 + name: Title + poster_id: 1 + content: Content + is_pull: false + is_closed: false diff --git a/testdata/fixtures/repository.yml b/testdata/fixtures/repository.yml new file mode 100644 index 0000000000000..7a9e047962fce --- /dev/null +++ b/testdata/fixtures/repository.yml @@ -0,0 +1,24 @@ +- + id: 1 + owner_id: 1 + lower_name: foo + name: foo + num_issues: 2 + +- + id: 2 + owner_id: 2 + lower_name: bar + name: bar + +- + id: 3 + owner_id: 3 + lower_name: foo + name: foo + +- + id: 4 + owner_id: 4 + lower_name: bar + name: bar diff --git a/testdata/fixtures/user.yml b/testdata/fixtures/user.yml new file mode 100644 index 0000000000000..71b8cc9fd28cd --- /dev/null +++ b/testdata/fixtures/user.yml @@ -0,0 +1,19 @@ +- + id: 1 + lower_name: user1 + name: user1 + full_name: User 1 + email: user1@gogs.io + passwd: "" + avatar: "" + avatar_email: "" + +- + id: 2 + lower_name: user2 + name: user2 + full_name: User 2 + email: user2@gogs.io + passwd: "" + avatar: "" + avatar_email: "" diff --git a/testutil/routers.go b/testutil/routers.go new file mode 100644 index 0000000000000..81ec4fbefa8bb --- /dev/null +++ b/testutil/routers.go @@ -0,0 +1,92 @@ +package testutil + +import ( + "bytes" + "fmt" + "io" + "log" + "net/http" + "net/http/httptest" + "net/url" + "path/filepath" + + "github.com/go-gitea/gitea/cmd" + "github.com/go-gitea/gitea/models" + "github.com/go-gitea/gitea/modules/setting" + "github.com/go-gitea/gitea/routers" + + "gopkg.in/macaron.v1" + "gopkg.in/testfixtures.v2" +) + +const ( + CONTENT_TYPE_FORM = "application/x-www-form-urlencoded" + CONTENT_TYPE_JSON = "application/json" +) + +var ( + theMacaron *macaron.Macaron + fixtures *testfixtures.Context +) + +func getMacaron() *macaron.Macaron { + if theMacaron == nil { + theMacaron = cmd.NewMacaron() + } + return theMacaron +} + +func ServeHTTP(w http.ResponseWriter, r *http.Request) { + getMacaron().ServeHTTP(w, r) +} + +func NewTestContext(method, path, contentType string, body []byte, userId string) (w *httptest.ResponseRecorder, r *http.Request) { + var bodyReader io.Reader + if body != nil { + bodyReader = bytes.NewReader(body) + } + + w = httptest.NewRecorder() + r, _ = http.NewRequest(method, path, bodyReader) + if len(contentType) > 0 { + r.Header.Set("Content-Type", contentType) + if contentType == CONTENT_TYPE_FORM { + r.PostForm = url.Values{} + } + } + if len(userId) > 0 { + r.AddCookie(&http.Cookie{Name: "user_id", Value: userId}) + } + return +} + +func TableCount(tableName string) (count int64) { + models.Database().QueryRow(fmt.Sprintf("SELECT COUNT(*) FROM \"%s\"", tableName)).Scan(&count) + return +} + +func LastId(tableName string) (lastId int64) { + models.Database().QueryRow(fmt.Sprintf("SELECT MAX(id) FROM \"%s\"", tableName)).Scan(&lastId) + return +} + +func TestGlobalInit() { + setting.CustomConf = filepath.Join(setting.GiteaPath(), "testdata/app_test.ini") + routers.GlobalInit() + + var err error + fixtures, err = testfixtures.NewFolder( + models.Database(), + &testfixtures.SQLite{}, + filepath.Join(setting.GiteaPath(), "testdata/fixtures"), + ) + if err != nil { + log.Fatal(err) + } +} + +func PrepareTestDatabase() { + if err := fixtures.Load(); err != nil { + log.Fatal(err) + } +} diff --git a/vendor/gopkg.in/testfixtures.v2/LICENSE b/vendor/gopkg.in/testfixtures.v2/LICENSE new file mode 100644 index 0000000000000..894ee77ed2f71 --- /dev/null +++ b/vendor/gopkg.in/testfixtures.v2/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/gopkg.in/testfixtures.v2/README.md b/vendor/gopkg.in/testfixtures.v2/README.md new file mode 100644 index 0000000000000..c66b23af97dd5 --- /dev/null +++ b/vendor/gopkg.in/testfixtures.v2/README.md @@ -0,0 +1,323 @@ +# Go Test Fixtures + +[![license](https://img.shields.io/github/license/mashape/apistatus.svg?maxAge=2592000)](https://github.com/go-testfixtures/testfixtures/blob/master/LICENSE) +[![Join the chat at https://gitter.im/go-testfixtures/testfixtures](https://badges.gitter.im/go-testfixtures/testfixtures.svg)](https://gitter.im/go-testfixtures/testfixtures?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![GoDoc](https://godoc.org/gopkg.in/testfixtures.v1?status.svg)](https://godoc.org/gopkg.in/testfixtures.v1) +[![Build Status](https://travis-ci.org/go-testfixtures/testfixtures.svg?branch=master)](https://travis-ci.org/go-testfixtures/testfixtures) +[![Go Report Card](https://goreportcard.com/badge/github.com/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 to being catch 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 `.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: This post is awesome! + 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: Are you kidding me? + 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 + +# ... +``` + +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 + c, 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 + +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{} +``` + +## 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/LICENSE) + +## 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 + +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 diff --git a/vendor/gopkg.in/testfixtures.v2/deprecated.go b/vendor/gopkg.in/testfixtures.v2/deprecated.go new file mode 100644 index 0000000000000..b83eeef436f7e --- /dev/null +++ b/vendor/gopkg.in/testfixtures.v2/deprecated.go @@ -0,0 +1,59 @@ +package testfixtures + +import ( + "database/sql" +) + +type ( + DataBaseHelper Helper // Deprecated: Use Helper instead + + PostgreSQLHelper struct { // Deprecated: Use PostgreSQL{} instead + PostgreSQL + UseAlterConstraint bool + } + MySQLHelper struct { // Deprecated: Use MySQL{} instead + MySQL + } + SQLiteHelper struct { // Deprecated: Use SQLite{} instead + SQLite + } + SQLServerHelper struct { // Deprecated: Use SQLServer{} instead + SQLServer + } + OracleHelper struct { // Deprecated: Use Oracle{} instead + 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() +} diff --git a/vendor/gopkg.in/testfixtures.v2/helper.go b/vendor/gopkg.in/testfixtures.v2/helper.go new file mode 100644 index 0000000000000..f1c19f29d4e9f --- /dev/null +++ b/vendor/gopkg.in/testfixtures.v2/helper.go @@ -0,0 +1,38 @@ +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(*sql.DB) string + quoteKeyword(string) string + whileInsertOnTable(*sql.Tx, string, func() error) error +} + +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() +} diff --git a/vendor/gopkg.in/testfixtures.v2/mysql.go b/vendor/gopkg.in/testfixtures.v2/mysql.go new file mode 100644 index 0000000000000..3c96c1b293f98 --- /dev/null +++ b/vendor/gopkg.in/testfixtures.v2/mysql.go @@ -0,0 +1,45 @@ +package testfixtures + +import ( + "database/sql" + "fmt" +) + +// MySQL is the MySQL helper for this package +type MySQL struct { + baseHelper +} + +func (*MySQL) paramType() int { + return paramTypeQuestion +} + +func (*MySQL) quoteKeyword(str string) string { + return fmt.Sprintf("`%s`", str) +} + +func (*MySQL) databaseName(db *sql.DB) (dbName string) { + db.QueryRow("SELECT DATABASE()").Scan(&dbName) + return +} + +func (h *MySQL) disableReferentialIntegrity(db *sql.DB, loadFn loadFunction) error { + // re-enable after load + defer db.Exec("SET FOREIGN_KEY_CHECKS = 1") + + tx, err := db.Begin() + if err != nil { + return err + } + + if _, err = tx.Exec("SET FOREIGN_KEY_CHECKS = 0"); err != nil { + return err + } + + if err = loadFn(tx); err != nil { + tx.Rollback() + return err + } + + return tx.Commit() +} diff --git a/vendor/gopkg.in/testfixtures.v2/options.go b/vendor/gopkg.in/testfixtures.v2/options.go new file mode 100644 index 0000000000000..83199d4e26796 --- /dev/null +++ b/vendor/gopkg.in/testfixtures.v2/options.go @@ -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 +} diff --git a/vendor/gopkg.in/testfixtures.v2/oracle.go b/vendor/gopkg.in/testfixtures.v2/oracle.go new file mode 100644 index 0000000000000..59600ebfc5269 --- /dev/null +++ b/vendor/gopkg.in/testfixtures.v2/oracle.go @@ -0,0 +1,132 @@ +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(db *sql.DB) (dbName string) { + db.QueryRow("SELECT user FROM DUAL").Scan(&dbName) + return +} + +func (*Oracle) getEnabledConstraints(db *sql.DB) ([]oracleConstraint, error) { + constraints := make([]oracleConstraint, 0) + rows, err := db.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) + } + return constraints, nil +} + +func (*Oracle) getSequences(db *sql.DB) ([]string, error) { + sequences := make([]string, 0) + rows, err := db.Query("SELECT sequence_name FROM user_sequences") + if err != nil { + return nil, err + } + + defer rows.Close() + for rows.Next() { + var sequence string + rows.Scan(&sequence) + sequences = append(sequences, sequence) + } + return sequences, nil +} + +func (h *Oracle) resetSequences(db *sql.DB) error { + for _, sequence := range h.sequences { + _, err := db.Exec(fmt.Sprintf("DROP SEQUENCE %s", h.quoteKeyword(sequence))) + if err != nil { + return err + } + _, err = db.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) error { + // re-enable after load + defer func() { + for _, c := range h.enabledConstraints { + db.Exec(fmt.Sprintf("ALTER TABLE %s ENABLE CONSTRAINT %s", h.quoteKeyword(c.tableName), h.quoteKeyword(c.constraintName))) + } + }() + + // 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 + } + + if err = loadFn(tx); err != nil { + tx.Rollback() + return err + } + + if err = tx.Commit(); err != nil { + return err + } + + return h.resetSequences(db) +} diff --git a/vendor/gopkg.in/testfixtures.v2/postgresql.go b/vendor/gopkg.in/testfixtures.v2/postgresql.go new file mode 100644 index 0000000000000..ecc5a5cfa8c11 --- /dev/null +++ b/vendor/gopkg.in/testfixtures.v2/postgresql.go @@ -0,0 +1,211 @@ +package testfixtures + +import ( + "database/sql" + "fmt" +) + +// 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 +} + +type pgConstraint struct { + tableName string + constraintName string +} + +func (h *PostgreSQL) init(db *sql.DB) error { + var err error + + h.tables, err = h.getTables(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(db *sql.DB) (dbName string) { + db.QueryRow("SELECT current_database()").Scan(&dbName) + return +} + +func (h *PostgreSQL) getTables(db *sql.DB) ([]string, error) { + var tables []string + + sql := ` +SELECT table_name +FROM information_schema.tables +WHERE table_schema = 'public' + AND table_type = 'BASE TABLE'; +` + rows, err := db.Query(sql) + if err != nil { + return nil, err + } + + defer rows.Close() + for rows.Next() { + var table string + rows.Scan(&table) + tables = append(tables, table) + } + return tables, nil +} + +func (h *PostgreSQL) getSequences(db *sql.DB) ([]string, error) { + var sequences []string + + sql := "SELECT relname FROM pg_class WHERE relkind = 'S'" + rows, err := db.Query(sql) + 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) + } + return sequences, nil +} + +func (*PostgreSQL) getNonDeferrableConstraints(db *sql.DB) ([]pgConstraint, error) { + var constraints []pgConstraint + + sql := ` +SELECT table_name, constraint_name +FROM information_schema.table_constraints +WHERE constraint_type = 'FOREIGN KEY' + AND is_deferrable = 'NO'` + rows, err := db.Query(sql) + if err != nil { + return nil, err + } + + defer rows.Close() + for rows.Next() { + var constraint pgConstraint + err = rows.Scan(&constraint.tableName, &constraint.constraintName) + if err != nil { + return nil, err + } + constraints = append(constraints, constraint) + } + return constraints, nil +} + +func (h *PostgreSQL) disableTriggers(db *sql.DB, loadFn loadFunction) 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)) + } + db.Exec(sql) + }() + + 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) 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)) + } + db.Exec(sql) + }() + + 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 + } + + if _, err = tx.Exec("SET CONSTRAINTS ALL DEFERRED"); err != nil { + return nil + } + + if err = loadFn(tx); err != nil { + tx.Rollback() + return err + } + + return tx.Commit() +} + +func (h *PostgreSQL) disableReferentialIntegrity(db *sql.DB, loadFn loadFunction) error { + // ensure sequences being reset after load + defer h.resetSequences(db) + + if h.UseAlterConstraint { + return h.makeConstraintsDeferrable(db, loadFn) + } else { + 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 +} diff --git a/vendor/gopkg.in/testfixtures.v2/sqlite.go b/vendor/gopkg.in/testfixtures.v2/sqlite.go new file mode 100644 index 0000000000000..4d7fa4fdabf3b --- /dev/null +++ b/vendor/gopkg.in/testfixtures.v2/sqlite.go @@ -0,0 +1,40 @@ +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(db *sql.DB) (dbName string) { + var seq int + var main string + db.QueryRow("PRAGMA database_list").Scan(&seq, &main, &dbName) + dbName = filepath.Base(dbName) + return +} + +func (*SQLite) disableReferentialIntegrity(db *sql.DB, loadFn loadFunction) error { + tx, err := db.Begin() + if err != nil { + return err + } + + if _, err = tx.Exec("PRAGMA defer_foreign_keys = ON"); err != nil { + return err + } + + if err = loadFn(tx); err != nil { + return err + } + + return tx.Commit() +} diff --git a/vendor/gopkg.in/testfixtures.v2/sqlserver.go b/vendor/gopkg.in/testfixtures.v2/sqlserver.go new file mode 100644 index 0000000000000..1399f5c400842 --- /dev/null +++ b/vendor/gopkg.in/testfixtures.v2/sqlserver.go @@ -0,0 +1,110 @@ +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.getTables(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(db *sql.DB) (dbname string) { + db.QueryRow("SELECT DB_NAME()").Scan(&dbname) + return +} + +func (*SQLServer) getTables(db *sql.DB) ([]string, error) { + rows, err := db.Query("SELECT table_name FROM information_schema.tables") + if err != nil { + return nil, err + } + + tables := make([]string, 0) + defer rows.Close() + for rows.Next() { + var table string + rows.Scan(&table) + tables = append(tables, table) + } + return tables, nil +} + +func (*SQLServer) tableHasIdentityColumn(tx *sql.Tx, tableName string) bool { + sql := ` +SELECT COUNT(*) +FROM SYS.IDENTITY_COLUMNS +WHERE OBJECT_NAME(OBJECT_ID) = ? +` + var count int + tx.QueryRow(sql, tableName).Scan(&count) + return count > 0 + +} + +func (h *SQLServer) whileInsertOnTable(tx *sql.Tx, tableName string, fn func() error) error { + if h.tableHasIdentityColumn(tx, tableName) { + defer tx.Exec(fmt.Sprintf("SET IDENTITY_INSERT %s OFF", h.quoteKeyword(tableName))) + _, 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) error { + // ensure the triggers are re-enable after all + defer func() { + sql := "" + for _, table := range h.tables { + sql += fmt.Sprintf("ALTER TABLE %s WITH CHECK CHECK CONSTRAINT ALL;", h.quoteKeyword(table)) + } + if _, err := db.Exec(sql); err != nil { + fmt.Printf("Error on re-enabling constraints: %v\n", err) + } + }() + + sql := "" + 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 + } + + if err = loadFn(tx); err != nil { + tx.Rollback() + return err + } + + return tx.Commit() +} diff --git a/vendor/gopkg.in/testfixtures.v2/testfixtures.go b/vendor/gopkg.in/testfixtures.v2/testfixtures.go new file mode 100644 index 0000000000000..bd95580f2adc7 --- /dev/null +++ b/vendor/gopkg.in/testfixtures.v2/testfixtures.go @@ -0,0 +1,279 @@ +package testfixtures + +import ( + "database/sql" + "errors" + "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 ( + // 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"`) + + dbnameRegexp = regexp.MustCompile("(?i)test") +) + +// NewFolder craetes 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 craetes 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 { + if !dbnameRegexp.MatchString(c.helper.databaseName(c.db)) { + return ErrNotTestDatabase + } + } + + err := c.helper.disableReferentialIntegrity(c.db, func(tx *sql.Tx) error { + for _, file := range c.fixturesFiles { + if err := file.delete(tx, c.helper); err != nil { + return err + } + + err := c.helper.whileInsertOnTable(tx, file.fileNameWithoutExtension(), func() error { + for _, i := range file.insertSQLs { + if _, err := tx.Exec(i.sql, i.params...); err != nil { + return err + } + } + return nil + }) + if err != nil { + return err + } + } + return nil + }) + return err +} + +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)) + + switch h.paramType() { + case paramTypeDollar: + sqlValues = append(sqlValues, fmt.Sprintf("$%d", i)) + case paramTypeQuestion: + sqlValues = append(sqlValues, "?") + case paramTypeColon: + switch { + case isDateTime(value): + sqlValues = append(sqlValues, fmt.Sprintf("to_date(:%d, 'YYYY-MM-DD HH24:MI:SS')", i)) + case isDate(value): + sqlValues = append(sqlValues, fmt.Sprintf("to_date(:%d, 'YYYY-MM-DD')", i)) + case isTime(value): + sqlValues = append(sqlValues, fmt.Sprintf("to_date(:%d, 'HH24:MI:SS')", i)) + default: + sqlValues = append(sqlValues, fmt.Sprintf(":%d", i)) + } + } + i++ + values = append(values, value) + } + + 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 +} diff --git a/vendor/gopkg.in/testfixtures.v2/time.go b/vendor/gopkg.in/testfixtures.v2/time.go new file mode 100644 index 0000000000000..6796707500478 --- /dev/null +++ b/vendor/gopkg.in/testfixtures.v2/time.go @@ -0,0 +1,36 @@ +package testfixtures + +import "regexp" + +var ( + regexpDate = regexp.MustCompile("\\d\\d\\d\\d-\\d\\d-\\d\\d") + regexpDateTime = regexp.MustCompile("\\d\\d\\d\\d-\\d\\d-\\d\\d \\d\\d:\\d\\d:\\d\\d") + regexpTime = regexp.MustCompile("\\d\\d:\\d\\d:\\d\\d") +) + +func isDate(value interface{}) bool { + str, isStr := value.(string) + if !isStr { + return false + } + + return regexpDate.MatchString(str) +} + +func isDateTime(value interface{}) bool { + str, isStr := value.(string) + if !isStr { + return false + } + + return regexpDateTime.MatchString(str) +} + +func isTime(value interface{}) bool { + str, isStr := value.(string) + if !isStr { + return false + } + + return regexpTime.MatchString(str) +} diff --git a/vendor/vendor.json b/vendor/vendor.json index c963e9f286888..db6ab885fa9ed 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -38,6 +38,12 @@ "revision": "fb1f79c6b65acda83063cbc69f6bba1522558bfc", "revisionTime": "2016-01-17T19:21:50Z" }, + { + "checksumSHA1": "Lf3uUXTkKK5DJ37BxQvxO1Fq+K8=", + "path": "github.com/davecgh/go-spew/spew", + "revision": "976c720a22c8eb4eb6a0b4348ad85ad12491a506", + "revisionTime": "2016-09-25T22:06:09Z" + }, { "checksumSHA1": "K3Gp8Tv/B8otlbsOfQp3UpJGaaE=", "path": "github.com/go-gitea/git", @@ -50,12 +56,6 @@ "revision": "0a0a04ccf7a5e6b93d9a5507701635330cf4579c", "revisionTime": "2016-11-07T15:06:50Z" }, - { - "checksumSHA1": "Lf3uUXTkKK5DJ37BxQvxO1Fq+K8=", - "path": "github.com/davecgh/go-spew/spew", - "revision": "976c720a22c8eb4eb6a0b4348ad85ad12491a506", - "revisionTime": "2016-09-25T22:06:09Z" - }, { "checksumSHA1": "qM/kf31cT2cxjtHxdzbu8q8jPq0=", "path": "github.com/go-macaron/binding", @@ -512,6 +512,12 @@ "revision": "e6179049628164864e6e84e973cfb56335748dea", "revisionTime": "2014-12-09T11:07:59Z" }, + { + "checksumSHA1": "LUbT9kZUJcQkU/6GSbnsai+b5t4=", + "path": "gopkg.in/testfixtures.v2", + "revision": "b9ef14dc461bf934d8df2dfc6f1f456be5664cca", + "revisionTime": "2016-10-15T20:31:37Z" + }, { "checksumSHA1": "JQBqnAXO83ic7bwer/MwurhQMtg=", "path": "strk.kbt.io/projects/go/libravatar",