Newer
Older
// Copyright (c) 2012-present The upper.io/db authors. All rights reserved.
//
// 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.
// Tests for the mongodb adapter.
package mongo
import (
José Carlos Nieto
committed
"math/rand"
"strings"
"time"
"gopkg.in/mgo.v2/bson"
type artistType struct {
ID bson.ObjectId `bson:"_id,omitempty"`
Name string `bson:"name"`
}
// Structure for testing conversions and datatypes.
Uint uint `bson:"_uint"`
Uint8 uint8 `bson:"_uint8"`
Uint16 uint16 `bson:"_uint16"`
Uint32 uint32 `bson:"_uint32"`
Uint64 uint64 `bson:"_uint64"`
Int int `bson:"_int"`
Int8 int8 `bson:"_int8"`
Int16 int16 `bson:"_int16"`
Int32 int32 `bson:"_int32"`
Int64 int64 `bson:"_int64"`
Float32 float32 `bson:"_float32"`
Float64 float64 `bson:"_float64"`
Bool bool `bson:"_bool"`
String string `bson:"_string"`
José Carlos Nieto
committed
Date time.Time `bson:"_date"`
DateN *time.Time `bson:"_nildate"`
DateP *time.Time `bson:"_ptrdate"`
Time time.Duration `bson:"_time"`
José Carlos Nieto
committed
var testValues testValuesStruct
func init() {
t := time.Date(2012, 7, 28, 1, 2, 3, 0, time.Local)
testValues = testValuesStruct{
1, 1, 1, 1, 1,
-1, -1, -1, -1, -1,
1.337, 1.337,
true,
"Hello world!",
t,
nil,
&t,
time.Second * time.Duration(7331),
}
}
type AdapterTests struct {
testsuite.Suite
}
func (s *AdapterTests) SetupSuite() {
s.Helper = &Helper{}
}
func (s *AdapterTests) TestOpenWithWrongData() {
var rightSettings, wrongSettings ConnectionURL
// Attempt to open with safe settings.
rightSettings = ConnectionURL{
Database: settings.Database,
Host: settings.Host,
User: settings.User,
Password: settings.Password,
}
// Attempt to open an empty database.
// Attempt to open with wrong password.
wrongSettings = ConnectionURL{
Database: settings.Database,
Host: settings.Host,
User: settings.User,
// Attempt to open with wrong database.
wrongSettings = ConnectionURL{
Host: settings.Host,
User: settings.User,
Password: settings.Password,
// Attempt to open with wrong username.
wrongSettings = ConnectionURL{
Database: settings.Database,
Host: settings.Host,
// Opening database.
sess, err := Open(settings)
// We should close the database when it's no longer in use.
// Getting a list of all collections in this database.
collections, err := sess.Collections()
for _, name := range collections {
// Pointing the collection.
col := sess.Collection(name)
// The collection may ot may not exists.
exists := col.Exists()
if exists == true {
// Truncating the structure, if exists.
err = col.Truncate()
// Opening database.
sess, err := Open(settings)
// We should close the database when it's no longer in use.
// Getting a pointer to the "artist" collection.
artist := sess.Collection("artist")
"name": "Ozzie",
})
// Inserting a struct.
id, err = artist.Insert(struct {
Name string
}{
"Flea",
})
_, ok = id.(bson.ObjectId)
s.True(ok)
s.True(id.(bson.ObjectId).Valid())
// Inserting a struct (using tags to specify the field name).
id, err = artist.Insert(struct {
ArtistName string `bson:"name"`
}{
"Slash",
})
_, ok = id.(bson.ObjectId)
s.True(ok)
s.True(id.(bson.ObjectId).Valid())
// Inserting a pointer to a struct
id, err = artist.Insert(&struct {
ArtistName string `bson:"name"`
}{
"Metallica",
})
_, ok = id.(bson.ObjectId)
s.True(ok)
s.True(id.(bson.ObjectId).Valid())
// Inserting a pointer to a map
id, err = artist.Insert(&map[string]string{
_, ok = id.(bson.ObjectId)
s.True(ok)
s.True(id.(bson.ObjectId).Valid())
José Carlos Nieto
committed
total, err := artist.Find().Count()
s.NoError(err)
s.Equal(uint64(5), total)
func (s *AdapterTests) TestGetNonExistentRow_Issue426() {
// Opening database.
sess, err := Open(settings)
defer sess.Close()
artist := sess.Collection("artist")
var one artistType
err = artist.Find(db.Cond{"name": "nothing"}).One(&one)
s.NotZero(err)
s.Equal(db.ErrNoMoreRows, err)
var all []artistType
err = artist.Find(db.Cond{"name": "nothing"}).All(&all)
s.Zero(err, "All should not return mgo.ErrNotFound")
s.Equal(0, len(all))
var res db.Result
// Opening database.
sess, err := Open(settings)
// We should close the database when it's no longer in use.
artist := sess.Collection("artist")
res = artist.Find()
// Counting all the matching rows.
total, err := res.Count()
José Carlos Nieto
committed
var stats db.Collection
José Carlos Nieto
committed
José Carlos Nieto
committed
Numeric int `db:"numeric" bson:"numeric"`
Value int `db:"value" bson:"value"`
}
defer sess.Close()
stats = sess.Collection("statsTest")
José Carlos Nieto
committed
// Truncating table.
José Carlos Nieto
committed
// Adding row append.
for i := 0; i < 1000; i++ {
numeric, value := rand.Intn(10), rand.Intn(100)
_, err = stats.Insert(statsT{numeric, value})
s.NoError(err)
José Carlos Nieto
committed
}
// db.statsTest.group({key: {numeric: true}, initial: {sum: 0}, reduce: function(doc, prev) { prev.sum += 1}});
José Carlos Nieto
committed
// Testing GROUP BY
res := stats.Find().Group(bson.M{
"key": bson.M{"numeric": true},
"initial": bson.M{"sum": 0},
"reduce": `function(doc, prev) { prev.sum += 1}`,
})
var results []map[string]interface{}
err = res.All(&results)
José Carlos Nieto
committed
}
func (s *AdapterTests) TestResultNonExistentCount() {
sess, err := Open(settings)
defer sess.Close()
total, err := sess.Collection("notartist").Find().Count()
// Opening database.
sess, err := Open(settings)
// We should close the database when it's no longer in use.
defer sess.Close()
artist := sess.Collection("artist")
// Testing map
_, ok := rowM["_id"].(bson.ObjectId)
s.True(ok)
s.True(rowM["_id"].(bson.ObjectId).Valid())
name, ok := rowM["name"].(string)
s.True(ok)
s.NotZero(name)
// Testing struct
rowS := struct {
ID bson.ObjectId `bson:"_id"`
Name string `bson:"name"`
}{}
res = artist.Find()
s.True(rowS.ID.Valid())
s.NotZero(rowS.Name)
// Testing tagged struct
Value1 bson.ObjectId `bson:"_id"`
Value2 string `bson:"name"`
}{}
res = artist.Find()
s.True(rowT.Value1.Valid())
s.NotZero(rowT.Value2)
// Testing Result.All() with a slice of maps.
res = artist.Find()
allRowsM := []map[string]interface{}{}
err = res.All(&allRowsM)
// Testing Result.All() with a slice of structs.
res = artist.Find()
allRowsS := []struct {
ID bson.ObjectId `bson:"_id"`
Name string
}{}
// Testing Result.All() with a slice of tagged structs.
res = artist.Find()
Value1 bson.ObjectId `bson:"_id"`
Value2 string `bson:"name"`
}{}
// Opening database.
sess, err := Open(settings)
// We should close the database when it's no longer in use.
// Getting a pointer to the "artist" collection.
artist := sess.Collection("artist")
// Value
value := struct {
Name string
}{}
// Getting the first artist.
res := artist.Find(db.Cond{"_id": db.NotEq(nil)}).Limit(1)
err = res.One(&value)
// Updating with a map
"name": strings.ToUpper(value.Name),
}
err = res.One(&value)
José Carlos Nieto
committed
// Updating with a struct
Name string
}{strings.ToLower(value.Name)}
José Carlos Nieto
committed
err = res.One(&value)
// Updating with a tagged struct
Value1 string `bson:"name"`
}{strings.Replace(value.Name, "z", "Z", -1)}
err = res.One(&value)
sess, err := Open(settings)
// We should close the database when it's no longer in use.
defer sess.Close()
// Getting a pointer to the "artist" collection.
artist := sess.Collection("artist")
res := artist.Find(db.Cond{"_id": db.NotIn([]int{0, -1})})
// Opening database.
sess, err := Open(settings)
// We should close the database when it's no longer in use.
// Getting a pointer to the "artist" collection.
artist := sess.Collection("artist")
José Carlos Nieto
committed
// Getting the first artist.
res := artist.Find(db.Cond{"_id": db.NotEq(nil)}).Limit(1)
var first struct {
err = res.One(&first)
res = artist.Find(db.Cond{"_id": db.Eq(first.ID)})
// Trying to remove the row.
// Opening database.
sess, err := Open(settings)
// We should close the database when it's no longer in use.
// Getting a pointer to the "data_types" collection.
dataTypes := sess.Collection("data_types")
// Inserting our test subject.
id, err := dataTypes.Insert(testValues)
// Trying to get the same subject we added.
res := dataTypes.Find(db.Cond{"_id": db.Eq(id)})
exists, err := res.Count()
// Trying to dump the subject into an empty structure of the same type.
var item testValuesStruct
// The original value and the test subject must match.
// Opening database.
sess, err := Open(settings)
// We should close the database when it's no longer in use.
defer sess.Close()
// Getting a pointer to the "artist" collection.
artist := sess.Collection("artist")
err = artist.Truncate()
for i := 0; i < 999; i++ {
_, err = artist.Insert(artistType{
Name: fmt.Sprintf("artist-%d", i),
})
}
q := sess.Collection("artist").Find().Paginate(15)
paginator := q.Paginate(13)
var zerothPage []artistType
err = paginator.Page(0).All(&zerothPage)
var secondPage []artistType
err = paginator.Page(2).All(&secondPage)
tp, err := paginator.TotalPages()
s.NoError(err)
s.NotZero(tp)
s.Equal(uint(77), tp)
s.NoError(err)
s.NotZero(ti)
s.Equal(uint64(999), ti)
var seventySixthPage []artistType
err = paginator.Page(76).All(&seventySixthPage)
s.NoError(err)
s.Equal(11, len(seventySixthPage))
var seventySeventhPage []artistType
err = paginator.Page(77).All(&seventySeventhPage)
s.NoError(err)
s.Equal(0, len(seventySeventhPage))
var hundredthPage []artistType
err = paginator.Page(100).All(&hundredthPage)
s.NoError(err)
s.Equal(0, len(hundredthPage))
for i := uint(0); i < tp; i++ {
current := paginator.Page(i)
var items []artistType
err := current.All(&items)
if len(items) < 1 {
break
}
for j := 0; j < len(items); j++ {
s.Equal(fmt.Sprintf("artist-%d", int64(13*int(i)+j)), items[j].Name)
}
}
paginator = paginator.Cursor("_id")
{
current := paginator.Page(0)
for i := 0; ; i++ {
var items []artistType
err := current.All(&items)
if len(items) < 1 {
break
}
for j := 0; j < len(items); j++ {
s.Equal(fmt.Sprintf("artist-%d", int64(13*int(i)+j)), items[j].Name)
}
current = current.NextPage(items[len(items)-1].ID)
}
}
{
log.Printf("Page 76")
current := paginator.Page(76)
for i := 76; ; i-- {
var items []artistType
err := current.All(&items)
break
}
for j := 0; j < len(items); j++ {
s.Equal(fmt.Sprintf("artist-%d", 13*int(i)+j), items[j].Name)
}
current = current.PrevPage(items[0].ID)
}
}
{
resultPaginator := sess.Collection("artist").Find().Paginate(15)
count, err := resultPaginator.TotalPages()
var items []artistType
err = resultPaginator.Page(5).All(&items)
for j := 0; j < len(items); j++ {
s.Equal(fmt.Sprintf("artist-%d", 15*5+j), items[j].Name)
}
resultPaginator = resultPaginator.Cursor("_id").Page(0)
for i := 0; ; i++ {
var items []artistType
err = resultPaginator.All(&items)
if len(items) < 1 {
break
}
for j := 0; j < len(items); j++ {
s.Equal(fmt.Sprintf("artist-%d", 15*i+j), items[j].Name)
}
resultPaginator = resultPaginator.NextPage(items[len(items)-1].ID)
}
resultPaginator = resultPaginator.Cursor("_id").Page(66)
for i := 66; ; i-- {
var items []artistType
err = resultPaginator.All(&items)
if len(items) < 1 {
break
}
for j := 0; j < len(items); j++ {
s.Equal(fmt.Sprintf("artist-%d", 15*i+j), items[j].Name)
}
resultPaginator = resultPaginator.PrevPage(items[0].ID)
}
}
}
func TestAdapter(t *testing.T) {
suite.Run(t, &AdapterTests{})
}