fix: sync account freeze peer visibility
This commit is contained in:
parent
d9875b5caa
commit
eba402946a
26 changed files with 1034 additions and 19 deletions
|
|
@ -119,6 +119,64 @@ func TestProjectorUsesFallbackWhenProfilePhotoHidden(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestProjectorAccountFreezeIsViewerScopedAndReversible(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
const (
|
||||
frozenUserID = int64(4001)
|
||||
otherViewer = int64(4002)
|
||||
)
|
||||
freezes := &fakeAccountFreezes{items: map[int64]domain.AccountFreeze{
|
||||
frozenUserID: {UserID: frozenUserID, Frozen: true, Version: 3},
|
||||
}}
|
||||
projector := New(WithAccountFreezeProvider(freezes))
|
||||
base := []domain.User{{
|
||||
ID: frozenUserID,
|
||||
FirstName: "Frozen",
|
||||
// Viewer-scoped fields must never be trusted from a reused base object.
|
||||
RestrictionReasons: []domain.UserRestrictionReason{{Platform: "all", Reason: "stale", Text: "stale"}},
|
||||
}}
|
||||
|
||||
otherView, err := projector.ForViewer(ctx, otherViewer, base)
|
||||
if err != nil {
|
||||
t.Fatalf("ForViewer(other): %v", err)
|
||||
}
|
||||
got := projectionUser(t, otherView, frozenUserID)
|
||||
if !reflect.DeepEqual(got.RestrictionReasons, domain.AccountFrozenRestrictionReasons()) {
|
||||
t.Fatalf("other-view restriction = %+v, want frozen restriction", got.RestrictionReasons)
|
||||
}
|
||||
if base[0].RestrictionReasons[0].Reason != "stale" {
|
||||
t.Fatalf("projection mutated base user: %+v", base[0])
|
||||
}
|
||||
|
||||
selfView, err := projector.ForViewer(ctx, frozenUserID, base)
|
||||
if err != nil {
|
||||
t.Fatalf("ForViewer(self): %v", err)
|
||||
}
|
||||
if reasons := projectionUser(t, selfView, frozenUserID).RestrictionReasons; len(reasons) != 0 {
|
||||
t.Fatalf("self-view restriction = %+v, want none", reasons)
|
||||
}
|
||||
|
||||
batch, err := projector.ForViewers(ctx, []int64{otherViewer, frozenUserID}, base)
|
||||
if err != nil {
|
||||
t.Fatalf("ForViewers: %v", err)
|
||||
}
|
||||
if reasons := projectionUser(t, batch[otherViewer], frozenUserID).RestrictionReasons; !reflect.DeepEqual(reasons, domain.AccountFrozenRestrictionReasons()) {
|
||||
t.Fatalf("batch other-view restriction = %+v", reasons)
|
||||
}
|
||||
if reasons := projectionUser(t, batch[frozenUserID], frozenUserID).RestrictionReasons; len(reasons) != 0 {
|
||||
t.Fatalf("batch self-view restriction = %+v, want none", reasons)
|
||||
}
|
||||
|
||||
freezes.items = nil
|
||||
unfrozenView, err := projector.ForViewer(ctx, otherViewer, otherView)
|
||||
if err != nil {
|
||||
t.Fatalf("ForViewer(after unfreeze): %v", err)
|
||||
}
|
||||
if reasons := projectionUser(t, unfrozenView, frozenUserID).RestrictionReasons; len(reasons) != 0 {
|
||||
t.Fatalf("unfrozen projection retained restriction = %+v", reasons)
|
||||
}
|
||||
}
|
||||
|
||||
// TestForViewersEquivalentToForViewer 锁定 fan-out 模板化的核心安全网:ForViewers(viewers, users)
|
||||
// 的每个 viewer 切片必须与逐 viewer 的 ForViewer(viewer, users) 字节等价(隐私/改名/头像投影
|
||||
// 不能因 O(owner) 模板化而漂移泄漏)。**唯一允许的差异是 personal photo overlay**:v1 模板不做
|
||||
|
|
@ -243,6 +301,20 @@ type fakeProfilePhotos struct {
|
|||
fallback map[int64]domain.ProfilePhotoRef
|
||||
}
|
||||
|
||||
type fakeAccountFreezes struct {
|
||||
items map[int64]domain.AccountFreeze
|
||||
}
|
||||
|
||||
func (f *fakeAccountFreezes) AccountFreezes(_ context.Context, ids []int64) (map[int64]domain.AccountFreeze, error) {
|
||||
out := make(map[int64]domain.AccountFreeze)
|
||||
for _, id := range ids {
|
||||
if freeze, ok := f.items[id]; ok {
|
||||
out[id] = freeze
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (p fakeProfilePhotos) CurrentProfilePhotos(_ context.Context, _ domain.PeerType, ids []int64) (map[int64]domain.ProfilePhotoRef, error) {
|
||||
return p.CurrentProfilePhotosKind(context.Background(), domain.PeerTypeUser, ids, domain.ProfilePhotoKindProfile)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue