chore: refresh gramsrv public release
This commit is contained in:
parent
75cebe8dbf
commit
70b6820474
1274 changed files with 378751 additions and 59919 deletions
163
internal/compat/layerwire/gate_test.go
Normal file
163
internal/compat/layerwire/gate_test.go
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
package layerwire
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// isInbound reports whether a constructor is a client->server (Input*) type the
|
||||
// server never emits, so it cannot appear in downgraded output.
|
||||
func isInbound(cl *ctorLayout) bool {
|
||||
return strings.HasPrefix(cl.result, "Input") || strings.HasPrefix(cl.name, "input")
|
||||
}
|
||||
|
||||
// collectReachableTypes returns abstract/bare type names that can appear in
|
||||
// downgraded output at a layer: those referenced by a *retained* field of a
|
||||
// constructor that is itself emittable (not a function, not a 227-only type that
|
||||
// is replaced wholesale, not an inbound Input* type).
|
||||
func collectReachableTypes(lt *layerTables) map[string]bool {
|
||||
refs := map[string]bool{}
|
||||
var addField func(f *fieldLayout)
|
||||
addField = func(f *fieldLayout) {
|
||||
switch f.kind {
|
||||
case kindObject, kindBareObject:
|
||||
refs[f.typeName] = true
|
||||
case kindVector, kindVectorBare:
|
||||
addField(f.elem)
|
||||
}
|
||||
}
|
||||
for crc, cl := range canonical.byCRC {
|
||||
if cl.isFunc || isInbound(cl) || lt.newTypes[crc] {
|
||||
continue
|
||||
}
|
||||
var keep map[string]bool
|
||||
if r := lt.rules[crc]; r != nil && r.structural == "" {
|
||||
keep = r.keep
|
||||
}
|
||||
for i := range cl.fields {
|
||||
f := &cl.fields[i]
|
||||
if f.isFlags {
|
||||
continue
|
||||
}
|
||||
if keep != nil && !keep[f.name] {
|
||||
continue // dropped by a mechanical rule
|
||||
}
|
||||
addField(f)
|
||||
}
|
||||
}
|
||||
return refs
|
||||
}
|
||||
|
||||
// unemittedAllowlist is the curated set of reachable-but-unhandled 227-only
|
||||
// constructors that telesrv does not actually emit (confirmed against the
|
||||
// outbound-constructor scoping audit, 2026-06-25). They live behind features the
|
||||
// server lacks (instant-view rich pages, AI compose, managed bots, web-browser
|
||||
// settings, guest chat, star-gift rarity/craft, join-chat bot results). The gate
|
||||
// fails if a NEW reachable type appears that is neither handled nor listed here,
|
||||
// forcing a human to triage on every gotd bump / client upgrade.
|
||||
var unemittedAllowlist = map[string]bool{
|
||||
"aiComposeTone": true,
|
||||
"aiComposeToneDefault": true,
|
||||
"aiComposeToneExample": true,
|
||||
"botInlineMessageRichMessage": true,
|
||||
"channelAdminLogEventActionParticipantEditRank": true,
|
||||
"joinChatBotResultApproved": true,
|
||||
"joinChatBotResultDeclined": true,
|
||||
"joinChatBotResultQueued": true,
|
||||
"joinChatBotResultWebView": true,
|
||||
"messages.chatInviteJoinResultWebView": true,
|
||||
"messages.emojiGameDiceInfo": true,
|
||||
"messages.emojiGameUnavailable": true,
|
||||
"requestPeerTypeCreateBot": true,
|
||||
"richMessage": true,
|
||||
"sendMessageRichMessageDraftAction": true,
|
||||
"starGiftAttributeRarity": true,
|
||||
"starGiftAttributeRarityEpic": true,
|
||||
"starGiftAttributeRarityLegendary": true,
|
||||
"starGiftAttributeRarityRare": true,
|
||||
"starGiftAttributeRarityUncommon": true,
|
||||
"topPeerCategoryBotsGuestChat": true,
|
||||
"updateAiComposeTones": true,
|
||||
"updateBotGuestChatQuery": true,
|
||||
"updateChatParticipantRank": true,
|
||||
"updateEmojiGameInfo": true,
|
||||
"updateJoinChatWebViewDecision": true,
|
||||
"updateManagedBot": true,
|
||||
"updateNewBotConnection": true,
|
||||
"updateStarGiftCraftFail": true,
|
||||
"updateWebBrowserException": true,
|
||||
"updateWebBrowserSettings": true,
|
||||
"webDomainException": true,
|
||||
"webPageAttributeAiComposeTone": true,
|
||||
// Structural changed-types telesrv does not emit (see design Appendix C and
|
||||
// the scoping audit); their hand transforms are deferred to CI-todo.
|
||||
"pageListOrderedItemText": true,
|
||||
"pageListOrderedItemBlocks": true,
|
||||
"starGiftAttributeModel": true,
|
||||
"starGiftAttributeBackdrop": true,
|
||||
"starGiftAttributePattern": true,
|
||||
"urlAuthResultAccepted": true,
|
||||
"inputMediaPoll": true, // inbound only
|
||||
}
|
||||
|
||||
func newTypeHandled(crc uint32, result string) bool {
|
||||
if newTypeFallbacks[crc] != nil {
|
||||
return true
|
||||
}
|
||||
return newTypeFallbacksByType[result] != nil
|
||||
}
|
||||
|
||||
// TestCoverageGate is the drift gate. For every supported layer, each 227-only
|
||||
// or structural constructor that can appear in downgraded output must be either
|
||||
// handled (fallback / structural transform) or explicitly allowlisted as not
|
||||
// emitted. A bare failure means new wire shape slipped in unhandled.
|
||||
func TestCoverageGate(t *testing.T) {
|
||||
for layer := SupportedFloor; layer < CanonicalLayer; layer++ {
|
||||
lt := tables[layer]
|
||||
if lt == nil {
|
||||
t.Fatalf("no tables for layer %d", layer)
|
||||
}
|
||||
reach := collectReachableTypes(lt)
|
||||
|
||||
// Structural rules that are reachable need a registered transform.
|
||||
for crc, r := range lt.rules {
|
||||
if r.structural == "" {
|
||||
continue
|
||||
}
|
||||
cl := canonical.byCRC[crc]
|
||||
reachable := cl != nil && reach[cl.result] && !isInbound(cl)
|
||||
handled := structuralTransforms[r.structural] != nil
|
||||
if reachable && !handled && !unemittedAllowlist[nameOf(crc)] {
|
||||
t.Errorf("layer %d: reachable structural %s (%#08x) has no transform", layer, nameOf(crc), crc)
|
||||
}
|
||||
}
|
||||
|
||||
// New constructors reachable through a retained field need a fallback.
|
||||
var gaps []string
|
||||
for crc := range lt.newTypes {
|
||||
cl := canonical.byCRC[crc]
|
||||
if cl == nil || cl.isFunc || isInbound(cl) {
|
||||
continue
|
||||
}
|
||||
if !reach[cl.result] {
|
||||
continue
|
||||
}
|
||||
if newTypeHandled(crc, cl.result) || unemittedAllowlist[cl.name] {
|
||||
continue
|
||||
}
|
||||
gaps = append(gaps, cl.name)
|
||||
}
|
||||
if len(gaps) > 0 {
|
||||
sort.Strings(gaps)
|
||||
t.Errorf("layer %d: %d reachable 227-only types lack a fallback or allowlist entry:\n %v", layer, len(gaps), gaps)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func nameOf(crc uint32) string {
|
||||
if cl := canonical.byCRC[crc]; cl != nil {
|
||||
return cl.name
|
||||
}
|
||||
return "?"
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue