0

We are deploying the code in CI/CD fashion via Terraform. So, we have a lambda function under which I have written a code for retrieving the specific secret creds.

Below is my lambda code:

logger = get_provisioner_logger()
session = boto3.session.Session()
client = session.client(
    service_name="secretsmanager",
    region_name=""
)


def lambda_handler(event, context):
    logger.info("lambda invoked with event: " + str(event))
    domain_name = "db-creds"
    secret_data = getCredentials(domain_name)
    acc = secret_data["acc"]
    user = secret_data["user"]
    password = secret_data["pass"]
    #....
    #here we are invoking other methods where we are passing the above creds
    #....
    return handler.handle(event)


def getCredentials(domain_name):
    try:
        response = client.get_secret_value(SecretId=domain_name)
        result = json.loads(response['SecretString'])
        print(result)
        logger.info("Got value for secret %s.", domain_name)
        return result
    except UnableToRetrieveDetails as e:
        logger.error("unable to retrieve secret details due to ", str(e))
        raise e

Now, I have written a test case where I am trying to mock the client and trying to fake the return value of the response but am unable to do so. Below is my code:

from unittest import TestCase
from unittest.mock import patch
from api.lambda_handler import getCredentials
import json

@patch('api.lambda_handler.client')
class TestSecretManagerMethod(TestCase):

    def test_get_secret_creds(self, sm):
        sm.response.return_value = {"secret":"gotsomecreds"}
        actual = getCredentials("db-creds")
        self.assertEqual(actual, "hey")

It gives me below error:

 raise TypeError(f'the JSON object must be str, bytes or bytearray, '
TypeError: the JSON object must be str, bytes or bytearray, not MagicMock. 

What exactly am I missing here?

whatsinthename
  • 1,828
  • 20
  • 59

1 Answers1

2

Kevin's answer is on the right track.

The problem is that you're mixing up what needs to be patched and returned.

In your code you're saying: If you call client.response() then return {"secret":"gotsomecreds"}. The problem is that this is not what's happening in your code.

You're actually calling client.get_secret_value(something) and that's what needs to be patched:

@patch('api.lambda_handler.client')
class TestSecretManagerMethod(unittest.TestCase):
    def test_get_secret_creds(self, sm):
        sm.get_secret_value.return_value = {
            "SecretString": '{"secret": "gotsomecreds"}',
        }
        actual = getCredentials("db-creds")
        self.assertEqual(actual, {"secret": "gotsomecreds"})

I also took the liberty of fixing the assertion, because the way you set it up "hey" won't be returned.

As an aside, I highly recommend you check out the moto project. It provides mocks around most of the AWS API and will save you a lot of work.

Maurice
  • 11,482
  • 2
  • 25
  • 45
  • 1
    Yeah, the `hey` was passed in order to ensure that I get the actual result at least. I knew it will fail because the actual won't match the expected. But yeah I got it where I went wrong. Thanks a lot, @Maurice. – whatsinthename Dec 21 '22 at 10:24