github.com/arduino/arduino-cloud-cli@v0.0.0-20240517070944-e7a449561083/internal/template/load_test.go (about)

     1  // This file is part of arduino-cloud-cli.
     2  //
     3  // Copyright (C) 2021 ARDUINO SA (http://www.arduino.cc/)
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published
     7  // by the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    17  
    18  package template
    19  
    20  import (
    21  	"context"
    22  	"testing"
    23  
    24  	iotclient "github.com/arduino/iot-client-go"
    25  	"github.com/gofrs/uuid"
    26  	"github.com/google/go-cmp/cmp"
    27  )
    28  
    29  const (
    30  	// Real IDs will be UUIDs v4 like this: 9231a50b-8680-4489-a465-2b769fc310cb
    31  	// Here we use these text strings to improve test errors readability
    32  	switchyID    = "switchy-id"
    33  	relayID      = "relay-id"
    34  	blinkSpeedID = "blink_speed-id"
    35  
    36  	thingOverriddenID   = "thing-overridden-id"
    37  	switchyOverriddenID = "switchy-overridden-id"
    38  )
    39  
    40  var (
    41  	dashboardTemplateTest = map[string]interface{}{
    42  		"id":   "home-security-alarm-dashboard",
    43  		"name": "Home Security Alarm",
    44  		"widgets": []interface{}{
    45  			map[string]interface{}{
    46  				"type": "Messenger", "name": "message_update",
    47  				"variables": []interface{}{map[string]interface{}{"thing_id": "home-security-alarm", "variable_id": "message_update"}},
    48  			},
    49  			map[string]interface{}{
    50  				"type": "Switch", "name": "light_alarm",
    51  				"variables": []interface{}{map[string]interface{}{"thing_id": "home-security-alarm", "variable_id": "light_alarm"}},
    52  				"options":   map[string]interface{}{"showLabels": true},
    53  			},
    54  		},
    55  	}
    56  
    57  	dashboardDetailed = &iotclient.Dashboardv2{
    58  		Name: "dashboard",
    59  		Widgets: []iotclient.Widget{
    60  			{Name: "Switch-name", Height: 1, HeightMobile: 2, Width: 3, WidthMobile: 4,
    61  				X: 5, XMobile: 6, Y: 7, YMobile: 8, Options: map[string]interface{}{"showLabels": true},
    62  				Type: "Switch",
    63  			},
    64  		},
    65  	}
    66  
    67  	dashboardNoOptions = &iotclient.Dashboardv2{
    68  		Name: "dashboard-no-options",
    69  		Widgets: []iotclient.Widget{
    70  			{Name: "Switch-name", Height: 1, HeightMobile: 2, Width: 3, WidthMobile: 4,
    71  				X: 5, XMobile: 6, Y: 7, YMobile: 8, Options: map[string]interface{}{},
    72  				Type: "Switch",
    73  			},
    74  		},
    75  	}
    76  
    77  	dashboardWithVariable = &iotclient.Dashboardv2{
    78  		Name: "dashboard-with-variable",
    79  		Widgets: []iotclient.Widget{
    80  			{Name: "Switch-name", Height: 1, HeightMobile: 2, Width: 3, WidthMobile: 4,
    81  				X: 5, XMobile: 6, Y: 7, YMobile: 8, Options: map[string]interface{}{"showLabels": true}, Type: "Switch",
    82  				Variables: []string{switchyID},
    83  			},
    84  		},
    85  	}
    86  
    87  	dashboardVariableOverride = &iotclient.Dashboardv2{
    88  		Name: "dashboard-with-variable",
    89  		Widgets: []iotclient.Widget{
    90  			{Name: "Switch-name", Height: 1, HeightMobile: 2, Width: 3, WidthMobile: 4,
    91  				X: 5, XMobile: 6, Y: 7, YMobile: 8, Options: map[string]interface{}{"showLabels": true}, Type: "Switch",
    92  				Variables: []string{switchyOverriddenID},
    93  			},
    94  		},
    95  	}
    96  
    97  	dashboardTwoWidgets = &iotclient.Dashboardv2{
    98  		Name: "dashboard-two-widgets",
    99  		Widgets: []iotclient.Widget{
   100  			{Name: "blink_speed", Height: 7, Width: 8,
   101  				X: 7, Y: 5, Options: map[string]interface{}{"min": float64(0), "max": float64(5000)}, Type: "Slider",
   102  				Variables: []string{blinkSpeedID},
   103  			},
   104  			{Name: "relay_2", Height: 5, Width: 5,
   105  				X: 5, Y: 0, Options: map[string]interface{}{"showLabels": true}, Type: "Switch",
   106  				Variables: []string{relayID},
   107  			},
   108  		},
   109  	}
   110  )
   111  
   112  func TestLoadTemplate(t *testing.T) {
   113  	tests := []struct {
   114  		name     string
   115  		file     string
   116  		override map[string]string
   117  		want     map[string]interface{}
   118  	}{
   119  
   120  		{
   121  			name: "yaml dashboard template",
   122  			file: "testdata/home-security-dashboard.yaml",
   123  			want: dashboardTemplateTest,
   124  		},
   125  
   126  		{
   127  			name: "json dashboard template",
   128  			file: "testdata/home-security-dashboard.json",
   129  			want: dashboardTemplateTest,
   130  		},
   131  	}
   132  
   133  	for _, tt := range tests {
   134  		t.Run(tt.name, func(t *testing.T) {
   135  			var got map[string]interface{}
   136  			err := loadTemplate(tt.file, &got)
   137  			if err != nil {
   138  				t.Errorf("%v", err)
   139  			}
   140  			if !cmp.Equal(got, tt.want) {
   141  				t.Errorf("Wrong template received, diff:\n%s", cmp.Diff(tt.want, got))
   142  			}
   143  		})
   144  	}
   145  }
   146  
   147  type thingShowTest struct{}
   148  
   149  func (t *thingShowTest) ThingShow(ctx context.Context, thingID string) (*iotclient.ArduinoThing, error) {
   150  	if thingID == thingOverriddenID {
   151  		return &iotclient.ArduinoThing{
   152  			Properties: []iotclient.ArduinoProperty{
   153  				{Id: switchyOverriddenID, VariableName: "switchy"},
   154  			},
   155  		}, nil
   156  	}
   157  	return &iotclient.ArduinoThing{
   158  		Properties: []iotclient.ArduinoProperty{
   159  			{Id: switchyID, VariableName: "switchy"},
   160  			{Id: relayID, VariableName: "relay_2"},
   161  			{Id: blinkSpeedID, VariableName: "blink_speed"},
   162  		},
   163  	}, nil
   164  }
   165  
   166  func TestLoadDashboard(t *testing.T) {
   167  	mockThingShow := &thingShowTest{}
   168  	tests := []struct {
   169  		name     string
   170  		file     string
   171  		override map[string]string
   172  		want     *iotclient.Dashboardv2
   173  	}{
   174  		{
   175  			name:     "dashboard detailed",
   176  			file:     "testdata/dashboard-detailed.yaml",
   177  			override: nil,
   178  			want:     dashboardDetailed,
   179  		},
   180  
   181  		{
   182  			name:     "dashboard with wrong options to be filtered out",
   183  			file:     "testdata/dashboard-wrong-options.yaml",
   184  			override: nil,
   185  			want:     dashboardDetailed,
   186  		},
   187  
   188  		{
   189  			name:     "dashboard without options, should have a not nil map",
   190  			file:     "testdata/dashboard-no-options.yaml",
   191  			override: nil,
   192  			want:     dashboardNoOptions,
   193  		},
   194  
   195  		{
   196  			name:     "dashboard with variable, mocked variable id is concatenation of thing_id and variable_id",
   197  			file:     "testdata/dashboard-with-variable.yaml",
   198  			override: nil,
   199  			want:     dashboardWithVariable,
   200  		},
   201  
   202  		{
   203  			name:     "dashboard with variable, thing is overridden",
   204  			file:     "testdata/dashboard-with-variable.yaml",
   205  			override: map[string]string{"thing": thingOverriddenID},
   206  			want:     dashboardVariableOverride,
   207  		},
   208  
   209  		{
   210  			name:     "dashboard with two widgets",
   211  			file:     "testdata/dashboard-two-widgets.yaml",
   212  			override: nil,
   213  			want:     dashboardTwoWidgets,
   214  		},
   215  	}
   216  
   217  	for _, tt := range tests {
   218  		t.Run(tt.name, func(t *testing.T) {
   219  			got, err := LoadDashboard(context.TODO(), tt.file, tt.override, mockThingShow)
   220  			if err != nil {
   221  				t.Errorf("%v", err)
   222  			}
   223  
   224  			for i := range got.Widgets {
   225  				// check widget id generation
   226  				id := got.Widgets[i].Id
   227  				_, err := uuid.FromString(id)
   228  				if err != nil {
   229  					t.Errorf("Widget ID is not a valid UUID: %s", id)
   230  				}
   231  				// Remove generated id to be able to compare the widget with the expected one
   232  				got.Widgets[i].Id = ""
   233  			}
   234  
   235  			if !cmp.Equal(got, tt.want) {
   236  				t.Errorf("Wrong template received, diff:\n%s", cmp.Diff(tt.want, got))
   237  			}
   238  		})
   239  	}
   240  }