// Create creates the given application.
func (a *ApplicationAPI) Create(ctx context.Context, req *pb.CreateApplicationRequest) (*pb.CreateApplicationResponse, error) {
	var eui lorawan.EUI64
	if err := eui.UnmarshalText([]byte(req.AppEUI)); err != nil {
		return nil, err
	}

	if err := storage.CreateApplication(a.ctx.DB, models.Application{AppEUI: eui, Name: req.Name}); err != nil {
		return nil, err
	}

	return &pb.CreateApplicationResponse{}, nil
}
func TestNodeSessionAPI(t *testing.T) {
	conf := common.GetTestConfig()

	Convey("Given a clean database and api instance", t, func() {
		db, err := storage.OpenDatabase(conf.PostgresDSN)
		So(err, ShouldBeNil)
		common.MustResetDB(db)

		p := storage.NewRedisPool(conf.RedisURL)
		common.MustFlushRedis(p)

		lsCtx := loraserver.Context{DB: db, RedisPool: p, NetID: [3]byte{1, 2, 3}}
		ctx := context.Background()
		api := NewNodeSessionAPI(lsCtx)

		Convey("Given an application and node are created (fk constraints)", func() {
			app := models.Application{
				AppEUI: [8]byte{1, 2, 3, 4, 5, 6, 7, 8},
				Name:   "test app",
			}
			So(storage.CreateApplication(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{},
			}
			So(storage.CreateNode(db, node), ShouldBeNil)

			Convey("When creating a node-session", func() {
				_, err := api.Create(ctx, &pb.CreateNodeSessionRequest{
					DevAddr:     "06020304",
					DevEUI:      node.DevEUI.String(),
					AppEUI:      node.AppEUI.String(),
					AppSKey:     node.AppKey.String(),
					NwkSKey:     node.AppKey.String(),
					FCntUp:      10,
					FCntDown:    11,
					RxDelay:     1,
					Rx1DROffset: 2,
					CFList: []uint32{
						868700000,
					},
				})
				So(err, ShouldBeNil)

				Convey("Then it can be retrieved by DevAddr", func() {
					resp, err := api.Get(ctx, &pb.GetNodeSessionRequest{DevAddr: "06020304"})
					So(err, ShouldBeNil)
					So(resp, ShouldResemble, &pb.GetNodeSessionResponse{
						DevAddr:     "06020304",
						DevEUI:      node.DevEUI.String(),
						AppEUI:      node.AppEUI.String(),
						AppSKey:     node.AppKey.String(),
						NwkSKey:     node.AppKey.String(),
						FCntUp:      10,
						FCntDown:    11,
						RxDelay:     1,
						Rx1DROffset: 2,
						CFList: []uint32{
							868700000,
							0,
							0,
							0,
							0,
						},
					})
				})

				Convey("Then it can be retrieved by DevEUI", func() {
					resp, err := api.GetByDevEUI(ctx, &pb.GetNodeSessionByDevEUIRequest{DevEUI: node.DevEUI.String()})
					So(err, ShouldBeNil)
					So(resp, ShouldResemble, &pb.GetNodeSessionResponse{
						DevAddr:     "06020304",
						DevEUI:      node.DevEUI.String(),
						AppEUI:      node.AppEUI.String(),
						AppSKey:     node.AppKey.String(),
						NwkSKey:     node.AppKey.String(),
						FCntUp:      10,
						FCntDown:    11,
						RxDelay:     1,
						Rx1DROffset: 2,
						CFList: []uint32{
							868700000,
							0,
							0,
							0,
							0,
						},
					})
				})

				Convey("When updating the node-session", func() {
					_, err := api.Update(ctx, &pb.UpdateNodeSessionRequest{
						DevAddr:     "06020304",
						DevEUI:      node.DevEUI.String(),
						AppEUI:      node.AppEUI.String(),
						AppSKey:     node.AppKey.String(),
						NwkSKey:     node.AppKey.String(),
						FCntUp:      20,
						FCntDown:    22,
						RxDelay:     10,
						Rx1DROffset: 20,
						CFList: []uint32{
							868700000,
							868800000,
						},
					})
					So(err, ShouldBeNil)

					Convey("Then the node-session has been updated", func() {
						resp, err := api.Get(ctx, &pb.GetNodeSessionRequest{DevAddr: "06020304"})
						So(err, ShouldBeNil)
						So(resp, ShouldResemble, &pb.GetNodeSessionResponse{
							DevAddr:     "06020304",
							DevEUI:      node.DevEUI.String(),
							AppEUI:      node.AppEUI.String(),
							AppSKey:     node.AppKey.String(),
							NwkSKey:     node.AppKey.String(),
							FCntUp:      20,
							FCntDown:    22,
							RxDelay:     10,
							Rx1DROffset: 20,
							CFList: []uint32{
								868700000,
								868800000,
								0,
								0,
								0,
							},
						})
					})
				})

				Convey("When deleting the node-session", func() {
					_, err := api.Delete(ctx, &pb.DeleteNodeSessionRequest{DevAddr: "06020304"})
					So(err, ShouldBeNil)

					Convey("Then the node-session has been deleted", func() {
						_, err := api.Get(ctx, &pb.GetNodeSessionRequest{DevAddr: "06020304"})
						So(err, ShouldNotBeNil)
					})
				})
			})
		})
	})
}
func TestHandleJoinRequestPackets(t *testing.T) {
	conf := common.GetTestConfig()

	Convey("Given a dummy gateway and application backend and a clean Postgres and Redis database", t, func() {
		a := &testApplicationBackend{
			rxPayloadChan:           make(chan models.RXPayload, 1),
			notificationPayloadChan: make(chan interface{}, 10),
		}
		g := &testGatewayBackend{
			rxPacketChan: make(chan models.RXPacket, 1),
			txPacketChan: make(chan models.TXPacket, 1),
		}
		p := storage.NewRedisPool(conf.RedisURL)
		common.MustFlushRedis(p)
		db, err := storage.OpenDatabase(conf.PostgresDSN)
		So(err, ShouldBeNil)
		common.MustResetDB(db)

		ctx := Context{
			RedisPool:   p,
			Gateway:     g,
			Application: a,
			DB:          db,
		}

		Convey("Given a node and application in the database", func() {
			app := models.Application{
				AppEUI: [8]byte{1, 2, 3, 4, 5, 6, 7, 8},
				Name:   "test app",
			}
			So(storage.CreateApplication(ctx.DB, app), ShouldBeNil)

			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, 9, 10, 11, 12, 13, 14, 15, 16},

				RXDelay:     3,
				RX1DROffset: 2,
			}
			So(storage.CreateNode(ctx.DB, node), ShouldBeNil)

			Convey("Given a JoinRequest with correct DevEUI but incorrect AppEUI", func() {
				phy := lorawan.PHYPayload{
					MHDR: lorawan.MHDR{
						MType: lorawan.JoinRequest,
						Major: lorawan.LoRaWANR1,
					},
					MACPayload: &lorawan.JoinRequestPayload{
						AppEUI:   [8]byte{1, 2, 3, 4, 5, 6, 7, 9},
						DevEUI:   node.DevEUI,
						DevNonce: [2]byte{1, 2},
					},
				}
				So(phy.SetMIC(node.AppKey), ShouldBeNil)

				rxPacket := models.RXPacket{
					PHYPayload: phy,
					RXInfo: models.RXInfo{
						Frequency: common.Band.UplinkChannels[0].Frequency,
						DataRate:  common.Band.DataRates[common.Band.UplinkChannels[0].DataRates[0]],
					},
				}

				Convey("then handleRXPacket returns an error", func() {
					So(handleRXPacket(ctx, rxPacket), ShouldResemble, errors.New("node 0807060504030201 belongs to application 0102030405060708, 0102030405060709 was given"))
				})
			})

			Convey("Given a JoinRequest packet", func() {
				phy := lorawan.PHYPayload{
					MHDR: lorawan.MHDR{
						MType: lorawan.JoinRequest,
						Major: lorawan.LoRaWANR1,
					},
					MACPayload: &lorawan.JoinRequestPayload{
						AppEUI:   app.AppEUI,
						DevEUI:   node.DevEUI,
						DevNonce: [2]byte{1, 2},
					},
				}
				So(phy.SetMIC(node.AppKey), ShouldBeNil)

				rxPacket := models.RXPacket{
					PHYPayload: phy,
					RXInfo: models.RXInfo{
						Frequency: common.Band.UplinkChannels[0].Frequency,
						DataRate:  common.Band.DataRates[common.Band.UplinkChannels[0].DataRates[0]],
					},
				}

				Convey("When calling handleRXPacket", func() {
					So(handleRXPacket(ctx, rxPacket), ShouldBeNil)

					Convey("Then a JoinAccept was sent to the node", func() {
						txPacket := <-g.txPacketChan
						phy := txPacket.PHYPayload
						So(phy.DecryptJoinAcceptPayload(node.AppKey), ShouldBeNil)
						So(phy.MHDR.MType, ShouldEqual, lorawan.JoinAccept)

						Convey("Then it was sent after 5s", func() {
							So(txPacket.TXInfo.Timestamp, ShouldEqual, rxPacket.RXInfo.Timestamp+uint32(5*time.Second/time.Microsecond))
						})

						Convey("Then the RXDelay is set to 3s", func() {
							jaPL := phy.MACPayload.(*lorawan.JoinAcceptPayload)
							So(jaPL.RXDelay, ShouldEqual, 3)
						})

						Convey("Then the DLSettings are set correctly", func() {
							jaPL := phy.MACPayload.(*lorawan.JoinAcceptPayload)
							So(jaPL.DLSettings.RX2DataRate, ShouldEqual, uint8(common.Band.RX2DataRate))
							So(jaPL.DLSettings.RX1DROffset, ShouldEqual, node.RX1DROffset)
						})

						Convey("Then a node-session was created", func() {
							jaPL := phy.MACPayload.(*lorawan.JoinAcceptPayload)

							_, err := storage.GetNodeSession(ctx.RedisPool, jaPL.DevAddr)
							So(err, ShouldBeNil)
						})

						Convey("Then the dev-nonce was added to the used dev-nonces", func() {
							node, err := storage.GetNode(ctx.DB, node.DevEUI)
							So(err, ShouldBeNil)
							So([2]byte{1, 2}, ShouldBeIn, node.UsedDevNonces)
						})

						Convey("Then a join notification was sent to the application", func() {
							notification := <-a.notificationPayloadChan
							join, ok := notification.(models.JoinNotification)
							So(ok, ShouldBeTrue)
							So(join.DevEUI, ShouldResemble, node.DevEUI)
						})
					})
				})
			})
		})
	})
}
Exemple #4
0
func TestNodeAPI(t *testing.T) {
	conf := common.GetTestConfig()

	Convey("Given a clean database with an application and api instance", t, func() {
		db, err := storage.OpenDatabase(conf.PostgresDSN)
		So(err, ShouldBeNil)
		common.MustResetDB(db)
		p := storage.NewRedisPool(conf.RedisURL)
		common.MustFlushRedis(p)

		ctx := context.Background()
		lsCtx := loraserver.Context{DB: db, RedisPool: p}
		api := NewNodeAPI(lsCtx)

		app := models.Application{
			AppEUI: [8]byte{1, 2, 3, 4, 5, 6, 7, 8},
			Name:   "test app",
		}
		So(storage.CreateApplication(db, app), ShouldBeNil)

		Convey("When creating a node", func() {
			_, err := api.Create(ctx, &pb.CreateNodeRequest{
				DevEUI:      "0807060504030201",
				AppEUI:      "0102030405060708",
				AppKey:      "01020304050607080102030405060708",
				RxDelay:     1,
				Rx1DROffset: 3,
			})
			So(err, ShouldBeNil)

			Convey("The node has been created", func() {
				node, err := api.Get(ctx, &pb.GetNodeRequest{DevEUI: "0807060504030201"})
				So(err, ShouldBeNil)
				So(node, ShouldResemble, &pb.GetNodeResponse{
					DevEUI:      "0807060504030201",
					AppEUI:      "0102030405060708",
					AppKey:      "01020304050607080102030405060708",
					RxDelay:     1,
					Rx1DROffset: 3,
				})
			})

			Convey("Then listing the nodes returns a single items", func() {
				nodes, err := api.List(ctx, &pb.ListNodeRequest{
					Limit: 10,
				})
				So(err, ShouldBeNil)
				So(nodes.Result, ShouldHaveLength, 1)
				So(nodes.TotalCount, ShouldEqual, 1)
				So(nodes.Result[0], ShouldResemble, &pb.GetNodeResponse{
					DevEUI:      "0807060504030201",
					AppEUI:      "0102030405060708",
					AppKey:      "01020304050607080102030405060708",
					RxDelay:     1,
					Rx1DROffset: 3,
				})
			})

			Convey("Then listing the nodes for a given AppEUI returns a single item", func() {
				nodes, err := api.ListByAppEUI(ctx, &pb.ListNodeByAppEUIRequest{
					Limit:  10,
					AppEUI: "0102030405060708",
				})
				So(err, ShouldBeNil)
				So(nodes.Result, ShouldHaveLength, 1)
				So(nodes.TotalCount, ShouldEqual, 1)
				So(nodes.Result[0], ShouldResemble, &pb.GetNodeResponse{
					DevEUI:      "0807060504030201",
					AppEUI:      "0102030405060708",
					AppKey:      "01020304050607080102030405060708",
					RxDelay:     1,
					Rx1DROffset: 3,
				})
			})

			Convey("When updating the node", func() {
				_, err := api.Update(ctx, &pb.UpdateNodeRequest{
					DevEUI:      "0807060504030201",
					AppEUI:      "0102030405060708",
					AppKey:      "08070605040302010807060504030201",
					RxDelay:     3,
					Rx1DROffset: 1,
				})
				So(err, ShouldBeNil)

				Convey("Then the node has been updated", func() {
					node, err := api.Get(ctx, &pb.GetNodeRequest{DevEUI: "0807060504030201"})
					So(err, ShouldBeNil)
					So(node, ShouldResemble, &pb.GetNodeResponse{
						DevEUI:      "0807060504030201",
						AppEUI:      "0102030405060708",
						AppKey:      "08070605040302010807060504030201",
						RxDelay:     3,
						Rx1DROffset: 1,
					})
				})
			})

			Convey("After deleting the node", func() {
				_, err := api.Delete(ctx, &pb.DeleteNodeRequest{DevEUI: "0807060504030201"})
				So(err, ShouldBeNil)

				Convey("Then listing the nodes returns zero nodes", func() {
					nodes, err := api.List(ctx, &pb.ListNodeRequest{Limit: 10})
					So(err, ShouldBeNil)
					So(nodes.TotalCount, ShouldEqual, 0)
					So(nodes.Result, ShouldHaveLength, 0)
				})
			})

			Convey("Given a tx payload in the the queue", func() {
				So(storage.AddTXPayloadToQueue(p, models.TXPayload{
					DevEUI: [8]byte{8, 7, 6, 5, 4, 3, 2, 1},
					Data:   []byte("hello!"),
				}), ShouldBeNil)
				count, err := storage.GetTXPayloadQueueSize(p, [8]byte{8, 7, 6, 5, 4, 3, 2, 1})
				So(err, ShouldBeNil)
				So(count, ShouldEqual, 1)

				Convey("When flushing the tx-payload queue", func() {
					_, err := api.FlushTXPayloadQueue(ctx, &pb.FlushTXPayloadQueueRequest{DevEUI: "0807060504030201"})
					So(err, ShouldBeNil)

					Convey("Then the queue is empty", func() {
						count, err := storage.GetTXPayloadQueueSize(p, [8]byte{8, 7, 6, 5, 4, 3, 2, 1})
						So(err, ShouldBeNil)
						So(count, ShouldEqual, 0)
					})
				})
			})
		})
	})
}