aboutsummaryrefslogtreecommitdiff
path: root/query_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'query_test.go')
-rw-r--r--query_test.go201
1 files changed, 196 insertions, 5 deletions
diff --git a/query_test.go b/query_test.go
index 388183e..000c385 100644
--- a/query_test.go
+++ b/query_test.go
@@ -87,7 +87,7 @@ func TestUpdateBasic(t *testing.T) {
Name string `db:"name"`
}
store := oblast.MustNewStore[basicRecord](
- oblast.PostgresDialect(),
+ oblast.SqliteDialect(),
oblast.TableNameIs("basic_records"),
oblast.PrimaryKeyIs("id"),
)
@@ -98,7 +98,7 @@ func TestUpdateBasic(t *testing.T) {
for idx := range batchSize {
r := basicRecord{ID: int64(42 + idx), Name: "updated"}
records[idx] = r
- md.ForQuery(`UPDATE "basic_records" SET "name" = $1 WHERE "id" = $2`).
+ md.ForQuery(`UPDATE "basic_records" SET "name" = ? WHERE "id" = ?`).
ExpectExecWithArgs(r.Name, r.ID).
AndReturnRowsAffected(1)
}
@@ -116,7 +116,7 @@ func TestDeleteBasic(t *testing.T) {
Name string `db:"name"`
}
store := oblast.MustNewStore[basicRecord](
- oblast.PostgresDialect(),
+ oblast.SqliteDialect(),
oblast.TableNameIs("basic_records"),
oblast.PrimaryKeyIs("id"),
)
@@ -127,7 +127,7 @@ func TestDeleteBasic(t *testing.T) {
for idx := range batchSize {
r := basicRecord{ID: int64(42 + idx), Name: "removed"}
records[idx] = r
- md.ForQuery(`DELETE FROM "basic_records" WHERE "id" = $1`).
+ md.ForQuery(`DELETE FROM "basic_records" WHERE "id" = ?`).
ExpectExecWithArgs(r.ID).
AndReturnRowsAffected(1)
}
@@ -136,4 +136,195 @@ func TestDeleteBasic(t *testing.T) {
}
}
-// TODO: more test coverage for query.go
+func TestWriteQueriesNotPossible(t *testing.T) {
+ md := mock.NewDriver()
+ db := sql.OpenDB(md)
+
+ type basicRecord struct {
+ ID int64 `db:"id,auto"`
+ Name string `db:"name"`
+ }
+ store := oblast.MustNewStore[basicRecord](
+ oblast.SqliteDialect(),
+ // no TableNameIs() or PrimaryKeyIs() given
+ )
+
+ r := basicRecord{Name: "foo"}
+ _, err := store.Insert(db, r)
+ assert.ErrEqual(t, err, "cannot execute Insert() because query could not be autogenerated")
+
+ r.ID = 42
+ err = store.Update(db, r)
+ assert.ErrEqual(t, err, "cannot execute Update() because query could not be autogenerated")
+
+ err = store.Delete(db, r)
+ assert.ErrEqual(t, err, "cannot execute Delete() because query could not be autogenerated")
+}
+
+func TestWriteQueriesFailDuringPrepare(t *testing.T) {
+ md := mock.NewDriver()
+ db := sql.OpenDB(md)
+
+ type basicRecord struct {
+ ID int64 `db:"id,auto"`
+ Name string `db:"name"`
+ }
+ store := oblast.MustNewStore[basicRecord](
+ oblast.SqliteDialect(),
+ oblast.TableNameIs("basic_records"),
+ oblast.PrimaryKeyIs("id"),
+ )
+
+ for _, batchSize := range []int{1, oblast.PrepareThreshold - 1, oblast.PrepareThreshold + 1} {
+ records := make([]basicRecord, batchSize)
+ for idx := range batchSize {
+ records[idx] = basicRecord{Name: "foo"}
+ }
+
+ _, err := store.Insert(db, records...)
+ baseError := `unexpected query: INSERT INTO "basic_records" ("name") VALUES (?)`
+ if batchSize < oblast.PrepareThreshold {
+ assert.ErrEqual(t, err, "during Exec() for record with idx = 0: "+baseError)
+ } else {
+ assert.ErrEqual(t, err, "during Prepare(): "+baseError)
+ }
+
+ for idx := range batchSize {
+ records[idx].ID = int64(42 + idx)
+ }
+
+ err = store.Update(db, records...)
+ baseError = `unexpected query: UPDATE "basic_records" SET "name" = ? WHERE "id" = ?`
+ if batchSize < oblast.PrepareThreshold {
+ assert.ErrEqual(t, err, "during Exec() for record with idx = 0: "+baseError)
+ } else {
+ assert.ErrEqual(t, err, "during Prepare(): "+baseError)
+ }
+
+ err = store.Delete(db, records...)
+ baseError = `unexpected query: DELETE FROM "basic_records" WHERE "id" = ?`
+ if batchSize < oblast.PrepareThreshold {
+ assert.ErrEqual(t, err, "during Exec() for record with idx = 0: "+baseError)
+ } else {
+ assert.ErrEqual(t, err, "during Prepare(): "+baseError)
+ }
+ }
+
+ store = oblast.MustNewStore[basicRecord](
+ oblast.PostgresDialect(), // for test coverage of insertUsingReturningClause()
+ oblast.TableNameIs("basic_records"),
+ oblast.PrimaryKeyIs("id"),
+ )
+
+ for _, batchSize := range []int{1, oblast.PrepareThreshold - 1, oblast.PrepareThreshold + 1} {
+ records := make([]basicRecord, batchSize)
+ for idx := range batchSize {
+ records[idx] = basicRecord{Name: "foo"}
+ }
+
+ _, err := store.Insert(db, records...)
+ baseError := `unexpected query: INSERT INTO "basic_records" ("name") VALUES ($1) RETURNING "id"`
+ if batchSize < oblast.PrepareThreshold {
+ assert.ErrEqual(t, err, "during QueryRow() for record with idx = 0: "+baseError)
+ } else {
+ assert.ErrEqual(t, err, "during Prepare(): "+baseError)
+ }
+ }
+}
+
+func TestUpdateFailsOnMissingRecord(t *testing.T) {
+ md := mock.NewDriver()
+ db := sql.OpenDB(md)
+
+ type basicRecord struct {
+ ID int64 `db:"id,auto"`
+ Name string `db:"name"`
+ }
+ store := oblast.MustNewStore[basicRecord](
+ oblast.SqliteDialect(),
+ oblast.TableNameIs("basic_records"),
+ oblast.PrimaryKeyIs("id"),
+ )
+
+ md.ForQuery(`UPDATE "basic_records" SET "name" = ? WHERE "id" = ?`).
+ ExpectExecWithArgs("changed", 42).
+ AndReturnRowsAffected(0)
+ err := store.Update(db, basicRecord{ID: 42, Name: "changed"})
+ assert.ErrEqual(t, err, "could not UPDATE record that does not exist in the database: id = 42")
+ _, hasCorrectType := err.(oblast.MissingRecordError[basicRecord]) //nolint:errorlint // we explicitly do not want a wrapped error
+ assert.Equal(t, hasCorrectType, true)
+}
+
+func TestInsertWithUnsignedIdField(t *testing.T) {
+ md := mock.NewDriver()
+ db := sql.OpenDB(md)
+
+ type basicRecord struct {
+ ID uint64 `db:"id,auto"` // not int64!
+ Name string `db:"name"`
+ }
+
+ t.Run("using LastInsertID", func(t *testing.T) {
+ store := oblast.MustNewStore[basicRecord](
+ oblast.SqliteDialect(),
+ oblast.TableNameIs("basic_records"),
+ oblast.PrimaryKeyIs("id"),
+ )
+
+ // success case
+ md.ForQuery(`INSERT INTO "basic_records" ("name") VALUES (?)`).
+ ExpectExecWithArgs("first").
+ AndReturnLastInsertId(42).
+ AndReturnRowsAffected(1)
+ records := must.Return(store.Insert(db, basicRecord{Name: "first"}))(t)
+ assert.SliceEqual(t, records, basicRecord{ID: 42, Name: "first"})
+
+ // error case: negative ID cannot be cast to uint64
+ md.ForQuery(`INSERT INTO "basic_records" ("name") VALUES (?)`).
+ ExpectExecWithArgs("second").
+ AndReturnLastInsertId(-42).
+ AndReturnRowsAffected(1)
+ _, err := store.Insert(db, basicRecord{Name: "second"})
+ assert.ErrEqual(t, err, "LastInsertId() = -42 for record with idx = 0 cannot be converted to uint")
+
+ // error case: cannot Insert() a record that already has its ID field filled
+ md.ForQuery(`INSERT INTO "basic_records" ("name") VALUES (?)`).
+ ExpectExecWithArgs("third").
+ AndReturnLastInsertId(42).
+ AndReturnRowsAffected(1)
+ _, err = store.Insert(db, basicRecord{ID: 23, Name: "third"})
+ assert.ErrEqual(t, err, `refusing to INSERT record with idx = 0 that already has non-zero values in its "auto" columns`)
+ })
+
+ t.Run("using RETURNING clause", func(t *testing.T) {
+ store := oblast.MustNewStore[basicRecord](
+ oblast.PostgresDialect(),
+ oblast.TableNameIs("basic_records"),
+ oblast.PrimaryKeyIs("id"),
+ )
+
+ // success case
+ md.ForQuery(`INSERT INTO "basic_records" ("name") VALUES ($1) RETURNING "id"`).
+ ExpectQueryWithArgs("first").
+ AndReturnColumns("id").
+ WithRow(42)
+ records := must.Return(store.Insert(db, basicRecord{Name: "first"}))(t)
+ assert.SliceEqual(t, records, basicRecord{ID: 42, Name: "first"})
+
+ // error case: negative ID cannot be cast to uint64
+ md.ForQuery(`INSERT INTO "basic_records" ("name") VALUES ($1) RETURNING "id"`).
+ ExpectQueryWithArgs("second").
+ AndReturnColumns("id").
+ WithRow(-42)
+ _, err := store.Insert(db, basicRecord{Name: "second"})
+ assert.ErrEqual(t, err, `during QueryRow() for record with idx = 0: sql: Scan error on column index 0, name "id": converting driver.Value type int ("-42") to a uint64: invalid syntax`)
+
+ // error case: cannot Insert() a record that already has its ID field filled
+ md.ForQuery(`INSERT INTO "basic_records" ("name") VALUES ($1) RETURNING "id"`).
+ ExpectQueryWithArgs("third").
+ AndReturnColumns("id").
+ WithRow(42)
+ _, err = store.Insert(db, basicRecord{ID: 23, Name: "third"})
+ assert.ErrEqual(t, err, `refusing to INSERT record with idx = 0 that already has non-zero values in its "auto" columns`)
+ })
+}