github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/tests/plugins/node/node_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 node 20 21 import ( 22 "context" 23 "encoding/json" 24 "fmt" 25 "testing" 26 "time" 27 28 "github.com/e154/smart-home/adaptors" 29 "github.com/e154/smart-home/common/events" 30 "github.com/e154/smart-home/plugins/node" 31 "github.com/e154/smart-home/system/bus" 32 "github.com/e154/smart-home/system/mqtt" 33 "github.com/e154/smart-home/system/scripts" 34 "github.com/e154/smart-home/system/supervisor" 35 "github.com/e154/smart-home/system/zigbee2mqtt" 36 . "github.com/e154/smart-home/tests/plugins" 37 . "github.com/smartystreets/goconvey/convey" 38 ) 39 40 func TestNode(t *testing.T) { 41 42 Convey("node", t, func(ctx C) { 43 _ = container.Invoke(func(adaptors *adaptors.Adaptors, 44 scriptService scripts.ScriptService, 45 supervisor supervisor.Supervisor, 46 zigbee2mqtt zigbee2mqtt.Zigbee2mqtt, 47 mqttServer mqtt.MqttServ, 48 eventBus bus.Bus) { 49 50 // register plugins 51 err := AddPlugin(adaptors, "node") 52 ctx.So(err, ShouldBeNil) 53 54 serviceCh := WaitService(eventBus, time.Second*5, "Supervisor", "Mqtt") 55 pluginsCh := WaitPlugins(eventBus, time.Second*5, "node") 56 go mqttServer.Start() 57 supervisor.Start(context.Background()) 58 defer mqttServer.Shutdown() 59 defer supervisor.Shutdown(context.Background()) 60 So(<-serviceCh, ShouldBeTrue) 61 So(<-pluginsCh, ShouldBeTrue) 62 63 // add entity 64 // ------------------------------------------------ 65 66 nodeEnt := GetNewNode("main") 67 err = adaptors.Entity.Add(context.Background(), nodeEnt) 68 ctx.So(err, ShouldBeNil) 69 70 eventBus.Publish("system/models/entities/"+nodeEnt.Id.String(), events.EventCreatedEntityModel{ 71 EntityId: nodeEnt.Id, 72 }) 73 74 time.Sleep(time.Second) 75 76 // common 77 // ------------------------------------------------ 78 var closed = false 79 ch := make(chan events.EventStateChanged) 80 defer close(ch) 81 fn := func(topic string, msg interface{}) { 82 switch v := msg.(type) { 83 case events.EventStateChanged: 84 if !closed { 85 ch <- v 86 } 87 } 88 } 89 eventBus.Subscribe("system/entities/+", fn, false) 90 defer eventBus.Unsubscribe("system/entities/+", fn) 91 defer func() { 92 closed = true 93 }() 94 // ------------------------------------------------ 95 96 // wait message 97 _, ok := WaitT[events.EventStateChanged](time.Second*2, ch) 98 ctx.So(ok, ShouldBeTrue) 99 100 now := time.Now() 101 102 t.Run("ping", func(t *testing.T) { 103 Convey("case", t, func(ctx C) { 104 105 b, err := json.Marshal(node.MessageStatus{ 106 Status: "enabled", 107 Thread: 1, 108 Rps: 2, 109 Min: 3, 110 Max: 4, 111 StartedAt: now, 112 }) 113 ctx.So(err, ShouldBeNil) 114 err = mqttServer.Publish("system/plugins/node/main/ping", b, 0, false) 115 ctx.So(err, ShouldBeNil) 116 117 // wait message 118 msg, ok := WaitT[events.EventStateChanged](time.Second*2, ch) 119 ctx.So(ok, ShouldBeTrue) 120 121 ctx.So(msg.OldState.State, ShouldNotBeNil) 122 ctx.So(msg.OldState.State.Name, ShouldEqual, "wait") 123 ctx.So(msg.NewState.State, ShouldNotBeNil) 124 ctx.So(msg.NewState.State.Name, ShouldEqual, "connected") 125 ctx.So(msg.NewState.Attributes[node.AttrThread].Int64(), ShouldEqual, 1) 126 ctx.So(msg.NewState.Attributes[node.AttrRps].Int64(), ShouldEqual, 2) 127 ctx.So(msg.NewState.Attributes[node.AttrMin].Int64(), ShouldEqual, 3) 128 ctx.So(msg.NewState.Attributes[node.AttrMax].Int64(), ShouldEqual, 4) 129 ctx.So(msg.NewState.Attributes[node.AttrStartedAt].Time(), ShouldEqual, now) 130 ctx.So(ok, ShouldBeTrue) 131 }) 132 }) 133 134 mqttCli := mqttServer.NewClient("cli") 135 136 t.Run("request", func(t *testing.T) { 137 Convey("case", t, func(ctx C) { 138 ch := make(chan struct{}) 139 defer close(ch) 140 _ = mqttCli.Subscribe("system/plugins/node/main/req/#", func(client mqtt.MqttCli, message mqtt.Message) { 141 req := node.MessageRequest{} 142 err = json.Unmarshal(message.Payload, &req) 143 ctx.So(err, ShouldBeNil) 144 ctx.So(req.EntityId, ShouldEqual, "plugin.test") 145 ctx.So(req.DeviceType, ShouldEqual, "test") 146 ch <- struct{}{} 147 }) 148 defer mqttCli.Unsubscribe("home/node/main/req/#") 149 150 req := node.MessageRequest{ 151 EntityId: "plugin.test", 152 DeviceType: "test", 153 Properties: nil, 154 Command: nil, 155 } 156 eventBus.Publish(fmt.Sprintf("system/plugins/node/main/req/%s", nodeEnt.Id), req) 157 158 // wait message 159 _, ok := WaitT[struct{}](time.Second*2, ch) 160 ctx.So(ok, ShouldBeTrue) 161 162 }) 163 }) 164 165 t.Run("response", func(t *testing.T) { 166 Convey("case", t, func(ctx C) { 167 168 ch := make(chan struct{}) 169 defer close(ch) 170 topic := fmt.Sprintf("system/plugins/node/main/resp/%s", "plugin.test") 171 fn := func(topic string, resp node.MessageResponse) { 172 ctx.So(topic, ShouldEqual, "system/plugins/node/main/resp/plugin.test") 173 ctx.So(resp.EntityId, ShouldEqual, "plugin.test") 174 ctx.So(resp.DeviceType, ShouldEqual, "test") 175 ctx.So(resp.Status, ShouldEqual, "success") 176 ch <- struct{}{} 177 } 178 _ = eventBus.Subscribe(topic, fn) 179 defer func() { _ = eventBus.Unsubscribe(topic, fn) }() 180 181 time.Sleep(time.Millisecond * 500) 182 183 b, err := json.Marshal(node.MessageResponse{ 184 EntityId: "plugin.test", 185 DeviceType: "test", 186 Properties: nil, 187 Response: nil, 188 Status: "success", 189 }) 190 ctx.So(err, ShouldBeNil) 191 192 _ = mqttCli.Publish(fmt.Sprintf("system/plugins/node/main/resp/%s", "plugin.test"), b) 193 194 // wait message 195 _, ok := WaitT[struct{}](time.Second*2, ch) 196 ctx.So(ok, ShouldBeTrue) 197 198 time.Sleep(time.Millisecond * 500) 199 }) 200 }) 201 }) 202 }) 203 }