selvatico / go-mocket Goto Github PK
View Code? Open in Web Editor NEWGo GORM & SQL mocking library
License: MIT License
Go GORM & SQL mocking library
License: MIT License
The statment of UPSERT in postgres:
INSERT INTO ... ON CONFLICT ON CONSTRAINT DO UPDATE ...
Currently in
https://github.com/Selvatico/go-mocket/blob/master/stmt.go#L16, command only takes in first word.
And for INSERT Case, there is only RowsAffected = 1 option if do not identify the error.
https://github.com/Selvatico/go-mocket/blob/master/stmt.go#L86
Proposed solution, one of the two will work for postgres.
detect whether there is a update followed by insert
instead of checking command using first word, do a backward search of the key words lists.
func SetupTests(t *testing.T) *gorm.DB {
mocket.Catcher.Register()
mocket.Catcher.Logging = true
db, err := gorm.Open(mocket.DriverName, "connection_string")
if err != nil {
t.Fatalf(err.Error())
}
return db
}
func TestInsertStoragePool(t *testing.T) {
cases := []struct {
givenPool StoragePool
wantedError error
}{
{ // happy path
givenPool: StoragePool{
Name: "POOL1",
PoolId: "1",
Type: "NetworkFilesystem",
Volumes: []Volume{
{
Name: "VOL01",
StorageId: "1",
Type: "ROOT",
VolumeId: "1",
},
},
},
wantedError: nil,
},
}
for _, c := range cases {
db := SetupTests(t)
db.LogMode(true)
reply := []map[string]interface{}{{"id": 5}}
mocket.Catcher.Reset().NewMock().WithQuery("INSERT INTO \"storage_pools\"").WithID(int64(5)).WithArgs(c.givenPool).WithReply(reply)
err := InsertStoragePool(db, &c.givenPool)
if err != nil {
t.Error(err)
}
fmt.Fprintf(os.Stderr, "\nPOOLID=%d\n", c.givenPool.ID)
}
}
What is this testing?
According to go test, this test succeeds with flying colors.
Curious if this is planned or not?
I have the following code testing an INSERT. The goal is to simulate a key already exists; which, when specified with 'ON CONFLICT DO NOTHING' returns no error and instead just RowsAffected() on the result returns 0.
GlobalMock := mocket.Catcher.Reset()
GlobalMock.Logging = true
expectedReservation := GetReservation()
GlobalMock.NewMock().WithQuery(
`INSERT INTO "reservations" ("created_at","updated_at","deleted_at","dataset_project","dataset_name","dataset_domain","dataset_version","tag_name","owner_id","expires_at","serialized_metadata") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11) ON CONFLICT DO NOTHING`,
).WithRowsNum(0)
What I am seeing is that no matter what I change the WithRowsNum() called value to the RowsAffected always returns 1. This same syntax works well in testing an UPDATE command, so it's peculiar that it is not working here. Any thoughts?
Using go_ibm_db with DB2 and occasional use MERGE statement then found
unimplemented statement Exec command type of MERGE
then is it possible to support DB2 statement ?
I'd like to propose a new API, take a look at this example:
import "testing"
import mocket "github.com/selvatico/go-mocket"
import "app/app/entity"
func TestDelete(t *testing.T) {
var expectedId int = 1
notification := entity.Notification{}
notification.SetId(expectedId)
notificationService := GetNotificationService()
definitions := map[string]interface{}{
"type": "update",
"table": "notifications",
"set": map[string]interface{}{"deleted_at": mocket.Any},
"where": map[string]interface{}{"id": expectedId},
}
query := mocket.NewQueryMock(definitions) // create the query mock
mocket.WatchQuery(query) // start the watcher
notificationService.Delete(¬ification) // Trigger query
// assertion, it can be made of two ways
// it can be:
mocket.assertWasCalled(query, t) // assert it was called
// or:
if mocket.assertWasCalled(query) == false {
t.Fail()
}
}
I think this is more readable, and easy to write.
@Selvatico Does go-mocket have support for aggregate SQL functions like count()?
In GORM if I'm doing something like:
dbHandle.Model(&models.User{}).Where("user_id = ?", username).Count(&count)
How do I mock this?
Using Gorm with Postgres, CREATE commands (CREATE TABLE, CREATE DATABASE) generate error:
unimplemented statement Exec command type of "CREATE"
Looks like an easy fix to add this.
Hi, I am using gorm as ORM in my golang project. I was initially using sqlmock for unit testing now I have switched to go-mocket since it is recommended by the gorm-community and I faced issues while mocking postgres INSERT queries. But now the problem is that I am unable to mock my db transactions. Can someone guide me with this
Many schemas user UUID for a primary key (vs the default int). Add a method that gives the WithID() functionality with UUID, like WithUUID()
Great library. Works wonderfully, except when I am trying to match long multiline queries. copied the query recommended by the
2019/06/26 11:29:07 mock_catcher: check query:
SELECT c.week, c.count FROM (
SELECT date_trunc('week', scheduled_at) week, count(*)
FROM w
WHERE id=abf10e8d-5607-430e-acd0-f6bd6a201631
AND scheduled_at <> 0001-01-01 00:00:00 +0000 UTC
AND is_optional IS NOT true
AND type <> rest
GROUP BY week
ORDER BY week) s
JOIN
(
SELECT date_trunc('week', completed_at) week, count(*)
FROM w
WHERE id=abf10e8d-5607-430e-acd0-f6bd6a201631
AND completed_at <> 0001-01-01 00:00:00 +0000 UTC
AND is_optional IS NOT true
AND type <> rest
GROUP BY week
ORDER BY week
) completed
ON s.week=c.week
WHERE c.count>=s.count
Struggling to insert the query into my test and have it match. Returns [0 rows affected or returned ]
It seems very sensitive to spacing? Am I not copying the spaces properly? Is it possible to make it ignore spacing/tabs?
Any advice would be very appreciated. Thank you.
When calling ColumnTypes on a rows object, I get:
panic: runtime error: index out of range [0] with length 0 [recovered]
panic: runtime error: index out of range [0] with length 0
goroutine 67 [running]:
testing.tRunner.func1.1(0x1ba1bc0, 0xc00078d240)
/usr/local/Cellar/go/1.14/libexec/src/testing/testing.go:941 +0x3d0
testing.tRunner.func1(0xc000902480)
/usr/local/Cellar/go/1.14/libexec/src/testing/testing.go:944 +0x3f9
panic(0x1ba1bc0, 0xc00078d240)
/usr/local/Cellar/go/1.14/libexec/src/runtime/panic.go:967 +0x15d
github.com/Selvatico/go-mocket.(*RowsCursor).ColumnTypeScanType(0xc000910180, 0x0, 0xc000910180, 0xa60b848)
/Users/ianzink/go/pkg/mod/github.com/!selvatico/[email protected]/rows.go:51 +0x8d
database/sql.rowsColumnInfoSetupConnLocked(0x1fb79e0, 0xc000910180, 0xc000455d08, 0x128efa7, 0x1fb31a0)
/usr/local/Cellar/go/1.14/libexec/src/database/sql/sql.go:2944 +0x135
database/sql.(*Rows).ColumnTypes(0xc000910200, 0x0, 0x0, 0x0, 0x0, 0x0)
/usr/local/Cellar/go/1.14/libexec/src/database/sql/sql.go:2872 +0x1ba
When I run
var mockedId int64 = 1
mocket.Catcher.Reset().NewMock().WithQuery("INSERT INTO groups").WithId(mockedId)
And then run a GORM CREATE
if err := db.Create(&group).Error; err != nil {
log.Printf("Error creating group: %v", err)
return nil, err
}
My group ID begin returned is 5577006791947779410
It's not that big of a deal because I can just test that an ID is there.
My group model is
type Group struct {
gorm.Model // Imports the ID field which is uint
Name string
Description string
Users []User `gorm:"many2many:groups_users;"`
}
I noticed that the statement prepared by go-mocket and the one prepared by GORM were not the exact same:
`MOCK_FAKE_DRIVER` is not officially supported, running under compatibility mode.
2020/01/30 22:30:01 mock_catcher: check query: SELECT * FROM "documentos" WHERE "documentos"."deleted_at" IS NULL AND ((clave = 50602121900026335208700100001010000000000181580890)) ORDER BY "documentos"."id" ASC LIMIT 1
(/home/jorge/code/kue/fero/dev/backend/utils.go:450)
[2020-01-30 22:30:01] [1.00ms] SELECT * FROM "documentos" WHERE "documentos"."deleted_at" IS NULL AND ((clave = '50602121900026335208700100001010000000000181580890')) ORDER BY "documentos"."id" ASC LIMIT 1
[0 rows affected or returned ]
notice that the long string of numbers has single quotes and the statement prepared by GORM has them. In #2 someone pointed out what was the issue (this line https://github.com/Selvatico/go-mocket/blob/master/stmt.go#L114 ). I'm aware of the challenge with getting a different query from GORM than the one go-mocket prepares, I would advise on providing users with a diff of sorts to warn them about the differences because small things like single quotes vs no quotes made me spent several hours figuring out what I was doing wrong.
The subject is vague, but hear me out. Parameter handling is broken. If you have '%' in a parameter it will cause the output to be formatted incorrectly by formatting the previously formatted string. Offending code here:
https://github.com/Selvatico/go-mocket/blob/master/stmt.go#L111
I was in the middle of writing a PR to make it handle the question marks properly, but then I noticed this test:
https://github.com/Selvatico/go-mocket/blob/master/response_test.go#L154
Which claimed to handle postgres dollar-style parameters as well, however the test never does anything with the parameters (only a partial match on the query string).
This got me thinking. I'm not sure that the code (in stmt.go line 111) should be trying to place the parameters in the query string at all. Technically, the database itself receives the parameters along with the parameterized query and does the parsing itself, at least in the case of Postgres (I'd imagine other DBs do it this way as well): https://www.pgcon.org/2014/schedule/attachments/330_postgres-for-the-wire.pdf
I think it would be easier to use as well if it didn't attempt to replace the parameters, as you could do a query match + parameters match to get a more precise match.
If you agree, I can submit a PR to remove the code in stmt.go line 111.
My tests are passing half of the time and failing half of the time when using go-mocket mock and a response struct.
example test:
Describe("GetApplicationStatuses", func() {
Context("on success", func() {
It("should should write inReview and ineligible counts to context", func() {
params := []gin.Param{gin.Param{Key: "company_id", Value: "1"}}
mockContext.Params = params
mockContext.Set("employeeIDs", []string{"1a"})
mockReply := []map[string]interface{}{{"InReview": 1, "Ineligible": 2}}
globalMock.NewMock().
WithQuery("case when app.status in('SUBMITTED','REQUIRES_ACTION')").
WithReply(mockReply)
returnedFunc := GetApplicationStatuses()
returnedFunc(mockContext)
var expectValue = ApplicationStatus{InReview: 1, Ineligible: 2}
Expect(mockContext.Value("Applications")).To(Equal(expectValue))
})
})
failure:
Expected
<middleware.ApplicationStatus>: {InReview: 2, Ineligible: 1}
to equal
<middleware.ApplicationStatus>: {InReview: 1, Ineligible: 2}
func & struct being tested:
func GetApplicationStatuses() gin.HandlerFunc {
return func(c *gin.Context) {
var (
inReview,
ineligible int
applications ApplicationStatus
)
rows, err := db.Table("application as app").
Select("COALESCE(sum(case when app.status in('SUBMITTED','REQUIRES_ACTION') then 1 else 0 end),0) as in_review, "+
"COALESCE(sum(case when app.status = 'INELIGIBLE' then 1 else 0 end),0) as ineligible").
Where("app.id in(select employee.id from employee where employee.x_id in(?))", c.Value("employeeIDs")).
Rows()
if err != nil {
utils.LogError(
fmt.Sprintf("Error getting application statuses: %v", err),
"middleware.GetApplicationStatuses",
)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
"message": "Error getting application statuses.",
})
return
}
for rows.Next() {
err := rows.Scan(&inReview, &ineligible)
if err != nil {
utils.LogError(
fmt.Sprintf("Error getting application statuses: %v", err),
"middleware.GetApplicationStatuses",
)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
"message": "Error getting application statuses.",
})
return
}
applications = ApplicationStatus{inReview, ineligible}
}
c.Set("Applications", applications)
}
}
// ApplicationStatus struct for response to GetApplicationStatuses
type ApplicationStatus struct {
InReview int `json:"inReview gorm:"column:InReview"`
Ineligible int `json:"ineligible" gorm:"column:Ineligible"`
}
half of the time the test passes fine, the other half it fails because InReview and Ineligible values are swapped. I've been banging my head against the wall with it for a week now. any help would be greatly appreciated. Seems to be happening anytime I'm using a response struct or there is > 1 column on the WithReply. Thanks for a great mocking package btw.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.