owpengram-server/internal/app/translation/service_test.go

108 lines
4.1 KiB
Go

package translation
import (
"context"
"errors"
"testing"
"time"
"telesrv/internal/domain"
"telesrv/internal/store/memory"
)
type testLimiter struct{ cost int }
func (l *testLimiter) AllowN(_ context.Context, _ string, cost, _ int, _ time.Duration) (bool, int, error) {
l.cost = cost
return true, 0, nil
}
type testProvider struct {
translate func([]domain.TranslationText) ([]domain.TranslationText, error)
}
func (testProvider) Name() string { return "test" }
func (p testProvider) Translate(_ context.Context, texts []domain.TranslationText, _, _ string) ([]domain.TranslationText, error) {
return p.translate(texts)
}
type testPrivateMessages struct{ messages []domain.Message }
func (s testPrivateMessages) GetMessages(_ context.Context, _ int64, _ []int) (domain.MessageList, error) {
return domain.MessageList{Messages: append([]domain.Message(nil), s.messages...)}, nil
}
func TestTranslateDirectTextPreservesBatchOrder(t *testing.T) {
limiter := &testLimiter{}
svc := NewService(nil, nil, memory.NewDialogStore(), WithRateLimiter(limiter, 60, time.Minute), WithProviders(testProvider{translate: func(in []domain.TranslationText) ([]domain.TranslationText, error) {
out := make([]domain.TranslationText, len(in))
for i := range in {
out[i].Text = "zh:" + in[i].Text
}
return out, nil
}}))
got, err := svc.Translate(context.Background(), domain.TranslationRequest{
UserID: 1,
Texts: []domain.TranslationText{{Text: "one"}, {Text: "two"}},
ToLang: "zh",
})
if err != nil {
t.Fatalf("Translate: %v", err)
}
if len(got.Texts) != 2 || got.Texts[0].Text != "zh:one" || got.Texts[1].Text != "zh:two" {
t.Fatalf("Translate result = %#v", got.Texts)
}
if limiter.cost != 2 {
t.Fatalf("rate limit cost = %d, want 2 text items", limiter.cost)
}
}
func TestTranslateMessageIDsRejectsWrongPeerAndMissingID(t *testing.T) {
peer := domain.Peer{Type: domain.PeerTypeUser, ID: 2}
svc := NewService(testPrivateMessages{messages: []domain.Message{
{ID: 10, Peer: peer, Body: "visible"},
{ID: 11, Peer: domain.Peer{Type: domain.PeerTypeUser, ID: 3}, Body: "wrong peer"},
}}, nil, memory.NewDialogStore(), WithProviders(testProvider{translate: func(in []domain.TranslationText) ([]domain.TranslationText, error) {
return in, nil
}}))
for _, ids := range [][]int{{10, 11}, {10, 12}} {
_, err := svc.Translate(context.Background(), domain.TranslationRequest{UserID: 1, Peer: peer, IDs: ids, ToLang: "en"})
if !errors.Is(err, domain.ErrTranslationMessageInvalid) {
t.Fatalf("Translate ids %v err = %v, want message invalid", ids, err)
}
}
}
func TestTranslateRejectsOversizeAndProviderShapeMismatch(t *testing.T) {
svc := NewService(nil, nil, memory.NewDialogStore(), WithProviders(testProvider{translate: func(in []domain.TranslationText) ([]domain.TranslationText, error) {
return in[:len(in)-1], nil
}}))
texts := make([]domain.TranslationText, domain.MaxTranslationTexts+1)
for i := range texts {
texts[i].Text = "x"
}
if _, err := svc.Translate(context.Background(), domain.TranslationRequest{UserID: 1, Texts: texts, ToLang: "en"}); !errors.Is(err, domain.ErrTranslationInputTooLong) {
t.Fatalf("oversize err = %v", err)
}
if _, err := svc.Translate(context.Background(), domain.TranslationRequest{UserID: 1, Texts: texts[:2], ToLang: "en"}); !errors.Is(err, domain.ErrTranslationProviderUnavailable) {
t.Fatalf("shape mismatch err = %v", err)
}
}
func TestPeerDisabledIsAccountAndPeerScoped(t *testing.T) {
settings := memory.NewDialogStore()
svc := NewService(nil, nil, settings)
peer := domain.Peer{Type: domain.PeerTypeUser, ID: 2}
if changed, err := svc.SetPeerDisabled(context.Background(), 1, peer, true); err != nil || !changed {
t.Fatalf("disable = %v/%v", changed, err)
}
if disabled, _ := svc.PeerDisabled(context.Background(), 1, peer); !disabled {
t.Fatal("owner preference not persisted")
}
if disabled, _ := svc.PeerDisabled(context.Background(), 2, peer); disabled {
t.Fatal("preference leaked to another account")
}
if changed, err := svc.SetPeerDisabled(context.Background(), 1, peer, false); err != nil || !changed {
t.Fatalf("enable = %v/%v", changed, err)
}
}