package stardb import ( "errors" "reflect" "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"` } 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 = ?" if query != expectedQuery { t.Errorf("Expected query:\n%s\nGot:\n%s", expectedQuery, query) } expectedParamCount := 7 // 6 fields + 1 primary key 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)) } }