PageRenderTime 95ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/internal/loraserver/loraserver_test.go

https://gitlab.com/3maple/loraserver
Go | 420 lines | 354 code | 65 blank | 1 comment | 0 complexity | 79810ac1a841e7ef6ae88e962743a307 MD5 | raw file
  1. package loraserver
  2. import (
  3. "errors"
  4. "testing"
  5. "time"
  6. "github.com/brocaar/loraserver/models"
  7. "github.com/brocaar/lorawan"
  8. . "github.com/smartystreets/goconvey/convey"
  9. )
  10. func TestHandleDataUpPackets(t *testing.T) {
  11. conf := getConfig()
  12. Convey("Given a clean state", t, func() {
  13. app := &testApplicationBackend{
  14. rxPayloadChan: make(chan models.RXPayload, 1),
  15. txPayloadChan: make(chan models.TXPayload, 1),
  16. notificationPayloadChan: make(chan interface{}, 10),
  17. }
  18. gw := &testGatewayBackend{
  19. rxPacketChan: make(chan models.RXPacket, 1),
  20. txPacketChan: make(chan models.TXPacket, 1),
  21. }
  22. p := NewRedisPool(conf.RedisURL)
  23. mustFlushRedis(p)
  24. ctx := Context{
  25. RedisPool: p,
  26. Gateway: gw,
  27. Application: app,
  28. }
  29. Convey("Given a stored node session", func() {
  30. ns := models.NodeSession{
  31. DevAddr: [4]byte{1, 2, 3, 4},
  32. DevEUI: [8]byte{1, 2, 3, 4, 5, 6, 7, 8},
  33. AppSKey: [16]byte{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
  34. NwkSKey: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
  35. FCntUp: 8,
  36. FCntDown: 5,
  37. AppEUI: [8]byte{8, 7, 6, 5, 4, 3, 2, 1},
  38. }
  39. So(saveNodeSession(p, ns), ShouldBeNil)
  40. Convey("Given an UnconfirmedDataUp packet", func() {
  41. fPort := uint8(1)
  42. phy := lorawan.PHYPayload{
  43. MHDR: lorawan.MHDR{
  44. MType: lorawan.UnconfirmedDataUp,
  45. Major: lorawan.LoRaWANR1,
  46. },
  47. MACPayload: &lorawan.MACPayload{
  48. FHDR: lorawan.FHDR{
  49. DevAddr: ns.DevAddr,
  50. FCnt: 10,
  51. },
  52. FPort: &fPort,
  53. FRMPayload: []lorawan.Payload{
  54. &lorawan.DataPayload{Bytes: []byte("hello!")},
  55. },
  56. },
  57. }
  58. So(phy.EncryptFRMPayload(ns.AppSKey), ShouldBeNil)
  59. So(phy.SetMIC(ns.NwkSKey), ShouldBeNil)
  60. rxPacket := models.RXPacket{
  61. PHYPayload: phy,
  62. RXInfo: models.RXInfo{
  63. Frequency: Band.UplinkChannels[0].Frequency,
  64. DataRate: Band.DataRates[Band.UplinkChannels[0].DataRates[0]],
  65. },
  66. }
  67. Convey("Given that the application backend returns an error", func() {
  68. app.err = errors.New("BOOM")
  69. Convey("Then handleRXPacket returns an error", func() {
  70. So(handleRXPacket(ctx, rxPacket), ShouldNotBeNil)
  71. Convey("Then the FCntUp has not been incremented", func() {
  72. nsUpdated, err := getNodeSession(p, ns.DevAddr)
  73. So(err, ShouldBeNil)
  74. So(nsUpdated, ShouldResemble, ns)
  75. })
  76. })
  77. })
  78. Convey("When the frame-counter is invalid", func() {
  79. ns.FCntUp = 11
  80. So(saveNodeSession(p, ns), ShouldBeNil)
  81. Convey("Then handleRXPacket returns a frame-counter related error", func() {
  82. err := handleRXPacket(ctx, rxPacket)
  83. So(err, ShouldResemble, errors.New("invalid FCnt or too many dropped frames"))
  84. })
  85. })
  86. Convey("When the MIC is invalid", func() {
  87. rxPacket.PHYPayload.MIC = [4]byte{1, 2, 3, 4}
  88. Convey("Then handleRXPacket returns a MIC related error", func() {
  89. err := handleRXPacket(ctx, rxPacket)
  90. So(err, ShouldResemble, errors.New("invalid MIC"))
  91. })
  92. })
  93. Convey("When calling handleRXPacket", func() {
  94. So(handleRXPacket(ctx, rxPacket), ShouldBeNil)
  95. Convey("Then a rx info notification was sent", func() {
  96. notification := <-app.notificationPayloadChan
  97. _, ok := notification.(models.RXInfoNotification)
  98. So(ok, ShouldBeTrue)
  99. })
  100. Convey("Then the packet is correctly received by the application backend", func() {
  101. packet := <-app.rxPayloadChan
  102. So(packet, ShouldResemble, models.RXPayload{
  103. DevEUI: ns.DevEUI,
  104. FPort: 1,
  105. GatewayCount: 1,
  106. Data: []byte("hello!"),
  107. })
  108. })
  109. Convey("Then the FCntUp must be synced", func() {
  110. nsUpdated, err := getNodeSession(p, ns.DevAddr)
  111. So(err, ShouldBeNil)
  112. So(nsUpdated.FCntUp, ShouldEqual, 10)
  113. })
  114. Convey("Then no downlink data was sent to the gateway", func() {
  115. var received bool
  116. select {
  117. case <-gw.txPacketChan:
  118. received = true
  119. case <-time.After(time.Second):
  120. // nothing to do
  121. }
  122. So(received, ShouldEqual, false)
  123. })
  124. })
  125. Convey("Given an enqueued TXPayload (unconfirmed)", func() {
  126. txPayload := models.TXPayload{
  127. Confirmed: false,
  128. DevEUI: ns.DevEUI,
  129. FPort: 5,
  130. Data: []byte("hello back!"),
  131. }
  132. So(addTXPayloadToQueue(p, txPayload), ShouldBeNil)
  133. Convey("When calling handleRXPacket", func() {
  134. So(handleRXPacket(ctx, rxPacket), ShouldBeNil)
  135. Convey("Then a rx info notification was sent", func() {
  136. notification := <-app.notificationPayloadChan
  137. So(notification, ShouldHaveSameTypeAs, models.RXInfoNotification{})
  138. })
  139. Convey("Then the packet is received by the application backend", func() {
  140. _ = <-app.rxPayloadChan
  141. })
  142. Convey("Then a packet is sent to the gateway", func() {
  143. txPacket := <-gw.txPacketChan
  144. macPL, ok := txPacket.PHYPayload.MACPayload.(*lorawan.MACPayload)
  145. So(ok, ShouldBeTrue)
  146. Convey("Then this packet contains the expected values", func() {
  147. So(txPacket.PHYPayload.MHDR.MType, ShouldEqual, lorawan.UnconfirmedDataDown)
  148. So(macPL.FHDR.FCnt, ShouldEqual, ns.FCntDown)
  149. So(macPL.FHDR.FCtrl.ACK, ShouldBeFalse)
  150. So(*macPL.FPort, ShouldEqual, 5)
  151. So(txPacket.PHYPayload.DecryptFRMPayload(ns.AppSKey), ShouldBeNil)
  152. So(len(macPL.FRMPayload), ShouldEqual, 1)
  153. pl, ok := macPL.FRMPayload[0].(*lorawan.DataPayload)
  154. So(ok, ShouldBeTrue)
  155. So(pl.Bytes, ShouldResemble, []byte("hello back!"))
  156. })
  157. })
  158. Convey("Then the FCntDown was incremented", func() {
  159. ns2, err := getNodeSession(p, ns.DevAddr)
  160. So(err, ShouldBeNil)
  161. So(ns2.FCntDown, ShouldEqual, ns.FCntDown+1)
  162. })
  163. Convey("Then the TXPayload queue is empty", func() {
  164. txPayload, _, err := getTXPayloadAndRemainingFromQueue(p, ns.DevEUI)
  165. So(err, ShouldBeNil)
  166. So(txPayload, ShouldBeNil)
  167. })
  168. })
  169. })
  170. Convey("Given an enqueued TXPayload which exceeds the max size", func() {
  171. txPayload := models.TXPayload{
  172. DevEUI: ns.DevEUI,
  173. FPort: 5,
  174. Data: []byte("hello back!hello back!hello back!hello back!hello back!hello back!hello back!hello back!hello back!hello back!"),
  175. Reference: "testcase",
  176. }
  177. So(addTXPayloadToQueue(p, txPayload), ShouldBeNil)
  178. Convey("When calling handleRXPacket", func() {
  179. So(handleRXPacket(ctx, rxPacket), ShouldBeNil)
  180. Convey("Then an rx info and error notification was sent", func() {
  181. notification := <-app.notificationPayloadChan
  182. So(notification, ShouldHaveSameTypeAs, models.RXInfoNotification{})
  183. notification = <-app.notificationPayloadChan
  184. So(notification, ShouldHaveSameTypeAs, models.ErrorNotification{})
  185. })
  186. Convey("Then the TXPayload queue is empty", func() {
  187. txPayload, _, err := getTXPayloadAndRemainingFromQueue(p, ns.DevEUI)
  188. So(err, ShouldBeNil)
  189. So(txPayload, ShouldBeNil)
  190. })
  191. })
  192. })
  193. Convey("Given an enqueued TXPayload (confirmed)", func() {
  194. txPayload := models.TXPayload{
  195. Confirmed: true,
  196. DevEUI: ns.DevEUI,
  197. FPort: 5,
  198. Data: []byte("hello back!"),
  199. }
  200. So(addTXPayloadToQueue(p, txPayload), ShouldBeNil)
  201. Convey("When calling handleRXPacket", func() {
  202. So(handleRXPacket(ctx, rxPacket), ShouldBeNil)
  203. Convey("Then an rx info and ACK notification were sent", func() {
  204. notification := <-app.notificationPayloadChan
  205. So(notification, ShouldHaveSameTypeAs, models.RXInfoNotification{})
  206. })
  207. Convey("Then the packet is received by the application backend", func() {
  208. _ = <-app.rxPayloadChan
  209. })
  210. Convey("Then a packet is sent to the gateway", func() {
  211. txPacket := <-gw.txPacketChan
  212. macPL, ok := txPacket.PHYPayload.MACPayload.(*lorawan.MACPayload)
  213. So(ok, ShouldBeTrue)
  214. Convey("Then this packet contains the expected values", func() {
  215. So(txPacket.PHYPayload.MHDR.MType, ShouldEqual, lorawan.ConfirmedDataDown)
  216. So(macPL.FHDR.FCnt, ShouldEqual, ns.FCntDown)
  217. So(macPL.FHDR.FCtrl.ACK, ShouldBeFalse)
  218. So(*macPL.FPort, ShouldEqual, 5)
  219. So(txPacket.PHYPayload.DecryptFRMPayload(ns.AppSKey), ShouldBeNil)
  220. So(len(macPL.FRMPayload), ShouldEqual, 1)
  221. pl, ok := macPL.FRMPayload[0].(*lorawan.DataPayload)
  222. So(ok, ShouldBeTrue)
  223. So(pl.Bytes, ShouldResemble, []byte("hello back!"))
  224. })
  225. })
  226. Convey("Then the TXPayload is still in the queue", func() {
  227. tx, _, err := getTXPayloadAndRemainingFromQueue(p, ns.DevEUI)
  228. So(err, ShouldBeNil)
  229. So(tx, ShouldResemble, &txPayload)
  230. })
  231. Convey("Then the FCntDown was not incremented", func() {
  232. ns2, err := getNodeSession(p, ns.DevAddr)
  233. So(err, ShouldBeNil)
  234. So(ns2.FCntDown, ShouldEqual, ns.FCntDown)
  235. Convey("Given the node sends an ACK", func() {
  236. phy := lorawan.PHYPayload{
  237. MHDR: lorawan.MHDR{
  238. MType: lorawan.UnconfirmedDataUp,
  239. Major: lorawan.LoRaWANR1,
  240. },
  241. MACPayload: &lorawan.MACPayload{
  242. FHDR: lorawan.FHDR{
  243. DevAddr: ns.DevAddr,
  244. FCnt: 11,
  245. FCtrl: lorawan.FCtrl{
  246. ACK: true,
  247. },
  248. },
  249. },
  250. }
  251. So(phy.SetMIC(ns.NwkSKey), ShouldBeNil)
  252. rxPacket := models.RXPacket{
  253. PHYPayload: phy,
  254. }
  255. _ = <-app.notificationPayloadChan // drain the notification channel
  256. So(handleRXPacket(ctx, rxPacket), ShouldBeNil)
  257. Convey("Then the FCntDown was incremented", func() {
  258. ns2, err := getNodeSession(p, ns.DevAddr)
  259. So(err, ShouldBeNil)
  260. So(ns2.FCntDown, ShouldEqual, ns.FCntDown+1)
  261. Convey("Then the TXPayload queue is empty", func() {
  262. txPayload, _, err := getTXPayloadAndRemainingFromQueue(p, ns.DevEUI)
  263. So(err, ShouldBeNil)
  264. So(txPayload, ShouldBeNil)
  265. })
  266. Convey("Then an rx info and ACK notification were sent", func() {
  267. notification := <-app.notificationPayloadChan
  268. So(notification, ShouldHaveSameTypeAs, models.RXInfoNotification{})
  269. notification = <-app.notificationPayloadChan
  270. So(notification, ShouldHaveSameTypeAs, models.ACKNotification{})
  271. })
  272. })
  273. })
  274. })
  275. })
  276. })
  277. })
  278. Convey("Given a ConfirmedDataUp packet", func() {
  279. fPort := uint8(1)
  280. phy := lorawan.PHYPayload{
  281. MHDR: lorawan.MHDR{
  282. MType: lorawan.ConfirmedDataUp,
  283. Major: lorawan.LoRaWANR1,
  284. },
  285. MACPayload: &lorawan.MACPayload{
  286. FHDR: lorawan.FHDR{
  287. DevAddr: ns.DevAddr,
  288. FCnt: 10,
  289. },
  290. FPort: &fPort,
  291. FRMPayload: []lorawan.Payload{
  292. &lorawan.DataPayload{Bytes: []byte("hello!")},
  293. },
  294. },
  295. }
  296. So(phy.EncryptFRMPayload(ns.AppSKey), ShouldBeNil)
  297. So(phy.SetMIC(ns.NwkSKey), ShouldBeNil)
  298. rxPacket := models.RXPacket{
  299. PHYPayload: phy,
  300. RXInfo: models.RXInfo{
  301. Frequency: Band.UplinkChannels[0].Frequency,
  302. DataRate: Band.DataRates[Band.UplinkChannels[0].DataRates[0]],
  303. },
  304. }
  305. Convey("Given an enqueued TXPayload which exceeds the max size", func() {
  306. txPayload := models.TXPayload{
  307. DevEUI: ns.DevEUI,
  308. FPort: 5,
  309. Data: []byte("hello back!hello back!hello back!hello back!hello back!hello back!hello back!hello back!hello back!hello back!"),
  310. Reference: "testcase",
  311. }
  312. So(addTXPayloadToQueue(p, txPayload), ShouldBeNil)
  313. Convey("When calling handleRXPacket", func() {
  314. So(handleRXPacket(ctx, rxPacket), ShouldBeNil)
  315. Convey("Then an rx info and error notification were sent", func() {
  316. notification := <-app.notificationPayloadChan
  317. So(notification, ShouldHaveSameTypeAs, models.RXInfoNotification{})
  318. notification = <-app.notificationPayloadChan
  319. So(notification, ShouldHaveSameTypeAs, models.ErrorNotification{})
  320. })
  321. Convey("Then the TXPayload queue is empty", func() {
  322. txPayload, _, err := getTXPayloadAndRemainingFromQueue(p, ns.DevEUI)
  323. So(err, ShouldBeNil)
  324. So(txPayload, ShouldBeNil)
  325. })
  326. })
  327. })
  328. Convey("When calling handleRXPacket", func() {
  329. So(handleRXPacket(ctx, rxPacket), ShouldBeNil)
  330. Convey("Then the packet is correctly received by the application backend", func() {
  331. packet := <-app.rxPayloadChan
  332. So(packet, ShouldResemble, models.RXPayload{
  333. DevEUI: ns.DevEUI,
  334. FPort: 1,
  335. GatewayCount: 1,
  336. Data: []byte("hello!"),
  337. })
  338. })
  339. Convey("Then a ACK packet is sent to the gateway", func() {
  340. txPacket := <-gw.txPacketChan
  341. macPL, ok := txPacket.PHYPayload.MACPayload.(*lorawan.MACPayload)
  342. So(ok, ShouldBeTrue)
  343. So(macPL.FHDR.FCtrl.ACK, ShouldBeTrue)
  344. So(macPL.FHDR.FCnt, ShouldEqual, 5)
  345. Convey("Then the FCntDown counter has incremented", func() {
  346. ns, err := getNodeSession(ctx.RedisPool, ns.DevAddr)
  347. So(err, ShouldBeNil)
  348. So(ns.FCntDown, ShouldEqual, 6)
  349. })
  350. })
  351. })
  352. })
  353. })
  354. })
  355. }