github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/tests/models/entity_test.go (about)

     1  // This file is part of the Smart Home
     2  // Program complex distribution https://github.com/e154/smart-home
     3  // Copyright (C) 2016-2023, Filippov Alex
     4  //
     5  // This library is free software: you can redistribute it and/or
     6  // modify it under the terms of the GNU Lesser General Public
     7  // License as published by the Free Software Foundation; either
     8  // version 3 of the License, or (at your option) any later version.
     9  //
    10  // This library 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 GNU
    13  // Library General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public
    16  // License along with this library.  If not, see
    17  // <https://www.gnu.org/licenses/>.
    18  
    19  package models
    20  
    21  import (
    22  	"context"
    23  	"testing"
    24  
    25  	"github.com/e154/smart-home/adaptors"
    26  	"github.com/e154/smart-home/common"
    27  	m "github.com/e154/smart-home/models"
    28  	"github.com/e154/smart-home/system/access_list"
    29  	"github.com/e154/smart-home/system/migrations"
    30  	. "github.com/smartystreets/goconvey/convey"
    31  )
    32  
    33  func TestEntity(t *testing.T) {
    34  	Convey("entity", t, func(ctx C) {
    35  		err := container.Invoke(func(adaptors *adaptors.Adaptors,
    36  			migrations *migrations.Migrations,
    37  			accessList access_list.AccessListService) {
    38  
    39  			// clear database
    40  			_ = migrations.Purge()
    41  
    42  			// add scripts
    43  			script1 := &m.Script{
    44  				Lang:   common.ScriptLangCoffee,
    45  				Name:   "script1",
    46  				Source: "print 'OK'",
    47  			}
    48  			script2 := &m.Script{
    49  				Lang:   common.ScriptLangCoffee,
    50  				Name:   "script2",
    51  				Source: "print 'OK'",
    52  			}
    53  			var err error
    54  			script1.Id, err = adaptors.Script.Add(context.Background(), script1)
    55  			So(err, ShouldBeNil)
    56  			script2.Id, err = adaptors.Script.Add(context.Background(), script2)
    57  			So(err, ShouldBeNil)
    58  
    59  			// plugins
    60  			err = AddPlugin(adaptors, "sensor")
    61  			So(err, ShouldBeNil)
    62  
    63  			// add image
    64  			image1 := &m.Image{
    65  				Url:  "foo",
    66  				Name: "foo",
    67  			}
    68  			image2 := &m.Image{
    69  				Url:  "bar",
    70  				Name: "bar",
    71  			}
    72  			image1.Id, err = adaptors.Image.Add(context.Background(), image1)
    73  			So(err, ShouldBeNil)
    74  			image2.Id, err = adaptors.Image.Add(context.Background(), image2)
    75  			So(err, ShouldBeNil)
    76  
    77  			t.Run("Create", func(t *testing.T) {
    78  				Convey("", t, func(ctx C) {
    79  					// entity
    80  					entity := &m.Entity{
    81  						Id:         common.EntityId("sensor.entity1"),
    82  						PluginName: "sensor",
    83  						AutoLoad:   true,
    84  						ImageId:    common.Int64(image1.Id),
    85  						Scripts: []*m.Script{
    86  							script1,
    87  						},
    88  						Actions: []*m.EntityAction{
    89  							{
    90  								Name:     "ACTION1",
    91  								ScriptId: common.Int64(script1.Id),
    92  								ImageId:  common.Int64(image2.Id),
    93  							},
    94  						},
    95  						States: []*m.EntityState{
    96  							{
    97  								Name: "STATE1",
    98  							},
    99  						},
   100  						Metrics: []*m.Metric{
   101  							{
   102  								Name:        "bar",
   103  								Description: "bar",
   104  								Options: m.MetricOptions{
   105  									Items: []m.MetricOptionsItem{
   106  										{
   107  											Name:        "foo",
   108  											Description: "foo",
   109  											Color:       "foo",
   110  											Translate:   "foo",
   111  											Label:       "foo",
   112  										},
   113  									},
   114  								},
   115  							},
   116  						},
   117  						Attributes: NetAttr(),
   118  						Settings:   NetSettings(),
   119  						Tags: []*m.Tag{
   120  							{Name: "foo"},
   121  						},
   122  					}
   123  					err = adaptors.Entity.Add(context.Background(), entity)
   124  					So(err, ShouldBeNil)
   125  
   126  					entity, err = adaptors.Entity.GetById(context.Background(), entity.Id)
   127  					So(err, ShouldBeNil)
   128  
   129  					So(entity.AutoLoad, ShouldBeTrue)
   130  					So(len(entity.Actions), ShouldEqual, 1)
   131  					So(entity.Actions[0].Name, ShouldEqual, "ACTION1")
   132  					So(entity.Actions[0].Image, ShouldNotBeNil)
   133  					So(entity.Actions[0].Image.Name, ShouldEqual, "bar")
   134  					So(entity.Image, ShouldNotBeNil)
   135  					So(entity.Image.Name, ShouldEqual, "foo")
   136  					So(len(entity.States), ShouldEqual, 1)
   137  					So(entity.States[0].Name, ShouldEqual, "STATE1")
   138  					So(len(entity.Scripts), ShouldEqual, 1)
   139  					So(entity.Scripts[0].Name, ShouldEqual, "script1")
   140  					So(len(entity.Metrics), ShouldEqual, 1)
   141  					So(len(entity.Tags), ShouldEqual, 1)
   142  					So(entity.Tags[0].Name, ShouldEqual, "foo")
   143  					So(entity.Metrics[0].Name, ShouldEqual, "bar")
   144  					So(entity.Metrics[0].Options.Items[0].Name, ShouldEqual, "foo")
   145  					So(entity.Settings, ShouldNotBeEmpty)
   146  					So(entity.Settings["s"].String(), ShouldEqual, "s")
   147  					So(entity.Attributes, ShouldNotBeEmpty)
   148  					So(entity.Attributes["s"].Name, ShouldEqual, "s")
   149  					So(entity.Attributes["s"].String(), ShouldEqual, "")
   150  				})
   151  			})
   152  
   153  			t.Run("Update", func(t *testing.T) {
   154  				Convey("", t, func(ctx C) {
   155  					// entity
   156  					entity := &m.Entity{
   157  						Id:         common.EntityId("sensor.entity1"),
   158  						AutoLoad:   true,
   159  						PluginName: "sensor",
   160  						Scripts: []*m.Script{
   161  							script2,
   162  						},
   163  						Actions: []*m.EntityAction{
   164  							{
   165  								Name:     "ACTION2",
   166  								ScriptId: common.Int64(script1.Id),
   167  							},
   168  						},
   169  						States: []*m.EntityState{
   170  							{
   171  								Name: "STATE2",
   172  							},
   173  						},
   174  						Metrics: []*m.Metric{
   175  							{
   176  								Name:        "bar2",
   177  								Description: "bar2",
   178  								Options: m.MetricOptions{
   179  									Items: []m.MetricOptionsItem{
   180  										{
   181  											Name:        "foo2",
   182  											Description: "foo2",
   183  											Color:       "foo2",
   184  											Translate:   "foo2",
   185  											Label:       "foo2",
   186  										},
   187  									},
   188  								},
   189  							},
   190  						},
   191  						Tags: []*m.Tag{
   192  							{Name: "bar"},
   193  						},
   194  					}
   195  					err = adaptors.Entity.Update(context.Background(), entity)
   196  					So(err, ShouldBeNil)
   197  
   198  					entity, err = adaptors.Entity.GetById(context.Background(), entity.Id)
   199  					So(err, ShouldBeNil)
   200  
   201  					So(len(entity.Actions), ShouldEqual, 1)
   202  					So(entity.Actions[0].Name, ShouldEqual, "ACTION2")
   203  					So(len(entity.States), ShouldEqual, 1)
   204  					So(entity.States[0].Name, ShouldEqual, "STATE2")
   205  					So(len(entity.Scripts), ShouldEqual, 1)
   206  					So(entity.Scripts[0].Name, ShouldEqual, "script2")
   207  					So(len(entity.Tags), ShouldEqual, 1)
   208  					So(entity.Tags[0].Name, ShouldEqual, "bar")
   209  					So(len(entity.Metrics), ShouldEqual, 1)
   210  					So(entity.Metrics[0].Name, ShouldEqual, "bar2")
   211  					So(entity.Metrics[0].Options.Items[0].Name, ShouldEqual, "foo2")
   212  
   213  					// v2
   214  					entity.Actions = []*m.EntityAction{}
   215  					entity.Tags = nil
   216  
   217  					err = adaptors.Entity.Update(context.Background(), entity)
   218  					So(err, ShouldBeNil)
   219  
   220  					entity, err = adaptors.Entity.GetById(context.Background(), entity.Id)
   221  					So(err, ShouldBeNil)
   222  
   223  					So(len(entity.Actions), ShouldEqual, 0)
   224  					So(len(entity.States), ShouldEqual, 1)
   225  					So(entity.States[0].Name, ShouldEqual, "STATE2")
   226  					So(len(entity.Scripts), ShouldEqual, 1)
   227  					So(entity.Scripts[0].Name, ShouldEqual, "script2")
   228  					So(len(entity.Tags), ShouldEqual, 0)
   229  					So(len(entity.Metrics), ShouldEqual, 1)
   230  					So(entity.Metrics[0].Name, ShouldEqual, "bar2")
   231  					So(entity.Metrics[0].Options.Items[0].Name, ShouldEqual, "foo2")
   232  
   233  					// v3
   234  					entity.Actions = []*m.EntityAction{
   235  						{
   236  							Name:     "ACTION2",
   237  							ScriptId: common.Int64(script1.Id),
   238  						},
   239  					}
   240  					entity.States = []*m.EntityState{}
   241  					err = adaptors.Entity.Update(context.Background(), entity)
   242  					So(err, ShouldBeNil)
   243  
   244  					entity, err = adaptors.Entity.GetById(context.Background(), entity.Id)
   245  					So(err, ShouldBeNil)
   246  
   247  					So(len(entity.Actions), ShouldEqual, 1)
   248  					So(entity.Actions[0].Name, ShouldEqual, "ACTION2")
   249  					So(len(entity.States), ShouldEqual, 0)
   250  					So(len(entity.Scripts), ShouldEqual, 1)
   251  					So(entity.Scripts[0].Name, ShouldEqual, "script2")
   252  					So(len(entity.Metrics), ShouldEqual, 1)
   253  					So(entity.Metrics[0].Name, ShouldEqual, "bar2")
   254  					So(entity.Metrics[0].Options.Items[0].Name, ShouldEqual, "foo2")
   255  
   256  					// v4
   257  					entity.Actions = []*m.EntityAction{}
   258  					entity.States = []*m.EntityState{}
   259  					entity.Scripts = []*m.Script{
   260  						script1,
   261  						script2,
   262  					}
   263  					err = adaptors.Entity.Update(context.Background(), entity)
   264  					So(err, ShouldBeNil)
   265  
   266  					entity, err = adaptors.Entity.GetById(context.Background(), entity.Id)
   267  					So(err, ShouldBeNil)
   268  
   269  					So(len(entity.Actions), ShouldEqual, 0)
   270  					So(len(entity.States), ShouldEqual, 0)
   271  					So(len(entity.Scripts), ShouldEqual, 2)
   272  					So(len(entity.Metrics), ShouldEqual, 1)
   273  					So(entity.Metrics[0].Name, ShouldEqual, "bar2")
   274  					So(entity.Metrics[0].Options.Items[0].Name, ShouldEqual, "foo2")
   275  
   276  					// v5
   277  					entity.Actions = []*m.EntityAction{}
   278  					entity.States = []*m.EntityState{}
   279  					entity.Scripts = []*m.Script{}
   280  					entity.Metrics = []*m.Metric{}
   281  
   282  					err = adaptors.Entity.Update(context.Background(), entity)
   283  					So(err, ShouldBeNil)
   284  
   285  					entity, err = adaptors.Entity.GetById(context.Background(), entity.Id)
   286  					So(err, ShouldBeNil)
   287  
   288  					So(len(entity.Actions), ShouldEqual, 0)
   289  					So(len(entity.States), ShouldEqual, 0)
   290  					So(len(entity.Scripts), ShouldEqual, 0)
   291  					So(len(entity.Metrics), ShouldEqual, 0)
   292  
   293  					// v5
   294  					entity.Actions = []*m.EntityAction{}
   295  					entity.States = []*m.EntityState{}
   296  					entity.Scripts = []*m.Script{}
   297  					entity.Metrics = []*m.Metric{
   298  						{
   299  							Name:        "bar4",
   300  							Description: "bar4",
   301  							Options: m.MetricOptions{
   302  								Items: []m.MetricOptionsItem{
   303  									{
   304  										Name:        "foo4",
   305  										Description: "foo4",
   306  										Color:       "foo4",
   307  										Translate:   "foo4",
   308  										Label:       "foo4",
   309  									},
   310  								},
   311  							},
   312  						},
   313  					}
   314  
   315  					err = adaptors.Entity.Update(context.Background(), entity)
   316  					So(err, ShouldBeNil)
   317  
   318  					entity, err = adaptors.Entity.GetById(context.Background(), entity.Id)
   319  					So(err, ShouldBeNil)
   320  
   321  					So(len(entity.Actions), ShouldEqual, 0)
   322  					So(len(entity.States), ShouldEqual, 0)
   323  					So(len(entity.Scripts), ShouldEqual, 0)
   324  					So(len(entity.Metrics), ShouldEqual, 1)
   325  					So(entity.Metrics[0].Name, ShouldEqual, "bar4")
   326  					So(entity.Metrics[0].Options.Items[0].Name, ShouldEqual, "foo4")
   327  				})
   328  			})
   329  
   330  			t.Run("Import", func(t *testing.T) {
   331  				Convey("", t, func(ctx C) {
   332  					// entity
   333  					entity := &m.Entity{
   334  						Id:         common.EntityId("sensor.entity2"),
   335  						PluginName: "sensor",
   336  						Scripts: []*m.Script{
   337  							{
   338  								Id:     456,
   339  								Lang:   common.ScriptLangCoffee,
   340  								Name:   "script3",
   341  								Source: "print 'OK'",
   342  							},
   343  							{
   344  								Id:     789,
   345  								Lang:   common.ScriptLangCoffee,
   346  								Name:   "script2",
   347  								Source: "print 'OK'",
   348  							},
   349  						},
   350  						Actions: []*m.EntityAction{
   351  							{
   352  								Id:          123,
   353  								Name:        "ACTION3",
   354  								Description: "ACTION3",
   355  								Script: &m.Script{
   356  									Id:     456,
   357  									Lang:   common.ScriptLangCoffee,
   358  									Name:   "script3",
   359  									Source: "print 'OK'",
   360  								},
   361  							},
   362  						},
   363  						States: []*m.EntityState{
   364  							{
   365  								Id:          123,
   366  								Description: "STATE3",
   367  								Name:        "STATE3",
   368  							},
   369  						},
   370  						Metrics: []*m.Metric{
   371  							{
   372  								Name:        "bar3",
   373  								Description: "bar3",
   374  								Options: m.MetricOptions{
   375  									Items: []m.MetricOptionsItem{
   376  										{
   377  											Name:        "foo3",
   378  											Description: "foo3",
   379  											Color:       "foo3",
   380  											Translate:   "foo3",
   381  											Label:       "foo3",
   382  										},
   383  									},
   384  								},
   385  							},
   386  						},
   387  						Tags: []*m.Tag{
   388  							{Name: "foo"},
   389  							{Name: "bar"},
   390  						},
   391  					}
   392  					err = adaptors.Entity.Import(context.Background(), entity)
   393  					So(err, ShouldBeNil)
   394  
   395  					entity, err = adaptors.Entity.GetById(context.Background(), entity.Id)
   396  					So(err, ShouldBeNil)
   397  
   398  					So(len(entity.Actions), ShouldEqual, 1)
   399  					So(entity.Actions[0].Name, ShouldEqual, "ACTION3")
   400  					So(len(entity.States), ShouldEqual, 1)
   401  					So(entity.States[0].Name, ShouldEqual, "STATE3")
   402  					So(len(entity.Scripts), ShouldEqual, 2)
   403  					So(entity.Scripts[0].Name, ShouldEqual, "script2")
   404  					So(entity.Scripts[1].Name, ShouldEqual, "script3")
   405  					So(len(entity.Tags), ShouldEqual, 2)
   406  					So(entity.Tags[0].Name, ShouldEqual, "foo")
   407  					So(entity.Tags[1].Name, ShouldEqual, "bar")
   408  					So(len(entity.Metrics), ShouldEqual, 1)
   409  					So(entity.Metrics[0].Name, ShouldEqual, "bar3")
   410  					So(entity.Metrics[0].Options.Items[0].Name, ShouldEqual, "foo3")
   411  
   412  					entity3 := &m.Entity{
   413  						Id:         common.EntityId("sensor.entity3"),
   414  						PluginName: "sensor",
   415  						Scripts:    []*m.Script{},
   416  						Actions:    []*m.EntityAction{},
   417  						States:     []*m.EntityState{},
   418  						Metrics:    []*m.Metric{},
   419  						Attributes: NetAttr(),
   420  						Settings:   NetSettings(),
   421  					}
   422  					err = adaptors.Entity.Import(context.Background(), entity3)
   423  					So(err, ShouldBeNil)
   424  
   425  					entity, err = adaptors.Entity.GetById(context.Background(), entity3.Id)
   426  					So(err, ShouldBeNil)
   427  
   428  					So(len(entity.Actions), ShouldEqual, 0)
   429  					So(len(entity.States), ShouldEqual, 0)
   430  					So(len(entity.Scripts), ShouldEqual, 0)
   431  					So(len(entity.Metrics), ShouldEqual, 0)
   432  					So(len(entity.Tags), ShouldEqual, 0)
   433  					So(entity.Settings, ShouldNotBeEmpty)
   434  					So(entity.Settings["s"].String(), ShouldEqual, "s")
   435  					So(entity.Attributes, ShouldNotBeEmpty)
   436  					So(entity.Attributes["s"].Name, ShouldEqual, "s")
   437  					So(entity.Attributes["s"].String(), ShouldEqual, "")
   438  
   439  					err = adaptors.Entity.Import(context.Background(), entity3)
   440  					So(err, ShouldNotBeNil)
   441  				})
   442  			})
   443  
   444  			t.Run("Delete", func(t *testing.T) {
   445  				Convey("", t, func(ctx C) {
   446  					err = adaptors.Entity.Delete(context.Background(), "sensor.entity2")
   447  					So(err, ShouldBeNil)
   448  
   449  					err = adaptors.Entity.Delete(context.Background(), "sensor.entity2")
   450  					So(err, ShouldBeNil)
   451  
   452  				})
   453  			})
   454  
   455  			t.Run("List", func(t *testing.T) {
   456  				Convey("", t, func(ctx C) {
   457  					list, total, err := adaptors.Entity.List(context.Background(), 5, 0, "desc", "id", false, nil, nil, nil)
   458  					So(err, ShouldBeNil)
   459  					So(total, ShouldEqual, 2)
   460  					So(len(list), ShouldEqual, 2)
   461  				})
   462  			})
   463  
   464  			t.Run("Search", func(t *testing.T) {
   465  				Convey("", t, func(ctx C) {
   466  
   467  					list, total, err := adaptors.Entity.Search(context.Background(), "entity23", 5, 0)
   468  					So(err, ShouldBeNil)
   469  					So(total, ShouldEqual, 0)
   470  					So(len(list), ShouldEqual, 0)
   471  
   472  					list, total, err = adaptors.Entity.Search(context.Background(), "entity", 5, 0)
   473  					So(err, ShouldBeNil)
   474  					So(total, ShouldEqual, 2)
   475  					So(len(list), ShouldEqual, 2)
   476  
   477  					list, total, err = adaptors.Entity.Search(context.Background(), "entity1", 5, 0)
   478  					So(err, ShouldBeNil)
   479  					So(total, ShouldEqual, 1)
   480  					So(len(list), ShouldEqual, 1)
   481  				})
   482  			})
   483  
   484  			t.Run("GetById", func(t *testing.T) {
   485  				Convey("", t, func(ctx C) {
   486  
   487  					_, err := adaptors.Entity.GetById(context.Background(), "sensor.entity23", false)
   488  					So(err, ShouldNotBeNil)
   489  
   490  					entity, err := adaptors.Entity.GetById(context.Background(), "sensor.entity1", false)
   491  					So(err, ShouldBeNil)
   492  					So(entity.Id, ShouldEqual, "sensor.entity1")
   493  				})
   494  			})
   495  
   496  			t.Run("GetByIds", func(t *testing.T) {
   497  				Convey("", t, func(ctx C) {
   498  
   499  					list, err := adaptors.Entity.GetByIds(context.Background(), []common.EntityId{"sensor.entity23"}, false)
   500  					So(err, ShouldBeNil)
   501  					So(len(list), ShouldEqual, 0)
   502  
   503  					list, err = adaptors.Entity.GetByIds(context.Background(), []common.EntityId{"sensor.entity1"}, false)
   504  					So(err, ShouldBeNil)
   505  					So(list[0].Id, ShouldEqual, "sensor.entity1")
   506  				})
   507  			})
   508  
   509  			t.Run("GetByType", func(t *testing.T) {
   510  				Convey("", t, func(ctx C) {
   511  
   512  					list, err := adaptors.Entity.GetByType(context.Background(), "foo", 5, 0)
   513  					So(err, ShouldBeNil)
   514  					So(len(list), ShouldEqual, 0)
   515  
   516  					list, err = adaptors.Entity.GetByType(context.Background(), "sensor", 5, 0)
   517  					So(err, ShouldBeNil)
   518  					So(len(list), ShouldEqual, 1)
   519  				})
   520  			})
   521  
   522  			t.Run("UpdateAutoload", func(t *testing.T) {
   523  				Convey("", t, func(ctx C) {
   524  
   525  					err := adaptors.Entity.UpdateAutoload(context.Background(), "sensor.entity1", false)
   526  					So(err, ShouldBeNil)
   527  
   528  					entity, err := adaptors.Entity.GetById(context.Background(), "sensor.entity1", false)
   529  					So(err, ShouldBeNil)
   530  					So(entity.AutoLoad, ShouldEqual, false)
   531  
   532  					err = adaptors.Entity.UpdateAutoload(context.Background(), "sensor.entity1", true)
   533  					So(err, ShouldBeNil)
   534  
   535  					entity, err = adaptors.Entity.GetById(context.Background(), "sensor.entity1", false)
   536  					So(err, ShouldBeNil)
   537  					So(entity.AutoLoad, ShouldEqual, true)
   538  				})
   539  			})
   540  
   541  		})
   542  		So(err, ShouldBeNil)
   543  	})
   544  }