package integration import ( "bytes" "context" "encoding/json" "fmt" "net/http/httptest" "testing" "time" "github.com/break/junhong_cmp_fiber/internal/bootstrap" internalMiddleware "github.com/break/junhong_cmp_fiber/internal/middleware" "github.com/break/junhong_cmp_fiber/internal/model" "github.com/break/junhong_cmp_fiber/internal/routes" "github.com/break/junhong_cmp_fiber/pkg/auth" "github.com/break/junhong_cmp_fiber/pkg/config" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/queue" "github.com/break/junhong_cmp_fiber/pkg/response" "github.com/break/junhong_cmp_fiber/tests/testutil" "github.com/gofiber/fiber/v2" "github.com/redis/go-redis/v9" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" ) const ( testDBDSN = "host=cxd.whcxd.cn port=16159 user=erp_pgsql password=erp_2025 dbname=junhong_cmp_test sslmode=disable TimeZone=Asia/Shanghai" testRedisAddr = "cxd.whcxd.cn:16299" testRedisPasswd = "cpNbWtAaqgo1YJmbMp3h" testRedisDB = 6 ) type authorizationTestEnv struct { db *gorm.DB rdb *redis.Client tokenManager *auth.TokenManager app *fiber.App adminToken string agentToken string enterprise *model.Enterprise card1 *model.IotCard card2 *model.IotCard auth1 *model.EnterpriseCardAuthorization shop *model.Shop agentAccount *model.Account t *testing.T } func setupTestEnvVars(t *testing.T) { t.Helper() t.Setenv("JUNHONG_DATABASE_HOST", "cxd.whcxd.cn") t.Setenv("JUNHONG_DATABASE_PORT", "16159") t.Setenv("JUNHONG_DATABASE_USER", "erp_pgsql") t.Setenv("JUNHONG_DATABASE_PASSWORD", "erp_2025") t.Setenv("JUNHONG_DATABASE_DBNAME", "junhong_cmp_test") t.Setenv("JUNHONG_DATABASE_SSLMODE", "disable") t.Setenv("JUNHONG_REDIS_ADDRESS", "cxd.whcxd.cn") t.Setenv("JUNHONG_REDIS_PORT", "16299") t.Setenv("JUNHONG_REDIS_PASSWORD", "cpNbWtAaqgo1YJmbMp3h") t.Setenv("JUNHONG_REDIS_DB", "6") t.Setenv("JUNHONG_JWT_SECRET_KEY", "dev-secret-key-for-testing-only-32chars!") t.Setenv("JUNHONG_SERVER_ADDRESS", ":3000") t.Setenv("JUNHONG_LOGGING_LEVEL", "debug") t.Setenv("JUNHONG_LOGGING_DEVELOPMENT", "true") } func setupAuthorizationTestEnv(t *testing.T) *authorizationTestEnv { t.Helper() setupTestEnvVars(t) cfg, err := config.Load() require.NoError(t, err) err = config.Set(cfg) require.NoError(t, err) zapLogger, _ := zap.NewDevelopment() db, err := gorm.Open(postgres.Open(testDBDSN), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) require.NoError(t, err) rdb := redis.NewClient(&redis.Options{ Addr: testRedisAddr, Password: testRedisPasswd, DB: testRedisDB, }) ctx := context.Background() err = rdb.Ping(ctx).Err() require.NoError(t, err) testPrefix := fmt.Sprintf("test:%s:", t.Name()) keys, _ := rdb.Keys(ctx, testPrefix+"*").Result() if len(keys) > 0 { rdb.Del(ctx, keys...) } tokenManager := auth.NewTokenManager(rdb, 24*time.Hour, 7*24*time.Hour) superAdmin := testutil.CreateSuperAdmin(t, db) adminToken, _ := testutil.GenerateTestToken(t, rdb, superAdmin, "web") queueClient := queue.NewClient(rdb, zapLogger) deps := &bootstrap.Dependencies{ DB: db, Redis: rdb, Logger: zapLogger, TokenManager: tokenManager, QueueClient: queueClient, } result, err := bootstrap.Bootstrap(deps) require.NoError(t, err) app := fiber.New(fiber.Config{ ErrorHandler: internalMiddleware.ErrorHandler(zapLogger), }) routes.RegisterRoutes(app, result.Handlers, result.Middlewares) ts := time.Now().Unix() % 100000 shop := &model.Shop{ ShopName: "AUTH_TEST_SHOP", ShopCode: fmt.Sprintf("AS%d", ts), Level: 1, Status: constants.StatusEnabled, } shop.Creator = superAdmin.ID shop.Updater = superAdmin.ID require.NoError(t, db.Create(shop).Error) enterprise := &model.Enterprise{ EnterpriseName: "AUTH_TEST_ENTERPRISE", EnterpriseCode: fmt.Sprintf("AE%d", ts), OwnerShopID: &shop.ID, Status: constants.StatusEnabled, } enterprise.Creator = superAdmin.ID enterprise.Updater = superAdmin.ID require.NoError(t, db.Create(enterprise).Error) card1 := &model.IotCard{ ICCID: fmt.Sprintf("AC1%d", ts), MSISDN: "13800001001", CardType: "data_card", Status: 1, ShopID: &shop.ID, } card2 := &model.IotCard{ ICCID: fmt.Sprintf("AC2%d", ts), MSISDN: "13800001002", CardType: "data_card", Status: 1, ShopID: &shop.ID, } require.NoError(t, db.Create(card1).Error) require.NoError(t, db.Create(card2).Error) now := time.Now() auth1 := &model.EnterpriseCardAuthorization{ EnterpriseID: enterprise.ID, CardID: card1.ID, AuthorizedBy: superAdmin.ID, AuthorizedAt: now, AuthorizerType: constants.UserTypePlatform, Remark: "集成测试授权记录", } require.NoError(t, db.Create(auth1).Error) agentAccount := &model.Account{ Username: fmt.Sprintf("aa%d", ts), Phone: fmt.Sprintf("138%05d", ts), Password: "hashed_password", UserType: constants.UserTypeAgent, ShopID: &shop.ID, Status: constants.StatusEnabled, } agentAccount.Creator = superAdmin.ID agentAccount.Updater = superAdmin.ID require.NoError(t, db.Create(agentAccount).Error) agentToken, _ := testutil.GenerateTestToken(t, rdb, agentAccount, "web") return &authorizationTestEnv{ db: db, rdb: rdb, tokenManager: tokenManager, app: app, adminToken: adminToken, agentToken: agentToken, enterprise: enterprise, card1: card1, card2: card2, auth1: auth1, shop: shop, agentAccount: agentAccount, t: t, } } func (e *authorizationTestEnv) teardown() { e.db.Exec("DELETE FROM tb_enterprise_card_authorization WHERE enterprise_id = ?", e.enterprise.ID) e.db.Exec("DELETE FROM tb_iot_card WHERE iccid LIKE 'AC%'") e.db.Exec("DELETE FROM tb_enterprise WHERE enterprise_code LIKE 'AE%'") e.db.Exec("DELETE FROM tb_account WHERE username LIKE 'aa%'") e.db.Exec("DELETE FROM tb_shop WHERE shop_code LIKE 'AS%'") ctx := context.Background() testPrefix := fmt.Sprintf("test:%s:", e.t.Name()) keys, _ := e.rdb.Keys(ctx, testPrefix+"*").Result() if len(keys) > 0 { e.rdb.Del(ctx, keys...) } e.rdb.Close() } func TestAuthorization_List(t *testing.T) { env := setupAuthorizationTestEnv(t) defer env.teardown() t.Run("平台用户获取授权记录列表", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/admin/authorizations?page=1&page_size=20", nil) req.Header.Set("Authorization", "Bearer "+env.adminToken) resp, err := env.app.Test(req, -1) 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) data, ok := result.Data.(map[string]interface{}) require.True(t, ok) items, ok := data["items"].([]interface{}) require.True(t, ok) assert.GreaterOrEqual(t, len(items), 1) }) t.Run("按企业ID筛选授权记录", func(t *testing.T) { url := fmt.Sprintf("/api/admin/authorizations?enterprise_id=%d&page=1&page_size=20", env.enterprise.ID) req := httptest.NewRequest("GET", url, nil) req.Header.Set("Authorization", "Bearer "+env.adminToken) resp, err := env.app.Test(req, -1) 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) data := result.Data.(map[string]interface{}) total := int(data["total"].(float64)) assert.Equal(t, 1, total) }) t.Run("按ICCID筛选授权记录", func(t *testing.T) { url := fmt.Sprintf("/api/admin/authorizations?iccid=%s&page=1&page_size=20", env.card1.ICCID) req := httptest.NewRequest("GET", url, nil) req.Header.Set("Authorization", "Bearer "+env.adminToken) resp, err := env.app.Test(req, -1) 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) data := result.Data.(map[string]interface{}) total := int(data["total"].(float64)) assert.Equal(t, 1, total) }) t.Run("按状态筛选-有效授权", func(t *testing.T) { url := fmt.Sprintf("/api/admin/authorizations?enterprise_id=%d&status=1&page=1&page_size=20", env.enterprise.ID) req := httptest.NewRequest("GET", url, nil) req.Header.Set("Authorization", "Bearer "+env.adminToken) resp, err := env.app.Test(req, -1) 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 TestAuthorization_GetDetail(t *testing.T) { env := setupAuthorizationTestEnv(t) defer env.teardown() t.Run("获取授权记录详情", func(t *testing.T) { url := fmt.Sprintf("/api/admin/authorizations/%d", env.auth1.ID) req := httptest.NewRequest("GET", url, nil) req.Header.Set("Authorization", "Bearer "+env.adminToken) resp, err := env.app.Test(req, -1) 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) data := result.Data.(map[string]interface{}) assert.Equal(t, float64(env.auth1.ID), data["id"]) assert.Equal(t, float64(env.enterprise.ID), data["enterprise_id"]) assert.Equal(t, "AUTH_TEST_ENTERPRISE", data["enterprise_name"]) assert.Equal(t, env.card1.ICCID, data["iccid"]) assert.Equal(t, "集成测试授权记录", data["remark"]) assert.Equal(t, float64(1), data["status"]) }) t.Run("获取不存在的授权记录", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/admin/authorizations/999999", nil) req.Header.Set("Authorization", "Bearer "+env.adminToken) resp, err := env.app.Test(req, -1) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, 404, resp.StatusCode) var result response.Response err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) assert.NotEqual(t, 0, result.Code) }) } func TestAuthorization_UpdateRemark(t *testing.T) { env := setupAuthorizationTestEnv(t) defer env.teardown() t.Run("更新授权记录备注", func(t *testing.T) { url := fmt.Sprintf("/api/admin/authorizations/%d/remark", env.auth1.ID) body := map[string]string{"remark": "更新后的备注内容"} bodyBytes, _ := json.Marshal(body) req := httptest.NewRequest("PUT", url, bytes.NewReader(bodyBytes)) req.Header.Set("Authorization", "Bearer "+env.adminToken) req.Header.Set("Content-Type", "application/json") resp, err := env.app.Test(req, -1) 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) data := result.Data.(map[string]interface{}) assert.Equal(t, "更新后的备注内容", data["remark"]) }) t.Run("更新不存在的授权记录备注", func(t *testing.T) { body := map[string]string{"remark": "不会更新"} bodyBytes, _ := json.Marshal(body) req := httptest.NewRequest("PUT", "/api/admin/authorizations/999999/remark", bytes.NewReader(bodyBytes)) req.Header.Set("Authorization", "Bearer "+env.adminToken) req.Header.Set("Content-Type", "application/json") resp, err := env.app.Test(req, -1) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, 404, resp.StatusCode) }) } func TestAuthorization_DataPermission(t *testing.T) { env := setupAuthorizationTestEnv(t) defer env.teardown() ts2 := time.Now().Unix() % 100000 otherShop := &model.Shop{ ShopName: "OTHER_TEST_SHOP", ShopCode: fmt.Sprintf("OS%d", ts2), Level: 1, Status: constants.StatusEnabled, } otherShop.Creator = 1 otherShop.Updater = 1 require.NoError(t, env.db.Create(otherShop).Error) defer env.db.Exec("DELETE FROM tb_shop WHERE id = ?", otherShop.ID) otherEnterprise := &model.Enterprise{ EnterpriseName: "OTHER_TEST_ENTERPRISE", EnterpriseCode: fmt.Sprintf("OE%d", ts2), OwnerShopID: &otherShop.ID, Status: constants.StatusEnabled, } otherEnterprise.Creator = 1 otherEnterprise.Updater = 1 require.NoError(t, env.db.Create(otherEnterprise).Error) defer env.db.Exec("DELETE FROM tb_enterprise WHERE id = ?", otherEnterprise.ID) otherCard := &model.IotCard{ ICCID: fmt.Sprintf("OC%d", ts2), MSISDN: "13800002001", CardType: "data_card", Status: 1, ShopID: &otherShop.ID, } require.NoError(t, env.db.Create(otherCard).Error) defer env.db.Exec("DELETE FROM tb_iot_card WHERE id = ?", otherCard.ID) now := time.Now() otherAuth := &model.EnterpriseCardAuthorization{ EnterpriseID: otherEnterprise.ID, CardID: otherCard.ID, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: constants.UserTypePlatform, Remark: "其他店铺的授权记录", } require.NoError(t, env.db.Create(otherAuth).Error) defer env.db.Exec("DELETE FROM tb_enterprise_card_authorization WHERE id = ?", otherAuth.ID) t.Run("代理用户只能看到自己店铺的授权记录", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/admin/authorizations?page=1&page_size=100", nil) req.Header.Set("Authorization", "Bearer "+env.agentToken) resp, err := env.app.Test(req, -1) 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) data := result.Data.(map[string]interface{}) items := data["items"].([]interface{}) sawOtherAuth := false sawOwnAuth := false for _, item := range items { itemMap := item.(map[string]interface{}) authID := uint(itemMap["id"].(float64)) if authID == otherAuth.ID { sawOtherAuth = true } if authID == env.auth1.ID { sawOwnAuth = true } } assert.False(t, sawOtherAuth, "代理用户不应该看到其他店铺的授权记录 (otherAuth.ID=%d)", otherAuth.ID) assert.True(t, sawOwnAuth, "代理用户应该能看到自己店铺的授权记录 (auth1.ID=%d)", env.auth1.ID) }) t.Run("平台用户可以看到所有授权记录", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/admin/authorizations?page=1&page_size=100", nil) req.Header.Set("Authorization", "Bearer "+env.adminToken) resp, err := env.app.Test(req, -1) 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) data := result.Data.(map[string]interface{}) total := int(data["total"].(float64)) assert.GreaterOrEqual(t, total, 2, "平台用户应该能看到所有授权记录") }) } func TestAuthorization_Unauthorized(t *testing.T) { env := setupAuthorizationTestEnv(t) defer env.teardown() t.Run("无Token访问被拒绝", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/admin/authorizations", nil) resp, err := env.app.Test(req, -1) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, 401, resp.StatusCode) }) t.Run("无效Token访问被拒绝", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/admin/authorizations", nil) req.Header.Set("Authorization", "Bearer invalid_token") resp, err := env.app.Test(req, -1) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, 401, resp.StatusCode) }) }