[Local Agent] b/282592569 Retrieve commission RPC request.

functional proof: https://paste.googleplex.com/5265599176376320
Test coverage: https://paste.googleplex.com/4949417630105600
Design Doc: go/ghts:dd:matter-enhance

Change-Id: I7ffe0b50f01dbdd69b025eb53948c653bd0959eb
diff --git a/local_agent/local_agent.py b/local_agent/local_agent.py
index 25398d1..5400fa1 100644
--- a/local_agent/local_agent.py
+++ b/local_agent/local_agent.py
@@ -50,6 +50,7 @@
     '~/.config/google/local_agent_config.ini')
 _START_TEST_SUITE_METHOD = 'startTestSuite'
 _END_TEST_SUITE_METHOD = 'endTestSuite'
+_COMMISSION_TO_GOOGLE_FABRIC_METHOD = 'commissionToGoogleFabric'
 _USER_CONFIG_ROOT_KEY = 'ServerConfig'
 _USER_CONFIG_AMS_HOST = 'AMS_HOST'
 _USER_CONFIG_AMS_PORT = 'AMS_PORT'
@@ -67,6 +68,8 @@
     START_TEST_SUITE = enum.auto()
     END_TEST_SUITE = enum.auto()
     DEVICE_QUERY_CONTROL = enum.auto()
+    COMMISSION_TO_GOOGLE_FABRIC = enum.auto()
+
 
 # ======================== Module level functions ========================== #
 def rpc_request_type(method: str) -> RpcRequestType:
@@ -82,6 +85,8 @@
         return RpcRequestType.START_TEST_SUITE
     elif method == _END_TEST_SUITE_METHOD:
         return RpcRequestType.END_TEST_SUITE
+    elif method == _COMMISSION_TO_GOOGLE_FABRIC_METHOD:
+        return RpcRequestType.COMMISSION_TO_GOOGLE_FABRIC
     else:
         return RpcRequestType.DEVICE_QUERY_CONTROL
 # ========================================================================== #
@@ -323,6 +328,10 @@
             elif req_type == RpcRequestType.DEVICE_QUERY_CONTROL:
                 resp = self._translator.dispatch_to_cmd_handler(rpc_request)
 
+            elif req_type == RpcRequestType.COMMISSION_TO_GOOGLE_FABRIC:
+                # TODO(b/282592569): Move commission_to_google_fabric out of translator.
+                resp = self._translator.commission_to_google_fabric(rpc_request)
+
             else:
                 raise errors.InvalidRPCError(
                     f'Invalid RPC request type {req_type}.')
diff --git a/local_agent/tests/unit_tests/test_local_agent.py b/local_agent/tests/unit_tests/test_local_agent.py
index 184685b..6d05c94 100644
--- a/local_agent/tests/unit_tests/test_local_agent.py
+++ b/local_agent/tests/unit_tests/test_local_agent.py
@@ -39,9 +39,11 @@
 _FAKE_RPC_RESPONSE = {'fake': 'response'}
 _FAKE_RPC_ID = 'fake-rpc-id'
 _FAKE_ERROR_MSG = 'fake-error-msg'
+_FAKE_COMMISSION_ERROR_MSG = 'Unable to commission the device.'
 _FAKE_ARTIFACTS_DIR = 'fake-artifacts-dir'
 _START_TEST_SUITE = 'startTestSuite'
 _END_TEST_SUITE = 'endTestSuite'
+_COMMISSION_TO_GOOGLE_FABRIC = 'commissionToGoogleFabric'
 _LOCK_DEVICE = 'setLock'
 _FAKE_CONTROLLER_PACKAGE = 'FAKE_CONTROLLER_PACKAGE'
 #############################################################################
@@ -612,6 +614,35 @@
         self.assertEqual(_FAKE_RPC_RESPONSE, rpc_response)
         mock_dispatch.assert_called_once_with(fake_rpc_request)
 
