unbind device push token on logout

This commit is contained in:
richjohnrambo
2026-09-23 11:39:16 +08:00
parent c4355ac236
commit a0d0bf574e
5 changed files with 229 additions and 9 deletions
+113 -2
View File
@@ -235,8 +235,11 @@ type testAppReleaseStore struct {
}
type testDevicePushTokenStore struct {
token *types.DevicePushToken
tenantID types.TenantID
token *types.DevicePushToken
tenantID types.TenantID
deletedTenantID types.TenantID
deletedUserID types.Uid
deletedDeviceID string
}
type testSafetyStore struct {
@@ -752,6 +755,13 @@ func (s *testDevicePushTokenStore) Upsert(tenantID types.TenantID, token *types.
return token, nil
}
func (s *testDevicePushTokenStore) Delete(tenantID types.TenantID, uid types.Uid, deviceID string) error {
s.deletedTenantID = tenantID
s.deletedUserID = uid
s.deletedDeviceID = deviceID
return nil
}
func (s *testSafetyStore) CreateRiskChallenge(tenantID types.TenantID, challenge *types.RiskChallenge) (*types.RiskChallenge, error) {
now := time.Now().UTC().Round(time.Millisecond)
challenge.CreatedAt = now
@@ -2907,6 +2917,30 @@ func TestServeIMDevicePushToken(t *testing.T) {
}
}
func TestIMLogoutDeviceID(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/v1/im/auth/logout", bytes.NewBufferString(`{"device_id":"device-1","ignored":true}`))
req.Header.Set("X-Device-ID", "header-device")
rec := httptest.NewRecorder()
deviceID, ok := imLogoutDeviceID(rec, req)
if !ok || deviceID != "device-1" || rec.Code != http.StatusOK {
t.Fatalf("unexpected body device id: ok=%v device_id=%q status=%d body=%s", ok, deviceID, rec.Code, rec.Body.String())
}
req = httptest.NewRequest(http.MethodPost, "/v1/im/auth/logout", nil)
req.Header.Set("X-IM-Device-ID", "header-device")
rec = httptest.NewRecorder()
deviceID, ok = imLogoutDeviceID(rec, req)
if !ok || deviceID != "header-device" || rec.Code != http.StatusOK {
t.Fatalf("unexpected header device id: ok=%v device_id=%q status=%d body=%s", ok, deviceID, rec.Code, rec.Body.String())
}
req = httptest.NewRequest(http.MethodPost, "/v1/im/auth/logout", bytes.NewBufferString(`{"device_id":"bad/device"}`))
rec = httptest.NewRecorder()
if deviceID, ok = imLogoutDeviceID(rec, req); ok || rec.Code != http.StatusBadRequest {
t.Fatalf("invalid device id accepted: ok=%v device_id=%q status=%d body=%s", ok, deviceID, rec.Code, rec.Body.String())
}
}
func TestServeIMConversationSearch(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
@@ -3780,6 +3814,83 @@ func TestServeIMGroupDetailPatchUsesBusinessSettingsVersion(t *testing.T) {
}
}
func TestServeIMGroupDetailReturnsGoneForDissolvedSettingsWithoutSubscription(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
oldStore := store.Store
oldGroupBusiness := store.GroupBusiness
mockStore := mock_store.NewMockPersistentStorageInterface(ctrl)
tokenAuth := mock_auth.NewMockAuthHandler(ctrl)
mockStore.EXPECT().GetLogicalAuthHandler("token").Return(tokenAuth)
tokenAuth.EXPECT().IsInitialized().Return(true)
tokenAuth.EXPECT().Authenticate(auth.AuthContext{}, []byte("token-1")).
Return(&auth.Rec{TenantID: 7, Uid: 42, AuthLevel: auth.LevelAuth}, nil, nil)
store.Store = mockStore
store.GroupBusiness = &testGroupBusinessStore{settings: &types.GroupSettings{
Topic: "grpAlpha", OwnerUserID: types.Uid(42), Name: "Alpha",
GroupStatus: types.GroupStatusDissolved, Version: 2,
}}
defer func() {
store.Store = oldStore
store.GroupBusiness = oldGroupBusiness
}()
req := httptest.NewRequest(http.MethodGet, "/v1/im/groups/grpAlpha", nil)
req.Header.Set("Authorization", "Bearer token-1")
rec := httptest.NewRecorder()
serveIMGroupResource(rec, req)
if rec.Code != http.StatusGone {
t.Fatalf("detail status: got %d body=%s", rec.Code, rec.Body.String())
}
if reason := decodeIMHTTPResponse(t, rec).Reason; reason != imReasonGroupDissolved {
t.Fatalf("unexpected reason: %q", reason)
}
}
func TestServeIMGroupDetailReturnsGoneForDeletedTopicWithoutSubscription(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
oldStore := store.Store
oldGroupBusiness := store.GroupBusiness
oldTopics := store.Topics
mockStore := mock_store.NewMockPersistentStorageInterface(ctrl)
tokenAuth := mock_auth.NewMockAuthHandler(ctrl)
mockStore.EXPECT().GetLogicalAuthHandler("token").Return(tokenAuth)
tokenAuth.EXPECT().IsInitialized().Return(true)
tokenAuth.EXPECT().Authenticate(auth.AuthContext{}, []byte("token-1")).
Return(&auth.Rec{TenantID: 7, Uid: 42, AuthLevel: auth.LevelAuth}, nil, nil)
topics := mock_store.NewMockTopicsPersistenceInterface(ctrl)
topics.EXPECT().Get(types.TenantID(7), "grpAlpha").Return(&types.Topic{
ObjHeader: types.ObjHeader{Id: "grpAlpha"},
TenantID: 7,
State: types.StateDeleted,
}, nil)
store.Store = mockStore
store.Topics = topics
store.GroupBusiness = &testGroupBusinessStore{settings: &types.GroupSettings{
Topic: "grpAlpha", OwnerUserID: types.Uid(42), Name: "Alpha",
GroupStatus: types.GroupStatusActive, Version: 1,
}}
defer func() {
store.Store = oldStore
store.Topics = oldTopics
store.GroupBusiness = oldGroupBusiness
}()
req := httptest.NewRequest(http.MethodGet, "/v1/im/groups/grpAlpha", nil)
req.Header.Set("Authorization", "Bearer token-1")
rec := httptest.NewRecorder()
serveIMGroupResource(rec, req)
if rec.Code != http.StatusGone {
t.Fatalf("detail status: got %d body=%s", rec.Code, rec.Body.String())
}
if reason := decodeIMHTTPResponse(t, rec).Reason; reason != imReasonGroupDissolved {
t.Fatalf("unexpected reason: %q", reason)
}
}
func TestServeIMGroupMyProfileKeepsSubscriptionNicknameWhenBusinessMemberMissing(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()