Skip to content

Commit 6ba1391

Browse files
authored
Merge pull request #51 from sachinshaji/feat/findsources
Improve findsources functionality
2 parents b113561 + 95cea75 commit 6ba1391

File tree

8 files changed

+550
-37
lines changed

8 files changed

+550
-37
lines changed

capycli/bom/findsources.py

Lines changed: 360 additions & 31 deletions
Large diffs are not rendered by default.

capycli/common/script_base.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,15 @@ def login(self, token: str = "", url: str = "", oauth2: bool = False) -> bool:
4646
self.sw360_url = url
4747

4848
if not self.sw360_url:
49-
print_red(" No SW360 server URL specified!")
50-
sys.exit(ResultCode.RESULT_ERROR_ACCESSING_SW360)
49+
print_red(" No SW360 server URL specified!")
50+
sys.exit(ResultCode.RESULT_ERROR_ACCESSING_SW360)
5151

5252
if self.sw360_url[-1] != "/":
5353
self.sw360_url += "/"
5454

5555
if not sw360_api_token:
56-
print_red(" No SW360 API token specified!")
57-
sys.exit(ResultCode.RESULT_AUTH_ERROR)
56+
print_red(" No SW360 API token specified!")
57+
sys.exit(ResultCode.RESULT_AUTH_ERROR)
5858

5959
self.client = sw360.sw360_api.SW360(self.sw360_url, sw360_api_token, oauth2)
6060

capycli/main/options.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,13 @@ def register_options(self):
160160
help="use this token for access to SW360",
161161
)
162162

