internal/store/postgres/langpack_seed_integration_test.go
Rama main · réplica de lectura del repositorio original.
package postgres
import (
"context"
"path/filepath"
"strings"
"testing"
"time"
applangpack "telesrv/internal/app/langpack"
"telesrv/internal/domain"
)
func TestLangPackSeedReconciliationPostgres(t *testing.T) {
pool := testPool(t)
ctx := context.Background()
packName := "seedtest-" + randomSuffix(t)
store := NewLangPackStore(pool)
t.Cleanup(func() {
_, _ = pool.Exec(ctx, "DELETE FROM lang_pack_strings WHERE lang_pack = $1", packName)
_, _ = pool.Exec(ctx, "DELETE FROM lang_packs WHERE lang_pack = $1", packName)
_, _ = pool.Exec(ctx, "DELETE FROM seed_states WHERE key LIKE $1 OR key = $2 OR key = $3", "langpack:v1:entry:"+packName+":%", "langpack:v1:catalog:"+packName, "langpack:v2:catalog:"+packName)
})
seedV1 := domain.LangPackSeed{
Catalog: packName,
Scopes: []string{packName},
Packs: []domain.LangPackSeedEntry{{
SourceHash: "source-v1",
ContentHash: "hash-v1",
StringsCount: 2,
Size: 1234,
ModTime: time.Unix(1700000000, 123).UnixNano(),
ContentLoaded: true,
Pack: domain.LangPack{
LangPack: packName,
LangCode: "fr",
Version: 1,
Strings: []domain.LangPackString{
{Key: "LanguageName", Value: "Français"},
{Key: "old", Value: "old"},
},
},
}},
}
written, err := store.ReconcileSeed(ctx, seedV1)
if err != nil || written != 2 {
t.Fatalf("reconcile v1 = %d, %v", written, err)
}
seedV1Unloaded := seedV1
seedV1Unloaded.Packs = append([]domain.LangPackSeedEntry(nil), seedV1.Packs...)
seedV1Unloaded.Packs[0].Pack.Strings = nil
seedV1Unloaded.Packs[0].ContentLoaded = false
if written, err := store.ReconcileSeed(ctx, seedV1Unloaded); err != nil || written != 0 {
t.Fatalf("reconcile unchanged v1 = %d, %v", written, err)
}
catalog, err := store.GetSeedCatalog(ctx, packName)
if err != nil || !domain.SameSeedCatalogSnapshot(catalog, seedV1.CatalogSnapshot()) {
t.Fatalf("seed catalog = %+v, %v", catalog, err)
}
// A stat-only refresh must keep the source hash and preserve nanosecond precision through the JSON round-trip.
catalog.Packs[0].ModTime++
if err := store.PutSeedCatalog(ctx, catalog); err != nil {
t.Fatalf("refresh seed catalog: %v", err)
}
refreshed, err := store.GetSeedCatalog(ctx, packName)
if err != nil || !domain.SameSeedCatalogSnapshot(refreshed, catalog) {
t.Fatalf("refreshed seed catalog = %+v, %v", refreshed, err)
}
seedV2 := domain.LangPackSeed{
Catalog: packName,
Scopes: []string{packName},
Packs: []domain.LangPackSeedEntry{{
SourceHash: "source-v2",
ContentHash: "hash-v2",
StringsCount: 2,
ContentLoaded: true,
Pack: domain.LangPack{
LangPack: packName,
LangCode: "fr",
Version: 2,
Strings: []domain.LangPackString{
{Key: "LanguageName", Value: "Français v2"},
{Key: "new", Value: "new"},
},
},
}},
}
written, err = store.ReconcileSeed(ctx, seedV2)
if err != nil || written != 2 {
t.Fatalf("reconcile v2 = %d, %v", written, err)
}
pack, err := store.GetPack(ctx, packName, "fr", 0)
if err != nil || pack.Version != 2 || len(pack.Strings) != 2 || postgresLangPackValue(pack.Strings, "old") != "" || postgresLangPackValue(pack.Strings, "new") != "new" {
t.Fatalf("replaced postgres pack = %+v, err %v", pack, err)
}
mutated := seedV2
mutated.Packs = append([]domain.LangPackSeedEntry(nil), seedV2.Packs...)
mutated.Packs[0].ContentHash = "changed-without-version"
if _, err := store.ReconcileSeed(ctx, mutated); err == nil || !strings.Contains(err.Error(), "without version bump") {
t.Fatalf("same-version mutation error = %v", err)
}
rollback := seedV2
rollback.Packs = append([]domain.LangPackSeedEntry(nil), seedV2.Packs...)
rollback.Packs[0].Pack.Version = 1
rollback.Packs[0].ContentHash = "rollback"
if n, err := store.ReconcileSeed(ctx, rollback); err != nil || n != 0 {
t.Fatalf("older seed reconcile = %d, %v", n, err)
}
keptPack, keptErr := store.GetPack(ctx, packName, "fr", 0)
if keptErr != nil || keptPack.Version != 2 || len(keptPack.Strings) != 2 {
t.Fatalf("pack regressed after older seed = %+v, err %v", keptPack, keptErr)
}
if written, err := store.ReconcileSeed(ctx, domain.LangPackSeed{Catalog: packName}); err != nil || written != 0 {
t.Fatalf("remove missing language = %d, %v", written, err)
}
languages, err := store.ListLanguages(ctx, packName)
if err != nil || len(languages) != 0 {
t.Fatalf("languages after manifest removal = %+v, err %v", languages, err)
}
}
func postgresLangPackValue(items []domain.LangPackString, key string) string {
for _, item := range items {
if item.Key == key {
return item.Value
}
}
return ""
}
func TestBundledLangPackSeedPostgres(t *testing.T) {
pool := testPool(t)
ctx := context.Background()
scopes := []string{"android", "android_x", "ios", "macos", "tdesktop", "weba"}
t.Cleanup(func() {
_, _ = pool.Exec(ctx, "DELETE FROM lang_pack_strings WHERE lang_pack = ANY($1::text[])", scopes)
_, _ = pool.Exec(ctx, "DELETE FROM lang_packs WHERE lang_pack = ANY($1::text[])", scopes)
for _, scope := range scopes {
_, _ = pool.Exec(ctx, "DELETE FROM seed_states WHERE key LIKE $1", "langpack:v1:entry:"+scope+":%")
}
_, _ = pool.Exec(ctx, "DELETE FROM seed_states WHERE key = 'langpack:v1:catalog:langpack'")
_, _ = pool.Exec(ctx, "DELETE FROM seed_states WHERE key = 'langpack:v2:catalog:langpack'")
})
service := applangpack.NewService(NewLangPackStore(pool))
root := filepath.Join("..", "..", "..", "data", "langpack")
started := time.Now()
seeded, err := service.SeedDirectory(ctx, root)
firstDuration := time.Since(started)
if err != nil {
t.Fatalf("seed bundled langpacks to postgres: %v", err)
}
if seeded < 50000 {
t.Fatalf("seeded bundled strings = %d, want full catalog", seeded)
}
started = time.Now()
seededAgain, err := service.SeedDirectory(ctx, root)
secondDuration := time.Since(started)
if err != nil || seededAgain != 0 {
t.Fatalf("reconcile unchanged bundled langpacks = %d, %v", seededAgain, err)
}
var metadataCount, activeCount int64
if err := pool.QueryRow(ctx, `
SELECT
(SELECT COALESCE(sum(strings_count), 0) FROM lang_packs WHERE lang_pack = ANY($1::text[])),
(SELECT count(*) FROM lang_pack_strings WHERE lang_pack = ANY($1::text[]) AND NOT deleted)
`, scopes).Scan(&metadataCount, &activeCount); err != nil {
t.Fatalf("count seeded langpack rows: %v", err)
}
if metadataCount != activeCount {
t.Fatalf("seeded strings_count = %d, active rows = %d", metadataCount, activeCount)
}
t.Logf("bundled langpack seed: first=%s unchanged=%s strings=%d", firstDuration, secondDuration, metadataCount)
}