Skip to content
This repository was archived by the owner on Mar 31, 2025. It is now read-only.

Commit f51a706

Browse files
authored
Merge pull request #132 from rust-lang/org-tests
Generalize organization handling in tests
2 parents 3fc73d0 + 8a16c88 commit f51a706

File tree

2 files changed

+94
-75
lines changed

2 files changed

+94
-75
lines changed

src/github/tests/mod.rs

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::github::tests::test_utils::{BranchProtectionBuilder, DataModel, RepoData, TeamData};
1+
use crate::github::tests::test_utils::{
2+
BranchProtectionBuilder, DEFAULT_ORG, DataModel, RepoData, TeamData,
3+
};
24
use rust_team_data::v1::{BranchProtectionMode, RepoPermission};
35

46
mod test_utils;
@@ -17,7 +19,7 @@ fn team_create() {
1719
let user = model.create_user("mark");
1820
let user2 = model.create_user("jan");
1921
let gh = model.gh_model();
20-
model.create_team(TeamData::new("admins").gh_team("admins-gh", &[user, user2]));
22+
model.create_team(TeamData::new("admins").gh_team(DEFAULT_ORG, "admins-gh", &[user, user2]));
2123
let team_diff = model.diff_teams(gh);
2224
insta::assert_debug_snapshot!(team_diff, @r###"
2325
[
@@ -48,7 +50,7 @@ fn team_add_member() {
4850
let mut model = DataModel::default();
4951
let user = model.create_user("mark");
5052
let user2 = model.create_user("jan");
51-
model.create_team(TeamData::new("admins").gh_team("admins-gh", &[user]));
53+
model.create_team(TeamData::new("admins").gh_team(DEFAULT_ORG, "admins-gh", &[user]));
5254
let gh = model.gh_model();
5355

5456
model.get_team("admins").add_gh_member("admins-gh", user2);
@@ -85,11 +87,11 @@ fn team_dont_add_member_if_invitation_is_pending() {
8587
let mut model = DataModel::default();
8688
let user = model.create_user("mark");
8789
let user2 = model.create_user("jan");
88-
model.create_team(TeamData::new("admins").gh_team("admins-gh", &[user]));
90+
model.create_team(TeamData::new("admins").gh_team(DEFAULT_ORG, "admins-gh", &[user]));
8991
let mut gh = model.gh_model();
9092

9193
model.get_team("admins").add_gh_member("admins-gh", user2);
92-
gh.add_invitation("admins-gh", "jan");
94+
gh.add_invitation(DEFAULT_ORG, "admins-gh", "jan");
9395

9496
let team_diff = model.diff_teams(gh);
9597
insta::assert_debug_snapshot!(team_diff, @"[]");
@@ -100,7 +102,7 @@ fn team_remove_member() {
100102
let mut model = DataModel::default();
101103
let user = model.create_user("mark");
102104
let user2 = model.create_user("jan");
103-
model.create_team(TeamData::new("admins").gh_team("admins-gh", &[user, user2]));
105+
model.create_team(TeamData::new("admins").gh_team(DEFAULT_ORG, "admins-gh", &[user, user2]));
104106
let gh = model.gh_model();
105107

106108
model
@@ -142,8 +144,8 @@ fn team_delete() {
142144
// won't be generated, because no organization is known to scan for existing unmanaged teams.
143145
model.create_team(
144146
TeamData::new("admins")
145-
.gh_team("admins-gh", &[user])
146-
.gh_team("users-gh", &[user]),
147+
.gh_team(DEFAULT_ORG, "admins-gh", &[user])
148+
.gh_team(DEFAULT_ORG, "users-gh", &[user]),
147149
);
148150
let gh = model.gh_model();
149151

src/github/tests/test_utils.rs

+84-67
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::{HashMap, HashSet};
1+
use std::collections::{BTreeSet, HashMap, HashSet};
22

33
use derive_builder::Builder;
44
use rust_team_data::v1;
@@ -13,7 +13,7 @@ use crate::github::{
1313
RepoDiff, SyncGitHub, TeamDiff, api, construct_branch_protection, convert_permission,
1414
};
1515

16-
const DEFAULT_ORG: &str = "rust-lang";
16+
pub const DEFAULT_ORG: &str = "rust-lang";
1717

1818
type UserId = u64;
1919

@@ -74,11 +74,12 @@ impl DataModel {
7474
.map(|user| (user.github_id, user.name.clone()))
7575
.collect();
7676

77-
let mut team_memberships: HashMap<String, HashMap<UserId, TeamMember>> = HashMap::default();
78-
let mut teams = vec![];
77+
let mut orgs: HashMap<String, GithubOrg> = HashMap::default();
78+
7979
for team in &self.teams {
8080
for gh_team in &team.gh_teams {
81-
let res = team_memberships.insert(
81+
let org = orgs.entry(gh_team.org.clone()).or_default();
82+
let res = org.team_memberships.insert(
8283
gh_team.name.clone(),
8384
gh_team
8485
.members
@@ -96,27 +97,26 @@ impl DataModel {
9697
);
9798
assert!(res.is_none());
9899

99-
teams.push(api::Team {
100-
id: Some(teams.len() as u64),
100+
org.teams.push(api::Team {
101+
id: Some(org.teams.len() as u64),
101102
name: gh_team.name.clone(),
102103
description: Some("Managed by the rust-lang/team repository.".to_string()),
103104
privacy: TeamPrivacy::Closed,
104105
slug: gh_team.name.clone(),
105-
})
106+
});
107+
108+
org.members.extend(gh_team.members.iter().copied());
106109
}
107110
}
108111

109-
let mut repos = HashMap::default();
110-
let mut repo_members: HashMap<String, RepoMembers> = HashMap::default();
111-
let mut branch_protections = HashMap::new();
112-
113112
for repo in &self.repos {
114-
repos.insert(
113+
let org = orgs.entry(repo.org.clone()).or_default();
114+
org.repos.insert(
115115
repo.name.clone(),
116116
Repo {
117-
node_id: repos.len().to_string(),
117+
node_id: org.repos.len().to_string(),
118118
name: repo.name.clone(),
119-
org: DEFAULT_ORG.to_string(),
119+
org: repo.org.clone(),
120120
description: repo.description.clone(),
121121
homepage: repo.homepage.clone(),
122122
archived: false,
@@ -146,7 +146,8 @@ impl DataModel {
146146
permission: convert_permission(&m.permission),
147147
})
148148
.collect();
149-
repo_members.insert(repo.name.clone(), RepoMembers { teams, members });
149+
org.repo_members
150+
.insert(repo.name.clone(), RepoMembers { teams, members });
150151

151152
let repo_v1: v1::Repo = repo.clone().into();
152153
let mut protections = vec![];
@@ -156,19 +157,15 @@ impl DataModel {
156157
construct_branch_protection(&repo_v1, protection),
157158
));
158159
}
159-
branch_protections.insert(repo.name.clone(), protections);
160+
org.branch_protections
161+
.insert(repo.name.clone(), protections);
160162
}
161163

162-
GithubMock {
163-
users,
164-
owners: Default::default(),
165-
teams,
166-
team_memberships,
167-
team_invitations: Default::default(),
168-
repos,
169-
repo_members,
170-
branch_protections,
164+
if orgs.is_empty() {
165+
orgs.insert(DEFAULT_ORG.to_string(), GithubOrg::default());
171166
}
167+
168+
GithubMock { users, orgs }
172169
}
173170

174171
pub fn diff_teams(&self, github: GithubMock) -> Vec<TeamDiff> {
@@ -249,10 +246,10 @@ impl From<TeamData> for v1::Team {
249246
}
250247

251248
impl TeamDataBuilder {
252-
pub fn gh_team(mut self, name: &str, members: &[UserId]) -> Self {
249+
pub fn gh_team(mut self, org: &str, name: &str, members: &[UserId]) -> Self {
253250
let mut gh_teams = self.gh_teams.unwrap_or_default();
254251
gh_teams.push(GitHubTeam {
255-
org: DEFAULT_ORG.to_string(),
252+
org: org.to_string(),
256253
name: name.to_string(),
257254
members: members.to_vec(),
258255
});
@@ -265,6 +262,8 @@ impl TeamDataBuilder {
265262
#[builder(pattern = "owned")]
266263
pub struct RepoData {
267264
name: String,
265+
#[builder(default = DEFAULT_ORG.to_string())]
266+
org: String,
268267
#[builder(default)]
269268
pub description: String,
270269
#[builder(default)]
@@ -307,6 +306,7 @@ impl From<RepoData> for v1::Repo {
307306
fn from(value: RepoData) -> Self {
308307
let RepoData {
309308
name,
309+
org,
310310
description,
311311
homepage,
312312
bots,
@@ -317,7 +317,7 @@ impl From<RepoData> for v1::Repo {
317317
branch_protections,
318318
} = value;
319319
Self {
320-
org: DEFAULT_ORG.to_string(),
320+
org,
321321
name: name.clone(),
322322
description,
323323
homepage,
@@ -411,28 +411,30 @@ impl BranchProtectionBuilder {
411411
pub struct GithubMock {
412412
// user ID -> login
413413
users: HashMap<UserId, String>,
414-
// org name -> user ID
415-
owners: HashMap<String, Vec<UserId>>,
416-
teams: Vec<Team>,
417-
// Team name -> members
418-
team_memberships: HashMap<String, HashMap<UserId, TeamMember>>,
419-
// Team name -> list of invited users
420-
team_invitations: HashMap<String, Vec<String>>,
421-
// Repo name -> repo data
422-
repos: HashMap<String, Repo>,
423-
// Repo name -> (teams, members)
424-
repo_members: HashMap<String, RepoMembers>,
425-
// Repo name -> Vec<(protection ID, branch protection)>
426-
branch_protections: HashMap<String, Vec<(String, BranchProtection)>>,
414+
// org name -> organization data
415+
orgs: HashMap<String, GithubOrg>,
427416
}
428417

429418
impl GithubMock {
430-
pub fn add_invitation(&mut self, repo: &str, user: &str) {
431-
self.team_invitations
419+
pub fn add_invitation(&mut self, org: &str, repo: &str, user: &str) {
420+
self.get_org_mut(org)
421+
.team_invitations
432422
.entry(repo.to_string())
433423
.or_default()
434424
.push(user.to_string());
435425
}
426+
427+
fn get_org(&self, org: &str) -> &GithubOrg {
428+
self.orgs
429+
.get(org)
430+
.unwrap_or_else(|| panic!("Org {org} not found"))
431+
}
432+
433+
fn get_org_mut(&mut self, org: &str) -> &mut GithubOrg {
434+
self.orgs
435+
.get_mut(org)
436+
.unwrap_or_else(|| panic!("Org {org} not found"))
437+
}
436438
}
437439

438440
impl GithubRead for GithubMock {
@@ -446,48 +448,47 @@ impl GithubRead for GithubMock {
446448
}
447449

448450
fn org_owners(&self, org: &str) -> anyhow::Result<HashSet<UserId>> {
449-
Ok(self
450-
.owners
451-
.get(org)
452-
.cloned()
453-
.unwrap_or_default()
454-
.into_iter()
455-
.collect())
451+
Ok(self.get_org(org).owners.iter().copied().collect())
456452
}
457453

458454
fn org_teams(&self, org: &str) -> anyhow::Result<Vec<(String, String)>> {
459-
assert_eq!(org, DEFAULT_ORG);
460455
Ok(self
456+
.get_org(org)
461457
.teams
462458
.iter()
463459
.map(|team| (team.name.clone(), team.slug.clone()))
464460
.collect())
465461
}
466462

467-
fn team(&self, _org: &str, team: &str) -> anyhow::Result<Option<Team>> {
468-
Ok(self.teams.iter().find(|t| t.name == team).cloned())
463+
fn team(&self, org: &str, team: &str) -> anyhow::Result<Option<Team>> {
464+
Ok(self
465+
.get_org(org)
466+
.teams
467+
.iter()
468+
.find(|t| t.name == team)
469+
.cloned())
469470
}
470471

471472
fn team_memberships(
472473
&self,
473474
team: &Team,
474-
_org: &str,
475+
org: &str,
475476
) -> anyhow::Result<HashMap<UserId, TeamMember>> {
476-
let memberships = self
477+
Ok(self
478+
.get_org(org)
477479
.team_memberships
478480
.get(&team.name)
479481
.cloned()
480-
.unwrap_or_default();
481-
Ok(memberships)
482+
.unwrap_or_default())
482483
}
483484

484485
fn team_membership_invitations(
485486
&self,
486487
org: &str,
487488
team: &str,
488489
) -> anyhow::Result<HashSet<String>> {
489-
assert_eq!(org, DEFAULT_ORG);
490490
Ok(self
491+
.get_org(org)
491492
.team_invitations
492493
.get(team)
493494
.cloned()
@@ -497,13 +498,15 @@ impl GithubRead for GithubMock {
497498
}
498499

499500
fn repo(&self, org: &str, repo: &str) -> anyhow::Result<Option<Repo>> {
500-
assert_eq!(org, DEFAULT_ORG);
501-
Ok(self.repos.get(repo).cloned())
501+
Ok(self
502+
.orgs
503+
.get(org)
504+
.and_then(|org| org.repos.get(repo).cloned()))
502505
}
503506

504507
fn repo_teams(&self, org: &str, repo: &str) -> anyhow::Result<Vec<RepoTeam>> {
505-
assert_eq!(org, DEFAULT_ORG);
506508
Ok(self
509+
.get_org(org)
507510
.repo_members
508511
.get(repo)
509512
.cloned()
@@ -512,9 +515,8 @@ impl GithubRead for GithubMock {
512515
}
513516

514517
fn repo_collaborators(&self, org: &str, repo: &str) -> anyhow::Result<Vec<RepoUser>> {
515-
// The mock currently only supports mocking one GitHub organization.
516-
assert_eq!(org, DEFAULT_ORG);
517518
Ok(self
519+
.get_org(org)
518520
.repo_members
519521
.get(repo)
520522
.cloned()
@@ -527,9 +529,7 @@ impl GithubRead for GithubMock {
527529
org: &str,
528530
repo: &str,
529531
) -> anyhow::Result<HashMap<String, (String, BranchProtection)>> {
530-
assert_eq!(org, DEFAULT_ORG);
531-
532-
let Some(protections) = self.branch_protections.get(repo) else {
532+
let Some(protections) = self.get_org(org).branch_protections.get(repo) else {
533533
return Ok(Default::default());
534534
};
535535
let mut result = HashMap::default();
@@ -541,6 +541,23 @@ impl GithubRead for GithubMock {
541541
}
542542
}
543543

544+
#[derive(Default)]
545+
struct GithubOrg {
546+
members: BTreeSet<UserId>,
547+
owners: BTreeSet<UserId>,
548+
teams: Vec<Team>,
549+
// Team name -> list of invited users
550+
team_invitations: HashMap<String, Vec<String>>,
551+
// Team name -> members
552+
team_memberships: HashMap<String, HashMap<UserId, TeamMember>>,
553+
// Repo name -> repo data
554+
repos: HashMap<String, Repo>,
555+
// Repo name -> (teams, members)
556+
repo_members: HashMap<String, RepoMembers>,
557+
// Repo name -> Vec<(protection ID, branch protection)>
558+
branch_protections: HashMap<String, Vec<(String, BranchProtection)>>,
559+
}
560+
544561
#[derive(Clone)]
545562
pub struct RepoMembers {
546563
teams: Vec<RepoTeam>,

0 commit comments

Comments
 (0)