|
| 1 | +# |
| 2 | +# Copyright (c) nexB Inc. and others. All rights reserved. |
| 3 | +# VulnerableCode is a trademark of nexB Inc. |
| 4 | +# SPDX-License-Identifier: Apache-2.0 |
| 5 | +# See http://www.apache.org/licenses/LICENSE-2.0 for the license text. |
| 6 | +# See https://github.com/aboutcode-org/vulnerablecode for support or download. |
| 7 | +# See https://aboutcode.org for more information about nexB OSS projects. |
| 8 | +# |
| 9 | + |
| 10 | +import unittest |
| 11 | +from unittest.mock import MagicMock |
| 12 | +from unittest.mock import patch as mock_patch |
| 13 | + |
| 14 | +from vulnerabilities.pipelines.v2_improvers.collect_patch_texts import CollectPatchTextsPipeline |
| 15 | +from vulnerabilities.pipelines.v2_improvers.collect_patch_texts import get_raw_patch_url |
| 16 | + |
| 17 | + |
| 18 | +class TestCollectPatchTextsPipeline(unittest.TestCase): |
| 19 | + def setUp(self): |
| 20 | + self.pipeline = CollectPatchTextsPipeline() |
| 21 | + |
| 22 | + def test_get_raw_patch_url(self): |
| 23 | + url = "https://github.com/user/repo/commit/abc1234567890" |
| 24 | + expected = "https://github.com/user/repo/commit/abc1234567890.patch" |
| 25 | + self.assertEqual(get_raw_patch_url(url), expected) |
| 26 | + |
| 27 | + url = "https://github.com/user/repo/pull/123" |
| 28 | + expected = "https://github.com/user/repo/pull/123.patch" |
| 29 | + self.assertEqual(get_raw_patch_url(url), expected) |
| 30 | + |
| 31 | + url = "https://gitlab.com/user/repo/-/commit/abc1234567890" |
| 32 | + expected = "https://gitlab.com/user/repo/-/commit/abc1234567890.patch" |
| 33 | + self.assertEqual(get_raw_patch_url(url), expected) |
| 34 | + |
| 35 | + url = "https://gitlab.com/user/repo/-/merge_requests/123" |
| 36 | + expected = "https://gitlab.com/user/repo/-/merge_requests/123.patch" |
| 37 | + self.assertEqual(get_raw_patch_url(url), expected) |
| 38 | + |
| 39 | + url = "https://example.com/fix.patch" |
| 40 | + self.assertEqual(get_raw_patch_url(url), url) |
| 41 | + |
| 42 | + url = "https://example.com/some/article" |
| 43 | + self.assertIsNone(get_raw_patch_url(url)) |
| 44 | + |
| 45 | + @mock_patch("vulnerabilities.pipelines.v2_improvers.collect_patch_texts.Patch") |
| 46 | + @mock_patch("requests.get") |
| 47 | + def test_collect_and_store_patch_texts(self, mock_get, mock_patch_model): |
| 48 | + # Setup mock Patch objects |
| 49 | + p1 = MagicMock(patch_url="https://github.com/u/r/commit/c1", patch_text=None) |
| 50 | + p2 = MagicMock(patch_url="https://github.com/u/r/pull/1", patch_text="") |
| 51 | + p3 = MagicMock(patch_url="https://example.com/no-patch", patch_text=None) |
| 52 | + p4 = MagicMock(patch_url="https://example.com/fix.patch", patch_text=None) |
| 53 | + |
| 54 | + # Mock the queryset iterator |
| 55 | + mock_qs = MagicMock() |
| 56 | + mock_qs.count.return_value = 4 |
| 57 | + # iterator() relies on chunk_size, we just return the list |
| 58 | + mock_qs.iterator.return_value = [p1, p2, p3, p4] |
| 59 | + |
| 60 | + # filter() returns the mock_qs |
| 61 | + mock_patch_model.objects.filter.return_value = mock_qs |
| 62 | + |
| 63 | + def side_effect(url, timeout=10): |
| 64 | + mock_resp = MagicMock() |
| 65 | + mock_resp.status_code = 404 |
| 66 | + if url == "https://github.com/u/r/commit/c1.patch": |
| 67 | + mock_resp.status_code = 200 |
| 68 | + mock_resp.text = "diff --git a/file b/file\n+code" |
| 69 | + elif url == "https://github.com/u/r/pull/1.patch": |
| 70 | + mock_resp.status_code = 200 |
| 71 | + mock_resp.text = "diff --git a/pr b/pr\n+pr_code" |
| 72 | + elif url == "https://example.com/fix.patch": |
| 73 | + mock_resp.status_code = 200 |
| 74 | + mock_resp.text = "diff --git a/direct b/direct\n+direct_code" |
| 75 | + return mock_resp |
| 76 | + |
| 77 | + mock_get.side_effect = side_effect |
| 78 | + |
| 79 | + self.pipeline.collect_and_store_patch_texts() |
| 80 | + |
| 81 | + # Assertions |
| 82 | + # p1 should be updated and saved |
| 83 | + self.assertEqual(p1.patch_text, "diff --git a/file b/file\n+code") |
| 84 | + p1.save.assert_called_once() |
| 85 | + |
| 86 | + # p2 should be updated and saved |
| 87 | + self.assertEqual(p2.patch_text, "diff --git a/pr b/pr\n+pr_code") |
| 88 | + p2.save.assert_called_once() |
| 89 | + |
| 90 | + # p3 should NOT be updated (didn't fetch) |
| 91 | + # It was None initially, should remain None or whatever logic left it. |
| 92 | + # But specifically save() should NOT be called if we didn't update it |
| 93 | + p3.save.assert_not_called() |
| 94 | + |
| 95 | + # p4 should be updated and saved |
| 96 | + self.assertEqual(p4.patch_text, "diff --git a/direct b/direct\n+direct_code") |
| 97 | + p4.save.assert_called_once() |
0 commit comments