Skip to content

Commit

Permalink
test: first pytest-based testcases for "project createbom"
Browse files Browse the repository at this point in the history
Fixes #39 by adding the necessary infrastructure for pytest-based test
classes and refactoring the "project createbom" testcases to use it.

This shows the pytest assert syntax and how to capture stdout with
pytest using the "capsys" fixture.
  • Loading branch information
gernot-h committed Aug 21, 2023
1 parent 8cecd12 commit 7639986
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 58 deletions.
6 changes: 5 additions & 1 deletion tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def __init__(self):
self.outputformat = ""


class TestBase(unittest.TestCase):
class TestBasePytest:
MYTOKEN = "MYTOKEN"
MYURL = "https://my.server.com/"
ERROR_MSG_NO_LOGIN = "Unable to login"
Expand Down Expand Up @@ -474,3 +474,7 @@ def get_cli_file_mit(self) -> str:
<ExternalIds />
</ComponentLicenseInformation>
"""


class TestBase(unittest.TestCase, TestBasePytest):
pass
105 changes: 48 additions & 57 deletions tests/test_create_bom.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@

import os

import pytest
import responses
from cyclonedx.model import ExternalReferenceType

from capycli.common.capycli_bom_support import CaPyCliBom
from capycli.main.result_codes import ResultCode
from capycli.project.create_bom import CreateBom
from tests.test_base import AppArguments, TestBase
from tests.test_base import AppArguments, TestBasePytest


class TestCreateBom(TestBase):
class TestCreateBom(TestBasePytest):
OUTPUTFILE = "output.json"

def test_show_help(self) -> None:
Expand All @@ -31,7 +32,7 @@ def test_show_help(self) -> None:
args.help = True

out = self.capture_stdout(sut.run, args)
self.assertTrue("usage: CaPyCli project createbom" in out)
assert "usage: CaPyCli project createbom" in out

@responses.activate
def test_no_login(self) -> None:
Expand All @@ -45,11 +46,9 @@ def test_no_login(self) -> None:
args.debug = True
args.verbose = True

try:
with pytest.raises(SystemExit) as ex:
sut.run(args)
self.assertTrue(False, "Failed to report login failure")
except SystemExit as ex:
self.assertEqual(ResultCode.RESULT_AUTH_ERROR, ex.code)
assert ResultCode.RESULT_AUTH_ERROR == ex.value.code

@responses.activate
def test_no_output_file(self) -> None:
Expand All @@ -59,18 +58,16 @@ def test_no_output_file(self) -> None:
args.command = []
args.command.append("project")
args.command.append("createbom")
args.sw360_token = TestBase.MYTOKEN
args.sw360_url = TestBase.MYURL
args.sw360_token = TestBasePytest.MYTOKEN
args.sw360_url = TestBasePytest.MYURL
args.debug = True
args.verbose = True

self.add_login_response()

try:
with pytest.raises(SystemExit) as ex:
sut.run(args)
self.assertTrue(False, "Failed to report login failure")
except SystemExit as ex:
self.assertEqual(ResultCode.RESULT_COMMAND_ERROR, ex.code)
assert ResultCode.RESULT_COMMAND_ERROR == ex.value.code

@responses.activate
def test_no_project_identification(self) -> None:
Expand All @@ -82,16 +79,14 @@ def test_no_project_identification(self) -> None:
args.command.append("createbom")
args.debug = True
args.verbose = True
args.sw360_token = TestBase.MYTOKEN
args.sw360_url = TestBase.MYURL
args.sw360_token = TestBasePytest.MYTOKEN
args.sw360_url = TestBasePytest.MYURL

self.add_login_response()

try:
with pytest.raises(SystemExit) as ex:
sut.run(args)
self.assertTrue(False, "Failed to report login failure")
except SystemExit as ex:
self.assertEqual(ResultCode.RESULT_COMMAND_ERROR, ex.code)
assert ResultCode.RESULT_COMMAND_ERROR == ex.value.code

@responses.activate
def test_project_not_found(self) -> None:
Expand All @@ -101,8 +96,8 @@ def test_project_not_found(self) -> None:
args.command = []
args.command.append("project")
args.command.append("createbom")
args.sw360_token = TestBase.MYTOKEN
args.sw360_url = TestBase.MYURL
args.sw360_token = TestBasePytest.MYTOKEN
args.sw360_url = TestBasePytest.MYURL
args.debug = True
args.verbose = True
args.id = "34ef5c5452014c52aa9ce4bc180624d8"
Expand All @@ -120,18 +115,16 @@ def test_project_not_found(self) -> None:
adding_headers={"Authorization": "Token " + self.MYTOKEN},
)

try:
with pytest.raises(SystemExit) as ex:
sut.run(args)
self.assertTrue(False, "Failed to report login failure")
except SystemExit as ex:
self.assertEqual(ResultCode.RESULT_ERROR_ACCESSING_SW360, ex.code)
assert ResultCode.RESULT_ERROR_ACCESSING_SW360 == ex.value.code

@responses.activate
def test_create_bom_multiple_purls(self):
def test_create_bom_multiple_purls(self, capsys):
sut = CreateBom()

self.add_login_response()
sut.login(token=TestBase.MYTOKEN, url=TestBase.MYURL)
sut.login(token=TestBasePytest.MYTOKEN, url=TestBasePytest.MYURL)

# the first release
responses.add(
Expand All @@ -156,20 +149,18 @@ def test_create_bom_multiple_purls(self):
adding_headers={"Authorization": "Token " + self.MYTOKEN},
)

out = self.capture_stdout(sut.create_project_bom, self.get_project_for_test())
self.assertIn("Multiple purls added", out)

# TODO self.capture_stdout doesn't allow us to get return value,
# so re-run test. See also https://github.com/sw360/capycli/issues/39
cdx_components = sut.create_project_bom(self.get_project_for_test())
self.assertEqual(cdx_components[0].purl, "pkg:deb/debian/[email protected] pkg:pypi/[email protected]")
captured = capsys.readouterr()

assert "Multiple purls added" in captured.out
assert cdx_components[0].purl == "pkg:deb/debian/[email protected] pkg:pypi/[email protected]"

@responses.activate
def test_project_by_id(self):
sut = CreateBom()

self.add_login_response()
sut.login(token=TestBase.MYTOKEN, url=TestBase.MYURL)
sut.login(token=TestBasePytest.MYTOKEN, url=TestBasePytest.MYURL)

# the project
project = self.get_project_for_test()
Expand Down Expand Up @@ -218,27 +209,27 @@ def test_project_by_id(self):

cdx_bom = sut.create_project_cdx_bom("p001")
cx_comp = cdx_bom.components[0]
self.assertEqual(cx_comp.purl, release["externalIds"]["package-url"])
assert cx_comp.purl == release["externalIds"]["package-url"]

ext_refs_src_url = [e for e in cx_comp.external_references if e.comment == CaPyCliBom.SOURCE_URL_COMMENT]
self.assertEqual(len(ext_refs_src_url), 1)
self.assertEqual(ext_refs_src_url[0].url, release["sourceCodeDownloadurl"])
self.assertEqual(ext_refs_src_url[0].type, ExternalReferenceType.DISTRIBUTION)
assert len(ext_refs_src_url) == 1
assert ext_refs_src_url[0].url == release["sourceCodeDownloadurl"]
assert ext_refs_src_url[0].type == ExternalReferenceType.DISTRIBUTION

ext_refs_src_file = [e for e in cx_comp.external_references if e.comment == CaPyCliBom.SOURCE_FILE_COMMENT]
self.assertEqual(len(ext_refs_src_file), 2)
self.assertEqual(ext_refs_src_file[0].url, release["_embedded"]["sw360:attachments"][0]["filename"])
self.assertEqual(ext_refs_src_file[0].type, ExternalReferenceType.DISTRIBUTION)
self.assertEqual(ext_refs_src_file[0].hashes[0].alg, "SHA-1")
self.assertEqual(ext_refs_src_file[0].hashes[0].content, release["_embedded"]["sw360:attachments"][0]["sha1"])
assert len(ext_refs_src_file) == 2
assert ext_refs_src_file[0].url == release["_embedded"]["sw360:attachments"][0]["filename"]
assert ext_refs_src_file[0].type == ExternalReferenceType.DISTRIBUTION
assert ext_refs_src_file[0].hashes[0].alg == "SHA-1"
assert ext_refs_src_file[0].hashes[0].content == release["_embedded"]["sw360:attachments"][0]["sha1"]

ext_refs_vcs = [e for e in cx_comp.external_references if e.type == ExternalReferenceType.VCS]
self.assertEqual(len(ext_refs_vcs), 1)
self.assertEqual(ext_refs_vcs[0].url, release["repository"]["url"])
assert len(ext_refs_vcs) == 1
assert ext_refs_vcs[0].url == release["repository"]["url"]

self.assertEqual(cdx_bom.metadata.component.name, project["name"])
self.assertEqual(cdx_bom.metadata.component.version, project["version"])
self.assertEqual(cdx_bom.metadata.component.description, project["description"])
assert cdx_bom.metadata.component.name == project["name"]
assert cdx_bom.metadata.component.version == project["version"]
assert cdx_bom.metadata.component.description == project["description"]

@responses.activate
def test_project_show_by_name(self):
Expand All @@ -248,8 +239,8 @@ def test_project_show_by_name(self):
args.command = []
args.command.append("project")
args.command.append("createbom")
args.sw360_token = TestBase.MYTOKEN
args.sw360_url = TestBase.MYURL
args.sw360_token = TestBasePytest.MYTOKEN
args.sw360_url = TestBasePytest.MYURL
args.debug = True
args.verbose = True
args.name = "CaPyCLI"
Expand Down Expand Up @@ -326,15 +317,15 @@ def test_project_show_by_name(self):
out = self.capture_stdout(sut.run, args)
# self.dump_textfile(out, "DUMP.TXT")

self.assertTrue("Searching for project..." in out)
self.assertTrue("Project name: CaPyCLI, 1.9.0" in out)
self.assertTrue("cli-support 1.3" in out)
self.assertTrue("wheel 0.38.4" in out)
assert "Searching for project..." in out
assert "Project name: CaPyCLI, 1.9.0" in out
assert "cli-support 1.3" in out
assert "wheel 0.38.4" in out

self.assertTrue(os.path.isfile(self.OUTPUTFILE))
assert os.path.isfile(self.OUTPUTFILE)
sbom = CaPyCliBom.read_sbom(self.OUTPUTFILE)
self.assertIsNotNone(sbom)
self.assertEqual(2, len(sbom.components))
assert sbom is not None
assert 2 == len(sbom.components)

self.delete_file(self.OUTPUTFILE)

Expand Down

0 comments on commit 7639986

Please sign in to comment.