214 lines
5.2 KiB
Go
214 lines
5.2 KiB
Go
package sq
|
||
|
||
import (
|
||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
func TestBuildInsertStatementBasic(t *testing.T) {
|
||
type r struct {
|
||
ID string `db:"id"`
|
||
Name string `db:"name"`
|
||
}
|
||
|
||
q := fakeQueryable{}
|
||
sqlstr, pp, err := BuildInsertStatement(q, "users", r{ID: "1", Name: "alice"})
|
||
tst.AssertNoErr(t, err)
|
||
|
||
tst.AssertTrue(t, strings.HasPrefix(sqlstr, "INSERT INTO users ("))
|
||
tst.AssertTrue(t, strings.Contains(sqlstr, "id"))
|
||
tst.AssertTrue(t, strings.Contains(sqlstr, "name"))
|
||
tst.AssertEqual(t, 2, len(pp))
|
||
|
||
values := []any{}
|
||
for _, v := range pp {
|
||
values = append(values, v)
|
||
}
|
||
|
||
hasID, hasName := false, false
|
||
for _, v := range values {
|
||
if vs, ok := v.(string); ok {
|
||
if vs == "1" {
|
||
hasID = true
|
||
}
|
||
if vs == "alice" {
|
||
hasName = true
|
||
}
|
||
}
|
||
}
|
||
tst.AssertTrue(t, hasID)
|
||
tst.AssertTrue(t, hasName)
|
||
}
|
||
|
||
func TestBuildInsertStatementSkipsUnexported(t *testing.T) {
|
||
type r struct {
|
||
ID string `db:"id"`
|
||
hidden string `db:"hidden"` //nolint:unused
|
||
}
|
||
|
||
q := fakeQueryable{}
|
||
sqlstr, pp, err := BuildInsertStatement(q, "users", r{ID: "1"})
|
||
tst.AssertNoErr(t, err)
|
||
tst.AssertEqual(t, 1, len(pp))
|
||
tst.AssertTrue(t, !strings.Contains(sqlstr, "hidden"))
|
||
}
|
||
|
||
func TestBuildInsertStatementSkipsNoTagAndDash(t *testing.T) {
|
||
type r struct {
|
||
ID string `db:"id"`
|
||
Skip1 string `db:"-"`
|
||
Skip2 string
|
||
}
|
||
|
||
q := fakeQueryable{}
|
||
sqlstr, pp, err := BuildInsertStatement(q, "users", r{ID: "1", Skip1: "x", Skip2: "y"})
|
||
tst.AssertNoErr(t, err)
|
||
tst.AssertEqual(t, 1, len(pp))
|
||
tst.AssertTrue(t, !strings.Contains(sqlstr, "Skip"))
|
||
}
|
||
|
||
func TestBuildInsertStatementNoFields(t *testing.T) {
|
||
type r struct {
|
||
Skip string
|
||
}
|
||
q := fakeQueryable{}
|
||
_, _, err := BuildInsertStatement(q, "x", r{})
|
||
if err == nil {
|
||
t.Fatal("expected error for no usable fields")
|
||
}
|
||
}
|
||
|
||
func TestBuildInsertStatementNilPointer(t *testing.T) {
|
||
type r struct {
|
||
ID string `db:"id"`
|
||
Note *string `db:"note"`
|
||
}
|
||
|
||
q := fakeQueryable{}
|
||
sqlstr, pp, err := BuildInsertStatement(q, "users", r{ID: "1", Note: nil})
|
||
tst.AssertNoErr(t, err)
|
||
|
||
// Only id is parameterized; nil pointer becomes literal NULL
|
||
tst.AssertEqual(t, 1, len(pp))
|
||
tst.AssertTrue(t, strings.Contains(sqlstr, "NULL"))
|
||
}
|
||
|
||
func TestBuildInsertStatementWithConverter(t *testing.T) {
|
||
type r struct {
|
||
ID string `db:"id"`
|
||
Flag bool `db:"flag"`
|
||
}
|
||
|
||
q := fakeQueryable{converters: []DBTypeConverter{ConverterBoolToBit}}
|
||
_, pp, err := BuildInsertStatement(q, "users", r{ID: "1", Flag: true})
|
||
tst.AssertNoErr(t, err)
|
||
tst.AssertEqual(t, 2, len(pp))
|
||
|
||
foundOne := false
|
||
for _, v := range pp {
|
||
if vi, ok := v.(int64); ok && vi == 1 {
|
||
foundOne = true
|
||
}
|
||
}
|
||
tst.AssertTrue(t, foundOne)
|
||
}
|
||
|
||
func TestBuildUpdateStatementBasic(t *testing.T) {
|
||
type r struct {
|
||
ID string `db:"id"`
|
||
Name string `db:"name"`
|
||
}
|
||
|
||
q := fakeQueryable{}
|
||
sqlstr, pp, err := BuildUpdateStatement(q, "users", r{ID: "1", Name: "alice"}, "id")
|
||
tst.AssertNoErr(t, err)
|
||
|
||
tst.AssertTrue(t, strings.HasPrefix(sqlstr, "UPDATE users SET "))
|
||
tst.AssertTrue(t, strings.Contains(sqlstr, "name = :"))
|
||
tst.AssertTrue(t, strings.Contains(sqlstr, "(id = :"))
|
||
tst.AssertEqual(t, 2, len(pp))
|
||
}
|
||
|
||
func TestBuildUpdateStatementMissingID(t *testing.T) {
|
||
type r struct {
|
||
Name string `db:"name"`
|
||
}
|
||
|
||
q := fakeQueryable{}
|
||
_, _, err := BuildUpdateStatement(q, "users", r{Name: "alice"}, "id")
|
||
if err == nil {
|
||
t.Fatal("expected error for missing id column")
|
||
}
|
||
}
|
||
|
||
func TestBuildUpdateStatementOnlyID(t *testing.T) {
|
||
type r struct {
|
||
ID string `db:"id"`
|
||
}
|
||
|
||
q := fakeQueryable{}
|
||
_, _, err := BuildUpdateStatement(q, "users", r{ID: "1"}, "id")
|
||
if err == nil {
|
||
t.Fatal("expected error when no SET clauses")
|
||
}
|
||
}
|
||
|
||
func TestBuildUpdateStatementNilPointer(t *testing.T) {
|
||
type r struct {
|
||
ID string `db:"id"`
|
||
Note *string `db:"note"`
|
||
}
|
||
|
||
q := fakeQueryable{}
|
||
sqlstr, _, err := BuildUpdateStatement(q, "users", r{ID: "1", Note: nil}, "id")
|
||
tst.AssertNoErr(t, err)
|
||
tst.AssertTrue(t, strings.Contains(sqlstr, "note = NULL"))
|
||
}
|
||
|
||
func TestBuildInsertMultipleStatementBasic(t *testing.T) {
|
||
type r struct {
|
||
ID string `db:"id"`
|
||
Name string `db:"name"`
|
||
}
|
||
|
||
q := fakeQueryable{}
|
||
sqlstr, pp, err := BuildInsertMultipleStatement(q, "users", []r{
|
||
{ID: "1", Name: "alice"},
|
||
{ID: "2", Name: "bob"},
|
||
})
|
||
tst.AssertNoErr(t, err)
|
||
|
||
tst.AssertTrue(t, strings.Contains(sqlstr, `INSERT INTO "users"`))
|
||
tst.AssertTrue(t, strings.Contains(sqlstr, `"id"`))
|
||
tst.AssertTrue(t, strings.Contains(sqlstr, `"name"`))
|
||
// 2 rows × 2 fields = 4 placeholders
|
||
tst.AssertEqual(t, 4, len(pp))
|
||
|
||
// Two value tuples should appear -> exactly one "), (" separator
|
||
tst.AssertEqual(t, 1, strings.Count(sqlstr, "), ("))
|
||
}
|
||
|
||
func TestBuildInsertMultipleStatementEmpty(t *testing.T) {
|
||
type r struct {
|
||
ID string `db:"id"`
|
||
}
|
||
q := fakeQueryable{}
|
||
_, _, err := BuildInsertMultipleStatement(q, "x", []r{})
|
||
if err == nil {
|
||
t.Fatal("expected error for empty input")
|
||
}
|
||
}
|
||
|
||
func TestBuildInsertMultipleStatementNilPointer(t *testing.T) {
|
||
type r struct {
|
||
ID string `db:"id"`
|
||
Note *string `db:"note"`
|
||
}
|
||
|
||
q := fakeQueryable{}
|
||
sqlstr, _, err := BuildInsertMultipleStatement(q, "users", []r{{ID: "1", Note: nil}})
|
||
tst.AssertNoErr(t, err)
|
||
tst.AssertTrue(t, strings.Contains(sqlstr, "NULL"))
|
||
}
|