688 lines
15 KiB
Go
688 lines
15 KiB
Go
package notifications
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"io/ioutil"
|
||
"net"
|
||
"net/http"
|
||
"reflect"
|
||
"strings"
|
||
"testing"
|
||
"time"
|
||
|
||
core "github.com/diphantxm/ozon-api-client"
|
||
)
|
||
|
||
type testData struct {
|
||
raw string
|
||
object interface{}
|
||
}
|
||
|
||
func pingTest(t *testing.T) testData {
|
||
return testData{
|
||
object: &pingRequest{
|
||
Common: Common{MessageType: PingType},
|
||
Time: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||
},
|
||
raw: `{
|
||
"message_type": "TYPE_PING",
|
||
"time": "2019-08-24T14:15:22Z"
|
||
}`,
|
||
}
|
||
}
|
||
|
||
func newPostingTest(t *testing.T) testData {
|
||
return testData{
|
||
object: &NewPosting{
|
||
Common: Common{MessageType: NewPostingType},
|
||
PostingNumber: "24219509-0020-1",
|
||
Products: []Product{
|
||
{
|
||
SKU: 147451959,
|
||
Quantity: 2,
|
||
},
|
||
},
|
||
InProccessAt: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-01-26T06:56:36.294Z"),
|
||
WarehouseId: 18850503335000,
|
||
SellerId: 15,
|
||
},
|
||
raw: `{
|
||
"message_type": "TYPE_NEW_POSTING",
|
||
"posting_number": "24219509-0020-1",
|
||
"products": [
|
||
{
|
||
"sku": 147451959,
|
||
"quantity": 2
|
||
}
|
||
],
|
||
"in_process_at": "2021-01-26T06:56:36.294Z",
|
||
"warehouse_id": 18850503335000,
|
||
"seller_id": 15
|
||
}`,
|
||
}
|
||
}
|
||
|
||
func postingCancelledTest(t *testing.T) testData {
|
||
return testData{
|
||
object: &PostingCancelled{
|
||
Common: Common{MessageType: PostingCancelledType},
|
||
PostingNumber: "24219509-0020-1",
|
||
Products: []Product{
|
||
{
|
||
SKU: 147451959,
|
||
Quantity: 1,
|
||
},
|
||
},
|
||
OldState: "posting_transferred_to_courier_service",
|
||
NewState: "posting_canceled",
|
||
ChangedStateDate: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-01-26T06:56:36.294Z"),
|
||
Reason: Reason{Id: 1, Message: "message"},
|
||
WarehouseId: 1,
|
||
SellerId: 15,
|
||
},
|
||
raw: `{
|
||
"message_type": "TYPE_POSTING_CANCELLED",
|
||
"posting_number": "24219509-0020-1",
|
||
"products": [
|
||
{
|
||
"sku": 147451959,
|
||
"quantity": 1
|
||
}
|
||
],
|
||
"old_state": "posting_transferred_to_courier_service",
|
||
"new_state": "posting_canceled",
|
||
"changed_state_date": "2021-01-26T06:56:36.294Z",
|
||
"reason": {
|
||
"id": 1,
|
||
"message": "message"
|
||
},
|
||
"warehouse_id": 1,
|
||
"seller_id": 15
|
||
}`,
|
||
}
|
||
}
|
||
|
||
func cutoffDateChangedTest(t *testing.T) testData {
|
||
return testData{
|
||
object: &CutoffDateChanged{
|
||
Common: Common{MessageType: CutoffDateChangedType},
|
||
PostingNumber: "24219509-0020-2",
|
||
NewCutoffDate: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-11-24T07:00:00Z"),
|
||
OldCutoffDate: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-11-21T10:00:00Z"),
|
||
WarehouseId: 1,
|
||
SellerId: 15,
|
||
},
|
||
raw: `{
|
||
"message_type": "TYPE_CUTOFF_DATE_CHANGED",
|
||
"posting_number": "24219509-0020-2",
|
||
"new_cutoff_date": "2021-11-24T07:00:00Z",
|
||
"old_cutoff_date": "2021-11-21T10:00:00Z",
|
||
"warehouse_id": 1,
|
||
"seller_id": 15
|
||
}`,
|
||
}
|
||
}
|
||
|
||
func deliveryDateChangedTest(t *testing.T) testData {
|
||
return testData{
|
||
object: &DeliveryDateChanged{
|
||
Common: Common{MessageType: DeliveryDateChangedType},
|
||
PostingNumber: "24219509-0020-2",
|
||
NewDeliveryDateBegin: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-11-24T07:00:00Z"),
|
||
NewDeliveryDateEnd: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-11-24T16:00:00Z"),
|
||
OldDeliveryDateBegin: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-11-21T10:00:00Z"),
|
||
OldDeliveryDateEnd: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-11-21T19:00:00Z"),
|
||
WarehouseId: 1,
|
||
SellerId: 15,
|
||
},
|
||
raw: `{
|
||
"message_type": "TYPE_DELIVERY_DATE_CHANGED",
|
||
"posting_number": "24219509-0020-2",
|
||
"new_delivery_date_begin": "2021-11-24T07:00:00Z",
|
||
"new_delivery_date_end": "2021-11-24T16:00:00Z",
|
||
"old_delivery_date_begin": "2021-11-21T10:00:00Z",
|
||
"old_delivery_date_end": "2021-11-21T19:00:00Z",
|
||
"warehouse_id": 1,
|
||
"seller_id": 15
|
||
}`,
|
||
}
|
||
}
|
||
|
||
func priceIndexChangedTest(t *testing.T) testData {
|
||
return testData{
|
||
object: &PriceIndexChanged{
|
||
Common: Common{MessageType: PriceIndexChangedType},
|
||
UpdatedAt: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2022-06-21T05:52:46.648533678Z"),
|
||
SKU: 147451959,
|
||
ProductId: 1234,
|
||
PriceIndex: 5678,
|
||
SellerId: 15,
|
||
},
|
||
raw: `{
|
||
"seller_id": 15,
|
||
"message_type": "TYPE_PRICE_INDEX_CHANGED",
|
||
"updated_at":"2022-06-21T05:52:46.648533678Z",
|
||
"sku": 147451959,
|
||
"product_id": 1234,
|
||
"price_index": 5678
|
||
}`,
|
||
}
|
||
}
|
||
|
||
func stocksChangedTest(t *testing.T) testData {
|
||
return testData{
|
||
object: &StocksChanged{
|
||
Common: Common{MessageType: StocksChangedType},
|
||
Items: []Item{
|
||
{
|
||
UpdatedAt: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-09-01T14:15:22Z"),
|
||
SKU: 5678,
|
||
ProductId: 1234,
|
||
Stocks: []Stock{
|
||
{
|
||
WarehouseId: 10,
|
||
Present: 50,
|
||
Reserved: 5,
|
||
},
|
||
},
|
||
},
|
||
},
|
||
SellerId: 15,
|
||
},
|
||
raw: `{
|
||
"message_type": "TYPE_STOCKS_CHANGED",
|
||
"seller_id": 15,
|
||
"items": [
|
||
{
|
||
"product_id": 1234,
|
||
"sku": 5678,
|
||
"updated_at": "2021-09-01T14:15:22Z",
|
||
"stocks": [
|
||
{
|
||
"warehouse_id": 10,
|
||
"present": 50,
|
||
"reserved": 5
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}`,
|
||
}
|
||
}
|
||
|
||
func newMessageTest(t *testing.T) testData {
|
||
return testData{
|
||
object: &NewMessage{
|
||
Common: Common{MessageType: NewMessageType},
|
||
ChatId: "b646d975-0c9c-4872-9f41-8b1e57181063",
|
||
ChatType: "Buyer_Seller",
|
||
MessageId: "3000000000817031942",
|
||
CreatedAt: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2022-07-18T20:58:04.528Z"),
|
||
User: User{Id: "115568", Type: "Customer"},
|
||
Data: []string{"Message text"},
|
||
SellerId: 7,
|
||
},
|
||
raw: `{
|
||
"message_type": "TYPE_NEW_MESSAGE",
|
||
"chat_id": "b646d975-0c9c-4872-9f41-8b1e57181063",
|
||
"chat_type": "Buyer_Seller",
|
||
"message_id": "3000000000817031942",
|
||
"created_at": "2022-07-18T20:58:04.528Z",
|
||
"user": {
|
||
"id": "115568",
|
||
"type": "Customer"
|
||
},
|
||
"data": [
|
||
"Message text"
|
||
],
|
||
"seller_id": 7
|
||
}`,
|
||
}
|
||
}
|
||
|
||
func updateMessageTest(t *testing.T) testData {
|
||
return testData{
|
||
object: &UpdateMessage{
|
||
NewMessage: NewMessage{
|
||
Common: Common{MessageType: UpdateMessageType},
|
||
ChatId: "b646d975-0c9c-4872-9f41-8b1e57181063",
|
||
ChatType: "Buyer_Seller",
|
||
MessageId: "3000000000817031942",
|
||
CreatedAt: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2022-07-18T20:58:04.528Z"),
|
||
User: User{
|
||
Id: "115568",
|
||
Type: "Сustomer",
|
||
},
|
||
Data: []string{"Message text"},
|
||
SellerId: 7,
|
||
},
|
||
UpdatedAt: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2022-07-18T20:59:04.528Z"),
|
||
},
|
||
raw: `{
|
||
"message_type": "TYPE_UPDATE_MESSAGE",
|
||
"chat_id": "b646d975-0c9c-4872-9f41-8b1e57181063",
|
||
"chat_type": "Buyer_Seller",
|
||
"message_id": "3000000000817031942",
|
||
"created_at": "2022-07-18T20:58:04.528Z",
|
||
"updated_at": "2022-07-18T20:59:04.528Z",
|
||
"user": {
|
||
"id": "115568",
|
||
"type": "Сustomer"
|
||
},
|
||
"data": [
|
||
"Message text"
|
||
],
|
||
"seller_id": 7
|
||
}`,
|
||
}
|
||
}
|
||
|
||
func createUpdateItemTest(t *testing.T) testData {
|
||
return testData{
|
||
object: &CreateOrUpdateItem{
|
||
Common: Common{MessageType: "TYPE_CREATE_OR_UPDATE_ITEM"},
|
||
OfferId: "1234",
|
||
ProductId: 5678,
|
||
IsError: false,
|
||
ChangedAt: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2022-09-01T14:15:22Z"),
|
||
SellerId: 15,
|
||
},
|
||
raw: `{
|
||
"message_type": "TYPE_CREATE_OR_UPDATE_ITEM",
|
||
"seller_id": 15,
|
||
"offer_id": "1234",
|
||
"product_id": 5678,
|
||
"is_error": false,
|
||
"changed_at": "2022-09-01T14:15:22Z"
|
||
}`,
|
||
}
|
||
}
|
||
|
||
func stateChangedTest(t *testing.T) testData {
|
||
return testData{
|
||
object: &StateChanged{
|
||
Common: Common{MessageType: "TYPE_STATE_CHANGED"},
|
||
PostingNumber: "24219509-0020-2",
|
||
NewState: "posting_delivered",
|
||
ChangedStateDate: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-02-02T15:07:46.765Z"),
|
||
WarehouseId: 1,
|
||
SellerId: 15,
|
||
},
|
||
raw: `{
|
||
"message_type": "TYPE_STATE_CHANGED",
|
||
"posting_number": "24219509-0020-2",
|
||
"new_state": "posting_delivered",
|
||
"changed_state_date": "2021-02-02T15:07:46.765Z",
|
||
"warehouse_id": 1,
|
||
"seller_id": 15
|
||
}`,
|
||
}
|
||
}
|
||
|
||
func messageReadTest(t *testing.T) testData {
|
||
return testData{
|
||
object: &MessageRead{
|
||
LastReadMessageId: "3000000000817031942",
|
||
NewMessage: NewMessage{
|
||
Common: Common{MessageType: "TYPE_MESSAGE_READ"},
|
||
ChatId: "b646d975-0c9c-4872-9f41-8b1e57181063",
|
||
ChatType: "Buyer_Seller",
|
||
MessageId: "3000000000817031942",
|
||
CreatedAt: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2022-07-18T20:58:04.528Z"),
|
||
User: User{
|
||
Id: "115568",
|
||
Type: "Сustomer",
|
||
},
|
||
SellerId: 7,
|
||
},
|
||
},
|
||
raw: `{
|
||
"message_type": "TYPE_MESSAGE_READ",
|
||
"chat_id": "b646d975-0c9c-4872-9f41-8b1e57181063",
|
||
"chat_type": "Buyer_Seller",
|
||
"message_id": "3000000000817031942",
|
||
"created_at": "2022-07-18T20:58:04.528Z",
|
||
"user": {
|
||
"id": "115568",
|
||
"type": "Сustomer"
|
||
},
|
||
"last_read_message_id": "3000000000817031942",
|
||
"seller_id": 7
|
||
}`,
|
||
}
|
||
}
|
||
|
||
func chatClosedTest(t *testing.T) testData {
|
||
return testData{
|
||
object: &ChatClosed{
|
||
Common: Common{MessageType: ChatClosedType},
|
||
ChatId: "b646d975-0c9c-4872-9f41-8b1e57181063",
|
||
ChatType: "Buyer_Seller",
|
||
User: User{Id: "115568", Type: "Customer"},
|
||
SellerId: 7,
|
||
},
|
||
raw: `{
|
||
"message_type": "TYPE_CHAT_CLOSED",
|
||
"chat_id": "b646d975-0c9c-4872-9f41-8b1e57181063",
|
||
"chat_type": "Buyer_Seller",
|
||
"user": {
|
||
"id": "115568",
|
||
"type": "Customer"
|
||
},
|
||
"seller_id": 7
|
||
}`,
|
||
}
|
||
}
|
||
|
||
func TestNotificationServer(t *testing.T) {
|
||
testCases := []struct {
|
||
request testData
|
||
response string
|
||
}{
|
||
{
|
||
pingTest(t),
|
||
`{
|
||
"version": "1.0",
|
||
"name": "Ozon Seller API"
|
||
}`,
|
||
},
|
||
{
|
||
newPostingTest(t),
|
||
`{
|
||
"result": true
|
||
}`,
|
||
},
|
||
{
|
||
postingCancelledTest(t),
|
||
`{
|
||
"result": true
|
||
}`,
|
||
},
|
||
{
|
||
stateChangedTest(t),
|
||
`{
|
||
"result": true
|
||
}`,
|
||
},
|
||
{
|
||
cutoffDateChangedTest(t),
|
||
`{
|
||
"result": true
|
||
}`,
|
||
},
|
||
{
|
||
deliveryDateChangedTest(t),
|
||
`{
|
||
"result": true
|
||
}`,
|
||
},
|
||
{
|
||
createUpdateItemTest(t),
|
||
`{
|
||
"result": true
|
||
}`,
|
||
},
|
||
{
|
||
priceIndexChangedTest(t),
|
||
`{
|
||
"result": true
|
||
}`,
|
||
},
|
||
{
|
||
stocksChangedTest(t),
|
||
`{
|
||
"result": true
|
||
}`,
|
||
},
|
||
{
|
||
newMessageTest(t),
|
||
`{
|
||
"result": true
|
||
}`,
|
||
},
|
||
{
|
||
updateMessageTest(t),
|
||
`{
|
||
"result": true
|
||
}`,
|
||
},
|
||
{
|
||
messageReadTest(t),
|
||
`{
|
||
"result": true
|
||
}`,
|
||
},
|
||
{
|
||
chatClosedTest(t),
|
||
`{
|
||
"result": true
|
||
}`,
|
||
},
|
||
}
|
||
|
||
port := getFreePort()
|
||
|
||
client := http.Client{}
|
||
server := NewNotificationServer(port)
|
||
server.Register(NewPostingType, comparatorWith(newPostingTest(t).object))
|
||
server.Register(PostingCancelledType, comparatorWith(postingCancelledTest(t).object))
|
||
server.Register(StateChangedType, comparatorWith(stateChangedTest(t).object))
|
||
server.Register(CutoffDateChangedType, comparatorWith(cutoffDateChangedTest(t).object))
|
||
server.Register(DeliveryDateChangedType, comparatorWith(deliveryDateChangedTest(t).object))
|
||
server.Register(CreateOrUpdateType, comparatorWith(createUpdateItemTest(t).object))
|
||
server.Register(PriceIndexChangedType, comparatorWith(priceIndexChangedTest(t).object))
|
||
server.Register(StocksChangedType, comparatorWith(stocksChangedTest(t).object))
|
||
server.Register(NewMessageType, comparatorWith(newMessageTest(t).object))
|
||
server.Register(UpdateMessageType, comparatorWith(updateMessageTest(t).object))
|
||
server.Register(MessageReadType, comparatorWith(messageReadTest(t).object))
|
||
server.Register(ChatClosedType, comparatorWith(chatClosedTest(t).object))
|
||
go func() {
|
||
if err := server.Run(); err != nil {
|
||
t.Fatalf("notification server is down: %s", err)
|
||
}
|
||
}()
|
||
|
||
// TODO: get rid of it
|
||
// Needed to make sure server is running
|
||
time.Sleep(3 * time.Second)
|
||
|
||
for _, testCase := range testCases {
|
||
httpResp, err := client.Post(fmt.Sprintf("http://0.0.0.0:%d/", port), "application/json", strings.NewReader(testCase.request.raw))
|
||
if err != nil {
|
||
t.Error(err)
|
||
continue
|
||
}
|
||
|
||
gotJson, err := ioutil.ReadAll(httpResp.Body)
|
||
if err != nil {
|
||
t.Error(err)
|
||
continue
|
||
}
|
||
|
||
expected := map[string]interface{}{}
|
||
got := map[string]interface{}{}
|
||
err = json.Unmarshal(gotJson, &got)
|
||
if err != nil {
|
||
t.Error(err)
|
||
continue
|
||
}
|
||
err = json.Unmarshal([]byte(testCase.response), &expected)
|
||
if err != nil {
|
||
t.Error(err)
|
||
continue
|
||
}
|
||
|
||
if err := compare(expected, got); err != nil {
|
||
t.Error(err)
|
||
continue
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestNotificationServerErrors(t *testing.T) {
|
||
testCases := []struct {
|
||
request testData
|
||
response string
|
||
}{
|
||
{
|
||
testData{
|
||
raw: `{
|
||
"message_type": "string"
|
||
}`,
|
||
},
|
||
`
|
||
{
|
||
"error": {
|
||
"code": "500",
|
||
"message": "unsupported type: string",
|
||
"details": ""
|
||
}
|
||
}`,
|
||
},
|
||
{
|
||
testData{
|
||
raw: `invalid json`,
|
||
},
|
||
`{
|
||
"error": {
|
||
"code": "400",
|
||
"message": "invalid character 'i' looking for beginning of value",
|
||
"details": ""
|
||
}
|
||
}`,
|
||
},
|
||
{
|
||
testData{
|
||
raw: `{
|
||
"message_type": "TYPE_NEW_POSTING",
|
||
"field": [[
|
||
}`,
|
||
},
|
||
`{
|
||
"error": {
|
||
"code": "400",
|
||
"message": "invalid character '}' looking for beginning of value",
|
||
"details": ""
|
||
}
|
||
}`,
|
||
},
|
||
{
|
||
testData{
|
||
raw: `{
|
||
"message_type": "TYPE_NEW_POSTING"
|
||
}`,
|
||
},
|
||
`{
|
||
"result": true
|
||
}`,
|
||
},
|
||
{
|
||
testData{
|
||
raw: `{
|
||
"message_type": "TYPE_PING",
|
||
"time": "2019-08-24T14:15:22Z",
|
||
}`,
|
||
},
|
||
`{
|
||
"error": {
|
||
"code": "400",
|
||
"message": "invalid character '}' looking for beginning of object key string",
|
||
"details": ""
|
||
}
|
||
}`,
|
||
},
|
||
{
|
||
testData{
|
||
raw: `{
|
||
"message_type": "TYPE_CHAT_CLOSED"
|
||
}`,
|
||
},
|
||
`{
|
||
"result": true
|
||
}`,
|
||
},
|
||
}
|
||
|
||
port := getFreePort()
|
||
|
||
client := http.Client{}
|
||
server := NewNotificationServer(port)
|
||
server.Register(NewPostingType, func(req interface{}) error {
|
||
return fmt.Errorf("just error")
|
||
})
|
||
go func() {
|
||
if err := server.Run(); err != nil {
|
||
t.Fatalf("notification server is down: %s", err)
|
||
}
|
||
}()
|
||
|
||
// TODO: get rid of it
|
||
// Needed to make sure server is running
|
||
time.Sleep(3 * time.Second)
|
||
|
||
for _, testCase := range testCases {
|
||
httpResp, err := client.Post(fmt.Sprintf("http://0.0.0.0:%d/", port), "application/json", strings.NewReader(testCase.request.raw))
|
||
if err != nil {
|
||
t.Error(err)
|
||
continue
|
||
continue
|
||
}
|
||
|
||
gotJson, err := ioutil.ReadAll(httpResp.Body)
|
||
if err != nil {
|
||
t.Error(err)
|
||
continue
|
||
continue
|
||
}
|
||
|
||
expected := map[string]interface{}{}
|
||
got := map[string]interface{}{}
|
||
err = json.Unmarshal(gotJson, &got)
|
||
if err != nil {
|
||
t.Error(err)
|
||
continue
|
||
continue
|
||
}
|
||
err = json.Unmarshal([]byte(testCase.response), &expected)
|
||
if err != nil {
|
||
t.Error(err)
|
||
continue
|
||
continue
|
||
}
|
||
|
||
if err := compare(expected, got); err != nil {
|
||
t.Error(err)
|
||
continue
|
||
continue
|
||
}
|
||
}
|
||
}
|
||
|
||
func compare(expected map[string]interface{}, got map[string]interface{}) error {
|
||
for k, v := range expected {
|
||
if gotValue, ok := got[k]; !ok {
|
||
return fmt.Errorf("key %s is expected to present", k)
|
||
} else if !reflect.DeepEqual(gotValue, v) {
|
||
return fmt.Errorf("key %s is not equal, got: %v, want: %v", k, gotValue, v)
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func getFreePort() int {
|
||
listener, _ := net.Listen("tcp", ":0")
|
||
defer listener.Close()
|
||
|
||
return listener.Addr().(*net.TCPAddr).Port
|
||
}
|
||
|
||
func comparatorWith(v1 interface{}) func(v2 interface{}) error {
|
||
return func(v2 interface{}) error {
|
||
if !reflect.DeepEqual(v1, v2) {
|
||
return fmt.Errorf("objects are not equal:\n got: %#v,\n want: %#v", v2, v1)
|
||
}
|
||
return nil
|
||
}
|
||
}
|