Giter Site home page Giter Site logo

go-mocket's People

Contributors

daavaa avatar ddzhagayev avatar selvatico avatar smgladkovskiy avatar truecarry avatar vallieres avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-mocket's Issues

Support Upsert in postgres

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.

  1. detect whether there is a update followed by insert

  2. instead of checking command using first word, do a backward search of the key words lists.

What is this testing? I can't get this library to actual fail a test.

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?

  1. The primary key is getting set to 5577006791947779410, not 5.
  2. The WithReply is apparently not doing anything because this is not failing.
  3. My Type column above is an enumerated type. When I set the Type to an invalid value, this test still succeeds in flying colors.

According to go test, this test succeeds with flying colors.

RowsAffected not set during INSERT statement match

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?

MERGE statement DB2 support

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 ?

API Improvement

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(&notification) // 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.

Aggregate SQL function support

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

Add CREATE to Exec Commands

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.

Examples for mocking database transactions using go-mocket

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

Add A Way to do WithID using a UUID

Many schemas user UUID for a primary key (vs the default int). Add a method that gives the WithID() functionality with UUID, like WithUUID()

struggling with mock_catcher: check query and long queries with spaces

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.

ColumnTypes() seems to not be supported with v1.0.7

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

.WithId() is returning a different number then given

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;"`
}

Paremeter handling still not handled properly

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.

Parameter Handling

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.

WithReply values getting interchanged during tests

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.