Untitled

 avatar
unknown
plain_text
a month ago
12 kB
4
Indexable
package orderv1

import (
	"context"
	"database/sql"
	"errors"
	"testing"

	"connectrpc.com/connect"
	"github.com/google/uuid"
	"github.com/newmo-oss/ctxtime"
	"go.uber.org/mock/gomock"
	"google.golang.org/genproto/googleapis/type/money"
	"google.golang.org/protobuf/types/known/timestamppb"

	"github.com/kouzoh/mercari-globalone/internal/layers/tier2/modules/order/infrastructure/db"
	"github.com/kouzoh/mercari-globalone/internal/mercari/authx"
	"github.com/kouzoh/mercari-globalone/internal/pkg/pkgcmp"
	i18n "github.com/kouzoh/mercari-globalone/internal/pkg/proto/framework/i18n/v1"
	orderv1 "github.com/kouzoh/mercari-globalone/internal/pkg/proto/layers/tier2/modules/order/applications/order/v1"
	"github.com/kouzoh/mercari-globalone/internal/pkg/types"
)

func TestListOrders_Success(t *testing.T) {
	t.Parallel()

	type args struct {
		ctx context.Context
		req *connect.Request[orderv1.ListOrdersRequest]
	}
	type fields struct {
		db func(ctrl *gomock.Controller) DB
	}

	fakeUserID := "123"
	now := ctxtime.Now(context.Background())
	defUUID1 := uuid.MustParse("bbebb006-5582-405a-b54e-be16c02a23fd")
	defUUID2 := uuid.MustParse("75c78443-b3ec-439d-a420-ec6ccb73a2f7")

	tests := []struct {
		name   string
		args   args
		fields fields
		want   *connect.Response[orderv1.ListOrdersResponse]
	}{
		{
			name: "success: list orders",
			args: args{
				ctx: authx.GetFakeMercariPAT(context.Background(), t, fakeUserID),
				req: connect.NewRequest(&orderv1.ListOrdersRequest{
					BuyerId:  fakeUserID,
					PageSize: 2,
				}),
			},
			fields: fields{
				db: mockDBWithOrders(defUUID1, defUUID2, fakeUserID),
			},
			want: createSuccessResponse(defUUID1, defUUID2, fakeUserID),
		},
		{
			name: "success: list orders not found",
			args: args{
				ctx: authx.GetFakeMercariPAT(context.Background(), t, fakeUserID),
				req: connect.NewRequest(&orderv1.ListOrdersRequest{
					BuyerId:  fakeUserID,
					PageSize: 1,
					PageToken: func() string {
						token, err := buildListOrdersPageToken(uuid.New().String(), now)
						if err != nil {
							return ""
						}
						return token
					}(),
				}),
			},
			fields: fields{db: mockDBWithNoOrders()},
			want:   connect.NewResponse(&orderv1.ListOrdersResponse{}),
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			app := NewApplication(nil, tt.fields.db(gomock.NewController(t)))

			res, err := app.ListOrders(tt.args.ctx, tt.args.req)
			if err != nil {
				t.Errorf("ListOrders() failed - error = %v", err)
			}

			if diff := pkgcmp.Diff(tt.want, res); diff != "" {
				t.Errorf("ListOrders() response mismatch = (-want +got):\n%s, %v", diff, err)
			}
		})
	}
}

