// Create creates the given Node. func (a *NodeAPI) Create(ctx context.Context, req *pb.CreateNodeRequest) (*pb.CreateNodeResponse, error) { var appEUI, devEUI lorawan.EUI64 var appKey lorawan.AES128Key if err := appEUI.UnmarshalText([]byte(req.AppEUI)); err != nil { return nil, err } if err := devEUI.UnmarshalText([]byte(req.DevEUI)); err != nil { return nil, err } if err := appKey.UnmarshalText([]byte(req.AppKey)); err != nil { return nil, err } node := models.Node{ DevEUI: devEUI, AppEUI: appEUI, AppKey: appKey, RXDelay: uint8(req.RxDelay), RX1DROffset: uint8(req.Rx1DROffset), } if req.ChannelListID > 0 { node.ChannelListID = &req.ChannelListID } if err := storage.CreateNode(a.ctx.DB, node); err != nil { return nil, err } return &pb.CreateNodeResponse{}, nil }
func TestValidateDevNonce(t *testing.T) { Convey("Given a Node", t, func() { n := models.Node{ UsedDevNonces: make([][2]byte, 0), } Convey("Then any given dev-nonce is valid", func() { dn := [2]byte{1, 2} So(n.ValidateDevNonce(dn), ShouldBeTrue) Convey("Then the dev-nonce is added to the used nonces", func() { So(n.UsedDevNonces, ShouldContain, dn) }) }) Convey("Given a Node has 10 used nonces", func() { n.UsedDevNonces = [][2]byte{ {1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 6}, {7, 7}, {8, 8}, {9, 9}, {10, 10}, } Convey("Then an already used nonce is invalid", func() { So(n.ValidateDevNonce([2]byte{1, 1}), ShouldBeFalse) Convey("Then the UsedDevNonces still has length 10", func() { So(n.UsedDevNonces, ShouldHaveLength, 10) }) }) Convey("Then a new nonce is valid", func() { So(n.ValidateDevNonce([2]byte{0, 0}), ShouldBeTrue) Convey("Then the UsedDevNonces still has length 10", func() { So(n.UsedDevNonces, ShouldHaveLength, 10) }) Convey("Then the new nonce was added to the back of the list", func() { So(n.UsedDevNonces[9], ShouldEqual, [2]byte{0, 0}) }) }) }) }) }
func TestNodeMethods(t *testing.T) { conf := common.GetTestConfig() Convey("Given a clean database with application", t, func() { db, err := OpenDatabase(conf.PostgresDSN) So(err, ShouldBeNil) common.MustResetDB(db) app := models.Application{ AppEUI: [8]byte{1, 2, 3, 4, 5, 6, 7, 8}, Name: "test app", } So(CreateApplication(db, app), ShouldBeNil) Convey("When creating a node", func() { node := models.Node{ DevEUI: [8]byte{8, 7, 6, 5, 4, 3, 2, 1}, AppEUI: app.AppEUI, AppKey: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8}, RXDelay: 2, RX1DROffset: 3, } So(CreateNode(db, node), ShouldBeNil) Convey("Whe can get it", func() { node2, err := GetNode(db, node.DevEUI) node2.UsedDevNonces = nil So(err, ShouldBeNil) So(node2, ShouldResemble, node) }) Convey("Then get nodes returns a single item", func() { nodes, err := GetNodes(db, 10, 0) So(err, ShouldBeNil) So(nodes, ShouldHaveLength, 1) nodes[0].UsedDevNonces = nil So(nodes[0], ShouldResemble, node) }) Convey("Then get nodes for AppEUI returns a single item", func() { nodes, err := GetNodesForAppEUI(db, app.AppEUI, 10, 0) So(err, ShouldBeNil) So(nodes, ShouldHaveLength, 1) nodes[0].UsedDevNonces = nil So(nodes[0], ShouldResemble, node) }) Convey("Then get nodes count returns 1", func() { count, err := GetNodesCount(db) So(err, ShouldBeNil) So(count, ShouldEqual, 1) }) Convey("Then get nodes count for AppEUI returns 1", func() { count, err := GetNodesForAppEUICount(db, app.AppEUI) So(err, ShouldBeNil) So(count, ShouldEqual, 1) }) Convey("When updating the node", func() { node.AppKey = [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} So(UpdateNode(db, node), ShouldBeNil) Convey("Then the nodes has been updated", func() { node2, err := GetNode(db, node.DevEUI) So(err, ShouldBeNil) node2.UsedDevNonces = nil So(node2, ShouldResemble, node) }) }) Convey("When deleting the node", func() { So(DeleteNode(db, node.DevEUI), ShouldBeNil) Convey("Then get nodes count returns 0", func() { count, err := GetNodesCount(db) So(err, ShouldBeNil) So(count, ShouldEqual, 0) }) }) }) }) }
func TestNodeAPI(t *testing.T) { conf := getConfig() Convey("Given a clean database and an API instance", t, func() { db, err := OpenDatabase(conf.PostgresDSN) So(err, ShouldBeNil) mustResetDB(db) ctx := Context{ DB: db, } api := NewNodeAPI(ctx) Convey("Given an application is created (fk constraint)", func() { app := models.Application{ AppEUI: [8]byte{1, 2, 3, 4, 5, 6, 7, 8}, Name: "test app", } // we need to create the app since the node has a fk constraint So(createApplication(ctx.DB, app), ShouldBeNil) node := models.Node{ DevEUI: [8]byte{8, 7, 6, 5, 4, 3, 2, 1}, AppEUI: [8]byte{1, 2, 3, 4, 5, 6, 7, 8}, AppKey: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, UsedDevNonces: [][2]byte{}, } Convey("When calling Create", func() { var devEUI lorawan.EUI64 So(api.Create(node, &devEUI), ShouldBeNil) So(devEUI, ShouldEqual, node.DevEUI) Convey("Then the node has been created", func() { var node2 models.Node So(api.Get(node.DevEUI, &node2), ShouldBeNil) So(node2, ShouldResemble, node) Convey("Then the node can ben updated", func() { node.UsedDevNonces = [][2]byte{ {1, 2}, } So(api.Update(node, &devEUI), ShouldBeNil) So(api.Get(node.DevEUI, &node2), ShouldBeNil) So(node2, ShouldResemble, node) }) Convey("Then the node can be deleted", func() { So(api.Delete(node.DevEUI, &devEUI), ShouldBeNil) So(api.Get(node.DevEUI, &node2), ShouldNotBeNil) }) }) Convey("Then the list of nodes has size 1", func() { var nodes []models.Node So(api.GetList(models.GetListRequest{ Limit: 10, Offset: 0, }, &nodes), ShouldBeNil) So(nodes, ShouldHaveLength, 1) }) }) }) }) }
func TestGetCFListForNode(t *testing.T) { conf := getConfig() Convey("Given an application, node (without channel-list) and channel-list with 2 channels", t, func() { db, err := OpenDatabase(conf.PostgresDSN) So(err, ShouldBeNil) mustResetDB(db) ctx := Context{ DB: db, } channelList := models.ChannelList{ Name: "test channels", } So(createChannelList(ctx.DB, &channelList), ShouldBeNil) So(createChannel(ctx.DB, &models.Channel{ ChannelListID: channelList.ID, Channel: 3, Frequency: 868400000, }), ShouldBeNil) So(createChannel(ctx.DB, &models.Channel{ ChannelListID: channelList.ID, Channel: 4, Frequency: 868500000, }), ShouldBeNil) app := models.Application{ AppEUI: [8]byte{1, 2, 3, 4, 5, 6, 7, 8}, Name: "test app", } So(createApplication(ctx.DB, app), ShouldBeNil) node := models.Node{ DevEUI: [8]byte{8, 7, 6, 5, 4, 3, 2, 1}, AppEUI: app.AppEUI, } So(createNode(ctx.DB, node), ShouldBeNil) Convey("Then getCFListForNode returns nil", func() { cFList, err := getCFListForNode(ctx.DB, node) So(err, ShouldBeNil) So(cFList, ShouldBeNil) }) Convey("Given the node has the channel-list configured", func() { node.ChannelListID = &channelList.ID So(updateNode(ctx.DB, node), ShouldBeNil) Convey("Then getCFListForNode returns the CFList with the configured channels", func() { cFList, err := getCFListForNode(ctx.DB, node) So(err, ShouldBeNil) So(cFList, ShouldResemble, &lorawan.CFList{ 868400000, 868500000, 0, 0, 0, }) }) Convey("Given the configured ISM band does not allow a CFList", func() { defer func() { Band.ImplementsCFlist = true }() Band.ImplementsCFlist = false Convey("Then getCFListForNode returns nil", func() { cFList, err := getCFListForNode(ctx.DB, node) So(err, ShouldBeNil) So(cFList, ShouldBeNil) }) }) }) }) }