package lang import ( "fmt" "testing" ) func TestParseInput(t *testing.T) { tests := []struct { name string input string want AST wantErr bool }{ { name: "simple server definition", input: `server MyApp host "localhost" port 8080`, want: AST{ Definitions: []Definition{ { Server: &Server{ Name: "MyApp", Settings: []ServerSetting{ {Host: stringPtr("localhost")}, {Port: intPtr(8080)}, }, }, }, }, }, wantErr: false, }, { name: "server with just name", input: `server MyApp`, want: AST{ Definitions: []Definition{ { Server: &Server{ Name: "MyApp", }, }, }, }, wantErr: false, }, { name: "entity with basic fields", input: `entity User desc "User management" id: uuid required unique email: string required validate email validate min_length "5" name: string default "Anonymous"`, want: AST{ Definitions: []Definition{ { Entity: &Entity{ Name: "User", Description: stringPtr("User management"), Fields: []Field{ { Name: "id", Type: "uuid", Required: true, Unique: true, }, { Name: "email", Type: "string", Required: true, Validations: []Validation{ {Type: "email"}, {Type: "min_length", Value: stringPtr("5")}, }, }, { Name: "name", Type: "string", Default: stringPtr("Anonymous"), }, }, }, }, }, }, wantErr: false, }, { name: "entity with relationship", input: `entity User profile: uuid relates to Profile as one via "user_id"`, want: AST{ Definitions: []Definition{ { Entity: &Entity{ Name: "User", Fields: []Field{ { Name: "profile", Type: "uuid", Relationship: &Relationship{ Type: "Profile", Cardinality: "one", ForeignKey: stringPtr("user_id"), }, }, }, }, }, }, }, wantErr: false, }, { name: "endpoint with parameters and auth", input: `endpoint GET "/users" for User desc "List users" auth param page: int from query param limit: int required from query returns list as "json" fields [id, email, name]`, want: AST{ Definitions: []Definition{ { Endpoint: &Endpoint{ Method: "GET", Path: "/users", Entity: stringPtr("User"), Description: stringPtr("List users"), Auth: true, Params: []EndpointParam{ { Name: "page", Type: "int", Source: "query", }, { Name: "limit", Type: "int", Required: true, Source: "query", }, }, Response: &ResponseSpec{ Type: "list", Format: stringPtr("json"), Fields: []string{"id", "email", "name"}, }, }, }, }, }, wantErr: false, }, { name: "endpoint with custom logic", input: `endpoint PUT "/users/{id}" for User param id: uuid required from path param user_data: object required from body returns object custom "update_user_logic"`, want: AST{ Definitions: []Definition{ { Endpoint: &Endpoint{ Method: "PUT", Path: "/users/{id}", Entity: stringPtr("User"), Params: []EndpointParam{ { Name: "id", Type: "uuid", Required: true, Source: "path", }, { Name: "user_data", Type: "object", Required: true, Source: "body", }, }, Response: &ResponseSpec{ Type: "object", }, CustomLogic: stringPtr("update_user_logic"), }, }, }, }, wantErr: false, }, { name: "page with meta tags and auth", input: `page UserManagement at "/admin/users" layout AdminLayout title "User Management" auth meta description "Manage system users" meta keywords "users, admin, management"`, want: AST{ Definitions: []Definition{ { Page: &Page{ Name: "UserManagement", Path: "/admin/users", Layout: "AdminLayout", Title: stringPtr("User Management"), Auth: true, Meta: []MetaTag{ {Name: "description", Content: "Manage system users"}, {Name: "keywords", Content: "users, admin, management"}, }, }, }, }, }, wantErr: false, }, { name: "page with table component full configuration", input: `page UserList at "/users" layout MainLayout component Table for User fields [email, name, id] actions [edit via "/users/{id}", delete via "/users/{id}", create via "/users"] data from "/users" style modern classes ["table-striped", "table-hover"] pagination size 20 filters [email as text label "Search email", name as text label "Search name"] validate`, want: AST{ Definitions: []Definition{ { Page: &Page{ Name: "UserList", Path: "/users", Layout: "MainLayout", Components: []Component{ { Type: "Table", Entity: stringPtr("User"), Config: []ComponentAttr{ { Fields: &ComponentFields{ Fields: []string{"email", "name", "id"}, }, }, { Actions: &ComponentActions{ Actions: []ComponentAction{ {Name: "edit", Endpoint: stringPtr("/users/{id}")}, {Name: "delete", Endpoint: stringPtr("/users/{id}")}, {Name: "create", Endpoint: stringPtr("/users")}, }, }, }, { DataSource: &ComponentDataSource{ Endpoint: "/users", }, }, { Style: &ComponentStyle{ Theme: stringPtr("modern"), Classes: []string{"table-striped", "table-hover"}, }, }, { Pagination: &ComponentPagination{ PageSize: intPtr(20), }, }, { Filters: &ComponentFilters{ Filters: []ComponentFilter{ {Field: "email", Type: "text", Label: stringPtr("Search email")}, {Field: "name", Type: "text", Label: stringPtr("Search name")}, }, }, }, { Validation: true, }, }, }, }, }, }, }, }, wantErr: false, }, { name: "page with form component", input: `page UserForm at "/users/new" layout MainLayout component Form for User fields [email, name] actions [save via "/users", cancel] style clean validate`, want: AST{ Definitions: []Definition{ { Page: &Page{ Name: "UserForm", Path: "/users/new", Layout: "MainLayout", Components: []Component{ { Type: "Form", Entity: stringPtr("User"), Config: []ComponentAttr{ { Fields: &ComponentFields{ Fields: []string{"email", "name"}, }, }, { Actions: &ComponentActions{ Actions: []ComponentAction{ {Name: "save", Endpoint: stringPtr("/users")}, {Name: "cancel"}, }, }, }, { Style: &ComponentStyle{ Theme: stringPtr("clean"), }, }, { Validation: true, }, }, }, }, }, }, }, }, wantErr: false, }, { name: "mixed definitions - complete application", input: `server MyApp host "localhost" port 8080 entity User desc "User management" id: uuid required unique email: string required validate email name: string default "Anonymous" endpoint GET "/users" for User desc "List users" auth param page: int from query returns list as "json" fields [id, email, name] page UserList at "/users" layout MainLayout title "Users" component Table for User fields [email, name] data from "/users" pagination size 10`, want: AST{ Definitions: []Definition{ { Server: &Server{ Name: "MyApp", Settings: []ServerSetting{ {Host: stringPtr("localhost")}, {Port: intPtr(8080)}, }, }, }, { Entity: &Entity{ Name: "User", Description: stringPtr("User management"), Fields: []Field{ { Name: "id", Type: "uuid", Required: true, Unique: true, }, { Name: "email", Type: "string", Required: true, Validations: []Validation{ {Type: "email"}, }, }, { Name: "name", Type: "string", Default: stringPtr("Anonymous"), }, }, }, }, { Endpoint: &Endpoint{ Method: "GET", Path: "/users", Entity: stringPtr("User"), Description: stringPtr("List users"), Auth: true, Params: []EndpointParam{ { Name: "page", Type: "int", Source: "query", }, }, Response: &ResponseSpec{ Type: "list", Format: stringPtr("json"), Fields: []string{"id", "email", "name"}, }, }, }, { Page: &Page{ Name: "UserList", Path: "/users", Layout: "MainLayout", Title: stringPtr("Users"), Components: []Component{ { Type: "Table", Entity: stringPtr("User"), Config: []ComponentAttr{ { Fields: &ComponentFields{ Fields: []string{"email", "name"}, }, }, { DataSource: &ComponentDataSource{ Endpoint: "/users", }, }, { Pagination: &ComponentPagination{ PageSize: intPtr(10), }, }, }, }, }, }, }, }, }, wantErr: false, }, { name: "entity with multiple relationship types", input: `entity Post author: uuid relates to User as one tags: string relates to Tag as many through "post_tags" comments: uuid relates to Comment as many via "post_id"`, want: AST{ Definitions: []Definition{ { Entity: &Entity{ Name: "Post", Fields: []Field{ { Name: "author", Type: "uuid", Relationship: &Relationship{ Type: "User", Cardinality: "one", }, }, { Name: "tags", Type: "string", Relationship: &Relationship{ Type: "Tag", Cardinality: "many", Through: stringPtr("post_tags"), }, }, { Name: "comments", Type: "uuid", Relationship: &Relationship{ Type: "Comment", Cardinality: "many", ForeignKey: stringPtr("post_id"), }, }, }, }, }, }, }, wantErr: false, }, { name: "endpoint with all HTTP methods", input: `endpoint POST "/users" for User endpoint GET "/users/{id}" for User endpoint PUT "/users/{id}" for User endpoint DELETE "/users/{id}" for User endpoint PATCH "/users/{id}" for User`, want: AST{ Definitions: []Definition{ { Endpoint: &Endpoint{ Method: "POST", Path: "/users", Entity: stringPtr("User"), }, }, { Endpoint: &Endpoint{ Method: "GET", Path: "/users/{id}", Entity: stringPtr("User"), }, }, { Endpoint: &Endpoint{ Method: "PUT", Path: "/users/{id}", Entity: stringPtr("User"), }, }, { Endpoint: &Endpoint{ Method: "DELETE", Path: "/users/{id}", Entity: stringPtr("User"), }, }, { Endpoint: &Endpoint{ Method: "PATCH", Path: "/users/{id}", Entity: stringPtr("User"), }, }, }, }, wantErr: false, }, { name: "component with all filter types", input: `page TestPage at "/test" layout MainLayout component Table for User filters [name as text, age as number, created_at as date, active as select]`, want: AST{ Definitions: []Definition{ { Page: &Page{ Name: "TestPage", Path: "/test", Layout: "MainLayout", Components: []Component{ { Type: "Table", Entity: stringPtr("User"), Config: []ComponentAttr{ { Filters: &ComponentFilters{ Filters: []ComponentFilter{ {Field: "name", Type: "text"}, {Field: "age", Type: "number"}, {Field: "created_at", Type: "date"}, {Field: "active", Type: "select"}, }, }, }, }, }, }, }, }, }, }, wantErr: false, }, { name: "invalid syntax should error", input: `invalid syntax here`, want: AST{}, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := ParseInput(tt.input) if (err != nil) != tt.wantErr { t.Errorf("ParseInput() error = %v, wantErr %v", err, tt.wantErr) return } if !astEqual(got, tt.want) { t.Errorf("ParseInput() mismatch.\nGot: %+v\nWant: %+v", got, tt.want) } }) } } // Custom comparison function for AST structures func astEqual(got, want AST) bool { if len(got.Definitions) != len(want.Definitions) { return false } for i := range got.Definitions { if !definitionEqual(got.Definitions[i], want.Definitions[i]) { return false } } return true } func definitionEqual(got, want Definition) bool { if (got.Server == nil) != (want.Server == nil) { return false } if got.Server != nil && want.Server != nil { return serverEqual(*got.Server, *want.Server) } if (got.Entity == nil) != (want.Entity == nil) { return false } if got.Entity != nil && want.Entity != nil { return entityEqual(*got.Entity, *want.Entity) } if (got.Endpoint == nil) != (want.Endpoint == nil) { return false } if got.Endpoint != nil && want.Endpoint != nil { return endpointEqual(*got.Endpoint, *want.Endpoint) } if (got.Page == nil) != (want.Page == nil) { return false } if got.Page != nil && want.Page != nil { return pageEqual(*got.Page, *want.Page) } return true } func serverEqual(got, want Server) bool { if got.Name != want.Name { fmt.Printf("Server Name mismatch: got=%s, want=%s\n", got.Name, want.Name) return false } if len(got.Settings) != len(want.Settings) { fmt.Printf("Server Settings length mismatch: got=%d, want=%d\n", len(got.Settings), len(want.Settings)) return false } for i := range got.Settings { if !serverSettingEqual(got.Settings[i], want.Settings[i]) { fmt.Printf("Server Setting mismatch at index %d\n", i) return false } } return true } func serverSettingEqual(got, want ServerSetting) bool { if (got.Host == nil) != (want.Host == nil) { fmt.Printf("Server Host presence mismatch: got=%v, want=%v\n", got.Host != nil, want.Host != nil) return false } if got.Host != nil && want.Host != nil { if *got.Host != *want.Host { fmt.Printf("Server Host mismatch: got=%s, want=%s\n", *got.Host, *want.Host) return false } } if (got.Port == nil) != (want.Port == nil) { fmt.Printf("Server Port presence mismatch: got=%v, want=%v\n", got.Port != nil, want.Port != nil) return false } if got.Port != nil && want.Port != nil { if *got.Port != *want.Port { fmt.Printf("Server Port mismatch: got=%d, want=%d\n", *got.Port, *want.Port) return false } } return true } func entityEqual(got, want Entity) bool { if got.Name != want.Name { fmt.Printf("Entity Name mismatch: got=%s, want=%s\n", got.Name, want.Name) return false } if !stringPtrEqual(got.Description, want.Description) { gotDesc := "nil" wantDesc := "nil" if got.Description != nil { gotDesc = fmt.Sprintf(`"%s"`, *got.Description) } if want.Description != nil { wantDesc = fmt.Sprintf(`"%s"`, *want.Description) } fmt.Printf("Description mismatch: got=%s, want=%s\n", gotDesc, wantDesc) return false } if len(got.Fields) != len(want.Fields) { fmt.Printf("Fields length mismatch: got=%d, want=%d\n", len(got.Fields), len(want.Fields)) return false } for i := range got.Fields { if !fieldEqual(got.Fields[i], want.Fields[i]) { return false } } return true } func endpointEqual(got, want Endpoint) bool { if got.Method != want.Method { fmt.Printf("Endpoint Method mismatch: got=%s, want=%s\n", got.Method, want.Method) return false } if got.Path != want.Path { fmt.Printf("Endpoint Path mismatch: got=%s, want=%s\n", got.Path, want.Path) return false } if (got.Entity == nil) != (want.Entity == nil) { fmt.Printf("Endpoint Entity presence mismatch: got=%v, want=%v\n", got.Entity, want.Entity) return false } if got.Entity != nil && want.Entity != nil { if *got.Entity != *want.Entity { fmt.Printf("Endpoint Entity mismatch: got=%+v, want=%+v\n", *got.Entity, *want.Entity) return false } } if (got.Description == nil) != (want.Description == nil) { fmt.Printf("Endpoint Description presence mismatch: got=%v, want=%v\n", got.Description, want.Description) return false } if got.Description != nil && want.Description != nil { if *got.Description != *want.Description { fmt.Printf("Endpoint Description mismatch: got=%s, want=%s\n", *got.Description, *want.Description) return false } } if got.Auth != want.Auth { fmt.Printf("Endpoint Auth mismatch: got=%v, want=%v\n", got.Auth, want.Auth) return false } if len(got.Params) != len(want.Params) { fmt.Printf("Endpoint Params length mismatch: got=%d, want=%d\n", len(got.Params), len(want.Params)) return false } for i := range got.Params { if got.Params[i].Name != want.Params[i].Name { fmt.Printf("Endpoint Param Name mismatch at index %d: got=%s, want=%s\n", i, got.Params[i].Name, want.Params[i].Name) return false } if got.Params[i].Type != want.Params[i].Type { fmt.Printf("Endpoint Param Type mismatch at index %d: got=%s, want=%s\n", i, got.Params[i].Type, want.Params[i].Type) return false } if got.Params[i].Required != want.Params[i].Required { fmt.Printf("Endpoint Param Required mismatch at index %d: got=%v, want=%v\n", i, got.Params[i].Required, want.Params[i].Required) return false } if got.Params[i].Source != want.Params[i].Source { fmt.Printf("Endpoint Param Source mismatch at index %d: got=%s, want=%s\n", i, got.Params[i].Source, want.Params[i].Source) return false } } if (got.Response == nil) != (want.Response == nil) { fmt.Printf("Endpoint Response presence mismatch: got=%v, want=%v\n", got.Response, want.Response) return false } if got.Response != nil && want.Response != nil { if got.Response.Type != want.Response.Type { fmt.Printf("Endpoint Response Type mismatch: got=%s, want=%s\n", got.Response.Type, want.Response.Type) return false } if !stringPtrEqual(got.Response.Format, want.Response.Format) { fmt.Printf("Endpoint Response Format mismatch: got=%v, want=%v\n", got.Response.Format, want.Response.Format) return false } if len(got.Response.Fields) != len(want.Response.Fields) { fmt.Printf("Endpoint Response Fields length mismatch: got=%d, want=%d\n", len(got.Response.Fields), len(want.Response.Fields)) return false } for i := range got.Response.Fields { if got.Response.Fields[i] != want.Response.Fields[i] { fmt.Printf("Endpoint Response Field mismatch at index %d: got=%s, want=%s\n", i, got.Response.Fields[i], want.Response.Fields[i]) return false } } } if (got.CustomLogic == nil) != (want.CustomLogic == nil) { fmt.Printf("Endpoint CustomLogic presence mismatch: got=%v, want=%v\n", got.CustomLogic, want.CustomLogic) return false } if got.CustomLogic != nil && want.CustomLogic != nil { if *got.CustomLogic != *want.CustomLogic { fmt.Printf("Endpoint CustomLogic mismatch: got=%s, want=%s\n", *got.CustomLogic, *want.CustomLogic) return false } } return true } func pageEqual(got, want Page) bool { if got.Name != want.Name { fmt.Printf("Page Name mismatch: got=%s, want=%s\n", got.Name, want.Name) return false } if got.Path != want.Path { fmt.Printf("Page Path mismatch: got=%s, want=%s\n", got.Path, want.Path) return false } if got.Layout != want.Layout { fmt.Printf("Page Layout mismatch: got=%s, want=%s\n", got.Layout, want.Layout) return false } if !stringPtrEqual(got.Title, want.Title) { gotTitle := "nil" wantTitle := "nil" if got.Title != nil { gotTitle = *got.Title } if want.Title != nil { wantTitle = *want.Title } fmt.Printf("Page Title mismatch: got=%s, want=%s\n", gotTitle, wantTitle) return false } if got.Auth != want.Auth { fmt.Printf("Page Auth mismatch: got=%v, want=%v\n", got.Auth, want.Auth) return false } if len(got.Meta) != len(want.Meta) { fmt.Printf("Page Meta length mismatch: got=%d, want=%d\n", len(got.Meta), len(want.Meta)) return false } for i := range got.Meta { if got.Meta[i].Name != want.Meta[i].Name { fmt.Printf("Page Meta Name mismatch at index %d: got=%s, want=%s\n", i, got.Meta[i].Name, want.Meta[i].Name) return false } if got.Meta[i].Content != want.Meta[i].Content { fmt.Printf("Page Meta Content mismatch at index %d: got=%s, want=%s\n", i, got.Meta[i].Content, want.Meta[i].Content) return false } } if len(got.Components) != len(want.Components) { fmt.Printf("Page Components length mismatch: got=%d, want=%d\n", len(got.Components), len(want.Components)) return false } for i := range got.Components { if got.Components[i].Type != want.Components[i].Type { fmt.Printf("Page Component Type mismatch at index %d: got=%s, want=%s\n", i, got.Components[i].Type, want.Components[i].Type) return false } if !stringPtrEqual(got.Components[i].Entity, want.Components[i].Entity) { fmt.Printf("Page Component Entity mismatch at index %d: got=%v, want=%v\n", i, got.Components[i].Entity, want.Components[i].Entity) return false } if len(got.Components[i].Config) != len(want.Components[i].Config) { fmt.Printf("Page Component Config length mismatch at index %d: got=%d, want=%d\n", i, len(got.Components[i].Config), len(want.Components[i].Config)) return false } for j := range got.Components[i].Config { if !componentAttrEqual(got.Components[i].Config[j], want.Components[i].Config[j]) { fmt.Printf("Page Component Config mismatch at index %d.%d\n", i, j) return false } } } return true } func componentAttrEqual(got, want ComponentAttr) bool { if got.Fields != nil && want.Fields != nil { if len(got.Fields.Fields) != len(want.Fields.Fields) { fmt.Printf("Component Fields length mismatch: got=%d, want=%d\n", len(got.Fields.Fields), len(want.Fields.Fields)) return false } for i := range got.Fields.Fields { if got.Fields.Fields[i] != want.Fields.Fields[i] { fmt.Printf("Component Field mismatch at index %d: got=%s, want=%s\n", i, got.Fields.Fields[i], want.Fields.Fields[i]) return false } } } if got.Actions != nil && want.Actions != nil { if len(got.Actions.Actions) != len(want.Actions.Actions) { fmt.Printf("Component Actions length mismatch: got=%d, want=%d\n", len(got.Actions.Actions), len(want.Actions.Actions)) return false } for i := range got.Actions.Actions { if got.Actions.Actions[i].Name != want.Actions.Actions[i].Name { fmt.Printf("Component Action Name mismatch at index %d: got=%s, want=%s\n", i, got.Actions.Actions[i].Name, want.Actions.Actions[i].Name) return false } if !stringPtrEqual(got.Actions.Actions[i].Endpoint, want.Actions.Actions[i].Endpoint) { fmt.Printf("Component Action Endpoint mismatch at index %d: got=%v, want=%v\n", i, got.Actions.Actions[i].Endpoint, want.Actions.Actions[i].Endpoint) return false } } } if got.DataSource != nil && want.DataSource != nil { if got.DataSource.Endpoint != want.DataSource.Endpoint { fmt.Printf("Component DataSource Endpoint mismatch: got=%s, want=%s\n", got.DataSource.Endpoint, want.DataSource.Endpoint) return false } } if got.Style != nil && want.Style != nil { if !stringPtrEqual(got.Style.Theme, want.Style.Theme) { gotTheme := "nil" wantTheme := "nil" if got.Style.Theme != nil { gotTheme = *got.Style.Theme } if want.Style.Theme != nil { wantTheme = *want.Style.Theme } fmt.Printf("Component Style Theme mismatch: got=%s, want=%s\n", gotTheme, wantTheme) return false } if len(got.Style.Classes) != len(want.Style.Classes) { fmt.Printf("Component Style Classes length mismatch: got=%d, want=%d\n", len(got.Style.Classes), len(want.Style.Classes)) return false } for i := range got.Style.Classes { if got.Style.Classes[i] != want.Style.Classes[i] { fmt.Printf("Component Style Class mismatch at index %d: got=%s, want=%s\n", i, got.Style.Classes[i], want.Style.Classes[i]) return false } } } if got.Pagination != nil && want.Pagination != nil { // If ComponentPagination exists, pagination is enabled gotEnabled := true wantEnabled := true if gotEnabled != wantEnabled { fmt.Printf("Component Pagination Enabled mismatch: got=%v, want=%v\n", gotEnabled, wantEnabled) return false } if !intPtrEqual(got.Pagination.PageSize, want.Pagination.PageSize) { fmt.Printf("Component Pagination PageSize mismatch: got=%v, want=%v\n", got.Pagination.PageSize, want.Pagination.PageSize) return false } } if got.Filters != nil && want.Filters != nil { if len(got.Filters.Filters) != len(want.Filters.Filters) { fmt.Printf("Component Filters length mismatch: got=%d, want=%d\n", len(got.Filters.Filters), len(want.Filters.Filters)) return false } for i := range got.Filters.Filters { if got.Filters.Filters[i].Field != want.Filters.Filters[i].Field { fmt.Printf("Component Filter Field mismatch at index %d: got=%s, want=%s\n", i, got.Filters.Filters[i].Field, want.Filters.Filters[i].Field) return false } if got.Filters.Filters[i].Type != want.Filters.Filters[i].Type { fmt.Printf("Component Filter Type mismatch at index %d: got=%s, want=%s\n", i, got.Filters.Filters[i].Type, want.Filters.Filters[i].Type) return false } if !stringPtrEqual(got.Filters.Filters[i].Label, want.Filters.Filters[i].Label) { fmt.Printf("Component Filter Label mismatch at index %d: got=%v, want=%v\n", i, got.Filters.Filters[i].Label, want.Filters.Filters[i].Label) return false } } } if got.Validation != want.Validation { fmt.Printf("Component Validation mismatch: got=%v, want=%v\n", got.Validation, want.Validation) return false } return true } func fieldEqual(got, want Field) bool { if got.Name != want.Name { fmt.Printf("Field Name mismatch: got=%s, want=%s\n", got.Name, want.Name) return false } if got.Type != want.Type { fmt.Printf("Field Type mismatch: got=%s, want=%s\n", got.Type, want.Type) return false } if got.Required != want.Required { fmt.Printf("Field Required mismatch: got=%v, want=%v\n", got.Required, want.Required) return false } if got.Unique != want.Unique { fmt.Printf("Field Unique mismatch: got=%v, want=%v\n", got.Unique, want.Unique) return false } if len(got.Validations) != len(want.Validations) { fmt.Printf("Field Validations length mismatch: got=%d, want=%d\n", len(got.Validations), len(want.Validations)) return false } for i := range got.Validations { if got.Validations[i].Type != want.Validations[i].Type { fmt.Printf("Field Validation Type mismatch at index %d: got=%s, want=%s\n", i, got.Validations[i].Type, want.Validations[i].Type) return false } if !stringPtrEqual(got.Validations[i].Value, want.Validations[i].Value) { fmt.Printf("Field Validation Value mismatch at index %d: got=%v, want=%v\n", i, got.Validations[i].Value, want.Validations[i].Value) return false } } if (got.Relationship == nil) != (want.Relationship == nil) { fmt.Printf("Field Relationship presence mismatch: got=%v, want=%v\n", got.Relationship, want.Relationship) return false } if got.Relationship != nil && want.Relationship != nil { if got.Relationship.Type != want.Relationship.Type { fmt.Printf("Field Relationship Type mismatch: got=%s, want=%s\n", got.Relationship.Type, want.Relationship.Type) return false } if got.Relationship.Cardinality != want.Relationship.Cardinality { fmt.Printf("Field Relationship Cardinality mismatch: got=%s, want=%s\n", got.Relationship.Cardinality, want.Relationship.Cardinality) return false } if !stringPtrEqual(got.Relationship.ForeignKey, want.Relationship.ForeignKey) { fmt.Printf("Field Relationship ForeignKey mismatch: got=%v, want=%v\n", got.Relationship.ForeignKey, want.Relationship.ForeignKey) return false } if !stringPtrEqual(got.Relationship.Through, want.Relationship.Through) { fmt.Printf("Field Relationship Through mismatch: got=%v, want=%v\n", got.Relationship.Through, want.Relationship.Through) return false } } return true } func stringPtrEqual(got, want *string) bool { if (got == nil) != (want == nil) { return false } if got != nil && want != nil { return *got == *want } return true } func intPtrEqual(got, want *int) bool { if (got == nil) != (want == nil) { return false } if got != nil && want != nil { return *got == *want } return true } // Helper functions for creating pointers func stringPtr(s string) *string { return &s } func intPtr(i int) *int { return &i }