+    @mock.patch.object(
+        translation_layer.TranslationLayer, 'commission_to_google_fabric')
+    def test_handle_rpc_request_commission_to_google_fabric_on_success(self, mock_commission):
+        """Verifies handle_rpc_request to commission a device to google fabric on success."""
+        mock_commission.return_value = _FAKE_RPC_RESPONSE
+        fake_rpc_request = {'method': _COMMISSION_TO_GOOGLE_FABRIC}
+
+        rpc_response = self.proc._handle_rpc_request(fake_rpc_request)
+
+        self.assertEqual(_FAKE_RPC_RESPONSE, rpc_response)
+        mock_commission.assert_called_once_with(fake_rpc_request)
+
+    @mock.patch.object(translation_layer.TranslationLayer, 'commission_to_google_fabric')
+    def test_handle_rpc_request_returns_a_commissioning_error(
+        self, mock_commission
+    ):
+        """Verifies handle_rpc_request returns a commissioning error."""
+        mock_commission.side_effect = errors.InvalidRPCError(
+            _FAKE_COMMISSION_ERROR_MSG
+        )
+        fake_rpc_request = {
+            'id': _FAKE_RPC_ID,
+            'method': _COMMISSION_TO_GOOGLE_FABRIC,
+        }
+
+        rpc_response = self.proc._handle_rpc_request(fake_rpc_request)
+
+        self.assertIn(_FAKE_COMMISSION_ERROR_MSG, rpc_response['error']['message'])
+
     @mock.patch.object(local_agent, 'rpc_request_type', return_value='')
     def test_handle_rpc_request_invalid_rpc_type(self, mock_req_type):
         """Verifies handle_rpc_request on failure with invalid RPC type."""
diff --git a/local_agent/tests/unit_tests/test_translation_layer.py b/local_agent/tests/unit_tests/test_translation_layer.py
index ddfa844..f292b9c 100644
--- a/local_agent/tests/unit_tests/test_translation_layer.py
+++ b/local_agent/tests/unit_tests/test_translation_layer.py
@@ -28,8 +28,10 @@
 _FAKE_DEVICE_TYPE = 'fake-device-type'
 _FAKE_DEVICE_ID = 'fake-device-id'
 _FAKE_CAPABILITY = 'fake-device-capability'
+_FAKE_PAIRING_CODE = 'fake-pairing-code'
 _SET_ON = 'setOn'
 _SET_LOCK = 'setLock'
+_COMMISSION_TO_GOOGLE_FABRIC = 'commissionToGoogleFabric'
 ##############################################################################
 
 
@@ -218,6 +220,21 @@
         self.mock_client.send_rpc_response.assert_called_once()
         self.assertTrue(self.translator.is_rpc_timeout(_FAKE_DEVICE_ID))
 
+    def test_commission_to_google_fabric_on_failure(self):
+        """Verifies commission_to_google_fabric throws an error when pairing code not found."""
+        invalid_rpc_request = rpc_request(_COMMISSION_TO_GOOGLE_FABRIC, {})
+        error_msg = 'Invalid rpc command, no pairingCode in params was found.'
+        with self.assertRaisesRegex(agent_errors.InvalidRPCError, error_msg):
+            self.translator.commission_to_google_fabric(invalid_rpc_request)
+
+    def test_commission_to_google_fabric_on_success(self):
+        """Verifies commission_to_google_fabric on success when pairing code is provided."""
+        valid_rpc_request = rpc_request(
+            _COMMISSION_TO_GOOGLE_FABRIC, {'pairingCode': _FAKE_PAIRING_CODE})
+        response = self.translator.commission_to_google_fabric(valid_rpc_request)
+
+        # Id is fake and it was made by `rpc_request` wrapper.
+        self.assertEqual(response, {'id': 0, 'jsonrpc': '2.0', 'result': {}})
 
 if __name__ == '__main__':
     unittest.main(failfast=True)
diff --git a/local_agent/translation_layer/translation_layer.py b/local_agent/translation_layer/translation_layer.py
index eeb02e6..5da05fe 100644
--- a/local_agent/translation_layer/translation_layer.py
+++ b/local_agent/translation_layer/translation_layer.py
@@ -280,3 +280,26 @@
                             'message': err_mesg}
                         self._ams_client.send_rpc_response(err_resp)
                         self._timeout_rpc.add(rpc_id)
+
+    def commission_to_google_fabric(self, rpc_request: dict[str, dict[str, str]]) -> dict[str, Any]:
+        """Subroutine for handling COMMISSION_TO_GOOGLE_FABRIC command.
+
+        Args:
+            rpc_request: JSON-RPC request.
+
+        Raises:
+            InvalidRPCError: Invalid RPC.
+
+        Returns:
+            RPC response.
+        """
+        pairing_code = rpc_request['params'].get('pairingCode')
+        if pairing_code is None:
+            raise agent_errors.InvalidRPCError(
+                'Invalid rpc command, no pairingCode in params was found.')
+
+        request_id = rpc_request['id']
+        # TODO(b/282592569): Use this pairing code to commission a matter device on GHA.
+        logger.info(f'Completed request for {request_id}, pairingCode: {pairing_code}')
+
+        return {'id': request_id, 'jsonrpc': '2.0', 'result': {}}