package structs import ( "fmt" "reflect" "strings" "testing" ) func TestEncodeDecode(t *testing.T) { arg := &RegisterRequest{ Datacenter: "foo", Node: "bar", Address: "baz", Service: &NodeService{ Service: "test", Address: "127.0.0.2", }, } buf, err := Encode(RegisterRequestType, arg) if err != nil { t.Fatalf("err: %v", err) } var out RegisterRequest err = Decode(buf[1:], &out) if err != nil { t.Fatalf("err: %v", err) } if !reflect.DeepEqual(arg.Service, out.Service) { t.Fatalf("bad: %#v %#v", arg.Service, out.Service) } if !reflect.DeepEqual(arg, &out) { t.Fatalf("bad: %#v %#v", arg, out) } } func TestStructs_Implements(t *testing.T) { var ( _ RPCInfo = &RegisterRequest{} _ RPCInfo = &DeregisterRequest{} _ RPCInfo = &DCSpecificRequest{} _ RPCInfo = &ServiceSpecificRequest{} _ RPCInfo = &NodeSpecificRequest{} _ RPCInfo = &ChecksInStateRequest{} _ RPCInfo = &KVSRequest{} _ RPCInfo = &KeyRequest{} _ RPCInfo = &KeyListRequest{} _ RPCInfo = &SessionRequest{} _ RPCInfo = &SessionSpecificRequest{} _ RPCInfo = &EventFireRequest{} _ RPCInfo = &ACLPolicyRequest{} _ RPCInfo = &KeyringRequest{} _ CompoundResponse = &KeyringResponses{} ) } // testServiceNode gives a fully filled out ServiceNode instance. func testServiceNode() *ServiceNode { return &ServiceNode{ Node: "node1", Address: "127.0.0.1", ServiceID: "service1", ServiceName: "dogs", ServiceTags: []string{"prod", "v1"}, ServiceAddress: "127.0.0.2", ServicePort: 8080, ServiceEnableTagOverride: true, RaftIndex: RaftIndex{ CreateIndex: 1, ModifyIndex: 2, }, } } func TestStructs_ServiceNode_Clone(t *testing.T) { sn := testServiceNode() clone := sn.Clone() if !reflect.DeepEqual(sn, clone) { t.Fatalf("bad: %v", clone) } sn.ServiceTags = append(sn.ServiceTags, "hello") if reflect.DeepEqual(sn, clone) { t.Fatalf("clone wasn't independent of the original") } } func TestStructs_ServiceNode_Conversions(t *testing.T) { sn := testServiceNode() sn2 := sn.ToNodeService().ToServiceNode("node1", "127.0.0.1") if !reflect.DeepEqual(sn, sn2) { t.Fatalf("bad: %v", sn2) } } func TestStructs_NodeService_IsSame(t *testing.T) { ns := &NodeService{ ID: "node1", Service: "theservice", Tags: []string{"foo", "bar"}, Address: "127.0.0.1", Port: 1234, EnableTagOverride: true, } if !ns.IsSame(ns) { t.Fatalf("should be equal to itself") } other := &NodeService{ ID: "node1", Service: "theservice", Tags: []string{"foo", "bar"}, Address: "127.0.0.1", Port: 1234, EnableTagOverride: true, RaftIndex: RaftIndex{ CreateIndex: 1, ModifyIndex: 2, }, } if !ns.IsSame(other) || !other.IsSame(ns) { t.Fatalf("should not care about Raft fields") } check := func(twiddle, restore func()) { if !ns.IsSame(other) || !other.IsSame(ns) { t.Fatalf("should be the same") } twiddle() if ns.IsSame(other) || other.IsSame(ns) { t.Fatalf("should not be the same") } restore() if !ns.IsSame(other) || !other.IsSame(ns) { t.Fatalf("should be the same") } } check(func() { other.ID = "XXX" }, func() { other.ID = "node1" }) check(func() { other.Service = "XXX" }, func() { other.Service = "theservice" }) check(func() { other.Tags = nil }, func() { other.Tags = []string{"foo", "bar"} }) check(func() { other.Tags = []string{"foo"} }, func() { other.Tags = []string{"foo", "bar"} }) check(func() { other.Address = "XXX" }, func() { other.Address = "127.0.0.1" }) check(func() { other.Port = 9999 }, func() { other.Port = 1234 }) check(func() { other.EnableTagOverride = false }, func() { other.EnableTagOverride = true }) } func TestStructs_HealthCheck_IsSame(t *testing.T) { hc := &HealthCheck{ Node: "node1", CheckID: "check1", Name: "thecheck", Status: HealthPassing, Notes: "it's all good", Output: "lgtm", ServiceID: "service1", ServiceName: "theservice", } if !hc.IsSame(hc) { t.Fatalf("should be equal to itself") } other := &HealthCheck{ Node: "node1", CheckID: "check1", Name: "thecheck", Status: HealthPassing, Notes: "it's all good", Output: "lgtm", ServiceID: "service1", ServiceName: "theservice", RaftIndex: RaftIndex{ CreateIndex: 1, ModifyIndex: 2, }, } if !hc.IsSame(other) || !other.IsSame(hc) { t.Fatalf("should not care about Raft fields") } check := func(field *string) { if !hc.IsSame(other) || !other.IsSame(hc) { t.Fatalf("should be the same") } old := *field *field = "XXX" if hc.IsSame(other) || other.IsSame(hc) { t.Fatalf("should not be the same") } *field = old if !hc.IsSame(other) || !other.IsSame(hc) { t.Fatalf("should be the same") } } check(&other.Node) check(&other.CheckID) check(&other.Name) check(&other.Status) check(&other.Notes) check(&other.Output) check(&other.ServiceID) check(&other.ServiceName) } func TestStructs_CheckServiceNodes_Shuffle(t *testing.T) { // Make a huge list of nodes. var nodes CheckServiceNodes for i := 0; i < 100; i++ { nodes = append(nodes, CheckServiceNode{ Node: &Node{ Node: fmt.Sprintf("node%d", i), Address: fmt.Sprintf("127.0.0.%d", i+1), }, }) } // Keep track of how many unique shuffles we get. uniques := make(map[string]struct{}) for i := 0; i < 100; i++ { nodes.Shuffle() var names []string for _, node := range nodes { names = append(names, node.Node.Node) } key := strings.Join(names, "|") uniques[key] = struct{}{} } // We have to allow for the fact that there won't always be a unique // shuffle each pass, so we just look for smell here without the test // being flaky. if len(uniques) < 50 { t.Fatalf("unique shuffle ratio too low: %d/100", len(uniques)) } } func TestStructs_CheckServiceNodes_Filter(t *testing.T) { nodes := CheckServiceNodes{ CheckServiceNode{ Node: &Node{ Node: "node1", Address: "127.0.0.1", }, Checks: HealthChecks{ &HealthCheck{ Status: HealthWarning, }, }, }, CheckServiceNode{ Node: &Node{ Node: "node2", Address: "127.0.0.2", }, Checks: HealthChecks{ &HealthCheck{ Status: HealthPassing, }, }, }, CheckServiceNode{ Node: &Node{ Node: "node3", Address: "127.0.0.3", }, Checks: HealthChecks{ &HealthCheck{ Status: HealthCritical, }, }, }, } // Test the case where warnings are allowed. { twiddle := make(CheckServiceNodes, len(nodes)) if n := copy(twiddle, nodes); n != len(nodes) { t.Fatalf("bad: %d", n) } filtered := twiddle.Filter(false) expected := CheckServiceNodes{ nodes[0], nodes[1], } if !reflect.DeepEqual(filtered, expected) { t.Fatalf("bad: %v", filtered) } } // Limit to only passing checks. { twiddle := make(CheckServiceNodes, len(nodes)) if n := copy(twiddle, nodes); n != len(nodes) { t.Fatalf("bad: %d", n) } filtered := twiddle.Filter(true) expected := CheckServiceNodes{ nodes[1], } if !reflect.DeepEqual(filtered, expected) { t.Fatalf("bad: %v", filtered) } } } func TestStructs_DirEntry_Clone(t *testing.T) { e := &DirEntry{ LockIndex: 5, Key: "hello", Flags: 23, Value: []byte("this is a test"), Session: "session1", RaftIndex: RaftIndex{ CreateIndex: 1, ModifyIndex: 2, }, } clone := e.Clone() if !reflect.DeepEqual(e, clone) { t.Fatalf("bad: %v", clone) } e.Value = []byte("a new value") if reflect.DeepEqual(e, clone) { t.Fatalf("clone wasn't independent of the original") } }