Files
go-telegram/cmd/genapi/extra_test.go
T
lukaszraczylo d666cda3b1 feat(genapi): derive discriminators and decode RichText alternates
The generator gated all union decoder emission behind knownDiscriminators, a
hand-curated map of 13 entries. Any sealed union absent from that map received
no decoder, so decoding must fail. Bot API v10 added RichBlock and RichText,
which were absent. Roughly 8.6 percent of Telegram updates failed with HTTP 400.

The generator now derives discriminators from the specification instead of a
fixed list. A gate check fails the build when a union resolves no discriminator.
Eleven unions gain decoders that never had one: BotCommandScope, InputMedia,
InputPaidMedia, InputPollMedia, InputPollOptionMedia, InputProfilePhoto,
InputRichBlock, InputStoryContent, PassportElementError, RichBlock and RichText.

RichText can also appear as a bare string or an array of RichText. The scraper
now extracts these alternate shapes into the intermediate representation. The
emitter adds RichTextPlain and RichTextSequence. The decoder dispatches on the
first JSON byte before it reads any discriminator.

Unions without alternates must regenerate byte for byte. The golden test proves
this.

Claude-Session: https://claude.ai/code/session_01Fu2wXqGUrfVBi5aLafKk8U
2026-09-15 10:59:48 +01:00

646 lines
22 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(nil, "", 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(nil, "", 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(nil, "", 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(nil, "", 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(nil, "", 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(nil, "", 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(nil, "", 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(nil, "", 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(nil, "", 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(nil, "", 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(nil, "", 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(nil, "", 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(nil, "", 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(nil, "", 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(nil, "", 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, knownDiscriminators)
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, knownDiscriminators)
require.True(t, ok)
require.Equal(t, "ChatMember", name)
}
func TestUnionTypeFor_ArrayNilElem(t *testing.T) {
tr := spec.TypeRef{Kind: spec.KindArray}
_, ok := unionTypeFor(tr, knownDiscriminators)
require.False(t, ok)
}
func TestUnionTypeFor_NotUnion(t *testing.T) {
tr := spec.TypeRef{Kind: spec.KindNamed, Name: "Message"}
_, ok := unionTypeFor(tr, knownDiscriminators)
require.False(t, ok)
}
func TestUnionTypeFor_Unknown(t *testing.T) {
tr := spec.TypeRef{Kind: spec.Kind(99)}
_, ok := unionTypeFor(tr, knownDiscriminators)
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, knownDiscriminators)
require.Equal(t, "ChatMember", name)
}
func TestUnionNameByVariants_Unknown(t *testing.T) {
name := unionNameByVariants([]string{"X", "Y", "Z"}, knownDiscriminators)
require.Equal(t, "", name)
}
// ---------------------------------------------------------------------------
// hasUnionElem
// ---------------------------------------------------------------------------
func TestHasUnionElem_NonArray(t *testing.T) {
tr := spec.TypeRef{Kind: spec.KindNamed, Name: "ChatMember"}
require.False(t, hasUnionElem(tr, knownDiscriminators))
}
func TestHasUnionElem_ArrayNilElem(t *testing.T) {
tr := spec.TypeRef{Kind: spec.KindArray}
require.False(t, hasUnionElem(tr, knownDiscriminators))
}
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, knownDiscriminators))
}
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, knownDiscriminators))
}
// ---------------------------------------------------------------------------
// 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, knownDiscriminators)
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("testMethod", c.field, unionTypes, nil)
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, knownDiscriminators)
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, knownDiscriminators)
require.False(t, ok)
}
// ---------------------------------------------------------------------------
// funcs() returns a non-nil FuncMap with expected keys
// ---------------------------------------------------------------------------
func TestFuncs_HasExpectedKeys(t *testing.T) {
fm := funcs(nil, nil)
require.NotNil(t, fm)
for _, key := range []string{"goType", "docComment", "returnGoType", "unionFields"} {
require.NotNil(t, fm[key], "funcs() missing key %q", key)
}
}