func mockDBWithOrders(defUUID1, defUUID2 uuid.UUID, fakeUserID string) func(ctrl *gomock.Controller) DB {
	return func(ctrl *gomock.Controller) DB {
		mockDB := NewMockDB(ctrl)

		// Mock the ListOrdersByBuyerID function
		mockDB.EXPECT().ListOrdersByBuyerID(gomock.Any(), db.ListOrdersByBuyerIDParams{
			BuyerID: "123",
			Limit:   3,
		}).Return([]db.Order{
			{ID: defUUID1, BuyerID: fakeUserID},
			{ID: defUUID2, BuyerID: fakeUserID},
		}, nil)

		// Mock the ListOrderProductsWithSnapshotByOrderIDs function
		mockDB.EXPECT().ListOrderProductsWithSnapshotByOrderIDs(gomock.Any(), db.ListOrderProductsWithSnapshotByOrderIDsParams{
			OrderIds:  []uuid.UUID{defUUID1, defUUID2},
			IsDeleted: false,
		}).Return([]db.ListOrderProductsWithSnapshotByOrderIDsRow{
			{
				OrderProduct: db.OrderProduct{
					ID:                defUUID1,
					OrderID:           defUUID1,
					ProductIdentifier: "product-3456",
					SellerID:          "seller-9012",
					Status:            db.OrderProductStatusPlaced,
					Quantity:          2,
					UnitPrice:         "1000.0",
					CurrencyCode:      "USD",
					LifecycleID:       types.StringToNullUUID(types.Ptr("e540af23-4561-4989-a9f4-a566d15ef78c")),
				},
				OrderProductSnapshot: db.OrderProductSnapshot{
					OrderProductID: defUUID1,
					Title:          "Sample Product",
					Body:           sql.NullString{String: "This is a sample product", Valid: true},
					LanguageCode:   "en",
				},
			},
			{
				OrderProduct: db.OrderProduct{
					ID:                defUUID2,
					OrderID:           defUUID2,
					ProductIdentifier: "product-3456",
					SellerID:          "seller-9012",
					Status:            db.OrderProductStatusPlaced,
					Quantity:          2,
					UnitPrice:         "1000.0",
					CurrencyCode:      "USD",
					LifecycleID:       types.StringToNullUUID(types.Ptr("e540af23-4561-4989-a9f4-a566d15ef78c")),
				},
				OrderProductSnapshot: db.OrderProductSnapshot{
					OrderProductID: defUUID2,
					Title:          "Sample Product",
					Body:           sql.NullString{String: "This is a sample product", Valid: true},
					LanguageCode:   "en",
				},
			},
		}, nil)

		// Mock the ListOrderExtraFeesByOrderIDs function
		mockDB.EXPECT().ListOrderExtraFeesByOrderIDs(gomock.Any(), db.ListOrderExtraFeesByOrderIDsParams{
			OrderIds:  []uuid.UUID{defUUID1, defUUID2},
			IsDeleted: false,
		}).Return([]db.OrderExtraFee{
			{
				ID:           defUUID1,
				OrderID:      defUUID1,
				Payer:        OrderExtraFeePayertoDB(orderv1.OrderExtraFee_EXTRA_FEE_PAYER_BUYER),
				Type:         OrderExtraFeeTypetoDB(orderv1.OrderExtraFee_EXTRA_FEE_TYPE_PROXY_SERVICE_PLAN_FEE),
				Amount:       "100.0",
				CurrencyCode: "USD",
			},
			{
				ID:           defUUID2,
				OrderID:      defUUID2,
				Payer:        OrderExtraFeePayertoDB(orderv1.OrderExtraFee_EXTRA_FEE_PAYER_BUYER),
				Type:         OrderExtraFeeTypetoDB(orderv1.OrderExtraFee_EXTRA_FEE_TYPE_PROXY_SERVICE_PLAN_FEE),
				Amount:       "100.0",
				CurrencyCode: "USD",
			},
		}, nil)

		return mockDB
	}
}

func mockDBWithNoOrders() func(ctrl *gomock.Controller) DB {
	return func(ctrl *gomock.Controller) DB {
		mockDB := NewMockDB(ctrl)
		mockDB.EXPECT().ListOrdersByCursor(gomock.Any(), gomock.Any()).Return([]db.Order{}, sql.ErrNoRows).Times(1)
		return mockDB
	}
}

func createSuccessResponse(defUUID1, defUUID2 uuid.UUID, fakeUserID string) *connect.Response[orderv1.ListOrdersResponse] {
	return connect.NewResponse(&orderv1.ListOrdersResponse{
		Orders: []*orderv1.Order{
			{
				OrderId: defUUID1.String(),
				BuyerId: fakeUserID,
				OrderProducts: []*orderv1.OrderProduct{
					{
						OrderProductId:    defUUID1.String(),
						SellerId:          "seller-9012",
						State:             orderv1.OrderProduct_ORDER_PRODUCT_STATE_PLACED,
						ProductIdentifier: "product-3456",
						Title:             "Sample Product",
						Body:              "This is a sample product",
						UnitPrice: &money.Money{
							CurrencyCode: "USD",
							Units:        1000,
						},
						Quantity:    2,
						LifecycleId: "e540af23-4561-4989-a9f4-a566d15ef78c",
						Language:    &i18n.Language{Code: "en"},
						Events: []orderv1.Event{
							orderv1.Event_EVENT_XB_PROXY_PARTNER_PURCHASED,
							orderv1.Event_EVENT_CANCELED,
						},
					},
				},
			},
			{
				OrderId: defUUID2.String(),
				BuyerId: fakeUserID,
				OrderProducts: []*orderv1.OrderProduct{
					{
						OrderProductId:    defUUID2.String(),
						SellerId:          "seller-9012",
						State:             orderv1.OrderProduct_ORDER_PRODUCT_STATE_PLACED,
						ProductIdentifier: "product-3456",
						Title:             "Sample Product",
						Body:              "This is a sample product",
						UnitPrice: &money.Money{
							CurrencyCode: "USD",
							Units:        1000,
						},
						Quantity:    2,
						LifecycleId: "e540af23-4561-4989-a9f4-a566d15ef78c",
						Language:    &i18n.Language{Code: "en"},
						Events: []orderv1.Event{
							orderv1.Event_EVENT_XB_PROXY_PARTNER_PURCHASED,
							orderv1.Event_EVENT_CANCELED,
						},
					},
				},
			},
		},
	})
}

