db: add tests for LFS (#6087)

* Improve DB test setup

* Discard GORM logs in non-verbose mode

* Add tests to lfs

* Fix data race
pull/6092/head
ᴜɴᴋɴᴡᴏɴ 2020-04-11 02:56:37 +08:00 committed by GitHub
parent 62dda96159
commit e077ecdd9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 184 additions and 44 deletions

View File

@ -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
}

View File

@ -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,33 +21,9 @@ 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)
db := &accessTokens{
DB: initTestDB(t, "accessTokens", new(AccessToken)),
}
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)
}
now := time.Now().Truncate(time.Second)
clock := func() time.Time { return now }
db := &accessTokens{DB: gdb, clock: clock}
for _, tc := range []struct {
name string
@ -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))
}

View File

@ -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}

117
internal/db/lfs_test.go Normal file
View File

@ -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)
}

View File

@ -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
}