mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 09:40:12 +00:00
130 lines
4.0 KiB
Go
130 lines
4.0 KiB
Go
package model
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
)
|
|
|
|
// Server.MarshalJSON projects Server.UserID into a public owner field while
|
|
// keeping the raw UserID json-hidden. The lookup function is package-level
|
|
// and shared across tests; each subtest installs its own stub and restores
|
|
// the original to avoid leaking state.
|
|
func TestServerMarshalJSONOwnerProjection(t *testing.T) {
|
|
original := ServerOwnerLookup
|
|
t.Cleanup(func() { ServerOwnerLookup = original })
|
|
|
|
tests := []struct {
|
|
name string
|
|
uid uint64
|
|
lookup func(uid uint64) (ServerOwnerInfo, bool)
|
|
wantID uint64
|
|
wantHasName bool
|
|
wantName string
|
|
}{
|
|
{
|
|
// uid=0 is the legacy global agent secret pseudo-owner. The
|
|
// lookup deliberately returns ok=false so the frontend can
|
|
// render it as "Global Agent" instead of a real username.
|
|
name: "uid_zero_has_no_username",
|
|
uid: 0,
|
|
lookup: func(uint64) (ServerOwnerInfo, bool) {
|
|
return ServerOwnerInfo{}, false
|
|
},
|
|
wantID: 0,
|
|
wantHasName: false,
|
|
},
|
|
{
|
|
// Known user → username flows through to the wire so the
|
|
// admin frontend can show it without a separate /user fetch
|
|
// (which members cannot call anyway).
|
|
name: "known_user_has_username",
|
|
uid: 42,
|
|
lookup: func(uid uint64) (ServerOwnerInfo, bool) {
|
|
return ServerOwnerInfo{ID: uid, Username: "alice"}, true
|
|
},
|
|
wantID: 42,
|
|
wantHasName: true,
|
|
wantName: "alice",
|
|
},
|
|
{
|
|
// Deleted user → lookup returns ok=false. The wire still
|
|
// carries owner.id so the frontend can render an "Unknown
|
|
// user (#id)" placeholder; otherwise the row would silently
|
|
// appear ownerless and ops would lose the audit trail.
|
|
name: "deleted_user_keeps_id_without_username",
|
|
uid: 999,
|
|
lookup: func(uint64) (ServerOwnerInfo, bool) {
|
|
return ServerOwnerInfo{}, false
|
|
},
|
|
wantID: 999,
|
|
wantHasName: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
ServerOwnerLookup = tc.lookup
|
|
s := &Server{Common: Common{ID: 7, UserID: tc.uid}, Name: "srv"}
|
|
|
|
raw, err := json.Marshal(s)
|
|
if err != nil {
|
|
t.Fatalf("marshal: %v", err)
|
|
}
|
|
|
|
var got struct {
|
|
Owner *ServerOwnerInfo `json:"owner"`
|
|
// Owner must never appear as the raw uid via Common.UserID;
|
|
// the Common.UserID json tag is "-" and a regression that
|
|
// flips it to "user_id" would expose internal owner ids
|
|
// to the wire bypassing the lookup-controlled rendering.
|
|
UserID *uint64 `json:"user_id,omitempty"`
|
|
}
|
|
if err := json.Unmarshal(raw, &got); err != nil {
|
|
t.Fatalf("unmarshal: %v", err)
|
|
}
|
|
|
|
if got.UserID != nil {
|
|
t.Fatalf("Common.UserID must not appear on the wire as user_id, got %d", *got.UserID)
|
|
}
|
|
if got.Owner == nil {
|
|
t.Fatalf("owner field must always be present, raw=%s", raw)
|
|
}
|
|
if got.Owner.ID != tc.wantID {
|
|
t.Fatalf("owner.id=%d, want %d", got.Owner.ID, tc.wantID)
|
|
}
|
|
if tc.wantHasName {
|
|
if got.Owner.Username != tc.wantName {
|
|
t.Fatalf("owner.username=%q, want %q", got.Owner.Username, tc.wantName)
|
|
}
|
|
} else if got.Owner.Username != "" {
|
|
t.Fatalf("owner.username must be omitted for uid=%d, got %q", tc.uid, got.Owner.Username)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// When no lookup is installed (tests / headless tools), MarshalJSON must
|
|
// still emit a minimal owner record so consumers do not crash on missing
|
|
// fields. Without this guard a future refactor could silently drop the
|
|
// owner key entirely whenever the hook is nil.
|
|
func TestServerMarshalJSONEmitsOwnerWithoutLookup(t *testing.T) {
|
|
original := ServerOwnerLookup
|
|
t.Cleanup(func() { ServerOwnerLookup = original })
|
|
ServerOwnerLookup = nil
|
|
|
|
raw, err := json.Marshal(&Server{Common: Common{ID: 1, UserID: 17}, Name: "srv"})
|
|
if err != nil {
|
|
t.Fatalf("marshal: %v", err)
|
|
}
|
|
|
|
var got struct {
|
|
Owner *ServerOwnerInfo `json:"owner"`
|
|
}
|
|
if err := json.Unmarshal(raw, &got); err != nil {
|
|
t.Fatalf("unmarshal: %v", err)
|
|
}
|
|
if got.Owner == nil || got.Owner.ID != 17 || got.Owner.Username != "" {
|
|
t.Fatalf("expected bare owner record {id:17}, got %+v", got.Owner)
|
|
}
|
|
}
|