/ tests / Organization.py
Organization.py
  1  ############################ Copyrights and license ############################
  2  #                                                                              #
  3  # Copyright 2012 Vincent Jacques <vincent@vincent-jacques.net>                 #
  4  # Copyright 2012 Zearin <zearin@gonk.net>                                      #
  5  # Copyright 2013 Vincent Jacques <vincent@vincent-jacques.net>                 #
  6  # Copyright 2014 Vincent Jacques <vincent@vincent-jacques.net>                 #
  7  # Copyright 2016 Jannis Gebauer <ja.geb@me.com>                                #
  8  # Copyright 2016 Peter Buckley <dx-pbuckley@users.noreply.github.com>          #
  9  # Copyright 2017 Balázs Rostás <rostas.balazs@gmail.com>                     #
 10  # Copyright 2018 Anton Nguyen <afnguyen85@gmail.com>                           #
 11  # Copyright 2018 Jacopo Notarstefano <jacopo.notarstefano@gmail.com>           #
 12  # Copyright 2018 Jasper van Wanrooy <jasper@vanwanrooy.net>                    #
 13  # Copyright 2018 Raihaan <31362124+res0nance@users.noreply.github.com>         #
 14  # Copyright 2018 Tim Boring <tboring@hearst.com>                               #
 15  # Copyright 2018 sfdye <tsfdye@gmail.com>                                      #
 16  # Copyright 2023 Mauricio Martinez <mauricio.martinez@premise.com>             #
 17  #                                                                              #
 18  # This file is part of PyGithub.                                               #
 19  # http://pygithub.readthedocs.io/                                              #
 20  #                                                                              #
 21  # PyGithub is free software: you can redistribute it and/or modify it under    #
 22  # the terms of the GNU Lesser General Public License as published by the Free  #
 23  # Software Foundation, either version 3 of the License, or (at your option)    #
 24  # any later version.                                                           #
 25  #                                                                              #
 26  # PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY  #
 27  # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    #
 28  # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more #
 29  # details.                                                                     #
 30  #                                                                              #
 31  # You should have received a copy of the GNU Lesser General Public License     #
 32  # along with PyGithub. If not, see <http://www.gnu.org/licenses/>.             #
 33  #                                                                              #
 34  ################################################################################
 35  
 36  from datetime import datetime, timezone
 37  from unittest import mock
 38  
 39  import github
 40  
 41  from . import Framework
 42  
 43  
 44  class Organization(Framework.TestCase):
 45      def setUp(self):
 46          super().setUp()
 47          self.org = self.g.get_organization("BeaverSoftware")
 48  
 49      def testAttributes(self):
 50          self.assertEqual(self.org.avatar_url, "https://avatars1.githubusercontent.com/u/1?v=4")
 51          self.assertEqual(self.org.billing_email, "foo@example.com")
 52          self.assertEqual(self.org.blog, "http://www.example.com")
 53          self.assertEqual(self.org.collaborators, 9)
 54          self.assertEqual(self.org.company, None)
 55          self.assertEqual(
 56              self.org.created_at,
 57              datetime(2014, 1, 9, 16, 56, 17, tzinfo=timezone.utc),
 58          )
 59          self.assertEqual(self.org.default_repository_permission, "none")
 60          self.assertEqual(self.org.description, "BeaverSoftware writes software.")
 61          self.assertEqual(self.org.disk_usage, 2)
 62          self.assertEqual(self.org.email, "")
 63          self.assertEqual(self.org.followers, 0)
 64          self.assertEqual(self.org.following, 0)
 65          self.assertEqual(self.org.gravatar_id, None)
 66          self.assertTrue(self.org.has_organization_projects)
 67          self.assertTrue(self.org.has_repository_projects)
 68          self.assertEqual(self.org.hooks_url, "https://api.github.com/orgs/BeaverSoftware/hooks")
 69          self.assertEqual(self.org.html_url, "https://github.com/BeaverSoftware")
 70          self.assertEqual(self.org.id, 1)
 71          self.assertEqual(self.org.issues_url, "https://api.github.com/orgs/BeaverSoftware/issues")
 72          self.assertEqual(self.org.location, "Paris, France")
 73          self.assertEqual(self.org.login, "BeaverSoftware")
 74          self.assertFalse(self.org.members_can_create_repositories)
 75          self.assertEqual(self.org.name, "BeaverSoftware")
 76          self.assertEqual(self.org.owned_private_repos, 0)
 77          self.assertEqual(self.org.plan.name, "free")
 78          self.assertEqual(self.org.plan.private_repos, 3)
 79          self.assertEqual(self.org.plan.space, 1)
 80          self.assertEqual(self.org.plan.filled_seats, 3)
 81          self.assertEqual(self.org.plan.seats, 0)
 82          self.assertEqual(self.org.private_gists, 0)
 83          self.assertEqual(self.org.public_gists, 0)
 84          self.assertEqual(self.org.public_repos, 27)
 85          self.assertEqual(self.org.total_private_repos, 7)
 86          self.assertEqual(self.org.two_factor_requirement_enabled, None)
 87          self.assertEqual(self.org.type, "Organization")
 88          self.assertEqual(self.org.url, "https://api.github.com/orgs/BeaverSoftware")
 89          self.assertEqual(repr(self.org), 'Organization(login="BeaverSoftware")')
 90  
 91      def testAddMembersDefaultRole(self):
 92          lyloa = self.g.get_user("lyloa")
 93          self.assertFalse(self.org.has_in_members(lyloa))
 94          self.org.add_to_members(lyloa, role="member")
 95          # 'Pending' members won't be in /orgs/:org/members/:user
 96          self.assertFalse(self.org.has_in_members(lyloa))
 97          self.org.remove_from_membership(lyloa)
 98          self.assertFalse(self.org.has_in_members(lyloa))
 99  
