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