package integration import ( "encoding/json" "fmt" "testing" "time" "github.com/break/junhong_cmp_fiber/internal/model" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/response" "github.com/break/junhong_cmp_fiber/tests/testutils/integ" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestDevice_List(t *testing.T) { env := integ.NewIntegrationTestEnv(t) // 创建测试设备 devices := []*model.Device{ {DeviceNo: "TEST_DEVICE_001", DeviceName: "测试设备1", DeviceType: "router", MaxSimSlots: 4, Status: constants.DeviceStatusInStock}, {DeviceNo: "TEST_DEVICE_002", DeviceName: "测试设备2", DeviceType: "router", MaxSimSlots: 2, Status: constants.DeviceStatusInStock}, {DeviceNo: "TEST_DEVICE_003", DeviceName: "测试设备3", DeviceType: "mifi", MaxSimSlots: 1, Status: constants.DeviceStatusDistributed}, } for _, device := range devices { require.NoError(t, env.TX.Create(device).Error) } t.Run("获取设备列表-无过滤", func(t *testing.T) { resp, err := env.AsSuperAdmin().Request("GET", "/api/admin/devices?page=1&page_size=20", nil) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, 200, resp.StatusCode) var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.Equal(t, 0, result.Code) }) t.Run("获取设备列表-按设备类型过滤", func(t *testing.T) { resp, err := env.AsSuperAdmin().Request("GET", "/api/admin/devices?device_type=router", nil) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, 200, resp.StatusCode) var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.Equal(t, 0, result.Code) }) t.Run("获取设备列表-按状态过滤", func(t *testing.T) { resp, err := env.AsSuperAdmin().Request("GET", fmt.Sprintf("/api/admin/devices?status=%d", constants.DeviceStatusInStock), nil) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, 200, resp.StatusCode) var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.Equal(t, 0, result.Code) }) t.Run("未认证请求应返回错误", func(t *testing.T) { resp, err := env.ClearAuth().Request("GET", "/api/admin/devices", nil) require.NoError(t, err) defer resp.Body.Close() var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.NotEqual(t, 0, result.Code, "未认证请求应返回错误码") }) } func TestDevice_GetByID(t *testing.T) { env := integ.NewIntegrationTestEnv(t) // 创建测试设备 device := &model.Device{ DeviceNo: "TEST_DEVICE_GET_001", DeviceName: "测试设备详情", DeviceType: "router", MaxSimSlots: 4, Status: constants.DeviceStatusInStock, } require.NoError(t, env.TX.Create(device).Error) t.Run("获取设备详情-成功", func(t *testing.T) { url := fmt.Sprintf("/api/admin/devices/%d", device.ID) resp, err := env.AsSuperAdmin().Request("GET", url, nil) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, 200, resp.StatusCode) var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.Equal(t, 0, result.Code) // 验证返回数据 dataMap, ok := result.Data.(map[string]interface{}) require.True(t, ok) assert.Equal(t, "TEST_DEVICE_GET_001", dataMap["device_no"]) }) t.Run("获取不存在的设备-应返回错误", func(t *testing.T) { resp, err := env.AsSuperAdmin().Request("GET", "/api/admin/devices/999999", nil) require.NoError(t, err) defer resp.Body.Close() var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.NotEqual(t, 0, result.Code, "不存在的设备应返回错误码") }) } func TestDevice_Delete(t *testing.T) { env := integ.NewIntegrationTestEnv(t) device := &model.Device{ DeviceNo: "TEST_DEVICE_DEL_001", DeviceName: "测试删除设备", DeviceType: "router", MaxSimSlots: 4, Status: constants.DeviceStatusInStock, } require.NoError(t, env.TX.Create(device).Error) t.Run("删除设备-成功", func(t *testing.T) { url := fmt.Sprintf("/api/admin/devices/%d", device.ID) resp, err := env.AsSuperAdmin().Request("DELETE", url, nil) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, 200, resp.StatusCode) var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.Equal(t, 0, result.Code) var deletedDevice model.Device err = env.RawDB().Unscoped().First(&deletedDevice, device.ID).Error require.NoError(t, err) assert.NotNil(t, deletedDevice.DeletedAt) }) } func TestDeviceImport_TaskList(t *testing.T) { env := integ.NewIntegrationTestEnv(t) task := &model.DeviceImportTask{ TaskNo: "TEST_DEVICE_IMPORT_001", Status: model.ImportTaskStatusCompleted, BatchNo: "TEST_BATCH_001", TotalCount: 100, } require.NoError(t, env.TX.Create(task).Error) t.Run("获取导入任务列表", func(t *testing.T) { resp, err := env.AsSuperAdmin().Request("GET", "/api/admin/devices/import/tasks?page=1&page_size=20", nil) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, 200, resp.StatusCode) var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.Equal(t, 0, result.Code) }) t.Run("获取导入任务详情", func(t *testing.T) { url := fmt.Sprintf("/api/admin/devices/import/tasks/%d", task.ID) resp, err := env.AsSuperAdmin().Request("GET", url, nil) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, 200, resp.StatusCode) var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.Equal(t, 0, result.Code) }) } func TestDevice_GetByIMEI(t *testing.T) { env := integ.NewIntegrationTestEnv(t) // 创建测试设备 device := &model.Device{ DeviceNo: "TEST_IMEI_001", DeviceName: "测试IMEI查询设备", DeviceType: "router", MaxSimSlots: 4, Status: constants.DeviceStatusInStock, } require.NoError(t, env.TX.Create(device).Error) t.Run("通过IMEI查询设备详情-成功", func(t *testing.T) { url := fmt.Sprintf("/api/admin/devices/by-imei/%s", device.DeviceNo) resp, err := env.AsSuperAdmin().Request("GET", url, nil) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, 200, resp.StatusCode) var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.Equal(t, 0, result.Code) // 验证返回数据 dataMap, ok := result.Data.(map[string]interface{}) require.True(t, ok) assert.Equal(t, "TEST_IMEI_001", dataMap["device_no"]) assert.Equal(t, "测试IMEI查询设备", dataMap["device_name"]) }) t.Run("通过不存在的IMEI查询-应返回错误", func(t *testing.T) { resp, err := env.AsSuperAdmin().Request("GET", "/api/admin/devices/by-imei/NONEXISTENT_IMEI", nil) require.NoError(t, err) defer resp.Body.Close() var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.NotEqual(t, 0, result.Code, "不存在的IMEI应返回错误码") }) t.Run("未认证请求-应返回错误", func(t *testing.T) { url := fmt.Sprintf("/api/admin/devices/by-imei/%s", device.DeviceNo) resp, err := env.ClearAuth().Request("GET", url, nil) require.NoError(t, err) defer resp.Body.Close() var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.NotEqual(t, 0, result.Code, "未认证请求应返回错误码") }) } func TestDevice_BatchSetSeriesBinding(t *testing.T) { env := integ.NewIntegrationTestEnv(t) shop := env.CreateTestShop("测试店铺", 1, nil) agentAccount := env.CreateTestAccount(fmt.Sprintf("agent_dev_%d", time.Now().UnixNano()), "password123", constants.UserTypeAgent, &shop.ID, nil) series := createTestPackageSeries(t, env, "测试系列") createTestAllocation(t, env, shop.ID, series.ID, 0) devices := []*model.Device{ {DeviceNo: fmt.Sprintf("DEV_%d_001", time.Now().UnixNano()), DeviceName: "测试设备1", DeviceType: "router", MaxSimSlots: 4, Status: constants.DeviceStatusInStock, ShopID: &shop.ID}, {DeviceNo: fmt.Sprintf("DEV_%d_002", time.Now().UnixNano()), DeviceName: "测试设备2", DeviceType: "mifi", MaxSimSlots: 2, Status: constants.DeviceStatusInStock, ShopID: &shop.ID}, {DeviceNo: fmt.Sprintf("DEV_%d_003", time.Now().UnixNano()), DeviceName: "测试设备3", DeviceType: "router", MaxSimSlots: 4, Status: constants.DeviceStatusInStock, ShopID: &shop.ID}, } for _, device := range devices { require.NoError(t, env.TX.Create(device).Error) } t.Run("批量设置设备系列绑定-成功", func(t *testing.T) { body := map[string]interface{}{ "device_ids": []uint{devices[0].ID, devices[1].ID}, "series_id": series.ID, } jsonBody, _ := json.Marshal(body) resp, err := env.AsUser(agentAccount).Request("PATCH", "/api/admin/devices/series-binding", jsonBody) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, 200, resp.StatusCode) var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.Equal(t, 0, result.Code, "应返回成功: %s", result.Message) if result.Data != nil { dataMap := result.Data.(map[string]interface{}) assert.Equal(t, float64(2), dataMap["success_count"], "应有2个设备成功绑定") assert.Equal(t, float64(0), dataMap["fail_count"], "应无失败") } else { t.Logf("Response data is nil: code=%d, message=%s", result.Code, result.Message) } var updatedDevice model.Device err = env.RawDB().Where("id = ?", devices[0].ID).First(&updatedDevice).Error require.NoError(t, err) assert.NotNil(t, updatedDevice.SeriesID) assert.Equal(t, series.ID, *updatedDevice.SeriesID) }) t.Run("清除设备系列绑定-series_id=0", func(t *testing.T) { body := map[string]interface{}{ "device_ids": []uint{devices[0].ID}, "series_id": 0, } jsonBody, _ := json.Marshal(body) resp, err := env.AsUser(agentAccount).Request("PATCH", "/api/admin/devices/series-binding", jsonBody) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, 200, resp.StatusCode) var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.Equal(t, 0, result.Code) var updatedDevice model.Device err = env.RawDB().Where("id = ?", devices[0].ID).First(&updatedDevice).Error require.NoError(t, err) assert.Nil(t, updatedDevice.SeriesID, "系列分配应被清除") }) t.Run("批量设置-部分设备不存在", func(t *testing.T) { body := map[string]interface{}{ "device_ids": []uint{devices[2].ID, 999999}, "series_id": series.ID, } jsonBody, _ := json.Marshal(body) resp, err := env.AsUser(agentAccount).Request("PATCH", "/api/admin/devices/series-binding", jsonBody) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, 200, resp.StatusCode) var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.Equal(t, 0, result.Code) if result.Data != nil { dataMap := result.Data.(map[string]interface{}) assert.Equal(t, float64(1), dataMap["success_count"], "应有1个设备成功") assert.Equal(t, float64(1), dataMap["fail_count"], "应有1个设备失败") if dataMap["failed_items"] != nil { failedItems := dataMap["failed_items"].([]interface{}) assert.Len(t, failedItems, 1) failedItem := failedItems[0].(map[string]interface{}) assert.Equal(t, float64(999999), failedItem["device_id"]) } } }) t.Run("设置不存在的系列分配-应失败", func(t *testing.T) { body := map[string]interface{}{ "device_ids": []uint{devices[2].ID}, "series_id": 999999, } jsonBody, _ := json.Marshal(body) resp, err := env.AsUser(agentAccount).Request("PATCH", "/api/admin/devices/series-binding", jsonBody) require.NoError(t, err) defer resp.Body.Close() var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.NotEqual(t, 0, result.Code, "不存在的系列分配应返回错误") }) t.Run("设置禁用的系列-应失败", func(t *testing.T) { disabledSeries := createTestPackageSeries(t, env, "禁用系列") env.TX.Model(&model.PackageSeries{}).Where("id = ?", disabledSeries.ID).Update("status", constants.StatusDisabled) body := map[string]interface{}{ "device_ids": []uint{devices[2].ID}, "series_id": disabledSeries.ID, } jsonBody, _ := json.Marshal(body) resp, err := env.AsUser(agentAccount).Request("PATCH", "/api/admin/devices/series-binding", jsonBody) require.NoError(t, err) defer resp.Body.Close() var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.NotEqual(t, 0, result.Code, "禁用的系列分配应返回错误") }) t.Run("代理商设置其他店铺的设备-应失败", func(t *testing.T) { otherShop := env.CreateTestShop("其他店铺", 1, nil) otherDevice := &model.Device{ DeviceNo: fmt.Sprintf("OTHER_%d", time.Now().UnixNano()), DeviceName: "其他设备", DeviceType: "router", MaxSimSlots: 4, Status: constants.DeviceStatusInStock, ShopID: &otherShop.ID, } require.NoError(t, env.TX.Create(otherDevice).Error) body := map[string]interface{}{ "device_ids": []uint{otherDevice.ID}, "series_id": series.ID, } jsonBody, _ := json.Marshal(body) resp, err := env.AsUser(agentAccount).Request("PATCH", "/api/admin/devices/series-binding", jsonBody) require.NoError(t, err) defer resp.Body.Close() var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) dataMap := result.Data.(map[string]interface{}) assert.Equal(t, float64(0), dataMap["success_count"], "不应有成功") assert.Equal(t, float64(1), dataMap["fail_count"], "应全部失败") }) t.Run("超级管理员可以设置任意店铺的设备", func(t *testing.T) { anotherShop := env.CreateTestShop("另一个店铺", 1, nil) anotherDevice := &model.Device{ DeviceNo: fmt.Sprintf("ADMIN_%d", time.Now().UnixNano()), DeviceName: "管理员设备", DeviceType: "router", MaxSimSlots: 4, Status: constants.DeviceStatusInStock, ShopID: &anotherShop.ID, } require.NoError(t, env.TX.Create(anotherDevice).Error) createTestAllocation(t, env, anotherShop.ID, series.ID, 0) body := map[string]interface{}{ "device_ids": []uint{anotherDevice.ID}, "series_id": series.ID, } jsonBody, _ := json.Marshal(body) resp, err := env.AsSuperAdmin().Request("PATCH", "/api/admin/devices/series-binding", jsonBody) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, 200, resp.StatusCode) var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.Equal(t, 0, result.Code, "超级管理员应能设置任意店铺的设备") dataMap := result.Data.(map[string]interface{}) assert.Equal(t, float64(1), dataMap["success_count"]) }) t.Run("未认证请求应返回错误", func(t *testing.T) { body := map[string]interface{}{ "device_ids": []uint{devices[0].ID}, "series_id": series.ID, } jsonBody, _ := json.Marshal(body) resp, err := env.ClearAuth().Request("PATCH", "/api/admin/devices/series-binding", jsonBody) require.NoError(t, err) defer resp.Body.Close() var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.NotEqual(t, 0, result.Code, "未认证请求应返回错误码") }) t.Run("空设备ID列表-返回成功但无操作", func(t *testing.T) { body := map[string]interface{}{ "device_ids": []uint{}, "series_id": series.ID, } jsonBody, _ := json.Marshal(body) resp, err := env.AsUser(agentAccount).Request("PATCH", "/api/admin/devices/series-binding", jsonBody) require.NoError(t, err) defer resp.Body.Close() var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.Equal(t, 0, result.Code, "当前实现:空列表返回成功") if result.Data != nil { dataMap := result.Data.(map[string]interface{}) assert.Equal(t, float64(0), dataMap["success_count"], "空列表无成功项") } }) }