diff --git a/.gitignore b/.gitignore index 2093b0a..4db68f3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.o *.a *.so +*.out # Folders _obj diff --git a/README.md b/README.md index 5ed6b8e..d0d24f5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # mongo [![GoDoc](https://godoc.org/github.com/ddspog/mongo?status.svg)](https://godoc.org/github.com/ddspog/mongo) [![Go Report Card](https://goreportcard.com/badge/github.com/ddspog/mongo)](https://goreportcard.com/report/github.com/ddspog/mongo) [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) [![Travis CI](https://travis-ci.org/ddspog/mongo.svg?branch=master)](https://travis-ci.org/ddspog/mongo) -by [ddspog](http://hithub.com/ddspog) +by [ddspog](http://github.com/ddspog) Package **mongo** helps you mask the connection to MongoDB using mgo package. @@ -66,11 +66,28 @@ if err := mongo.Connect(); err != nil { } ``` +## Testing + +This package contains a nice coverage with the unit tests, within the +objetives of the project. + +The elements, embedded and mocks sub-packages have low coverage because +they fulfill a need to mock mgo elements. These packages only embedded +mgo objects to mock, and by this a lot of unused functions were created +to fulfill interface requisites. + +On the other hand, model, handler and mongo package have full coverage, +being the focus of this project. + +The project also contains a set of acceptance tests. I've have set the +test-acceptance task with the commands to run it. These tests requires +a mongo test database to be available. It creates, search and remove +elements from it, being reusable without broking the database. + ## Contribution This package has some objectives from now: * Being incorporate on mgo package (possible fork) on possible future. -* Creating real tests with MongoDB connections. Any interest in help is much appreciated. \ No newline at end of file diff --git a/Taskfile.yml b/Taskfile.yml index 85642a2..00c0211 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -12,11 +12,11 @@ test-model: - go test {{.REPO_PATH}}/model -v --cover silent: true -test-example: - desc: Run a test with an mongo instance running. +test-acceptance: + desc: Run acceptance tests with a real mongo instance running. cmds: - - echo "Calling tests example execution ..." - - go test {{.REPO_PATH}}/example -v --cover + - echo "Calling acceptance tests execution ..." + - go test {{.REPO_PATH}} -v --cover -tags=acceptance silent: true test-mongo: @@ -26,8 +26,15 @@ test-mongo: - go test {{.REPO_PATH}} -v --cover silent: true +cover: + desc: Check cover of all unit tests. + cmds: + - echo "Checking coverage for all unit tests ..." + - goverage ./... + - go tool cover -html=coverage.out + test: - deps: [test-handler, test-model, test-mongo, test-example] + deps: [test-handler, test-model, test-mongo] desc: Run all tests. gen-mock-collection: diff --git a/example/real_test.go b/acceptance_test.go similarity index 97% rename from example/real_test.go rename to acceptance_test.go index 3f99cb0..3487a86 100644 --- a/example/real_test.go +++ b/acceptance_test.go @@ -1,4 +1,6 @@ -package example +// +build acceptance + +package mongo import ( "fmt" @@ -6,11 +8,7 @@ import ( "testing" "github.com/ddspog/mongo/model" - - "github.com/ddspog/mongo" - "github.com/ddspog/mspec/bdd" - "github.com/globalsign/mgo/bson" ) @@ -22,126 +20,6 @@ const ( testid04 = "000000007465737469643034" ) -// Feature Real connection to MongoDB -// - As a developer, -// - I want to be able to connect to MongoDB with this package, -// - So that I could use the Handle methods to to various operations on a real DB. -func Test_Real_connection_to_MongoDB(t *testing.T) { - given, _, _ := bdd.Sentences() - - given(t, "a local database with a products collection with 3 documents", func(when bdd.When) { - when("connecting with its url", func(it bdd.It) { - os.Setenv("MONGODB_URL", "mongodb://localhost:27017/test") - err := mongo.Connect() - defer mongo.Disconnect() - - it("should connect with no problems", func(assert bdd.Assert) { - assert.NoError(err) - }) - - conn := NewDBSocket() - defer conn.Close() - - db := conn.DB() - - it("should open a socket containing valid DB", func(assert bdd.Assert) { - assert.NotNil(db) - }) - - p, err := NewProductHandle().Link(db) - - it("should link correctly with products collection", func(assert bdd.Assert) { - assert.NoError(err) - }) - - n, err := p.Count() - - it("should count 3 documents on products collection", func(assert bdd.Assert) { - assert.NoError(err) - assert.Equal(3, n) - }) - }) - }) -} - -// Feature Read data on MongoDB -// - As a developer, -// - I want to be able to connect and retrieve data from MongoDB, -// - So I can use these functionalities on real applications. -func Test_Read_data_on_MongoDB(t *testing.T) { - given, like, s := bdd.Sentences() - - given(t, fmt.Sprintf("a local database with a products collection that contains documents with ids: '%[1]s', '%[2]s', '%[3]s'", testid01, testid02, testid03), func(when bdd.When) { - os.Setenv("MONGODB_URL", "mongodb://localhost:27017/test") - _ = mongo.Connect() - defer mongo.Disconnect() - - conn := NewDBSocket() - defer conn.Close() - db := conn.DB() - - p := NewProductHandle() - p.Link(db) - - when("using p.Find() with '%[1]v' as document id'", func(it bdd.It, args ...interface{}) { - p.DocumentV.IDV = bson.ObjectIdHex(args[0].(string)) - product, errFind := p.Find() - - it("should run without errors", func(assert bdd.Assert) { - assert.NoError(errFind) - }) - - it("should return a product with id '%[1]v'", func(assert bdd.Assert) { - assert.Equal(args[0].(string), product.ID().Hex()) - }) - }, like( - s(testid01), s(testid02), s(testid03), - )) - - when("using p.FindAll() with a empty Document", func(it bdd.It) { - p.DocumentV = NewProduct() - products, errFindAll := p.FindAll() - - it("should run without errors", func(assert bdd.Assert) { - assert.NoError(errFindAll) - }) - - it("should return 3 documents", func(assert bdd.Assert) { - assert.Equal(3, len(products)) - }) - - if len(products) == 3 { - it("should return the %[1]vth document with id '%[2]v'", func(assert bdd.Assert, args ...interface{}) { - assert.Equal(args[1].(string), products[args[0].(int)-1].ID().Hex()) - }, like( - s(1, testid01), s(2, testid02), s(3, testid03), - )) - } - }) - - when("using p.FindAll() on a Document with id '%[1]v'", func(it bdd.It, args ...interface{}) { - p.DocumentV.IDV = bson.ObjectIdHex(args[0].(string)) - products, errFindAll := p.FindAll() - - it("should run without errors", func(assert bdd.Assert) { - assert.NoError(errFindAll) - }) - - it("should return 1 documents", func(assert bdd.Assert) { - assert.Equal(1, len(products)) - }) - - if len(products) == 1 { - it("should return the 1th document with id '%[1]v'", func(assert bdd.Assert) { - assert.Equal(args[0].(string), products[0].ID().Hex()) - }) - } - }, like( - s(testid01), s(testid02), s(testid03), - )) - }) -} - // Feature Manipulate data on MongoDB // - As a developer, // - I want to be able to connect and manipulate data from MongoDB, @@ -151,8 +29,8 @@ func Test_Manipulate_data_on_MongoDB(t *testing.T) { given(t, fmt.Sprintf("a local database with a products collection that contains documents with ids: '%[1]s', '%[2]s', '%[3]s'", testid01, testid02, testid03), func(when bdd.When) { os.Setenv("MONGODB_URL", "mongodb://localhost:27017/test") - _ = mongo.Connect() - defer mongo.Disconnect() + _ = Connect() + defer Disconnect() conn := NewDBSocket() defer conn.Close() @@ -314,3 +192,123 @@ func Test_Manipulate_data_on_MongoDB(t *testing.T) { }) }) } + +// Feature Real connection to MongoDB +// - As a developer, +// - I want to be able to connect to MongoDB with this package, +// - So that I could use the Handle methods to to various operations on a real DB. +func Test_Real_connection_to_MongoDB(t *testing.T) { + given, _, _ := bdd.Sentences() + + given(t, "a local database with a products collection with 3 documents", func(when bdd.When) { + when("connecting with its url", func(it bdd.It) { + os.Setenv("MONGODB_URL", "mongodb://localhost:27017/test") + err := Connect() + defer Disconnect() + + it("should connect with no problems", func(assert bdd.Assert) { + assert.NoError(err) + }) + + conn := NewDBSocket() + defer conn.Close() + + db := conn.DB() + + it("should open a socket containing valid DB", func(assert bdd.Assert) { + assert.NotNil(db) + }) + + p, err := NewProductHandle().Link(db) + + it("should link correctly with products collection", func(assert bdd.Assert) { + assert.NoError(err) + }) + + n, err := p.Count() + + it("should count 3 documents on products collection", func(assert bdd.Assert) { + assert.NoError(err) + assert.Equal(3, n) + }) + }) + }) +} + +// Feature Read data on MongoDB +// - As a developer, +// - I want to be able to connect and retrieve data from MongoDB, +// - So I can use these functionalities on real applications. +func Test_Read_data_on_MongoDB(t *testing.T) { + given, like, s := bdd.Sentences() + + given(t, fmt.Sprintf("a local database with a products collection that contains documents with ids: '%[1]s', '%[2]s', '%[3]s'", testid01, testid02, testid03), func(when bdd.When) { + os.Setenv("MONGODB_URL", "mongodb://localhost:27017/test") + _ = Connect() + defer Disconnect() + + conn := NewDBSocket() + defer conn.Close() + db := conn.DB() + + p := NewProductHandle() + p.Link(db) + + when("using p.Find() with '%[1]v' as document id'", func(it bdd.It, args ...interface{}) { + p.DocumentV.IDV = bson.ObjectIdHex(args[0].(string)) + product, errFind := p.Find() + + it("should run without errors", func(assert bdd.Assert) { + assert.NoError(errFind) + }) + + it("should return a product with id '%[1]v'", func(assert bdd.Assert) { + assert.Equal(args[0].(string), product.ID().Hex()) + }) + }, like( + s(testid01), s(testid02), s(testid03), + )) + + when("using p.FindAll() with a empty Document", func(it bdd.It) { + p.DocumentV = NewProduct() + products, errFindAll := p.FindAll() + + it("should run without errors", func(assert bdd.Assert) { + assert.NoError(errFindAll) + }) + + it("should return 3 documents", func(assert bdd.Assert) { + assert.Equal(3, len(products)) + }) + + if len(products) == 3 { + it("should return the %[1]vth document with id '%[2]v'", func(assert bdd.Assert, args ...interface{}) { + assert.Equal(args[1].(string), products[args[0].(int)-1].ID().Hex()) + }, like( + s(1, testid01), s(2, testid02), s(3, testid03), + )) + } + }) + + when("using p.FindAll() on a Document with id '%[1]v'", func(it bdd.It, args ...interface{}) { + p.DocumentV.IDV = bson.ObjectIdHex(args[0].(string)) + products, errFindAll := p.FindAll() + + it("should run without errors", func(assert bdd.Assert) { + assert.NoError(errFindAll) + }) + + it("should return 1 documents", func(assert bdd.Assert) { + assert.Equal(1, len(products)) + }) + + if len(products) == 1 { + it("should return the 1th document with id '%[1]v'", func(assert bdd.Assert) { + assert.Equal(args[0].(string), products[0].ID().Hex()) + }) + } + }, like( + s(testid01), s(testid02), s(testid03), + )) + }) +} diff --git a/connect_test.go b/connect_test.go index f00123d..2baa6ac 100644 --- a/connect_test.go +++ b/connect_test.go @@ -22,17 +22,17 @@ func Test_Connection_with_MongoDB(t *testing.T) { makeMGO, _ := mocks.NewMockMGOSetup(t) makeMongo, _ := NewMockMongoSetup(t) - defer finish(makeMGO, makeMongo) + defer Finish(makeMGO, makeMongo) - given, like, s := bdd.Sentences() + given, _, _ := bdd.Sentences() - given(t, "a database named '%[1]v' with a products collection with 10 elements", func(when bdd.When, args ...interface{}) { - db := makeMGO.DatabaseMock(args[0].(string), func(mcl *mocks.MockCollectioner) { + given(t, "a database named 'products' with a products collection with 10 elements", func(when bdd.When) { + db := makeMGO.DatabaseMock("products", func(mcl *mocks.MockCollectioner) { mcl.ExpectCountReturn(10) }) - makeMongo.ParseURL().Returns(elements.NewDatabaseInfo(args[0].(string)), nil) - makeMongo.Dial().Returns(makeMGO.SessionMock(args[0].(string), db), nil) + makeMongo.ParseURL().Returns(elements.NewDatabaseInfo("products"), nil) + makeMongo.Dial().Returns(makeMGO.SessionMock("products", db), nil) when("calling mongo.Connect()", func(it bdd.It) { it("should run with no problems", func(assert bdd.Assert) { @@ -42,15 +42,17 @@ func Test_Connection_with_MongoDB(t *testing.T) { when("running mongo.ConsumeDatabaseOnSession() to link p handler on '%[1]v' collection", func(it bdd.It) { var n int - var err error + var errCount, errLink error ConsumeDatabaseOnSession(func(db elements.Databaser) { - p := newProductCount(args[0].(string)) - n, err = p.Link(db).Count() + p := NewProductHandle() + _, errLink = p.Link(db) + n, errCount = p.Count() }) it("p.Count() should return no errors", func(assert bdd.Assert) { - assert.NoError(err) + assert.NoError(errLink) + assert.NoError(errCount) }) it("p.Count() should return 10", func(assert bdd.Assert) { assert.Equal(n, 10) @@ -58,9 +60,7 @@ func Test_Connection_with_MongoDB(t *testing.T) { }) Disconnect() - }, like( - s("test"), s("db01"), s("db02"), - )) + }) } // Feature Connect only with valid URLs. @@ -73,7 +73,7 @@ func Test_Connect_only_with_valid_URLs(t *testing.T) { makeMGO, _ := mocks.NewMockMGOSetup(t) makeMongo, _ := NewMockMongoSetup(t) - defer finish(makeMGO, makeMongo) + defer Finish(makeMGO, makeMongo) given, _, _ := bdd.Sentences() diff --git a/example/product.go b/example/product.go deleted file mode 100644 index d8fe9cf..0000000 --- a/example/product.go +++ /dev/null @@ -1,54 +0,0 @@ -package example - -import ( - "github.com/ddspog/mongo/model" - "github.com/globalsign/mgo/bson" -) - -// Product it's a type embedding the Document struct. -type Product struct { - IDV bson.ObjectId `json:"_id,omitempty" bson:"_id,omitempty"` - CreatedOnV int64 `json:"created_on,omitempty" bson:"created_on,omitempty"` - UpdatedOnV int64 `json:"updated_on,omitempty" bson:"updated_on,omitempty"` -} - -// NewProduct returns a empty Product. -func NewProduct() (p *Product) { - p = &Product{} - return -} - -// ID returns the _id attribute of a Document. -func (p *Product) ID() (id bson.ObjectId) { - id = p.IDV - return -} - -// CreatedOn returns the created_on attribute of a Document. -func (p *Product) CreatedOn() (t int64) { - t = p.CreatedOnV - return -} - -// UpdatedOn returns the updated_on attribute of a Document. -func (p *Product) UpdatedOn() (t int64) { - t = p.UpdatedOnV - return -} - -// GenerateID creates a new id for a document. -func (p *Product) GenerateID() { - p.IDV = model.NewID() -} - -// CalculateCreatedOn update the created_on attribute with a value -// corresponding to actual time. -func (p *Product) CalculateCreatedOn() { - p.CreatedOnV = model.NowInMilli() -} - -// CalculateUpdatedOn update the updated_on attribute with a value -// corresponding to actual time. -func (p *Product) CalculateUpdatedOn() { - p.UpdatedOnV = model.NowInMilli() -} diff --git a/helpers_test.go b/helpers_test.go index 2c1c39f..febb722 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -1,54 +1,13 @@ package mongo -import ( - "github.com/ddspog/mongo/elements" - "github.com/ddspog/mongo/handler" -) - -// productCounter it's a helping handler that only serves to count -// documents on collection. -type productCounter interface { - Name() string - Link(elements.Databaser) productCounter - Count() (int, error) -} - -// productCount it's a handle that implements productCounter. -type productCount struct { - *handler.Handle - name string -} - -// newProductCount returns a empty productCount. -func newProductCount(n string) (p *productCount) { - p = &productCount{ - Handle: handler.New(), - name: n, - } - return -} - -// Name returns the name of connection that productHandle can connect. -func (p *productCount) Name() (n string) { - n = p.name - return -} - -// Link connects the productHandle to collection. -func (p *productCount) Link(db elements.Databaser) (h productCounter) { - p.Handle.Link(db, p.Name()) - h = p - return -} - -// finisher defines a type that can be finished, closing all pendant +// Finisher defines a type that can be finished, closing all pendant // operations. -type finisher interface { +type Finisher interface { Finish() } -// finish calls Finish for all finishers received. -func finish(fs ...finisher) { +// Finish calls Finish for all finishers received. +func Finish(fs ...Finisher) { for _, f := range fs { f.Finish() } diff --git a/mocks/mock_helper.go b/mocks/mock_helper.go index 5d9787b..e7ce076 100644 --- a/mocks/mock_helper.go +++ b/mocks/mock_helper.go @@ -55,11 +55,11 @@ func (s *MockMGOSetup) SessionMock(db string, mdb *MockDatabaser) (ms *MockSessi // DatabaseMock create a Database mock that expect an C to return the // Collectioner mocked from function. -func (s *MockMGOSetup) DatabaseMock(n string, f func(*MockCollectioner)) (mdb *MockDatabaser) { +func (s *MockMGOSetup) DatabaseMock(name string, f func(*MockCollectioner)) (mdb *MockDatabaser) { mdb = NewMockDatabaser(s.controller()) mcl := NewMockCollectioner(s.controller()) f(mcl) - mdb.EXPECT().C(n).AnyTimes().Return(mcl) + mdb.EXPECT().C(name).AnyTimes().Return(mcl) return } diff --git a/example/productHandler.go b/product_test.go similarity index 70% rename from example/productHandler.go rename to product_test.go index 0b7c0f2..5ba880c 100644 --- a/example/productHandler.go +++ b/product_test.go @@ -1,4 +1,4 @@ -package example +package mongo import ( "github.com/ddspog/mongo/elements" @@ -108,3 +108,51 @@ func (p *ProductHandle) Document() (d *Product) { d = p.DocumentV return } + +// Product it's a type embedding the Document struct. +type Product struct { + IDV bson.ObjectId `json:"_id,omitempty" bson:"_id,omitempty"` + CreatedOnV int64 `json:"created_on,omitempty" bson:"created_on,omitempty"` + UpdatedOnV int64 `json:"updated_on,omitempty" bson:"updated_on,omitempty"` +} + +// NewProduct returns a empty Product. +func NewProduct() (p *Product) { + p = &Product{} + return +} + +// ID returns the _id attribute of a Document. +func (p *Product) ID() (id bson.ObjectId) { + id = p.IDV + return +} + +// CreatedOn returns the created_on attribute of a Document. +func (p *Product) CreatedOn() (t int64) { + t = p.CreatedOnV + return +} + +// UpdatedOn returns the updated_on attribute of a Document. +func (p *Product) UpdatedOn() (t int64) { + t = p.UpdatedOnV + return +} + +// GenerateID creates a new id for a document. +func (p *Product) GenerateID() { + p.IDV = model.NewID() +} + +// CalculateCreatedOn update the created_on attribute with a value +// corresponding to actual time. +func (p *Product) CalculateCreatedOn() { + p.CreatedOnV = model.NowInMilli() +} + +// CalculateUpdatedOn update the updated_on attribute with a value +// corresponding to actual time. +func (p *Product) CalculateUpdatedOn() { + p.UpdatedOnV = model.NowInMilli() +} diff --git a/example/setup.go b/setup_mongo_test.go similarity index 81% rename from example/setup.go rename to setup_mongo_test.go index 9d51fe0..d3bb434 100644 --- a/example/setup.go +++ b/setup_mongo_test.go @@ -1,6 +1,5 @@ -package example +package mongo -import "github.com/ddspog/mongo" import "github.com/ddspog/mongo/elements" func NewDBSocket() (db DatabaseSocketer) { @@ -22,7 +21,7 @@ type DatabaseSocket struct { } func (d *DatabaseSocket) DB() (db elements.Databaser) { - go mongo.ConsumeDatabaseOnSession(func(db elements.Databaser) { + go ConsumeDatabaseOnSession(func(db elements.Databaser) { d.db <- db <-d.quit })