Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / internal/integration/placement_integration_test.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | internal/integration/placement_integration_test.go |
| Original SHA-256 | 8b2fb5311d73516593892147714985a06bb395ebe7935733240c61a88d030d92 |
| Git blob SHA | 775e3c7d4ac5e144637e49efa1759e7a40de87f7 |
| Reading-copy SHA-256 | 8b2fb5311d73516593892147714985a06bb395ebe7935733240c61a88d030d92 |
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/integration/placement_integration_test.go | feature/opportunity-to-outcome | 720d47fae476b0d38477ae4401697d372b1ea2bc | Open original |
| Janady13/missionmatch / internal/integration/placement_integration_test.go | feature/aioe-upgrade | 2178e7298d0e5d9af83afa12402fd587f0ba6ed8 | Open original |
Recovered source transcript
Download complete source text ↓370 lines · 14.7 KB
//go:build integration
package integration
import (
"context"
"sync"
"testing"
"github.com/google/uuid"
"github.com/missionworkforce/missionmatch/internal/modules/recruiting"
)
// WARNING. These tests create organizations, users, candidates, job orders and placements. Point
// MM_APP_DSN and MM_MIGRATOR_DSN at a scratch or staging database, never at production.
// newAssignment returns a service, a context, and n applications already on a requisition with `seats`.
func newAssignment(t *testing.T, seats, n int) (*recruiting.Service, context.Context, uuid.UUID, []uuid.UUID) {
t.Helper()
svc, ctx, req, cands := newPipeline(t, seats, n)
apps := make([]uuid.UUID, n)
for i, c := range cands {
a, err := svc.Submit(ctx, req, recruiting.CreateApplicationInput{CandidateID: c.String()})
if err != nil {
t.Fatalf("submit %d: %v", i, err)
}
apps[i] = uuid.MustParse(a.ID)
}
return svc, ctx, req, apps
}
// TestOfferToPlacementWalk is the whole revenue path: extend, accept, place, end.
func TestOfferToPlacementWalk(t *testing.T) {
svc, ctx, _, apps := newAssignment(t, 1, 1)
app := apps[0]
iv, err := svc.ScheduleInterview(ctx, app, recruiting.CreateInterviewInput{
Round: 1, Kind: "client", ScheduledAt: "2026-08-01T14:00:00Z", Interviewer: "Dana Whitfield"})
if err != nil {
t.Fatalf("schedule interview: %v", err)
}
if iv.Outcome != "scheduled" {
t.Errorf("new interview outcome = %q, want scheduled", iv.Outcome)
}
if _, err := svc.UpdateInterview(ctx, uuid.MustParse(iv.ID), recruiting.UpdateInterviewInput{
ExpectedUpdatedAt: iv.UpdatedAt, Outcome: strp("passed"), CompletedAt: strp("2026-08-01T15:00:00Z")}); err != nil {
t.Fatalf("complete interview: %v", err)
}
o, err := svc.ExtendOffer(ctx, app, recruiting.CreateOfferInput{
EmploymentType: "contract", BillRate: "165.0000", PayRate: "110.0000"})
if err != nil {
t.Fatalf("extend offer: %v", err)
}
// Margin must be exact: 165 minus 110 is 55, which is 33.3333 percent of 165.
if o.Margin.GrossPerHour == nil || o.Margin.GrossPerHour.String() != "55" {
t.Errorf("gross margin = %v, want 55", o.Margin.GrossPerHour)
}
if o.Margin.Pct == nil || o.Margin.Pct.String() != "33.3333" {
t.Errorf("margin pct = %v, want 33.3333", o.Margin.Pct)
}
if len(o.NextStatuses) == 0 {
t.Error("an extended offer offers no next statuses")
}
o, err = svc.RespondToOffer(ctx, uuid.MustParse(o.ID), recruiting.OfferStatusInput{
ExpectedStatus: "extended", Status: "accepted"})
if err != nil {
t.Fatalf("accept: %v", err)
}
if o.RespondedAt == "" {
t.Error("an accepted offer carries no responded_at")
}
p, err := svc.CreatePlacement(ctx, app, recruiting.CreatePlacementInput{
OfferID: o.ID, EmploymentType: "contract", StartDate: "2026-09-01",
BillRate: "165.0000", PayRate: "110.0000", GuaranteeDays: intp(90)})
if err != nil {
t.Fatalf("create placement: %v", err)
}
if !p.Live || p.Status != "pending_start" {
t.Errorf("new placement is status %q live=%v", p.Status, p.Live)
}
if p.Guarantee.EndsOn != "2026-11-29" { // 1 Sep plus 89 days
t.Errorf("guarantee ends %s, want 2026-11-29", p.Guarantee.EndsOn)
}
// Creating the placement must have moved the application to placed, in the same transaction.
d, err := svc.GetApplication(ctx, app)
if err != nil {
t.Fatalf("get application: %v", err)
}
if d.Stage != "placed" {
t.Fatalf("after placing, the application is at %q, want placed", d.Stage)
}
// Ending inside the guarantee as a fall off must breach; the application leaves the pipeline.
p, err = svc.EndPlacement(ctx, uuid.MustParse(p.ID), recruiting.EndPlacementInput{
Status: "fell_off", EndDate: "2026-10-01",
DispositionCode: "candidate_withdrew", Reason: "Took a permanent role elsewhere"})
if err != nil {
t.Fatalf("end placement: %v", err)
}
if p.Live {
t.Error("an ended placement still reports as live")
}
if !p.Guarantee.Breached {
t.Error("falling off inside the guarantee did not breach it")
}
d, _ = svc.GetApplication(ctx, app)
if d.Stage != "withdrawn" {
t.Errorf("after the fall off the application is at %q, want withdrawn", d.Stage)
}
// The high water mark must still record that this person was placed.
if d.FurthestStage != "placed" {
t.Errorf("furthest stage = %q, want placed", d.FurthestStage)
}
}
func strp(s string) *string { return &s }
func intp(i int) *int { return &i }
// TestPlacementRequiresAndHoldsTheStage. A live placement and stage placed are one fact. Neither may
// exist without the other, and the database refuses both directions.
func TestPlacementRequiresAndHoldsTheStage(t *testing.T) {
svc, ctx, _, apps := newAssignment(t, 1, 1)
app := apps[0]
p, err := svc.CreatePlacement(ctx, app, recruiting.CreatePlacementInput{
EmploymentType: "contract", StartDate: "2026-09-01", BillRate: "150", PayRate: "100"})
if err != nil {
t.Fatalf("create placement: %v", err)
}
// Moving the application off placed while the assignment is live must be refused, naming the fix.
_, err = svc.MoveStage(ctx, app, recruiting.MoveStageInput{
ExpectedStage: "placed", ToStage: "rejected", DispositionCode: "client_declined"})
if err == nil {
t.Fatal("the application left stage placed while a live placement stood")
}
// Once ended, the move is allowed.
if _, err := svc.EndPlacement(ctx, uuid.MustParse(p.ID), recruiting.EndPlacementInput{
Status: "completed", EndDate: "2027-01-01"}); err != nil {
t.Fatalf("end placement: %v", err)
}
if _, err := svc.MoveStage(ctx, app, recruiting.MoveStageInput{
ExpectedStage: "placed", ToStage: "rejected", DispositionCode: "client_declined"}); err != nil {
t.Errorf("after ending the assignment the move was still refused: %v", err)
}
}
// TestPlacementRespectsTheSeatCap. Creating a placement moves the stage, so it inherits the seat cap and
// a full requisition refuses the placement before any money is recorded.
func TestPlacementRespectsTheSeatCap(t *testing.T) {
svc, ctx, _, apps := newAssignment(t, 1, 2)
if _, err := svc.CreatePlacement(ctx, apps[0], recruiting.CreatePlacementInput{
EmploymentType: "contract", StartDate: "2026-09-01", BillRate: "150", PayRate: "100"}); err != nil {
t.Fatalf("first placement: %v", err)
}
if _, err := svc.CreatePlacement(ctx, apps[1], recruiting.CreatePlacementInput{
EmploymentType: "contract", StartDate: "2026-09-01", BillRate: "150", PayRate: "100"}); err == nil {
t.Fatal("a second placement was created against a requisition with one opening")
}
}
// TestOnlyOneAcceptedOfferPerApplication. Two accepted offers means two contradictory sets of agreed
// terms with nothing to say which one is the revenue.
func TestOnlyOneAcceptedOfferPerApplication(t *testing.T) {
svc, ctx, _, apps := newAssignment(t, 1, 1)
app := apps[0]
o1, err := svc.ExtendOffer(ctx, app, recruiting.CreateOfferInput{BillRate: "150", PayRate: "100"})
if err != nil {
t.Fatalf("first offer: %v", err)
}
// A second LIVE offer is refused outright.
if _, err := svc.ExtendOffer(ctx, app, recruiting.CreateOfferInput{BillRate: "160", PayRate: "100"}); err == nil {
t.Fatal("a second live offer was extended")
}
if _, err := svc.RespondToOffer(ctx, uuid.MustParse(o1.ID), recruiting.OfferStatusInput{
ExpectedStatus: "extended", Status: "accepted"}); err != nil {
t.Fatalf("accept: %v", err)
}
// Now that the first is accepted a new one may be extended, but it cannot also be accepted.
o2, err := svc.ExtendOffer(ctx, app, recruiting.CreateOfferInput{BillRate: "170", PayRate: "100"})
if err != nil {
t.Fatalf("second offer after acceptance: %v", err)
}
if _, err := svc.RespondToOffer(ctx, uuid.MustParse(o2.ID), recruiting.OfferStatusInput{
ExpectedStatus: "extended", Status: "accepted"}); err == nil {
t.Fatal("a second offer was accepted while another acceptance stood")
}
}
// TestOfferLifecycleIsEnforced. A declined offer cannot later be accepted; re offering is a new offer so
// the history keeps both.
func TestOfferLifecycleIsEnforced(t *testing.T) {
svc, ctx, _, apps := newAssignment(t, 1, 1)
o, err := svc.ExtendOffer(ctx, apps[0], recruiting.CreateOfferInput{BillRate: "150", PayRate: "100"})
if err != nil {
t.Fatalf("extend: %v", err)
}
id := uuid.MustParse(o.ID)
if _, err := svc.RespondToOffer(ctx, id, recruiting.OfferStatusInput{
ExpectedStatus: "extended", Status: "declined", DeclineReason: "rate too low"}); err != nil {
t.Fatalf("decline: %v", err)
}
if _, err := svc.RespondToOffer(ctx, id, recruiting.OfferStatusInput{Status: "accepted"}); err == nil {
t.Fatal("a declined offer was accepted after the fact")
}
// An accepted offer may still be rescinded, because a client pulling a role after acceptance is real.
o2, _ := svc.ExtendOffer(ctx, apps[0], recruiting.CreateOfferInput{BillRate: "160", PayRate: "100"})
id2 := uuid.MustParse(o2.ID)
if _, err := svc.RespondToOffer(ctx, id2, recruiting.OfferStatusInput{
ExpectedStatus: "extended", Status: "accepted"}); err != nil {
t.Fatalf("accept: %v", err)
}
if _, err := svc.RespondToOffer(ctx, id2, recruiting.OfferStatusInput{
ExpectedStatus: "accepted", Status: "rescinded"}); err != nil {
t.Errorf("an accepted offer could not be rescinded: %v", err)
}
}
// TestPlacementCannotBorrowAnotherCandidatesOffer. Reproduced against 0048 before the fix: a placement
// carrying an offer extended to a different person, so the rates and the money came from someone else.
func TestPlacementCannotBorrowAnotherCandidatesOffer(t *testing.T) {
svc, ctx, _, apps := newAssignment(t, 2, 2)
other, err := svc.ExtendOffer(ctx, apps[1], recruiting.CreateOfferInput{BillRate: "200", PayRate: "120"})
if err != nil {
t.Fatalf("offer to the second candidate: %v", err)
}
if _, err := svc.CreatePlacement(ctx, apps[0], recruiting.CreatePlacementInput{
OfferID: other.ID, EmploymentType: "contract", StartDate: "2026-09-01",
BillRate: "150", PayRate: "100"}); err == nil {
t.Fatal("a placement was created carrying another candidate's offer")
}
// Control: the candidate's own offer is accepted.
own, _ := svc.ExtendOffer(ctx, apps[0], recruiting.CreateOfferInput{BillRate: "150", PayRate: "100"})
if _, err := svc.CreatePlacement(ctx, apps[0], recruiting.CreatePlacementInput{
OfferID: own.ID, EmploymentType: "contract", StartDate: "2026-09-01",
BillRate: "150", PayRate: "100"}); err != nil {
t.Errorf("a placement with the candidate's own offer was refused: %v", err)
}
}
// TestConcurrentPlacementsIntoOneSeat. Eight recruiters placing eight different candidates into a single
// seat at the same instant: exactly one may win, and no money is recorded for the losers.
func TestConcurrentPlacementsIntoOneSeat(t *testing.T) {
svc, ctx, _, apps := newAssignment(t, 1, 8)
var wg sync.WaitGroup
errs := make([]error, len(apps))
start := make(chan struct{})
for i, app := range apps {
wg.Add(1)
go func(i int, app uuid.UUID) {
defer wg.Done()
<-start
_, errs[i] = svc.CreatePlacement(ctx, app, recruiting.CreatePlacementInput{
EmploymentType: "contract", StartDate: "2026-09-01", BillRate: "150", PayRate: "100"})
}(i, app)
}
close(start)
wg.Wait()
won := 0
for _, e := range errs {
if e == nil {
won++
}
}
if won != 1 {
t.Fatalf("%d of %d concurrent placements into one seat succeeded, want exactly 1", won, len(apps))
}
list, err := svc.ListPlacements(ctx, "live", 100, 0)
if err != nil {
t.Fatalf("list: %v", err)
}
if list.Total != 1 {
t.Errorf("%d live placements recorded, want 1", list.Total)
}
}
// TestDirectHireCarriesAFeeNotASpread. The two revenue shapes must never be blended.
func TestDirectHireCarriesAFeeNotASpread(t *testing.T) {
svc, ctx, _, apps := newAssignment(t, 1, 1)
p, err := svc.CreatePlacement(ctx, apps[0], recruiting.CreatePlacementInput{
EmploymentType: "direct_hire", StartDate: "2026-09-01",
Salary: "150000.00", FeeAmount: "30000.00"})
if err != nil {
t.Fatalf("direct hire placement: %v", err)
}
if p.Margin.Fee == nil || p.Margin.Fee.String() != "30000" {
t.Errorf("fee = %v, want 30000", p.Margin.Fee)
}
if p.Margin.GrossPerHour != nil || p.Margin.Pct != nil {
t.Error("a direct hire reported an hourly margin")
}
// A contract placement with no rates is refused rather than recorded as earning nothing.
if _, err := svc.CreatePlacement(ctx, apps[0], recruiting.CreatePlacementInput{
EmploymentType: "contract", StartDate: "2026-09-01"}); err == nil {
t.Error("a contract placement with no rates was accepted")
}
}
// TestAssignmentReadsEverythingAtOnce.
func TestAssignmentReadsEverythingAtOnce(t *testing.T) {
svc, ctx, _, apps := newAssignment(t, 1, 1)
app := apps[0]
if _, err := svc.ScheduleInterview(ctx, app, recruiting.CreateInterviewInput{Round: 1}); err != nil {
t.Fatalf("interview: %v", err)
}
if _, err := svc.ScheduleInterview(ctx, app, recruiting.CreateInterviewInput{Round: 2, Kind: "panel"}); err != nil {
t.Fatalf("interview 2: %v", err)
}
// The same round twice is refused, so two recruiters cannot both book "round 2".
if _, err := svc.ScheduleInterview(ctx, app, recruiting.CreateInterviewInput{Round: 2}); err == nil {
t.Error("the same interview round was booked twice")
}
if _, err := svc.ExtendOffer(ctx, app, recruiting.CreateOfferInput{BillRate: "150", PayRate: "100"}); err != nil {
t.Fatalf("offer: %v", err)
}
if _, err := svc.CreatePlacement(ctx, app, recruiting.CreatePlacementInput{
EmploymentType: "contract", StartDate: "2026-09-01", BillRate: "150", PayRate: "100"}); err != nil {
t.Fatalf("placement: %v", err)
}
a, err := svc.GetAssignment(ctx, app)
if err != nil {
t.Fatalf("assignment: %v", err)
}
if len(a.Interviews) != 2 || len(a.Offers) != 1 || len(a.Placements) != 1 {
t.Errorf("assignment has %d interviews, %d offers, %d placements, want 2, 1, 1",
len(a.Interviews), len(a.Offers), len(a.Placements))
}
if a.Stage != "placed" {
t.Errorf("stage = %q, want placed", a.Stage)
}
}
// TestAssignmentTenantIsolation, with a control.
func TestAssignmentTenantIsolation(t *testing.T) {
svcA, ctxA, _, appsA := newAssignment(t, 1, 1)
o, err := svcA.ExtendOffer(ctxA, appsA[0], recruiting.CreateOfferInput{BillRate: "150", PayRate: "100"})
if err != nil {
t.Fatalf("offer in A: %v", err)
}
_, ctxB, _, _ := newAssignment(t, 1, 1)
if _, err := svcA.GetAssignment(ctxB, appsA[0]); err == nil {
t.Fatal("tenant B read tenant A's assignment")
}
if _, err := svcA.RespondToOffer(ctxB, uuid.MustParse(o.ID), recruiting.OfferStatusInput{
ExpectedStatus: "extended", Status: "accepted"}); err == nil {
t.Fatal("tenant B accepted tenant A's offer")
}
listB, err := svcA.ListPlacements(ctxB, "", 100, 0)
if err != nil {
t.Fatalf("list in B: %v", err)
}
if listB.Total != 0 {
t.Errorf("tenant B sees %d placements from another tenant", listB.Total)
}
// Control: A still sees its own offer.
a, err := svcA.GetAssignment(ctxA, appsA[0])
if err != nil || len(a.Offers) != 1 {
t.Fatalf("tenant A sees %d of its own offers, want 1 (err=%v)", len(a.Offers), err)
}
}