89 lines
3.2 KiB
Go
89 lines
3.2 KiB
Go
package service
|
||
|
||
import (
|
||
"errors"
|
||
"strings"
|
||
"testing"
|
||
|
||
"github.com/DATA-DOG/go-sqlmock"
|
||
)
|
||
|
||
// expectWarehouseSummary 让 WarehouseRepository.List 返回一组仓库/库位,用于驱动
|
||
// ResolveInventoryScope。传 nil 表示"该公司没有仓库"。
|
||
func expectWarehouseSummary(mock sqlmock.Sqlmock, organizationID int64, warehouseStatus, locationStatus *string) {
|
||
rows := sqlmock.NewRows([]string{"id", "organization_id", "code", "name", "status", "location_id", "location_code", "location_name", "location_status"})
|
||
if warehouseStatus != nil {
|
||
var locationID interface{} = nil
|
||
var locationCode, locationName, locStatus interface{} = nil, nil, nil
|
||
if locationStatus != nil {
|
||
locationID, locationCode, locationName, locStatus = 20, "L", "库位", *locationStatus
|
||
}
|
||
rows.AddRow(10, organizationID, "W", "仓库", *warehouseStatus, locationID, locationCode, locationName, locStatus)
|
||
}
|
||
mock.ExpectQuery("SELECT w.id, w.organization_id").WithArgs(organizationID).WillReturnRows(rows)
|
||
}
|
||
|
||
func strptr(s string) *string { return &s }
|
||
|
||
func TestResolveInventoryScopeUnavailableCases(t *testing.T) {
|
||
cases := []struct {
|
||
name string
|
||
warehouseStatus *string
|
||
locationStatus *string
|
||
requireActive bool
|
||
wantSubstring string
|
||
}{
|
||
{"no warehouse", nil, nil, true, "请先配置本公司仓库"},
|
||
{"no location", strptr("active"), nil, true, "请先配置本公司仓库的库位"},
|
||
{"disabled warehouse", strptr("inactive"), strptr("active"), true, "本公司仓库已停用"},
|
||
{"disabled location", strptr("active"), strptr("inactive"), true, "本公司库位已停用"},
|
||
}
|
||
for _, tc := range cases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
mock := outboundMock(t)
|
||
expectWarehouseSummary(mock, 1, tc.warehouseStatus, tc.locationStatus)
|
||
_, err := ResolveInventoryScope(1, tc.requireActive)
|
||
if !errors.Is(err, ErrInventoryScopeUnavailable) {
|
||
t.Fatalf("got %v, want ErrInventoryScopeUnavailable", err)
|
||
}
|
||
if !strings.Contains(err.Error(), tc.wantSubstring) {
|
||
t.Fatalf("message %q must contain %q", err.Error(), tc.wantSubstring)
|
||
}
|
||
if err := mock.ExpectationsWereMet(); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestResolveInventoryScopeReturnsActiveDimensions(t *testing.T) {
|
||
mock := outboundMock(t)
|
||
expectWarehouseSummary(mock, 3, strptr("active"), strptr("active"))
|
||
scope, err := ResolveInventoryScope(3, true)
|
||
if err != nil {
|
||
t.Fatalf("resolve scope: %v", err)
|
||
}
|
||
if scope.OrganizationID != 3 || scope.WarehouseID != 10 || scope.LocationID != 20 {
|
||
t.Fatalf("scope=%+v, want 3/10/20", scope)
|
||
}
|
||
if err := mock.ExpectationsWereMet(); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
}
|
||
|
||
// 只读场景(requireActive=false):停用的仓库、库位仍返回其维度,方便核验历史余额。
|
||
func TestResolveInventoryScopeKeepsDisabledDimensionsForReads(t *testing.T) {
|
||
mock := outboundMock(t)
|
||
expectWarehouseSummary(mock, 1, strptr("inactive"), strptr("inactive"))
|
||
scope, err := ResolveInventoryScope(1, false)
|
||
if err != nil {
|
||
t.Fatalf("resolve scope for reads: %v", err)
|
||
}
|
||
if scope.WarehouseID != 10 || scope.LocationID != 20 {
|
||
t.Fatalf("scope=%+v, want disabled dimensions preserved", scope)
|
||
}
|
||
if err := mock.ExpectationsWereMet(); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
}
|