// NewRedisHandler creates a new Redis-backed Handler func NewRedisHandler(client *redis.Client, ttnBrokerID string) Handler { return &handler{ devices: device.NewRedisDeviceStore(client, "handler"), applications: application.NewRedisApplicationStore(client, "handler"), ttnBrokerID: ttnBrokerID, } }
func TestEnqueueDownlink(t *testing.T) { a := New(t) appID := "app1" devID := "dev1" h := &handler{ Component: &component.Component{Ctx: GetLogger(t, "TestEnqueueDownlink")}, devices: device.NewRedisDeviceStore(GetRedisClient(), "handler-test-enqueue-downlink"), mqttEvent: make(chan *types.DeviceEvent, 10), } err := h.EnqueueDownlink(&types.DownlinkMessage{ AppID: appID, DevID: devID, }) a.So(err, ShouldNotBeNil) h.devices.Set(&device.Device{ AppID: appID, DevID: devID, }) defer func() { h.devices.Delete(appID, devID) }() err = h.EnqueueDownlink(&types.DownlinkMessage{ AppID: appID, DevID: devID, PayloadFields: map[string]interface{}{ "string": "hello!", "int": 42, "bool": true, }, }) a.So(err, ShouldBeNil) dev, _ := h.devices.Get(appID, devID) a.So(dev.NextDownlink, ShouldNotBeEmpty) a.So(dev.NextDownlink.PayloadFields, ShouldHaveLength, 3) }
func TestConvertFromLoRaWAN(t *testing.T) { a := New(t) h := &handler{ devices: device.NewRedisDeviceStore(GetRedisClient(), "handler-test-convert-from-lorawan"), Component: &component.Component{Ctx: GetLogger(t, "TestConvertFromLoRaWAN")}, mqttEvent: make(chan *types.DeviceEvent, 10), } h.devices.Set(&device.Device{ DevID: "devid", AppID: "appid", }) defer func() { h.devices.Delete("appid", "devid") }() ttnUp, appUp := buildLorawanUplink([]byte{0x40, 0x04, 0x03, 0x02, 0x01, 0x20, 0x01, 0x00, 0x0A, 0x46, 0x55, 0x96, 0x42, 0x92, 0xF2}) err := h.ConvertFromLoRaWAN(h.Ctx, ttnUp, appUp) a.So(err, ShouldBeNil) a.So(appUp.PayloadRaw, ShouldResemble, []byte{0xaa, 0xbc}) a.So(appUp.FCnt, ShouldEqual, 1) }
func TestConvertToLoRaWAN(t *testing.T) { a := New(t) h := &handler{ devices: device.NewRedisDeviceStore(GetRedisClient(), "handler-test-convert-to-lorawan"), Component: &component.Component{Ctx: GetLogger(t, "TestConvertToLoRaWAN")}, } h.devices.Set(&device.Device{ DevID: "devid", AppID: "appid", }) defer func() { h.devices.Delete("appid", "devid") }() appDown, ttnDown := buildLorawanDownlink([]byte{0xaa, 0xbc}) err := h.ConvertToLoRaWAN(h.Ctx, appDown, ttnDown) a.So(err, ShouldBeNil) a.So(ttnDown.Payload, ShouldResemble, []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0xa1, 0x33, 0x68, 0x0A, 0x08, 0xBD}) appDown, ttnDown = buildLorawanDownlink([]byte{0xaa, 0xbc}) appDown.FPort = 8 err = h.ConvertToLoRaWAN(h.Ctx, appDown, ttnDown) a.So(err, ShouldBeNil) a.So(ttnDown.Payload, ShouldResemble, []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x00, 0x01, 0x00, 0x08, 0xa1, 0x33, 0x41, 0xA9, 0xFA, 0x03}) }
func TestHandleUplink(t *testing.T) { a := New(t) var err error var wg WaitGroup appEUI := types.AppEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8}) appID := "appid" devEUI := types.DevEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8}) devID := "devid" h := &handler{ Component: &component.Component{Ctx: GetLogger(t, "TestHandleUplink")}, devices: device.NewRedisDeviceStore(GetRedisClient(), "handler-test-handle-uplink"), applications: application.NewRedisApplicationStore(GetRedisClient(), "handler-test-handle-uplink"), } h.InitStatus() dev := &device.Device{ AppID: appID, DevID: devID, AppEUI: appEUI, DevEUI: devEUI, } h.devices.Set(dev) defer func() { h.devices.Delete(appID, devID) }() h.applications.Set(&application.Application{ AppID: appID, }) defer func() { h.applications.Delete(appID) }() h.mqttUp = make(chan *types.UplinkMessage) h.mqttEvent = make(chan *types.DeviceEvent, 10) h.downlink = make(chan *pb_broker.DownlinkMessage) uplink, _ := buildLorawanUplink([]byte{0x40, 0x04, 0x03, 0x02, 0x01, 0x00, 0x01, 0x00, 0x0A, 0x4D, 0xDA, 0x23, 0x99, 0x61, 0xD4}) downlinkEmpty := []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x21, 0xEA, 0x8B, 0x0E} downlinkACK := []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x20, 0x00, 0x00, 0x0A, 0x3B, 0x3F, 0x77, 0x0B} downlinkMAC := []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x05, 0x00, 0x00, 0x03, 0x30, 0x00, 0x00, 0x00, 0x0A, 0x4D, 0x11, 0x55, 0x01} expected := []byte{0x60, 0x04, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x66, 0xE6, 0x1D, 0x49, 0x82, 0x84} downlink := &pb_broker.DownlinkMessage{ DownlinkOption: &pb_broker.DownlinkOption{ ProtocolConfig: &pb_protocol.TxConfiguration{Protocol: &pb_protocol.TxConfiguration_Lorawan{Lorawan: &pb_lorawan.TxConfiguration{ FCnt: 0, }}}, }, } // Test Uplink, no downlink option available wg.Add(1) go func() { <-h.mqttUp wg.Done() }() err = h.HandleUplink(uplink) a.So(err, ShouldBeNil) wg.WaitFor(50 * time.Millisecond) uplink.ResponseTemplate = downlink // Test Uplink, no downlink needed wg.Add(1) go func() { <-h.mqttUp wg.Done() }() downlink.Payload = downlinkEmpty err = h.HandleUplink(uplink) a.So(err, ShouldBeNil) wg.WaitFor(50 * time.Millisecond) // Test Uplink, ACK downlink needed wg.Add(2) go func() { <-h.mqttUp wg.Done() }() go func() { <-h.downlink wg.Done() }() downlink.Payload = downlinkACK err = h.HandleUplink(uplink) a.So(err, ShouldBeNil) wg.WaitFor(50 * time.Millisecond) // Test Uplink, MAC downlink needed wg.Add(2) go func() { <-h.mqttUp wg.Done() }() go func() { <-h.downlink wg.Done() }() downlink.Payload = downlinkMAC err = h.HandleUplink(uplink) a.So(err, ShouldBeNil) wg.WaitFor(50 * time.Millisecond) dev.StartUpdate() dev.NextDownlink = &types.DownlinkMessage{ PayloadRaw: []byte{0xaa, 0xbc}, } // Test Uplink, Data downlink needed h.devices.Set(dev) wg.Add(2) go func() { <-h.mqttUp wg.Done() }() go func() { dl := <-h.downlink a.So(dl.Payload, ShouldResemble, expected) wg.Done() }() downlink.Payload = downlinkEmpty err = h.HandleUplink(uplink) a.So(err, ShouldBeNil) wg.WaitFor(50 * time.Millisecond) dev, _ = h.devices.Get(appID, devID) a.So(dev.NextDownlink, ShouldBeNil) }
func TestHandleMQTT(t *testing.T) { host := os.Getenv("MQTT_ADDRESS") if host == "" { host = "localhost:1883" } a := New(t) var wg WaitGroup c := mqtt.NewClient(apex.Wrap(GetLogger(t, "TestHandleMQTT")), "test", "", "", fmt.Sprintf("tcp://%s", host)) err := c.Connect() a.So(err, ShouldBeNil) appID := "handler-mqtt-app1" devID := "handler-mqtt-dev1" h := &handler{ Component: &component.Component{Ctx: GetLogger(t, "TestHandleMQTT")}, devices: device.NewRedisDeviceStore(GetRedisClient(), "handler-test-handle-mqtt"), } h.devices.Set(&device.Device{ AppID: appID, DevID: devID, }) defer func() { h.devices.Delete(appID, devID) }() err = h.HandleMQTT("", "", fmt.Sprintf("tcp://%s", host)) a.So(err, ShouldBeNil) c.PublishDownlink(types.DownlinkMessage{ AppID: appID, DevID: devID, PayloadRaw: []byte{0xAA, 0xBC}, }).Wait() <-time.After(50 * time.Millisecond) dev, _ := h.devices.Get(appID, devID) a.So(dev.NextDownlink, ShouldNotBeNil) wg.Add(1) c.SubscribeDeviceUplink(appID, devID, func(client mqtt.Client, r_appID string, r_devID string, req types.UplinkMessage) { a.So(r_appID, ShouldEqual, appID) a.So(r_devID, ShouldEqual, devID) a.So(req.PayloadRaw, ShouldResemble, []byte{0xAA, 0xBC}) wg.Done() }).Wait() h.mqttUp <- &types.UplinkMessage{ DevID: devID, AppID: appID, PayloadRaw: []byte{0xAA, 0xBC}, PayloadFields: map[string]interface{}{ "field": "value", }, } wg.Add(1) c.SubscribeDeviceActivations(appID, devID, func(client mqtt.Client, r_appID string, r_devID string, req types.Activation) { a.So(r_appID, ShouldEqual, appID) a.So(r_devID, ShouldEqual, devID) wg.Done() }).Wait() h.mqttEvent <- &types.DeviceEvent{ DevID: devID, AppID: appID, Event: types.ActivationEvent, } a.So(wg.WaitFor(200*time.Millisecond), ShouldBeNil) }
func TestHandleActivation(t *testing.T) { a := New(t) h := &handler{ Component: &component.Component{Ctx: GetLogger(t, "TestHandleActivation")}, applications: application.NewRedisApplicationStore(GetRedisClient(), "handler-test-activation"), devices: device.NewRedisDeviceStore(GetRedisClient(), "handler-test-activation"), } h.InitStatus() h.mqttEvent = make(chan *types.DeviceEvent, 10) var wg WaitGroup appEUI := types.AppEUI{1, 2, 3, 4, 5, 6, 7, 8} appID := appEUI.String() devEUI := types.DevEUI{1, 2, 3, 4, 5, 6, 7, 8} devID := devEUI.String() unknownDevEUI := types.DevEUI{8, 7, 6, 5, 4, 3, 2, 1} appKey := [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} h.applications.Set(&application.Application{ AppID: appID, }) defer func() { h.applications.Delete(appID) }() h.devices.Set(&device.Device{ AppID: appID, DevID: devID, AppEUI: appEUI, DevEUI: devEUI, AppKey: appKey, }) defer func() { h.devices.Delete(appID, devID) }() // Unknown res, err := doTestHandleActivation(h, appEUI, unknownDevEUI, [2]byte{1, 2}, appKey, ) a.So(err, ShouldNotBeNil) a.So(res, ShouldBeNil) // Wrong AppKey res, err = doTestHandleActivation(h, appEUI, devEUI, [2]byte{1, 2}, [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ) a.So(err, ShouldNotBeNil) a.So(res, ShouldBeNil) wg.Add(1) go func() { <-h.mqttEvent wg.Done() }() // Known res, err = doTestHandleActivation(h, appEUI, devEUI, [2]byte{1, 2}, appKey, ) a.So(err, ShouldBeNil) a.So(res, ShouldNotBeNil) wg.WaitFor(50 * time.Millisecond) // Same DevNonce used twice res, err = doTestHandleActivation(h, appEUI, devEUI, [2]byte{1, 2}, appKey, ) a.So(err, ShouldNotBeNil) a.So(res, ShouldBeNil) wg.Add(1) go func() { <-h.mqttEvent wg.Done() }() // Other DevNonce res, err = doTestHandleActivation(h, appEUI, devEUI, [2]byte{2, 1}, appKey, ) a.So(err, ShouldBeNil) a.So(res, ShouldNotBeNil) wg.WaitFor(50 * time.Millisecond) // TODO: Validate response // TODO: Check DB }
func TestHandleDownlink(t *testing.T) { a := New(t) var err error var wg WaitGroup appID := "app2" devID := "dev2" appEUI := types.AppEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8}) devEUI := types.DevEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8}) h := &handler{ Component: &component.Component{Ctx: GetLogger(t, "TestHandleDownlink")}, devices: device.NewRedisDeviceStore(GetRedisClient(), "handler-test-handle-downlink"), applications: application.NewRedisApplicationStore(GetRedisClient(), "handler-test-enqueue-downlink"), downlink: make(chan *pb_broker.DownlinkMessage), mqttEvent: make(chan *types.DeviceEvent, 10), } h.InitStatus() // Neither payload nor Fields provided : ERROR err = h.HandleDownlink(&types.DownlinkMessage{ AppID: appID, DevID: devID, }, &pb_broker.DownlinkMessage{ AppEui: &appEUI, DevEui: &devEUI, }) a.So(err, ShouldNotBeNil) h.devices.Set(&device.Device{ AppID: appID, DevID: devID, }) defer func() { h.devices.Delete(appID, devID) }() err = h.HandleDownlink(&types.DownlinkMessage{ AppID: appID, DevID: devID, }, &pb_broker.DownlinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0}, }) a.So(err, ShouldBeNil) // Payload provided wg.Add(1) go func() { dl := <-h.downlink a.So(dl.Payload, ShouldNotBeEmpty) wg.Done() }() err = h.HandleDownlink(&types.DownlinkMessage{ AppID: appID, DevID: devID, PayloadRaw: []byte{0xAA, 0xBC}, }, &pb_broker.DownlinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0}, DownlinkOption: &pb_broker.DownlinkOption{}, }) a.So(err, ShouldBeNil) wg.WaitFor(100 * time.Millisecond) // Both Payload and Fields provided h.applications.Set(&application.Application{ AppID: appID, Encoder: `function Encoder (payload){ return [96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0] }`, }) defer func() { h.applications.Delete(appID) }() jsonFields := map[string]interface{}{"temperature": 11} err = h.HandleDownlink(&types.DownlinkMessage{ FPort: 1, AppID: appID, DevID: devID, PayloadFields: jsonFields, PayloadRaw: []byte{0xAA, 0xBC}, }, &pb_broker.DownlinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0}, }) a.So(err, ShouldNotBeNil) // JSON Fields provided wg.Add(1) go func() { dl := <-h.downlink a.So(dl.Payload, ShouldNotBeEmpty) wg.Done() }() err = h.HandleDownlink(&types.DownlinkMessage{ FPort: 1, AppID: appID, DevID: devID, PayloadFields: jsonFields, }, &pb_broker.DownlinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0}, DownlinkOption: &pb_broker.DownlinkOption{}, }) a.So(err, ShouldBeNil) wg.WaitFor(100 * time.Millisecond) }