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")) }