package app import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" "time" "git.5iot.com/iotlcm-public/iotlcm-app/internal/consts" "git.5iot.com/iotlcm-public/iotlcm-app/internal/model" "git.5iot.com/iotlcm-public/iotlcm-app/internal/model/entity" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" ) func TestPhoneRegisterParamRequiresSmsCode(t *testing.T) { err := g.Validator().Data(&model.AppPhoneRegisterParam{ TenantId: "000000", Phone: "13800008000", Password: "123456", NickName: "测试用户", }).Run(context.Background()) if err == nil { t.Fatal("AppPhoneRegisterParam should require smsCode") } } func TestAppSmsCodeHelpersUseRedisKeys(t *testing.T) { key := appSmsCacheKey("", "13800008000", "login") if key != "app:sms:000000:login:13800008000" { t.Fatalf("unexpected sms cache key: %s", key) } if key := appSmsCooldownCacheKey("", "13800008000"); key != "app:sms:cooldown:000000:13800008000" { t.Fatalf("unexpected sms cooldown key: %s", key) } } func TestAppSmsCooldownScopeAndDuration(t *testing.T) { if appSmsCooldownTTL != time.Minute { t.Fatalf("sms cooldown = %v, want one minute", appSmsCooldownTTL) } key := appSmsCooldownCacheKey("000000", "13800008000") if key != appSmsCooldownCacheKey("", "13800008000") { t.Fatal("default tenant should share the same cooldown key") } if key == appSmsCooldownCacheKey("000000", "13800008001") { t.Fatal("different phones should have independent cooldown keys") } if key == appSmsCooldownCacheKey("000001", "13800008000") { t.Fatal("different tenants should have independent cooldown keys") } } func TestDeletedRegisterUserNameFitsSysUserUniqueColumn(t *testing.T) { name := deletedRegisterUserName(88, "18800004698") if name != "del88_314698" { t.Fatalf("unexpected deleted register username: %s", name) } if len(name) > 30 { t.Fatalf("deleted register username length = %d, want <= 30", len(name)) } } func TestIsSysUserNameUniqueKeyError(t *testing.T) { if !isSysUserNameUniqueKeyError(gerror.New("Error 1062: Duplicate entry '000000-18800004698' for key 'sys_user.uk_user_name'")) { t.Fatal("expected sys_user.uk_user_name duplicate error to be recognized") } if isSysUserNameUniqueKeyError(gerror.New("Error 1062: Duplicate entry 'x' for key 'other_key'")) { t.Fatal("unexpected duplicate key match for other indexes") } } func TestAppWechatMiniSecretMissing(t *testing.T) { if !appWechatMiniSecretMissing("") { t.Fatal("empty mini app secret should be treated as missing") } if !appWechatMiniSecretMissing(appWechatMiniAppSecretPlaceholder) { t.Fatal("placeholder mini app secret should be treated as missing") } if appWechatMiniSecretMissing("real-secret") { t.Fatal("non-placeholder mini app secret should not be treated as missing") } } func TestAppEnsureWechatBindPhoneVerifiedSkipsVerifiedPhone(t *testing.T) { err := appEnsureWechatBindPhoneVerified( context.Background(), &model.AppWechatBindPhoneParam{}, &appWechatBindTicketCache{VerifiedPhone: "13800008000"}, "13800008000", ) if err != nil { t.Fatalf("verified phone should skip captcha verification, got: %v", err) } err = appEnsureWechatBindPhoneVerified( context.Background(), &model.AppWechatBindPhoneParam{}, &appWechatBindTicketCache{VerifiedPhone: "13800008001"}, "13800008000", ) if err == nil { t.Fatal("different phone should require captcha verification") } } func TestAvailableAppLoginUsersByPasswordFiltersCandidates(t *testing.T) { svc := &sAppAuth{} users := []*entity.SysUser{ appAuthTestUser(1, "phone-a", "right-password"), appAuthTestUser(2, "phone-b", "other-password"), } matched, err := svc.availableAppLoginUsersByPassword(users, "wrong-password") if err != nil { t.Fatalf("availableAppLoginUsersByPassword returned error: %v", err) } if len(matched) != 0 { t.Fatalf("wrong password matched %d users, want 0", len(matched)) } matched, err = svc.availableAppLoginUsersByPassword(users, "right-password") if err != nil { t.Fatalf("availableAppLoginUsersByPassword returned error: %v", err) } if len(matched) != 1 || matched[0].UserId != 1 { t.Fatalf("right password matched %#v, want only user 1", matched) } } func appAuthTestUser(userId int64, username string, password string) *entity.SysUser { salt := "abcde" return &entity.SysUser{ UserId: userId, UserName: username, Salt: salt, Password: appPasswordEncryptForStore(password, salt), Status: consts.SysUserStatusNormal, } } func TestCallAppCodeHTTPUsesSceneTemplate(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var request struct { Config struct { Scene string `json:"scene"` } `json:"config"` Params struct { PhoneNumber string `json:"phoneNumber"` MsgContent string `json:"msgContent"` } `json:"params"` } if err := json.NewDecoder(r.Body).Decode(&request); err != nil { t.Errorf("decode request: %v", err) w.WriteHeader(http.StatusBadRequest) return } if request.Config.Scene != "bind_phone" { t.Errorf("scene = %q, want bind_phone", request.Config.Scene) } if request.Params.PhoneNumber != "13800008000" { t.Errorf("phoneNumber = %q, want 13800008000", request.Params.PhoneNumber) } if request.Params.MsgContent != "123456" { t.Errorf("msgContent = %q, want raw verification code", request.Params.MsgContent) } w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"code":0,"message":"发送成功"}`)) })) defer server.Close() err := callAppCodeHTTP(context.Background(), appCodeHttpSenderConfig{ Url: server.URL, Method: http.MethodPost, BodyTemplate: `{"config":{"scene":"{scene}"},"params":{"phoneNumber":"{contact}","msgContent":"{code}"}}`, }, "13800008000", "123456", appSmsServiceScene("wechatBindPhone")) if err != nil { t.Fatalf("callAppCodeHTTP returned error: %v", err) } }