100      def testAddMembersAdminRole(self):
101          lyloa = self.g.get_user("lyloa")
102          self.assertFalse(self.org.has_in_members(lyloa))
103          self.org.add_to_members(lyloa, role="admin")
104          # 'Pending' members won't be in /orgs/:org/members/:user
105          self.assertFalse(self.org.has_in_members(lyloa))
106          self.org.remove_from_membership(lyloa)
107          self.assertFalse(self.org.has_in_members(lyloa))
108  
109      def testEditWithoutArguments(self):
110          self.org.edit()
111  
112      def testEditWithAllArguments(self):
113          self.org.edit(
114              "BeaverSoftware2@vincent-jacques.net",
115              "http://vincent-jacques.net",
116              "Company edited by PyGithub",
117              "Description edited by PyGithub",
118              "BeaverSoftware2@vincent-jacques.net",
119              "Location edited by PyGithub",
120              "Name edited by PyGithub",
121          )
122          self.assertEqual(self.org.billing_email, "BeaverSoftware2@vincent-jacques.net")
123          self.assertEqual(self.org.blog, "http://vincent-jacques.net")
124          self.assertEqual(self.org.company, "Company edited by PyGithub")
125          self.assertEqual(self.org.description, "Description edited by PyGithub")
126          self.assertEqual(self.org.email, "BeaverSoftware2@vincent-jacques.net")
127          self.assertEqual(self.org.location, "Location edited by PyGithub")
128          self.assertEqual(self.org.name, "Name edited by PyGithub")
129  
130      def testEditHookWithMinimalParameters(self):
131          hook = self.org.create_hook("web", {"url": "http://foobar.com"})
132          hook = self.org.edit_hook(hook.id, "mobile", {"url": "http://barfoo.com"})
133          self.assertEqual(hook.name, "mobile")
134  
135      def testEditHookWithAllParameters(self):
136          hook = self.org.create_hook("web", {"url": "http://foobar.com"}, ["fork"], False)
137          hook = self.org.edit_hook(hook.id, "mobile", {"url": "http://barfoo.com"}, ["spoon"], True)
138          self.assertEqual(hook.name, "mobile")
139          self.assertEqual(hook.events, ["spoon"])
140          self.assertEqual(hook.active, True)
141  
142      def testCreateTeam(self):
143          team = self.org.create_team("Team created by PyGithub")
144          self.assertEqual(team.id, 189850)
145  
146      def testCreateTeamWithAllArguments(self):
147          repo = self.org.get_repo("FatherBeaver")
148          team = self.org.create_team(
149              "Team also created by PyGithub",
150              [repo],
151              "push",
152              "secret",
153              "Description also created by PyGithub",
154          )
155          self.assertEqual(team.id, 189852)
156          self.assertEqual(team.description, "Description also created by PyGithub")
157  
158      def testDeleteHook(self):
159          hook = self.org.create_hook("web", {"url": "http://foobar.com"})
160          self.org.delete_hook(hook.id)
161  
162      def testPublicMembers(self):
163          lyloa = self.g.get_user("Lyloa")
164          self.assertFalse(self.org.has_in_public_members(lyloa))
165          self.org.add_to_public_members(lyloa)
166          self.assertTrue(self.org.has_in_public_members(lyloa))
167          self.org.remove_from_public_members(lyloa)
168          self.assertFalse(self.org.has_in_public_members(lyloa))
169  
170      def testGetPublicMembers(self):
171          self.assertListKeyEqual(self.org.get_public_members(), lambda u: u.login, ["jacquev6"])
172  
173      def testGetHook(self):
174          hook = self.org.get_hook(257993)
175          self.assertEqual(hook.name, "web")
176  
177      def testGetHooks(self):
178          self.assertListKeyEqual(self.org.get_hooks(), lambda h: h.id, [257993])
179  
180      def testGetHookDelivery(self):
181          delivery = self.org.get_hook_delivery(257993, 12345)
182          self.assertEqual(delivery.id, 12345)
183          self.assertEqual(delivery.guid, "abcde-12345")
184          self.assertEqual(
185              delivery.delivered_at,
186              datetime(2012, 5, 27, 6, 0, 32, tzinfo=timezone.utc),
187          )
188          self.assertEqual(delivery.redelivery, False)
189          self.assertEqual(delivery.duration, 0.27)
190          self.assertEqual(delivery.status, "OK")
191          self.assertEqual(delivery.status_code, 200)
192          self.assertEqual(delivery.event, "issues")
193          self.assertEqual(delivery.action, "opened")
194          self.assertEqual(delivery.installation_id, 123)
195          self.assertEqual(delivery.repository_id, 456)
196          self.assertEqual(delivery.url, "https://www.example-webhook.com")
197          self.assertIsInstance(delivery.request, github.HookDelivery.HookDeliveryRequest)
198          self.assertEqual(delivery.request.headers, {"content-type": "application/json"})
199          self.assertEqual(delivery.request.payload, {"action": "opened"})
200          self.assertIsInstance(delivery.response, github.HookDelivery.HookDeliveryResponse)
201          self.assertEqual(delivery.response.headers, {"content-type": "text/html;charset=utf-8"})
202          self.assertEqual(delivery.response.payload, "ok")
203  
204      def testGetHookDeliveries(self):
205          deliveries = list(self.org.get_hook_deliveries(257993))
206          self.assertEqual(len(deliveries), 1)
207          self.assertEqual(deliveries[0].id, 12345)
208          self.assertEqual(deliveries[0].guid, "abcde-12345")
209          self.assertEqual(
210              deliveries[0].delivered_at,
211              datetime(2012, 5, 27, 6, 0, 32, tzinfo=timezone.utc),
212          )
213          self.assertEqual(deliveries[0].redelivery, False)
214          self.assertEqual(deliveries[0].duration, 0.27)
215          self.assertEqual(deliveries[0].status, "OK")
216          self.assertEqual(deliveries[0].status_code, 200)
217          self.assertEqual(deliveries[0].event, "issues")
218          self.assertEqual(deliveries[0].action, "opened")
219          self.assertEqual(deliveries[0].installation_id, 123)
220          self.assertEqual(deliveries[0].repository_id, 456)
221          self.assertEqual(deliveries[0].url, "https://www.example-webhook.com")
222  
223      def testGetIssues(self):
224          self.assertListKeyEqual(self.org.get_issues(), lambda i: i.id, [])
225  
226      def testGetIssuesWithAllArguments(self):
227          requestedByUser = self.g.get_user().get_repo("PyGithub").get_label("Requested by user")
228          issues = self.org.get_issues(
229              "assigned",
230              "closed",
231              [requestedByUser],
232              "comments",
233              "asc",
234              datetime(2012, 5, 28, 23, 0, 0, tzinfo=timezone.utc),
235          )
236          self.assertListKeyEqual(issues, lambda i: i.id, [])
237  
238      def testGetMembers(self):
239          self.assertListKeyEqual(self.org.get_members(), lambda u: u.login, ["cjuniet", "jacquev6", "Lyloa"])
240  
241      def testGetOutsideCollaborators(self):
242          self.assertListKeyEqual(self.org.get_outside_collaborators(), lambda u: u.login, ["octocat"])
243  
244      def testOutsideCollaborators(self):
245          octocat = self.g.get_user("octocat")
246          self.org.convert_to_outside_collaborator(octocat)
247          self.assertListKeyEqual(self.org.get_outside_collaborators(), lambda u: u.login, ["octocat"])
248          self.org.remove_outside_collaborator(octocat)
249          self.assertEqual(list(self.org.get_outside_collaborators()), [])
250  
251      def testMembers(self):
252          lyloa = self.g.get_user("Lyloa")
253          self.assertTrue(self.org.has_in_members(lyloa))
254          self.org.remove_from_members(lyloa)
255          self.assertFalse(self.org.has_in_members(lyloa))
256  
257      def testGetRepos(self):
258          repos = self.org.get_repos()
259          self.assertListKeyEqual(repos, lambda r: r.name, ["FatherBeaver", "TestPyGithub"])
260          self.assertListKeyEqual(repos, lambda r: r.has_pages, [True, False])
261          self.assertListKeyEqual(repos, lambda r: r.has_wiki, [True, True])
262  
263      def testGetReposSorted(self):
264          repos = self.org.get_repos(sort="updated", direction="desc")
265          self.assertListKeyEqual(
266              repos,
267              lambda r: r.name,
268              ["TestPyGithub", "FatherBeaver"],
269          )
270          self.assertListKeyEqual(
271              repos,
272              lambda r: r.has_pages,
273              [False, True],
274          )
275  
276      def testGetReposWithType(self):
277          repos = self.org.get_repos("public")
278          self.assertListKeyEqual(repos, lambda r: r.name, ["FatherBeaver", "PyGithub"])
279          self.assertListKeyEqual(repos, lambda r: r.has_pages, [True, True])
280  
281      def testGetEvents(self):
282          self.assertListKeyEqual(
283              self.org.get_events(),
284              lambda e: e.type,
285              [
286                  "CreateEvent",
287                  "CreateEvent",
288                  "PushEvent",
289                  "PushEvent",
290                  "DeleteEvent",
291                  "DeleteEvent",
292                  "PushEvent",
293                  "PushEvent",
294                  "DeleteEvent",
295                  "DeleteEvent",
296                  "PushEvent",
297                  "PushEvent",
298                  "PushEvent",
299                  "CreateEvent",
300                  "CreateEvent",
301                  "CreateEvent",
302                  "CreateEvent",
303                  "CreateEvent",
304                  "PushEvent",
305                  "PushEvent",
306                  "PushEvent",
307                  "PushEvent",
308                  "PushEvent",
309                  "PushEvent",
310                  "ForkEvent",
311                  "CreateEvent",
312              ],
313          )
314  
315      def testGetTeams(self):
316          self.assertListKeyEqual(self.org.get_teams(), lambda t: t.name, ["Members", "Owners"])
317  
318      def testGetTeamBySlug(self):
319          team = self.org.get_team_by_slug("Members")
320          self.assertEqual(team.id, 141496)
321  
322      def testCreateHookWithMinimalParameters(self):
323          hook = self.org.create_hook("web", {"url": "http://foobar.com"})
324          self.assertEqual(hook.id, 257967)
325  
326      def testCreateHookWithAllParameters(self):
327          hook = self.org.create_hook("web", {"url": "http://foobar.com"}, ["fork"], False)
328          self.assertTrue(hook.active)
329          self.assertEqual(hook.id, 257993)
330  
331      def testCreateRepoWithMinimalArguments(self):
332          repo = self.org.create_repo(name="TestPyGithub")
333          self.assertEqual(repo.url, "https://api.github.com/repos/BeaverSoftware/TestPyGithub")
334          self.assertTrue(repo.has_wiki)
335          self.assertTrue(repo.has_pages)
336  
337      def testCreateRepoWithAllArguments(self):
338          team = self.org.get_team(141496)
339          repo = self.org.create_repo(
340              name="TestPyGithub2",
341              description="Repo created by PyGithub",
342              homepage="http://foobar.com",
343              private=False,
344              visibility="public",
345              has_issues=False,
346              has_projects=False,
347              has_wiki=False,
348              has_downloads=False,
349              team_id=team.id,
350              allow_update_branch=True,
351              allow_squash_merge=False,
352              allow_merge_commit=False,
353              allow_rebase_merge=True,
354              delete_branch_on_merge=False,
355          )
356          self.assertEqual(repo.url, "https://api.github.com/repos/BeaverSoftware/TestPyGithub2")
357          self.assertTrue(repo.allow_update_branch)
358          self.assertFalse(repo.has_wiki)
359          self.assertFalse(repo.has_pages)
360  
361      def testCreateRepositoryWithAutoInit(self):
362          repo = self.org.create_repo(name="TestPyGithub", auto_init=True, gitignore_template="Python")
363          self.assertEqual(repo.url, "https://api.github.com/repos/BeaverSoftware/TestPyGithub")
364          self.assertTrue(repo.has_pages)
365          self.assertTrue(repo.has_wiki)
366  
367      def testCreateFork(self):
368          pygithub = self.g.get_user("jacquev6").get_repo("PyGithub")
369          repo = self.org.create_fork(pygithub)
370          self.assertEqual(repo.url, "https://api.github.com/repos/BeaverSoftware/PyGithub")
371          self.assertFalse(repo.has_wiki)
372          self.assertFalse(repo.has_pages)
373  
374      def testCreateRepoFromTemplate(self):
375          template_repo = self.g.get_repo("actions/hello-world-docker-action")
376  
377          repo = self.org.create_repo_from_template("hello-world-docker-action-new", template_repo)
378          self.assertEqual(
379              repo.url,
380              "https://api.github.com/repos/BeaverSoftware/hello-world-docker-action-new",
381          )
382          self.assertFalse(repo.is_template)
383  
384      def testCreateRepoFromTemplateWithAllArguments(self):
385          template_repo = self.g.get_repo("actions/hello-world-docker-action")
386  
387          description = "My repo from template"
388          private = True
389          repo = self.org.create_repo_from_template(
390              "hello-world-docker-action-new",
391              template_repo,
392              description=description,
393              private=private,
394          )
395          self.assertEqual(repo.description, description)
396          self.assertTrue(repo.private)
397  
398      @mock.patch("github.PublicKey.encrypt")
399      def testCreateSecret(self, encrypt):
400          # encrypt returns a non-deterministic value, we need to mock it so the replay data matches
401          encrypt.return_value = "M+5Fm/BqTfB90h3nC7F3BoZuu3nXs+/KtpXwxm9gG211tbRo0F5UiN0OIfYT83CKcx9oKES9Va4E96/b"
402          secret = self.org.create_secret("secret-name", "secret-value", "all")
403          self.assertIsNotNone(secret)
404  
405      @mock.patch("github.PublicKey.encrypt")
406      def testCreateSecretSelected(self, encrypt):
407          repos = [self.org.get_repo("TestPyGithub"), self.org.get_repo("FatherBeaver")]
408          # encrypt returns a non-deterministic value, we need to mock it so the replay data matches
409          encrypt.return_value = "M+5Fm/BqTfB90h3nC7F3BoZuu3nXs+/KtpXwxm9gG211tbRo0F5UiN0OIfYT83CKcx9oKES9Va4E96/b"
410          secret = self.org.create_secret("secret-name", "secret-value", "selected", repos)
411          self.assertIsNotNone(secret)
412          self.assertEqual(secret.visibility, "selected")
413          self.assertEqual(list(secret.selected_repositories), repos)
414  
415      def testGetSecret(self):
416          repos = [self.org.get_repo("TestPyGithub"), self.org.get_repo("FatherBeaver")]
417          secret = self.org.get_secret("secret-name")
418          self.assertEqual(secret.name, "secret-name")
419          self.assertEqual(secret.created_at, datetime(2019, 8, 10, 14, 59, 22, tzinfo=timezone.utc))
420          self.assertEqual(secret.updated_at, datetime(2020, 1, 10, 14, 59, 22, tzinfo=timezone.utc))
421          self.assertEqual(secret.visibility, "selected")
422          self.assertEqual(list(secret.selected_repositories), repos)
423          self.assertEqual(secret.url, "https://api.github.com/orgs/BeaverSoftware/actions/secrets/secret-name")
424  
425      def testGetSecrets(self):
426          secrets = self.org.get_secrets()
427          self.assertEqual(len(list(secrets)), 1)
428  
429      def testInviteUserWithNeither(self):
430          with self.assertRaises(AssertionError) as raisedexp:
431              self.org.invite_user()
432          self.assertEqual("specify only one of email or user", str(raisedexp.exception))
433  
434      def testInviteUserWithBoth(self):
435          jacquev6 = self.g.get_user("jacquev6")
436          with self.assertRaises(AssertionError) as raisedexp:
437              self.org.invite_user(email="foo", user=jacquev6)
438          self.assertEqual("specify only one of email or user", str(raisedexp.exception))
439  
440      def testInviteUserByName(self):
441          jacquev6 = self.g.get_user("jacquev6")
442          self.org.invite_user(user=jacquev6)
443  
444      def testInviteUserByEmail(self):
445          self.org.invite_user(email="foo@example.com")
446  
447      def testInviteUserWithRoleAndTeam(self):
448          team = self.org.create_team("Team created by PyGithub")
449          self.org.invite_user(email="foo@example.com", role="billing_manager", teams=[team])
450  
451      def testInviteUserAsNonOwner(self):
452          with self.assertRaises(github.GithubException) as raisedexp:
453              self.org.invite_user(email="bar@example.com")
454          self.assertEqual(raisedexp.exception.status, 403)
455          self.assertEqual(
456              raisedexp.exception.data,
457              {
458                  "documentation_url": "https://developer.github.com/v3/orgs/members/#create-organization-invitation",
459                  "message": "You must be an admin to create an invitation to an organization.",
460              },
461          )
462  
463      def testCreateMigration(self):
464          self.org = self.g.get_organization("sample-test-organisation")
465          self.assertTrue(isinstance(self.org.create_migration(["sample-repo"]), github.Migration.Migration))
466  
467      def testGetMigrations(self):
468          self.org = self.g.get_organization("sample-test-organisation")
469          self.assertEqual(self.org.get_migrations().totalCount, 2)
470  
471      def testGetInstallations(self):
472          installations = self.org.get_installations()
473          self.assertEqual(installations[0].id, 123456)
474          self.assertEqual(installations[0].app_id, 10101)
475          self.assertEqual(installations[0].target_id, 3344556)
476          self.assertEqual(installations[0].target_type, "User")
477          self.assertEqual(installations.totalCount, 1)
478  
479      def testCreateVariable(self):
480          variable = self.org.create_variable("variable-name", "variable-value", "all")
481          self.assertIsNotNone(variable)
482  
483      def testCreateVariableSelected(self):
484          repos = [self.org.get_repo("TestPyGithub"), self.org.get_repo("FatherBeaver")]
485          variable = self.org.create_variable("variable-name", "variable-value", "selected", repos)
486          self.assertIsNotNone(variable)
487          self.assertEqual(list(variable.selected_repositories), repos)
488  
489      def testGetVariable(self):
490          repos = [self.org.get_repo("TestPyGithub"), self.org.get_repo("FatherBeaver")]
491          variable = self.org.get_variable("variable-name")
492          self.assertEqual(variable.name, "variable-name")
493          self.assertEqual(variable.created_at, datetime(2019, 8, 10, 14, 59, 22, tzinfo=timezone.utc))
494          self.assertEqual(variable.updated_at, datetime(2020, 1, 10, 14, 59, 22, tzinfo=timezone.utc))
495          self.assertEqual(variable.visibility, "selected")
496          self.assertEqual(list(variable.selected_repositories), repos)
497          self.assertEqual(variable.url, "https://api.github.com/orgs/BeaverSoftware/actions/variables/variable-name")
498  
499      def testGetVariables(self):
500          variables = self.org.get_variables()
501          self.assertEqual(len(list(variables)), 1)