agent build
This commit is contained in:
@@ -2,6 +2,10 @@ package seeder_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -298,3 +302,115 @@ func TestSeedShiftsStableForAnchor(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The seed must exercise every application_status. It produced four of seven —
|
||||
// `shortlisted`, `rejected` and `assigned` existed only in the schema — and that
|
||||
// gap was hiding real defects rather than merely being incomplete: the funnel
|
||||
// dropped `rejected` and `assigned` out of every stage bucket, and the agent's
|
||||
// candidate lookup offered somebody already on a shift as a person to chase.
|
||||
// Neither is reachable by any test written against data that never produces them.
|
||||
func TestSeedExercisesEveryApplicationStatus(t *testing.T) {
|
||||
h := testutil.New(t)
|
||||
ctx := context.Background()
|
||||
|
||||
rows, err := h.Pool.Query(ctx, `SELECT unnest(enum_range(NULL::application_status))::text`)
|
||||
if err != nil {
|
||||
t.Fatalf("read enum: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
var declared []string
|
||||
for rows.Next() {
|
||||
var s string
|
||||
if err := rows.Scan(&s); err != nil {
|
||||
t.Fatalf("scan enum: %v", err)
|
||||
}
|
||||
declared = append(declared, s)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
t.Fatalf("enum rows: %v", err)
|
||||
}
|
||||
if len(declared) == 0 {
|
||||
t.Fatal("application_status has no values")
|
||||
}
|
||||
|
||||
for _, status := range declared {
|
||||
var n int
|
||||
if err := h.Pool.QueryRow(ctx,
|
||||
`SELECT count(*) FROM job_applications WHERE status = $1::application_status`,
|
||||
status).Scan(&n); err != nil {
|
||||
t.Fatalf("count %s: %v", status, err)
|
||||
}
|
||||
if n == 0 {
|
||||
t.Errorf("no seeded application is %q — nothing can test the paths that handle it", status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `assigned` is only meaningful if an assignment row backs it: the status says
|
||||
// somebody is on a shift, and without the row it says it of nobody.
|
||||
func TestAnAssignedApplicationHasAnAssignment(t *testing.T) {
|
||||
h := testutil.New(t)
|
||||
ctx := context.Background()
|
||||
|
||||
var orphans int
|
||||
if err := h.Pool.QueryRow(ctx, `
|
||||
SELECT count(*) FROM job_applications a
|
||||
WHERE a.status = 'assigned'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM assignments s
|
||||
WHERE s.application_id = a.id AND s.org_id = a.org_id)`).Scan(&orphans); err != nil {
|
||||
t.Fatalf("count orphans: %v", err)
|
||||
}
|
||||
if orphans != 0 {
|
||||
t.Errorf("%d applications are 'assigned' with no assignment row behind them", orphans)
|
||||
}
|
||||
|
||||
// And the reverse: every position still reports the empty case honestly.
|
||||
var positions, withAssignment int
|
||||
if err := h.Pool.QueryRow(ctx,
|
||||
`SELECT count(*), count(*) FILTER (WHERE EXISTS (
|
||||
SELECT 1 FROM assignments s WHERE s.job_posting_id = p.id))
|
||||
FROM job_postings p`).Scan(&positions, &withAssignment); err != nil {
|
||||
t.Fatalf("count positions: %v", err)
|
||||
}
|
||||
if withAssignment == 0 || withAssignment >= positions {
|
||||
t.Errorf("%d of %d positions have an assignment; want some but not all, so both "+
|
||||
"the populated and the empty case are covered", withAssignment, positions)
|
||||
}
|
||||
}
|
||||
|
||||
// seed.json is generated from krow-demo/src/api/seed.js. The two used to be
|
||||
// hand-maintained copies of one dataset, which fails quietly: the demo and the
|
||||
// API answer the same question with different numbers, and the first symptom is
|
||||
// a page disagreeing with an agent.
|
||||
//
|
||||
// This asserts the fixture is a *generated artefact*, not that it is *current*.
|
||||
// Currency needs the generator, which is JavaScript and lives in the other
|
||||
// repository — the frontend suite runs it and compares byte-for-byte, and
|
||||
// `make seed-fixture-check` runs the same comparison from here. What this
|
||||
// catches is a fixture built or replaced by hand, which carries no marker and
|
||||
// would otherwise be indistinguishable from a generated one.
|
||||
func TestFixtureIsGeneratedNotHandWritten(t *testing.T) {
|
||||
path := os.Getenv("SEED_FIXTURE_PATH")
|
||||
if path == "" {
|
||||
path = filepath.Join("..", "..", "..", "seed", "fixtures", "seed.json")
|
||||
}
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Skipf("fixture not readable at %s: %v", path, err)
|
||||
}
|
||||
|
||||
var head struct {
|
||||
Generated string `json:"_generated"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &head); err != nil {
|
||||
t.Fatalf("fixture is not valid JSON: %v", err)
|
||||
}
|
||||
if head.Generated == "" {
|
||||
t.Error("seed.json carries no `_generated` marker — it looks hand-written. " +
|
||||
"Regenerate it with `make seed-fixture` rather than editing it directly.")
|
||||
}
|
||||
if !strings.Contains(head.Generated, "seed.js") {
|
||||
t.Errorf("`_generated` does not name its source: %q", head.Generated)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user