func TestListOrders_Error(t *testing.T) {
	t.Parallel()

	type args struct {
		ctx context.Context
		req *connect.Request[orderv1.ListOrdersRequest]
	}

	type fields struct {
		db func(ctrl *gomock.Controller) DB
	}

	fakeUserID := "123"

	tests := []struct {
		name        string
		args        args
		fields      fields
		wantErrCode connect.Code
	}{
		{
			name: "error: proto validation",
			args: args{
				ctx: authx.GetFakeMercariPAT(context.Background(), t, "789"),
				req: connect.NewRequest(&orderv1.ListOrdersRequest{
					PageSize: 2,
				}),
			},
			fields: fields{db: func(ctrl *gomock.Controller) DB {
				mockDB := NewMockDB(ctrl)
				return mockDB
			}},
			wantErrCode: connect.CodeInvalidArgument,
		},
		{
			name: "error: anonymous user not allowed",
			args: args{
				ctx: context.Background(),
				req: connect.NewRequest(&orderv1.ListOrdersRequest{
					BuyerId:  fakeUserID,
					PageSize: 2,
				}),
			},
			fields: fields{db: func(ctrl *gomock.Controller) DB {
				mockDB := NewMockDB(ctrl)
				return mockDB
			}},
			wantErrCode: connect.CodeUnauthenticated,
		},
		{
			name: "error: permission denied",
			args: args{
				ctx: authx.GetFakeMercariPAT(context.Background(), t, "789"),
				req: connect.NewRequest(&orderv1.ListOrdersRequest{
					BuyerId:  fakeUserID,
					PageSize: 2,
				}),
			},
			fields: fields{db: func(ctrl *gomock.Controller) DB {
				mockDB := NewMockDB(ctrl)
				return mockDB
			}},
			wantErrCode: connect.CodePermissionDenied,
		},
		{
			name: "error: invalid page_token",
			args: args{
				ctx: authx.GetFakeMercariPAT(context.Background(), t, fakeUserID),
				req: connect.NewRequest(&orderv1.ListOrdersRequest{
					BuyerId:   fakeUserID,
					PageSize:  2,
					PageToken: "yo",
				}),
			},
			fields: fields{db: func(ctrl *gomock.Controller) DB {
				mockDB := NewMockDB(ctrl)
				return mockDB
			}},
			wantErrCode: connect.CodeInvalidArgument,
		},
		{
			name: "error: list orders",
			args: args{
				ctx: authx.GetFakeMercariPAT(context.Background(), t, fakeUserID),
				req: connect.NewRequest(&orderv1.ListOrdersRequest{
					BuyerId:  fakeUserID,
					PageSize: 2,
				}),
			},
			fields: fields{db: func(ctrl *gomock.Controller) DB {
				mockDB := NewMockDB(ctrl)
				mockDB.EXPECT().ListOrdersByBuyerID(gomock.Any(), gomock.Any()).Return([]db.Order{}, connect.NewError(connect.CodeInvalidArgument, errors.New("invalid argument"))).Times(1)
				return mockDB
			}},
			wantErrCode: connect.CodeInvalidArgument,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			app := NewApplication(nil, tt.fields.db(gomock.NewController(t)))

			_, err := app.ListOrders(tt.args.ctx, tt.args.req)
			if got := connect.CodeOf(err); got != tt.wantErrCode {
				t.Errorf("ListOrders() error_code = %v, want %v", got, tt.wantErrCode)
			}
		})
	}
}

func getBaseReqOrder() *orderv1.Order {
	return &orderv1.Order{
		BuyerId: "buyer-5678",
		OrderProducts: []*orderv1.OrderProduct{
			{
				SellerId:          "seller-9012",
				ProductIdentifier: "product-3456",
				Title:             "Sample Product",
				Body:              "This is a sample product",
				UnitPrice: &money.Money{
					Units:        1000,
					Nanos:        0,
					CurrencyCode: "USD",
				},
				Quantity: 2,
				Language: &i18n.Language{
					Code: "en",
				},
				Metadata:    map[string]string{"size": "M"},
				LifecycleId: xbLifecycleID,
			},
		},
		OrderExtraFees: []*orderv1.OrderExtraFee{{
			OrderProductId: "",
			Type:           orderv1.OrderExtraFee_EXTRA_FEE_TYPE_PROXY_SERVICE_PLAN_FEE,
			Amount: &money.Money{
				CurrencyCode: "USD",
				Units:        100,
				Nanos:        0,
			},
			Payer: orderv1.OrderExtraFee_EXTRA_FEE_PAYER_BUYER,
		}},
		CreateTime: &timestamppb.Timestamp{Seconds: 1688208000},
	}
}

func getBaseRespOrder(uuid uuid.UUID) *orderv1.Order {
	order := getBaseReqOrder()
	order.OrderId = uuid.String()
	order.GetOrderProducts()[0].OrderProductId = uuid.String()
	order.GetOrderProducts()[0].State = orderv1.OrderProduct_ORDER_PRODUCT_STATE_PLACED
	order.GetOrderProducts()[0].LifecycleId = xbLifecycleID
	order.GetOrderProducts()[0].Events = []orderv1.Event{
		orderv1.Event_EVENT_XB_PROXY_PARTNER_PURCHASED,
		orderv1.Event_EVENT_CANCELED,
	}
	order.GetOrderExtraFees()[0].OrderExtraFeeId = uuid.String()
	return order
}
Editor is loading...
Leave a Comment