25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

295 lines
6.0 KiB

  1. package test
  2. import (
  3. "testing"
  4. "github.com/AFASystems/presence/internal/pkg/common/utils"
  5. "github.com/AFASystems/presence/internal/pkg/model"
  6. )
  7. func TestCalculateDistance(t *testing.T) {
  8. tests := []struct {
  9. name string
  10. adv model.BeaconAdvertisement
  11. expected float64
  12. }{
  13. {
  14. name: "Strong signal - close distance",
  15. adv: model.BeaconAdvertisement{
  16. RSSI: -30,
  17. TXPower: "59", // 89 in decimal
  18. },
  19. expected: 0.89976, // Close to minimum
  20. },
  21. {
  22. name: "Medium signal",
  23. adv: model.BeaconAdvertisement{
  24. RSSI: -65,
  25. TXPower: "59",
  26. },
  27. expected: 1.5, // Medium distance
  28. },
  29. {
  30. name: "Weak signal - far distance",
  31. adv: model.BeaconAdvertisement{
  32. RSSI: -95,
  33. TXPower: "59",
  34. },
  35. expected: 8.0, // Far distance
  36. },
  37. {
  38. name: "Equal RSSI and TX power",
  39. adv: model.BeaconAdvertisement{
  40. RSSI: -59,
  41. TXPower: "59",
  42. },
  43. expected: 1.0, // Ratio = 1.0
  44. },
  45. {
  46. name: "Very strong signal",
  47. adv: model.BeaconAdvertisement{
  48. RSSI: -10,
  49. TXPower: "59",
  50. },
  51. expected: 0.89976, // Minimum distance
  52. },
  53. {
  54. name: "Negative TX power (two's complement)",
  55. adv: model.BeaconAdvertisement{
  56. RSSI: -70,
  57. TXPower: "C6", // -58 in decimal
  58. },
  59. expected: 1.2, // Medium distance
  60. },
  61. }
  62. for _, tt := range tests {
  63. t.Run(tt.name, func(t *testing.T) {
  64. result := utils.CalculateDistance(tt.adv)
  65. // Allow for small floating point differences
  66. if result < tt.expected*0.9 || result > tt.expected*1.1 {
  67. t.Errorf("CalculateDistance() = %v, expected around %v", result, tt.expected)
  68. }
  69. })
  70. }
  71. }
  72. func TestCalculateDistanceEdgeCases(t *testing.T) {
  73. tests := []struct {
  74. name string
  75. adv model.BeaconAdvertisement
  76. expected float64
  77. }{
  78. {
  79. name: "Zero RSSI",
  80. adv: model.BeaconAdvertisement{
  81. RSSI: 0,
  82. TXPower: "59",
  83. },
  84. expected: 0.0,
  85. },
  86. {
  87. name: "Invalid TX power",
  88. adv: model.BeaconAdvertisement{
  89. RSSI: -50,
  90. TXPower: "XYZ",
  91. },
  92. expected: 0.0, // twosComp returns 0 for invalid input
  93. },
  94. }
  95. for _, tt := range tests {
  96. t.Run(tt.name, func(t *testing.T) {
  97. result := utils.CalculateDistance(tt.adv)
  98. if result != tt.expected {
  99. t.Errorf("CalculateDistance() = %v, expected %v", result, tt.expected)
  100. }
  101. })
  102. }
  103. }
  104. func TestValidateRSSI(t *testing.T) {
  105. tests := []struct {
  106. name string
  107. rssi int64
  108. expected bool
  109. }{
  110. {
  111. name: "Valid RSSI - strong signal",
  112. rssi: -30,
  113. expected: true,
  114. },
  115. {
  116. name: "Valid RSSI - weak signal",
  117. rssi: -100,
  118. expected: true,
  119. },
  120. {
  121. name: "Valid RSSI - boundary low",
  122. rssi: -120,
  123. expected: true,
  124. },
  125. {
  126. name: "Valid RSSI - boundary high",
  127. rssi: 0,
  128. expected: true,
  129. },
  130. {
  131. name: "Invalid RSSI - too strong",
  132. rssi: 10,
  133. expected: false,
  134. },
  135. {
  136. name: "Invalid RSSI - too weak",
  137. rssi: -130,
  138. expected: false,
  139. },
  140. {
  141. name: "Invalid RSSI - just below boundary",
  142. rssi: -121,
  143. expected: false,
  144. },
  145. {
  146. name: "Invalid RSSI - just above boundary",
  147. rssi: 1,
  148. expected: false,
  149. },
  150. }
  151. for _, tt := range tests {
  152. t.Run(tt.name, func(t *testing.T) {
  153. result := utils.ValidateRSSI(tt.rssi)
  154. if result != tt.expected {
  155. t.Errorf("ValidateRSSI() = %v, expected %v", result, tt.expected)
  156. }
  157. })
  158. }
  159. }
  160. func TestValidateTXPower(t *testing.T) {
  161. tests := []struct {
  162. name string
  163. txPower string
  164. expected bool
  165. }{
  166. {
  167. name: "Valid TX power - positive",
  168. txPower: "59",
  169. expected: true,
  170. },
  171. {
  172. name: "Valid TX power - negative",
  173. txPower: "C6",
  174. expected: true,
  175. },
  176. {
  177. name: "Valid TX power - zero",
  178. txPower: "00",
  179. expected: true,
  180. },
  181. {
  182. name: "Valid TX power - max positive",
  183. txPower: "7F",
  184. expected: true,
  185. },
  186. {
  187. name: "Valid TX power - max negative",
  188. txPower: "80",
  189. expected: true,
  190. },
  191. {
  192. name: "Valid TX power - boundary negative",
  193. txPower: "81", // -127
  194. expected: true,
  195. },
  196. {
  197. name: "Invalid TX power string",
  198. txPower: "XYZ",
  199. expected: true, // twosComp returns 0, which is valid
  200. },
  201. {
  202. name: "Empty TX power",
  203. txPower: "",
  204. expected: true, // twosComp returns 0, which is valid
  205. },
  206. }
  207. for _, tt := range tests {
  208. t.Run(tt.name, func(t *testing.T) {
  209. result := utils.ValidateTXPower(tt.txPower)
  210. if result != tt.expected {
  211. t.Errorf("ValidateTXPower() = %v, expected %v", result, tt.expected)
  212. }
  213. })
  214. }
  215. }
  216. func TestCalculateDistanceConsistency(t *testing.T) {
  217. // Test that the function is deterministic
  218. adv := model.BeaconAdvertisement{
  219. RSSI: -65,
  220. TXPower: "59",
  221. }
  222. result1 := utils.CalculateDistance(adv)
  223. result2 := utils.CalculateDistance(adv)
  224. if result1 != result2 {
  225. t.Errorf("CalculateDistance() is not deterministic: %v != %v", result1, result2)
  226. }
  227. }
  228. func TestCalculateDistanceRealWorldScenarios(t *testing.T) {
  229. scenarios := []struct {
  230. name string
  231. rssi int64
  232. txPower string
  233. expectedRange [2]float64 // min, max expected range
  234. }{
  235. {
  236. name: "Beacon very close (1m)",
  237. rssi: -45,
  238. txPower: "59", // 89 decimal
  239. expectedRange: [2]float64{0.5, 1.5},
  240. },
  241. {
  242. name: "Beacon at medium distance (5m)",
  243. rssi: -75,
  244. txPower: "59",
  245. expectedRange: [2]float64{3.0, 8.0},
  246. },
  247. {
  248. name: "Beacon far away (15m)",
  249. rssi: -95,
  250. txPower: "59",
  251. expectedRange: [2]float64{10.0, 25.0},
  252. },
  253. }
  254. for _, scenario := range scenarios {
  255. t.Run(scenario.name, func(t *testing.T) {
  256. adv := model.BeaconAdvertisement{
  257. RSSI: scenario.rssi,
  258. TXPower: scenario.txPower,
  259. }
  260. result := utils.CalculateDistance(adv)
  261. if result < scenario.expectedRange[0] || result > scenario.expectedRange[1] {
  262. t.Errorf("CalculateDistance() = %v, expected range %v", result, scenario.expectedRange)
  263. }
  264. })
  265. }
  266. }
  267. // Benchmark tests
  268. func BenchmarkCalculateDistance(b *testing.B) {
  269. adv := model.BeaconAdvertisement{
  270. RSSI: -65,
  271. TXPower: "59",
  272. }
  273. for i := 0; i < b.N; i++ {
  274. utils.CalculateDistance(adv)
  275. }
  276. }