stardb/orm_test.go

276 lines
6.3 KiB
Go
Raw Permalink Normal View History

2026-03-07 19:27:44 +08:00
package stardb
import (
"errors"
"reflect"
2026-03-07 19:27:44 +08:00
"testing"
"time"
)
type User struct {
ID int64 `db:"id"`
Name string `db:"name"`
Email string `db:"email"`
Age int `db:"age"`
Balance float64 `db:"balance"`
Active bool `db:"active"`
CreatedAt time.Time `db:"created_at"`
}
type Profile struct {
UserID int `db:"user_id"`
Bio string `db:"bio"`
Avatar string `db:"avatar"`
}
type NestedUser struct {
ID int64 `db:"id"`
Name string `db:"name"`
Profile `db:"---"`
}
type NestedUserPtr struct {
ID int64 `db:"id"`
Name string `db:"name"`
Profile *Profile `db:"---"`
}
type AutoIDOnly struct {
ID int64 `db:"id"`
}
type HiddenTagged struct {
ID int64 `db:"id"`
hidden string `db:"hidden"`
}
2026-03-07 19:27:44 +08:00
func TestBuildInsertSQL(t *testing.T) {
user := User{
ID: 1,
Name: "Test",
Email: "test@example.com",
Age: 30,
Balance: 100.0,
Active: true,
CreatedAt: time.Now(),
}
query, params, err := buildInsertSQL(&user, "users", "id")
if err != nil {
t.Fatalf("buildInsertSQL failed: %v", err)
}
expectedQuery := "INSERT INTO users (name, email, age, balance, active, created_at) VALUES (?, ?, ?, ?, ?, ?)"
if query != expectedQuery {
t.Errorf("Expected query:\n%s\nGot:\n%s", expectedQuery, query)
}
expectedParams := []string{":name", ":email", ":age", ":balance", ":active", ":created_at"}
if len(params) != len(expectedParams) {
t.Errorf("Expected %d params, got %d", len(expectedParams), len(params))
}
for i, param := range params {
if param != expectedParams[i] {
t.Errorf("Expected param %s, got %s", expectedParams[i], param)
}
}
}
func TestBuildUpdateSQL(t *testing.T) {
user := User{
ID: 1,
Name: "Test",
Email: "test@example.com",
Age: 30,
Balance: 100.0,
Active: true,
CreatedAt: time.Now(),
}
query, params, err := buildUpdateSQL(&user, "users", "id")
if err != nil {
t.Fatalf("buildUpdateSQL failed: %v", err)
}
expectedQuery := "UPDATE users SET name = ?, email = ?, age = ?, balance = ?, active = ?, created_at = ? WHERE id = ?"
2026-03-07 19:27:44 +08:00
if query != expectedQuery {
t.Errorf("Expected query:\n%s\nGot:\n%s", expectedQuery, query)
}
expectedParamCount := 7 // 6 fields + 1 primary key
2026-03-07 19:27:44 +08:00
if len(params) != expectedParamCount {
t.Errorf("Expected %d params, got %d", expectedParamCount, len(params))
}
}
func TestBuildInsertSQL_NoColumns(t *testing.T) {
model := AutoIDOnly{ID: 1}
_, _, err := buildInsertSQL(&model, "users", "id")
if err == nil {
t.Fatal("Expected error when no columns remain to insert, got nil")
}
if !errors.Is(err, ErrNoInsertColumns) {
t.Fatalf("Expected ErrNoInsertColumns, got %v", err)
}
}
func TestBuildInsertSQL_EmptyTableName(t *testing.T) {
user := User{
ID: 1,
Name: "Test",
}
_, _, err := buildInsertSQL(&user, "", "id")
if err == nil {
t.Fatal("Expected error when table name is empty, got nil")
}
if !errors.Is(err, ErrTableNameEmpty) {
t.Fatalf("Expected ErrTableNameEmpty, got %v", err)
}
}
func TestBuildUpdateSQL_NoPrimaryKey(t *testing.T) {
user := User{
ID: 1,
Name: "Test",
}
_, _, err := buildUpdateSQL(&user, "users")
if err == nil {
t.Fatal("Expected error when no primary key is provided, got nil")
}
if !errors.Is(err, ErrPrimaryKeyRequired) {
t.Fatalf("Expected ErrPrimaryKeyRequired, got %v", err)
}
}
func TestBuildUpdateSQL_EmptyTableName(t *testing.T) {
user := User{
ID: 1,
Name: "Test",
}
_, _, err := buildUpdateSQL(&user, "", "id")
if err == nil {
t.Fatal("Expected error when table name is empty, got nil")
}
if !errors.Is(err, ErrTableNameEmpty) {
t.Fatalf("Expected ErrTableNameEmpty, got %v", err)
}
}
func TestBuildUpdateSQL_OnlyPrimaryKey(t *testing.T) {
model := AutoIDOnly{ID: 1}
_, _, err := buildUpdateSQL(&model, "users", "id")
if err == nil {
t.Fatal("Expected error when no fields remain for SET clause, got nil")
}
if !errors.Is(err, ErrNoUpdateFields) {
t.Fatalf("Expected ErrNoUpdateFields, got %v", err)
}
}
func TestGetStructFieldValues_NilNestedPointer(t *testing.T) {
user := NestedUserPtr{
ID: 1,
Name: "Test",
Profile: nil,
}
values, err := getStructFieldValues(user, "db")
if err != nil {
t.Fatalf("getStructFieldValues failed: %v", err)
}
if values["id"] != int64(1) {
t.Errorf("Expected id=1, got %v", values["id"])
}
if values["name"] != "Test" {
t.Errorf("Expected name=Test, got %v", values["name"])
}
}
func TestGetStructFieldNames_NilNestedPointer(t *testing.T) {
user := NestedUserPtr{
ID: 1,
Name: "Test",
Profile: nil,
}
names, err := getStructFieldNames(user, "db")
if err != nil {
t.Fatalf("getStructFieldNames failed: %v", err)
}
expected := []string{"id", "name"}
if !reflect.DeepEqual(names, expected) {
t.Errorf("Expected names %v, got %v", expected, names)
}
}
func TestGetStructFieldNames_SkipUnexportedField(t *testing.T) {
model := HiddenTagged{
ID: 1,
hidden: "secret",
}
names, err := getStructFieldNames(model, "db")
if err != nil {
t.Fatalf("getStructFieldNames failed: %v", err)
}
expected := []string{"id"}
if !reflect.DeepEqual(names, expected) {
t.Errorf("Expected names %v, got %v", expected, names)
}
}
func TestGetStructFieldValues_NilTarget(t *testing.T) {
_, err := getStructFieldValues(nil, "db")
if err == nil {
t.Fatal("Expected error for nil target, got nil")
}
if !errors.Is(err, ErrTargetNil) {
t.Fatalf("Expected ErrTargetNil, got %v", err)
}
}
func TestGetStructFieldNames_NilTarget(t *testing.T) {
_, err := getStructFieldNames(nil, "db")
if err == nil {
t.Fatal("Expected error for nil target, got nil")
}
if !errors.Is(err, ErrTargetNil) {
t.Fatalf("Expected ErrTargetNil, got %v", err)
}
}
func TestClearReflectCache(t *testing.T) {
type cacheUser struct {
ID int64 `db:"id"`
Name string `db:"name"`
}
typ := reflect.TypeOf(cacheUser{})
plan1, err := getStructTagPlan(typ, "db")
if err != nil {
t.Fatalf("getStructTagPlan failed: %v", err)
}
if len(plan1) != 2 {
t.Fatalf("Expected 2 fields in plan, got %d", len(plan1))
}
ClearReflectCache()
plan2, err := getStructTagPlan(typ, "db")
if err != nil {
t.Fatalf("getStructTagPlan after clear failed: %v", err)
}
if len(plan2) != 2 {
t.Fatalf("Expected 2 fields in plan after clear, got %d", len(plan2))
}
}