package recruiting import ( "reflect" "testing" ) func reflectTypeOf(v any) reflect.Type { return reflect.TypeOf(v) } // TestActivityVocabulariesMatchMigration pins the Go allowlists against the CHECK constraints in 0049. func TestActivityVocabulariesMatchMigration(t *testing.T) { for _, k := range []string{"call", "email", "inmail", "text", "meeting", "note", "submission", "screen"} { if !activityKinds[k] { t.Errorf("kind %q is in migration 0049 but missing here", k) } } if len(activityKinds) != 8 { t.Errorf("%d kinds, migration 0049 permits 8", len(activityKinds)) } for _, d := range []string{"outbound", "inbound", "internal"} { if !activityDirections[d] { t.Errorf("direction %q is in migration 0049 but missing here", d) } } if len(activityDirections) != 3 { t.Errorf("%d directions, migration 0049 permits 3", len(activityDirections)) } for _, o := range []string{"logged", "connected", "no_answer", "left_message", "replied", "bounced", "declined"} { if !activityOutcomes[o] { t.Errorf("outcome %q is in migration 0049 but missing here", o) } } if len(activityOutcomes) != 7 { t.Errorf("%d outcomes, migration 0049 permits 7", len(activityOutcomes)) } // Only the outcomes that mean a human was actually reached count as a connect. Widening this set // would silently inflate every connect rate ever reported. for _, o := range []string{"connected", "replied"} { if !connectingOutcomes[o] { t.Errorf("%q means a person was reached but is not counted as a connect", o) } } for _, o := range []string{"logged", "no_answer", "left_message", "bounced", "declined"} { if connectingOutcomes[o] { t.Errorf("%q does not mean a person was reached but is counted as a connect", o) } } if len(connectingOutcomes) != 2 { t.Errorf("%d connecting outcomes, want exactly 2", len(connectingOutcomes)) } } // TestConnectRateIsExactAndAbsentOnNoAttempts. A rate of "0.00" on nobody dialling claims a failure that // did not happen; an absent rate says there is nothing to report. And the arithmetic is exact decimal, // because these numbers are compared between people. func TestConnectRateIsExactAndAbsentOnNoAttempts(t *testing.T) { for _, c := range []struct { part, total int want string }{ {0, 0, ""}, // nobody dialled: no rate, not a zero rate {0, 10, "0.00"}, // ten dials, no connects: a real zero {1, 3, "33.33"}, {2, 3, "66.67"}, {10, 10, "100.00"}, {1, 8, "12.50"}, {5, 0, ""}, // impossible, but a divide by zero must not panic } { if got := rateOf(c.part, c.total); got != c.want { t.Errorf("rateOf(%d, %d) = %q, want %q", c.part, c.total, got, c.want) } } } // TestLogActivityInputCannotClaimCapture is a structural guard. The whole point of auto_captured is that // the subject cannot set it about themselves, and the first way that breaks is someone adding the field // to the request type. If this fails, the flag has stopped meaning anything. func TestLogActivityInputCannotClaimCapture(t *testing.T) { var in LogActivityInput // A compile time check would be ideal; reflection over the JSON tags is the runtime equivalent and // fails loudly the moment the field is added. for _, forbidden := range []string{"auto_captured", "actor_user_id", "actor_name"} { if hasJSONField(in, forbidden) { t.Errorf("LogActivityInput exposes %q, so a caller could claim it", forbidden) } } } func hasJSONField(v any, name string) bool { t := reflectTypeOf(v) for i := 0; i < t.NumField(); i++ { if tag := t.Field(i).Tag.Get("json"); tag == name || tag == name+",omitempty" { return true } } return false }