beacon v2 discovery: advertise metamesh.core + service resources on 239.255.99.1:9099, /api/neighbors ?cap= filter
Build and Push Docker Image / build (push) Failing after 14s
Build and Push Docker Image / build (push) Failing after 14s
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
package meshdisco
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCapMatches(t *testing.T) {
|
||||
cases := []struct {
|
||||
pattern, cap string
|
||||
want bool
|
||||
}{
|
||||
{"metamesh.transport/*", "metamesh.transport/nzb@1", true},
|
||||
{"metamesh.transport/*", "metamesh.transport/ipfs", true},
|
||||
{"metamesh.transport/*", "metamesh.transport", false},
|
||||
{"metamesh.transport/*", "metamesh.transportx/a", false},
|
||||
{"metamesh.transport/*", "metamesh.transport/", false},
|
||||
{"metamesh.transport/nzb", "metamesh.transport/nzb@2", true},
|
||||
{"metamesh.transport/nzb@2", "metamesh.transport/nzb@2", true},
|
||||
{"metamesh.transport/nzb@2", "metamesh.transport/nzb@1", false},
|
||||
{"metamesh.transport/nzb@2", "metamesh.transport/nzb", false},
|
||||
{"metamesh.transport/*@1", "metamesh.transport/nzb@1", true},
|
||||
{"metamesh.core", "metamesh.core", true},
|
||||
{"metamesh.core", "metamesh.core2", false},
|
||||
{"mcp", "mcp/x", false},
|
||||
{"*", "anything@3", true},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if got := CapMatches(c.pattern, c.cap); got != c.want {
|
||||
t.Errorf("CapMatches(%q, %q) = %v, want %v", c.pattern, c.cap, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRejectsV1AndForeign(t *testing.T) {
|
||||
for _, raw := range []string{
|
||||
`{"type":"discovery"}`,
|
||||
`{"type":"announce","name":"x","tools":[]}`,
|
||||
`{"v":1,"type":"announce","name":"meta-sort"}`,
|
||||
`{"proto":"beacon","v":3,"type":"probe"}`,
|
||||
`{"proto":"beacon","v":2,"type":"announce"}`,
|
||||
`{"proto":"beacon","v":2,"type":"advertise"}`,
|
||||
"\x00garbage",
|
||||
} {
|
||||
if _, ok := Parse([]byte(raw)); ok {
|
||||
t.Errorf("Parse(%q) accepted", raw)
|
||||
}
|
||||
}
|
||||
if _, ok := Parse([]byte(`{"proto":"beacon","v":2,"type":"probe","want":["metamesh.core"]}`)); !ok {
|
||||
t.Error("valid probe rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNeighborRowCarriesCapsAndBaseUrl(t *testing.T) {
|
||||
nb := newNeighbor(
|
||||
NodeInfo{Name: "meta-sort", Instance: "metasort-app"},
|
||||
[]Resource{{ID: "service", Caps: []string{"metamesh.service/meta-sort"}, Endpoints: map[string]string{"ui": "https://x"}}},
|
||||
"10.0.0.1", time.Now(),
|
||||
)
|
||||
if nb.BaseUrl != "https://x" || len(nb.Caps) != 1 || !nb.Matches(CapAnyService) {
|
||||
t.Fatalf("unexpected row: %+v", nb)
|
||||
}
|
||||
}
|
||||
|
||||
// End to end over loopback: a probe with a non-matching want gets no reply;
|
||||
// a matching one gets the advertise; advertise then bye updates the view.
|
||||
func TestProbeAndRecord(t *testing.T) {
|
||||
port := freePort(t)
|
||||
n := New(Config{
|
||||
Name: "meta-core", Instance: "core-1", Port: port, Interval: time.Hour,
|
||||
Payload: func() (string, []Resource) {
|
||||
return "running", []Resource{{ID: "core", Caps: []string{CapCore}}}
|
||||
},
|
||||
})
|
||||
if err := n.Start(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer n.Stop()
|
||||
|
||||
c, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
dst := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: port}
|
||||
buf := make([]byte, ReadBufferSize)
|
||||
|
||||
send := func(m Message) {
|
||||
b, _ := json.Marshal(m)
|
||||
if _, err := c.WriteToUDP(b, dst); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
send(Message{Proto: Proto, V: Version, Type: TypeProbe, From: "cli", Want: []string{"metamesh.service/*"}})
|
||||
c.SetReadDeadline(time.Now().Add(300 * time.Millisecond))
|
||||
if _, _, err := c.ReadFromUDP(buf); err == nil {
|
||||
t.Fatal("non-matching probe got a reply")
|
||||
}
|
||||
|
||||
send(Message{Proto: Proto, V: Version, Type: TypeProbe, From: "cli", Want: []string{CapCore}})
|
||||
c.SetReadDeadline(time.Now().Add(2 * time.Second))
|
||||
nr, _, err := c.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("matching probe got no reply: %v", err)
|
||||
}
|
||||
if m, ok := Parse(buf[:nr]); !ok || m.Type != TypeAdvertise || m.Node.Instance != "core-1" {
|
||||
t.Fatalf("bad reply: %s", buf[:nr])
|
||||
}
|
||||
|
||||
peer := &NodeInfo{Name: "meta-sort", Instance: "sort-1"}
|
||||
send(Message{Proto: Proto, V: Version, Type: TypeAdvertise, Node: peer,
|
||||
Resources: []Resource{{ID: "service", Caps: []string{"metamesh.service/meta-sort"}}}})
|
||||
waitFor(t, func() bool { return len(n.Neighbors()) == 1 })
|
||||
send(Message{Proto: Proto, V: Version, Type: TypeBye, Node: peer})
|
||||
waitFor(t, func() bool { return len(n.Neighbors()) == 0 })
|
||||
}
|
||||
|
||||
func freePort(t *testing.T) int {
|
||||
c, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
return c.LocalAddr().(*net.UDPAddr).Port
|
||||
}
|
||||
|
||||
func waitFor(t *testing.T, cond func() bool) {
|
||||
t.Helper()
|
||||
deadline := time.Now().Add(2 * time.Second)
|
||||
for time.Now().Before(deadline) {
|
||||
if cond() {
|
||||
return
|
||||
}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
}
|
||||
t.Fatal("condition not reached")
|
||||
}
|
||||
Reference in New Issue
Block a user