package opportunities import ( "errors" "testing" ) // THE DATABASE'S REFUSALS MUST ARRIVE AS SENTENCES, not as raw constraint violations. // // Every CHECK in migration 0102 encodes a rule somebody thought about: a signed agreement carries its date, a // commitment names exactly one person, an expiry cannot precede a signature. Surfacing them as // "ERROR: new row violates check constraint ck_kpc_names_somebody" wastes the fact that the rule was written // down, and leaves a capture manager guessing at something they could fix in seconds. func TestConstraintViolationsBecomeActionableSentences(t *testing.T) { for _, c := range []struct { name, raw string want error }{ {"duplicate partner", `ERROR: duplicate key value violates unique constraint "uq_teaming_partner_per_pursuit"`, ErrPartnerExists}, {"commitment names nobody", `ERROR: new row for relation "key_personnel_commitments" violates check constraint "ck_kpc_names_somebody"`, ErrCommitmentNeedsPerson}, {"signed with no date", `ERROR: violates check constraint "ck_teaming_signed_dated"`, ErrSignedNeedsDate}, {"committed with no date", `ERROR: violates check constraint "ck_kpc_committed_dated"`, ErrSignedNeedsDate}, } { got := translateTeaming(errors.New(c.raw)) if !errors.Is(got, c.want) { t.Errorf("%s: got %v, want %v", c.name, got, c.want) } } } // An error the module does not recognise passes through UNCHANGED rather than being flattened into one of the // known sentences. A wrong explanation is worse than no explanation: it sends somebody to fix a rule they did // not break. func TestAnUnknownDatabaseErrorIsNotGuessedAt(t *testing.T) { raw := errors.New("ERROR: connection reset by peer") if got := translateTeaming(raw); got != raw { t.Fatalf("an unrelated error was translated into %v, which would send somebody to the wrong fix", got) } } // The expiry rule reads as a sentence too, and it is the one a capture manager trips most: entering the // signature and expiry the wrong way round. func TestExpiryBeforeSigningExplainsItself(t *testing.T) { got := translateTeaming(errors.New(`violates check constraint "ck_kpc_expiry_after_signing"`)) if !errors.Is(got, ErrInvalidInput) { t.Fatalf("got %v, want an invalid input error", got) } if got.Error() == ErrInvalidInput.Error() { t.Error("the message says nothing beyond invalid input, so it does not say what to change") } }