Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/foss_cli_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ jobs:
- name: Install host dependencies
run: |
apt-get -qq update
apt-get install -qq gcc git nmap xz-utils
apt-get install -qq bzip2 gcc git nmap xz-utils
rm -rf /var/lib/apt/lists/*
- name: Install Python dependencies
run: |
pip install poetry
poetry install --with=dev
- name: Install files in shared volume
run: |
tar xJf tests/files/base-files_11.tar.xz -C /tmp
tar xvf tests/files/base-files_10.3-debian10-test.tar.bz2 -C /tmp
- name: Check services
run: nmap fossology -p 80
- name: Run tests
Expand Down
21 changes: 17 additions & 4 deletions .github/workflows/fossologytests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,33 @@ jobs:
- name: Install host dependencies
run: |
apt-get -qq update
apt-get install -qq gcc git nmap xz-utils
apt-get install -qq bzip2 curl gcc git nmap xz-utils
rm -rf /var/lib/apt/lists/*
- name: Install Python dependencies
run: |
pip install poetry
poetry install --with=dev
- name: Install files in shared volume
run: |
tar xJf tests/files/base-files_11.tar.xz -C /tmp
tar xvf tests/files/base-files_10.3-debian10-test.tar.bz2 -C /tmp
- name: Check services
run: nmap fossology -p 80
run: |
nmap fossology -p 80
for i in $(seq 1 60); do
if curl --fail --silent --show-error http://fossology/repo/api/v1/info > /dev/null; then
echo "Fossology API is up"
break
fi
if [ "$i" -eq 60 ]; then
echo "Fossology API did not become ready in time" >&2
exit 1
fi
echo "Waiting for Fossology API... attempt $i/60"
sleep 5
done
- name: Run tests
run: |
poetry run coverage run --source=fossology -m pytest
poetry run coverage run --source=fossology -m pytest --ignore-glob="tests/test_foss_cli*.py"
poetry run coverage report -m
- name: upload codecoverage results only if the PR originates from the upstream repository
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
Expand Down
24 changes: 22 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ Fossology Python also offers a command line interface to simplify interactions w

.. code:: bash

$ foss_cli -vv upload_file tests/files/zlib_1.2.11.dfsg-0ubuntu2.debian.tar.xz \
$ foss_cli -vv upload_file tests/files/base-files_10.3-debian10-test.tar.bz2 \
--folder_name FossFolder
--access_level public

Expand Down Expand Up @@ -200,6 +200,25 @@ Develop
To avoid running the whole testsuite during development of a new branch with changing only touching the code related
to the CLI, name your branch ``feat/cli-{something}`` and only the ``test_foss_cli_*`` will run in the pull request context.

**Testing Requirements for New Code**

When contributing new features or API extensions, ensure comprehensive test coverage using these guidelines:

- **Unit Tests with Responses Framework**: Use the `responses <https://github.com/getsentry/responses>`_ library
to mock HTTP requests and provide unit test coverage for all code paths. This allows fast, isolated testing without
requiring a running Fossology instance. See existing tests in ``tests/`` directory for examples of the
``@responses.activate`` decorator pattern.

- **Integration Tests**: Provide at least one generic integration test that runs against an actual Fossology instance.
These tests validate real API behavior and can be executed:

- **Locally**: Run against a Fossology Docker container (see the Test section below for setup instructions)
- **In CI/CD**: Tests automatically run via GitHub Actions in the `API Tests job
<https://github.com/fossology/fossology-python/.github/workflows/fossologytests.yml>`_

- **Coverage Requirements**: All new code paths must be covered by tests. Pull requests without adequate test
coverage will not be accepted. Use ``poetry run coverage`` to verify coverage locally.

**AI-Assisted Contributions**

AI coding agents are active in this repository. If you are an AI agent or using AI-assisted tooling,
Expand Down Expand Up @@ -275,7 +294,8 @@ The testsuite available in this project expects a running Fossology instance und
.. code:: shell

docker pull fossology/fossology
tar xJf tests/files/base-files_11.tar.xz -C /tmp
tar xvf tests/files/base-files_10.3-debian10-test.tar.bz2 -C /tmp
chmod a+r /tmp/base-files-10.3
docker run --mount src="/tmp",dst=/tmp,type=bind --name fossology -p 80:80 fossology/fossology

- Start the complete test suite or a specific test case (and generate coverage report):
Expand Down
2 changes: 1 addition & 1 deletion docs-source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
copyright = "2021, Siemens AG"

# The full version, including major/minor/patch tags
release = "3.5.0"
release = "3.6.0"


# -- General configuration ---------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions docs-source/sample_workflow.rst
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,10 @@ We first get an example file from our github repository test environment and the
upload it to the server.


>>> filename = "my_base-files_11.tar.xz"
>>> filename = "my_base-files_10.3-debian10-test.tar.bz2"
>>> path_to_upload_file = pathlib.Path.cwd() / filename
>>> if not path_to_upload_file.exists():
... url = "https://github.com/fossology/fossology-python/blob/master/tests/files/base-files_11.tar.xz"
... url = "https://github.com/fossology/fossology-python/blob/master/tests/files/base-files_10.3-debian10-test.tar.bz2"
... r = requests.get(url)
... with open(path_to_upload_file, "wb") as fp:
... len = fp.write(r.content)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ version_toml = [
"pyproject.toml:tool.poetry.version",
]
version_variables = [
"docs-source/conf.py:version",
"docs-source/conf.py:release",
]

[tool.poetry.dependencies]
Expand Down
39 changes: 8 additions & 31 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,6 @@ def foss_v2(foss_server: str, foss_token: str, foss_agents: Agents):
foss.close()


@pytest.fixture(scope="session")
def test_file_path() -> str:
return "tests/files/base-files_11.tar.xz"


@pytest.fixture(scope="session")
def upload_folder(foss: fossology.Fossology) -> Generator:
name = "UploadFolderTest"
Expand Down Expand Up @@ -227,7 +222,7 @@ def upload(
time.sleep(5)


@pytest.fixture(scope="function")
@pytest.fixture(scope="session")
def upload_v2(
foss_v2: fossology.Fossology,
test_file_path: str,
Expand All @@ -248,23 +243,11 @@ def upload_v2(

@pytest.fixture(scope="session")
def upload_with_jobs(
foss: fossology.Fossology, test_file_path: str, foss_schedule_agents: dict
) -> Generator:
upload = foss.upload_file(
foss.rootFolder,
file=test_file_path,
description="Test upload_with_jobs via fossology-python lib",
access_level=AccessLevel.PUBLIC,
wait_time=5,
)
if upload:
jobs_lookup(foss, upload)
foss.schedule_jobs(foss.rootFolder, upload, foss_schedule_agents)
jobs_lookup(foss, upload)
yield upload
foss.delete_upload(upload)
time.sleep(5)

foss: fossology.Fossology, upload: Upload, foss_schedule_agents: dict
):
foss.schedule_jobs(foss.rootFolder, upload, foss_schedule_agents)
jobs_lookup(foss, upload)
return upload

@pytest.fixture(scope="session")
def created_foss_user(foss: fossology.Fossology, foss_user: dict) -> Generator:
Expand All @@ -275,15 +258,9 @@ def created_foss_user(foss: fossology.Fossology, foss_user: dict) -> Generator:
foss.delete_user(user)


# foss_cli specific
@pytest.fixture(scope="session")
def click_test_file_path() -> str:
return "tests/files"


@pytest.fixture(scope="session")
def click_test_file() -> str:
return "zlib_1.2.11.dfsg-0ubuntu2.debian.tar.xz"
def test_file_path() -> str:
return "tests/files/base-files_10.3-debian10-test.tar.bz2"


@pytest.fixture(scope="session")
Expand Down
Binary file removed tests/files/base-files_11.tar.xz
Binary file not shown.
Binary file removed tests/files/zlib_1.2.11.dfsg-0ubuntu2.debian.tar.xz
Binary file not shown.
8 changes: 4 additions & 4 deletions tests/test_foss_cli_flow_cmds.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
from fossology import foss_cli


def test_upload_file(runner, click_test_file_path, click_test_file, click_test_dict):
def test_upload_file(runner, test_file_path, click_test_dict):
"""Test the CLI."""
d = click_test_dict
q_path = PurePath(click_test_file_path, click_test_file)
q_path = PurePath(test_file_path)
result = runner.invoke(
foss_cli.cli,
[
Expand All @@ -30,15 +30,15 @@ def test_upload_file(runner, click_test_file_path, click_test_file, click_test_d
assert result.exit_code == 0
assert d["VERBOSE"] == 2
assert d["DEBUG"]
assert d["UPLOAD"].uploadname == click_test_file
assert d["UPLOAD"].uploadname == q_path.name
assert "Summary of upload" in result.output
time.sleep(2)
result = runner.invoke(
foss_cli.cli,
[
"-vv",
"delete_upload",
click_test_file,
q_path.name,
],
obj=d,
catch_exceptions=False,
Expand Down
24 changes: 12 additions & 12 deletions tests/test_foss_cli_start_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@


def test_start_workflow_calling_with_wrong_report_format_exits_with_1(
runner, click_test_file_path, click_test_file, click_test_dict
runner, test_file_path, click_test_dict
):
d = click_test_dict
q_path = PurePath(click_test_file_path, click_test_file)
q_path = PurePath(test_file_path)
result = runner.invoke(
foss_cli.cli,
["-vv", "start_workflow", str(q_path), "--report_format", "imp"],
Expand All @@ -24,10 +24,10 @@ def test_start_workflow_calling_with_wrong_report_format_exits_with_1(


def test_start_workflow_calling_with_wrong_access_level_exits_with_1(
runner, click_test_file_path, click_test_file, click_test_dict
runner, test_file_path, click_test_dict
):
d = click_test_dict
q_path = PurePath(click_test_file_path, click_test_file)
q_path = PurePath(test_file_path)
result = runner.invoke(
foss_cli.cli,
["-vv", "start_workflow", str(q_path), "--access_level", "imp"],
Expand All @@ -39,10 +39,10 @@ def test_start_workflow_calling_with_wrong_access_level_exits_with_1(


def test_start_workflow_a_dry_run_without_reuse_newest_upload_always_exits_with_1(
runner, click_test_file_path, click_test_file, click_test_dict
runner, test_file_path, click_test_dict
):
d = click_test_dict
q_path = PurePath(click_test_file_path, click_test_file)
q_path = PurePath(test_file_path)
result = runner.invoke(
foss_cli.cli,
[
Expand All @@ -63,13 +63,13 @@ def test_start_workflow_a_dry_run_without_reuse_newest_upload_always_exits_with_


def test_start_workflow_reuse_newest_job(
runner, click_test_file_path, click_test_file, click_test_dict
runner, test_file_path, click_test_dict
):
d = click_test_dict
# first upload is the initial one
# - it uploads
# - it triggers a job on the upload
q_path = PurePath(click_test_file_path, click_test_file)
q_path = PurePath(test_file_path)
result = runner.invoke(
foss_cli.cli,
[
Expand Down Expand Up @@ -108,16 +108,16 @@ def test_start_workflow_reuse_newest_job(
obj=d,
catch_exceptions=False,
)
assert f"Can reuse upload for {click_test_file}" in result.output
assert f"Can reuse old job on Upload {click_test_file}" in result.output
assert f"Can reuse upload for {q_path.name}" in result.output
assert f"Can reuse old job on Upload {q_path.name}" in result.output
assert "Generated report" in result.output
assert "Report downloaded" in result.output
assert "Report written to file: " in result.output
assert result.exit_code == 0


@pytest.fixture(scope="module", autouse=True)
def cleanup_module(runner, click_test_file, click_test_dict):
def cleanup_module(runner, test_file_path, click_test_dict):
# Setup code (if any)
yield
# Teardown code: this runs after all tests in the module
Expand All @@ -126,7 +126,7 @@ def cleanup_module(runner, click_test_file, click_test_dict):
[
"-vv",
"delete_upload",
click_test_file,
PurePath(test_file_path).name,
],
obj=click_test_dict,
catch_exceptions=False,
Expand Down
2 changes: 1 addition & 1 deletion tests/test_jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

def test_unpack_jobs(foss: Fossology, upload: Upload):
jobs, _ = foss.list_jobs(upload=upload)
assert len(jobs) == 1
assert len(jobs) == 3


def test_nogroup_jobs(foss: Fossology, upload: Upload, foss_schedule_agents: Dict):
Expand Down
8 changes: 4 additions & 4 deletions tests/test_upload_from.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ def test_upload_from_url_v2(foss_v2: Fossology):

def test_upload_from_server(foss: Fossology):
server = {
"path": "/tmp/base-files-11",
"name": "base-files-11",
"path": "/tmp/base-files-10.3",
"name": "base-files-10.3",
}
server_upload = foss.upload_file(
foss.rootFolder,
Expand All @@ -132,8 +132,8 @@ def test_upload_from_server(foss: Fossology):
@pytest.mark.xfail
def test_upload_from_server_v2(foss_v2: Fossology):
server = {
"path": "/tmp/base-files-11",
"name": "base-files-11",
"path": "/tmp/base-files_10.3-debian10-test",
"name": "base-files_10.3-debian10-test",
}
server_upload = foss_v2.upload_file(
foss_v2.rootFolder,
Expand Down
4 changes: 2 additions & 2 deletions tests/test_upload_licenses_copyrights.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ def test_upload_licenses_with_containers(foss: Fossology, upload_with_jobs: Uplo

def test_upload_licenses_agent_ojo(foss: Fossology, upload_with_jobs: Upload):
licenses = foss.upload_licenses(upload_with_jobs, agent="ojo")
assert len(licenses) == 9 # type: ignore
assert len(licenses) == 10 # type: ignore


def test_upload_licenses_agent_monk(foss: Fossology, upload_with_jobs: Upload):
licenses = foss.upload_licenses(upload_with_jobs, agent="monk")
assert len(licenses) == 22 # type: ignore
assert len(licenses) == 23 # type: ignore


def test_upload_licenses_and_copyrights(foss: Fossology, upload_with_jobs: Upload):
Expand Down
Loading
Loading