package service import ( "encoding/json" "strings" "testing" "time" "github.com/freefire/jiang13-bbs/model" "gorm.io/gorm" ) func staffDeletedComment() model.Comment { uid := uint(7) c := model.Comment{ ID: 11, PostID: 22, UserID: uid, Content: "不该出现在公开 JSON", Status: model.ContentStatusPublished, DeletedBy: 9, User: model.User{ ID: uid, Username: "alice", Nickname: "爱丽丝", Avatar: "/uploads/avatars/alice.jpg", }, } c.DeletedAt = gorm.DeletedAt{Time: time.Now(), Valid: true} c.CreatedAt = time.Now().Add(-time.Hour) c.UpdatedAt = c.CreatedAt return c } func marshalNode(t *testing.T, n CommentNode) map[string]any { t.Helper() b, err := json.Marshal(n) if err != nil { t.Fatalf("marshal: %v", err) } var raw map[string]any if err := json.Unmarshal(b, &raw); err != nil { t.Fatalf("unmarshal: %v", err) } return raw } func TestToCommentNodeHidesStaffDeletedAuthorFromPublic(t *testing.T) { c := staffDeletedComment() raw := marshalNode(t, toCommentNode(c, nil, false, 0, nil)) if _, ok := raw["user"]; ok { t.Fatalf("公众 JSON 不应含 user: %v", raw["user"]) } if _, ok := raw["user_id"]; ok { t.Fatalf("公众 JSON 不应含 user_id: %v", raw["user_id"]) } if raw["staff_deleted"] != true { t.Fatalf("staff_deleted=%v", raw["staff_deleted"]) } if s, _ := raw["content"].(string); s != "" { t.Fatalf("公众不应看到正文: %q", s) } blob, _ := json.Marshal(raw) for _, leak := range []string{"alice", "爱丽丝", "/uploads/avatars/alice.jpg"} { if strings.Contains(string(blob), leak) { t.Fatalf("公众 JSON 泄漏身份 %q: %s", leak, blob) } } } func TestToCommentNodeKeepsAuthorForCommenter(t *testing.T) { c := staffDeletedComment() raw := marshalNode(t, toCommentNode(c, nil, false, 7, nil)) if raw["user_id"] != float64(7) { t.Fatalf("评论者应看到 user_id: %v", raw["user_id"]) } user, ok := raw["user"].(map[string]any) if !ok { t.Fatalf("评论者应看到 user") } if user["username"] != "alice" { t.Fatalf("username=%v", user["username"]) } if raw["content"] != "不该出现在公开 JSON" { t.Fatalf("评论者应看到原文, got %v", raw["content"]) } } func TestToCommentNodeKeepsAuthorForModerator(t *testing.T) { c := staffDeletedComment() raw := marshalNode(t, toCommentNode(c, nil, true, 99, nil)) if raw["user_id"] != float64(7) { t.Fatalf("版主应看到 user_id") } if _, ok := raw["user"]; !ok { t.Fatal("版主应看到 user") } if raw["content"] != "不该出现在公开 JSON" { t.Fatalf("版主应看到原文, got %v", raw["content"]) } } func TestToCommentNodeSelfDeleteKeepsAuthor(t *testing.T) { c := staffDeletedComment() c.DeletedBy = c.UserID raw := marshalNode(t, toCommentNode(c, nil, false, 0, nil)) if raw["staff_deleted"] != nil && raw["staff_deleted"] != false { t.Fatalf("自删不应标 staff_deleted: %v", raw["staff_deleted"]) } if raw["user_id"] != float64(7) { t.Fatalf("自删应保留作者: %v", raw["user_id"]) } if s, _ := raw["content"].(string); s != "" { t.Fatalf("公众不应看到自删正文: %q", s) } } func TestToCommentNodeSelfDeleteKeepsContentForCommenter(t *testing.T) { c := staffDeletedComment() c.DeletedBy = c.UserID raw := marshalNode(t, toCommentNode(c, nil, false, 7, nil)) if raw["content"] != "不该出现在公开 JSON" { t.Fatalf("自删评论者应看到原文, got %v", raw["content"]) } }