github.com/line/line-bot-sdk-go/v7@v7.21.0/linebot/webhook_test.go (about)

     1  // Copyright 2016 LINE Corporation
     2  //
     3  // LINE Corporation licenses this file to you under the Apache License,
     4  // version 2.0 (the "License"); you may not use this file except in compliance
     5  // with the License. You may obtain a copy of the License at:
     6  //
     7  //   http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    11  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    12  // License for the specific language governing permissions and limitations
    13  // under the License.
    14  
    15  package linebot
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"crypto/hmac"
    21  	"crypto/sha256"
    22  	"crypto/tls"
    23  	"encoding/base64"
    24  	"encoding/json"
    25  	"io"
    26  	"net/http"
    27  	"net/http/httptest"
    28  	"reflect"
    29  	"strconv"
    30  	"testing"
    31  	"time"
    32  )
    33  
    34  var webhookTestRequestBody = `{
    35      "events": [
    36          {
    37              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
    38              "type": "message",
    39              "mode": "active",
    40              "timestamp": 1462629479859,
    41              "source": {
    42                  "type": "user",
    43                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
    44              },
    45  			"webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
    46              "deliveryContext": {
    47                  "isRedelivery": false
    48              },
    49              "message": {
    50                  "id": "325708",
    51                  "type": "text",
    52                  "text": "Hello, world"
    53              }
    54          },
    55          {
    56              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
    57              "type": "message",
    58              "mode": "active",
    59              "timestamp": 1462629479859,
    60              "source": {
    61                  "type": "group",
    62                  "groupId": "u206d25c2ea6bd87c17655609a1c37cb8",
    63                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
    64              },
    65              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
    66              "deliveryContext": {
    67                  "isRedelivery": true
    68              },
    69              "message": {
    70                  "id": "325708",
    71                  "type": "text",
    72                  "text": "Hello, world"
    73              }
    74          },
    75          {
    76              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
    77              "type": "message",
    78              "mode": "active",
    79              "timestamp": 1462629479859,
    80              "source": {
    81                  "type": "group",
    82                  "groupId": "u206d25c2ea6bd87c17655609a1c37cb8",
    83                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
    84              },
    85              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
    86              "deliveryContext": {
    87                  "isRedelivery": false
    88              },
    89              "message": {
    90                  "id": "325708",
    91                  "type": "text",
    92                  "text": "@openProfileUser Hello. @notOpenProfileUser Hello.",
    93                  "mention": {
    94                      "mentionees": [
    95                          {
    96                              "index": 0,
    97                              "length": 16,
    98                              "userId": "U0047556f2e40dba2456887320ba7c76d",
    99                              "type": "user"
   100                          },
   101                          {
   102                              "index": 24,
   103                              "length": 16,
   104                              "type": "user"
   105                          }
   106                      ]
   107                  }
   108              }
   109          },
   110          {
   111              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   112              "type": "message",
   113              "mode": "active",
   114              "timestamp": 1462629479859,
   115              "source": {
   116                  "type": "user",
   117                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
   118              },
   119              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   120              "deliveryContext": {
   121                  "isRedelivery": false
   122              },
   123              "message": {
   124                  "id": "325708",
   125                  "type": "image"
   126              }
   127          },
   128          {
   129              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTAA",
   130              "type": "message",
   131              "mode": "active",
   132              "timestamp": 1462629479859,
   133              "source": {
   134                  "type": "user",
   135                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
   136              },
   137              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   138              "deliveryContext": {
   139                  "isRedelivery": false
   140              },
   141              "message": {
   142                  "type": "image",
   143                  "id": "354718705033693861",
   144                  "contentProvider": {
   145                      "type": "line"
   146                  },
   147                  "imageSet": {
   148                      "id": "E005D41A7288F41B65593ED38FF6E9834B046AB36A37921A56BC236F13A91855",
   149                      "index": 2,
   150                      "total": 2
   151                  }
   152              }
   153          },
   154          {
   155              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   156              "type": "message",
   157              "mode": "active",
   158              "timestamp": 1462629479859,
   159              "source": {
   160                  "type": "user",
   161                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
   162              },
   163              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   164              "deliveryContext": {
   165                  "isRedelivery": false
   166              },
   167              "message": {
   168                  "type": "video",
   169                  "id": "325708",
   170                  "duration": 60000,
   171                  "contentProvider": {
   172                      "type": "external",
   173                      "originalContentUrl": "https://example.com/original.mp4",
   174                      "previewImageUrl": "https://example.com/preview.jpg"
   175                  }
   176              }
   177          },
   178          {
   179              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   180              "type": "message",
   181              "mode": "active",
   182              "timestamp": 1462629479859,
   183              "source": {
   184                  "type": "user",
   185                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
   186              },
   187              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   188              "deliveryContext": {
   189                  "isRedelivery": false
   190              },
   191              "message": {
   192                  "type": "audio",
   193                  "id": "325708",
   194                  "duration": 60000,
   195                  "contentProvider": {
   196                      "type": "line"
   197                  }
   198              }
   199          },
   200          {
   201              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   202              "type": "message",
   203              "mode": "active",
   204              "timestamp": 1462629479859,
   205              "source": {
   206                  "type": "user",
   207                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
   208              },
   209              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   210              "deliveryContext": {
   211                  "isRedelivery": false
   212              },
   213              "message": {
   214                  "id": "325708",
   215                  "type": "file",
   216                  "fileName": "file.txt",
   217                  "fileSize": 2138
   218              }
   219          },
   220          {
   221              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   222              "type": "message",
   223              "mode": "active",
   224              "timestamp": 1462629479859,
   225              "source": {
   226                  "type": "user",
   227                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
   228              },
   229              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   230              "deliveryContext": {
   231                  "isRedelivery": false
   232              },
   233              "message": {
   234                  "id": "325708",
   235                  "type": "location",
   236                  "title": "hello",
   237                  "address": "〒150-0002 東京都渋谷区渋谷2丁目21−1",
   238                  "latitude": 35.65910807942215,
   239                  "longitude": 139.70372892916203
   240              }
   241          },
   242          {
   243              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   244              "type": "message",
   245              "mode": "active",
   246              "timestamp": 1462629479859,
   247              "source": {
   248                  "type": "user",
   249                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
   250              },
   251              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   252              "deliveryContext": {
   253                  "isRedelivery": false
   254              },
   255              "message": {
   256                  "id": "325708",
   257                  "type": "sticker",
   258                  "packageId": "1",
   259                  "stickerId": "1",
   260                  "stickerResourceType": "STATIC"
   261              }
   262          },
   263          {
   264              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   265              "type": "message",
   266              "mode": "active",
   267              "timestamp": 1462629479859,
   268              "source": {
   269                  "type": "user",
   270                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
   271              },
   272              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   273              "deliveryContext": {
   274                  "isRedelivery": false
   275              },
   276              "message": {
   277                  "id": "325708",
   278                  "type": "sticker",
   279                  "packageId": "1",
   280                  "stickerId": "3",
   281                  "stickerResourceType": "ANIMATION_SOUND"
   282              }
   283          },
   284          {
   285              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   286              "type": "message",
   287              "mode": "active",
   288              "timestamp": 1462629479859,
   289              "source": {
   290                  "type": "user",
   291                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
   292              },
   293              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   294              "deliveryContext": {
   295                  "isRedelivery": false
   296              },
   297              "message": {
   298                  "id": "3257088",
   299                  "type": "sticker",
   300                  "packageId": "20",
   301                  "stickerId": "3",
   302                  "stickerResourceType": "MESSAGE",
   303  				"keywords": ["cony","sally","Staring","hi","whatsup","line","howdy","HEY","Peeking","wave","peek","Hello","yo","greetings"]
   304              }
   305          },
   306          {
   307              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   308              "type": "follow",
   309              "mode": "active",
   310              "timestamp": 1462629479859,
   311              "source": {
   312                  "type": "user",
   313                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
   314              },
   315              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   316              "deliveryContext": {
   317                  "isRedelivery": false
   318              }
   319          },
   320          {
   321              "type": "unfollow",
   322              "mode": "active",
   323              "timestamp": 1462629479859,
   324              "source": {
   325                  "type": "user",
   326                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
   327              },
   328              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   329              "deliveryContext": {
   330                  "isRedelivery": false
   331              }
   332          },
   333          {
   334              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   335              "type": "join",
   336              "mode": "active",
   337              "timestamp": 1462629479859,
   338              "source": {
   339                  "type": "group",
   340                  "groupId": "cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
   341              },
   342              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   343              "deliveryContext": {
   344                  "isRedelivery": false
   345              }
   346          },
   347          {
   348              "type": "leave",
   349              "mode": "active",
   350              "timestamp": 1462629479859,
   351              "source": {
   352                  "type": "group",
   353                  "groupId": "cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
   354              },
   355              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   356              "deliveryContext": {
   357                  "isRedelivery": false
   358              }
   359          },
   360          {
   361              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   362              "type": "postback",
   363              "mode": "active",
   364              "timestamp": 1462629479859,
   365              "source": {
   366                  "type": "user",
   367                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
   368              },
   369              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   370              "deliveryContext": {
   371                  "isRedelivery": false
   372              },
   373              "postback": {
   374                  "data": "action=buyItem&itemId=123123&color=red"
   375              }
   376          },
   377          {
   378              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   379              "type": "postback",
   380              "mode": "active",
   381              "timestamp": 1462629479859,
   382              "source": {
   383                  "type": "user",
   384                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
   385              },
   386              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   387              "deliveryContext": {
   388                  "isRedelivery": false
   389              },
   390              "postback": {
   391                  "data": "action=sel&only=date",
   392  				"params": {
   393  					"date": "2017-09-03"
   394  				}
   395              }
   396          },
   397          {
   398              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   399              "type": "postback",
   400              "mode": "active",
   401              "timestamp": 1462629479859,
   402              "source": {
   403                  "type": "user",
   404                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
   405              },
   406              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   407              "deliveryContext": {
   408                  "isRedelivery": false
   409              },
   410              "postback": {
   411                  "data": "action=sel&only=time",
   412  				"params": {
   413  					"time": "15:38"
   414  				}
   415              }
   416          },
   417          {
   418              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   419              "type": "postback",
   420              "mode": "active",
   421              "timestamp": 1462629479859,
   422              "source": {
   423                  "type": "user",
   424                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
   425              },
   426              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   427              "deliveryContext": {
   428                  "isRedelivery": false
   429              },
   430              "postback": {
   431                  "data": "action=sel",
   432  				"params": {
   433  					"datetime": "2017-09-03T15:38"
   434  				}
   435              }
   436          },
   437          {
   438              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   439              "type": "postback",
   440              "mode": "active",
   441              "timestamp": 1462629479859,
   442              "source": {
   443                  "type": "user",
   444                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
   445              },
   446              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   447              "deliveryContext": {
   448                  "isRedelivery": false
   449              },
   450              "postback": {
   451                  "data": "action=richmenu-changed-to-a",
   452  								"params": {
   453  									"newRichMenuAliasId": "richmenu-alias-a",
   454  									"status": "SUCCESS"
   455  								}
   456              }
   457          },
   458          {
   459              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   460              "type": "beacon",
   461              "mode": "active",
   462              "timestamp": 1462629479859,
   463              "source": {
   464                  "type": "user",
   465                  "userId": "U012345678901234567890123456789ab"
   466              },
   467              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   468              "deliveryContext": {
   469                  "isRedelivery": false
   470              },
   471              "beacon": {
   472                  "hwid":"374591320",
   473                  "type":"enter"
   474              }
   475          },
   476          {
   477              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   478              "type": "beacon",
   479              "mode": "active",
   480              "timestamp": 1462629479859,
   481              "source": {
   482                  "type": "user",
   483                  "userId": "U012345678901234567890123456789ab"
   484              },
   485              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   486              "deliveryContext": {
   487                  "isRedelivery": false
   488              },
   489              "beacon": {
   490                  "hwid":"374591320",
   491                  "type":"enter",
   492                  "dm":"1234567890abcdef"
   493              }
   494          },
   495          {
   496              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   497              "type": "beacon",
   498              "mode": "active",
   499              "timestamp": 1462629479859,
   500              "source": {
   501                  "type": "user",
   502                  "userId": "U012345678901234567890123456789ab"
   503              },
   504              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   505              "deliveryContext": {
   506                  "isRedelivery": false
   507              },
   508              "beacon": {
   509                  "hwid":"374591320",
   510                  "type":"stay",
   511                  "dm":"1234567890abcdef"
   512              }
   513          },
   514          {
   515            "type": "accountLink",
   516            "mode": "active",
   517            "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   518            "source": {
   519              "userId": "U012345678901234567890123456789ab",
   520              "type": "user"
   521            },
   522            "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   523            "deliveryContext": {
   524              "isRedelivery": false
   525            },
   526            "timestamp": 1462629479859,
   527            "link": {
   528              "result": "ok",
   529              "nonce": "xxxxxxxxxxxxxxx"
   530            }
   531          },
   532          {
   533            "replyToken": "0f3779fba3b349968c5d07db31eabf65",
   534            "type": "memberJoined",
   535            "mode": "active",
   536            "timestamp": 1462629479859,
   537            "source": {
   538              "type": "group",
   539              "groupId": "C4af498062901234567890123456789ab"
   540            },
   541            "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   542            "deliveryContext": {
   543              "isRedelivery": false
   544            },
   545            "joined": {
   546              "members": [
   547                {
   548                  "type": "user",
   549                  "userId": "U4af498062901234567890123456789ab"
   550                },
   551                {
   552                  "type": "user",
   553                  "userId": "U91eeaf62d901234567890123456789ab"
   554                }
   555              ]
   556            }
   557          },
   558          {
   559            "type": "memberLeft",
   560            "mode": "active",
   561            "timestamp": 1462629479960,
   562            "source": {
   563              "type": "group",
   564              "groupId": "C4af498062901234567890123456789ab"
   565            },
   566            "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   567            "deliveryContext": {
   568              "isRedelivery": false
   569            },
   570            "left": {
   571              "members": [
   572                {
   573                  "type": "user",
   574                  "userId": "U4af498062901234567890123456789ab"
   575                },
   576                {
   577                  "type": "user",
   578                  "userId": "U91eeaf62d901234567890123456789ab"
   579                }
   580              ]
   581  		  }
   582          },
   583          {
   584            "type": "things",
   585            "mode": "active",
   586            "timestamp": 1462629479859,
   587            "source": {
   588              "type": "user",
   589              "userId": "U91eeaf62d901234567890123456789ab"
   590            },
   591            "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   592            "deliveryContext": {
   593              "isRedelivery": false
   594            },
   595            "things": {
   596              "deviceId": "t2c449c9d1...",
   597              "type": "link"
   598            }
   599          },
   600          {
   601            "type": "things",
   602            "mode": "active",
   603            "timestamp": 1462629479859,
   604            "source": {
   605              "type": "user",
   606              "userId": "U91eeaf62d901234567890123456789ab"
   607            },
   608            "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   609            "deliveryContext": {
   610              "isRedelivery": false
   611            },
   612            "things": {
   613              "deviceId": "t2c449c9d1...",
   614              "type": "unlink"
   615            }
   616          },
   617          {
   618            "type": "things",
   619            "mode": "active",
   620            "timestamp": 1462629479859,
   621            "source": {
   622              "type": "user",
   623              "userId": "U91eeaf62d901234567890123456789ab"
   624            },
   625            "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   626            "deliveryContext": {
   627              "isRedelivery": false
   628            },
   629            "things": {
   630              "deviceId": "t016b8dc6...",
   631              "type": "scenarioResult",
   632              "result": {
   633                "scenarioId": "01DE9CH7H...",
   634                "revision": 3,
   635                "startTime": 1563511217095,
   636                "endTime": 1563511217097,
   637                "resultCode": "success",
   638                "bleNotificationPayload": "AQ==",
   639                "actionResults": [
   640                  {
   641                    "type": "binary",
   642                    "data": "/w=="
   643                  }
   644                ]
   645              }
   646            }
   647          },
   648          {
   649            "type":"things",
   650            "mode": "active",
   651            "replyToken":"f026a377...",
   652            "source":{
   653              "userId":"U91eeaf62d901234567890123456789ab",
   654              "type":"user"
   655            },
   656            "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   657            "deliveryContext": {
   658              "isRedelivery": false
   659            },
   660            "timestamp":1563511218376,
   661            "things":{
   662              "deviceId":"t016b8d...",
   663              "result":{
   664                "scenarioId":"01DE9CH7H...",
   665                "revision":3,
   666                "startTime":1563511217095,
   667                "endTime":1563511217097,
   668                "resultCode":"gatt_error",
   669                "errorReason":"c.l.h.D: No characteristic is found for the given UUID.",
   670                "actionResults":[
   671  
   672                ]
   673              },
   674              "type":"scenarioResult"
   675            }
   676          },
   677          {
   678              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   679              "type": "message",
   680              "mode": "standby",
   681              "timestamp": 1462629479859,
   682              "source": {
   683                  "type": "user",
   684                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
   685              },
   686              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   687              "deliveryContext": {
   688                  "isRedelivery": false
   689              },
   690              "message": {
   691                  "id": "325708",
   692                  "type": "text",
   693                  "text": "Stand by me"
   694              }
   695          },
   696          {
   697              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   698              "type": "message",
   699              "mode": "active",
   700              "timestamp": 1462629479859,
   701              "source": {
   702                  "type": "user",
   703                  "userId": "u206d25c2ea6bd87c17655609a1c37cb8"
   704              },
   705              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   706              "deliveryContext": {
   707                  "isRedelivery": false
   708              },
   709              "message": {
   710                  "id": "325708",
   711                  "type": "text",
   712                  "text": "Hello, world! (love)",
   713                  "emojis": [
   714                      {
   715                          "index": 14,
   716                          "length": 6,
   717                          "productId": "5ac1bfd5040ab15980c9b435",
   718                          "emojiId": "001"
   719                      }
   720                  ]
   721              }
   722          },
   723          {
   724              "type": "unsend",
   725              "mode": "active",
   726              "timestamp": 1462629479859,
   727              "source": {
   728                  "type": "group",
   729                  "groupId": "Ca56f94637c...",
   730                  "userId": "U4af4980629..."
   731              },
   732              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   733              "deliveryContext": {
   734                  "isRedelivery": false
   735              },
   736              "unsend": {
   737                  "messageId": "325708"
   738              }
   739          },
   740          {
   741              "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   742              "type": "videoPlayComplete",
   743              "timestamp": 1462629479859,
   744              "mode": "active",
   745              "source": {
   746                  "type": "user",
   747                  "userId": "U4af4980629..."
   748              },
   749              "webhookEventId": "01FZ74ASS536FW97EX38NKCZQK",
   750              "deliveryContext": {
   751                  "isRedelivery": false
   752              },
   753              "videoPlayComplete": {
   754                  "trackingId": "track_id"
   755              }
   756          },
   757  		{
   758  			"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   759  			"type": "message",
   760  			"mode": "active",
   761  			"timestamp": 1462629479859,
   762  			"source": {
   763  				"type": "group",
   764  				"groupId": "Ca56f94637c...",
   765  				"userId": "U4af4980629..."
   766  			},
   767  			"webhookEventId": "01FZ74A0TDDPYRVKNK77XKC3ZR",
   768  			"deliveryContext": {
   769  				"isRedelivery": false
   770  			},
   771  			"message": {
   772  				"id": "444573844083572737",
   773  				"type": "text",
   774  				"text": "@All @example Good Morning!! (love)",
   775  				"emojis": [
   776  					{
   777  						"index": 29,
   778  						"length": 6,
   779  						"productId": "5ac1bfd5040ab15980c9b435",
   780  						"emojiId": "001"
   781  					}
   782  				],
   783  				"mention": {
   784  					"mentionees": [
   785  						{
   786  							"index": 0,
   787  							"length": 4,
   788  							"type": "all"
   789  						},
   790  						{
   791  							"index": 5,
   792  							"length": 8,
   793  							"userId": "U49585cd0d5...",
   794  							"type": "user"
   795  						}
   796  					]
   797  				}
   798  			}
   799  		}
   800      ]
   801  }
   802  `
   803  
   804  var webhookTestWantEvents = []*Event{
   805  	{
   806  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   807  		Type:       EventTypeMessage,
   808  		Mode:       EventModeActive,
   809  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
   810  		Source: &EventSource{
   811  			Type:   EventSourceTypeUser,
   812  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
   813  		},
   814  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
   815  		DeliveryContext: DeliveryContext{
   816  			IsRedelivery: false,
   817  		},
   818  		Message: &TextMessage{
   819  			ID:   "325708",
   820  			Text: "Hello, world",
   821  		},
   822  	},
   823  	{
   824  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   825  		Type:       EventTypeMessage,
   826  		Mode:       EventModeActive,
   827  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
   828  		Source: &EventSource{
   829  			Type:    EventSourceTypeGroup,
   830  			UserID:  "u206d25c2ea6bd87c17655609a1c37cb8",
   831  			GroupID: "u206d25c2ea6bd87c17655609a1c37cb8",
   832  		},
   833  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
   834  		DeliveryContext: DeliveryContext{
   835  			IsRedelivery: true,
   836  		},
   837  		Message: &TextMessage{
   838  			ID:   "325708",
   839  			Text: "Hello, world",
   840  		},
   841  	},
   842  	{
   843  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   844  		Type:       EventTypeMessage,
   845  		Mode:       EventModeActive,
   846  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
   847  		Source: &EventSource{
   848  			Type:    EventSourceTypeGroup,
   849  			UserID:  "u206d25c2ea6bd87c17655609a1c37cb8",
   850  			GroupID: "u206d25c2ea6bd87c17655609a1c37cb8",
   851  		},
   852  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
   853  		DeliveryContext: DeliveryContext{
   854  			IsRedelivery: false,
   855  		},
   856  		Message: &TextMessage{
   857  			ID:   "325708",
   858  			Text: "@openProfileUser Hello. @notOpenProfileUser Hello.",
   859  			Mention: &Mention{
   860  				Mentionees: []*Mentionee{
   861  					{
   862  						Index:  0,
   863  						Length: 16,
   864  						Type:   MentionedTargetTypeUser,
   865  						UserID: "U0047556f2e40dba2456887320ba7c76d",
   866  					},
   867  					{
   868  						Index:  24,
   869  						Length: 16,
   870  						Type:   MentionedTargetTypeUser,
   871  					},
   872  				},
   873  			},
   874  		},
   875  	},
   876  	{
   877  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   878  		Type:       EventTypeMessage,
   879  		Mode:       EventModeActive,
   880  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
   881  		Source: &EventSource{
   882  			Type:   EventSourceTypeUser,
   883  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
   884  		},
   885  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
   886  		DeliveryContext: DeliveryContext{
   887  			IsRedelivery: false,
   888  		},
   889  		Message: &ImageMessage{
   890  			ID: "325708",
   891  		},
   892  	},
   893  	{
   894  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTAA",
   895  		Type:       EventTypeMessage,
   896  		Mode:       EventModeActive,
   897  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
   898  		Source: &EventSource{
   899  			Type:   EventSourceTypeUser,
   900  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
   901  		},
   902  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
   903  		DeliveryContext: DeliveryContext{
   904  			IsRedelivery: false,
   905  		},
   906  		Message: &ImageMessage{
   907  			ID: "354718705033693861",
   908  			ContentProvider: &ContentProvider{
   909  				Type: ContentProviderTypeLINE,
   910  			},
   911  			ImageSet: &ImageSet{
   912  				ID:    "E005D41A7288F41B65593ED38FF6E9834B046AB36A37921A56BC236F13A91855",
   913  				Index: 2,
   914  				Total: 2,
   915  			},
   916  		},
   917  	},
   918  	{
   919  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   920  		Type:       EventTypeMessage,
   921  		Mode:       EventModeActive,
   922  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
   923  		Source: &EventSource{
   924  			Type:   EventSourceTypeUser,
   925  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
   926  		},
   927  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
   928  		DeliveryContext: DeliveryContext{
   929  			IsRedelivery: false,
   930  		},
   931  		Message: &VideoMessage{
   932  			ID: "325708",
   933  			ContentProvider: &ContentProvider{
   934  				Type:               ContentProviderTypeExternal,
   935  				OriginalContentURL: "https://example.com/original.mp4",
   936  				PreviewImageURL:    "https://example.com/preview.jpg",
   937  			},
   938  			Duration: 60000,
   939  		},
   940  	},
   941  	{
   942  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   943  		Type:       EventTypeMessage,
   944  		Mode:       EventModeActive,
   945  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
   946  		Source: &EventSource{
   947  			Type:   EventSourceTypeUser,
   948  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
   949  		},
   950  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
   951  		DeliveryContext: DeliveryContext{
   952  			IsRedelivery: false,
   953  		},
   954  		Message: &AudioMessage{
   955  			ID: "325708",
   956  			ContentProvider: &ContentProvider{
   957  				Type: ContentProviderTypeLINE,
   958  			},
   959  			Duration: 60000,
   960  		},
   961  	},
   962  	{
   963  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   964  		Type:       EventTypeMessage,
   965  		Mode:       EventModeActive,
   966  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
   967  		Source: &EventSource{
   968  			Type:   EventSourceTypeUser,
   969  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
   970  		},
   971  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
   972  		DeliveryContext: DeliveryContext{
   973  			IsRedelivery: false,
   974  		},
   975  		Message: &FileMessage{
   976  			ID:       "325708",
   977  			FileName: "file.txt",
   978  			FileSize: 2138,
   979  		},
   980  	},
   981  	{
   982  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
   983  		Type:       EventTypeMessage,
   984  		Mode:       EventModeActive,
   985  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
   986  		Source: &EventSource{
   987  			Type:   EventSourceTypeUser,
   988  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
   989  		},
   990  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
   991  		DeliveryContext: DeliveryContext{
   992  			IsRedelivery: false,
   993  		},
   994  		Message: &LocationMessage{
   995  			ID:        "325708",
   996  			Title:     "hello",
   997  			Address:   "〒150-0002 東京都渋谷区渋谷2丁目21−1",
   998  			Latitude:  35.65910807942215,
   999  			Longitude: 139.70372892916203,
  1000  		},
  1001  	},
  1002  	{
  1003  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  1004  		Type:       EventTypeMessage,
  1005  		Mode:       EventModeActive,
  1006  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1007  		Source: &EventSource{
  1008  			Type:   EventSourceTypeUser,
  1009  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
  1010  		},
  1011  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1012  		DeliveryContext: DeliveryContext{
  1013  			IsRedelivery: false,
  1014  		},
  1015  		Message: &StickerMessage{
  1016  			ID:                  "325708",
  1017  			PackageID:           "1",
  1018  			StickerID:           "1",
  1019  			StickerResourceType: StickerResourceTypeStatic,
  1020  		},
  1021  	},
  1022  	{
  1023  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  1024  		Type:       EventTypeMessage,
  1025  		Mode:       EventModeActive,
  1026  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1027  		Source: &EventSource{
  1028  			Type:   EventSourceTypeUser,
  1029  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
  1030  		},
  1031  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1032  		DeliveryContext: DeliveryContext{
  1033  			IsRedelivery: false,
  1034  		},
  1035  		Message: &StickerMessage{
  1036  			ID:                  "325708",
  1037  			PackageID:           "1",
  1038  			StickerID:           "3",
  1039  			StickerResourceType: StickerResourceTypeAnimationSound,
  1040  		},
  1041  	},
  1042  	{
  1043  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  1044  		Type:       EventTypeMessage,
  1045  		Mode:       EventModeActive,
  1046  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1047  		Source: &EventSource{
  1048  			Type:   EventSourceTypeUser,
  1049  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
  1050  		},
  1051  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1052  		DeliveryContext: DeliveryContext{
  1053  			IsRedelivery: false,
  1054  		},
  1055  		Message: &StickerMessage{
  1056  			ID:                  "3257088",
  1057  			PackageID:           "20",
  1058  			StickerID:           "3",
  1059  			StickerResourceType: StickerResourceTypePerStickerText,
  1060  			Keywords:            []string{"cony", "sally", "Staring", "hi", "whatsup", "line", "howdy", "HEY", "Peeking", "wave", "peek", "Hello", "yo", "greetings"},
  1061  		},
  1062  	},
  1063  	{
  1064  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  1065  		Type:       EventTypeFollow,
  1066  		Mode:       EventModeActive,
  1067  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1068  		Source: &EventSource{
  1069  			Type:   EventSourceTypeUser,
  1070  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
  1071  		},
  1072  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1073  		DeliveryContext: DeliveryContext{
  1074  			IsRedelivery: false,
  1075  		},
  1076  	},
  1077  	{
  1078  		Type:      EventTypeUnfollow,
  1079  		Mode:      EventModeActive,
  1080  		Timestamp: time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1081  		Source: &EventSource{
  1082  			Type:   EventSourceTypeUser,
  1083  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
  1084  		},
  1085  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1086  		DeliveryContext: DeliveryContext{
  1087  			IsRedelivery: false,
  1088  		},
  1089  	},
  1090  	{
  1091  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  1092  		Type:       EventTypeJoin,
  1093  		Mode:       EventModeActive,
  1094  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1095  		Source: &EventSource{
  1096  			Type:    EventSourceTypeGroup,
  1097  			GroupID: "cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  1098  		},
  1099  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1100  		DeliveryContext: DeliveryContext{
  1101  			IsRedelivery: false,
  1102  		},
  1103  	},
  1104  	{
  1105  		Type:      EventTypeLeave,
  1106  		Mode:      EventModeActive,
  1107  		Timestamp: time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1108  		Source: &EventSource{
  1109  			Type:    EventSourceTypeGroup,
  1110  			GroupID: "cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  1111  		},
  1112  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1113  		DeliveryContext: DeliveryContext{
  1114  			IsRedelivery: false,
  1115  		},
  1116  	},
  1117  	{
  1118  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  1119  		Type:       EventTypePostback,
  1120  		Mode:       EventModeActive,
  1121  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1122  		Source: &EventSource{
  1123  			Type:   EventSourceTypeUser,
  1124  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
  1125  		},
  1126  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1127  		DeliveryContext: DeliveryContext{
  1128  			IsRedelivery: false,
  1129  		},
  1130  		Postback: &Postback{
  1131  			Data: "action=buyItem&itemId=123123&color=red",
  1132  		},
  1133  	},
  1134  	{
  1135  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  1136  		Type:       EventTypePostback,
  1137  		Mode:       EventModeActive,
  1138  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1139  		Source: &EventSource{
  1140  			Type:   EventSourceTypeUser,
  1141  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
  1142  		},
  1143  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1144  		DeliveryContext: DeliveryContext{
  1145  			IsRedelivery: false,
  1146  		},
  1147  		Postback: &Postback{
  1148  			Data: "action=sel&only=date",
  1149  			Params: &Params{
  1150  				Date: "2017-09-03",
  1151  			},
  1152  		},
  1153  	},
  1154  	{
  1155  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  1156  		Type:       EventTypePostback,
  1157  		Mode:       EventModeActive,
  1158  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1159  		Source: &EventSource{
  1160  			Type:   EventSourceTypeUser,
  1161  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
  1162  		},
  1163  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1164  		DeliveryContext: DeliveryContext{
  1165  			IsRedelivery: false,
  1166  		},
  1167  		Postback: &Postback{
  1168  			Data: "action=sel&only=time",
  1169  			Params: &Params{
  1170  				Time: "15:38",
  1171  			},
  1172  		},
  1173  	},
  1174  	{
  1175  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  1176  		Type:       EventTypePostback,
  1177  		Mode:       EventModeActive,
  1178  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1179  		Source: &EventSource{
  1180  			Type:   EventSourceTypeUser,
  1181  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
  1182  		},
  1183  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1184  		DeliveryContext: DeliveryContext{
  1185  			IsRedelivery: false,
  1186  		},
  1187  		Postback: &Postback{
  1188  			Data: "action=sel",
  1189  			Params: &Params{
  1190  				Datetime: "2017-09-03T15:38",
  1191  			},
  1192  		},
  1193  	},
  1194  	{
  1195  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  1196  		Type:       EventTypePostback,
  1197  		Mode:       EventModeActive,
  1198  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1199  		Source: &EventSource{
  1200  			Type:   EventSourceTypeUser,
  1201  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
  1202  		},
  1203  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1204  		DeliveryContext: DeliveryContext{
  1205  			IsRedelivery: false,
  1206  		},
  1207  		Postback: &Postback{
  1208  			Data: "action=richmenu-changed-to-a",
  1209  			Params: &Params{
  1210  				NewRichMenuAliasID: "richmenu-alias-a",
  1211  				Status:             "SUCCESS",
  1212  			},
  1213  		},
  1214  	},
  1215  	{
  1216  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  1217  		Type:       EventTypeBeacon,
  1218  		Mode:       EventModeActive,
  1219  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1220  		Source: &EventSource{
  1221  			Type:   EventSourceTypeUser,
  1222  			UserID: "U012345678901234567890123456789ab",
  1223  		},
  1224  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1225  		DeliveryContext: DeliveryContext{
  1226  			IsRedelivery: false,
  1227  		},
  1228  		Beacon: &Beacon{
  1229  			Hwid:          "374591320",
  1230  			Type:          BeaconEventTypeEnter,
  1231  			DeviceMessage: []byte{},
  1232  		},
  1233  	},
  1234  	{
  1235  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  1236  		Type:       EventTypeBeacon,
  1237  		Mode:       EventModeActive,
  1238  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1239  		Source: &EventSource{
  1240  			Type:   EventSourceTypeUser,
  1241  			UserID: "U012345678901234567890123456789ab",
  1242  		},
  1243  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1244  		DeliveryContext: DeliveryContext{
  1245  			IsRedelivery: false,
  1246  		},
  1247  		Beacon: &Beacon{
  1248  			Hwid:          "374591320",
  1249  			Type:          BeaconEventTypeEnter,
  1250  			DeviceMessage: []byte{0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef},
  1251  		},
  1252  	},
  1253  	{
  1254  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  1255  		Type:       EventTypeBeacon,
  1256  		Mode:       EventModeActive,
  1257  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1258  		Source: &EventSource{
  1259  			Type:   EventSourceTypeUser,
  1260  			UserID: "U012345678901234567890123456789ab",
  1261  		},
  1262  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1263  		DeliveryContext: DeliveryContext{
  1264  			IsRedelivery: false,
  1265  		},
  1266  		Beacon: &Beacon{
  1267  			Hwid:          "374591320",
  1268  			Type:          BeaconEventTypeStay,
  1269  			DeviceMessage: []byte{0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef},
  1270  		},
  1271  	},
  1272  	{
  1273  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  1274  		Type:       EventTypeAccountLink,
  1275  		Mode:       EventModeActive,
  1276  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1277  		Source: &EventSource{
  1278  			Type:   EventSourceTypeUser,
  1279  			UserID: "U012345678901234567890123456789ab",
  1280  		},
  1281  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1282  		DeliveryContext: DeliveryContext{
  1283  			IsRedelivery: false,
  1284  		},
  1285  		AccountLink: &AccountLink{
  1286  			Result: AccountLinkResultOK,
  1287  			Nonce:  "xxxxxxxxxxxxxxx",
  1288  		},
  1289  	},
  1290  	{
  1291  		ReplyToken: "0f3779fba3b349968c5d07db31eabf65",
  1292  		Type:       EventTypeMemberJoined,
  1293  		Mode:       EventModeActive,
  1294  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1295  		Source: &EventSource{
  1296  			Type:    EventSourceTypeGroup,
  1297  			GroupID: "C4af498062901234567890123456789ab",
  1298  		},
  1299  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1300  		DeliveryContext: DeliveryContext{
  1301  			IsRedelivery: false,
  1302  		},
  1303  		Members: []*EventSource{
  1304  			{
  1305  				Type:   EventSourceTypeUser,
  1306  				UserID: "U4af498062901234567890123456789ab",
  1307  			},
  1308  			{
  1309  				Type:   EventSourceTypeUser,
  1310  				UserID: "U91eeaf62d901234567890123456789ab",
  1311  			},
  1312  		},
  1313  	},
  1314  	{
  1315  		Type:      EventTypeMemberLeft,
  1316  		Mode:      EventModeActive,
  1317  		Timestamp: time.Date(2016, time.May, 7, 13, 57, 59, int(960*time.Millisecond), time.UTC),
  1318  		Source: &EventSource{
  1319  			Type:    EventSourceTypeGroup,
  1320  			GroupID: "C4af498062901234567890123456789ab",
  1321  		},
  1322  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1323  		DeliveryContext: DeliveryContext{
  1324  			IsRedelivery: false,
  1325  		},
  1326  		Members: []*EventSource{
  1327  			{
  1328  				Type:   EventSourceTypeUser,
  1329  				UserID: "U4af498062901234567890123456789ab",
  1330  			},
  1331  			{
  1332  				Type:   EventSourceTypeUser,
  1333  				UserID: "U91eeaf62d901234567890123456789ab",
  1334  			},
  1335  		},
  1336  	},
  1337  	{
  1338  		Type:      EventTypeThings,
  1339  		Mode:      EventModeActive,
  1340  		Timestamp: time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1341  		Source: &EventSource{
  1342  			Type:   EventSourceTypeUser,
  1343  			UserID: "U91eeaf62d901234567890123456789ab",
  1344  		},
  1345  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1346  		DeliveryContext: DeliveryContext{
  1347  			IsRedelivery: false,
  1348  		},
  1349  		Things: &Things{
  1350  			DeviceID: `t2c449c9d1...`,
  1351  			Type:     `link`,
  1352  		},
  1353  	},
  1354  	{
  1355  		Type:      EventTypeThings,
  1356  		Mode:      EventModeActive,
  1357  		Timestamp: time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1358  		Source: &EventSource{
  1359  			Type:   EventSourceTypeUser,
  1360  			UserID: "U91eeaf62d901234567890123456789ab",
  1361  		},
  1362  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1363  		DeliveryContext: DeliveryContext{
  1364  			IsRedelivery: false,
  1365  		},
  1366  		Things: &Things{
  1367  			DeviceID: `t2c449c9d1...`,
  1368  			Type:     `unlink`,
  1369  		},
  1370  	},
  1371  	{
  1372  		Type:      EventTypeThings,
  1373  		Mode:      EventModeActive,
  1374  		Timestamp: time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1375  		Source: &EventSource{
  1376  			Type:   EventSourceTypeUser,
  1377  			UserID: "U91eeaf62d901234567890123456789ab",
  1378  		},
  1379  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1380  		DeliveryContext: DeliveryContext{
  1381  			IsRedelivery: false,
  1382  		},
  1383  		Things: &Things{
  1384  			DeviceID: "t016b8dc6...",
  1385  			Type:     "scenarioResult",
  1386  			Result: &ThingsResult{
  1387  				ScenarioID:             "01DE9CH7H...",
  1388  				Revision:               3,
  1389  				StartTime:              1563511217095,
  1390  				EndTime:                1563511217097,
  1391  				ResultCode:             ThingsResultCodeSuccess,
  1392  				BLENotificationPayload: []byte(`AQ==`),
  1393  				ActionResults: []*ThingsActionResult{
  1394  					{
  1395  						Type: ThingsActionResultTypeBinary,
  1396  						Data: []byte(`/w==`),
  1397  					},
  1398  				},
  1399  			},
  1400  		},
  1401  	},
  1402  	{
  1403  		Type:       EventTypeThings,
  1404  		Mode:       EventModeActive,
  1405  		ReplyToken: "f026a377...",
  1406  		Timestamp:  time.Date(2019, time.July, 19, 4, 40, 18, int(376*time.Millisecond), time.UTC),
  1407  		Source: &EventSource{
  1408  			Type:   EventSourceTypeUser,
  1409  			UserID: "U91eeaf62d901234567890123456789ab",
  1410  		},
  1411  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1412  		DeliveryContext: DeliveryContext{
  1413  			IsRedelivery: false,
  1414  		},
  1415  		Things: &Things{
  1416  			DeviceID: "t016b8d...",
  1417  			Type:     "scenarioResult",
  1418  			Result: &ThingsResult{
  1419  				ScenarioID:             "01DE9CH7H...",
  1420  				Revision:               3,
  1421  				StartTime:              1563511217095,
  1422  				EndTime:                1563511217097,
  1423  				ResultCode:             ThingsResultCodeGattError,
  1424  				ErrorReason:            "c.l.h.D: No characteristic is found for the given UUID.",
  1425  				ActionResults:          []*ThingsActionResult{},
  1426  				BLENotificationPayload: []byte{},
  1427  			},
  1428  		},
  1429  	},
  1430  	{
  1431  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  1432  		Type:       EventTypeMessage,
  1433  		Mode:       EventModeStandby,
  1434  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1435  		Source: &EventSource{
  1436  			Type:   EventSourceTypeUser,
  1437  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
  1438  		},
  1439  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1440  		DeliveryContext: DeliveryContext{
  1441  			IsRedelivery: false,
  1442  		},
  1443  		Message: &TextMessage{
  1444  			ID:   "325708",
  1445  			Text: "Stand by me",
  1446  		},
  1447  	},
  1448  	{
  1449  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  1450  		Type:       EventTypeMessage,
  1451  		Mode:       EventModeActive,
  1452  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1453  		Source: &EventSource{
  1454  			Type:   EventSourceTypeUser,
  1455  			UserID: "u206d25c2ea6bd87c17655609a1c37cb8",
  1456  		},
  1457  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1458  		DeliveryContext: DeliveryContext{
  1459  			IsRedelivery: false,
  1460  		},
  1461  		Message: &TextMessage{
  1462  			ID:   "325708",
  1463  			Text: "Hello, world! (love)",
  1464  			Emojis: []*Emoji{
  1465  				{Index: 14, Length: 6, ProductID: "5ac1bfd5040ab15980c9b435", EmojiID: "001"},
  1466  			},
  1467  		},
  1468  	},
  1469  	{
  1470  		Type:      EventTypeUnsend,
  1471  		Mode:      EventModeActive,
  1472  		Timestamp: time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1473  		Source: &EventSource{
  1474  			Type:    EventSourceTypeGroup,
  1475  			GroupID: "Ca56f94637c...",
  1476  			UserID:  "U4af4980629...",
  1477  		},
  1478  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1479  		DeliveryContext: DeliveryContext{
  1480  			IsRedelivery: false,
  1481  		},
  1482  		Unsend: &Unsend{
  1483  			MessageID: "325708",
  1484  		},
  1485  	},
  1486  	{
  1487  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  1488  		Type:       EventTypeVideoPlayComplete,
  1489  		Mode:       EventModeActive,
  1490  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1491  		Source: &EventSource{
  1492  			Type:   EventSourceTypeUser,
  1493  			UserID: "U4af4980629...",
  1494  		},
  1495  		WebhookEventID: "01FZ74ASS536FW97EX38NKCZQK",
  1496  		DeliveryContext: DeliveryContext{
  1497  			IsRedelivery: false,
  1498  		},
  1499  		VideoPlayComplete: &VideoPlayComplete{
  1500  			TrackingID: "track_id",
  1501  		},
  1502  	},
  1503  	{
  1504  		ReplyToken: "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  1505  		Type:       EventTypeMessage,
  1506  		Mode:       EventModeActive,
  1507  		Timestamp:  time.Date(2016, time.May, 7, 13, 57, 59, int(859*time.Millisecond), time.UTC),
  1508  		Source: &EventSource{
  1509  			Type:    EventSourceTypeGroup,
  1510  			GroupID: "Ca56f94637c...",
  1511  			UserID:  "U4af4980629...",
  1512  		},
  1513  		WebhookEventID: "01FZ74A0TDDPYRVKNK77XKC3ZR",
  1514  		DeliveryContext: DeliveryContext{
  1515  			IsRedelivery: false,
  1516  		},
  1517  		Message: &TextMessage{
  1518  			ID:   "444573844083572737",
  1519  			Text: "@All @example Good Morning!! (love)",
  1520  			Emojis: []*Emoji{
  1521  				{Index: 29, Length: 6, ProductID: "5ac1bfd5040ab15980c9b435", EmojiID: "001"},
  1522  			},
  1523  			Mention: &Mention{
  1524  				Mentionees: []*Mentionee{
  1525  					{
  1526  						Index:  0,
  1527  						Length: 4,
  1528  						Type:   MentionedTargetTypeAll,
  1529  					},
  1530  					{
  1531  						Index:  5,
  1532  						Length: 8,
  1533  						Type:   MentionedTargetTypeUser,
  1534  						UserID: "U49585cd0d5...",
  1535  					},
  1536  				},
  1537  			},
  1538  		},
  1539  	},
  1540  }
  1541  
  1542  func TestParseRequest(t *testing.T) {
  1543  	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1544  		client, err := New("testsecret", "testtoken")
  1545  		if err != nil {
  1546  			t.Error(err)
  1547  		}
  1548  		gotEvents, err := client.ParseRequest(r)
  1549  		if err != nil {
  1550  			if err == ErrInvalidSignature {
  1551  				w.WriteHeader(400)
  1552  			} else {
  1553  				w.WriteHeader(500)
  1554  				t.Error(err)
  1555  			}
  1556  			return
  1557  		}
  1558  		if len(gotEvents) != len(webhookTestWantEvents) {
  1559  			t.Errorf("Event length %d; want %d", len(gotEvents), len(webhookTestWantEvents))
  1560  		}
  1561  		for i, got := range gotEvents {
  1562  			want := webhookTestWantEvents[i]
  1563  			if !reflect.DeepEqual(got, want) {
  1564  				t.Errorf("Event %d %v; want %v", i, got, want)
  1565  				gota := got
  1566  				if !reflect.DeepEqual(
  1567  					gota.Things.Result.BLENotificationPayload,
  1568  					want.Things.Result.BLENotificationPayload,
  1569  				) {
  1570  					t.Log("this")
  1571  					t.Log(gota.Things.Result.BLENotificationPayload == nil)
  1572  					t.Log(want.Things.Result.BLENotificationPayload == nil)
  1573  				}
  1574  			}
  1575  		}
  1576  	}))
  1577  	defer server.Close()
  1578  	httpClient := &http.Client{
  1579  		Transport: &http.Transport{
  1580  			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
  1581  		},
  1582  	}
  1583  
  1584  	// invalid signature
  1585  	{
  1586  		body := []byte(webhookTestRequestBody)
  1587  		req, err := http.NewRequest("POST", server.URL, bytes.NewReader(body))
  1588  		if err != nil {
  1589  			t.Fatal(err)
  1590  		}
  1591  		req.Header.Set("X-Line-Signature", "invalidSignature")
  1592  		res, err := httpClient.Do(req)
  1593  		if err != nil {
  1594  			t.Fatal(err)
  1595  		}
  1596  		if res.StatusCode != 400 {
  1597  			t.Errorf("StatusCode %d; want %d", res.StatusCode, 400)
  1598  		}
  1599  	}
  1600  
  1601  	// valid signature
  1602  	{
  1603  		body := []byte(webhookTestRequestBody)
  1604  		req, err := http.NewRequest("POST", server.URL, bytes.NewReader(body))
  1605  		if err != nil {
  1606  			t.Fatal(err)
  1607  		}
  1608  		// generate signature
  1609  		mac := hmac.New(sha256.New, []byte("testsecret"))
  1610  		mac.Write(body)
  1611  
  1612  		req.Header.Set("X-Line-Signature", base64.StdEncoding.EncodeToString(mac.Sum(nil)))
  1613  		res, err := httpClient.Do(req)
  1614  		if err != nil {
  1615  			t.Fatal(err)
  1616  		}
  1617  		if res == nil {
  1618  			t.Fatal("response is nil")
  1619  		}
  1620  		if res.StatusCode != http.StatusOK {
  1621  			t.Errorf("status: %d", res.StatusCode)
  1622  		}
  1623  	}
  1624  }
  1625  
  1626  func TestEventMarshaling(t *testing.T) {
  1627  	testCases := &struct {
  1628  		Events []map[string]interface{} `json:"events"`
  1629  	}{}
  1630  	err := json.Unmarshal([]byte(webhookTestRequestBody), testCases)
  1631  	if err != nil {
  1632  		t.Fatal(err)
  1633  	}
  1634  	for i, want := range testCases.Events {
  1635  		t.Run(strconv.Itoa(i), func(t *testing.T) {
  1636  			gotJSON, err := json.Marshal(webhookTestWantEvents[i])
  1637  			if err != nil {
  1638  				t.Error(err)
  1639  				return
  1640  			}
  1641  			got := map[string]interface{}{}
  1642  			err = json.Unmarshal(gotJSON, &got)
  1643  			if err != nil {
  1644  				t.Error(err)
  1645  				return
  1646  			}
  1647  			if !reflect.DeepEqual(got, want) {
  1648  				t.Errorf("Event marshal %v; want %v", got, want)
  1649  			}
  1650  		})
  1651  	}
  1652  }
  1653  
  1654  func BenchmarkParseRequest(b *testing.B) {
  1655  	body := []byte(webhookTestRequestBody)
  1656  	client, err := New("testsecret", "testtoken")
  1657  	if err != nil {
  1658  		b.Fatal(err)
  1659  	}
  1660  	mac := hmac.New(sha256.New, []byte("testsecret"))
  1661  	mac.Write(body)
  1662  	sign := base64.StdEncoding.EncodeToString(mac.Sum(nil))
  1663  	b.ResetTimer()
  1664  
  1665  	for i := 0; i < b.N; i++ {
  1666  		req, _ := http.NewRequest("POST", "", bytes.NewReader(body))
  1667  		req.Header.Set("X-Line-Signature", sign)
  1668  		client.ParseRequest(req)
  1669  	}
  1670  }
  1671  
  1672  func TestGetWebhookInfo(t *testing.T) {
  1673  	type want struct {
  1674  		URLPath     string
  1675  		RequestBody []byte
  1676  		Response    *WebhookInfoResponse
  1677  		Error       error
  1678  	}
  1679  	testCases := []struct {
  1680  		Label        string
  1681  		ResponseCode int
  1682  		Response     []byte
  1683  		Want         want
  1684  	}{
  1685  		{
  1686  			Label:        "Success",
  1687  			ResponseCode: 200,
  1688  			Response:     []byte(`{"endpoint":"https://example.herokuapp.com/test","active":true}`),
  1689  			Want: want{
  1690  				URLPath:     APIEndpointGetWebhookInfo,
  1691  				RequestBody: []byte(""),
  1692  				Response: &WebhookInfoResponse{
  1693  					Endpoint: "https://example.herokuapp.com/test",
  1694  					Active:   true,
  1695  				},
  1696  			},
  1697  		},
  1698  		{
  1699  			Label:        "Internal server error",
  1700  			ResponseCode: 500,
  1701  			Response:     []byte("500 Internal server error"),
  1702  			Want: want{
  1703  				URLPath:     APIEndpointGetWebhookInfo,
  1704  				RequestBody: []byte(""),
  1705  				Error: &APIError{
  1706  					Code: 500,
  1707  				},
  1708  			},
  1709  		},
  1710  		{
  1711  			Label:        "Invalid channelAccessToken error",
  1712  			ResponseCode: 401,
  1713  			Response:     []byte(`{"message":"Authentication failed due to the following reason: invalid token. Confirm that the access token in the authorization header is valid."}`),
  1714  			Want: want{
  1715  				URLPath:     APIEndpointGetWebhookInfo,
  1716  				RequestBody: []byte(""),
  1717  				Error: &APIError{
  1718  					Code: 401,
  1719  					Response: &ErrorResponse{
  1720  						Message: "Authentication failed due to the following reason: invalid token. Confirm that the access token in the authorization header is valid.",
  1721  					},
  1722  				},
  1723  			},
  1724  		},
  1725  	}
  1726  
  1727  	var currentTestIdx int
  1728  	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1729  		defer r.Body.Close()
  1730  		tc := testCases[currentTestIdx]
  1731  		if r.Method != http.MethodGet {
  1732  			t.Errorf("Method %s; want %s", r.Method, http.MethodGet)
  1733  		}
  1734  		if r.URL.Path != tc.Want.URLPath {
  1735  			t.Errorf("URLPath %s; want %s", r.URL.Path, tc.Want.URLPath)
  1736  		}
  1737  		body, err := io.ReadAll(r.Body)
  1738  		if err != nil {
  1739  			t.Fatal(err)
  1740  		}
  1741  		if !reflect.DeepEqual(body, tc.Want.RequestBody) {
  1742  			t.Errorf("RequestBody %s; want %s", body, tc.Want.RequestBody)
  1743  		}
  1744  		w.WriteHeader(tc.ResponseCode)
  1745  		w.Write(tc.Response)
  1746  	}))
  1747  	defer server.Close()
  1748  
  1749  	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1750  		defer r.Body.Close()
  1751  		t.Error("Unexpected Data API call")
  1752  		w.WriteHeader(404)
  1753  		w.Write([]byte(`{"message":"Not found"}`))
  1754  	}))
  1755  	defer dataServer.Close()
  1756  
  1757  	client, err := mockClient(server, dataServer)
  1758  	if err != nil {
  1759  		t.Fatal(err)
  1760  	}
  1761  	for i, tc := range testCases {
  1762  		currentTestIdx = i
  1763  		t.Run(strconv.Itoa(i)+"/"+tc.Label, func(t *testing.T) {
  1764  			res, err := client.GetWebhookInfo().Do()
  1765  			if tc.Want.Error != nil {
  1766  				if !reflect.DeepEqual(err, tc.Want.Error) {
  1767  					t.Errorf("Error %v; want %v", err, tc.Want.Error)
  1768  				}
  1769  			} else {
  1770  				if err != nil {
  1771  					t.Error(err)
  1772  				}
  1773  			}
  1774  			if !reflect.DeepEqual(res, tc.Want.Response) {
  1775  				t.Errorf("Response %v; want %v", res, tc.Want.Response)
  1776  			}
  1777  		})
  1778  	}
  1779  }
  1780  
  1781  func TestGetWebhookInfoWithContext(t *testing.T) {
  1782  	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1783  		defer r.Body.Close()
  1784  		time.Sleep(10 * time.Millisecond)
  1785  		w.Write([]byte("{}"))
  1786  	}))
  1787  	defer server.Close()
  1788  
  1789  	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1790  		defer r.Body.Close()
  1791  		t.Error("Unexpected Data API call")
  1792  		w.WriteHeader(404)
  1793  		w.Write([]byte(`{"message":"Not found"}`))
  1794  	}))
  1795  	defer dataServer.Close()
  1796  
  1797  	client, err := mockClient(server, dataServer)
  1798  	if err != nil {
  1799  		t.Fatal(err)
  1800  	}
  1801  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Millisecond)
  1802  	defer cancel()
  1803  	_, err = client.GetWebhookInfo().WithContext(ctx).Do()
  1804  	expectCtxDeadlineExceed(ctx, err, t)
  1805  }
  1806  
  1807  func BenchmarkGetWebhookInfo(b *testing.B) {
  1808  	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1809  		defer r.Body.Close()
  1810  		w.Write([]byte(`{"endpoint":"https://example.herokuapp.com/test","active":true}`))
  1811  	}))
  1812  	defer server.Close()
  1813  
  1814  	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1815  		defer r.Body.Close()
  1816  		b.Error("Unexpected Data API call")
  1817  		w.WriteHeader(404)
  1818  		w.Write([]byte(`{"message":"Not found"}`))
  1819  	}))
  1820  	defer dataServer.Close()
  1821  
  1822  	client, err := mockClient(server, dataServer)
  1823  	if err != nil {
  1824  		b.Fatal(err)
  1825  	}
  1826  	b.ResetTimer()
  1827  	for i := 0; i < b.N; i++ {
  1828  		client.GetWebhookInfo().Do()
  1829  	}
  1830  }
  1831  
  1832  func TestTestWebhook(t *testing.T) {
  1833  	type want struct {
  1834  		URLPath     string
  1835  		RequestBody []byte
  1836  		Response    *TestWebhookResponse
  1837  		Error       error
  1838  	}
  1839  	testCases := []struct {
  1840  		Label        string
  1841  		ResponseCode int
  1842  		Response     []byte
  1843  		Want         want
  1844  	}{
  1845  		{
  1846  			Label:        "Success",
  1847  			ResponseCode: 200,
  1848  			Response: []byte(`{
  1849  				"success": true,
  1850  				"timestamp": "2020-09-30T05:38:20.031Z",
  1851  				"statusCode": 200,
  1852  				"reason": "OK",
  1853  				"detail": "200"
  1854  			}`),
  1855  			Want: want{
  1856  				URLPath:     APIEndpointTestWebhook,
  1857  				RequestBody: []byte(""),
  1858  				Response: &TestWebhookResponse{
  1859  					Success:    true,
  1860  					Timestamp:  time.Date(2020, time.September, 30, 05, 38, 20, int(31*time.Millisecond), time.UTC),
  1861  					StatusCode: 200,
  1862  					Reason:     "OK",
  1863  					Detail:     "200",
  1864  				},
  1865  			},
  1866  		},
  1867  		{
  1868  			Label:        "Failed with timeout",
  1869  			ResponseCode: 200,
  1870  			Response: []byte(`{
  1871  				"success": false,
  1872  				"timestamp": "2023-09-12T14:00:10.015Z",
  1873  				"statusCode": 0,
  1874  				"reason": "REQUEST_TIMEOUT",
  1875  				"detail": "Request timeout: https://example.com/"
  1876  			}`),
  1877  			Want: want{
  1878  				URLPath:     APIEndpointTestWebhook,
  1879  				RequestBody: []byte(""),
  1880  				Response: &TestWebhookResponse{
  1881  					Success:    false,
  1882  					Timestamp:  time.Date(2023, time.September, 12, 14, 00, 10, int(15*time.Millisecond), time.UTC),
  1883  					StatusCode: 0,
  1884  					Reason:     "REQUEST_TIMEOUT",
  1885  					Detail:     "Request timeout: https://example.com/",
  1886  				},
  1887  			},
  1888  		},
  1889  		{
  1890  			Label:        "Internal server error",
  1891  			ResponseCode: 500,
  1892  			Response:     []byte("500 Internal server error"),
  1893  			Want: want{
  1894  				URLPath:     APIEndpointTestWebhook,
  1895  				RequestBody: []byte(""),
  1896  				Error: &APIError{
  1897  					Code: 500,
  1898  				},
  1899  			},
  1900  		},
  1901  		{
  1902  			Label:        "Invalid channelAccessToken error",
  1903  			ResponseCode: 401,
  1904  			Response:     []byte(`{"message":"Authentication failed due to the following reason: invalid token. Confirm that the access token in the authorization header is valid."}`),
  1905  			Want: want{
  1906  				URLPath:     APIEndpointTestWebhook,
  1907  				RequestBody: []byte(""),
  1908  				Error: &APIError{
  1909  					Code: 401,
  1910  					Response: &ErrorResponse{
  1911  						Message: "Authentication failed due to the following reason: invalid token. Confirm that the access token in the authorization header is valid.",
  1912  					},
  1913  				},
  1914  			},
  1915  		},
  1916  	}
  1917  
  1918  	var currentTestIdx int
  1919  	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1920  		defer r.Body.Close()
  1921  		tc := testCases[currentTestIdx]
  1922  		if r.Method != http.MethodPost {
  1923  			t.Errorf("Method %s; want %s", r.Method, http.MethodPost)
  1924  		}
  1925  		if r.URL.Path != tc.Want.URLPath {
  1926  			t.Errorf("URLPath %s; want %s", r.URL.Path, tc.Want.URLPath)
  1927  		}
  1928  		body, err := io.ReadAll(r.Body)
  1929  		if err != nil {
  1930  			t.Fatal(err)
  1931  		}
  1932  		if !reflect.DeepEqual(body, tc.Want.RequestBody) {
  1933  			t.Errorf("RequestBody %s; want %s", body, tc.Want.RequestBody)
  1934  		}
  1935  		w.WriteHeader(tc.ResponseCode)
  1936  		w.Write(tc.Response)
  1937  	}))
  1938  	defer server.Close()
  1939  
  1940  	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1941  		defer r.Body.Close()
  1942  		t.Error("Unexpected Data API call")
  1943  		w.WriteHeader(404)
  1944  		w.Write([]byte(`{"message":"Not found"}`))
  1945  	}))
  1946  	defer dataServer.Close()
  1947  
  1948  	client, err := mockClient(server, dataServer)
  1949  	if err != nil {
  1950  		t.Fatal(err)
  1951  	}
  1952  	for i, tc := range testCases {
  1953  		currentTestIdx = i
  1954  		t.Run(strconv.Itoa(i)+"/"+tc.Label, func(t *testing.T) {
  1955  			res, err := client.TestWebhook().Do()
  1956  			if tc.Want.Error != nil {
  1957  				if !reflect.DeepEqual(err, tc.Want.Error) {
  1958  					t.Errorf("Error %v; want %v", err, tc.Want.Error)
  1959  				}
  1960  			} else {
  1961  				if err != nil {
  1962  					t.Error(err)
  1963  				}
  1964  			}
  1965  			if !reflect.DeepEqual(res, tc.Want.Response) {
  1966  				t.Errorf("Response %v; want %v", res, tc.Want.Response)
  1967  			}
  1968  		})
  1969  	}
  1970  }
  1971  
  1972  func TestTestWebhookWithContext(t *testing.T) {
  1973  	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1974  		defer r.Body.Close()
  1975  		time.Sleep(10 * time.Millisecond)
  1976  		w.Write([]byte("{}"))
  1977  	}))
  1978  	defer server.Close()
  1979  
  1980  	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1981  		defer r.Body.Close()
  1982  		t.Error("Unexpected Data API call")
  1983  		w.WriteHeader(404)
  1984  		w.Write([]byte(`{"message":"Not found"}`))
  1985  	}))
  1986  	defer dataServer.Close()
  1987  
  1988  	client, err := mockClient(server, dataServer)
  1989  	if err != nil {
  1990  		t.Fatal(err)
  1991  	}
  1992  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Millisecond)
  1993  	defer cancel()
  1994  	_, err = client.TestWebhook().WithContext(ctx).Do()
  1995  	expectCtxDeadlineExceed(ctx, err, t)
  1996  }
  1997  
  1998  func TestSetWebhookEndpointURL(t *testing.T) {
  1999  	type want struct {
  2000  		URLPath     string
  2001  		RequestBody []byte
  2002  		Response    *BasicResponse
  2003  		Error       error
  2004  	}
  2005  	testCases := []struct {
  2006  		Label        string
  2007  		Endpoint     string
  2008  		ResponseCode int
  2009  		RequestID    string
  2010  		Response     []byte
  2011  		Want         want
  2012  	}{
  2013  		{
  2014  			Label:        "Success",
  2015  			Endpoint:     "https://example.com/abcdefghijklmn",
  2016  			ResponseCode: 200,
  2017  			RequestID:    "f70dd685-499a-4231-a441-f24b8d4fba21",
  2018  			Response:     []byte(`{}`),
  2019  			Want: want{
  2020  				URLPath:     APIEndpointSetWebhookEndpoint,
  2021  				RequestBody: []byte(`{"endpoint":"https://example.com/abcdefghijklmn"}` + "\n"),
  2022  				Response: &BasicResponse{
  2023  					RequestID: "f70dd685-499a-4231-a441-f24b8d4fba21",
  2024  				},
  2025  			},
  2026  		},
  2027  		{
  2028  			Label:        "Internal server error",
  2029  			Endpoint:     "https://example.com/abcdefghijklmn",
  2030  			ResponseCode: 500,
  2031  			RequestID:    "f70dd685-499a-4231-a441-f24b8d4fba21",
  2032  			Response:     []byte("500 Internal server error"),
  2033  			Want: want{
  2034  				URLPath:     APIEndpointSetWebhookEndpoint,
  2035  				RequestBody: []byte(`{"endpoint":"https://example.com/abcdefghijklmn"}` + "\n"),
  2036  				Error: &APIError{
  2037  					Code: 500,
  2038  				},
  2039  			},
  2040  		},
  2041  		{
  2042  			Label:        "Invalid webhook URL error:not https",
  2043  			Endpoint:     "http://example.com/not/https",
  2044  			ResponseCode: 400,
  2045  			RequestID:    "f70dd685-499a-4231-a441-f24b8d4fba21",
  2046  			Response:     []byte(`{"message":"Invalid webhook endpoint URL"}`),
  2047  			Want: want{
  2048  				URLPath:     APIEndpointSetWebhookEndpoint,
  2049  				RequestBody: []byte(`{"endpoint":"http://example.com/not/https"}` + "\n"),
  2050  				Error: &APIError{
  2051  					Code: 400,
  2052  					Response: &ErrorResponse{
  2053  						Message: "Invalid webhook endpoint URL",
  2054  					},
  2055  				},
  2056  			},
  2057  		},
  2058  		{
  2059  			Label:        "Invalid webhook URL error:more 500 characters",
  2060  			Endpoint:     "https://example.com/exceed/500/characters",
  2061  			ResponseCode: 500,
  2062  			RequestID:    "f70dd685-499a-4231-a441-f24b8d4fba21",
  2063  			Response:     []byte(`{"message":"The request body has 1 error(s)","details":[{"message":"Size must be between 0 and 500","property":"endpoint"}]}`),
  2064  			Want: want{
  2065  				URLPath:     APIEndpointSetWebhookEndpoint,
  2066  				RequestBody: []byte(`{"endpoint":"https://example.com/exceed/500/characters"}` + "\n"),
  2067  				Error: &APIError{
  2068  					Code: 500,
  2069  					Response: &ErrorResponse{
  2070  						Message: "The request body has 1 error(s)",
  2071  						Details: []errorResponseDetail{
  2072  							{
  2073  								Message:  "Size must be between 0 and 500",
  2074  								Property: "endpoint",
  2075  							},
  2076  						},
  2077  					},
  2078  				},
  2079  			},
  2080  		},
  2081  	}
  2082  
  2083  	var currentTestIdx int
  2084  	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2085  		defer r.Body.Close()
  2086  		tc := testCases[currentTestIdx]
  2087  		if r.Method != http.MethodPut {
  2088  			t.Errorf("Method %s; want %s", r.Method, http.MethodPut)
  2089  		}
  2090  		if r.URL.Path != tc.Want.URLPath {
  2091  			t.Errorf("URLPath %s; want %s", r.URL.Path, tc.Want.URLPath)
  2092  		}
  2093  		body, err := io.ReadAll(r.Body)
  2094  		if err != nil {
  2095  			t.Fatal(err)
  2096  		}
  2097  		if !reflect.DeepEqual(body, tc.Want.RequestBody) {
  2098  			t.Errorf("RequestBody %s; want %s", body, tc.Want.RequestBody)
  2099  		}
  2100  		w.Header().Set("X-Line-Request-Id", tc.RequestID)
  2101  		w.WriteHeader(tc.ResponseCode)
  2102  		w.Write(tc.Response)
  2103  	}))
  2104  	defer server.Close()
  2105  
  2106  	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2107  		defer r.Body.Close()
  2108  		t.Error("Unexpected Data API call")
  2109  		w.WriteHeader(404)
  2110  		w.Write([]byte(`{"message":"Not found"}`))
  2111  	}))
  2112  	defer dataServer.Close()
  2113  
  2114  	client, err := mockClient(server, dataServer)
  2115  	if err != nil {
  2116  		t.Fatal(err)
  2117  	}
  2118  	for i, tc := range testCases {
  2119  		currentTestIdx = i
  2120  		t.Run(strconv.Itoa(i)+"/"+tc.Label, func(t *testing.T) {
  2121  			res, err := client.SetWebhookEndpointURL(tc.Endpoint).Do()
  2122  			if tc.Want.Error != nil {
  2123  				if !reflect.DeepEqual(err, tc.Want.Error) {
  2124  					t.Errorf("Error %v; want %v", err, tc.Want.Error)
  2125  				}
  2126  			} else {
  2127  				if err != nil {
  2128  					t.Error(err)
  2129  				}
  2130  			}
  2131  			if !reflect.DeepEqual(res, tc.Want.Response) {
  2132  				t.Errorf("Response %v; want %v", res, tc.Want.Response)
  2133  			}
  2134  		})
  2135  	}
  2136  }
  2137  
  2138  func TestSetWebhookEndpointURLWithContext(t *testing.T) {
  2139  	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2140  		defer r.Body.Close()
  2141  		time.Sleep(10 * time.Millisecond)
  2142  		w.Write([]byte("{}"))
  2143  	}))
  2144  	defer server.Close()
  2145  
  2146  	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2147  		defer r.Body.Close()
  2148  		t.Error("Unexpected Data API call")
  2149  		w.WriteHeader(404)
  2150  		w.Write([]byte(`{"message":"Not found"}`))
  2151  	}))
  2152  	defer dataServer.Close()
  2153  
  2154  	client, err := mockClient(server, dataServer)
  2155  	if err != nil {
  2156  		t.Fatal(err)
  2157  	}
  2158  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Millisecond)
  2159  	defer cancel()
  2160  	_, err = client.SetWebhookEndpointURL("https://example.com/abcdefghijklmn").WithContext(ctx).Do()
  2161  	expectCtxDeadlineExceed(ctx, err, t)
  2162  }
  2163  
  2164  func BenchmarkSetWebhookEndpointURL(b *testing.B) {
  2165  	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2166  		defer r.Body.Close()
  2167  		w.Write([]byte(`{}`))
  2168  	}))
  2169  	defer server.Close()
  2170  
  2171  	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2172  		defer r.Body.Close()
  2173  		b.Error("Unexpected Data API call")
  2174  		w.WriteHeader(404)
  2175  		w.Write([]byte(`{"message":"Not found"}`))
  2176  	}))
  2177  	defer dataServer.Close()
  2178  
  2179  	client, err := mockClient(server, dataServer)
  2180  	if err != nil {
  2181  		b.Fatal(err)
  2182  	}
  2183  	b.ResetTimer()
  2184  	for i := 0; i < b.N; i++ {
  2185  		client.SetWebhookEndpointURL("https://example.com/abcdefghijklmn").Do()
  2186  	}
  2187  }
  2188  
  2189  func BenchmarkTestWebhook(b *testing.B) {
  2190  	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2191  		defer r.Body.Close()
  2192  		w.Write([]byte("{}"))
  2193  	}))
  2194  	defer server.Close()
  2195  
  2196  	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2197  		defer r.Body.Close()
  2198  		b.Error("Unexpected data API call")
  2199  		w.WriteHeader(404)
  2200  		w.Write([]byte(`{"message":"Not found"}`))
  2201  	}))
  2202  	defer dataServer.Close()
  2203  
  2204  	client, err := mockClient(server, dataServer)
  2205  	if err != nil {
  2206  		b.Fatal(err)
  2207  	}
  2208  	b.ResetTimer()
  2209  	for i := 0; i < b.N; i++ {
  2210  		client.TestWebhook().Do()
  2211  	}
  2212  }