dodil/erp-brick-hr
tests/seed.py
"""The configuration every test and scenario starts from — written THROUGH THE DOORS (use cases), never as rows:
the gb pack with its starter playbooks, one org tree (Operations ← Studio, Campaigns) with chief positions, jobs,
an office, a schedule, and the groups and chat spaces each team gets.
`post(path, body)` is whatever reaches the hr routes: the brick's own app (paths as is) or a composition
(paths under /hr). Returns nothing; raises on any refusal.
"""
ORG = "acme"
SINCE = "2020-01-01"
DOMAIN = "co.example"
def seed(post, prefix: str = "") -> None:
def ok(path, body):
status, out = post(prefix + path, body)
assert 200 <= status < 300, (path, status, out)
return out
ok("/localisation-packs/gb", {"with_playbooks": True})
ok("/configuration/settings", {"rows": [{"key": "email_domain", "value": DOMAIN},
{"key": "default_org_id", "value": ORG},
{"key": "default_legal_entity", "value": "ACME-UK"},
{"key": "hr_mailbox", "value": f"people@{DOMAIN}"}]})
ok("/configuration/job", {"rows": [{"job_code": c, "title": t} for c, t in (
("designer", "Designer"), ("motion_designer", "Motion Designer"), ("producer", "Campaign Producer"),
("ops_lead", "Head of Operations"), ("studio_lead", "Studio Lead"), ("campaigns_lead", "Campaigns Lead"))]})
ok("/configuration/location", {"rows": [{"location_code": "LON", "name": "London", "address": "1 Example Sq",
"country": "GB", "timezone": "Europe/London",
"holiday_calendar": "gb-eaw"}]})
ok("/configuration/work_schedule", {"rows": [{"schedule_code": "FT", "label": "37.5 h Mon–Fri",
"weekly_minutes": 2250, "full_time_weekly_minutes": 2250,
"working_weekdays": [0, 1, 2, 3, 4]},
{"schedule_code": "PT3", "label": "3 days", "weekly_minutes": 1350,
"full_time_weekly_minutes": 2250, "working_weekdays": [0, 1, 2]}]})
ok("/configuration/unit_access", {"rows": [
{"org_unit_id": "STUDIO", "kind": "group", "target": f"team-studio@{DOMAIN}", "role": "member"},
{"org_unit_id": "STUDIO", "kind": "chat_space", "target": "Studio"},
{"org_unit_id": "CAMP", "kind": "group", "target": f"team-campaigns@{DOMAIN}", "role": "member"},
{"org_unit_id": "CAMP", "kind": "chat_space", "target": "Campaigns"},
{"org_unit_id": "*", "kind": "group", "target": f"all@{DOMAIN}", "role": "member"}]})
ok("/org", {"org_id": ORG, "kind": "org", "values": {"name": "Acme", "legal_entity_ref": "ACME-UK"}})
for uid, name, parent in (("OPS", "Operations", None), ("STUDIO", "Studio", "OPS"), ("CAMP", "Campaigns", "OPS")):
ok("/org", {"org_id": ORG, "kind": "unit", "id": uid, "valid_from": SINCE,
"values": {"name": name, "parent_org_unit_id": parent, "location_code": "LON"}})
for pid, uid, job in (("P-OPS", "OPS", "ops_lead"), ("P-STUDIO", "STUDIO", "studio_lead"),
("P-CAMP", "CAMP", "campaigns_lead")):
ok("/org", {"org_id": ORG, "kind": "position", "id": pid, "valid_from": SINCE,
"values": {"org_unit_id": uid, "job_code": job}})
ok("/org", {"org_id": ORG, "kind": "unit", "id": uid, "valid_from": SINCE,
"values": {"chief_position_id": pid}})
def hire_body(given: str, family: str, start: str, *, unit: str = "STUDIO", job: str = "designer",
worker_type: str = "employee_ft", email: str = None, **extra) -> dict:
body = {"legal_given_name": given, "legal_family_name": family, "worker_type": worker_type, "job_code": job,
"org_unit_id": unit, "start_date": start, "location_code": "LON", "schedule_code": "FT",
"personal_email": email or f"{given.lower()}.{family.lower()}@home.example"}
body.update(extra)
return body
def sign_in(post, person_id, sub: str, prefix: str = "", get=None) -> None:
"""Give a person a work email and a pool sub — the HR-25 path: a detail correction for the address, then the
invite, whose manual task an admin completes with the sub they created."""
status, p = get(f"{prefix}/people/{person_id}")
assert status == 200, p
first = p["detail_history"][0]["valid_from"]
status, out = post(f"{prefix}/people/{person_id}/corrections",
{"record": "detail", "valid_from": first, "values": {"work_email": f"{sub}@scenario.test"}})
assert status == 200, out
status, inv = post(f"{prefix}/people/{person_id}/invite", {})
assert status == 200, inv
if inv.get("state") != "done":
status, out = post(f"{prefix}/runs/{inv['run_id']}/steps/1/complete",
{"output": {"sub": sub, "email": f"{sub}@scenario.test", "created": True},
"provider_ref": sub})
assert status == 200, out