122 lines
4.8 KiB
Go
122 lines
4.8 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"telesrv/internal/domain"
|
|
"telesrv/internal/identity"
|
|
"telesrv/internal/store/memory"
|
|
)
|
|
|
|
// TestLoginCodeMessageTemplateResolutionPrecedence exercises
|
|
// WithLoginCodeMessageTemplate/resolveLoginCodeMessageTemplate's precedence
|
|
// (panel override > env default > compiled-in default) end to end through
|
|
// SignUp's bootstrap recordLoginMessage path (phone channel, no owner/dialog
|
|
// yet -- see service.go's rec.Channel == codeChannelPhone branch), mirroring
|
|
// how welcome_message_test.go exercises WithLoginWelcomeMessages.
|
|
func TestLoginCodeMessageTemplateResolutionPrecedence(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
newSvc := func(store *identity.Store, envDefault string) (*Service, *memory.MessageStore) {
|
|
dialogs := memory.NewDialogStore()
|
|
messages := memory.NewMessageStore(dialogs)
|
|
return NewService(memory.NewUserStore(), memory.NewAuthorizationStore(), memory.NewCodeStore(), nil, nil, "12345",
|
|
WithLoginMessages(messages, dialogs),
|
|
WithLoginCodeMessageTemplate(store, envDefault),
|
|
), messages
|
|
}
|
|
|
|
// No override, no env default: falls back to the compiled-in default.
|
|
t.Run("compiled-in default", func(t *testing.T) {
|
|
svc, messages := newSvc(nil, "")
|
|
u := signUpPhoneForLoginCodeMessage(t, ctx, svc, "+15550771001")
|
|
body := codeMessageBody(t, ctx, messages, u.ID)
|
|
if !strings.Contains(body, "Login code: 12345") {
|
|
t.Fatalf("body = %q, want compiled-in default rendering", body)
|
|
}
|
|
})
|
|
|
|
// Env default set, no panel override: env default wins.
|
|
t.Run("env default", func(t *testing.T) {
|
|
svc, messages := newSvc(nil, "Env says your code is {{code}}.")
|
|
u := signUpPhoneForLoginCodeMessage(t, ctx, svc, "+15550771002")
|
|
body := codeMessageBody(t, ctx, messages, u.ID)
|
|
if body != "Env says your code is 12345." {
|
|
t.Fatalf("body = %q, want env-default rendering", body)
|
|
}
|
|
})
|
|
|
|
// Panel override set: wins over both the env default and the compiled-in
|
|
// default, and is read fresh (not cached) -- see resolveLoginCodeMessageTemplate.
|
|
t.Run("panel override wins and is read fresh", func(t *testing.T) {
|
|
store := identity.NewStore(t.TempDir())
|
|
svc, messages := newSvc(store, "Env says your code is {{code}}.")
|
|
|
|
u := signUpPhoneForLoginCodeMessage(t, ctx, svc, "+15550771003")
|
|
body := codeMessageBody(t, ctx, messages, u.ID)
|
|
if body != "Env says your code is 12345." {
|
|
t.Fatalf("body before override = %q, want env-default rendering", body)
|
|
}
|
|
|
|
if err := store.SetLoginCodeMessageTemplate("Panel says your code is {{code}}."); err != nil {
|
|
t.Fatalf("SetLoginCodeMessageTemplate: %v", err)
|
|
}
|
|
u2 := signUpPhoneForLoginCodeMessage(t, ctx, svc, "+15550771004")
|
|
body2 := codeMessageBody(t, ctx, messages, u2.ID)
|
|
if body2 != "Panel says your code is 12345." {
|
|
t.Fatalf("body after override = %q, want panel-override rendering with no restart", body2)
|
|
}
|
|
})
|
|
|
|
// A saved-but-somehow-invalid panel override (missing {{code}} --
|
|
// bypassing the admin-API validation, e.g. a hand-edited identity.json)
|
|
// must never reach a client with the code silently missing: defense in
|
|
// depth falls back to the compiled-in default instead.
|
|
t.Run("invalid panel override falls back safely", func(t *testing.T) {
|
|
store := identity.NewStore(t.TempDir())
|
|
if err := store.SetLoginCodeMessageTemplate("no placeholder here"); err != nil {
|
|
t.Fatalf("SetLoginCodeMessageTemplate: %v", err)
|
|
}
|
|
svc, messages := newSvc(store, "")
|
|
u := signUpPhoneForLoginCodeMessage(t, ctx, svc, "+15550771005")
|
|
body := codeMessageBody(t, ctx, messages, u.ID)
|
|
if !strings.Contains(body, "12345") {
|
|
t.Fatalf("body = %q, want the code delivered via fallback despite invalid override", body)
|
|
}
|
|
})
|
|
}
|
|
|
|
func signUpPhoneForLoginCodeMessage(t *testing.T, ctx context.Context, svc *Service, phone string) domain.User {
|
|
t.Helper()
|
|
hash, err := svc.SendCode(ctx, phone)
|
|
if err != nil {
|
|
t.Fatalf("SendCode: %v", err)
|
|
}
|
|
verifyCodeForSignUp(t, svc, phone, hash, "12345")
|
|
u, _, err := svc.SignUp(ctx, domain.Authorization{}, phone, hash, "Code", "Template")
|
|
if err != nil {
|
|
t.Fatalf("SignUp: %v", err)
|
|
}
|
|
return u
|
|
}
|
|
|
|
// codeMessageBody finds the login-code delivery message among the user's
|
|
// full message history (not the dialog summary, which only tracks each
|
|
// peer's single top message -- SignUp's welcome message overwrites the
|
|
// 777000 dialog's top message right after the login-code one is written).
|
|
func codeMessageBody(t *testing.T, ctx context.Context, messages *memory.MessageStore, userID int64) string {
|
|
t.Helper()
|
|
list, err := messages.ListByUser(ctx, userID, domain.MessageFilter{Limit: 10})
|
|
if err != nil {
|
|
t.Fatalf("ListByUser: %v", err)
|
|
}
|
|
for _, msg := range list.Messages {
|
|
if strings.Contains(msg.Body, "12345") {
|
|
return msg.Body
|
|
}
|
|
}
|
|
t.Fatalf("no login-code message (containing 12345) found among %d messages for user %d", len(list.Messages), userID)
|
|
return ""
|
|
}
|