115 lines
4.1 KiB
Go
115 lines
4.1 KiB
Go
package layerwire
|
|
|
|
import "testing"
|
|
|
|
// TestInboundUpgradeTableWellFormed checks every inbound upgrade maps an old id
|
|
// to a real canonical method id, and that the old id is genuinely historical
|
|
// (not already a canonical constructor).
|
|
func TestInboundUpgradeTableWellFormed(t *testing.T) {
|
|
if len(inboundMethodUpgrades) == 0 {
|
|
t.Fatal("inboundMethodUpgrades is empty")
|
|
}
|
|
for oldID, newID := range inboundMethodUpgrades {
|
|
cl := canonical.byCRC[newID]
|
|
if cl == nil {
|
|
t.Errorf("upgrade target %#08x is not a canonical constructor", newID)
|
|
continue
|
|
}
|
|
if !cl.isFunc {
|
|
t.Errorf("upgrade target %s (%#08x) is not a method", cl.name, newID)
|
|
}
|
|
if oldID == newID {
|
|
t.Errorf("%s: old id equals canonical id %#08x", cl.name, oldID)
|
|
}
|
|
if prev := canonical.byCRC[oldID]; prev != nil {
|
|
t.Errorf("old id %#08x collides with canonical %s", oldID, prev.name)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestClientMethodAliasesWellFormed checks every hand-maintained client-drift
|
|
// alias maps to a real canonical method, and is reachable via UpgradeMethodCRC.
|
|
func TestClientMethodAliasesWellFormed(t *testing.T) {
|
|
for oldID, newID := range clientMethodAliases {
|
|
cl := canonical.byCRC[newID]
|
|
if cl == nil || !cl.isFunc {
|
|
t.Errorf("alias target %#08x is not a canonical method", newID)
|
|
}
|
|
if _, ok := inboundMethodUpgrades[oldID]; ok {
|
|
t.Errorf("alias %#08x duplicates a generated upgrade entry", oldID)
|
|
}
|
|
if got, ok := UpgradeMethodCRC(oldID); !ok || got != newID {
|
|
t.Errorf("UpgradeMethodCRC(%#08x) = (%#08x,%v), want (%#08x,true)", oldID, got, ok, newID)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestInboundDriftCoverage is the inbound drift gate: it statically proves every
|
|
// client-drift constructor in client-drift.tl can be upgraded to its canonical
|
|
// method — shared fields match (or have a converter), canonical-only required
|
|
// fields are defaultable, and renamed fields are mapped. Adding a TL line that
|
|
// isn't auto-upgradable fails here, telling the author exactly what converter or
|
|
// rename to declare (instead of discovering it at runtime).
|
|
func TestInboundDriftCoverage(t *testing.T) {
|
|
defaultable := map[wireKind]bool{
|
|
kindInt: true, kindLong: true, kindDouble: true, kindInt128: true, kindInt256: true,
|
|
kindBytes: true, kindString: true, kindBool: true, kindVector: true, kindVectorBare: true,
|
|
}
|
|
for crc, old := range driftModel.byCRC {
|
|
target := canonical.byName[old.name]
|
|
if target == nil {
|
|
t.Errorf("drift %s (%#08x): no canonical method of that name", old.name, crc)
|
|
continue
|
|
}
|
|
oldHas := map[string]*fieldLayout{}
|
|
for i := range old.fields {
|
|
oldHas[old.fields[i].name] = &old.fields[i]
|
|
}
|
|
for i := range target.fields {
|
|
nf := &target.fields[i]
|
|
if nf.isFlags {
|
|
continue
|
|
}
|
|
oldName := nf.name
|
|
if m, ok := driftFieldRenames[old.name+"\x00"+nf.name]; ok {
|
|
oldName = m
|
|
}
|
|
if of, ok := oldHas[oldName]; ok {
|
|
if typeSig(of) != typeSig(nf) && fieldConverters[typeSig(of)+"->"+typeSig(nf)] == nil {
|
|
t.Errorf("drift %s: field %q needs converter %s->%s", old.name, nf.name, typeSig(of), typeSig(nf))
|
|
}
|
|
continue
|
|
}
|
|
if nf.conditional() || nf.kind == kindTrue {
|
|
continue // optional canonical-only field — left absent
|
|
}
|
|
if !defaultable[nf.kind] {
|
|
t.Errorf("drift %s: canonical-only required field %q (kind %d) is not defaultable; declare a transform", old.name, nf.name, nf.kind)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestInboundUpgradeSendMessage validates the full chain for the highest-value
|
|
// method: a layer-220 client's messages.sendMessage id upgrades to the 227 id.
|
|
func TestInboundUpgradeSendMessage(t *testing.T) {
|
|
m220 := loadLayerModel(t, 220)
|
|
old, ok := m220.byName["messages.sendMessage"]
|
|
if !ok {
|
|
t.Fatal("messages.sendMessage missing from layer-220 schema")
|
|
}
|
|
canon, ok := canonical.byName["messages.sendMessage"]
|
|
if !ok {
|
|
t.Fatal("messages.sendMessage missing from canonical schema")
|
|
}
|
|
if old.crc == canon.crc {
|
|
t.Skip("sendMessage unchanged 220->227; nothing to upgrade")
|
|
}
|
|
newID, ok := UpgradeMethodCRC(old.crc)
|
|
if !ok {
|
|
t.Fatalf("sendMessage@220 (%#08x) not in upgrade table", old.crc)
|
|
}
|
|
if newID != canon.crc {
|
|
t.Fatalf("sendMessage upgrade = %#08x, want canonical %#08x", newID, canon.crc)
|
|
}
|
|
}
|