package services // Live-DB integration tests for PgTemplateStore (t-paliad-349 slice 4). // Skipped when TEST_DATABASE_URL is unset, mirroring the other live-DB // tests. Exercises the full round-trip: Create (version 1) → Get → // GetVersion → AddVersion (version 2, current re-pointed) → List, asserting // the carrier bytes, stylemap, and slots persist and resolve intact. import ( "bytes" "context" "errors" "os" "testing" "github.com/google/uuid" "github.com/jmoiron/sqlx" _ "github.com/lib/pq" "mgit.msbls.de/m/paliad/internal/db" "mgit.msbls.de/m/paliad/pkg/docforge" ) func TestPgTemplateStore_RoundTrip(t *testing.T) { url := os.Getenv("TEST_DATABASE_URL") if url == "" { t.Skip("TEST_DATABASE_URL not set — skipping live DB test") } if err := db.ApplyMigrations(url); err != nil { t.Fatalf("apply migrations: %v", err) } pool, err := sqlx.Connect("postgres", url) if err != nil { t.Fatalf("connect: %v", err) } defer pool.Close() ctx := context.Background() store := NewPgTemplateStore(pool) author := uuid.NewString() carrierV1 := []byte("PK\x03\x04 fake docx carrier v1") tmpl, err := store.Create(ctx, docforge.TemplateMetaInput{ NameDE: "Test-Vorlage", NameEN: "Test template", Firm: "HLC", CreatedBy: author, }, docforge.TemplateVersionInput{ CarrierBytes: carrierV1, Stylemap: map[string]string{"paragraph": "Normal", "heading_1": "Heading 1"}, Slots: []docforge.TemplateSlot{ {Key: "project.case_number", Anchor: "{{project.case_number}}", Label: "Aktenzeichen", OrderIndex: 0}, {Key: "parties.claimant.0.name", Anchor: "{{parties.claimant.0.name}}", OrderIndex: 1}, }, CreatedBy: author, }) if err != nil { t.Fatalf("Create: %v", err) } // Clean up the row (cascades to versions + slots) regardless of outcome. defer func() { _, _ = pool.ExecContext(ctx, `DELETE FROM paliad.templates WHERE id = $1`, tmpl.ID) }() // --- Create assertions: version 1, defaults applied, content intact. if tmpl.Version != 1 { t.Errorf("Create version = %d; want 1", tmpl.Version) } if tmpl.Kind != "submission" || tmpl.SourceFormat != "docx" { t.Errorf("defaults: kind=%q format=%q; want submission/docx", tmpl.Kind, tmpl.SourceFormat) } if !bytes.Equal(tmpl.CarrierBytes, carrierV1) { t.Errorf("carrier round-trip mismatch: got %q", tmpl.CarrierBytes) } if tmpl.Stylemap["heading_1"] != "Heading 1" { t.Errorf("stylemap[heading_1] = %q; want 'Heading 1'", tmpl.Stylemap["heading_1"]) } if len(tmpl.Slots) != 2 { t.Fatalf("len(slots) = %d; want 2", len(tmpl.Slots)) } if tmpl.Slots[0].Key != "project.case_number" || tmpl.Slots[0].Label != "Aktenzeichen" { t.Errorf("slot[0] = %+v; want project.case_number/Aktenzeichen", tmpl.Slots[0]) } // --- Get by template id resolves the current version. got, err := store.Get(ctx, tmpl.ID) if err != nil { t.Fatalf("Get: %v", err) } if got.Version != 1 || !bytes.Equal(got.CarrierBytes, carrierV1) || len(got.Slots) != 2 { t.Errorf("Get current version mismatch: v=%d slots=%d", got.Version, len(got.Slots)) } // --- AddVersion bumps to 2 and re-points current. carrierV2 := []byte("PK\x03\x04 fake docx carrier v2 edited") v2, err := store.AddVersion(ctx, tmpl.ID, docforge.TemplateVersionInput{ CarrierBytes: carrierV2, Stylemap: map[string]string{"paragraph": "HLpat-Body-B0"}, Slots: []docforge.TemplateSlot{{Key: "today", Anchor: "{{today}}", OrderIndex: 0}}, CreatedBy: author, }) if err != nil { t.Fatalf("AddVersion: %v", err) } if v2.Version != 2 { t.Errorf("AddVersion version = %d; want 2", v2.Version) } if !bytes.Equal(v2.CarrierBytes, carrierV2) || len(v2.Slots) != 1 || v2.Slots[0].Key != "today" { t.Errorf("AddVersion content mismatch: carrier/slots wrong") } // Get now resolves version 2 (current re-pointed). cur, err := store.Get(ctx, tmpl.ID) if err != nil { t.Fatalf("Get after AddVersion: %v", err) } if cur.Version != 2 || !bytes.Equal(cur.CarrierBytes, carrierV2) { t.Errorf("Get after AddVersion = v%d; want v2 with new carrier", cur.Version) } // --- List reflects the current version number, filtered by firm. metas, err := store.List(ctx, docforge.TemplateFilter{Firm: "HLC", ActiveOnly: true}) if err != nil { t.Fatalf("List: %v", err) } var found *docforge.TemplateMeta for i := range metas { if metas[i].ID == tmpl.ID { found = &metas[i] break } } if found == nil { t.Fatalf("List did not return the created template") } if found.Version != 2 { t.Errorf("List version = %d; want 2 (current)", found.Version) } // --- Unknown id → ErrTemplateNotFound. if _, err := store.Get(ctx, uuid.NewString()); !errors.Is(err, docforge.ErrTemplateNotFound) { t.Errorf("Get(unknown) err = %v; want ErrTemplateNotFound", err) } }