|
|
@@ -16,6 +16,7 @@ import (
|
|
|
"time"
|
|
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
|
+ "github.com/google/go-cmp/cmp/cmpopts"
|
|
|
"go4.org/mem"
|
|
|
"tailscale.com/control/controlknobs"
|
|
|
"tailscale.com/health"
|
|
|
@@ -1139,8 +1140,190 @@ func BenchmarkMapSessionDelta(b *testing.B) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// TestNetmapDisplayMessage checks that the various diff operations
|
|
|
+// (add/update/delete/clear) for [tailcfg.DisplayMessage] in a
|
|
|
+// [tailcfg.MapResponse] work as expected.
|
|
|
+func TestNetmapDisplayMessage(t *testing.T) {
|
|
|
+ type test struct {
|
|
|
+ name string
|
|
|
+ initialState *tailcfg.MapResponse
|
|
|
+ mapResponse tailcfg.MapResponse
|
|
|
+ wantMessages map[tailcfg.DisplayMessageID]tailcfg.DisplayMessage
|
|
|
+ }
|
|
|
+
|
|
|
+ tests := []test{
|
|
|
+ {
|
|
|
+ name: "basic-set",
|
|
|
+ mapResponse: tailcfg.MapResponse{
|
|
|
+ DisplayMessages: map[tailcfg.DisplayMessageID]*tailcfg.DisplayMessage{
|
|
|
+ "test-message": {
|
|
|
+ Title: "Testing",
|
|
|
+ Text: "This is a test message",
|
|
|
+ Severity: tailcfg.SeverityHigh,
|
|
|
+ ImpactsConnectivity: true,
|
|
|
+ PrimaryAction: &tailcfg.DisplayMessageAction{
|
|
|
+ URL: "https://www.example.com",
|
|
|
+ Label: "Learn more",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ wantMessages: map[tailcfg.DisplayMessageID]tailcfg.DisplayMessage{
|
|
|
+ "test-message": {
|
|
|
+ Title: "Testing",
|
|
|
+ Text: "This is a test message",
|
|
|
+ Severity: tailcfg.SeverityHigh,
|
|
|
+ ImpactsConnectivity: true,
|
|
|
+ PrimaryAction: &tailcfg.DisplayMessageAction{
|
|
|
+ URL: "https://www.example.com",
|
|
|
+ Label: "Learn more",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "delete-one",
|
|
|
+ initialState: &tailcfg.MapResponse{
|
|
|
+ DisplayMessages: map[tailcfg.DisplayMessageID]*tailcfg.DisplayMessage{
|
|
|
+ "message-a": {
|
|
|
+ Title: "Message A",
|
|
|
+ },
|
|
|
+ "message-b": {
|
|
|
+ Title: "Message B",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ mapResponse: tailcfg.MapResponse{
|
|
|
+ DisplayMessages: map[tailcfg.DisplayMessageID]*tailcfg.DisplayMessage{
|
|
|
+ "message-a": nil,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ wantMessages: map[tailcfg.DisplayMessageID]tailcfg.DisplayMessage{
|
|
|
+ "message-b": {
|
|
|
+ Title: "Message B",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "update-one",
|
|
|
+ initialState: &tailcfg.MapResponse{
|
|
|
+ DisplayMessages: map[tailcfg.DisplayMessageID]*tailcfg.DisplayMessage{
|
|
|
+ "message-a": {
|
|
|
+ Title: "Message A",
|
|
|
+ },
|
|
|
+ "message-b": {
|
|
|
+ Title: "Message B",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ mapResponse: tailcfg.MapResponse{
|
|
|
+ DisplayMessages: map[tailcfg.DisplayMessageID]*tailcfg.DisplayMessage{
|
|
|
+ "message-a": {
|
|
|
+ Title: "Message A updated",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ wantMessages: map[tailcfg.DisplayMessageID]tailcfg.DisplayMessage{
|
|
|
+ "message-a": {
|
|
|
+ Title: "Message A updated",
|
|
|
+ },
|
|
|
+ "message-b": {
|
|
|
+ Title: "Message B",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "add-one",
|
|
|
+ initialState: &tailcfg.MapResponse{
|
|
|
+ DisplayMessages: map[tailcfg.DisplayMessageID]*tailcfg.DisplayMessage{
|
|
|
+ "message-a": {
|
|
|
+ Title: "Message A",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ mapResponse: tailcfg.MapResponse{
|
|
|
+ DisplayMessages: map[tailcfg.DisplayMessageID]*tailcfg.DisplayMessage{
|
|
|
+ "message-b": {
|
|
|
+ Title: "Message B",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ wantMessages: map[tailcfg.DisplayMessageID]tailcfg.DisplayMessage{
|
|
|
+ "message-a": {
|
|
|
+ Title: "Message A",
|
|
|
+ },
|
|
|
+ "message-b": {
|
|
|
+ Title: "Message B",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "delete-all",
|
|
|
+ initialState: &tailcfg.MapResponse{
|
|
|
+ DisplayMessages: map[tailcfg.DisplayMessageID]*tailcfg.DisplayMessage{
|
|
|
+ "message-a": {
|
|
|
+ Title: "Message A",
|
|
|
+ },
|
|
|
+ "message-b": {
|
|
|
+ Title: "Message B",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ mapResponse: tailcfg.MapResponse{
|
|
|
+ DisplayMessages: map[tailcfg.DisplayMessageID]*tailcfg.DisplayMessage{
|
|
|
+ "*": nil,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ wantMessages: map[tailcfg.DisplayMessageID]tailcfg.DisplayMessage{},
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "delete-all-and-add",
|
|
|
+ initialState: &tailcfg.MapResponse{
|
|
|
+ DisplayMessages: map[tailcfg.DisplayMessageID]*tailcfg.DisplayMessage{
|
|
|
+ "message-a": {
|
|
|
+ Title: "Message A",
|
|
|
+ },
|
|
|
+ "message-b": {
|
|
|
+ Title: "Message B",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ mapResponse: tailcfg.MapResponse{
|
|
|
+ DisplayMessages: map[tailcfg.DisplayMessageID]*tailcfg.DisplayMessage{
|
|
|
+ "*": nil,
|
|
|
+ "message-c": {
|
|
|
+ Title: "Message C",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ wantMessages: map[tailcfg.DisplayMessageID]tailcfg.DisplayMessage{
|
|
|
+ "message-c": {
|
|
|
+ Title: "Message C",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, test := range tests {
|
|
|
+ t.Run(test.name, func(t *testing.T) {
|
|
|
+ ms := newTestMapSession(t, nil)
|
|
|
+
|
|
|
+ if test.initialState != nil {
|
|
|
+ ms.netmapForResponse(test.initialState)
|
|
|
+ }
|
|
|
+
|
|
|
+ nm := ms.netmapForResponse(&test.mapResponse)
|
|
|
+
|
|
|
+ if diff := cmp.Diff(test.wantMessages, nm.DisplayMessages, cmpopts.EquateEmpty()); diff != "" {
|
|
|
+ t.Errorf("unexpected warnings (-want +got):\n%s", diff)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// TestNetmapHealthIntegration checks that we get the expected health warnings
|
|
|
-// from processing a map response and passing the NetworkMap to a health tracker
|
|
|
+// from processing a [tailcfg.MapResponse] containing health messages and passing the
|
|
|
+// [netmap.NetworkMap] to a [health.Tracker].
|
|
|
func TestNetmapHealthIntegration(t *testing.T) {
|
|
|
ms := newTestMapSession(t, nil)
|
|
|
ht := health.Tracker{}
|
|
|
@@ -1182,3 +1365,56 @@ func TestNetmapHealthIntegration(t *testing.T) {
|
|
|
t.Fatalf("CurrentStatus().Warnings[\"control-health*\"] different than expected (-want +got)\n%s", d)
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+// TestNetmapDisplayMessageIntegration checks that we get the expected health
|
|
|
+// warnings from processing a [tailcfg.MapResponse] that contains DisplayMessages and
|
|
|
+// passing the [netmap.NetworkMap] to a [health.Tracker].
|
|
|
+func TestNetmapDisplayMessageIntegration(t *testing.T) {
|
|
|
+ ms := newTestMapSession(t, nil)
|
|
|
+ ht := health.Tracker{}
|
|
|
+
|
|
|
+ ht.SetIPNState("NeedsLogin", true)
|
|
|
+ ht.GotStreamedMapResponse()
|
|
|
+ baseWarnings := ht.CurrentState().Warnings
|
|
|
+
|
|
|
+ nm := ms.netmapForResponse(&tailcfg.MapResponse{
|
|
|
+ DisplayMessages: map[tailcfg.DisplayMessageID]*tailcfg.DisplayMessage{
|
|
|
+ "test-message": {
|
|
|
+ Title: "Testing",
|
|
|
+ Text: "This is a test message",
|
|
|
+ Severity: tailcfg.SeverityHigh,
|
|
|
+ ImpactsConnectivity: true,
|
|
|
+ PrimaryAction: &tailcfg.DisplayMessageAction{
|
|
|
+ URL: "https://www.example.com",
|
|
|
+ Label: "Learn more",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ })
|
|
|
+ ht.SetControlHealth(nm.DisplayMessages)
|
|
|
+
|
|
|
+ state := ht.CurrentState()
|
|
|
+
|
|
|
+ // Ignore warnings that aren't from the netmap
|
|
|
+ for k := range baseWarnings {
|
|
|
+ delete(state.Warnings, k)
|
|
|
+ }
|
|
|
+
|
|
|
+ want := map[health.WarnableCode]health.UnhealthyState{
|
|
|
+ "test-message": {
|
|
|
+ WarnableCode: "test-message",
|
|
|
+ Title: "Testing",
|
|
|
+ Text: "This is a test message",
|
|
|
+ Severity: health.SeverityHigh,
|
|
|
+ ImpactsConnectivity: true,
|
|
|
+ PrimaryAction: &health.UnhealthyStateAction{
|
|
|
+ URL: "https://www.example.com",
|
|
|
+ Label: "Learn more",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ if diff := cmp.Diff(want, state.Warnings); diff != "" {
|
|
|
+ t.Errorf("unexpected message contents (-want +got):\n%s", diff)
|
|
|
+ }
|
|
|
+}
|