Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions cotlib.go
Original file line number Diff line number Diff line change
Expand Up @@ -2201,6 +2201,14 @@ func (e *Event) ToXML() ([]byte, error) {
buf.WriteString(escapeAttr(c.Callsign))
buf.WriteByte('"')
}
// endpoint is what the TAK server uses to register the
// callsign->connection mapping; dropping it broke inbound DM
// routing to a self-SA presence (INFR-207 / INFR-206).
if c.Endpoint != "" {
buf.WriteString(` endpoint="`)
buf.WriteString(escapeAttr(c.Endpoint))
buf.WriteByte('"')
}
buf.WriteString("/>\n")
}
if g := e.Detail.Group; g != nil {
Expand Down
6 changes: 6 additions & 0 deletions event_schema_error_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
//go:build !novalidator

// This test exercises the validator schema-init error path (initErr /
// eventPointSchema), which only exists in the cgo validator build
// (event_schema.go is !novalidator). Without this tag it fails to compile
// under -tags novalidator.
package cotlib

import (
Expand Down
41 changes: 41 additions & 0 deletions roundtrip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,50 @@ package cotlib

import (
"context"
"strings"
"testing"
)

// TestContactEndpointRoundTrip guards INFR-207: ToXML() was a hand-written
// serializer that emitted <contact callsign> but dropped the endpoint attribute,
// even though Contact.Endpoint is set. The TAK server keys the callsign->
// connection mapping off that endpoint, so dropping it silently broke inbound
// direct-message routing to a self-SA presence (INFR-206). The endpoint must
// both appear in the serialized XML and survive a parse round-trip.
func TestContactEndpointRoundTrip(t *testing.T) {
orig, err := NewEvent("EP1", "a-f-G-U-C", 1.0, 2.0, 0)
if err != nil {
t.Fatalf("new event: %v", err)
}
orig.Detail = &Detail{
Contact: &Contact{Callsign: "NERVA", Endpoint: "*:-1:stcp"},
}
xmlData, err := orig.ToXML()
if err != nil {
t.Fatalf("marshal: %v", err)
}
ReleaseEvent(orig)

if !strings.Contains(string(xmlData), `endpoint="*:-1:stcp"`) {
t.Fatalf("ToXML dropped the contact endpoint attribute: %s", xmlData)
}

evt, err := UnmarshalXMLEvent(context.Background(), xmlData)
if err != nil {
t.Fatalf("unmarshal: %v", err)
}
if evt.Detail == nil || evt.Detail.Contact == nil {
t.Fatalf("no contact after round-trip: %#v", evt)
}
if got := evt.Detail.Contact.Endpoint; got != "*:-1:stcp" {
t.Errorf("endpoint not preserved through round-trip: got %q want %q", got, "*:-1:stcp")
}
if got := evt.Detail.Contact.Callsign; got != "NERVA" {
t.Errorf("callsign not preserved through round-trip: got %q want %q", got, "NERVA")
}
ReleaseEvent(evt)
}

func TestUnmarshalXMLEventRoundTrip(t *testing.T) {
orig, err := NewEvent("RT1", "a-f-G", 10.0, 20.0, 0)
if err != nil {
Expand Down
Loading