1115 lines
30 KiB
Go
1115 lines
30 KiB
Go
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
|
|
}
|