Sweeps internal/services + internal/handlers + internal/models to use the new proceeding codes landed by mig 096. Stable Code* constants live in internal/services/proceeding_mapping.go so a future rename needs to touch one file. Substantive changes: - proceeding_mapping.go gains ResolveCounterclaimRouting() — the cascade resolver that routes upc.ccr.cfi (illustrative peer) back to upc.inf.cfi with with_ccr=true as default flag (design doc S1). - deadline_search_service.go forum-bucket map updated; upc.ccr.cfi added to upc_cfi since it is a CFI peer. - project_service.go CreateCounterclaim default lookup parameterised so the SQL string carries the constant, not a literal. - proceeding_codes_shape_test.go: new file. Validates the shape regex standalone (always runs) and walks live DB rows asserting every active fristenrechner row matches the new shape + every stable Code* constant resolves to exactly one active row. Comments and test fixtures throughout the Go tree updated to the new shape. Tests pass under `go test ./internal/... -short`.
122 lines
3.9 KiB
Go
122 lines
3.9 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"regexp"
|
|
"testing"
|
|
|
|
"github.com/jmoiron/sqlx"
|
|
_ "github.com/lib/pq"
|
|
|
|
"mgit.msbls.de/m/paliad/internal/db"
|
|
)
|
|
|
|
// shapeRegex is the lowercase dot-separated form ratified by t-paliad-204
|
|
// and enforced at the DB layer by mig 096's paliad_proceeding_code_shape
|
|
// CHECK constraint. Every active fristenrechner-category row must match.
|
|
var shapeRegex = regexp.MustCompile(`^[a-z]+\.[a-z]+\.[a-z]+$`)
|
|
|
|
// TestProceedingCodeShape walks every active fristenrechner-category row
|
|
// in paliad.proceeding_types and asserts the `code` matches the
|
|
// taxonomy regex. Catches future inserts that slip past the CHECK
|
|
// constraint (e.g. via a manual psql edit on a staging snapshot) and
|
|
// catches drift between this Go layer's stable code constants and the
|
|
// DB.
|
|
//
|
|
// Mirrors the assertions in mig 096 §8 — same regex, same shape — so a
|
|
// failure here pinpoints which row went off-shape without making a DB
|
|
// trip first.
|
|
//
|
|
// Skipped when TEST_DATABASE_URL is unset, mirroring the pattern in
|
|
// project_service_test.go.
|
|
func TestProceedingCodeShape(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()
|
|
|
|
var rows []struct {
|
|
ID int `db:"id"`
|
|
Code string `db:"code"`
|
|
}
|
|
if err := pool.SelectContext(ctx, &rows,
|
|
`SELECT id, code FROM paliad.proceeding_types
|
|
WHERE category = 'fristenrechner' AND is_active = true
|
|
ORDER BY id`); err != nil {
|
|
t.Fatalf("load active fristenrechner rows: %v", err)
|
|
}
|
|
if len(rows) == 0 {
|
|
t.Fatal("no active fristenrechner rows — mig 096 likely not applied")
|
|
}
|
|
for _, r := range rows {
|
|
if !shapeRegex.MatchString(r.Code) {
|
|
t.Errorf("proceeding_types[id=%d] code=%q does not match taxonomy shape %s",
|
|
r.ID, r.Code, shapeRegex.String())
|
|
}
|
|
}
|
|
|
|
// Spot-check the stable code constants in proceeding_mapping.go all
|
|
// resolve to live rows. Catches a constant being renamed without a
|
|
// matching mig update.
|
|
stable := []string{
|
|
CodeUPCInfringement, CodeUPCRevocation, CodeUPCCounterclaim,
|
|
CodeUPCPreliminary, CodeUPCDamages, CodeUPCDiscovery,
|
|
CodeUPCAppealMerits, CodeUPCAppealOrder, CodeUPCAppealCost,
|
|
CodeDEInfringementLG, CodeDEInfringementOLG, CodeDEInfringementBGH,
|
|
CodeDENullityBPatG, CodeDENullityBGH,
|
|
CodeEPAGrant, CodeEPAOpposition, CodeEPAOppositionAppeal,
|
|
CodeDPMAOpposition, CodeDPMAAppealBPatG, CodeDPMAAppealBGH,
|
|
}
|
|
for _, c := range stable {
|
|
var hit int
|
|
if err := pool.GetContext(ctx, &hit,
|
|
`SELECT count(*) FROM paliad.proceeding_types
|
|
WHERE code = $1 AND is_active = true`, c); err != nil {
|
|
t.Fatalf("count rows for %s: %v", c, err)
|
|
}
|
|
if hit != 1 {
|
|
t.Errorf("stable code constant %q matches %d active rows, want 1", c, hit)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestProceedingCodeShapeRegexStandalone exercises the regex without
|
|
// hitting the DB so the shape rule is verified on every `go test ./...`
|
|
// run (no skip when TEST_DATABASE_URL is unset).
|
|
func TestProceedingCodeShapeRegexStandalone(t *testing.T) {
|
|
good := []string{
|
|
"upc.inf.cfi", "upc.rev.cfi", "upc.ccr.cfi", "upc.apl.merits",
|
|
"upc.apl.order", "upc.apl.cost", "de.inf.lg", "de.null.bgh",
|
|
"epa.opp.opd", "epa.grant.exa", "dpma.opp.dpma",
|
|
}
|
|
for _, code := range good {
|
|
if !shapeRegex.MatchString(code) {
|
|
t.Errorf("good code %q rejected by shape regex", code)
|
|
}
|
|
}
|
|
bad := []string{
|
|
"UPC_INF", // old uppercase
|
|
"upc.inf", // missing third position
|
|
"upc.inf.cfi.extra", // four positions
|
|
"upc..cfi", // empty middle
|
|
"upc-inf-cfi", // dashes
|
|
"_archived_litigation",
|
|
}
|
|
for _, code := range bad {
|
|
if shapeRegex.MatchString(code) {
|
|
t.Errorf("bad code %q accepted by shape regex", code)
|
|
}
|
|
}
|
|
}
|