Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / internal/modules/recruiting/requisition_status_test.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | internal/modules/recruiting/requisition_status_test.go |
| Original SHA-256 | 7cad9ebea307f33c9e570594faf0c791e827959027ad7018a949231e383dc901 |
| Git blob SHA | 16d977dfef6e01e2821dee05808c94a11af8d70e |
| Reading-copy SHA-256 | 7cad9ebea307f33c9e570594faf0c791e827959027ad7018a949231e383dc901 |
Every recorded occurrence
These 2 records contain identical original source bytes. Their paths and revisions remain part of the archive.
| Original record | Branch or archive | Revision / identifier | Original location |
|---|---|---|---|
| Janady13/missionmatch / internal/modules/recruiting/requisition_status_test.go | feature/opportunity-to-outcome | 720d47fae476b0d38477ae4401697d372b1ea2bc | Open original |
| Janady13/missionmatch / internal/modules/recruiting/requisition_status_test.go | feature/aioe-upgrade | 2178e7298d0e5d9af83afa12402fd587f0ba6ed8 | Open original |
Recovered source transcript
Download complete source text ↓344 lines · 12.9 KB
package recruiting
import (
"errors"
"math/rand"
"testing"
"time"
"github.com/shopspring/decimal"
)
var allReqStatuses = []string{reqDraft, reqOpen, reqOnHold, reqFilled, reqClosed, reqCancelled}
// simState mirrors the four columns a transition touches. Applying an effect here is the same arithmetic
// the SQL performs, so the invariants can be proved over the whole transition graph in microseconds
// instead of one round trip per case. The integration test then checks the database agrees.
type simState struct {
status string
openedAt time.Time
closedAt *time.Time
heldSeconds decimal.Decimal
heldSince *time.Time
}
func (s *simState) apply(e statusEffect, to string, now time.Time) {
if e.NoOp {
return
}
if e.SettleHold && s.heldSince != nil {
s.heldSeconds = s.heldSeconds.Add(secondsBetween(*s.heldSince, now))
s.heldSince = nil
}
if e.StartHold {
t := now
s.heldSince = &t
}
if e.SetClosedAt {
t := now
s.closedAt = &t
}
if e.ClearClosedAt {
s.closedAt = nil
}
s.status = to
}
// check asserts the two CHECK constraints migration 0047 and 0052 impose. A state that fails either one
// is a row Postgres would refuse, which at runtime is a 500 rather than a clean error.
func (s *simState) check(t *testing.T, ctx string) {
t.Helper()
if wantClosed := reqTerminal[s.status]; (s.closedAt != nil) != wantClosed {
t.Fatalf("%s: status %q with closed_at set=%v violates requisitions_closed_at_chk",
ctx, s.status, s.closedAt != nil)
}
if wantHold := s.status == reqOnHold; (s.heldSince != nil) != wantHold {
t.Fatalf("%s: status %q with held_since set=%v violates requisitions_held_since_chk",
ctx, s.status, s.heldSince != nil)
}
if s.heldSeconds.IsNegative() {
t.Fatalf("%s: held_seconds went negative (%s)", ctx, s.heldSeconds)
}
}
// TestReqStatusVocabularyMatchesMigration pins the Go set against the CHECK constraint in 0047.
func TestReqStatusVocabularyMatchesMigration(t *testing.T) {
if len(reqStatuses) != 6 {
t.Fatalf("vocabulary has %d entries, migration 0047 permits 6", len(reqStatuses))
}
for _, s := range allReqStatuses {
if !reqStatuses[s] {
t.Errorf("status %q is in migration 0047 but missing from the vocabulary", s)
}
}
// Every terminal status must be in the vocabulary, and the terminal set must match the closed_at
// CHECK in 0047 exactly.
for _, s := range []string{reqFilled, reqClosed, reqCancelled} {
if !reqTerminal[s] {
t.Errorf("%q carries closed_at in migration 0047 but is not marked terminal", s)
}
}
if len(reqTerminal) != 3 {
t.Errorf("terminal set has %d entries, migration 0047 names 3", len(reqTerminal))
}
}
// TestEveryTransitionPairIsDecided walks all thirty six ordered pairs. Every one must produce either a
// usable effect or a typed validation error, never a panic and never a bare error.
func TestEveryTransitionPairIsDecided(t *testing.T) {
legal, illegal := 0, 0
for _, from := range allReqStatuses {
for _, to := range allReqStatuses {
_, err := transitionFor(from, to)
switch {
case err == nil:
legal++
default:
var v *ValidationError
if !errors.As(err, &v) {
t.Errorf("%s to %s returned %T, want a ValidationError", from, to, err)
}
if !errors.Is(err, ErrInvalid) {
t.Errorf("%s to %s does not unwrap to ErrInvalid, so the handler cannot return 422", from, to)
}
illegal++
}
}
}
if legal+illegal != 36 {
t.Fatalf("decided %d pairs, want 36", legal+illegal)
}
// 6 self transitions plus the 14 edges declared in reqTransitions.
if legal != 20 {
t.Errorf("%d legal pairs, want 20 (6 self plus 14 edges)", legal)
}
}
// TestTransitionPreservesInvariants is the core proof. Starting from a valid state in every status, every
// legal transition must leave the row satisfying both database CHECK constraints.
func TestTransitionPreservesInvariants(t *testing.T) {
base := time.Date(2026, 3, 8, 0, 0, 0, 0, time.UTC)
for _, from := range allReqStatuses {
for _, to := range allReqStatuses {
e, err := transitionFor(from, to)
if err != nil {
continue
}
s := seedState(from, base)
s.check(t, "seed "+from)
now := base.Add(50 * time.Hour)
s.apply(e, to, now)
s.check(t, from+" to "+to)
}
}
}
// seedState builds a legal row in the given status.
func seedState(status string, base time.Time) *simState {
s := &simState{status: status, openedAt: base, heldSeconds: decimal.Zero}
if reqTerminal[status] {
c := base.Add(time.Hour)
s.closedAt = &c
}
if status == reqOnHold {
h := base.Add(time.Hour)
s.heldSince = &h
}
return s
}
// TestClosingWhileOnHoldSettlesTheHold is the specific defect the SettleHold rule exists to prevent. An
// order closed while paused must have its open hold bracket settled, or the bracket stays open forever
// and the accrued hold grows without bound against a dead row.
func TestClosingWhileOnHoldSettlesTheHold(t *testing.T) {
base := time.Date(2026, 3, 8, 0, 0, 0, 0, time.UTC)
for _, to := range []string{reqFilled, reqClosed, reqCancelled} {
t.Run(to, func(t *testing.T) {
s := seedState(reqOnHold, base) // held since base+1h
e, err := transitionFor(reqOnHold, to)
if err != nil {
t.Fatalf("on_hold to %s was refused: %v", to, err)
}
if !e.SettleHold {
t.Fatal("leaving a hold for a terminal status must settle the hold")
}
s.apply(e, to, base.Add(4*time.Hour))
if s.heldSince != nil {
t.Fatal("the hold bracket is still open on a closed order")
}
// Held from base+1h to base+4h is exactly three hours.
if !s.heldSeconds.Equal(decimal.NewFromInt(10800)) {
t.Errorf("settled %s seconds, want 10800", s.heldSeconds)
}
// And the age must not drift as time passes after the close.
_, a1 := reqAge(s.openedAt, s.closedAt, s.heldSeconds, s.heldSince, base.Add(5*time.Hour))
_, a2 := reqAge(s.openedAt, s.closedAt, s.heldSeconds, s.heldSince, base.Add(900*time.Hour))
if !a1.Equal(a2) {
t.Errorf("the age of a closed order moved with the clock: %s then %s", a1, a2)
}
})
}
}
// TestHoldAccumulatorIsExactAcrossShortHolds is the arithmetic migration 0052 exists to fix. Three ten
// hour holds are 1.25 days. The old int day column recorded zero, because each period truncated to zero
// before it was added, and the error was one directional so no amount of data would have revealed it.
func TestHoldAccumulatorIsExactAcrossShortHolds(t *testing.T) {
base := time.Date(2026, 5, 1, 0, 0, 0, 0, time.UTC)
s := &simState{status: reqOpen, openedAt: base, heldSeconds: decimal.Zero}
at := base
for i := 0; i < 3; i++ {
at = at.Add(2 * time.Hour)
e, _ := transitionFor(reqOpen, reqOnHold)
s.apply(e, reqOnHold, at)
at = at.Add(10 * time.Hour)
e, _ = transitionFor(reqOnHold, reqOpen)
s.apply(e, reqOpen, at)
s.check(t, "after hold cycle")
}
if !s.heldSeconds.Equal(decimal.NewFromInt(108000)) {
t.Fatalf("accumulated %s seconds, want 108000 (30 hours)", s.heldSeconds)
}
if got := days(s.heldSeconds); got != 1.25 {
t.Errorf("held time renders as %v days, want 1.25", got)
}
// Elapsed is 36 hours, of which 30 were on hold, leaving 6 hours of real work.
elapsed, adjusted := reqAge(s.openedAt, nil, s.heldSeconds, s.heldSince, at)
if !elapsed.Equal(decimal.NewFromInt(129600)) {
t.Errorf("elapsed %s seconds, want 129600", elapsed)
}
if !adjusted.Equal(decimal.NewFromInt(21600)) {
t.Errorf("adjusted %s seconds, want 21600", adjusted)
}
}
// TestAdjustedAgeIsNeverNegativeUnderRandomWalks is the property that matters most, because a negative
// age is the one arithmetic outcome that can never be a real measurement. Ten thousand random legal
// transition sequences, with the clock advancing by a random amount at every step, must never produce
// one, and must never break either database invariant.
func TestAdjustedAgeIsNeverNegativeUnderRandomWalks(t *testing.T) {
rng := rand.New(rand.NewSource(20260720)) // fixed seed: a failure must be reproducible
base := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
for walk := 0; walk < 10000; walk++ {
s := &simState{status: reqDraft, openedAt: base, heldSeconds: decimal.Zero}
now := base
prevHeld := decimal.Zero
for step := 0; step < 12; step++ {
// Advance by anything from a microsecond to nine days, so both sub second transitions and
// long holds are exercised.
now = now.Add(time.Duration(rng.Int63n(int64(9*24*time.Hour)) + int64(time.Microsecond)))
to := allReqStatuses[rng.Intn(len(allReqStatuses))]
e, err := transitionFor(s.status, to)
if err != nil {
continue // illegal from here, try another
}
from := s.status
s.apply(e, to, now)
s.check(t, "walk step")
// The accumulator is monotonic. A hold that has already been served cannot be un served.
if s.heldSeconds.LessThan(prevHeld) {
t.Fatalf("held_seconds decreased from %s to %s on %s to %s", prevHeld, s.heldSeconds, from, to)
}
prevHeld = s.heldSeconds
// Read the age at the transition instant and again later, in both cases through the same
// function the DTO uses.
for _, at := range []time.Time{now, now.Add(time.Duration(rng.Int63n(int64(72 * time.Hour))))} {
elapsed, adjusted := reqAge(s.openedAt, s.closedAt, s.heldSeconds, s.heldSince, at)
if adjusted.IsNegative() {
t.Fatalf("adjusted age negative (%s) after %s to %s", adjusted, from, to)
}
if adjusted.GreaterThan(elapsed) {
t.Fatalf("adjusted age %s exceeds elapsed %s, which means hold time was credited twice",
adjusted, elapsed)
}
if elapsed.IsNegative() {
t.Fatalf("elapsed age negative (%s), the clock ran backwards", elapsed)
}
}
}
}
}
// TestReqAgeUsesClosedAtNotNow. A closed order has a fixed age. If it were measured against the current
// clock, every historical time to fill in a report would grow a little every day it was read.
func TestReqAgeUsesClosedAtNotNow(t *testing.T) {
base := time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC)
closed := base.Add(240 * time.Hour)
e1, _ := reqAge(base, &closed, decimal.Zero, nil, base.Add(300*time.Hour))
e2, _ := reqAge(base, &closed, decimal.Zero, nil, base.Add(9000*time.Hour))
if !e1.Equal(e2) || !e1.Equal(decimal.NewFromInt(864000)) {
t.Errorf("a closed order measured %s then %s seconds, want 864000 both times", e1, e2)
}
}
// TestReqAgeCountsAHoldInProgress. A paused order must stop accruing workable age immediately, otherwise
// a client freezing hiring for a month makes the recruiter's numbers look bad.
func TestReqAgeCountsAHoldInProgress(t *testing.T) {
base := time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC)
held := base.Add(10 * time.Hour)
_, a1 := reqAge(base, nil, decimal.Zero, &held, base.Add(20*time.Hour))
_, a2 := reqAge(base, nil, decimal.Zero, &held, base.Add(400*time.Hour))
if !a1.Equal(a2) || !a1.Equal(decimal.NewFromInt(36000)) {
t.Errorf("adjusted age moved during a hold: %s then %s, want 36000 both times", a1, a2)
}
}
// TestSecondsBetweenKeepsMicroseconds. Postgres stores timestamptz to the microsecond. Rounding here
// would make short holds vanish, which is the same class of defect migration 0052 corrects.
func TestSecondsBetweenKeepsMicroseconds(t *testing.T) {
a := time.Date(2026, 7, 20, 1, 0, 0, 0, time.UTC)
b := a.Add(1500 * time.Microsecond)
if got := secondsBetween(a, b); !got.Equal(decimal.RequireFromString("0.0015")) {
t.Errorf("secondsBetween lost precision: %s, want 0.0015", got)
}
}
// TestNoOpTransitionChangesNothing. Sending the status a requisition already has is idempotent rather
// than an error, and records no event, so a retried request does not pad the history.
func TestNoOpTransitionChangesNothing(t *testing.T) {
base := time.Date(2026, 4, 1, 0, 0, 0, 0, time.UTC)
for _, s := range allReqStatuses {
e, err := transitionFor(s, s)
if err != nil {
t.Fatalf("%s to itself was refused: %v", s, err)
}
if !e.NoOp {
t.Errorf("%s to itself is not marked as a no op", s)
}
before := seedState(s, base)
after := seedState(s, base)
after.apply(e, s, base.Add(time.Hour))
if after.status != before.status || (after.closedAt == nil) != (before.closedAt == nil) ||
!after.heldSeconds.Equal(before.heldSeconds) || (after.heldSince == nil) != (before.heldSince == nil) {
t.Errorf("%s to itself changed the row", s)
}
}
}
// TestDraftCannotBeHeldOrFilled. A draft has not been released to anyone, so there is nothing to pause
// and nobody to place.
func TestDraftCannotBeHeldOrFilled(t *testing.T) {
for _, to := range []string{reqOnHold, reqFilled, reqClosed} {
if _, err := transitionFor(reqDraft, to); err == nil {
t.Errorf("a draft was allowed to move straight to %s", to)
}
}
}
// TestUnknownStatusIsRejectedBeforeTheGraphIsConsulted. An unknown target must be a clean 422 naming the
// field, not a lookup that misses and reports a confusing "cannot move to" message.
func TestUnknownStatusIsRejected(t *testing.T) {
_, err := transitionFor(reqOpen, "archived")
var v *ValidationError
if !errors.As(err, &v) || v.Field != "status" {
t.Fatalf("unknown status returned %v, want a ValidationError on status", err)
}
if v.Message != "is not a permitted status" {
t.Errorf("message %q does not say the value is unknown", v.Message)
}
}