sms_pdu_test.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. package device
  2. import (
  3. "errors"
  4. "strings"
  5. "testing"
  6. )
  7. func TestPrepareSMSSelectsDirectGSM7AndPDUEncodings(t *testing.T) {
  8. direct, err := prepareSMS("+12 345", "HELLO")
  9. if err != nil {
  10. t.Fatalf("prepare direct GSM-7: %v", err)
  11. }
  12. if direct.to != "+12345" ||
  13. direct.encoding != SMSEncodingGSM7Text ||
  14. direct.prompt != `AT+CMGS="+12345"` ||
  15. string(direct.payload) != "HELLO" {
  16. t.Fatalf("direct = %#v", direct)
  17. }
  18. gsmPDU, err := prepareSMS("+12345", "@")
  19. if err != nil {
  20. t.Fatalf("prepare GSM-7 PDU: %v", err)
  21. }
  22. if gsmPDU.encoding != SMSEncodingGSM7PDU ||
  23. gsmPDU.tpduLength != 11 ||
  24. string(gsmPDU.payload) != "00210005912143F500000100" {
  25. t.Fatalf("GSM PDU = %#v", gsmPDU)
  26. }
  27. unicode, err := prepareSMS("+12345", "你好")
  28. if err != nil {
  29. t.Fatalf("prepare UCS2 PDU: %v", err)
  30. }
  31. if unicode.encoding != SMSEncodingUCS2PDU ||
  32. unicode.tpduLength != 14 ||
  33. unicode.prompt != "AT+CMGS=14" ||
  34. string(unicode.payload) != "00210005912143F50008044F60597D" {
  35. t.Fatalf("UCS2 PDU = %#v", unicode)
  36. }
  37. }
  38. func TestPrepareAndDecodeTransportIndependentTPDU(t *testing.T) {
  39. parts, err := PrepareSMSSubmitTPDUs("+12345", "HELLO")
  40. if err != nil {
  41. t.Fatalf("PrepareSMSSubmitTPDUs: %v", err)
  42. }
  43. if len(parts) != 1 || parts[0].To != "+12345" || len(parts[0].TPDU) == 0 ||
  44. parts[0].TPDU[0]&0x03 != 1 || parts[0].TPDU[0]&0x20 == 0 {
  45. t.Fatalf("parts = %#v", parts)
  46. }
  47. message, err := DecodeSMSDeliverTPDU([]byte{
  48. 0x04, 0x05, 0x91, 0x21, 0x43, 0xf5, 0x00, 0x00,
  49. 0x42, 0x10, 0x20, 0x30, 0x40, 0x50, 0x00, 0x05,
  50. 0xc8, 0x22, 0x93, 0xf9, 0x04,
  51. })
  52. if err != nil || message.From != "+12345" || message.Text != "HELLO" {
  53. t.Fatalf("DecodeSMSDeliverTPDU = (%#v, %v)", message, err)
  54. }
  55. }
  56. func TestPrepareSMSRejectsInvalidAndOversizeMessages(t *testing.T) {
  57. if _, err := prepareSMS(`12"34`, "hello"); !errors.Is(err, ErrSMSInvalidRecipient) {
  58. t.Fatalf("invalid recipient error = %v", err)
  59. }
  60. if _, err := prepareSMS("12345", ""); !errors.Is(err, ErrSMSEmpty) {
  61. t.Fatalf("empty message error = %v", err)
  62. }
  63. if _, err := prepareSMS(
  64. "12345",
  65. strings.Repeat("A", 161),
  66. ); !errors.Is(err, ErrSMSTooLong) {
  67. t.Fatalf("long GSM-7 error = %v", err)
  68. }
  69. if _, err := prepareSMS(
  70. "12345",
  71. strings.Repeat("你", 71),
  72. ); !errors.Is(err, ErrSMSTooLong) {
  73. t.Fatalf("long UCS2 error = %v", err)
  74. }
  75. }
  76. func TestPrepareMultipartGSM7Uses153SeptetsAndSharedUDH(t *testing.T) {
  77. const reference = 0x7a
  78. text := strings.Repeat("A", 161)
  79. parts, err := prepareSMSPartsWithReference("+12345", text, reference)
  80. if err != nil {
  81. t.Fatalf("prepare multipart GSM-7: %v", err)
  82. }
  83. if len(parts) != 2 {
  84. t.Fatalf("parts = %d, want 2", len(parts))
  85. }
  86. for index, part := range parts {
  87. message, decodeErr := decodeSMSPDU(string(part.payload))
  88. if decodeErr != nil {
  89. t.Fatalf("decode part %d: %v", index+1, decodeErr)
  90. }
  91. wantText := strings.Repeat("A", 153)
  92. if index == 1 {
  93. wantText = strings.Repeat("A", 8)
  94. }
  95. if message.Text != wantText ||
  96. message.Concat == nil ||
  97. message.Concat.Reference != reference ||
  98. message.Concat.Total != 2 ||
  99. message.Concat.Sequence != index+1 ||
  100. part.part != index+1 ||
  101. part.total != 2 ||
  102. part.encoding != SMSEncodingGSM7PDU {
  103. t.Fatalf("part %d = prepared %#v, decoded %#v", index+1, part, message)
  104. }
  105. }
  106. }
  107. func TestPrepareMultipartGSM7DoesNotSplitExtensionEscape(t *testing.T) {
  108. text := strings.Repeat("A", 152) + "^" + strings.Repeat("B", 10)
  109. parts, err := prepareSMSPartsWithReference("+12345", text, 7)
  110. if err != nil {
  111. t.Fatalf("prepare multipart extension: %v", err)
  112. }
  113. if len(parts) != 2 {
  114. t.Fatalf("parts = %d, want 2", len(parts))
  115. }
  116. first, err := decodeSMSPDU(string(parts[0].payload))
  117. if err != nil {
  118. t.Fatalf("decode first part: %v", err)
  119. }
  120. second, err := decodeSMSPDU(string(parts[1].payload))
  121. if err != nil {
  122. t.Fatalf("decode second part: %v", err)
  123. }
  124. if first.Text != strings.Repeat("A", 152) ||
  125. second.Text != "^"+strings.Repeat("B", 10) {
  126. t.Fatalf("split text = %q + %q", first.Text, second.Text)
  127. }
  128. }
  129. func TestPrepareMultipartUCS2DoesNotSplitSurrogatePair(t *testing.T) {
  130. const reference = 0x52
  131. text := strings.Repeat("你", 66) + "😀" + strings.Repeat("好", 4)
  132. parts, err := prepareSMSPartsWithReference("+12345", text, reference)
  133. if err != nil {
  134. t.Fatalf("prepare multipart UCS2: %v", err)
  135. }
  136. if len(parts) != 2 {
  137. t.Fatalf("parts = %d, want 2", len(parts))
  138. }
  139. first, err := decodeSMSPDU(string(parts[0].payload))
  140. if err != nil {
  141. t.Fatalf("decode first part: %v", err)
  142. }
  143. second, err := decodeSMSPDU(string(parts[1].payload))
  144. if err != nil {
  145. t.Fatalf("decode second part: %v", err)
  146. }
  147. if first.Text != strings.Repeat("你", 66) ||
  148. second.Text != "😀"+strings.Repeat("好", 4) {
  149. t.Fatalf("split text = %q + %q", first.Text, second.Text)
  150. }
  151. for index, message := range []SMSMessage{first, second} {
  152. if message.Concat == nil ||
  153. message.Concat.Reference != reference ||
  154. message.Concat.Total != 2 ||
  155. message.Concat.Sequence != index+1 {
  156. t.Fatalf("concat part %d = %#v", index+1, message.Concat)
  157. }
  158. }
  159. }
  160. func TestPrepareMultipartRejectsMoreThan255Parts(t *testing.T) {
  161. if _, err := prepareSMSPartsWithReference(
  162. "12345",
  163. strings.Repeat("A", 153*255+1),
  164. 1,
  165. ); !errors.Is(err, ErrSMSTooLong) {
  166. t.Fatalf("oversize multipart GSM-7 error = %v", err)
  167. }
  168. if _, err := prepareSMSPartsWithReference(
  169. "12345",
  170. strings.Repeat("你", 67*255+1),
  171. 1,
  172. ); !errors.Is(err, ErrSMSTooLong) {
  173. t.Fatalf("oversize multipart UCS2 error = %v", err)
  174. }
  175. }
  176. func TestDecodeDeliverPDUHandlesGSM7AndUCS2(t *testing.T) {
  177. gsm, err := decodeSMSPDU(
  178. "000405912143F500004210203040500005C82293F904",
  179. )
  180. if err != nil {
  181. t.Fatalf("decode GSM-7: %v", err)
  182. }
  183. if gsm.Direction != SMSDirectionReceived ||
  184. gsm.From != "+12345" ||
  185. gsm.Text != "HELLO" ||
  186. gsm.Encoding != SMSEncodingGSM7PDU {
  187. t.Fatalf("GSM message = %#v", gsm)
  188. }
  189. if gsm.ServiceCenterTimestamp == nil ||
  190. gsm.ServiceCenterTimestamp.Year() != 2024 ||
  191. gsm.ServiceCenterTimestamp.Month() != 1 ||
  192. gsm.ServiceCenterTimestamp.Day() != 2 ||
  193. gsm.ServiceCenterTimestamp.Hour() != 3 ||
  194. gsm.ServiceCenterTimestamp.Minute() != 4 ||
  195. gsm.ServiceCenterTimestamp.Second() != 5 {
  196. t.Fatalf("timestamp = %v", gsm.ServiceCenterTimestamp)
  197. }
  198. ucs2, err := decodeSMSPDU(
  199. "004405912143F50008421020304050000A0500037A02014F60597D",
  200. )
  201. if err != nil {
  202. t.Fatalf("decode UCS2: %v", err)
  203. }
  204. if ucs2.Text != "你好" ||
  205. ucs2.Encoding != SMSEncodingUCS2PDU ||
  206. ucs2.Concat == nil ||
  207. ucs2.Concat.Reference != 0x7a ||
  208. ucs2.Concat.Total != 2 ||
  209. ucs2.Concat.Sequence != 1 {
  210. t.Fatalf("UCS2 message = %#v", ucs2)
  211. }
  212. }
  213. func TestDecodeSubmitAndStatusReportPDU(t *testing.T) {
  214. submit, err := decodeSMSPDU("00010005912143F500000100")
  215. if err != nil {
  216. t.Fatalf("decode submit: %v", err)
  217. }
  218. if submit.Direction != SMSDirectionSubmitted ||
  219. submit.To != "+12345" ||
  220. submit.Text != "@" {
  221. t.Fatalf("submit = %#v", submit)
  222. }
  223. report, err := decodeSMSPDU(
  224. "00022A05912143F5421020304050004210203050500000",
  225. )
  226. if err != nil {
  227. t.Fatalf("decode status report: %v", err)
  228. }
  229. if report.Direction != SMSDirectionStatusReport ||
  230. report.To != "+12345" ||
  231. report.MessageReference == nil ||
  232. *report.MessageReference != 42 ||
  233. report.StatusCode == nil ||
  234. *report.StatusCode != 0 ||
  235. report.DeliveryStatus != "delivered" {
  236. t.Fatalf("status report = %#v", report)
  237. }
  238. }
  239. func TestParseCMGLPreservesUndecodableRecord(t *testing.T) {
  240. messages := parseCMGL(okResponse(
  241. "+CMGL: 9,0,,4",
  242. "NOT-A-PDU",
  243. ))
  244. if len(messages) != 1 ||
  245. messages[0].Index != 9 ||
  246. messages[0].RawPDU != "NOT-A-PDU" ||
  247. messages[0].DecodeError == "" {
  248. t.Fatalf("messages = %#v", messages)
  249. }
  250. }