blob: 30a011c4f8d39b733939e7702a3f0656f3d23cbd [file]
"""Tests for the ASF organization model: committers, projects and the GitHub link database"""
import asyncio
import asfpy.sqlite
import pytest
import plugins.ldap
import plugins.projects
# Mirrors the ids table ASF infra provisions alongside the rest of gitbox.db; Boxer never creates it
IDS_SCHEMA = """CREATE TABLE ids (
asfid varchar PRIMARY KEY,
githubid varchar,
githubnum integer,
mfa integer,
updated varchar
)"""
@pytest.fixture
def linkdb(tmp_path):
dbhandle = asfpy.sqlite.DB(str(tmp_path / "boxer.db"))
dbhandle.runc(IDS_SCHEMA)
return dbhandle
class FakeRepo:
def __init__(self, filename, project, private=False):
self.filename = filename
self.project = project
self.private = private
def fake_ldap(monkeypatch, members, override=None):
"""Replaces the LDAP client with one that serves canned project memberships"""
class FakeLDAPClient:
def __init__(self, config, **kwargs):
self.ldap_override = override or {}
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
pass
async def get_members(self, project):
return members.get(project, ([], []))
monkeypatch.setattr(plugins.ldap, "LDAPClient", FakeLDAPClient)
def test_committer_without_a_link_row_has_no_github_identity(linkdb):
committer = plugins.projects.Committer("humbedooh", linkdb)
assert committer.github_login is None
assert committer.github_id is None
assert committer.github_mfa is False
def test_committer_loads_its_github_link_from_the_database(linkdb):
linkdb.insert("ids", {"asfid": "humbedooh", "githubid": "gh-humbedooh", "githubnum": 42, "mfa": 1})
committer = plugins.projects.Committer("humbedooh", linkdb)
assert committer.github_login == "gh-humbedooh"
assert committer.github_id == 42
assert committer.github_mfa is True
def test_committer_save_roundtrip(linkdb):
committer = plugins.projects.Committer("humbedooh", None)
committer.github_login = "gh-humbedooh"
committer.github_id = 42
committer.github_mfa = True
committer.save(linkdb)
assert plugins.projects.Committer("humbedooh", linkdb).github_login == "gh-humbedooh"
def test_committer_save_updates_an_existing_row(linkdb):
committer = plugins.projects.Committer("humbedooh", None)
committer.github_login = "gh-humbedooh"
committer.save(linkdb)
committer.github_mfa = True
committer.save(linkdb)
assert plugins.projects.Committer("humbedooh", linkdb).github_mfa is True
assert len(list(linkdb.fetch("ids", limit=None))) == 1
def test_committer_remove_deletes_the_link(linkdb):
"""Used for administrative lockouts, so the row must really be gone"""
committer = plugins.projects.Committer("humbedooh", None)
committer.github_login = "gh-humbedooh"
committer.save(linkdb)
committer.remove(linkdb)
assert linkdb.fetchone("ids", asfid="humbedooh") is None
def test_committer_equality_matches_plain_asf_ids():
"""expire_public_optin and the endpoints rely on `asf_id in project.committers`"""
committer = plugins.projects.Committer("humbedooh", None)
assert committer == "humbedooh"
assert committer != "sk"
assert committer == plugins.projects.Committer("humbedooh", None)
assert committer != 42
assert "humbedooh" in [committer]
assert hash(committer) == hash(plugins.projects.Committer("humbedooh", None))
def test_add_committer_returns_the_same_account_across_projects():
org = plugins.projects.Organization()
org.add_project("httpd", ["humbedooh"], [])
org.add_project("tomcat", ["humbedooh"], [])
assert len(org.committers) == 1
assert {p.name for p in org.committers[0].projects} == {"httpd", "tomcat"}
def test_add_project_ignores_duplicates_and_nameless_projects():
org = plugins.projects.Organization()
project = org.add_project("httpd", ["humbedooh"], [])
assert org.add_project("httpd", ["sk"], []) is None
assert org.projects["httpd"] is project
assert org.add_project("", ["sk"], []) is None
def test_add_repository_grants_public_repos_to_committers_and_private_to_pmc():
org = plugins.projects.Organization()
project = org.add_project("httpd", ["humbedooh", "sk"], ["sk"])
public = FakeRepo("httpd-site", "httpd")
private = FakeRepo("httpd-private", "httpd", private=True)
project.add_repository(public, private=False)
project.add_repository(private, private=True)
assert project.public_repos == [public]
assert project.private_repos == [private]
humbedooh = org.committers[0]
sk = [c for c in org.committers if c.asf_id == "sk"][0]
assert humbedooh.repositories == {public}
assert private in sk.repositories
def build_project(name, committers, pmc=()):
org = plugins.projects.Organization()
project = org.add_project(name, list(committers), list(pmc))
for committer in project.committers:
committer.github_login = f"gh-{committer.asf_id}"
committer.github_mfa = True
return project
def test_public_github_team_requires_mfa_from_the_live_map():
project = build_project("httpd", ["humbedooh", "sk"])
assert project.public_github_team({"gh-humbedooh": True, "gh-sk": False}) == ["gh-humbedooh"]
def test_public_github_team_falls_back_to_cached_mfa():
project = build_project("httpd", ["humbedooh", "sk"])
project.committers[1].github_mfa = False
assert project.public_github_team() == ["gh-humbedooh"]
def test_private_github_team_only_holds_the_pmc():
project = build_project("httpd", ["humbedooh", "sk"], pmc=["sk"])
assert project.private_github_team() == ["gh-sk"]
assert project.private_github_team({"gh-sk": False}) == []
def test_compile_data_groups_repositories_into_projects(monkeypatch):
fake_ldap(monkeypatch, {"httpd": (["humbedooh", "sk"], ["sk"])})
repos = [
FakeRepo("httpd-site", "httpd"),
FakeRepo("httpd-private", "httpd", private=True),
]
org = asyncio.run(plugins.projects.compile_data(plugins.ldap.LDAPConfig({}), repos, None))
project = org.projects["httpd"]
assert [r.filename for r in project.public_repos] == ["httpd-site"]
assert [r.filename for r in project.private_repos] == ["httpd-private"]
assert sorted(c.asf_id for c in project.committers) == ["humbedooh", "sk"]
assert [c.asf_id for c in project.pmc] == ["sk"]
def test_compile_data_keeps_projects_ldap_knows_nothing_about(monkeypatch):
"""An LDAP outage yields empty member lists; downstream code uses that emptiness as its
'no LDAP data' signal, so the project must still exist with no committers"""
fake_ldap(monkeypatch, {})
repos = [FakeRepo("mystery", "mystery")]
org = asyncio.run(plugins.projects.compile_data(plugins.ldap.LDAPConfig({}), repos, None))
assert org.projects["mystery"].committers == []
def test_compile_data_skips_repos_without_a_project(monkeypatch):
fake_ldap(monkeypatch, {})
repos = [FakeRepo("stray", "")]
org = asyncio.run(plugins.projects.compile_data(plugins.ldap.LDAPConfig({}), repos, None))
assert org.projects == {}
def test_compile_data_honours_alternate_owner_overrides(monkeypatch):
fake_ldap(
monkeypatch,
{"httpd": (["humbedooh"], ["humbedooh"]), "tomcat": (["sk"], ["sk"])},
override={"repository_alternate_owners": {"httpd-site": ["tomcat"]}},
)
repos = [FakeRepo("httpd-site", "httpd"), FakeRepo("tomcat", "tomcat")]
org = asyncio.run(plugins.projects.compile_data(plugins.ldap.LDAPConfig({}), repos, None))
assert {r.filename for r in org.projects["tomcat"].public_repos} == {"tomcat", "httpd-site"}