diff --git a/backend/go.mod b/backend/go.mod index 1f9a62d5..9835b166 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -41,6 +41,7 @@ require ( github.com/quic-go/quic-go v0.63.0 github.com/spf13/cobra v1.10.2 github.com/stretchr/testify v1.12.1 + github.com/vmihailenco/msgpack/v5 v5.4.1 github.com/zitadel/exifremove v0.1.0 go.opentelemetry.io/contrib/bridges/otelslog v0.21.0 go.opentelemetry.io/contrib/exporters/autoexport v0.72.0 @@ -178,7 +179,6 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.2 // indirect github.com/valyala/fastjson v1.6.10 // indirect - github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/x448/float16 v0.8.4 // indirect go.mongodb.org/mongo-driver/v2 v2.9.1 // indirect diff --git a/backend/internal/appconfig/legacy.go b/backend/internal/appconfig/legacy.go index a6d45059..65d34be6 100644 --- a/backend/internal/appconfig/legacy.go +++ b/backend/internal/appconfig/legacy.go @@ -1,16 +1,17 @@ package appconfig -import "encoding/json" +import "github.com/vmihailenco/msgpack/v5" -// UnmarshalJSON preserves settings stored in actor state before boolean toggles were replaced by modes -func (m *AppConfigModel) UnmarshalJSON(data []byte) error { +// DecodeMsgpack preserves settings stored in actor state before boolean toggles were replaced by modes +// Francis stores actor state as MessagePack keyed by Go field name, so the legacy keys are the old field names +func (m *AppConfigModel) DecodeMsgpack(dec *msgpack.Decoder) error { type config AppConfigModel value := struct { *config - LegacyNotificationEnabled AppConfigValue `json:"emailLoginNotificationEnabled"` - LegacyEmailVerificationEnabled AppConfigValue `json:"emailVerificationEnabled"` + LegacyNotificationEnabled AppConfigValue `msgpack:"EmailLoginNotificationEnabled"` + LegacyEmailVerificationEnabled AppConfigValue `msgpack:"EmailVerificationEnabled"` }{config: (*config)(m)} - if err := json.Unmarshal(data, &value); err != nil { + if err := dec.Decode(&value); err != nil { return err } if m.EmailLoginNotificationMode == "" && value.LegacyNotificationEnabled != "" { diff --git a/backend/internal/appconfig/model_test.go b/backend/internal/appconfig/model_test.go index d766773e..729c6aa2 100644 --- a/backend/internal/appconfig/model_test.go +++ b/backend/internal/appconfig/model_test.go @@ -1,7 +1,6 @@ package appconfig import ( - "encoding/json" "errors" "reflect" "strings" @@ -9,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/vmihailenco/msgpack/v5" "github.com/pocket-id/pocket-id/backend/internal/dto" ) @@ -302,26 +302,47 @@ func TestAppConfigModel_Update(t *testing.T) { }) } -func TestAppConfigModel_UnmarshalJSON(t *testing.T) { - t.Run("maps the legacy email verification toggle to a mode", func(t *testing.T) { - tests := map[string]AppConfigValue{ - "true": EmailVerificationOptional, - "false": EmailVerificationDisabled, +func TestAppConfigModel_DecodeMsgpack(t *testing.T) { + t.Run("maps the legacy toggles to modes", func(t *testing.T) { + tests := map[string]struct { + notification AppConfigValue + emailVerification AppConfigValue + }{ + "true": {LoginNotificationBrowserRecognition, EmailVerificationOptional}, + "false": {LoginNotificationDisabled, EmailVerificationDisabled}, } for enabled, expected := range tests { - var m AppConfigModel - err := json.Unmarshal([]byte(`{"emailVerificationEnabled":"`+enabled+`"}`), &m) + data, err := msgpack.Marshal(map[string]string{ + "AppName": "Legacy App", + "EmailLoginNotificationEnabled": enabled, + "EmailVerificationEnabled": enabled, + }) require.NoError(t, err) - assert.Equal(t, expected, m.EmailVerification) + var m AppConfigModel + err = msgpack.Unmarshal(data, &m) + require.NoError(t, err) + + assert.Equal(t, AppConfigValue("Legacy App"), m.AppName) + assert.Equal(t, expected.notification, m.EmailLoginNotificationMode) + assert.Equal(t, expected.emailVerification, m.EmailVerification) } }) - t.Run("an explicit email verification mode wins over the legacy toggle", func(t *testing.T) { - var m AppConfigModel - err := json.Unmarshal([]byte(`{"emailVerification":"required","emailVerificationEnabled":"false"}`), &m) + t.Run("stored modes win over the legacy toggles", func(t *testing.T) { + data, err := msgpack.Marshal(map[string]string{ + "EmailLoginNotificationMode": LoginNotificationAlways, + "EmailLoginNotificationEnabled": "false", + "EmailVerification": EmailVerificationRequired, + "EmailVerificationEnabled": "false", + }) require.NoError(t, err) + var m AppConfigModel + err = msgpack.Unmarshal(data, &m) + require.NoError(t, err) + + assert.Equal(t, AppConfigValue(LoginNotificationAlways), m.EmailLoginNotificationMode) assert.Equal(t, AppConfigValue(EmailVerificationRequired), m.EmailVerification) }) }