turash/bugulma/backend/internal/service/i18n_service_bulk_test.go
2025-12-15 10:06:41 +01:00

147 lines
5.4 KiB
Go

package service
import (
"context"
"io"
"net/http"
"strings"
"testing"
"bugulma/backend/internal/domain"
)
// minimal mocks
type mockLocRepo struct {
fieldValues map[string]string // key: entityID|field -> value
idsByField map[string][]string
}
func (m *mockLocRepo) GetEntitiesNeedingTranslation(ctx context.Context, entityType, field, targetLocale string, limit int) ([]string, error) {
return m.idsByField[field], nil
}
func (m *mockLocRepo) GetEntityFieldValue(ctx context.Context, entityType, entityID, field string) (string, error) {
k := entityID + "|" + field
return m.fieldValues[k], nil
}
func (m *mockLocRepo) FindExistingTranslationByRussianText(ctx context.Context, entityType, field, targetLocale, russianText string) (string, error) {
return "", nil
}
// Unused methods to satisfy interface - implement as noops
func (m *mockLocRepo) Create(ctx context.Context, loc *domain.Localization) error { return nil }
func (m *mockLocRepo) GetByEntityAndField(ctx context.Context, entityType, entityID, field, locale string) (*domain.Localization, error) {
return nil, nil
}
func (m *mockLocRepo) GetAllByEntity(ctx context.Context, entityType, entityID string) ([]*domain.Localization, error) {
return nil, nil
}
func (m *mockLocRepo) Update(ctx context.Context, loc *domain.Localization) error { return nil }
func (m *mockLocRepo) Delete(ctx context.Context, id string) error { return nil }
func (m *mockLocRepo) GetByEntityTypeAndLocale(ctx context.Context, entityType, locale string) ([]*domain.Localization, error) {
return nil, nil
}
func (m *mockLocRepo) GetAllLocales(ctx context.Context) ([]string, error) { return nil, nil }
func (m *mockLocRepo) GetSupportedLocalesForEntity(ctx context.Context, entityType, entityID string) ([]string, error) {
return nil, nil
}
func (m *mockLocRepo) BulkCreate(ctx context.Context, localizations []*domain.Localization) error {
return nil
}
func (m *mockLocRepo) BulkDelete(ctx context.Context, ids []string) error { return nil }
func (m *mockLocRepo) SearchLocalizations(ctx context.Context, query string, locale string, limit int) ([]*domain.Localization, error) {
return nil, nil
}
func (m *mockLocRepo) GetTranslationReuseCandidates(ctx context.Context, entityType, field, locale string) ([]domain.ReuseCandidate, error) {
return nil, nil
}
func (m *mockLocRepo) GetTranslationCountsByEntity(ctx context.Context) (map[string]map[string]int, error) {
return nil, nil
}
// fake roundtripper to mock HTTP responses from the translation service
type fakeRoundTripper struct {
responseBody string
}
func (f *fakeRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
body := io.NopCloser(strings.NewReader(f.responseBody))
return &http.Response{StatusCode: 200, Body: body, Header: http.Header{"Content-Type": {"application/json"}}}, nil
}
type mockLocService struct {
saved map[string]string // key: entityID|field|locale -> value
}
func (m *mockLocService) GetLocalizedValue(entityType, entityID, field, locale string) (string, error) {
return "", nil
}
func (m *mockLocService) SetLocalizedValue(entityType, entityID, field, locale, value string) error {
k := entityID + "|" + field + "|" + locale
m.saved[k] = value
return nil
}
func (m *mockLocService) GetAllLocalizedValues(entityType, entityID string) (map[string]map[string]string, error) {
return nil, nil
}
func (m *mockLocService) GetLocalizedEntity(entityType, entityID, locale string) (map[string]string, error) {
return nil, nil
}
func (m *mockLocService) GetSupportedLocalesForEntity(entityType, entityID string) ([]string, error) {
return nil, nil
}
func (m *mockLocService) DeleteLocalizedValue(entityType, entityID, field, locale string) error {
return nil
}
func (m *mockLocService) BulkSetLocalizedValues(entityType, entityID string, values map[string]map[string]string) error {
return nil
}
func (m *mockLocService) GetAllLocales() ([]string, error) { return nil, nil }
func (m *mockLocService) SearchLocalizations(query, locale string, limit int) ([]*domain.Localization, error) {
return nil, nil
}
func (m *mockLocService) ApplyLocalizationToEntity(entity domain.Localizable, locale string) error {
return nil
}
func TestBulkTranslateData_Simple(t *testing.T) {
locRepo := &mockLocRepo{
fieldValues: map[string]string{"org-1|name": "Пример"},
idsByField: map[string][]string{"name": {"org-1"}},
}
locSvc := &mockLocService{saved: make(map[string]string)}
// create a TranslationService with a fake client that returns canned response
ts := NewTranslationService("http://fake-ollama", "test-model")
ts.client = &http.Client{Transport: &fakeRoundTripper{responseBody: `{"model":"test","response":"translated:Пример:en","done":true}`}}
cache := NewTranslationCacheService(locRepo, locSvc)
svc := &I18nService{
locService: locSvc,
locRepo: locRepo,
translationSvc: ts,
cacheService: cache,
}
results, err := svc.BulkTranslateData(context.Background(), "organization", nil, "en", []string{"name"})
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if len(results) != 1 {
t.Fatalf("expected results for 1 entity, got %d", len(results))
}
if results["org-1"]["name"] != "translated:Пример:en" {
t.Fatalf("unexpected translation: %v", results["org-1"]["name"])
}
// Check that translation was saved via locService
saved := locSvc.saved["org-1|name|en"]
if saved != "translated:Пример:en" {
t.Fatalf("expected saved translation, got %v", saved)
}
}