owpengram-server/internal/store/postgres/langpack_seed_integration_test.go
A 1292540350 feat: sync langpack and auth updates
Sync telesrv commits 49f9bab and 04d9563 into the public mirror. Exclude private docs and runtime key material per sync rules.
2026-07-18 00:07:41 +08:00

164 lines
5.8 KiB
Go

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,
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 || len(catalog.Packs) != 1 || catalog.Packs[0].SourceHash != "source-v1" {
t.Fatalf("seed catalog = %+v, %v", catalog, 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 _, err := store.ReconcileSeed(ctx, rollback); err == nil || !strings.Contains(err.Error(), "version rollback") {
t.Fatalf("rollback error = %v", err)
}
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)
}