mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2025-12-13 17:10:36 +00:00
Some checks are pending
Build , Vet Test, and Lint / Run Vet Tests (1.23.x) (push) Waiting to run
Build , Vet Test, and Lint / Run Vet Tests (1.24.x) (push) Waiting to run
Build , Vet Test, and Lint / Lint Code (push) Waiting to run
Build , Vet Test, and Lint / Build (push) Waiting to run
Tests / Unit Tests (push) Waiting to run
Tests / Integration Tests (push) Waiting to run
332 lines
8.9 KiB
Go
332 lines
8.9 KiB
Go
package reflection
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
// Test models for GetModelColumnDetail
|
|
type TestModelForColumnDetail struct {
|
|
ID int `gorm:"column:rid_test;primaryKey;type:bigserial;not null" json:"id"`
|
|
Name string `gorm:"column:name;type:varchar(255);not null" json:"name"`
|
|
Email string `gorm:"column:email;type:varchar(255);unique;nullable" json:"email"`
|
|
Description string `gorm:"column:description;type:text;null" json:"description"`
|
|
ForeignKey int `gorm:"foreignKey:parent_id" json:"foreign_key"`
|
|
}
|
|
|
|
type EmbeddedBase struct {
|
|
ID int `gorm:"column:rid_base;primaryKey;identity" json:"id"`
|
|
CreatedAt string `gorm:"column:created_at;type:timestamp" json:"created_at"`
|
|
}
|
|
|
|
type ModelWithEmbeddedForDetail struct {
|
|
EmbeddedBase
|
|
Title string `gorm:"column:title;type:varchar(100);not null" json:"title"`
|
|
Content string `gorm:"column:content;type:text" json:"content"`
|
|
}
|
|
|
|
// Model with nil embedded pointer
|
|
type ModelWithNilEmbedded struct {
|
|
ID int `gorm:"column:id;primaryKey" json:"id"`
|
|
*EmbeddedBase
|
|
Name string `gorm:"column:name" json:"name"`
|
|
}
|
|
|
|
func TestGetModelColumnDetail(t *testing.T) {
|
|
t.Run("simple struct", func(t *testing.T) {
|
|
model := TestModelForColumnDetail{
|
|
ID: 1,
|
|
Name: "Test",
|
|
Email: "test@example.com",
|
|
Description: "Test description",
|
|
ForeignKey: 100,
|
|
}
|
|
|
|
details := GetModelColumnDetail(reflect.ValueOf(model))
|
|
|
|
if len(details) != 5 {
|
|
t.Errorf("Expected 5 fields, got %d", len(details))
|
|
}
|
|
|
|
// Check ID field
|
|
found := false
|
|
for _, detail := range details {
|
|
if detail.Name == "ID" {
|
|
found = true
|
|
if detail.SQLName != "rid_test" {
|
|
t.Errorf("Expected SQLName 'rid_test', got '%s'", detail.SQLName)
|
|
}
|
|
// Note: primaryKey (without underscore) is not detected as primary_key
|
|
// The function looks for "identity" or "primary_key" (with underscore)
|
|
if detail.SQLDataType != "bigserial" {
|
|
t.Errorf("Expected SQLDataType 'bigserial', got '%s'", detail.SQLDataType)
|
|
}
|
|
if detail.Nullable {
|
|
t.Errorf("Expected Nullable false, got true")
|
|
}
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("ID field not found in details")
|
|
}
|
|
})
|
|
|
|
t.Run("struct with embedded fields", func(t *testing.T) {
|
|
model := ModelWithEmbeddedForDetail{
|
|
EmbeddedBase: EmbeddedBase{
|
|
ID: 1,
|
|
CreatedAt: "2024-01-01",
|
|
},
|
|
Title: "Test Title",
|
|
Content: "Test Content",
|
|
}
|
|
|
|
details := GetModelColumnDetail(reflect.ValueOf(model))
|
|
|
|
// Should have 4 fields: ID, CreatedAt from embedded, Title, Content from main
|
|
if len(details) != 4 {
|
|
t.Errorf("Expected 4 fields, got %d", len(details))
|
|
}
|
|
|
|
// Check that embedded field is included
|
|
foundID := false
|
|
foundCreatedAt := false
|
|
for _, detail := range details {
|
|
if detail.Name == "ID" {
|
|
foundID = true
|
|
if detail.SQLKey != "primary_key" {
|
|
t.Errorf("Expected SQLKey 'primary_key' for embedded ID, got '%s'", detail.SQLKey)
|
|
}
|
|
}
|
|
if detail.Name == "CreatedAt" {
|
|
foundCreatedAt = true
|
|
}
|
|
}
|
|
if !foundID {
|
|
t.Errorf("Embedded ID field not found")
|
|
}
|
|
if !foundCreatedAt {
|
|
t.Errorf("Embedded CreatedAt field not found")
|
|
}
|
|
})
|
|
|
|
t.Run("nil embedded pointer is skipped", func(t *testing.T) {
|
|
model := ModelWithNilEmbedded{
|
|
ID: 1,
|
|
Name: "Test",
|
|
EmbeddedBase: nil, // nil embedded pointer
|
|
}
|
|
|
|
details := GetModelColumnDetail(reflect.ValueOf(model))
|
|
|
|
// Should have 2 fields: ID and Name (embedded is nil, so skipped)
|
|
if len(details) != 2 {
|
|
t.Errorf("Expected 2 fields (nil embedded skipped), got %d", len(details))
|
|
}
|
|
})
|
|
|
|
t.Run("pointer to struct", func(t *testing.T) {
|
|
model := &TestModelForColumnDetail{
|
|
ID: 1,
|
|
Name: "Test",
|
|
}
|
|
|
|
details := GetModelColumnDetail(reflect.ValueOf(model))
|
|
|
|
if len(details) != 5 {
|
|
t.Errorf("Expected 5 fields, got %d", len(details))
|
|
}
|
|
})
|
|
|
|
t.Run("invalid value", func(t *testing.T) {
|
|
var invalid reflect.Value
|
|
details := GetModelColumnDetail(invalid)
|
|
|
|
if len(details) != 0 {
|
|
t.Errorf("Expected 0 fields for invalid value, got %d", len(details))
|
|
}
|
|
})
|
|
|
|
t.Run("non-struct type", func(t *testing.T) {
|
|
details := GetModelColumnDetail(reflect.ValueOf(123))
|
|
|
|
if len(details) != 0 {
|
|
t.Errorf("Expected 0 fields for non-struct, got %d", len(details))
|
|
}
|
|
})
|
|
|
|
t.Run("nullable and not null detection", func(t *testing.T) {
|
|
model := TestModelForColumnDetail{}
|
|
details := GetModelColumnDetail(reflect.ValueOf(model))
|
|
|
|
for _, detail := range details {
|
|
switch detail.Name {
|
|
case "ID":
|
|
if detail.Nullable {
|
|
t.Errorf("ID should not be nullable (has 'not null')")
|
|
}
|
|
case "Name":
|
|
if detail.Nullable {
|
|
t.Errorf("Name should not be nullable (has 'not null')")
|
|
}
|
|
case "Email":
|
|
if !detail.Nullable {
|
|
t.Errorf("Email should be nullable (has 'nullable')")
|
|
}
|
|
case "Description":
|
|
if !detail.Nullable {
|
|
t.Errorf("Description should be nullable (has 'null')")
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("unique and uniqueindex detection", func(t *testing.T) {
|
|
type UniqueTestModel struct {
|
|
ID int `gorm:"column:id;primary_key"`
|
|
Username string `gorm:"column:username;unique"`
|
|
Email string `gorm:"column:email;uniqueindex"`
|
|
}
|
|
|
|
model := UniqueTestModel{}
|
|
details := GetModelColumnDetail(reflect.ValueOf(model))
|
|
|
|
for _, detail := range details {
|
|
switch detail.Name {
|
|
case "ID":
|
|
if detail.SQLKey != "primary_key" {
|
|
t.Errorf("ID should have SQLKey 'primary_key', got '%s'", detail.SQLKey)
|
|
}
|
|
case "Username":
|
|
if detail.SQLKey != "unique" {
|
|
t.Errorf("Username should have SQLKey 'unique', got '%s'", detail.SQLKey)
|
|
}
|
|
case "Email":
|
|
// The function checks for "unique" first, so uniqueindex is also detected as "unique"
|
|
// This is expected behavior based on the code logic
|
|
if detail.SQLKey != "unique" {
|
|
t.Errorf("Email should have SQLKey 'unique' (uniqueindex contains 'unique'), got '%s'", detail.SQLKey)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("foreign key detection", func(t *testing.T) {
|
|
// Note: The foreignkey extraction in generic_model.go has a bug where
|
|
// it requires ik > 0, so foreignkey at the start won't extract the value
|
|
type FKTestModel struct {
|
|
ParentID int `gorm:"column:parent_id;foreignkey:rid_parent;association_foreignkey:id_atevent"`
|
|
}
|
|
|
|
model := FKTestModel{}
|
|
details := GetModelColumnDetail(reflect.ValueOf(model))
|
|
|
|
if len(details) == 0 {
|
|
t.Fatal("Expected at least 1 field")
|
|
}
|
|
|
|
detail := details[0]
|
|
if detail.SQLKey != "foreign_key" {
|
|
t.Errorf("Expected SQLKey 'foreign_key', got '%s'", detail.SQLKey)
|
|
}
|
|
// Due to the bug in the code (requires ik > 0), the SQLName will be extracted
|
|
// when foreignkey is not at the beginning of the string
|
|
if detail.SQLName != "rid_parent" {
|
|
t.Errorf("Expected SQLName 'rid_parent', got '%s'", detail.SQLName)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestFnFindKeyVal(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
src string
|
|
key string
|
|
expected string
|
|
}{
|
|
{
|
|
name: "find column",
|
|
src: "column:user_id;primaryKey;type:bigint",
|
|
key: "column:",
|
|
expected: "user_id",
|
|
},
|
|
{
|
|
name: "find type",
|
|
src: "column:name;type:varchar(255);not null",
|
|
key: "type:",
|
|
expected: "varchar(255)",
|
|
},
|
|
{
|
|
name: "key not found",
|
|
src: "primaryKey;autoIncrement",
|
|
key: "column:",
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "key at end without semicolon",
|
|
src: "primaryKey;column:id",
|
|
key: "column:",
|
|
expected: "id",
|
|
},
|
|
{
|
|
name: "case insensitive search",
|
|
src: "Column:user_id;primaryKey",
|
|
key: "column:",
|
|
expected: "user_id",
|
|
},
|
|
{
|
|
name: "empty src",
|
|
src: "",
|
|
key: "column:",
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "multiple occurrences (returns first)",
|
|
src: "column:first;column:second",
|
|
key: "column:",
|
|
expected: "first",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := fnFindKeyVal(tt.src, tt.key)
|
|
if result != tt.expected {
|
|
t.Errorf("fnFindKeyVal(%q, %q) = %q, want %q", tt.src, tt.key, result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetModelColumnDetail_FieldValue(t *testing.T) {
|
|
model := TestModelForColumnDetail{
|
|
ID: 123,
|
|
Name: "TestName",
|
|
Email: "test@example.com",
|
|
}
|
|
|
|
details := GetModelColumnDetail(reflect.ValueOf(model))
|
|
|
|
for _, detail := range details {
|
|
if !detail.FieldValue.IsValid() {
|
|
t.Errorf("Field %s has invalid FieldValue", detail.Name)
|
|
}
|
|
|
|
// Check that FieldValue matches the actual value
|
|
switch detail.Name {
|
|
case "ID":
|
|
if detail.FieldValue.Int() != 123 {
|
|
t.Errorf("Expected ID FieldValue 123, got %v", detail.FieldValue.Int())
|
|
}
|
|
case "Name":
|
|
if detail.FieldValue.String() != "TestName" {
|
|
t.Errorf("Expected Name FieldValue 'TestName', got %v", detail.FieldValue.String())
|
|
}
|
|
case "Email":
|
|
if detail.FieldValue.String() != "test@example.com" {
|
|
t.Errorf("Expected Email FieldValue 'test@example.com', got %v", detail.FieldValue.String())
|
|
}
|
|
}
|
|
}
|
|
}
|