there is a remaining expectation which was not matched: ExpectedQuery => expecting Query, QueryContext or QueryRow which - go-gorm

I have this repository
func (r *WorkspaceRepository) Delete(id any) (bool, error) {
if err := r.db.Delete(&model.Workspace{}, "id = ?", id).Error; err != nil {
return false, err
}
return true, nil
}
Which does a Soft-Delete, the query which this Delete operation runs is this one
UPDATE "workspaces" SET "deleted_at"='2022-07-04 09:09:20.778' WHERE id = 'c4610193-b43a-4ed7-9ed6-9d67b3f97502' AND "workspaces"."deleted_at" IS NULL
My test
uid := uuid.New()
mock.ExpectBegin()
mock.ExpectQuery(regexp.QuoteMeta(`UPDATE "workspaces" SET`)).WithArgs(sqlmock.AnyArg(), uid)
mock.ExpectCommit()
_, err = repository.Delete(uid)
assert.NoError(t, err)
I got the error
workspace_test.go:165:
Error Trace: workspace_test.go:165
Error: Received unexpected error:
call to ExecQuery 'UPDATE "workspaces" SET "deleted_at"=$1 WHERE id = $2 AND "workspaces"."deleted_at" IS NULL' with args [{Name: Ordinal:1 Value:2022-07-04 10:54:16.4863731 -0300 -03} {Name: Ordinal:2 Value:ae4124d9-ed16-4dcf-beb9-fcdcf27ca7da}], was not expected, next expectation is: ExpectedQuery => expecting Query, QueryContext or QueryRow which:
- matches sql: 'UPDATE "workspaces" SET'
- is with arguments:
0 - {}
1 - ae4124d9-ed16-4dcf-beb9-fcdcf27ca7da; call to Rollback transaction, was not expected, next expectation is: ExpectedQuery => expecting Query, QueryContext or QueryRow which:
- matches sql: 'UPDATE "workspaces" SET'
- is with arguments:
0 - {}
1 - ae4124d9-ed16-4dcf-beb9-fcdcf27ca7da
Test: TestDeleteWorkspace

It took me some time to figure out, but again, sqlmock error explains the problem if you know how to read the error message.
In our code, we execute UPDATE statement that triggers Exec db driver command instead of Query that is used for selects.
The error message explains that sqlmock expects code to perform one of these operations:
next expectation is: ExpectedQuery => expecting Query, QueryContext or
QueryRow
We need to use ExpectExec() instead of ExpectQuery() in test to match our code. We also need to define how many records are updated in our mocked DB by adding WillReturnResult(sqlmock.NewResult(1, 1)).
Fix one line and test should pass:
mock.ExpectExec(regexp.QuoteMeta(`UPDATE "workspaces" SET`)).WithArgs(sqlmock.AnyArg(), uid).WillReturnResult(sqlmock.NewResult(1, 1))

Related

Submitting an SQL query with a slice parameter

I have a Snowflake query where I'm trying to update a field on all items where another field is in a list which is submitted to the query as a variable:
UPDATE my_table SET download_enabled = ? WHERE provider_id = ? AND symbol IN (?)
I've tried doing this query using the gosnowflake.Array function like this:
enable := true
provider := 1
query := "UPDATE my_table SET download_enabled = ? WHERE provider_id = ? AND symbol IN (?)"
if _, err := client.db.ExecContext(ctx, query, enable, provider,
gosnowflake.Array(assets)); err != nil {
fmt.Printf("Error: %v", err)
}
However, this code fails with the following error:
002099 (42601): SQL compilation error: Batch size of 1 for bind variable 1 not the same as previous size of 2.
So then, how can I submit a variable representing a list of values to an SQL query?
I found a potential workaround, which is to submit each item in the list as a separate parameter explicitly:
func Delimit(s string, sep string, count uint) string {
return strings.Repeat(s+sep, int(count)-1) + s
}
func doQuery(enable bool, provider int, assets ...string) error {
query := fmt.Sprintf("UPDATE my_table SET download_enabled = ? " +
"WHERE provider_id = ? AND symbol IN (%s)", Delimit("?", ", ", uint(len(assets))))
params := []interface{}{enable, provider}
for _, asset := range assets {
params = append(params, asset)
}
if _, err := client.db.ExecContext(ctx, query, params...); err != nil {
return err
}
return nil
}
Needless to say this is a less elegant solution then what I wanted but it does work.

