create employee table
Some checks failed
CI / test (push) Failing after 4m38s
CI / fixture (push) Failing after 9s

This commit is contained in:
2026-09-05 10:44:47 +05:30
parent dc785b917c
commit cf99866e12
11 changed files with 820 additions and 26 deletions

View File

@@ -0,0 +1,89 @@
package httpserver_test
import (
"net/http"
"testing"
)
// Who counts as the same person.
//
// The rule is the schema's and it is worth stating plainly, because the whole
// duplicate question turns on it: `worker_profiles` carries
// UNIQUE (org_id, email) and `email` is `citext`. So identity is the pair
// (organization, email), compared case-insensitively, and `full_name` carries
// NO uniqueness at all — an organization may employ any number of people with
// the same name, and they are different people.
//
// These are database guarantees rather than application checks, which is what
// makes them hold under concurrency: two simultaneous creates of the same
// identity cannot both win, whatever the callers checked first.
func createWorker(t *testing.T, r *rbac, act actor, name, email string) response {
t.Helper()
return r.as(act, "POST", "/api/v1/worker-profiles", map[string]any{
"full_name": name, "email": email,
})
}
// A name is not an identity. Two people who share one are two records.
func TestWorkersMayShareAName(t *testing.T) {
r := newRBAC(t)
const shared = "Shared Name"
first := createWorker(t, r, r.admin, shared, "shared-name-1@example.test")
second := createWorker(t, r, r.admin, shared, "shared-name-2@example.test")
for i, got := range []response{first, second} {
if got.code != http.StatusCreated {
t.Fatalf("create %d: %d (%v) — sharing a name must not block creation", i+1, got.code, got.body)
}
}
a := first.body["data"].(map[string]any)
b := second.body["data"].(map[string]any)
if a["id"] == b["id"] {
t.Fatal("two people sharing a name collapsed into one record")
}
if a["email"] == b["email"] {
t.Error("the second worker took the first one's email")
}
}
// The same identity cannot be created twice, whoever it claims to be, and the
// refusal is a conflict a caller can act on rather than a 500.
func TestTheSameIdentityCannotBeCreatedTwice(t *testing.T) {
r := newRBAC(t)
const email = "one-identity@example.test"
if got := createWorker(t, r, r.admin, "Person One", email); got.code != http.StatusCreated {
t.Fatalf("first create: %d (%v)", got.code, got.body)
}
for _, tc := range []struct{ name, who, email string }{
{"a different name on the same email", "Person Two", email},
{"the same email in another case", "Person Three", "ONE-IDENTITY@EXAMPLE.TEST"},
} {
t.Run(tc.name, func(t *testing.T) {
got := createWorker(t, r, r.admin, tc.who, tc.email)
if got.code != http.StatusConflict {
t.Errorf("= %d, want 409 — the identity is already taken", got.code)
}
if got.errCode(t) != "conflict" {
t.Errorf("error code = %q, want conflict", got.errCode(t))
}
})
}
}
// The identity is scoped to the organization, so the same email in another
// tenant is another person and is allowed.
func TestTheSameEmailInAnotherOrganizationIsAnotherPerson(t *testing.T) {
r := newRBAC(t)
const email = "cross-tenant-identity@example.test"
if got := createWorker(t, r, r.admin, "Inside", email); got.code != http.StatusCreated {
t.Fatalf("create inside: %d (%v)", got.code, got.body)
}
if got := createWorker(t, r, r.outsider, "Outside", email); got.code != http.StatusCreated {
t.Errorf("create in another organization = %d, want 201 — identity is (org, email)", got.code)
}
}