diff --git a/plinth/modules/gitweb/tests/gitweb.feature b/plinth/modules/gitweb/tests/gitweb.feature deleted file mode 100644 index 15685d5d4..000000000 --- a/plinth/modules/gitweb/tests/gitweb.feature +++ /dev/null @@ -1,113 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later - -@apps @gitweb @sso -Feature: gitweb Simple Git Hosting - Git web interface. - -Background: - Given I'm a logged in user - And the gitweb application is installed - -Scenario: Enable gitweb application - Given the gitweb application is disabled - When I enable the gitweb application - Then the gitweb site should be available - -Scenario: Create public repository - Given the gitweb application is enabled - And a public repository that doesn't exist - When I create the repository - Then the repository should be listed as a public - And the repository should be listed on gitweb - -Scenario: Create private repository - Given the gitweb application is enabled - And a private repository that doesn't exist - When I create the repository - Then the repository should be listed as a private - And the repository should be listed on gitweb - -@backups -Scenario: Backup and restore gitweb - Given the gitweb application is enabled - And a repository - When I create a backup of the gitweb app data with name test_gitweb - And I delete the repository - And I restore the gitweb app data backup with name test_gitweb - Then the repository should be restored - And the gitweb site should be available - -Scenario: Public gitweb site shows only public repositories - Given the gitweb application is enabled - And both public and private repositories exist - When I log out - Then the public repository should be listed on gitweb - And the private repository should not be listed on gitweb - -Scenario: Gitweb is not public if there are only private repositories - Given the gitweb application is enabled - And at least one repository exists - And all repositories are private - When I log out - And I access gitweb application - Then I should be prompted for login - And gitweb app should not be visible on the front page - -Scenario: Edit repository metadata - Given the gitweb application is enabled - And a public repository that doesn't exist - And a repository metadata: - description: Test Description - owner: Test Owner - access: private - When I create the repository - And I set the metadata of the repository - Then the metadata of the repository should be as set - -Scenario: Edit default branch of the repository - Given the gitweb application is enabled - And a repository with the branch branch1 - When I set branch1 as a default branch - Then the gitweb site should show branch1 as a default repo branch - -Scenario: Access public repository with git client - Given the gitweb application is enabled - And a public repository - When using a git client - Then the repository should be publicly readable - And the repository should not be publicly writable - And the repository should be privately writable - -Scenario: Access private repository with git client - Given the gitweb application is enabled - And a private repository - When using a git client - Then the repository should not be publicly readable - And the repository should not be publicly writable - And the repository should be privately readable - And the repository should be privately writable - -Scenario: User of git-access group can access gitweb site - Given the gitweb application is enabled - And all repositories are private - And the user gituser in group git-access exists - When I'm logged in as the user gituser - Then the gitweb site should be available - -Scenario: User not of git-access group can't access gitweb site - Given the gitweb application is enabled - And all repositories are private - And the user nogroupuser exists - When I'm logged in as the user nogroupuser - Then the gitweb site should not be available - -Scenario: Delete repository - Given the gitweb application is enabled - And a repository - When I delete the repository - Then the repository should not be listed - -Scenario: Disable gitweb application - Given the gitweb application is enabled - When I disable the gitweb application - Then the gitweb site should not be available diff --git a/plinth/modules/gitweb/tests/test_functional.py b/plinth/modules/gitweb/tests/test_functional.py index 80bb1e76d..207f4aa4c 100644 --- a/plinth/modules/gitweb/tests/test_functional.py +++ b/plinth/modules/gitweb/tests/test_functional.py @@ -2,185 +2,136 @@ """ Functional, browser based tests for gitweb app. """ - import contextlib import os import shutil import subprocess import tempfile -from pytest_bdd import given, parsers, scenarios, then, when +import pytest from plinth.tests import functional -scenarios('gitweb.feature') +pytestmark = [pytest.mark.apps, pytest.mark.gitweb] _default_url = functional.config['DEFAULT']['url'] -@given('a public repository') -@given('a repository') -@given('at least one repository exists') -def gitweb_repo(session_browser): - _create_repo(session_browser, 'Test-repo', 'public', True) +@pytest.fixture(scope='module', autouse=True) +def fixture_background(session_browser): + """Login and install the app.""" + functional.login(session_browser) + functional.install(session_browser, 'gitweb') + functional.app_enable(session_browser, 'gitweb') + yield + functional.login(session_browser) + functional.app_disable(session_browser, 'gitweb') -@given('a private repository') -def gitweb_private_repo(session_browser): - _create_repo(session_browser, 'Test-repo', 'private', True) +@pytest.fixture(autouse=True) +def fixture_login(session_browser): + """Login fixture.""" + functional.login(session_browser) + functional.app_enable(session_browser, 'gitweb') + yield -@given(parsers.parse('a repository with the branch {branch:w}')) -def _create_repo_with_branch(session_browser, branch): - _delete_repo(session_browser, 'Test-repo', ignore_missing=True) - _create_repo(session_browser, 'Test-repo', 'public') - _create_branch('Test-repo', branch) - - -@given('both public and private repositories exist') -def gitweb_public_and_private_repo(session_browser): - _create_repo(session_browser, 'Test-repo', 'public', True) - _create_repo(session_browser, 'Test-repo2', 'private', True) - - -@given(parsers.parse("a {access:w} repository that doesn't exist")) -def gitweb_nonexistent_repo(session_browser, access): - _delete_repo(session_browser, 'Test-repo', ignore_missing=True) - return dict(access=access) - - -@given('all repositories are private') -def gitweb_all_repositories_private(session_browser): +def test_all_repos_private(session_browser): + """Test repo accessability when all repos are private.""" + _create_repo(session_browser, 'Test-repo', 'private', ok_if_exists=True) _set_all_repos_private(session_browser) + if not functional.user_exists(session_browser, 'gitweb_user'): + functional.create_user(session_browser, 'gitweb_user', + groups=['git-access']) + if not functional.user_exists(session_browser, 'nogroupuser'): + functional.create_user(session_browser, 'nogroupuser', groups=[]) + + functional.login_with_account(session_browser, functional.base_url, + 'gitweb_user') + assert functional.is_available(session_browser, 'gitweb') + assert len(functional.find_on_front_page(session_browser, 'gitweb')) == 1 + + functional.login_with_account(session_browser, functional.base_url, + 'nogroupuser') + assert not functional.is_available(session_browser, 'gitweb') + assert len(functional.find_on_front_page(session_browser, 'gitweb')) == 0 + + functional.logout(session_browser) + functional.access_url(session_browser, 'gitweb') + assert functional.is_login_prompt(session_browser) + assert len(functional.find_on_front_page(session_browser, 'gitweb')) == 0 -@given(parsers.parse('a repository metadata:\n{metadata}'), - target_fixture='gitweb_repo_metadata') -def gitweb_repo_metadata(session_browser, metadata): - metadata_dict = {} - for item in metadata.split('\n'): - item = item.split(': ') - metadata_dict[item[0]] = item[1] - return metadata_dict - - -@when('I create the repository') -def gitweb_create_repo(session_browser, access): - _create_repo(session_browser, 'Test-repo', access) - - -@when('I delete the repository') -def gitweb_delete_repo(session_browser): +@pytest.mark.backups +def test_backup(session_browser): + """Test backing up and restoring.""" + _create_repo(session_browser, 'Test-repo', ok_if_exists=True) + functional.backup_create(session_browser, 'gitweb', 'test_gitweb') _delete_repo(session_browser, 'Test-repo') + functional.backup_restore(session_browser, 'gitweb', 'test_gitweb') + assert _repo_exists(session_browser, 'Test-repo') + assert functional.is_available(session_browser, 'gitweb') -@when(parsers.parse('I set {branch:w} as a default branch')) -def gitweb_set_default_branch(session_browser, branch): - _set_default_branch(session_browser, 'Test-repo', branch) +@pytest.mark.parametrize('access', ['public', 'private']) +@pytest.mark.parametrize('repo_name', ['Test-repo', 'Test-repo.git']) +def test_create_delete_repo(session_browser, access, repo_name): + """Test creating and deleting a repo and accessing with a git client.""" + _delete_repo(session_browser, repo_name, ignore_missing=True) + _create_repo(session_browser, repo_name, access) + + assert _repo_exists(session_browser, repo_name, access) + assert _site_repo_exists(session_browser, repo_name) + + if access == "public": + assert _repo_is_readable(repo_name) + else: + assert not _repo_is_readable(repo_name) + + assert not _repo_is_writable(repo_name) + assert _repo_is_readable(repo_name, with_auth=True) + assert _repo_is_writable(repo_name, with_auth=True) + + _delete_repo(session_browser, repo_name) + assert not _repo_exists(session_browser, repo_name) -@when('I set the metadata of the repository') -def gitweb_edit_repo_metadata(session_browser, gitweb_repo_metadata): - _edit_repo_metadata(session_browser, 'Test-repo', gitweb_repo_metadata) +def test_both_private_and_public_repo_exist(session_browser): + """Tests when both private and public repo exist.""" + _create_repo(session_browser, 'Test-repo', 'public', True) + _create_repo(session_browser, 'Test-repo-private', 'private', True) - -@when('using a git client') -def gitweb_using_git_client(): - pass - - -@then( - parsers.parse( - 'the gitweb site should show {branch:w} as a default repo branch')) -def gitweb_site_check_default_repo_branch(session_browser, branch): - assert _get_gitweb_site_default_repo_branch(session_browser, - 'Test-repo') == branch - - -@then('the repository should be restored') -@then('the repository should be listed as a public') -def gitweb_repo_should_exists(session_browser): - assert _repo_exists(session_browser, 'Test-repo', access='public') - - -@then('the repository should be listed as a private') -def gitweb_private_repo_should_exists(session_browser): - assert _repo_exists(session_browser, 'Test-repo', 'private') - - -@then('the repository should not be listed') -def gitweb_repo_should_not_exist(session_browser): - assert not _repo_exists(session_browser, 'Test-repo') - - -@then('the public repository should be listed on gitweb') -@then('the repository should be listed on gitweb') -def gitweb_repo_should_exist_on_gitweb(session_browser): + functional.logout(session_browser) assert _site_repo_exists(session_browser, 'Test-repo') + assert not _site_repo_exists(session_browser, 'Test-repo-private') -@then('the private repository should not be listed on gitweb') -def gitweb_private_repo_should_exists_on_gitweb(session_browser): - assert not _site_repo_exists(session_browser, 'Test-repo2') +def test_edit_repo_metadata(session_browser): + """Test edit repo metadata.""" + _create_repo(session_browser, 'Test-repo2', 'public', ok_if_exists=True) + _delete_repo(session_browser, 'Test-repo', ignore_missing=True) + repo_metadata = { + 'name': 'Test-repo', + 'description': 'Test Description', + 'owner': 'Test Owner', + 'access': 'private', + } + _edit_repo_metadata(session_browser, 'Test-repo2', repo_metadata) + assert _get_repo_metadata(session_browser, "Test-repo") == repo_metadata + + _create_branch('Test-repo', 'branch1') + _set_default_branch(session_browser, 'Test-repo', 'branch1') + assert _get_gitweb_site_default_repo_branch(session_browser, + 'Test-repo') == 'branch1' -@then('the metadata of the repository should be as set') -def gitweb_repo_metadata_should_match(session_browser, gitweb_repo_metadata): - actual_metadata = _get_repo_metadata(session_browser, 'Test-repo') - assert all(item in actual_metadata.items() - for item in gitweb_repo_metadata.items()) +def test_enable_disable(session_browser): + """Test enabling and disabling the app.""" + functional.app_disable(session_browser, 'gitweb') + assert not functional.is_available(session_browser, 'gitweb') - -@then('the repository should be publicly readable') -def gitweb_repo_publicly_readable(): - assert _repo_is_readable('Test-repo') - assert _repo_is_readable('Test-repo', url_git_extension=True) - - -@then('the repository should not be publicly readable') -def gitweb_repo_not_publicly_readable(): - assert not _repo_is_readable('Test-repo') - assert not _repo_is_readable('Test-repo', url_git_extension=True) - - -@then('the repository should not be publicly writable') -def gitweb_repo_not_publicly_writable(): - assert not _repo_is_writable('Test-repo') - assert not _repo_is_writable('Test-repo', url_git_extension=True) - - -@then('the repository should be privately readable') -def gitweb_repo_privately_readable(): - assert _repo_is_readable('Test-repo', with_auth=True) - assert _repo_is_readable('Test-repo', with_auth=True, - url_git_extension=True) - - -@then('the repository should be privately writable') -def gitweb_repo_privately_writable(): - assert _repo_is_writable('Test-repo', with_auth=True) - assert _repo_is_writable('Test-repo', with_auth=True, - url_git_extension=True) - - -def _create_branch(repo, branch): - """Create a branch on the remote repository.""" - repo_url = _get_repo_url(repo, with_auth=True) - - with _gitweb_temp_directory() as temp_directory: - repo_path = os.path.join(temp_directory, repo) - - _create_local_repo(repo_path) - - add_branch_commands = [['git', 'checkout', '-q', '-b', branch], - [ - 'git', '-c', 'user.name=Tester', '-c', - 'user.email=tester', 'commit', '-q', - '--allow-empty', '-m', 'test_branch1' - ], - ['git', 'push', '-q', '-f', repo_url, branch]] - for command in add_branch_commands: - subprocess.check_call(command, cwd=repo_path) + functional.app_enable(session_browser, 'gitweb') + assert functional.is_available(session_browser, 'gitweb') def _create_local_repo(path): @@ -200,7 +151,7 @@ def _create_repo(browser, repo, access=None, ok_if_exists=False): """Create repository.""" if not _repo_exists(browser, repo, access): _delete_repo(browser, repo, ignore_missing=True) - browser.find_link_by_href('/plinth/apps/gitweb/create/').first.click() + browser.links.find_by_href('/plinth/apps/gitweb/create/').first.click() browser.find_by_id('id_gitweb-name').fill(repo) if access == 'private': browser.find_by_id('id_gitweb-is_private').check() @@ -211,10 +162,32 @@ def _create_repo(browser, repo, access=None, ok_if_exists=False): assert False, 'Repo already exists.' +def _create_branch(repo, branch): + """Add a branch to the repo.""" + repo_url = _get_repo_url(repo, with_auth=True) + + with _gitweb_temp_directory() as temp_directory: + repo_path = os.path.join(temp_directory, repo) + + _create_local_repo(repo_path) + + add_branch_commands = [['git', 'checkout', '-q', '-b', branch], + [ + 'git', '-c', 'user.name=Tester', '-c', + 'user.email=tester', 'commit', '-q', + '--allow-empty', '-m', 'test_branch1' + ], + ['git', 'push', '-q', '-f', repo_url, branch]] + for command in add_branch_commands: + subprocess.check_call(command, cwd=repo_path) + + def _delete_repo(browser, repo, ignore_missing=False): """Delete repository.""" functional.nav_to_module(browser, 'gitweb') - delete_link = browser.find_link_by_href( + if repo.endswith('.git'): + repo = repo[:-4] + delete_link = browser.links.find_by_href( '/plinth/apps/gitweb/{}/delete/'.format(repo)) if delete_link or not ignore_missing: delete_link.first.click() @@ -224,20 +197,15 @@ def _delete_repo(browser, repo, ignore_missing=False): def _edit_repo_metadata(browser, repo, metadata): """Set repository metadata.""" functional.nav_to_module(browser, 'gitweb') - browser.find_link_by_href( + browser.links.find_by_href( '/plinth/apps/gitweb/{}/edit/'.format(repo)).first.click() - if 'name' in metadata: - browser.find_by_id('id_gitweb-name').fill(metadata['name']) - if 'description' in metadata: - browser.find_by_id('id_gitweb-description').fill( - metadata['description']) - if 'owner' in metadata: - browser.find_by_id('id_gitweb-owner').fill(metadata['owner']) - if 'access' in metadata: - if metadata['access'] == 'private': - browser.find_by_id('id_gitweb-is_private').check() - else: - browser.find_by_id('id_gitweb-is_private').uncheck() + browser.find_by_id('id_gitweb-name').fill(metadata['name']) + browser.find_by_id('id_gitweb-description').fill(metadata['description']) + browser.find_by_id('id_gitweb-owner').fill(metadata['owner']) + if metadata['access'] == 'private': + browser.find_by_id('id_gitweb-is_private').check() + else: + browser.find_by_id('id_gitweb-is_private').uncheck() functional.submit(browser) @@ -251,7 +219,7 @@ def _get_gitweb_site_default_repo_branch(browser, repo): def _get_repo_metadata(browser, repo): """Get repository metadata.""" functional.nav_to_module(browser, 'gitweb') - browser.find_link_by_href( + browser.links.find_by_href( '/plinth/apps/gitweb/{}/edit/'.format(repo)).first.click() metadata = {} for item in ['name', 'description', 'owner']: @@ -278,6 +246,22 @@ def _get_repo_url(repo, with_auth): scheme, functional.config['DEFAULT']['username'], password, url, repo) +def _gitweb_git_command_is_successful(command, cwd): + """Check if a command runs successfully or gives authentication error""" + # Tell OS not to translate command return messages + env = os.environ.copy() + env['LC_ALL'] = 'C' + + process = subprocess.run(command, capture_output=True, cwd=cwd, env=env, + check=False) + if process.returncode != 0: + if 'Authentication failed' in process.stderr.decode(): + return False + print(process.stdout.decode()) + process.check_returncode() # Raise exception + return True + + @contextlib.contextmanager def _gitweb_temp_directory(): """Create temporary directory""" @@ -286,26 +270,12 @@ def _gitweb_temp_directory(): shutil.rmtree(name) -def _gitweb_git_command_is_successful(command, cwd): - """Check if a command runs successfully or gives authentication error""" - # Tell OS not to translate command return messages - env = os.environ.copy() - env['LC_ALL'] = 'C' - - process = subprocess.run(command, capture_output=True, cwd=cwd, env=env) - if process.returncode != 0: - if 'Authentication failed' in process.stderr.decode(): - return False - print(process.stdout.decode()) - # raise exception - process.check_returncode() - return True - - def _repo_exists(browser, repo, access=None): """Check whether the repository exists.""" functional.nav_to_module(browser, 'gitweb') - links_found = browser.find_link_by_href('/gitweb/{}.git'.format(repo)) + if not repo.endswith('.git'): + repo = repo + ".git" + links_found = browser.links.find_by_href('/gitweb/{}'.format(repo)) access_matches = True if links_found and access: parent = links_found.first.find_by_xpath('..').first @@ -317,53 +287,26 @@ def _repo_exists(browser, repo, access=None): return bool(links_found) and access_matches -def _repo_is_readable(repo, with_auth=False, url_git_extension=False): +def _repo_is_readable(repo, with_auth=False): """Check if a git repo is readable with git client.""" url = _get_repo_url(repo, with_auth) - if url_git_extension: - url = url + '.git' git_command = ['git', 'clone', '-c', 'http.sslverify=false', url] with _gitweb_temp_directory() as cwd: return _gitweb_git_command_is_successful(git_command, cwd) -def _repo_is_writable(repo, with_auth=False, url_git_extension=False): +def _repo_is_writable(repo, with_auth=False): """Check if a git repo is writable with git client.""" url = _get_repo_url(repo, with_auth) - if url_git_extension: - url = url + '.git' - with _gitweb_temp_directory() as temp_directory: repo_directory = os.path.join(temp_directory, 'test-project') _create_local_repo(repo_directory) - git_push_command = ['git', 'push', '-qf', url, 'master'] return _gitweb_git_command_is_successful(git_push_command, repo_directory) -def _set_default_branch(browser, repo, branch): - """Set default branch of the repository.""" - functional.nav_to_module(browser, 'gitweb') - browser.find_link_by_href( - '/plinth/apps/gitweb/{}/edit/'.format(repo)).first.click() - browser.find_by_id('id_gitweb-default_branch').select(branch) - functional.submit(browser) - - -def _set_repo_access(browser, repo, access): - """Set repository as public or private.""" - functional.nav_to_module(browser, 'gitweb') - browser.find_link_by_href( - '/plinth/apps/gitweb/{}/edit/'.format(repo)).first.click() - if access == 'private': - browser.find_by_id('id_gitweb-is_private').check() - else: - browser.find_by_id('id_gitweb-is_private').uncheck() - functional.submit(browser) - - def _set_all_repos_private(browser): """Set all repositories private""" functional.nav_to_module(browser, 'gitweb') @@ -376,7 +319,30 @@ def _set_all_repos_private(browser): _set_repo_access(browser, repo, 'private') +def _set_default_branch(browser, repo, branch): + """Set default branch of the repository.""" + functional.nav_to_module(browser, 'gitweb') + browser.links.find_by_href( + '/plinth/apps/gitweb/{}/edit/'.format(repo)).first.click() + browser.find_by_id('id_gitweb-default_branch').select(branch) + functional.submit(browser) + + +def _set_repo_access(browser, repo, access): + """Set repository as public or private.""" + functional.nav_to_module(browser, 'gitweb') + browser.links.find_by_href( + '/plinth/apps/gitweb/{}/edit/'.format(repo)).first.click() + if access == 'private': + browser.find_by_id('id_gitweb-is_private').check() + else: + browser.find_by_id('id_gitweb-is_private').uncheck() + functional.submit(browser) + + def _site_repo_exists(browser, repo): """Check whether the repository exists on Gitweb site.""" browser.visit('{}/gitweb'.format(_default_url)) - return browser.find_by_css('a[href="/gitweb/{0}.git"]'.format(repo)) + if not repo.endswith('.git'): + repo = repo + ".git" + return bool(browser.find_by_css('a[href="/gitweb/{0}"]'.format(repo)))