aboutsummaryrefslogtreecommitdiff
path: root/internal/db
diff options
context:
space:
mode:
authorᴜɴᴋɴᴡᴏɴ <u@gogs.io>2020-04-11 02:56:37 +0800
committerGitHub <noreply@github.com>2020-04-11 02:56:37 +0800
commite077ecdd9d95ecb76d91105b3858ee48d52c0cc2 (patch)
treedf44f46888c08fc675d47e22e11a165fdb8d185d /internal/db
parent62dda96159055ff9d485078f257445b964eb5220 (diff)
db: add tests for LFS (#6087)
* Improve DB test setup * Discard GORM logs in non-verbose mode * Add tests to lfs * Fix data race
Diffstat (limited to 'internal/db')
-rw-r--r--internal/db/access_tokens.go15
-rw-r--r--internal/db/access_tokens_test.go45
-rw-r--r--internal/db/db.go7
-rw-r--r--internal/db/lfs_test.go117
-rw-r--r--internal/db/main_test.go44
5 files changed, 184 insertions, 44 deletions
diff --git a/internal/db/access_tokens.go b/internal/db/access_tokens.go
index f4f4ee80..b6cd5339 100644
--- a/internal/db/access_tokens.go
+++ b/internal/db/access_tokens.go
@@ -56,12 +56,12 @@ type AccessToken struct {
// NOTE: This is a GORM create hook.
func (t *AccessToken) BeforeCreate() {
- t.CreatedUnix = t.Created.Unix()
+ t.CreatedUnix = gorm.NowFunc().Unix()
}
// NOTE: This is a GORM update hook.
func (t *AccessToken) BeforeUpdate() {
- t.UpdatedUnix = t.Updated.Unix()
+ t.UpdatedUnix = gorm.NowFunc().Unix()
}
// NOTE: This is a GORM query hook.
@@ -69,14 +69,13 @@ func (t *AccessToken) AfterFind() {
t.Created = time.Unix(t.CreatedUnix, 0).Local()
t.Updated = time.Unix(t.UpdatedUnix, 0).Local()
t.HasUsed = t.Updated.After(t.Created)
- t.HasRecentActivity = t.Updated.Add(7 * 24 * time.Hour).After(time.Now())
+ t.HasRecentActivity = t.Updated.Add(7 * 24 * time.Hour).After(gorm.NowFunc())
}
var _ AccessTokensStore = (*accessTokens)(nil)
type accessTokens struct {
*gorm.DB
- clock func() time.Time
}
type ErrAccessTokenAlreadyExist struct {
@@ -101,10 +100,9 @@ func (db *accessTokens) Create(userID int64, name string) (*AccessToken, error)
}
token := &AccessToken{
- UserID: userID,
- Name: name,
- Sha1: tool.SHA1(gouuid.NewV4().String()),
- Created: db.clock(),
+ UserID: userID,
+ Name: name,
+ Sha1: tool.SHA1(gouuid.NewV4().String()),
}
return token, db.DB.Create(token).Error
}
@@ -150,6 +148,5 @@ func (db *accessTokens) List(userID int64) ([]*AccessToken, error) {
}
func (db *accessTokens) Save(t *AccessToken) error {
- t.Updated = db.clock()
return db.DB.Save(t).Error
}
diff --git a/internal/db/access_tokens_test.go b/internal/db/access_tokens_test.go
index f6d62745..d3dfb83f 100644
--- a/internal/db/access_tokens_test.go
+++ b/internal/db/access_tokens_test.go
@@ -5,15 +5,12 @@
package db
import (
- "fmt"
- "os"
- "path/filepath"
"testing"
"time"
+ "github.com/jinzhu/gorm"
"github.com/stretchr/testify/assert"
- "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/errutil"
)
@@ -24,34 +21,10 @@ func Test_accessTokens(t *testing.T) {
t.Parallel()
- dbpath := filepath.Join(os.TempDir(), fmt.Sprintf("gogs-%d.db", time.Now().Unix()))
- gdb, err := openDB(conf.DatabaseOpts{
- Type: "sqlite3",
- Path: dbpath,
- })
- if err != nil {
- t.Fatal(err)
- }
- t.Cleanup(func() {
- _ = gdb.Close()
-
- if t.Failed() {
- t.Logf("Database %q left intact for inspection", dbpath)
- return
- }
-
- _ = os.Remove(dbpath)
- })
-
- err = gdb.AutoMigrate(new(AccessToken)).Error
- if err != nil {
- t.Fatal(err)
+ db := &accessTokens{
+ DB: initTestDB(t, "accessTokens", new(AccessToken)),
}
- now := time.Now().Truncate(time.Second)
- clock := func() time.Time { return now }
- db := &accessTokens{DB: gdb, clock: clock}
-
for _, tc := range []struct {
name string
test func(*testing.T, *accessTokens)
@@ -64,7 +37,7 @@ func Test_accessTokens(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
t.Cleanup(func() {
- err := deleteTables(gdb, new(AccessToken))
+ err := deleteTables(db.DB, new(AccessToken))
if err != nil {
t.Fatal(err)
}
@@ -84,7 +57,13 @@ func test_accessTokens_Create(t *testing.T, db *accessTokens) {
assert.Equal(t, int64(1), token.UserID)
assert.Equal(t, "Test", token.Name)
assert.Equal(t, 40, len(token.Sha1), "sha1 length")
- assert.Equal(t, db.clock(), token.Created)
+
+ // Get it back and check the Created field
+ token, err = db.GetBySHA(token.Sha1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ assert.Equal(t, gorm.NowFunc().Format(time.RFC3339), token.Created.Format(time.RFC3339))
// Try create second access token with same name should fail
_, err = db.Create(token.UserID, token.Name)
@@ -197,5 +176,5 @@ func test_accessTokens_Save(t *testing.T, db *accessTokens) {
if err != nil {
t.Fatal(err)
}
- assert.Equal(t, db.clock(), token.Updated)
+ assert.Equal(t, gorm.NowFunc().Format(time.RFC3339), token.Updated.Format(time.RFC3339))
}
diff --git a/internal/db/db.go b/internal/db/db.go
index 27135eba..1be2cc4b 100644
--- a/internal/db/db.go
+++ b/internal/db/db.go
@@ -163,9 +163,12 @@ func Init() error {
return errors.Wrap(err, "migrate schemes")
}
- clock := func() time.Time {return time.Now().UTC().Truncate(time.Microsecond)}
+ gorm.NowFunc = func() time.Time {
+ return time.Now().UTC().Truncate(time.Microsecond)
+ }
+
// Initialize stores, sorted in alphabetical order.
- AccessTokens = &accessTokens{DB: db, clock: clock}
+ AccessTokens = &accessTokens{DB: db}
LoginSources = &loginSources{DB: db}
LFS = &lfs{DB: db}
Perms = &perms{DB: db}
diff --git a/internal/db/lfs_test.go b/internal/db/lfs_test.go
new file mode 100644
index 00000000..6eb14019
--- /dev/null
+++ b/internal/db/lfs_test.go
@@ -0,0 +1,117 @@
+// Copyright 2020 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package db
+
+import (
+ "testing"
+ "time"
+
+ "github.com/jinzhu/gorm"
+ "github.com/stretchr/testify/assert"
+
+ "gogs.io/gogs/internal/errutil"
+ "gogs.io/gogs/internal/lfsutil"
+)
+
+func Test_lfs(t *testing.T) {
+ if testing.Short() {
+ t.Skip()
+ }
+
+ t.Parallel()
+
+ db := &lfs{
+ DB: initTestDB(t, "lfs", new(LFSObject)),
+ }
+
+ for _, tc := range []struct {
+ name string
+ test func(*testing.T, *lfs)
+ }{
+ {"CreateObject", test_lfs_CreateObject},
+ {"GetObjectByOID", test_lfs_GetObjectByOID},
+ {"GetObjectsByOIDs", test_lfs_GetObjectsByOIDs},
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Cleanup(func() {
+ err := deleteTables(db.DB, new(LFSObject))
+ if err != nil {
+ t.Fatal(err)
+ }
+ })
+ tc.test(t, db)
+ })
+ }
+}
+
+func test_lfs_CreateObject(t *testing.T, db *lfs) {
+ // Create first LFS object
+ repoID := int64(1)
+ oid := lfsutil.OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f")
+ err := db.CreateObject(repoID, oid, 12, lfsutil.StorageLocal)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Get it back and check the CreatedAt field
+ object, err := db.GetObjectByOID(repoID, oid)
+ if err != nil {
+ t.Fatal(err)
+ }
+ assert.Equal(t, gorm.NowFunc().Format(time.RFC3339), object.CreatedAt.Format(time.RFC3339))
+
+ // Try create second LFS object with same oid should fail
+ err = db.CreateObject(repoID, oid, 12, lfsutil.StorageLocal)
+ assert.Error(t, err)
+}
+
+func test_lfs_GetObjectByOID(t *testing.T, db *lfs) {
+ // Create a LFS object
+ repoID := int64(1)
+ oid := lfsutil.OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f")
+ err := db.CreateObject(repoID, oid, 12, lfsutil.StorageLocal)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // We should be able to get it back
+ _, err = db.GetObjectByOID(repoID, oid)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Try to get a non-existent object
+ _, err = db.GetObjectByOID(repoID, "bad_oid")
+ expErr := ErrLFSObjectNotExist{args: errutil.Args{"repoID": repoID, "oid": lfsutil.OID("bad_oid")}}
+ assert.Equal(t, expErr, err)
+}
+
+func test_lfs_GetObjectsByOIDs(t *testing.T, db *lfs) {
+ // Create two LFS objects
+ repoID := int64(1)
+ oid1 := lfsutil.OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64f")
+ oid2 := lfsutil.OID("ef797c8118f02dfb649607dd5d3f8c7623048c9c063d532cc95c5ed7a898a64g")
+ err := db.CreateObject(repoID, oid1, 12, lfsutil.StorageLocal)
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = db.CreateObject(repoID, oid2, 12, lfsutil.StorageLocal)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // We should be able to get them back and ignore non-existent ones
+ objects, err := db.GetObjectsByOIDs(repoID, oid1, oid2, "bad_oid")
+ if err != nil {
+ t.Fatal(err)
+ }
+ assert.Equal(t, 2, len(objects), "number of objects")
+
+ assert.Equal(t, repoID, objects[0].RepoID)
+ assert.Equal(t, oid1, objects[0].OID)
+
+ assert.Equal(t, repoID, objects[1].RepoID)
+ assert.Equal(t, oid2, objects[1].OID)
+}
diff --git a/internal/db/main_test.go b/internal/db/main_test.go
index d141d3cd..8b4190da 100644
--- a/internal/db/main_test.go
+++ b/internal/db/main_test.go
@@ -7,12 +7,17 @@ package db
import (
"flag"
"fmt"
+ "io/ioutil"
"os"
+ "path/filepath"
"testing"
+ "time"
"github.com/jinzhu/gorm"
log "unknwon.dev/clog/v2"
+ "gogs.io/gogs/internal/conf"
+ "gogs.io/gogs/internal/dbutil"
"gogs.io/gogs/internal/testutil"
)
@@ -27,6 +32,10 @@ func TestMain(m *testing.M) {
os.Exit(1)
}
}
+
+ now := time.Now().Truncate(time.Second)
+ gorm.NowFunc = func() time.Time { return now }
+
os.Exit(m.Run())
}
@@ -39,3 +48,38 @@ func deleteTables(db *gorm.DB, tables ...interface{}) error {
}
return nil
}
+
+func initTestDB(t *testing.T, suite string, tables ...interface{}) *gorm.DB {
+ t.Helper()
+
+ dbpath := filepath.Join(os.TempDir(), fmt.Sprintf("gogs-%s-%d.db", suite, time.Now().Unix()))
+ db, err := openDB(conf.DatabaseOpts{
+ Type: "sqlite3",
+ Path: dbpath,
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Cleanup(func() {
+ _ = db.Close()
+
+ if t.Failed() {
+ t.Logf("Database %q left intact for inspection", dbpath)
+ return
+ }
+
+ _ = os.Remove(dbpath)
+ })
+
+ db.SingularTable(true)
+ if !testing.Verbose() {
+ db.SetLogger(&dbutil.Writer{Writer: ioutil.Discard})
+ }
+
+ err = db.AutoMigrate(tables...).Error
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return db
+}