| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768 |
- package dto
- import (
- "reflect"
- "strings"
- "testing"
- "github.com/Wei-Shaw/sub2api/internal/service"
- )
- // TestPublicSettingsInjectionPayload_SchemaDoesNotDrift guarantees the SSR
- // injection struct exposes every JSON field consumed by the frontend.
- //
- // Why this test exists: before we extracted a named PublicSettingsInjectionPayload
- // type, the inline struct was manually kept in sync with dto.PublicSettings and
- // drifted — ChannelMonitorEnabled / AvailableChannelsEnabled were missing, which
- // made the frontend read `undefined` on refresh and hide the "可用渠道" menu
- // until the async /api/v1/settings/public round-trip finished.
- //
- // This test compares the two JSON-tag sets and fails if injection is missing
- // any field that dto.PublicSettings exposes. Adding a new feature flag with
- // only a DTO entry will fail this test until the injection struct is updated.
- //
- // Intentional exclusions (fields present on dto.PublicSettings that SSR does
- // not need to inject) are listed in `dtoOnlyFields` below with a reason.
- func TestPublicSettingsInjectionPayload_SchemaDoesNotDrift(t *testing.T) {
- injection := jsonTags(reflect.TypeOf(service.PublicSettingsInjectionPayload{}))
- dtoKeys := jsonTags(reflect.TypeOf(PublicSettings{}))
- // Fields that legitimately live only on the DTO. Keep tiny; document each.
- dtoOnlyFields := map[string]string{
- // force_email_on_third_party_signup lives on the DTO but is not injected via SSR.
- "force_email_on_third_party_signup": "auth-source default, not a feature flag",
- }
- var missing []string
- for key := range dtoKeys {
- if _, ok := injection[key]; ok {
- continue
- }
- if _, allowed := dtoOnlyFields[key]; allowed {
- continue
- }
- missing = append(missing, key)
- }
- if len(missing) > 0 {
- t.Fatalf("service.PublicSettingsInjectionPayload is missing JSON fields present on dto.PublicSettings: %s\n"+
- "add the field to PublicSettingsInjectionPayload (and GetPublicSettingsForInjection), or "+
- "document the exclusion in dtoOnlyFields with a reason.", strings.Join(missing, ", "))
- }
- }
- func jsonTags(t reflect.Type) map[string]struct{} {
- out := make(map[string]struct{})
- for i := 0; i < t.NumField(); i++ {
- f := t.Field(i)
- tag := f.Tag.Get("json")
- if tag == "" || tag == "-" {
- continue
- }
- name := strings.SplitN(tag, ",", 2)[0]
- if name == "" {
- continue
- }
- out[name] = struct{}{}
- }
- return out
- }
|