functions/github/Assert-GitHubTeamRepoAccess.Tests.ps1

$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
. "$here\$sut"

. "$here\Invoke-GitHubRestMethod.ps1"

Describe "Assert-GitHubTeamRepoAccess" {

    $mockTeam = @{ organization = @{ login = "TestOrg"}; slug = "TestTeam" }

    Context "Add team access" {

        Mock _removeRepoPermissions {}
        Mock _updateRepoPermissions {}
        Mock _invokeUpdateTeamRepoPermissions {}

        It "should add a single missing repo to a team's access" {
            Mock _getGitHubTeamRepos {
                @(
                    @{ name = "RepoA"; permissions = @{pull=$true; triage=$false; push=$true; maintain=$false; admin=$false}; owner = "TestOrg" }
                )
            }

            $res = Assert-GitHubTeamRepoAccess -Team $mockTeam `
                                               -Repositories @("RepoA", "RepoB") `
                                               -RepoAccess "push"

            Assert-MockCalled _updateRepoPermissions -Exactly 0
            Assert-MockCalled _removeRepoPermissions -Exactly 0
            Assert-MockCalled _invokeUpdateTeamRepoPermissions -Exactly 1

            $res.repos_permission_added.Count | Should -Be 1
            $res.repos_permission_added | Should -Be @("TestOrg/RepoB")
            $res.repos_permission_updated.Count | Should -Be 0
            $res.repos_permission_removed.Count | Should -Be 0
        }

        It "should add multiple missing repos to a team's access" {
            Mock _getGitHubTeamRepos { @() }
            
            $res = Assert-GitHubTeamRepoAccess -Team $mockTeam `
                                               -Repositories @("RepoA", "RepoB") `
                                               -RepoAccess "push"

            Assert-MockCalled _updateRepoPermissions -Exactly 0
            Assert-MockCalled _removeRepoPermissions -Exactly 0
            Assert-MockCalled _invokeUpdateTeamRepoPermissions -Exactly 3   # it should be 2, but the 1 from the previous test is included in the count

            $res.repos_permission_added.Count | Should -Be 2
            $res.repos_permission_added | Should -Be @("TestOrg/RepoA", "TestOrg/RepoB")
            $res.repos_permission_updated.Count | Should -Be 0
            $res.repos_permission_removed.Count | Should -Be 0
        }

        It "should synchronise a teams' missing repo access" {
            # Simulate a team already have the correct access to a repo
            Mock _getGitHubTeamRepos { @( @{name = "RepoC"; permissions = @{pull=$true; triage=$false; push=$true; maintain=$false; admin=$false}} ) }
            
            $res = Assert-GitHubTeamRepoAccess -Team $mockTeam `
                                               -Repositories @("RepoC", "RepoD", "RepoE") `
                                               -RepoAccess "push"

            Assert-MockCalled _updateRepoPermissions -Exactly 0
            Assert-MockCalled _removeRepoPermissions -Exactly 0
            Assert-MockCalled _invokeUpdateTeamRepoPermissions -Exactly 5   # it should be 2, but the 3 from the previous tests are included in the count

            $res.repos_permission_added.Count | Should -Be 2
            $res.repos_permission_added | Should -Be @("TestOrg/RepoD", "TestOrg/RepoE")
            $res.repos_permission_updated.Count | Should -Be 0
            $res.repos_permission_removed.Count | Should -Be 0
        }
    }

    Context "Remove team access" {
        Mock _getGitHubTeamRepos {
            @(
                @{
                    name = "RepoA"
                    permissions = @{pull=$true; triage=$false; push=$true; maintain=$false; admin=$false}
                    owner = "TestOrg"
                }
                @{
                    name = "RepoB"
                    permissions = @{pull=$true; triage=$false; push=$true; maintain=$false; admin=$false}
                    owner = "TestOrg"
                }
                @{
                    name = "RepoC"
                    permissions = @{pull=$true; triage=$false; push=$true; maintain=$false; admin=$false}
                    owner = "TestOrg"
                }
            )
        }
        Mock _removeRepoPermissions {}
        Mock _addRepoPermissions {}
        Mock _updateRepoPermissions {}

        It "should remove a team's acceess to a single repo where explicit access is not specified" {
            $res = Assert-GitHubTeamRepoAccess -Team $mockTeam `
                                               -Repositories @("RepoA", "RepoC") `
                                               -RepoAccess "push"

            Assert-MockCalled _addRepoPermissions -Exactly 0
            Assert-MockCalled _removeRepoPermissions -Exactly 1
            Assert-MockCalled _updateRepoPermissions -Exactly 0

            $res.repos_permission_updated.Count | Should -Be 0
            $res.repos_permission_added.Count | Should -Be 0
            $res.repos_permission_removed.Count | Should -Be 1
            $res.repos_permission_removed[0] | Should -Be "TestOrg/RepoB"
        }

        It "should remove a team's acceess to multiple repos where explicit access is not specified" {
            $res = Assert-GitHubTeamRepoAccess -Team $mockTeam `
                                               -Repositories @("RepoA") `
                                               -RepoAccess "push"

            Assert-MockCalled _addRepoPermissions -Exactly 0
            Assert-MockCalled _removeRepoPermissions -Exactly 3     # it should be 2, but the 1 from the previous test is included in the count
            Assert-MockCalled _updateRepoPermissions -Exactly 0

            $res.repos_permission_updated.Count | Should -Be 0
            $res.repos_permission_added.Count | Should -Be 0
            $res.repos_permission_removed.Count | Should -Be 2
            $res.repos_permission_removed | Should -Be @("TestOrg/RepoB", "TestOrg/RepoC")
        }
    }

    Context "Update team access" {

        Mock _getGitHubTeamRepos {
            @(
                # Repo with the correct permissions
                @{
                    name = "RepoA"
                    permissions = @{pull=$true; triage=$false; push=$true; maintain=$false; admin=$false}
                    owner = "TestOrg"
                }
                # Repo with the incorrect permissions
                @{
                    name = "RepoB"
                    permissions = @{pull=$true; triage=$false; push=$false; maintain=$false; admin=$false}
                    owner = "TestOrg"
                }
            )
        }
        Mock _invokeUpdateTeamRepoPermissions {}
        Mock _addRepoPermissions {}
        Mock _removeRepoPermissions {}

        $res = Assert-GitHubTeamRepoAccess -Team $mockTeam `
                                           -Repositories @("RepoA", "RepoB") `
                                           -RepoAccess "push"

        It "should update an existing team's permissions when they change" {
            Assert-MockCalled _addRepoPermissions -Exactly 0
            Assert-MockCalled _removeRepoPermissions -Exactly 0
            Assert-MockCalled _invokeUpdateTeamRepoPermissions -Exactly 1

            $res.repos_permission_updated.Count | Should -Be 1
            $res.repos_permission_updated[0] | Should -Be "TestOrg/RepoB"
            $res.repos_permission_added.Count | Should -Be 0
            $res.repos_permission_removed.Count | Should -Be 0
        }
    }

    Context "Full synchronisation scenarios" {

        Mock _getGitHubTeamRepos {
            @(
                @{
                    name = "RepoA"
                    permissions = @{pull=$true; triage=$false; push=$true; maintain=$false; admin=$false}
                    owner = "TestOrg"
                }
                @{
                    name = "RepoC"
                    permissions = @{pull=$true; triage=$false; push=$false; maintain=$false; admin=$false}
                    owner = "TestOrg"
                }
                # A repo that should be removed, even though it's access permissions are incorrect
                @{
                    name = "RepoD"
                    permissions = @{pull=$true; triage=$false; push=$true; maintain=$false; admin=$true}
                    owner = "TestOrg"
                }
            )
        }
        Mock _removeRepoPermissions {}
        Mock _addRepoPermissions {}
        Mock _updateRepoPermissions {}

        It "should correctly add, remove and update a team's repo permissions" {

            $res = Assert-GitHubTeamRepoAccess -Team $mockTeam `
                                               -Repositories @("RepoA", "RepoB", "RepoC") `
                                               -RepoAccess "push"

            $res.repos_permission_added.Count | Should -Be 1
            $res.repos_permission_added[0] | Should -Be "TestOrg/RepoB"
            $res.repos_permission_removed.Count | Should -Be 1
            $res.repos_permission_removed[0] | Should -Be "TestOrg/RepoD"
            $res.repos_permission_updated.Count | Should -Be 1
            $res.repos_permission_updated[0] | Should -Be "TestOrg/RepoC"
        }
    }
}