package weapp import ( "encoding/base64" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" ) func TestAddExpressOrder(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { t.Fatalf("Expect 'POST' get '%s'", r.Method) } path := r.URL.EscapedPath() if path != apiAddExpressOrder { t.Fatalf("Except to path '%s',get '%s'", apiAddExpressOrder, path) } if err := r.ParseForm(); err != nil { t.Fatal(err) } if r.Form.Get("access_token") == "" { t.Fatalf("access_token can not be empty") } params := struct { AddSource uint8 `json:"add_source"` WXAppID string `json:"wx_appid"` OrderID string `json:"order_id"` OpenID string `json:"openid"` DeliveryID string `json:"delivery_id"` BizID string `json:"biz_id"` CustomRemark string `json:"custom_remark"` Sender struct { Name string `json:"name"` Tel string `json:"tel"` Mobile string `json:"mobile"` Company string `json:"company"` PostCode string `json:"post_code"` Country string `json:"country"` Province string `json:"province"` City string `json:"city"` Area string `json:"area"` Address string `json:"address"` } `json:"sender"` Receiver struct { Name string `json:"name"` Tel string `json:"tel"` Mobile string `json:"mobile"` Company string `json:"company"` PostCode string `json:"post_code"` Country string `json:"country"` Province string `json:"province"` City string `json:"city"` Area string `json:"area"` Address string `json:"address"` } `json:"receiver"` Cargo struct { Count uint `json:"count"` Weight float64 `json:"weight"` SpaceX float64 `json:"space_x"` SpaceY float64 `json:"space_y"` SpaceZ float64 `json:"space_z"` DetailList []struct { Name string `json:"name"` Count uint `json:"count"` } `json:"detail_list"` } `json:"cargo"` Shop struct { WXAPath string `json:"wxa_path"` IMGUrl string `json:"img_url"` GoodsName string `json:"goods_name"` GoodsCount uint `json:"goods_count"` } `json:"shop"` Insured struct { Used InsureStatus `json:"use_insured"` Value uint `json:"insured_value"` } `json:"insured"` Service struct { Type uint8 `json:"service_type"` Name string `json:"service_name"` } `json:"service"` ExpectTime uint `json:"expect_time"` }{} if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { t.Fatal(err) } if params.AddSource == 2 && params.WXAppID == "" { t.Error("param wx_appid can not be empty") } if params.AddSource != 2 && params.OpenID == "" { t.Error("param openid can not be empty") } if params.OrderID == "" { t.Error("param order_id can not be empty") } if params.DeliveryID == "" { t.Error("param delivery_id can not be empty") } if params.BizID == "" { t.Error("param biz_id can not be empty") } if params.Sender.Name == "" { t.Error("param sender.name can not be empty") } if params.Sender.Province == "" { t.Error("param sender.province can not be empty") } if params.Sender.City == "" { t.Error("param sender.city can not be empty") } if params.Sender.Area == "" { t.Error("param sender.area can not be empty") } if params.Sender.Address == "" { t.Error("param sender.address can not be empty") } if params.Receiver.Name == "" { t.Error("param receiver.name can not be empty") } if params.Receiver.Province == "" { t.Error("param receiver.province can not be empty") } if params.Receiver.City == "" { t.Error("param receiver.city can not be empty") } if params.Receiver.Area == "" { t.Error("param receiver.area can not be empty") } if params.Receiver.Address == "" { t.Error("param receiver.address can not be empty") } if params.Cargo.Count == 0 { t.Error("param cargo.count can not be zero") } if params.Cargo.Weight == 0 { t.Error("param cargo.weight can not be zero") } if params.Cargo.SpaceX == 0 { t.Error("param cargo.spaceX can not be zero") } if params.Cargo.SpaceY == 0 { t.Error("param cargo.spaceY can not be zero") } if params.Cargo.SpaceZ == 0 { t.Error("param cargo.spaceZ can not be zero") } if len(params.Cargo.DetailList) == 0 { t.Error("param cargo.detailList can not be empty") } else { if (params.Cargo.DetailList[0].Name) == "" { t.Error("param cargo.detailList.name can not be empty") } if (params.Cargo.DetailList[0].Count) == 0 { t.Error("param cargo.detailList.count can not be zero") } } if params.Shop.WXAPath == "" { t.Error("param shop.wxa_path can not be empty") } if params.Shop.IMGUrl == "" { t.Error("param shop.img_url can not be empty") } if params.Shop.GoodsName == "" { t.Error("param shop.goods_name can not be empty") } if params.Shop.GoodsCount == 0 { t.Error("param shop.goods_count can not be zero") } if params.Insured.Used == 0 { t.Error("param insured.use_insured can not be zero") } if params.Service.Name == "" { t.Error("param Service.service_name can not be empty") } w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") raw := `{ "errcode": 9300501, "errmsg": "delivery logic fail", "delivery_resultcode": 10002, "delivery_resultmsg": "客户密码不正确", "order_id": "01234567890123456789", "waybill_id": "123456789", "waybill_data": [ { "key": "SF_bagAddr", "value": "广州" }, { "key": "SF_mark", "value": "101- 07-03 509" } ] }` if _, err := w.Write([]byte(raw)); err != nil { t.Fatal(err) } })) defer ts.Close() creator := ExpressOrderCreator{ AddSource: 0, ExpressOrder: ExpressOrder{ OrderID: "01234567890123456789", OpenID: "oABC123456", DeliveryID: "SF", BizID: "xyz", CustomRemark: "易碎物品", Sender: ExpreseeUserInfo{ "张三", "020-88888888", "18666666666", "公司名", "123456", "中国", "广东省", "广州市", "海珠区", "XX路XX号XX大厦XX栋XX", }, Receiver: ExpreseeUserInfo{ "王小蒙", "020-77777777", "18610000000", "公司名", "654321", "中国", "广东省", "广州市", "天河区", "XX路XX号XX大厦XX栋XX", }, Shop: ExpressShop{ "/index/index?from=waybill&id=01234567890123456789", "https://mmbiz.qpic.cn/mmbiz_png/OiaFLUqewuIDNQnTiaCInIG8ibdosYHhQHPbXJUrqYSNIcBL60vo4LIjlcoNG1QPkeH5GWWEB41Ny895CokeAah8A/640", "一千零一夜钻石包&爱马仕铂金包", 2, }, Cargo: ExpressCargo{ 2, 5.5, 30.5, 20, 20, []CargoDetail{ { "一千零一夜钻石包", 1, }, { "爱马仕铂金包", 1, }, }, }, Insured: ExpressInsure{ 1, 10000, }, Service: ExpressService{ 0, "标准快递", }, }, } _, err := creator.create(ts.URL+apiAddExpressOrder, "mock-access-token") if err != nil { t.Fatal(err) } } func TestCancelExpressOrder(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { t.Fatalf("Expect 'POST' get '%s'", r.Method) } path := r.URL.EscapedPath() if path != apiCancelExpressOrder { t.Fatalf("Except to path '%s',get '%s'", apiCancelExpressOrder, path) } if err := r.ParseForm(); err != nil { t.Fatal(err) } if r.Form.Get("access_token") == "" { t.Fatalf("access_token can not be empty") } params := struct { OrderID string `json:"order_id"` OpenID string `json:"openid"` DeliveryID string `json:"delivery_id"` WaybillID string `json:"waybill_id"` }{} if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { t.Fatal(err) } if params.OrderID == "" { t.Error("param order_id can not be empty") } if params.DeliveryID == "" { t.Error("param delivery_id can not be empty") } if params.WaybillID == "" { t.Error("param waybill_id can not be empty") } w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") raw := `{ "errcode": 0, "errmsg": "ok" }` if _, err := w.Write([]byte(raw)); err != nil { t.Fatal(err) } })) defer ts.Close() canceler := ExpressOrderCanceler{ OrderID: "01234567890123456789", OpenID: "oABC123456", DeliveryID: "SF", WaybillID: "123456789", } _, err := canceler.cancel(ts.URL+apiCancelExpressOrder, "mock-access-token") if err != nil { t.Fatal(err) } } func TestGetAllDelivery(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { t.Fatalf("Expect 'GET' get '%s'", r.Method) } path := r.URL.EscapedPath() if path != apiGetAllDelivery { t.Fatalf("Except to path '%s',get '%s'", apiGetAllDelivery, path) } if err := r.ParseForm(); err != nil { t.Fatal(err) } if r.Form.Get("access_token") == "" { t.Fatalf("access_token can not be empty") } w.WriteHeader(http.StatusOK) raw := `{ "count": 8, "data": [ { "delivery_id": "BEST", "delivery_name": "百世快递" }, { "delivery_id": "EMS", "delivery_name": "中国邮政速递物流" }, { "delivery_id": "OTP", "delivery_name": "承诺达特快" }, { "delivery_id": "PJ", "delivery_name": "品骏物流" }, { "delivery_id": "SF", "delivery_name": "顺丰速运" }, { "delivery_id": "YTO", "delivery_name": "圆通速递" }, { "delivery_id": "YUNDA", "delivery_name": "韵达快递" }, { "delivery_id": "ZTO", "delivery_name": "中通快递" } ] }` if _, err := w.Write([]byte(raw)); err != nil { t.Fatal(err) } })) defer ts.Close() _, err := getAllDelivery(ts.URL+apiGetAllDelivery, "mock-access-token") if err != nil { t.Fatal(err) } } func TestGetExpressOrder(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { t.Fatalf("Expect 'POST' get '%s'", r.Method) } path := r.URL.EscapedPath() if path != apiGetExpressOrder { t.Fatalf("Except to path '%s',get '%s'", apiGetExpressOrder, path) } if err := r.ParseForm(); err != nil { t.Fatal(err) } if r.Form.Get("access_token") == "" { t.Fatalf("access_token can not be empty") } params := struct { OrderID string `json:"order_id"` OpenID string `json:"openid"` DeliveryID string `json:"delivery_id"` WaybillID string `json:"waybill_id"` }{} if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { t.Fatal(err) } if params.OrderID == "" { t.Error("param order_id can not be empty") } if params.DeliveryID == "" { t.Error("param delivery_id can not be empty") } if params.WaybillID == "" { t.Error("param waybill_id can not be empty") } w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") raw := `{ "errcode": 0, "errmsg": "ok" }` if _, err := w.Write([]byte(raw)); err != nil { t.Fatal(err) } })) defer ts.Close() canceler := ExpressOrderGetter{ OrderID: "01234567890123456789", OpenID: "oABC123456", DeliveryID: "SF", WaybillID: "123456789", } _, err := canceler.get(ts.URL+apiGetExpressOrder, "mock-access-token") if err != nil { t.Fatal(err) } } func TestGetExpressPath(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { t.Fatalf("Expect 'POST' get '%s'", r.Method) } path := r.URL.EscapedPath() expectedPath := "/cgi-bin/express/business/path/get" if path != expectedPath { t.Fatalf("Except to path '%s',get '%s'", expectedPath, path) } if err := r.ParseForm(); err != nil { t.Fatal(err) } if r.Form.Get("access_token") == "" { t.Fatalf("access_token can not be empty") } params := struct { OrderID string `json:"order_id"` OpenID string `json:"openid"` DeliveryID string `json:"delivery_id"` WaybillID string `json:"waybill_id"` }{} if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { t.Fatal(err) } if params.OrderID == "" { t.Error("param order_id can not be empty") } if params.DeliveryID == "" { t.Error("param delivery_id can not be empty") } if params.WaybillID == "" { t.Error("param waybill_id can not be empty") } w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") raw := `{ "openid": "OPENID", "delivery_id": "SF", "waybill_id": "12345678901234567890", "path_item_num": 3, "path_item_list": [ { "action_time": 1533052800, "action_type": 100001, "action_msg": "快递员已成功取件" }, { "action_time": 1533062800, "action_type": 200001, "action_msg": "快件已到达xxx集散中心,准备发往xxx" }, { "action_time": 1533072800, "action_type": 300001, "action_msg": "快递员已出发,联系电话xxxxxx" } ] }` if _, err := w.Write([]byte(raw)); err != nil { t.Fatal(err) } })) defer ts.Close() getter := ExpressPathGetter{ OrderID: "01234567890123456789", OpenID: "oABC123456", DeliveryID: "SF", WaybillID: "123456789", } _, err := getter.get(ts.URL+apiGetExpressPath, "mock-access-token") if err != nil { t.Fatal(err) } } func TestGetPrinter(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { t.Fatalf("Expect 'GET' get '%s'", r.Method) } path := r.URL.EscapedPath() expectedPath := "/cgi-bin/express/business/printer/getall" if path != expectedPath { t.Fatalf("Except to path '%s',get '%s'", expectedPath, path) } if err := r.ParseForm(); err != nil { t.Fatal(err) } if r.Form.Get("access_token") == "" { t.Fatalf("access_token can not be empty") } w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") raw := `{ "count": 2, "openid": [ "oABC", "oXYZ" ], "tagid_list": [ "123", "456" ] }` if _, err := w.Write([]byte(raw)); err != nil { t.Fatal(err) } })) defer ts.Close() _, err := getPrinter(ts.URL+apiGetPrinter, "mock-access-token") if err != nil { t.Fatal(err) } } func TestGetQuota(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { t.Fatalf("Expect 'POST' get '%s'", r.Method) } path := r.URL.EscapedPath() expectedPath := "/cgi-bin/express/business/quota/get" if path != expectedPath { t.Fatalf("Except to path '%s',get '%s'", expectedPath, path) } if err := r.ParseForm(); err != nil { t.Fatal(err) } if r.Form.Get("access_token") == "" { t.Fatalf("access_token can not be empty") } params := struct { DeliveryID string `json:"delivery_id"` BizID string `json:"biz_id"` }{} if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { t.Fatal(err) } if params.DeliveryID == "" { t.Error("param delivery_id can not be empty") } if params.BizID == "" { t.Error("param biz_id can not be empty") } w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") raw := `{ "quota_num": 210 }` if _, err := w.Write([]byte(raw)); err != nil { t.Fatal(err) } })) defer ts.Close() getter := QuotaGetter{ DeliveryID: "YTO", BizID: "xyz", } _, err := getter.get(ts.URL+apiGetQuota, "mock-access-token") if err != nil { t.Fatal(err) } } func TestOnPathUpdate(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { aesKey := base64.StdEncoding.EncodeToString([]byte("mock-aes-key")) srv, err := NewServer("mock-app-id", "mock-access-token", aesKey, "mock-mch-id", "mock-api-key", false) if err != nil { t.Fatal(err) } srv.OnExpressPathUpdate(func(mix *ExpressPathUpdateResult) { if mix.ToUserName == "" { t.Error("ToUserName can not be empty") } if mix.FromUserName == "" { t.Error("FromUserName can not be empty") } if mix.CreateTime == 0 { t.Error("CreateTime can not be zero") } if mix.MsgType != "event" { t.Error("Unexpected message type") } if mix.Event != "add_express_path" { t.Error("Unexpected message event") } if mix.DeliveryID == "" { t.Error("DeliveryID can not be empty") } if mix.WayBillID == "" { t.Error("WayBillID can not be empty") } if mix.Version == 0 { t.Error("Version can not be zero") } if mix.Count == 0 { t.Error("Count can not be zero") } if len(mix.Actions) > 0 { if mix.Actions[0].ActionTime == 0 { t.Error("Actions.ActionTime can not be zero") } if mix.Actions[0].ActionType == 0 { t.Error("Actions.ActionType can not be zero") } if mix.Actions[0].ActionMsg == "" { t.Error("Actions.ActionMsg can not be empty") } } }) if err := srv.Serve(w, r); err != nil { t.Fatal(err) } })) defer ts.Close() xmlData := ` 1546924844 3 3 1546924840 100001 1546924841 200001 1546924842 200001 ` res, err := http.Post(ts.URL, "text/xml", strings.NewReader(xmlData)) if err != nil { t.Fatal(err) } defer res.Body.Close() jsonData := `{ "ToUserName": "toUser", "FromUserName": "fromUser", "CreateTime": 1546924844, "MsgType": "event", "Event": "add_express_path", "DeliveryID": "SF", "WayBillId": "123456789", "Version": 2, "Count": 3, "Actions": [ { "ActionTime": 1546924840, "ActionType": 100001, "ActionMsg": "小哥A揽件成功" }, { "ActionTime": 1546924841, "ActionType": 200001, "ActionMsg": "到达广州集包地" }, { "ActionTime": 1546924842, "ActionType": 200001, "ActionMsg": "运往目的地" } ] }` res, err = http.Post(ts.URL, "application/json", strings.NewReader(jsonData)) if err != nil { t.Fatal(err) } defer res.Body.Close() } func TestTestUpdateOrder(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { t.Fatalf("Expect 'POST' get '%s'", r.Method) } path := r.URL.EscapedPath() expectedPath := "/cgi-bin/express/business/test_update_order" if path != expectedPath { t.Fatalf("Except to path '%s',get '%s'", expectedPath, path) } if err := r.ParseForm(); err != nil { t.Fatal(err) } if r.Form.Get("access_token") == "" { t.Fatalf("access_token can not be empty") } params := struct { BizID string `json:"biz_id"` // 商户id,需填test_biz_id OrderID string `json:"order_id"` // 订单ID,下单成功时返回 WaybillID string `json:"waybill_id"` // 运单 ID DeliveryID string `json:"delivery_id"` // 快递公司 ID ActionTime uint `json:"action_time"` // 轨迹变化 Unix 时间戳 ActionType int `json:"action_type"` // 轨迹变化类型 ActionMsg string `json:"action_msg"` // 轨迹变化具体信息说明,展示在快递轨迹详情页中。若有手机号码,则直接写11位手机号码。使用UTF-8编码。 }{} if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { t.Fatal(err) } if params.DeliveryID == "" { t.Error("param delivery_id can not be empty") } if params.OrderID == "" { t.Error("param order_id can not be empty") } if params.WaybillID == "" { t.Error("param waybill_id can not be empty") } if params.BizID == "" { t.Error("param biz_id can not be empty") } if params.ActionMsg == "" { t.Error("param action_msg can not be empty") } if params.ActionTime == 0 { t.Error("param action_time can not be empty") } if params.ActionType == 0 { t.Error("param action_type can not be empty") } w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") raw := `{ "errcode": 0, "errmsg": "ok" }` if _, err := w.Write([]byte(raw)); err != nil { t.Fatal(err) } })) defer ts.Close() params := `{ "biz_id": "test_biz_id", "order_id": "xxxxxxxxxxxx", "delivery_id": "TEST", "waybill_id": "xxxxxxxxxx", "action_time": 123456789, "action_type": 100001, "action_msg": "揽件阶段" }` tester := new(UpdateExpressOrderTester) err := json.Unmarshal([]byte(params), tester) if err != nil { t.Error(err) } _, err = tester.test(ts.URL+apiTestUpdateOrder, "mock-access-token") if err != nil { t.Error(err) } } func TestUpdatePrinter(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { t.Fatalf("Expect 'POST' get '%s'", r.Method) } path := r.URL.EscapedPath() if path != "/cgi-bin/express/business/printer/update" { t.Error("Invalid request path") } if err := r.ParseForm(); err != nil { t.Fatal(err) } if r.Form.Get("access_token") == "" { t.Fatalf("access_token can not be empty") } params := struct { OpenID string `json:"openid"` // 打印员 openid Type BindType `json:"update_type"` // 更新类型 TagIDList string `json:"tagid_list"` }{} if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil { t.Fatal(err) } if params.OpenID == "" { t.Error("param openid can not be empty") } if params.Type == "" { t.Error("param update_type can not be empty") } w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") raw := `{ "errcode": 0, "errmsg": "ok" }` if _, err := w.Write([]byte(raw)); err != nil { t.Fatal(err) } })) defer ts.Close() params := `{ "openid": "oJ4v0wRAfiXcnIbM3SgGEUkTw3Qw", "update_type": "bind", "tagid_list": "123,456" }` updater := new(PrinterUpdater) err := json.Unmarshal([]byte(params), updater) if err != nil { t.Fatal(err) } _, err = updater.update(ts.URL+apiUpdatePrinter, "mock-access-token") if err != nil { t.Fatal(err) } }