How to Unit Test Gorm Golang Preload Query

Recently, I was trying to query data with preload function that gorm provided.
Here's the actual implementation looks like:
func (repo *SectorRepository) GetSectorsByStockCode(stockCode string) *model.Sector {
var foundStocks []*model.Stock
repo.DB.Where("code = ?", stockCode).Preload("Sectors").Find(&foundStocks)
if foundStocks == nil {
return nil
} else if len(foundStocks) == 0 {
return nil
} else if foundStocks[0].Sectors == nil {
return nil
} else if len(foundStocks[0].Sectors) == 0 {
return nil
} else {
return foundStocks[0].Sectors[0]
}
}
Then this is what I've done on my unit test:
func Test_SectorRepo_GetSectorsByStockCode(t *testing.T) {
db, gdb, sqlMock, err := InitTestSectorRepo()
require.NoError(t, err)
defer db.Close()
repository := repository.SectorRepository{DB: gdb}
var stockCode = "AAPL"
var expectedCode = "code1"
var expectedName = "name1"
var exchangeCode = "exchange_code1"
var sectorCode = "sector_code1"
var now = time.Now()
sqlMock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "stocks" WHERE code = $1`)).WithArgs(stockCode).WillReturnRows(
sqlMock.NewRows([]string{"code", "name", "exchange_code", "created_at", "updated_at"}).
AddRow(expectedCode, expectedName, exchangeCode, now, now),
)
sqlMock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "stock_sectors" WHERE ("stock_sectors"."stock_code","stock_sectors"."stock_exchange_code") IN (($1,$2))`)).
WithArgs(expectedCode, exchangeCode).WillReturnRows(sqlMock.NewRows([]string{"stock_code", "stock_exchange_code", "sector_code"}).AddRow(expectedCode, exchangeCode, sectorCode))
sqlMock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "sectors" WHERE "sectors"."code" = $1'`)).WithArgs(stockCode).WillReturnRows(
sqlMock.NewRows([]string{"code", "name", "created_at", "updated_at"}).AddRow(sectorCode, "sector_name1", now, now),
)
res := repository.GetSectorsByStockCode(stockCode)
require.Equal(t, res.Name, expectedName)
}
But I got a failing message as provided below:
Running tool: /usr/local/go/bin/go test -timeout 30s -run ^Test_SectorRepo_GetSectorsByStockCode$ gitlab.com/xxx/yyy-service/test/repository
2022/07/02 20:38:10 [32m/Users/aaa/yyy-service/api/repository/sector_repository.go:38
[0m[33m[0.045ms] [34;1m[rows:1][0m SELECT * FROM "stock_sectors" WHERE ("stock_sectors"."stock_code","stock_sectors"."stock_exchange_code") IN (('code1','exchange_code1'))
2022/07/02 20:38:10 [31;1m/Users/aaa/yyy-service/api/repository/sector_repository.go:38 [35;1mQuery: could not match actual sql: "SELECT * FROM "sectors" WHERE "sectors"."code" = $1" with expected regexp "SELECT \* FROM "sectors" WHERE "sectors"\."code" = \$1'"
[0m[33m[0.025ms] [34;1m[rows:0][0m SELECT * FROM "sectors" WHERE "sectors"."code" = 'sector_code1'
2022/07/02 20:38:10 [31;1m/Users/aaa/yyy-service/api/repository/sector_repository.go:38 [35;1mQuery: could not match actual sql: "SELECT * FROM "sectors" WHERE "sectors"."code" = $1" with expected regexp "SELECT \* FROM "sectors" WHERE "sectors"\."code" = \$1'"
[0m[33m[1.300ms] [34;1m[rows:1][0m SELECT * FROM "stocks" WHERE code = 'AAPL'
--- FAIL: Test_SectorRepo_GetSectorsByStockCode (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x10 pc=0x14499f6]
goroutine 4 [running]:
testing.tRunner.func1.2({0x1497620, 0x1896c40})
/usr/local/go/src/testing/testing.go:1209 +0x24e
testing.tRunner.func1()
/usr/local/go/src/testing/testing.go:1212 +0x218
panic({0x1497620, 0x1896c40})
/usr/local/go/src/runtime/panic.go:1038 +0x215
gitlab.com/xxx/yyy-service/test/repository_test.Test_SectorRepo_GetSectorsByStockCode(0x0)
/Users/aaa/yyy-service/test/repository/sector_repository_test.go:131 +0x8b6
testing.tRunner(0xc00011b380, 0x1526fd8)
/usr/local/go/src/testing/testing.go:1259 +0x102
created by testing.(*T).Run
/usr/local/go/src/testing/testing.go:1306 +0x35a
FAIL gitlab.com/xxx/yyy-service/test/repository 0.533s
FAIL
I thought in the first place that gorm preload function will create subsequent queries, hence I on the above code I was trying to evaluate all the subsequent queries.
Until now I'm still figuring it out if it's possible to test the preload gorm query by using sqlmock.
If there's anyone who could help to answer this problem will be much appreciated :)

Golang SQL error expected 0 arguments got 3

I'm using github.com/denisenkom/go-mssqldb library and driver but getting an error sql: expected 0 arguments, got 3 exit status 1 when inserting new row.
tsql := "INSERT INTO Uploads (Fname, Fsize, Ftype) VALUES (#Fname, #Fsize, #Ftype );"
fmt.Printf("tsql = %s\n", tsql)
//Execute non-query with named parameters
res, err := db.ExecContext(
ctx,
tsql,
sql.Named("Fname", fname),
sql.Named("Fsize", fsize),
sql.Named("Ftype", ftype))
if err != nil {
log.Fatal(" AddRow_v1() -> Error creating new row: " + err.Error())
return -1, err
}
This issue might be related to driver name used in the connection string.
I've tried the same query with yours, the record is created without any errors.
I believe that you are currently using mssql in connection string; sql.Open("mssql", conn) (This issue has already been discussed in https://github.com/denisenkom/go-mssqldb/issues/594#issuecomment-809922317)
If you try again by replacing "mssql" to "sqlserver", the problem should be solved.

gorm raw sql query execution

Am running a query to check if a table exists or not using the gorm orm for golang. Below is my code.
package main
import (
"fmt"
"log"
"gorm.io/driver/postgres"
"gorm.io/gorm"
_ "github.com/lib/pq"
)
// App sets up and runs the app
type App struct {
DB *gorm.DB
}
`const tableCreationQuery = `SELECT count (*)
FROM information_schema.TABLES
WHERE (TABLE_SCHEMA = 'api_test') AND (TABLE_NAME = 'Users')`
func ensureTableExists() {
if err := a.DB.Exec(tableCreationQuery); err != nil {
log.Fatal(err)
}
}`
The expected response should be either 1 or 0. I got this from another SO answer. Instead I get this
2020/09/03 00:27:18 &{0xc000148900 1 0xc000119ba0 0}
exit status 1
FAIL go-auth 0.287s
My untrained mind says its a pointer but how do I reference the returned values to determine what was contained within?
If you want to check if your SQL statement was successfully executed in GORM you can use the following:
tx := DB.Exec(sqlStr, args...)
if tx.Error != nil {
return false
}
return true
However in your example are using a SELECT statement then you need to check the result, which will be better suited to use the DB.Raw() method like below
var exists bool
DB.Raw(sqlStr).Row().Scan(&exists)
return exists

How to ignore the error from attempting to drop tables that don't exist using RODBC?

I have a function that catches errors resulting from odbcQuery. How would one make it so that attempting to drop tables that don't exist does not throw an error?
The function:
runQuery <- function(script, channel, errors = T) {
run <- odbcQuery(channel, script)
# if the function has returned an error
if (run == -1L) {
if (errors) {
err <- odbcGetErrMsg(channel)
cat("\nobdcQuery() encountered an error running the following SQL ")
cat("query:\n\n", script, "\n\nError from Oracle server:\n")
return(err)
} else {
err <- invisible(run)
}
}
}