xref: /aosp_15_r20/external/federated-compute/fcp/demo/http_actions_test.py (revision 14675a029014e728ec732f129a32e299b2da0601)
1*14675a02SAndroid Build Coastguard Worker# Copyright 2022 Google LLC
2*14675a02SAndroid Build Coastguard Worker#
3*14675a02SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*14675a02SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*14675a02SAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*14675a02SAndroid Build Coastguard Worker#
7*14675a02SAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
8*14675a02SAndroid Build Coastguard Worker#
9*14675a02SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*14675a02SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*14675a02SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*14675a02SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*14675a02SAndroid Build Coastguard Worker# limitations under the License.
14*14675a02SAndroid Build Coastguard Worker"""Tests for http_actions."""
15*14675a02SAndroid Build Coastguard Worker
16*14675a02SAndroid Build Coastguard Workerimport gzip
17*14675a02SAndroid Build Coastguard Workerimport http
18*14675a02SAndroid Build Coastguard Workerimport http.client
19*14675a02SAndroid Build Coastguard Workerimport http.server
20*14675a02SAndroid Build Coastguard Workerimport socket
21*14675a02SAndroid Build Coastguard Workerimport threading
22*14675a02SAndroid Build Coastguard Workerfrom unittest import mock
23*14675a02SAndroid Build Coastguard Worker
24*14675a02SAndroid Build Coastguard Workerfrom absl.testing import absltest
25*14675a02SAndroid Build Coastguard Worker
26*14675a02SAndroid Build Coastguard Workerfrom fcp.demo import http_actions
27*14675a02SAndroid Build Coastguard Workerfrom fcp.protos.federatedcompute import common_pb2
28*14675a02SAndroid Build Coastguard Workerfrom fcp.protos.federatedcompute import eligibility_eval_tasks_pb2
29*14675a02SAndroid Build Coastguard Worker
30*14675a02SAndroid Build Coastguard Worker
31*14675a02SAndroid Build Coastguard Workerclass TestService:
32*14675a02SAndroid Build Coastguard Worker
33*14675a02SAndroid Build Coastguard Worker  def __init__(self):
34*14675a02SAndroid Build Coastguard Worker    self.proto_action = mock.Mock()
35*14675a02SAndroid Build Coastguard Worker    self.get_action = mock.Mock()
36*14675a02SAndroid Build Coastguard Worker    self.post_action = mock.Mock()
37*14675a02SAndroid Build Coastguard Worker
38*14675a02SAndroid Build Coastguard Worker  @http_actions.proto_action(
39*14675a02SAndroid Build Coastguard Worker      service='google.internal.federatedcompute.v1.EligibilityEvalTasks',
40*14675a02SAndroid Build Coastguard Worker      method='RequestEligibilityEvalTask')
41*14675a02SAndroid Build Coastguard Worker  def handle_proto_action(self, *args, **kwargs):
42*14675a02SAndroid Build Coastguard Worker    return self.proto_action(*args, **kwargs)
43*14675a02SAndroid Build Coastguard Worker
44*14675a02SAndroid Build Coastguard Worker  @http_actions.http_action(method='get', pattern='/get/{arg1}/{arg2}')
45*14675a02SAndroid Build Coastguard Worker  def handle_get_action(self, *args, **kwargs):
46*14675a02SAndroid Build Coastguard Worker    return self.get_action(*args, **kwargs)
47*14675a02SAndroid Build Coastguard Worker
48*14675a02SAndroid Build Coastguard Worker  @http_actions.http_action(method='post', pattern='/post/{arg1}/{arg2}')
49*14675a02SAndroid Build Coastguard Worker  def handle_post_action(self, *args, **kwargs):
50*14675a02SAndroid Build Coastguard Worker    return self.post_action(*args, **kwargs)
51*14675a02SAndroid Build Coastguard Worker
52*14675a02SAndroid Build Coastguard Worker
53*14675a02SAndroid Build Coastguard Workerclass TestHttpServer(http.server.HTTPServer):
54*14675a02SAndroid Build Coastguard Worker  pass
55*14675a02SAndroid Build Coastguard Worker
56*14675a02SAndroid Build Coastguard Worker
57*14675a02SAndroid Build Coastguard Workerclass HttpActionsTest(absltest.TestCase):
58*14675a02SAndroid Build Coastguard Worker
59*14675a02SAndroid Build Coastguard Worker  def setUp(self):
60*14675a02SAndroid Build Coastguard Worker    super().setUp()
61*14675a02SAndroid Build Coastguard Worker    self.service = TestService()
62*14675a02SAndroid Build Coastguard Worker    handler = http_actions.create_handler(self.service)
63*14675a02SAndroid Build Coastguard Worker    self._httpd = TestHttpServer(('localhost', 0), handler)
64*14675a02SAndroid Build Coastguard Worker    self._server_thread = threading.Thread(
65*14675a02SAndroid Build Coastguard Worker        target=self._httpd.serve_forever, daemon=True)
66*14675a02SAndroid Build Coastguard Worker    self._server_thread.start()
67*14675a02SAndroid Build Coastguard Worker    self.conn = http.client.HTTPConnection(
68*14675a02SAndroid Build Coastguard Worker        self._httpd.server_name, port=self._httpd.server_port)
69*14675a02SAndroid Build Coastguard Worker
70*14675a02SAndroid Build Coastguard Worker  def tearDown(self):
71*14675a02SAndroid Build Coastguard Worker    self._httpd.shutdown()
72*14675a02SAndroid Build Coastguard Worker    self._server_thread.join()
73*14675a02SAndroid Build Coastguard Worker    self._httpd.server_close()
74*14675a02SAndroid Build Coastguard Worker    super().tearDown()
75*14675a02SAndroid Build Coastguard Worker
76*14675a02SAndroid Build Coastguard Worker  def test_not_found(self):
77*14675a02SAndroid Build Coastguard Worker    self.conn.request('GET', '/no-match')
78*14675a02SAndroid Build Coastguard Worker    self.assertEqual(self.conn.getresponse().status, http.HTTPStatus.NOT_FOUND)
79*14675a02SAndroid Build Coastguard Worker
80*14675a02SAndroid Build Coastguard Worker  def test_proto_success(self):
81*14675a02SAndroid Build Coastguard Worker    expected_response = (
82*14675a02SAndroid Build Coastguard Worker        eligibility_eval_tasks_pb2.EligibilityEvalTaskResponse(
83*14675a02SAndroid Build Coastguard Worker            session_id='test'))
84*14675a02SAndroid Build Coastguard Worker    self.service.proto_action.return_value = expected_response
85*14675a02SAndroid Build Coastguard Worker
86*14675a02SAndroid Build Coastguard Worker    request = eligibility_eval_tasks_pb2.EligibilityEvalTaskRequest(
87*14675a02SAndroid Build Coastguard Worker        client_version=common_pb2.ClientVersion(version_code='test123'))
88*14675a02SAndroid Build Coastguard Worker    self.conn.request(
89*14675a02SAndroid Build Coastguard Worker        'POST',
90*14675a02SAndroid Build Coastguard Worker        '/v1/eligibilityevaltasks/test%2Fpopulation:request?%24alt=proto',
91*14675a02SAndroid Build Coastguard Worker        request.SerializeToString())
92*14675a02SAndroid Build Coastguard Worker    response = self.conn.getresponse()
93*14675a02SAndroid Build Coastguard Worker    self.assertEqual(response.status, http.HTTPStatus.OK)
94*14675a02SAndroid Build Coastguard Worker    response_proto = (
95*14675a02SAndroid Build Coastguard Worker        eligibility_eval_tasks_pb2.EligibilityEvalTaskResponse.FromString(
96*14675a02SAndroid Build Coastguard Worker            response.read()))
97*14675a02SAndroid Build Coastguard Worker    self.assertEqual(response_proto, expected_response)
98*14675a02SAndroid Build Coastguard Worker    # `population_name` should be set from the URL.
99*14675a02SAndroid Build Coastguard Worker    request.population_name = 'test/population'
100*14675a02SAndroid Build Coastguard Worker    self.service.proto_action.assert_called_once_with(request)
101*14675a02SAndroid Build Coastguard Worker
102*14675a02SAndroid Build Coastguard Worker  def test_proto_error(self):
103*14675a02SAndroid Build Coastguard Worker    self.service.proto_action.side_effect = http_actions.HttpError(
104*14675a02SAndroid Build Coastguard Worker        code=http.HTTPStatus.UNAUTHORIZED)
105*14675a02SAndroid Build Coastguard Worker
106*14675a02SAndroid Build Coastguard Worker    self.conn.request(
107*14675a02SAndroid Build Coastguard Worker        'POST',
108*14675a02SAndroid Build Coastguard Worker        '/v1/eligibilityevaltasks/test%2Fpopulation:request?%24alt=proto', b'')
109*14675a02SAndroid Build Coastguard Worker    response = self.conn.getresponse()
110*14675a02SAndroid Build Coastguard Worker    self.assertEqual(response.status, http.HTTPStatus.UNAUTHORIZED)
111*14675a02SAndroid Build Coastguard Worker
112*14675a02SAndroid Build Coastguard Worker  def test_proto_with_invalid_payload(self):
113*14675a02SAndroid Build Coastguard Worker    self.conn.request(
114*14675a02SAndroid Build Coastguard Worker        'POST',
115*14675a02SAndroid Build Coastguard Worker        '/v1/eligibilityevaltasks/test%2Fpopulation:request?%24alt=proto',
116*14675a02SAndroid Build Coastguard Worker        b'invalid')
117*14675a02SAndroid Build Coastguard Worker    response = self.conn.getresponse()
118*14675a02SAndroid Build Coastguard Worker    self.assertEqual(response.status, http.HTTPStatus.BAD_REQUEST)
119*14675a02SAndroid Build Coastguard Worker
120*14675a02SAndroid Build Coastguard Worker  def test_proto_with_gzip_encoding(self):
121*14675a02SAndroid Build Coastguard Worker    self.service.proto_action.return_value = (
122*14675a02SAndroid Build Coastguard Worker        eligibility_eval_tasks_pb2.EligibilityEvalTaskResponse())
123*14675a02SAndroid Build Coastguard Worker
124*14675a02SAndroid Build Coastguard Worker    request = eligibility_eval_tasks_pb2.EligibilityEvalTaskRequest(
125*14675a02SAndroid Build Coastguard Worker        client_version=common_pb2.ClientVersion(version_code='test123'))
126*14675a02SAndroid Build Coastguard Worker    self.conn.request('POST',
127*14675a02SAndroid Build Coastguard Worker                      '/v1/eligibilityevaltasks/test:request?%24alt=proto',
128*14675a02SAndroid Build Coastguard Worker                      gzip.compress(request.SerializeToString()),
129*14675a02SAndroid Build Coastguard Worker                      {'Content-Encoding': 'gzip'})
130*14675a02SAndroid Build Coastguard Worker    self.assertEqual(self.conn.getresponse().status, http.HTTPStatus.OK)
131*14675a02SAndroid Build Coastguard Worker    request.population_name = 'test'
132*14675a02SAndroid Build Coastguard Worker    self.service.proto_action.assert_called_once_with(request)
133*14675a02SAndroid Build Coastguard Worker
134*14675a02SAndroid Build Coastguard Worker  def test_proto_with_invalid_gzip_encoding(self):
135*14675a02SAndroid Build Coastguard Worker    self.conn.request('POST',
136*14675a02SAndroid Build Coastguard Worker                      '/v1/eligibilityevaltasks/test:request?%24alt=proto',
137*14675a02SAndroid Build Coastguard Worker                      b'invalid', {'Content-Encoding': 'gzip'})
138*14675a02SAndroid Build Coastguard Worker    response = self.conn.getresponse()
139*14675a02SAndroid Build Coastguard Worker    self.assertEqual(response.status, http.HTTPStatus.BAD_REQUEST)
140*14675a02SAndroid Build Coastguard Worker
141*14675a02SAndroid Build Coastguard Worker  def test_proto_with_unsupport_encoding(self):
142*14675a02SAndroid Build Coastguard Worker    self.conn.request('POST',
143*14675a02SAndroid Build Coastguard Worker                      '/v1/eligibilityevaltasks/test:request?%24alt=proto', b'',
144*14675a02SAndroid Build Coastguard Worker                      {'Content-Encoding': 'compress'})
145*14675a02SAndroid Build Coastguard Worker    self.assertEqual(self.conn.getresponse().status,
146*14675a02SAndroid Build Coastguard Worker                     http.HTTPStatus.BAD_REQUEST)
147*14675a02SAndroid Build Coastguard Worker    self.service.proto_action.assert_not_called()
148*14675a02SAndroid Build Coastguard Worker
149*14675a02SAndroid Build Coastguard Worker  def test_get_success(self):
150*14675a02SAndroid Build Coastguard Worker    self.service.get_action.return_value = http_actions.HttpResponse(
151*14675a02SAndroid Build Coastguard Worker        body=b'body',
152*14675a02SAndroid Build Coastguard Worker        headers={
153*14675a02SAndroid Build Coastguard Worker            'Content-Length': 4,
154*14675a02SAndroid Build Coastguard Worker            'Content-Type': 'application/x-test',
155*14675a02SAndroid Build Coastguard Worker        })
156*14675a02SAndroid Build Coastguard Worker
157*14675a02SAndroid Build Coastguard Worker    self.conn.request('GET', '/get/foo/bar')
158*14675a02SAndroid Build Coastguard Worker    response = self.conn.getresponse()
159*14675a02SAndroid Build Coastguard Worker    self.assertEqual(response.status, http.HTTPStatus.OK)
160*14675a02SAndroid Build Coastguard Worker    self.assertEqual(response.headers['Content-Length'], '4')
161*14675a02SAndroid Build Coastguard Worker    self.assertEqual(response.headers['Content-Type'], 'application/x-test')
162*14675a02SAndroid Build Coastguard Worker    self.assertEqual(response.read(), b'body')
163*14675a02SAndroid Build Coastguard Worker    self.service.get_action.assert_called_once_with(b'', arg1='foo', arg2='bar')
164*14675a02SAndroid Build Coastguard Worker
165*14675a02SAndroid Build Coastguard Worker  def test_get_error(self):
166*14675a02SAndroid Build Coastguard Worker    self.service.get_action.side_effect = http_actions.HttpError(
167*14675a02SAndroid Build Coastguard Worker        code=http.HTTPStatus.UNAUTHORIZED)
168*14675a02SAndroid Build Coastguard Worker
169*14675a02SAndroid Build Coastguard Worker    self.conn.request('GET', '/get/foo/bar')
170*14675a02SAndroid Build Coastguard Worker    self.assertEqual(self.conn.getresponse().status,
171*14675a02SAndroid Build Coastguard Worker                     http.HTTPStatus.UNAUTHORIZED)
172*14675a02SAndroid Build Coastguard Worker
173*14675a02SAndroid Build Coastguard Worker  def test_post_success(self):
174*14675a02SAndroid Build Coastguard Worker    self.service.post_action.return_value = http_actions.HttpResponse(
175*14675a02SAndroid Build Coastguard Worker        body=b'body',
176*14675a02SAndroid Build Coastguard Worker        headers={
177*14675a02SAndroid Build Coastguard Worker            'Content-Length': 4,
178*14675a02SAndroid Build Coastguard Worker            'Content-Type': 'application/x-test',
179*14675a02SAndroid Build Coastguard Worker        })
180*14675a02SAndroid Build Coastguard Worker
181*14675a02SAndroid Build Coastguard Worker    self.conn.request('POST', '/post/foo/bar', b'request-body')
182*14675a02SAndroid Build Coastguard Worker    response = self.conn.getresponse()
183*14675a02SAndroid Build Coastguard Worker    self.assertEqual(response.status, http.HTTPStatus.OK)
184*14675a02SAndroid Build Coastguard Worker    self.assertEqual(response.headers['Content-Length'], '4')
185*14675a02SAndroid Build Coastguard Worker    self.assertEqual(response.headers['Content-Type'], 'application/x-test')
186*14675a02SAndroid Build Coastguard Worker    self.assertEqual(response.read(), b'body')
187*14675a02SAndroid Build Coastguard Worker    self.service.post_action.assert_called_once_with(
188*14675a02SAndroid Build Coastguard Worker        b'request-body', arg1='foo', arg2='bar')
189*14675a02SAndroid Build Coastguard Worker
190*14675a02SAndroid Build Coastguard Worker  def test_post_error(self):
191*14675a02SAndroid Build Coastguard Worker    self.service.post_action.side_effect = http_actions.HttpError(
192*14675a02SAndroid Build Coastguard Worker        code=http.HTTPStatus.UNAUTHORIZED)
193*14675a02SAndroid Build Coastguard Worker
194*14675a02SAndroid Build Coastguard Worker    self.conn.request('POST', '/post/foo/bar', b'request-body')
195*14675a02SAndroid Build Coastguard Worker    self.assertEqual(self.conn.getresponse().status,
196*14675a02SAndroid Build Coastguard Worker                     http.HTTPStatus.UNAUTHORIZED)
197*14675a02SAndroid Build Coastguard Worker
198*14675a02SAndroid Build Coastguard Worker  def test_post_with_gzip_encoding(self):
199*14675a02SAndroid Build Coastguard Worker    self.service.post_action.return_value = http_actions.HttpResponse(body=b'')
200*14675a02SAndroid Build Coastguard Worker
201*14675a02SAndroid Build Coastguard Worker    self.conn.request('POST', '/post/foo/bar', gzip.compress(b'request-body'),
202*14675a02SAndroid Build Coastguard Worker                      {'Content-Encoding': 'gzip'})
203*14675a02SAndroid Build Coastguard Worker    self.assertEqual(self.conn.getresponse().status, http.HTTPStatus.OK)
204*14675a02SAndroid Build Coastguard Worker    self.service.post_action.assert_called_once_with(
205*14675a02SAndroid Build Coastguard Worker        b'request-body', arg1='foo', arg2='bar')
206*14675a02SAndroid Build Coastguard Worker
207*14675a02SAndroid Build Coastguard Worker  def test_post_with_unsupport_encoding(self):
208*14675a02SAndroid Build Coastguard Worker    self.conn.request('POST', '/post/foo/bar', b'',
209*14675a02SAndroid Build Coastguard Worker                      {'Content-Encoding': 'compress'})
210*14675a02SAndroid Build Coastguard Worker    self.assertEqual(self.conn.getresponse().status,
211*14675a02SAndroid Build Coastguard Worker                     http.HTTPStatus.BAD_REQUEST)
212*14675a02SAndroid Build Coastguard Worker    self.service.post_action.assert_not_called()
213*14675a02SAndroid Build Coastguard Worker
214*14675a02SAndroid Build Coastguard Worker
215*14675a02SAndroid Build Coastguard Workerif __name__ == '__main__':
216*14675a02SAndroid Build Coastguard Worker  absltest.main()
217