blob: 55eee605acce64c54270370554792d5e009c2700 [file] [log] [blame]
Trent Aptedbd3cb412023-08-18 11:37:02 +10001# Copyright 2023 The ChromiumOS Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Unit tests for the sdk_subtools api layer."""
6
7import os
Trent Aptedc94115a2023-09-20 16:39:18 +10008from pathlib import Path
9from typing import Dict, Iterator, List, Optional, Union
Trent Aptedbd3cb412023-08-18 11:37:02 +100010from unittest import mock
11
12import pytest
13
14from chromite.api import api_config
15from chromite.api.controller import sdk_subtools
16from chromite.api.gen.chromite.api import sdk_subtools_pb2
Trent Aptedc94115a2023-09-20 16:39:18 +100017from chromite.api.gen.chromiumos import common_pb2
Trent Aptedbd3cb412023-08-18 11:37:02 +100018from chromite.lib import cros_build_lib
19from chromite.lib import sysroot_lib
20from chromite.lib.parser import package_info
21
22
23def make_request(
24 chroot_path: Union[str, os.PathLike, None] = "fake_chroot_path"
25) -> sdk_subtools_pb2.BuildSdkSubtoolsRequest:
Trent Aptedc94115a2023-09-20 16:39:18 +100026 """Helper to build a build request message."""
Trent Aptedbd3cb412023-08-18 11:37:02 +100027 request = sdk_subtools_pb2.BuildSdkSubtoolsRequest()
28 if chroot_path is not None:
29 request.chroot.path = os.fspath(chroot_path)
30 return request
31
32
33def build_sdk_subtools(
34 request: sdk_subtools_pb2.BuildSdkSubtoolsRequest,
35 call_type: Optional[int] = api_config.ApiConfig.CALL_TYPE_EXECUTE,
36) -> sdk_subtools_pb2.BuildSdkSubtoolsResponse:
Trent Aptedc94115a2023-09-20 16:39:18 +100037 """Invokes sdk_subtools.BuildSdkSubtools and returns the response proto."""
Trent Aptedbd3cb412023-08-18 11:37:02 +100038 config = api_config.ApiConfig(call_type)
39 response = sdk_subtools_pb2.BuildSdkSubtoolsResponse()
40 sdk_subtools.BuildSdkSubtools(request, response, config)
41 return response
42
43
Trent Aptedc94115a2023-09-20 16:39:18 +100044def make_upload_request(
45 bundle_paths: Optional[List[str]] = None,
46) -> sdk_subtools_pb2.UploadSdkSubtoolsRequest:
47 """Helper to build an upload request message."""
48 request = sdk_subtools_pb2.UploadSdkSubtoolsRequest()
49 if bundle_paths is None:
50 bundle_paths = ["/path/to/bundle"]
51 request.bundle_paths.extend(
52 common_pb2.Path(path=p, location=common_pb2.Path.OUTSIDE)
53 for p in bundle_paths
54 )
55 return request
56
57
58def upload_sdk_subtools(
59 request: sdk_subtools_pb2.UploadSdkSubtoolsRequest,
60 call_type: Optional[int] = api_config.ApiConfig.CALL_TYPE_EXECUTE,
61) -> sdk_subtools_pb2.UploadSdkSubtoolsResponse:
62 """Invokes sdk_subtools.UploadSdkSubtools and returns the response proto."""
63 config = api_config.ApiConfig(call_type)
64 response = sdk_subtools_pb2.UploadSdkSubtoolsResponse()
65 sdk_subtools.UploadSdkSubtools(request, response, config)
66 return response
67
68
Trent Aptedbd3cb412023-08-18 11:37:02 +100069MockService = Dict[str, mock.MagicMock]
70
71
72@pytest.fixture(name="mock_service")
73def mock_service_fixture() -> Iterator[MockService]:
74 """Mocks the sdk_subtools service layer with mocks."""
75 with mock.patch.multiple(
76 "chromite.service.sdk_subtools",
77 setup_base_sdk=mock.DEFAULT,
78 update_packages=mock.DEFAULT,
Trent Aptedc94115a2023-09-20 16:39:18 +100079 bundle_and_prepare_upload=mock.DEFAULT,
80 upload_prepared_bundles=mock.DEFAULT,
Trent Aptedbd3cb412023-08-18 11:37:02 +100081 ) as dict_of_mocks:
Trent Aptedc94115a2023-09-20 16:39:18 +100082 # Default to a "successful" return with an empty list of bundle paths.
83 dict_of_mocks["bundle_and_prepare_upload"].return_value = ([], None)
Trent Aptedbd3cb412023-08-18 11:37:02 +100084 yield dict_of_mocks
85
86
Trent Aptedc94115a2023-09-20 16:39:18 +100087def test_build_validate_only(mock_service: MockService) -> None:
Trent Aptedbd3cb412023-08-18 11:37:02 +100088 """Verify a validate-only call does not execute any logic."""
89 build_sdk_subtools(
90 make_request(), api_config.ApiConfig.CALL_TYPE_VALIDATE_ONLY
91 )
92 for f in mock_service.values():
93 f.assert_not_called()
94
95
Trent Aptedc94115a2023-09-20 16:39:18 +100096def test_build_mock_call(mock_service: MockService) -> None:
Trent Aptedbd3cb412023-08-18 11:37:02 +100097 """Consistency check that a mock call does not execute any logic."""
98 build_sdk_subtools(
99 make_request(), api_config.ApiConfig.CALL_TYPE_MOCK_SUCCESS
100 )
101 for f in mock_service.values():
102 f.assert_not_called()
103
104
Trent Aptedc94115a2023-09-20 16:39:18 +1000105def test_build_success_no_bundles(mock_service: MockService) -> None:
106 """Test a successful call with zero bundles available."""
Trent Aptedbd3cb412023-08-18 11:37:02 +1000107 response = build_sdk_subtools(make_request())
108 mock_service["setup_base_sdk"].assert_called_once()
109 mock_service["update_packages"].assert_called_once()
Trent Aptedc94115a2023-09-20 16:39:18 +1000110 mock_service["bundle_and_prepare_upload"].assert_called_once()
Trent Aptedbd3cb412023-08-18 11:37:02 +1000111 assert not response.failed_package_data
112
113
Trent Aptedc94115a2023-09-20 16:39:18 +1000114def test_build_success_two_bundles(mock_service: MockService) -> None:
115 """Test a successful call with two bundles available."""
116 bundles = [
117 Path("/var/tmp/cros-subtools/shellcheck"),
118 Path("/var/tmp/cros-subtools/rustfmt"),
119 ]
120 mock_service["bundle_and_prepare_upload"].return_value = (bundles, None)
121 response = build_sdk_subtools(make_request())
122 assert [(p.path, p.location) for p in response.bundle_paths] == [
123 ("/var/tmp/cros-subtools/shellcheck", common_pb2.Path.INSIDE),
124 ("/var/tmp/cros-subtools/rustfmt", common_pb2.Path.INSIDE),
125 ]
126
127
Trent Aptedbd3cb412023-08-18 11:37:02 +1000128def test_package_update_failure(mock_service: MockService) -> None:
129 """Test output handling when package update fails."""
130 mock_service[
131 "update_packages"
132 ].side_effect = sysroot_lib.PackageInstallError(
133 "mock failure",
134 cros_build_lib.CompletedProcess(),
135 packages=[package_info.parse("some-category/some-package-0.42-r43")],
136 )
137 response = build_sdk_subtools(make_request())
138 mock_service["setup_base_sdk"].assert_called_once()
139 mock_service["update_packages"].assert_called_once()
Trent Aptedc94115a2023-09-20 16:39:18 +1000140 mock_service["bundle_and_prepare_upload"].assert_not_called()
Trent Aptedbd3cb412023-08-18 11:37:02 +1000141 assert len(response.failed_package_data) == 1
142 assert response.failed_package_data[0].name.package_name == "some-package"
143 assert response.failed_package_data[0].name.category == "some-category"
144 assert response.failed_package_data[0].name.version == "0.42-r43"
Trent Aptedc94115a2023-09-20 16:39:18 +1000145
146
147def test_upload_validate_only(mock_service: MockService) -> None:
148 """Verify a validate-only call does not execute any logic."""
149 upload_sdk_subtools(
150 make_upload_request(), api_config.ApiConfig.CALL_TYPE_VALIDATE_ONLY
151 )
152 for f in mock_service.values():
153 f.assert_not_called()
154
155
156def test_upload_mock_call(mock_service: MockService) -> None:
157 """Consistency check that a mock call does not execute any logic."""
158 upload_sdk_subtools(
159 make_upload_request(), api_config.ApiConfig.CALL_TYPE_MOCK_SUCCESS
160 )
161 for f in mock_service.values():
162 f.assert_not_called()
163
164
165def test_upload_success(mock_service: MockService) -> None:
166 """Test a successful call to upload."""
167 upload_sdk_subtools(make_upload_request())
168 mock_service["upload_prepared_bundles"].assert_called_once_with(
169 False, [Path("/path/to/bundle")]
170 )
171
172
173def test_upload_to_production(mock_service: MockService) -> None:
174 """Test a successful call to upload with use_production set."""
175 request = make_upload_request()
176 request.use_production = True
177 upload_sdk_subtools(request)
178 mock_service["upload_prepared_bundles"].assert_called_once_with(
179 True, [Path("/path/to/bundle")]
180 )