FreedomBox/plinth/modules/gitweb/tests/test_functional.py
Veiko Aasa 1268898176
gitweb: Fix modifying git repositories when gitweb app is disabled
Fixes #2408.

- When app is disabled, continue to update the app shortcut's login required
property. Otherwise, the value is current after the app is re-enabled.

- When public access is enabled, the gitweb-freedombox-auth.conf configuration
must be disabled. This can be done even if the configuration is already disabled
or does not exist. So, continue doing this.

Signed-off-by: Veiko Aasa <veiko17@disroot.org>
[sunil: Perform enable public access even if app is disabled]
[sunil: Perform shortcut update in all cases even if app is disabled]
Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
2024-02-27 15:06:27 -08:00

344 lines
13 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Functional, browser based tests for gitweb app.
"""
import contextlib
import os
import shutil
import subprocess
import tempfile
import pytest
from plinth.tests import functional
pytestmark = [pytest.mark.apps, pytest.mark.gitweb]
_default_url = functional.config['DEFAULT']['url']
class TestGitwebApp(functional.BaseAppTests):
app_name = 'gitweb'
has_service = False
has_web = True
def test_all_repos_private(self, 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
@pytest.mark.backups
def test_backup_restore(self, 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')
@pytest.mark.parametrize('access', ['public', 'private'])
@pytest.mark.parametrize('repo_name', ['Test-repo', 'Test-repo.git'])
@pytest.mark.parametrize('app_status', ['enabled', 'disabled'])
def test_create_delete_repo(self, session_browser, access, repo_name,
app_status):
"""Test creating and deleting a repo and accessing with a git
client."""
if app_status == "disabled":
functional.app_disable(session_browser, 'gitweb')
_delete_repo(session_browser, repo_name, ignore_missing=True)
_create_repo(session_browser, repo_name, access)
assert _repo_exists(session_browser, repo_name, access)
if app_status == "enabled":
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)
def test_both_private_and_public_repo_exist(self, 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)
functional.logout(session_browser)
assert _site_repo_exists(session_browser, 'Test-repo')
assert not _site_repo_exists(session_browser, 'Test-repo-private')
@pytest.mark.parametrize('app_status', ['enabled', 'disabled'])
def test_edit_repo_metadata(self, session_browser, app_status):
"""Test edit repo metadata."""
if app_status == "disabled":
functional.app_disable(session_browser, 'gitweb')
_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
if app_status == "enabled":
_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'
def _create_local_repo(path):
"""Create a local repository."""
shutil.rmtree(path, ignore_errors=True)
os.mkdir(path)
create_repo_commands = [
'git init -q', 'git config http.sslVerify false',
'git -c "user.name=Tester" -c "user.email=tester" '
'commit -q --allow-empty --no-gpg-sign -m "test"'
]
for command in create_repo_commands:
subprocess.check_call(command, shell=True, cwd=path)
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.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()
elif access == 'public':
browser.find_by_id('id_gitweb-is_private').uncheck()
functional.submit(browser, form_class='form-gitweb')
elif not ok_if_exists:
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', '--no-gpg-sign', '-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')
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()
functional.submit(browser, form_class='form-delete')
def _edit_repo_metadata(browser, repo, metadata):
"""Set repository metadata."""
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-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, form_class='form-gitweb')
def _get_gitweb_site_default_repo_branch(browser, repo):
functional.nav_to_module(browser, 'gitweb')
browser.find_by_css('a[href="/gitweb/{0}.git"]'.format(repo)).first.click()
return browser.find_by_css('.head').first.text
def _get_repo_metadata(browser, repo):
"""Get repository metadata."""
functional.nav_to_module(browser, 'gitweb')
browser.links.find_by_href(
'/plinth/apps/gitweb/{}/edit/'.format(repo)).first.click()
metadata = {}
for item in ['name', 'description', 'owner']:
metadata[item] = browser.find_by_id('id_gitweb-' + item).value
if browser.find_by_id('id_gitweb-is_private').value:
metadata['access'] = 'private'
else:
metadata['access'] = 'public'
return metadata
def _get_repo_url(repo, with_auth):
""""Get repository URL"""
scheme = 'http'
if _default_url.startswith('https://'):
scheme = 'https'
url = _default_url.split(
'://')[1] if '://' in _default_url else _default_url
password = 'gitweb_wrong_password'
if with_auth:
password = functional.config['DEFAULT']['password']
return '{0}://{1}:{2}@{3}/gitweb/{4}'.format(
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
process.check_returncode() # Raise exception
return True
@contextlib.contextmanager
def _gitweb_temp_directory():
"""Create temporary directory"""
name = tempfile.mkdtemp(prefix='plinth_test_gitweb_')
yield name
shutil.rmtree(name)
def _repo_exists(browser, repo, access=None):
"""Check whether the repository exists."""
functional.nav_to_module(browser, 'gitweb')
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
private_icon = parent.find_by_css('.repo-private-icon')
if access == 'private':
access_matches = bool(private_icon)
if access == 'public':
access_matches = not bool(private_icon)
return bool(links_found) and access_matches
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)
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):
"""Check if a git repo is writable with git client."""
url = _get_repo_url(repo, with_auth)
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', '-c', 'push.default=current', 'push', '-qf', url
]
return _gitweb_git_command_is_successful(git_push_command,
repo_directory)
def _set_all_repos_private(browser):
"""Set all repositories private"""
functional.nav_to_module(browser, 'gitweb')
public_repos = []
for element in browser.find_by_css('#gitweb-repo-list .list-group-item'):
if not element.find_by_css('.repo-private-icon'):
repo = element.find_by_css('.repo-label').first.text
public_repos.append(repo)
for repo in public_repos:
_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, form_class='form-gitweb')
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, form_class='form-gitweb')
def _site_repo_exists(browser, repo):
"""Check whether the repository exists on Gitweb site."""
browser.visit('{}/gitweb'.format(_default_url))
if not repo.endswith('.git'):
repo = repo + ".git"
return bool(browser.find_by_css('a[href="/gitweb/{0}"]'.format(repo)))