material_manage_system/internal/service/organization_access_test.go
文睿 张 970ba5ccf7 feat: 组织列表为每家公司补充仓库摘要
组织初始化接口 GET /api/auth/organizations 现在为每项授权公司附带
warehouse 摘要({id, name, status, location: {id, name, status} | null} | null),
结构与切换接口一致,供普通用户在公司切换器直接展示,无需放开管理员仓库配置接口。

- model:新增 WarehouseSummary/LocationSummary/OrganizationSummary,
  OrganizationAccess 每项改为内嵌公司资料再加 warehouse 摘要。
- repository:新增 WarehouseRepository.Summary 复用切换接口的解析规则;
  ListAccessible 逐公司补齐摘要,仓库或库位配置不唯一时报错而非随意取第一条,
  无仓库、无库位或已停用如实返回,不回退旧公司数据。
- service:Switch 复用 List 已解析的摘要,响应格式保持不变。
- 测试:新增服务层 sqlmock 用例与仓储层 MySQL 集成用例,
  覆盖启用唯一仓库加启用库位、无仓库、仓库停用三类。
2026-10-09 10:43:35 +08:00

72 lines
3.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"testing"
"time"
"github.com/DATA-DOG/go-sqlmock"
"github.com/wmms/wmms/pkg/database"
)
func organizationAccessMock(t *testing.T) sqlmock.Sqlmock {
t.Helper()
db, mock, err := sqlmock.New()
if err != nil {
t.Fatal(err)
}
previous := database.DB
database.DB = db
t.Cleanup(func() { database.DB = previous; db.Close() })
return mock
}
// 组织初始化列表要为每家公司附带与切换接口一致的仓库摘要。这里覆盖
// 无仓库(null)、仓库停用(如实返回停用状态)、启用唯一仓库加启用库位三类。
func TestOrganizationAccessListIncludesWarehouseSummary(t *testing.T) {
mock := organizationAccessMock(t)
now := time.Now()
organizationRows := sqlmock.NewRows([]string{"id", "code", "name", "status", "default_timezone", "created_at", "updated_at", "is_default"}).
AddRow(2, "TWO", "无仓库公司", "active", "Asia/Shanghai", now, now, false).
AddRow(3, "THREE", "停用仓库公司", "active", "Asia/Shanghai", now, now, false).
AddRow(4, "FOUR", "启用仓库公司", "active", "Asia/Shanghai", now, now, true)
mock.ExpectQuery("SELECT o.id,o.code,o.name,o.status").WithArgs(int64(7), int64(7)).WillReturnRows(organizationRows)
warehouseColumns := []string{"id", "organization_id", "code", "name", "status", "location_id", "location_code", "location_name", "location_status"}
// 无仓库:查询返回空集,摘要为 null。
mock.ExpectQuery("SELECT w.id, w.organization_id").WithArgs(int64(2)).WillReturnRows(sqlmock.NewRows(warehouseColumns))
// 仓库停用:仍返回该公司自己的仓库与库位,状态为停用。
mock.ExpectQuery("SELECT w.id, w.organization_id").WithArgs(int64(3)).
WillReturnRows(sqlmock.NewRows(warehouseColumns).AddRow(30, 3, "D3", "停用仓库", "inactive", 31, "L3", "停用仓库的库位", "active"))
// 启用唯一仓库加启用库位。
mock.ExpectQuery("SELECT w.id, w.organization_id").WithArgs(int64(4)).
WillReturnRows(sqlmock.NewRows(warehouseColumns).AddRow(40, 4, "D4", "启用仓库", "active", 41, "L4", "启用仓库的库位", "active"))
access, err := NewOrganizationAccessService().List(7)
if err != nil {
t.Fatal(err)
}
if len(access.Organizations) != 3 || access.DefaultOrganizationID == nil || *access.DefaultOrganizationID != 4 {
t.Fatalf("公司列表错误 %+v", access)
}
byID := make(map[int64]int, len(access.Organizations))
for i, item := range access.Organizations {
byID[item.ID] = i
}
if warehouse := access.Organizations[byID[2]].Warehouse; warehouse != nil {
t.Fatalf("无仓库公司应返回空摘要,实际 %+v", warehouse)
}
disabled := access.Organizations[byID[3]].Warehouse
if disabled == nil || disabled.ID != 30 || disabled.Status != "inactive" ||
disabled.Location == nil || disabled.Location.ID != 31 || disabled.Location.Status != "active" {
t.Fatalf("停用仓库摘要错误 %+v", disabled)
}
enabled := access.Organizations[byID[4]].Warehouse
if enabled == nil || enabled.ID != 40 || enabled.Status != "active" ||
enabled.Location == nil || enabled.Location.ID != 41 || enabled.Location.Status != "active" {
t.Fatalf("启用仓库摘要错误 %+v", enabled)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Fatal(err)
}
}