163+
self.parser.add_argument(
164+
"-gt",
165+
"--github_token",
166+
dest="github_token",
167+
help="use this token for access to github",
168+
)
169+
163170
self.parser.add_argument(
164171
"-oa",
165172
"--oauth2",

poetry.lock

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ capycli = "capycli.main.cli:main"
3737
python = "^3.8" # drop support for 3.6 and 3.7 because of wheel and cli-support
3838
colorama = "^0.4.3"
3939
requests = "^2.31.0" # fix CVE-2023-32681
40+
semver = "^2.9.1"
4041
packageurl-python = ">0.8, <1.0"
4142
pyjwt = "^1.7.1"
4243
openpyxl = "^3.0.3"
@@ -51,6 +52,7 @@ tomli = "^2.0.1"
5152
dateparser = "^1.1.8"
5253
urllib3 = "*"
5354
importlib-resources = "^5.12.0"
55+
beautifulsoup4 = "^4.11.1"
5456

5557
[tool.poetry.dev-dependencies]
5658
flake8 = ">=3.7.8"

tests/test_base.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ def __init__(self):
6363
self.outputformat = ""
6464
self.remote_granularity_list = None
6565
self.local_granularity_list = None
66+
self.github_token = ""
6667

6768

6869
class TestBasePytest:

tests/test_find_sources.py

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
# -------------------------------------------------------------------------------
88

99
import os
10+
import responses
1011

1112
import capycli.common.json_support
1213
import capycli.common.script_base
1314
from capycli.bom.findsources import FindSources
1415
from capycli.common.capycli_bom_support import CaPyCliBom, CycloneDxSupport
1516
from capycli.main.result_codes import ResultCode
1617
from tests.test_base import AppArguments, TestBase
18+
from unittest.mock import MagicMock, patch
1719

1820

1921
class TestFindSources(TestBase):
@@ -100,7 +102,7 @@ def test_find_sources(self) -> None:
100102
self.assertTrue("Using anonymous GitHub access" in out)
101103
self.assertTrue("8 components read from SBOM" in out)
102104
self.assertTrue("1 source files were already available" in out)
103-
self.assertTrue("5 source file URLs were found" in out)
105+
self.assertTrue("6 source file URLs were found" in out)
104106

105107
sbom = CaPyCliBom.read_sbom(args.outputfile)
106108
self.assertIsNotNone(sbom)
@@ -191,3 +193,136 @@ def test_normalize_version(self):
191193
actual = sut.to_semver_string(version)
192194
self.assertEqual(actual, expected)
193195
self.assertTrue(actual == expected, 'version %s is %s' % (actual, expected))
196+
197+
@responses.activate
198+
def test_get_release_component_id(self):
199+
# Mock the sw360 client
200+
mock_client = MagicMock()
201+
mock_client.get_release.return_value = {"_links": {"sw360:component": {"href": self.MYURL + 'components/123'}}}
202+
203+
# Call the method and assert the result
204+
find_sources = FindSources()
205+
find_sources.client = mock_client
206+
component_id = find_sources.get_release_component_id("some_release_id")
207+
self.assertEqual(component_id, "123")
208+
209+
@responses.activate
210+
def test_find_source_url_from_component(self):
211+
# Mock the client
212+
mock_client = MagicMock()
213+
mock_client.get_component.return_value = {"_embedded": {"sw360:releases": [{"_links": {"self": {"href": self.MYURL + 'releases/456'}}}]}}
214+
mock_client.get_release.return_value = {"_links": {"sw360:component": {"href": self.MYURL + 'components/123'}}, "sourceCodeDownloadurl": "http://github.com/some/repo/0.0.0"}
215+
216+
# Call the method and assert the result
217+
find_sources = FindSources()
218+
find_sources.client = mock_client # Inject the mocked client
219+
source_url = find_sources.find_source_url_from_component(component_id="some_component_id")
220+
self.assertEqual(source_url, "http://github.com/some/repo/0.0.0")
221+
222+
@patch('requests.get')
223+
@patch('bs4.BeautifulSoup')
224+
def test_get_pkg_go_repo_url_success(self, mock_beautifulsoup, mock_requests_get):
225+
# Mocking successful response
226+
mock_requests_get.return_value.text = '<div class="UnitMeta-repo"><a href="https://github.com/example/repo/1.0.0">Repo Link</a></div>'
227+
mock_beautifulsoup.return_value.find.return_value = MagicMock(get=lambda x: 'https://github.com/example/repo/1.0.0')
228+
find_sources = FindSources()
229+
repo_url = find_sources.get_pkg_go_repo_url('example/package')
230+
self.assertEqual(repo_url, 'https://github.com/example/repo/1.0.0')
231+
232+
@patch('requests.get', side_effect=Exception('Some error'))
233+
def test_get_pkg_go_repo_url_error(self, mock_requests_get):
234+
# Mocking an exception during the request
235+
find_sources = FindSources()
236+
repo_url = find_sources.get_pkg_go_repo_url('some/package')
237+
self.assertEqual(repo_url, 'https://pkg.go.dev/some/package')
238+
239+
@patch('capycli.bom.findsources.FindSources.get_github_info')
240+
@patch('capycli.bom.findsources.FindSources.get_matching_tag')
241+
def test_find_golang_url_github(self, mock_get_github_info, mock_get_matching_tag):
242+
# Mocking a GitHub scenario
243+
mock_get_github_info.return_value = 'https://pkg.go.dev/github.com/opencontainers/runc'
244+
mock_get_matching_tag.return_value = 'https://github.com/opencontainers/runc/archive/refs/tags/v1.0.1.zip'
245+
find_sources = FindSources()
246+
component = MagicMock()
247+
component.name = 'github.com/opencontainers/runc'
248+
component.version = 'v1.0.1'
249+
source_url = find_sources.find_golang_url(component)
250+
251+
self.assertEqual(source_url, 'https://pkg.go.dev/github.com/opencontainers/runc')
252+
253+
def test_find_golang_url_non_github(self):
254+
# Mocking a non-GitHub scenario
255+
find_sources = FindSources()
256+
component = MagicMock()
257+
component.name = 'example/package'
258+
component.version = 'v1.0.0'
259+
source_url = find_sources.find_golang_url(component)
260+
261+
self.assertEqual(source_url, '')
262+
263+
def test_no_matching_tag(self):
264+
265+
validTag = "3.2.0"
266+
invalidTag = "0.03"
267+
emptyString = ""
268+
githubUrl = "https://github.com/apache/kafka"
269+
zipball_url = "https://api.github.com/repos/apache/kafka/zipball/refs/tags/" + validTag
270+
sourceUrl = "https://github.com/apache/kafka/archive/refs/tags/" + validTag + ".zip"
271+
findResource = capycli.bom.findsources.FindSources()
272+
# test Empty tagInfo array
273+
tagInfo = []
274+
actual = capycli.bom.findsources.FindSources.get_matching_tag(findResource, tagInfo, validTag, githubUrl)
275+
self.assertEqual(actual, None)
276+
# test Empty tag string
277+
tagInfo = [{"name": emptyString, "zipball_url": zipball_url}]
278+
actual = capycli.bom.findsources.FindSources.get_matching_tag(findResource, tagInfo, validTag, githubUrl)
279+
self.assertEqual(actual, '')
280+
# test Empty url string
281+
tagInfo = [{"name": validTag, "zipball_url": emptyString}]
282+
actual = capycli.bom.findsources.FindSources.get_matching_tag(findResource, tagInfo, validTag, githubUrl)
283+
self.assertEqual(actual, None)
284+
# test non-matching tag
285+
tagInfo = [{"name": invalidTag, "zipball_url": zipball_url}]
286+
actual = capycli.bom.findsources.FindSources.get_matching_tag(findResource, tagInfo, validTag, githubUrl)
287+
self.assertEqual(actual, '')
288+
# test valid tag
289+
tagInfo = [{"name": validTag, "zipball_url": zipball_url}]
290+
actual = capycli.bom.findsources.FindSources.get_matching_tag(findResource, tagInfo, validTag, githubUrl)
291+
self.assertEqual(actual, sourceUrl)
292+
293+
@patch("time.sleep")
294+
def test_get_source_url_success(self, mock_sleep):
295+
mock_client = MagicMock()
296+
mock_release_id = "123"
297+
mock_source_url = "https://example.com/source.zip"
298+
299+
mock_client.get_release.return_value = {"sourceCodeDownloadurl": mock_source_url}
300+
301+
findsources = FindSources()
302+
findsources.client = mock_client
303+
result = findsources.get_source_url_from_release(mock_release_id)
304+
self.assertEqual(result, mock_source_url)
305+
mock_sleep.assert_not_called()
306+
307+
def test_get_source_url_no_source_url(self):
308+
mock_client = MagicMock()
309+
mock_release_id = "123"
310+
311+
mock_client.get_release.return_value = {"sourceCodeDownloadurl": ""}
312+
findsources = FindSources()
313+
findsources.client = mock_client
314+
315+
result = findsources.get_source_url_from_release(mock_release_id)
316+
self.assertIsNone(result)
317+
mock_client.get_release.assert_called_once_with(mock_release_id)
318+
319+
def test_get_source_url_exception(self):
320+
mock_client = MagicMock()
321+
mock_release_id = "123"
322+
323+
mock_client.get_release.side_effect = Exception("Unexpected error")
324+
findsources = FindSources()
325+
findsources.client = mock_client
326+
with self.assertRaises(Exception):
327+
findsources.get_source_url_from_release(mock_release_id)
328+
mock_client.get_release.assert_called_once_with(mock_release_id)

tests/test_get_dependencies_python.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ def test_process_poetry_lock_v2(self):
405405
self.assertTrue("Checking meta-data:" in out)
406406
self.assertTrue("cli-support" in out)
407407
self.assertTrue(self.OUTPUTFILE2 in out)
408-
self.assertTrue("34 components items written to file." in out)
408+
self.assertTrue("37 components items written to file." in out)
409409

410410
# ensure that dev dependencies are NOT listed
411411
self.assertTrue("flake8" not in out)

0 commit comments

Comments
 (0)