mirror of
https://github.com/lukaszraczylo/go-telegram.git
synced 2026-06-05 22:43:59 +00:00
9072e9eafb
A fully-generated, strongly-typed Go client for the Telegram Bot API. * 176 methods + 301 types generated from Bot API v10.0 * 1408 auto-generated tests (8 scenarios per method) * Typed unions throughout — no 'any' in the public surface * Pluggable HTTP transport and JSON codec (default goccy/go-json) * Built-in retry middleware honouring Telegram's retry_after * Generic dispatcher with filters and conversation handlers * Self-verifying codegen pipeline (regen → audit → emit → run tests) * 14 example bots covering common patterns
646 lines
21 KiB
Go
646 lines
21 KiB
Go
package main
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/lukaszraczylo/go-telegram/internal/spec"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// goType — all branches
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestGoType_Primitive(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
optional bool
|
|
want string
|
|
}{
|
|
{"bool", false, "bool"},
|
|
{"bool", true, "*bool"},
|
|
{"int64", false, "int64"},
|
|
{"int64", true, "*int64"},
|
|
{"float64", false, "float64"},
|
|
{"float64", true, "*float64"},
|
|
{"string", false, "string"},
|
|
{"string", true, "string"}, // string is not pointer-wrapped
|
|
}
|
|
for _, c := range cases {
|
|
tr := spec.TypeRef{Kind: spec.KindPrimitive, Name: c.name}
|
|
got := goType(tr, c.optional)
|
|
require.Equal(t, c.want, got, "goType(%q, optional=%v)", c.name, c.optional)
|
|
}
|
|
}
|
|
|
|
func TestGoType_Named_Required(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindNamed, Name: "Message"}
|
|
require.Equal(t, "Message", goType(tr, false))
|
|
require.Equal(t, "*Message", goType(tr, true))
|
|
}
|
|
|
|
func TestGoType_Named_InputFile(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindNamed, Name: "InputFile"}
|
|
// InputFile is always pointer even when required.
|
|
require.Equal(t, "*InputFile", goType(tr, false))
|
|
require.Equal(t, "*InputFile", goType(tr, true))
|
|
}
|
|
|
|
func TestGoType_Named_UnionInterface(t *testing.T) {
|
|
// ChatMember is a known discriminated union — no * even when optional.
|
|
tr := spec.TypeRef{Kind: spec.KindNamed, Name: "ChatMember"}
|
|
require.Equal(t, "ChatMember", goType(tr, false))
|
|
require.Equal(t, "ChatMember", goType(tr, true))
|
|
}
|
|
|
|
func TestGoType_Array_NilElem(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindArray}
|
|
require.Equal(t, "[]any", goType(tr, false))
|
|
}
|
|
|
|
func TestGoType_Array_WithElem(t *testing.T) {
|
|
elem := spec.TypeRef{Kind: spec.KindNamed, Name: "Update"}
|
|
tr := spec.TypeRef{Kind: spec.KindArray, ElemType: &elem}
|
|
require.Equal(t, "[]Update", goType(tr, false))
|
|
}
|
|
|
|
func TestGoType_OneOf_ChatID(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindOneOf, Variants: []string{"int64", "string"}}
|
|
require.Equal(t, "ChatID", goType(tr, false))
|
|
require.Equal(t, "*ChatID", goType(tr, true))
|
|
}
|
|
|
|
func TestGoType_OneOf_InputFile(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindOneOf, Variants: []string{"InputFile", "string"}}
|
|
require.Equal(t, "*InputFile", goType(tr, false))
|
|
}
|
|
|
|
func TestGoType_OneOf_SealedInterface(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindOneOf, Variants: []string{"A", "B"}}
|
|
require.Equal(t, "any", goType(tr, false))
|
|
}
|
|
|
|
func TestGoType_Unknown(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.Kind(99)}
|
|
require.Equal(t, "any", goType(tr, false))
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// returnGoType — all branches
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestReturnGoType_Primitive(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindPrimitive, Name: "bool"}
|
|
require.Equal(t, "bool", returnGoType(tr))
|
|
}
|
|
|
|
func TestReturnGoType_Named(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindNamed, Name: "Message"}
|
|
require.Equal(t, "*Message", returnGoType(tr))
|
|
}
|
|
|
|
func TestReturnGoType_Array_NilElem(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindArray}
|
|
require.Equal(t, "[]any", returnGoType(tr))
|
|
}
|
|
|
|
func TestReturnGoType_Array_WithElem(t *testing.T) {
|
|
elem := spec.TypeRef{Kind: spec.KindNamed, Name: "Update"}
|
|
tr := spec.TypeRef{Kind: spec.KindArray, ElemType: &elem}
|
|
require.Equal(t, "[]Update", returnGoType(tr))
|
|
}
|
|
|
|
func TestReturnGoType_OneOf_ChatID(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindOneOf, Variants: []string{"int64", "string"}}
|
|
require.Equal(t, "ChatID", returnGoType(tr))
|
|
}
|
|
|
|
func TestReturnGoType_OneOf_Other(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindOneOf, Variants: []string{"A", "B"}}
|
|
require.Equal(t, "any", returnGoType(tr))
|
|
}
|
|
|
|
func TestReturnGoType_Unknown(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.Kind(99)}
|
|
require.Equal(t, "any", returnGoType(tr))
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// returnGoElem — all branches
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestReturnGoElem_Primitive(t *testing.T) {
|
|
require.Equal(t, "int64", returnGoElem(spec.TypeRef{Kind: spec.KindPrimitive, Name: "int64"}))
|
|
}
|
|
|
|
func TestReturnGoElem_Named(t *testing.T) {
|
|
require.Equal(t, "Message", returnGoElem(spec.TypeRef{Kind: spec.KindNamed, Name: "Message"}))
|
|
}
|
|
|
|
func TestReturnGoElem_Array_NilElem(t *testing.T) {
|
|
require.Equal(t, "any", returnGoElem(spec.TypeRef{Kind: spec.KindArray}))
|
|
}
|
|
|
|
func TestReturnGoElem_Array_WithElem(t *testing.T) {
|
|
elem := spec.TypeRef{Kind: spec.KindNamed, Name: "PhotoSize"}
|
|
tr := spec.TypeRef{Kind: spec.KindArray, ElemType: &elem}
|
|
require.Equal(t, "[]PhotoSize", returnGoElem(tr))
|
|
}
|
|
|
|
func TestReturnGoElem_Unknown(t *testing.T) {
|
|
require.Equal(t, "any", returnGoElem(spec.TypeRef{Kind: spec.Kind(99)}))
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// multipartFieldEntry — all branches
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func makeField(name, jname, typName string, kind spec.Kind, required bool) spec.Field {
|
|
return spec.Field{
|
|
Name: name,
|
|
JSONName: jname,
|
|
Type: spec.TypeRef{Kind: kind, Name: typName},
|
|
Required: required,
|
|
}
|
|
}
|
|
|
|
func makeFieldVariants(name, jname string, kind spec.Kind, variants []string, required bool) spec.Field {
|
|
return spec.Field{
|
|
Name: name,
|
|
JSONName: jname,
|
|
Type: spec.TypeRef{Kind: kind, Variants: variants},
|
|
Required: required,
|
|
}
|
|
}
|
|
|
|
func TestMultipartFieldEntry_Int64Required(t *testing.T) {
|
|
f := makeField("ChatID", "chat_id", "int64", spec.KindPrimitive, true)
|
|
got := multipartFieldEntry(f)
|
|
require.Contains(t, got, `FormatInt`)
|
|
require.NotContains(t, got, "if p.")
|
|
}
|
|
|
|
func TestMultipartFieldEntry_Int64Optional(t *testing.T) {
|
|
f := makeField("MessageThreadID", "message_thread_id", "int64", spec.KindPrimitive, false)
|
|
got := multipartFieldEntry(f)
|
|
require.Contains(t, got, `FormatInt`)
|
|
require.Contains(t, got, "if p.")
|
|
}
|
|
|
|
func TestMultipartFieldEntry_StringRequired(t *testing.T) {
|
|
f := makeField("Text", "text", "string", spec.KindPrimitive, true)
|
|
got := multipartFieldEntry(f)
|
|
require.Contains(t, got, `out["text"]`)
|
|
require.NotContains(t, got, "if p.Text")
|
|
}
|
|
|
|
func TestMultipartFieldEntry_StringOptional(t *testing.T) {
|
|
f := makeField("ParseMode", "parse_mode", "string", spec.KindPrimitive, false)
|
|
got := multipartFieldEntry(f)
|
|
require.Contains(t, got, `if p.ParseMode`)
|
|
}
|
|
|
|
func TestMultipartFieldEntry_BoolRequired(t *testing.T) {
|
|
f := makeField("DisableNotification", "disable_notification", "bool", spec.KindPrimitive, true)
|
|
got := multipartFieldEntry(f)
|
|
require.Contains(t, got, `FormatBool`)
|
|
require.NotContains(t, got, "if p.")
|
|
}
|
|
|
|
func TestMultipartFieldEntry_BoolOptional(t *testing.T) {
|
|
f := makeField("Protected", "protect_content", "bool", spec.KindPrimitive, false)
|
|
got := multipartFieldEntry(f)
|
|
require.Contains(t, got, `FormatBool`)
|
|
require.Contains(t, got, "if p.")
|
|
}
|
|
|
|
func TestMultipartFieldEntry_Float64Required(t *testing.T) {
|
|
f := makeField("Latitude", "latitude", "float64", spec.KindPrimitive, true)
|
|
got := multipartFieldEntry(f)
|
|
require.Contains(t, got, `FormatFloat`)
|
|
require.NotContains(t, got, "if p.")
|
|
}
|
|
|
|
func TestMultipartFieldEntry_Float64Optional(t *testing.T) {
|
|
f := makeField("Longitude", "longitude", "float64", spec.KindPrimitive, false)
|
|
got := multipartFieldEntry(f)
|
|
require.Contains(t, got, `FormatFloat`)
|
|
require.Contains(t, got, "if p.")
|
|
}
|
|
|
|
func TestMultipartFieldEntry_OneOf_ChatIDRequired(t *testing.T) {
|
|
f := makeFieldVariants("ChatID", "chat_id", spec.KindOneOf, []string{"int64", "string"}, true)
|
|
got := multipartFieldEntry(f)
|
|
require.Contains(t, got, `.String()`)
|
|
require.NotContains(t, got, "IsZero")
|
|
}
|
|
|
|
func TestMultipartFieldEntry_OneOf_ChatIDOptional(t *testing.T) {
|
|
f := makeFieldVariants("ChatID", "chat_id", spec.KindOneOf, []string{"int64", "string"}, false)
|
|
got := multipartFieldEntry(f)
|
|
require.Contains(t, got, `IsZero`)
|
|
}
|
|
|
|
func TestMultipartFieldEntry_OneOf_InputFileOrString(t *testing.T) {
|
|
f := makeFieldVariants("Photo", "photo", spec.KindOneOf, []string{"InputFile", "string"}, false)
|
|
got := multipartFieldEntry(f)
|
|
require.Contains(t, got, `PathOrID`)
|
|
}
|
|
|
|
func TestMultipartFieldEntry_OneOf_SealedRequired(t *testing.T) {
|
|
f := makeFieldVariants("Markup", "reply_markup", spec.KindOneOf, []string{"A", "B"}, true)
|
|
got := multipartFieldEntry(f)
|
|
require.Contains(t, got, `json.Marshal`)
|
|
}
|
|
|
|
func TestMultipartFieldEntry_OneOf_SealedOptional(t *testing.T) {
|
|
f := makeFieldVariants("Markup", "reply_markup", spec.KindOneOf, []string{"A", "B"}, false)
|
|
got := multipartFieldEntry(f)
|
|
require.Contains(t, got, `json.Marshal`)
|
|
require.Contains(t, got, "if p.Markup")
|
|
}
|
|
|
|
func TestMultipartFieldEntry_Named_Required(t *testing.T) {
|
|
f := makeField("Entities", "entities", "MessageEntity", spec.KindNamed, true)
|
|
got := multipartFieldEntry(f)
|
|
require.Contains(t, got, `json.Marshal`)
|
|
require.NotContains(t, got, "if p.")
|
|
}
|
|
|
|
func TestMultipartFieldEntry_Named_Optional(t *testing.T) {
|
|
f := makeField("Entities", "entities", "MessageEntity", spec.KindNamed, false)
|
|
got := multipartFieldEntry(f)
|
|
require.Contains(t, got, `json.Marshal`)
|
|
require.Contains(t, got, "if p.")
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// unionTypeFor — all branches
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestUnionTypeFor_DirectNamed(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindNamed, Name: "ChatMember"}
|
|
name, ok := unionTypeFor(tr)
|
|
require.True(t, ok)
|
|
require.Equal(t, "ChatMember", name)
|
|
}
|
|
|
|
func TestUnionTypeFor_Array(t *testing.T) {
|
|
elem := spec.TypeRef{Kind: spec.KindNamed, Name: "ChatMember"}
|
|
tr := spec.TypeRef{Kind: spec.KindArray, ElemType: &elem}
|
|
name, ok := unionTypeFor(tr)
|
|
require.True(t, ok)
|
|
require.Equal(t, "ChatMember", name)
|
|
}
|
|
|
|
func TestUnionTypeFor_ArrayNilElem(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindArray}
|
|
_, ok := unionTypeFor(tr)
|
|
require.False(t, ok)
|
|
}
|
|
|
|
func TestUnionTypeFor_NotUnion(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindNamed, Name: "Message"}
|
|
_, ok := unionTypeFor(tr)
|
|
require.False(t, ok)
|
|
}
|
|
|
|
func TestUnionTypeFor_Unknown(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.Kind(99)}
|
|
_, ok := unionTypeFor(tr)
|
|
require.False(t, ok)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// unionNameByVariants
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestUnionNameByVariants_ChatMember(t *testing.T) {
|
|
// Use the actual variants from knownDiscriminators["ChatMember"].
|
|
variants := []string{
|
|
"ChatMemberOwner", "ChatMemberAdministrator", "ChatMemberMember",
|
|
"ChatMemberRestricted", "ChatMemberLeft", "ChatMemberBanned",
|
|
}
|
|
name := unionNameByVariants(variants)
|
|
require.Equal(t, "ChatMember", name)
|
|
}
|
|
|
|
func TestUnionNameByVariants_Unknown(t *testing.T) {
|
|
name := unionNameByVariants([]string{"X", "Y", "Z"})
|
|
require.Equal(t, "", name)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// hasUnionElem
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestHasUnionElem_NonArray(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindNamed, Name: "ChatMember"}
|
|
require.False(t, hasUnionElem(tr))
|
|
}
|
|
|
|
func TestHasUnionElem_ArrayNilElem(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindArray}
|
|
require.False(t, hasUnionElem(tr))
|
|
}
|
|
|
|
func TestHasUnionElem_ArrayUnionElem(t *testing.T) {
|
|
elem := spec.TypeRef{Kind: spec.KindNamed, Name: "ChatMember"}
|
|
tr := spec.TypeRef{Kind: spec.KindArray, ElemType: &elem}
|
|
require.True(t, hasUnionElem(tr))
|
|
}
|
|
|
|
func TestHasUnionElem_ArrayNonUnionElem(t *testing.T) {
|
|
elem := spec.TypeRef{Kind: spec.KindNamed, Name: "Message"}
|
|
tr := spec.TypeRef{Kind: spec.KindArray, ElemType: &elem}
|
|
require.False(t, hasUnionElem(tr))
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// unionFieldsOf
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestUnionFieldsOf_WithUnionField(t *testing.T) {
|
|
td := spec.TypeDecl{
|
|
Name: "ChatMemberUpdated",
|
|
Fields: []spec.Field{
|
|
{Name: "NewChatMember", JSONName: "new_chat_member", Type: spec.TypeRef{Kind: spec.KindNamed, Name: "ChatMember"}},
|
|
{Name: "OldChatMember", JSONName: "old_chat_member", Type: spec.TypeRef{Kind: spec.KindNamed, Name: "ChatMember"}},
|
|
{Name: "Date", JSONName: "date", Type: spec.TypeRef{Kind: spec.KindPrimitive, Name: "int64"}},
|
|
},
|
|
}
|
|
uf := unionFieldsOf(td)
|
|
require.Len(t, uf, 2)
|
|
require.Equal(t, "ChatMember", uf[0].UnionName)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// splitLines — edge cases
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestSplitLines_Empty(t *testing.T) {
|
|
require.Empty(t, splitLines(""))
|
|
}
|
|
|
|
func TestSplitLines_NoNewline(t *testing.T) {
|
|
got := splitLines("hello world")
|
|
require.Equal(t, []string{"hello world"}, got)
|
|
}
|
|
|
|
func TestSplitLines_TrailingNewline(t *testing.T) {
|
|
got := splitLines("line1\nline2\n")
|
|
require.Equal(t, []string{"line1", "line2"}, got)
|
|
}
|
|
|
|
func TestSplitLines_MultiLine(t *testing.T) {
|
|
got := splitLines("a\nb\nc")
|
|
require.Equal(t, []string{"a", "b", "c"}, got)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// docComment
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestDocComment_Empty(t *testing.T) {
|
|
require.Equal(t, "", docComment(""))
|
|
}
|
|
|
|
func TestDocComment_SingleLine(t *testing.T) {
|
|
got := docComment("Hello world.")
|
|
require.Equal(t, "// Hello world.\n", got)
|
|
}
|
|
|
|
func TestDocComment_MultiLine(t *testing.T) {
|
|
got := docComment("Line 1\nLine 2")
|
|
require.Contains(t, got, "// Line 1\n")
|
|
require.Contains(t, got, "// Line 2\n")
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// title
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestTitle_Empty(t *testing.T) {
|
|
require.Equal(t, "", title(""))
|
|
}
|
|
|
|
func TestTitle_Lowercase(t *testing.T) {
|
|
require.Equal(t, "SendMessage", title("sendMessage"))
|
|
}
|
|
|
|
func TestTitle_AlreadyUpper(t *testing.T) {
|
|
require.Equal(t, "GetMe", title("GetMe"))
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// mentionsInputFileTr
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestMentionsInputFileTr_Named(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindNamed, Name: "InputFile"}
|
|
require.True(t, mentionsInputFileTr(tr))
|
|
}
|
|
|
|
func TestMentionsInputFileTr_NotInputFile(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindNamed, Name: "Message"}
|
|
require.False(t, mentionsInputFileTr(tr))
|
|
}
|
|
|
|
func TestMentionsInputFileTr_Array(t *testing.T) {
|
|
elem := spec.TypeRef{Kind: spec.KindNamed, Name: "InputFile"}
|
|
tr := spec.TypeRef{Kind: spec.KindArray, ElemType: &elem}
|
|
require.True(t, mentionsInputFileTr(tr))
|
|
}
|
|
|
|
func TestMentionsInputFileTr_ArrayNilElem(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindArray}
|
|
require.False(t, mentionsInputFileTr(tr))
|
|
}
|
|
|
|
func TestMentionsInputFileTr_OneOf_WithInputFile(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindOneOf, Variants: []string{"InputFile", "string"}}
|
|
require.True(t, mentionsInputFileTr(tr))
|
|
}
|
|
|
|
func TestMentionsInputFileTr_OneOf_WithoutInputFile(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindOneOf, Variants: []string{"A", "B"}}
|
|
require.False(t, mentionsInputFileTr(tr))
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// loadAPI — error paths
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestLoadAPI_MissingFile(t *testing.T) {
|
|
_, err := loadAPI("/nonexistent/path/api.json")
|
|
require.Error(t, err)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// runtimeTypes filter in emitTypes
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestRuntimeTypes_NeverEmitted(t *testing.T) {
|
|
for name := range runtimeTypes {
|
|
require.True(t, runtimeTypes[name], "runtimeType %q should be true", name)
|
|
}
|
|
require.True(t, runtimeTypes["InputFile"])
|
|
require.True(t, runtimeTypes["ChatID"])
|
|
require.True(t, runtimeTypes["MessageOrBool"])
|
|
require.True(t, runtimeTypes["ResponseParameters"])
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// sentinelForField — all branches
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestSentinelForField(t *testing.T) {
|
|
unionTypes := map[string]bool{"ChatMember": true}
|
|
cases := []struct {
|
|
name string
|
|
field spec.Field
|
|
contains string
|
|
}{
|
|
{
|
|
name: "int64 primitive",
|
|
field: makeField("Count", "count", "int64", spec.KindPrimitive, true),
|
|
contains: "42",
|
|
},
|
|
{
|
|
name: "string primitive",
|
|
field: makeField("Text", "text", "string", spec.KindPrimitive, true),
|
|
contains: "test_value",
|
|
},
|
|
{
|
|
name: "bool primitive",
|
|
field: makeField("Flag", "flag", "bool", spec.KindPrimitive, true),
|
|
contains: "true",
|
|
},
|
|
{
|
|
name: "float64 primitive",
|
|
field: makeField("Lat", "lat", "float64", spec.KindPrimitive, true),
|
|
contains: "1.0",
|
|
},
|
|
{
|
|
name: "named ChatID",
|
|
field: makeField("ChatID", "chat_id", "ChatID", spec.KindNamed, true),
|
|
contains: "ChatIDFromInt",
|
|
},
|
|
{
|
|
name: "named InputFile",
|
|
field: makeField("Photo", "photo", "InputFile", spec.KindNamed, true),
|
|
contains: "InputFile",
|
|
},
|
|
{
|
|
name: "named union (nil-able)",
|
|
field: makeField("Member", "member", "ChatMember", spec.KindNamed, true),
|
|
contains: "nil",
|
|
},
|
|
{
|
|
name: "named required struct",
|
|
field: makeField("Chat", "chat", "Chat", spec.KindNamed, true),
|
|
contains: "Chat{}",
|
|
},
|
|
{
|
|
name: "named optional struct",
|
|
field: makeField("Chat", "chat", "Chat", spec.KindNamed, false),
|
|
contains: "&Chat{}",
|
|
},
|
|
{
|
|
name: "array",
|
|
field: spec.Field{Name: "Items", JSONName: "items", Type: spec.TypeRef{Kind: spec.KindArray}},
|
|
contains: "nil",
|
|
},
|
|
{
|
|
name: "oneOf ChatID variants",
|
|
field: makeFieldVariants("ChatID", "chat_id", spec.KindOneOf, []string{"int64", "string"}, true),
|
|
contains: "ChatIDFromInt",
|
|
},
|
|
{
|
|
name: "oneOf InputFile variants",
|
|
field: makeFieldVariants("Photo", "photo", spec.KindOneOf, []string{"InputFile", "string"}, true),
|
|
contains: "InputFile",
|
|
},
|
|
{
|
|
name: "oneOf sealed",
|
|
field: makeFieldVariants("Markup", "markup", spec.KindOneOf, []string{"A", "B"}, true),
|
|
contains: "nil",
|
|
},
|
|
{
|
|
name: "unknown kind",
|
|
field: spec.Field{Name: "X", JSONName: "x", Type: spec.TypeRef{Kind: spec.Kind(99)}},
|
|
contains: "nil",
|
|
},
|
|
}
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
got := sentinelForField(c.field, unionTypes)
|
|
require.Contains(t, got, c.contains, "sentinelForField for %q", c.name)
|
|
})
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// successBody — all branches
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestSuccessBody(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
tr spec.TypeRef
|
|
want string
|
|
}{
|
|
{"bool", spec.TypeRef{Kind: spec.KindPrimitive, Name: "bool"}, "true"},
|
|
{"int64", spec.TypeRef{Kind: spec.KindPrimitive, Name: "int64"}, "0"},
|
|
{"float64", spec.TypeRef{Kind: spec.KindPrimitive, Name: "float64"}, "0"},
|
|
{"string", spec.TypeRef{Kind: spec.KindPrimitive, Name: "string"}, `""`},
|
|
{"MessageOrBool", spec.TypeRef{Kind: spec.KindNamed, Name: "MessageOrBool"}, "true"},
|
|
{"named", spec.TypeRef{Kind: spec.KindNamed, Name: "Message"}, "{}"},
|
|
{"array", spec.TypeRef{Kind: spec.KindArray}, "[]"},
|
|
{"oneOf", spec.TypeRef{Kind: spec.KindOneOf, Variants: []string{"A", "B"}}, "null"},
|
|
{"unknown", spec.TypeRef{Kind: spec.Kind(99)}, "null"},
|
|
}
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
got := successBody(c.tr)
|
|
require.Equal(t, c.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// unionTypeFor — KindOneOf branch (variant-set match)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestUnionTypeFor_OneOfVariants(t *testing.T) {
|
|
// These variant *type names* match the ChatMember discriminator.
|
|
tr := spec.TypeRef{
|
|
Kind: spec.KindOneOf,
|
|
Variants: []string{
|
|
"ChatMemberOwner", "ChatMemberAdministrator", "ChatMemberMember",
|
|
"ChatMemberRestricted", "ChatMemberLeft", "ChatMemberBanned",
|
|
},
|
|
}
|
|
name, ok := unionTypeFor(tr)
|
|
require.True(t, ok)
|
|
require.Equal(t, "ChatMember", name)
|
|
}
|
|
|
|
func TestUnionTypeFor_OneOfNoMatch(t *testing.T) {
|
|
tr := spec.TypeRef{Kind: spec.KindOneOf, Variants: []string{"Foo", "Bar"}}
|
|
_, ok := unionTypeFor(tr)
|
|
require.False(t, ok)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// funcs() returns a non-nil FuncMap with expected keys
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestFuncs_HasExpectedKeys(t *testing.T) {
|
|
fm := funcs()
|
|
require.NotNil(t, fm)
|
|
for _, key := range []string{"goType", "docComment", "returnGoType", "unionFields"} {
|
|
require.NotNil(t, fm[key], "funcs() missing key %q", key)
|
|
}
|
|
}
|