Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invoke Function Without Parameters #604

Merged
merged 7 commits into from
Aug 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions samcli/commands/local/invoke/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,41 +26,50 @@
Invoking a Lambda function using input from stdin
$ echo '{"message": "Hey, are you there?" }' | sam local invoke "HelloWorldFunction" \n
"""
STDIN_FILE_NAME = "-"


@click.command("invoke", help=HELP_TEXT, short_help="Invokes a local Lambda function once.")
@click.option("--event", '-e',
type=click.Path(),
default="-", # Defaults to stdin
default=STDIN_FILE_NAME, # Defaults to stdin
help="JSON file containing event data passed to the Lambda function during invoke. If this option "
"is not specified, we will default to reading JSON from stdin")
@click.option("--no-event", is_flag=True, default=False, help="Invoke Function with an empty event")
@invoke_common_options
@cli_framework_options
@click.argument('function_identifier', required=False)
@pass_context
def cli(ctx, function_identifier, template, event, env_vars, debug_port, debug_args, debugger_path,
def cli(ctx, function_identifier, template, event, no_event, env_vars, debug_port, debug_args, debugger_path,
docker_volume_basedir, docker_network, log_file, skip_pull_image, profile, region):

# All logic must be implemented in the ``do_cli`` method. This helps with easy unit testing

do_cli(ctx, function_identifier, template, event, env_vars, debug_port, debug_args, debugger_path,
do_cli(ctx, function_identifier, template, event, no_event, env_vars, debug_port, debug_args, debugger_path,
docker_volume_basedir, docker_network, log_file, skip_pull_image, profile, region) # pragma: no cover


def do_cli(ctx, function_identifier, template, event, env_vars, debug_port, debug_args, # pylint: disable=R0914
debugger_path, docker_volume_basedir, docker_network, log_file, skip_pull_image, profile, region):
def do_cli(ctx, function_identifier, template, event, no_event, env_vars, debug_port, # pylint: disable=R0914
debug_args, debugger_path, docker_volume_basedir, docker_network, log_file, skip_pull_image, profile,
region):
"""
Implementation of the ``cli`` method, just separated out for unit testing purposes
"""

LOG.debug("local invoke command is called")

event_data = _get_event(event)
if no_event and event != STDIN_FILE_NAME:
# Do not know what the user wants. no_event and event both passed in.
raise UserException("no_event and event cannot be used together. Please provide only one.")

if no_event:
event_data = "{}"
else:
event_data = _get_event(event)

# Pass all inputs to setup necessary context to invoke function locally.
# Handler exception raised by the processor for invalid args and print errors
try:

with InvokeContext(template_file=template,
function_identifier=function_identifier,
env_vars_file=env_vars,
Expand Down Expand Up @@ -94,7 +103,7 @@ def _get_event(event_file_name):
:return string: Contents of the event file or stdin
"""

if event_file_name == "-":
if event_file_name == STDIN_FILE_NAME:
# If event is empty, listen to stdin for event data until EOF
LOG.info("Reading invoke payload from stdin (you can also pass it from file with --event)")

Expand Down
22 changes: 22 additions & 0 deletions tests/integration/local/invoke/test_integrations_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,25 @@ def test_invoke_when_function_writes_stderr(self):
process_stderr = b"".join(process.stderr.readlines()).strip()

self.assertIn("Docker Lambda is writing to stderr", process_stderr.decode('utf-8'))

def test_invoke_returns_expected_result_when_no_event_given(self):
command_list = self.get_command_list("EchoEventFunction", template_path=self.template_path)
command_list.append("--no-event")
process = Popen(command_list, stdout=PIPE)
return_code = process.wait()
process_stdout = b"".join(process.stdout.readlines()).strip()

self.assertEquals(return_code, 0)
self.assertEquals("{}", process_stdout.decode('utf-8'))

def test_invoke_raises_exception_with_noargs_and_event(self):
command_list = self.get_command_list("HelloWorldLambdaFunction",
template_path=self.template_path,
event_path=self.event_path)
command_list.append("--no-event")
process = Popen(command_list, stderr=PIPE)
process.wait()

process_stderr = b"".join(process.stderr.readlines()).strip()
error_output = process_stderr.decode('utf-8')
self.assertIn("no_event and event cannot be used together. Please provide only one.", error_output)
76 changes: 75 additions & 1 deletion tests/unit/commands/local/invoke/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from samcli.commands.exceptions import UserException
from samcli.commands.local.invoke.cli import do_cli as invoke_cli, _get_event as invoke_cli_get_event

STDIN_FILE_NAME = "-"


class TestCli(TestCase):

Expand All @@ -27,6 +29,7 @@ def setUp(self):
self.log_file = "logfile"
self.skip_pull_image = True
self.profile = "profile"
self.no_event = False
self.region = "region"

@patch("samcli.commands.local.invoke.cli.InvokeContext")
Expand All @@ -43,6 +46,7 @@ def test_cli_must_setup_context_and_invoke(self, get_event_mock, InvokeContextMo
function_identifier=self.function_id,
template=self.template,
event=self.eventfile,
no_event=self.no_event,
env_vars=self.env_vars,
debug_port=self.debug_port,
debug_args=self.debug_args,
Expand Down Expand Up @@ -73,6 +77,74 @@ def test_cli_must_setup_context_and_invoke(self, get_event_mock, InvokeContextMo
stderr=context_mock.stderr)
get_event_mock.assert_called_with(self.eventfile)

@patch("samcli.commands.local.invoke.cli.InvokeContext")
@patch("samcli.commands.local.invoke.cli._get_event")
def test_cli_must_invoke_with_no_event(self, get_event_mock, InvokeContextMock):
self.no_event = True
# Mock the __enter__ method to return a object inside a context manager
context_mock = Mock()
InvokeContextMock.return_value.__enter__.return_value = context_mock
invoke_cli(ctx=None,
function_identifier=self.function_id,
template=self.template,
event=STDIN_FILE_NAME,
no_event=self.no_event,
env_vars=self.env_vars,
debug_port=self.debug_port,
debug_args=self.debug_args,
debugger_path=self.debugger_path,
docker_volume_basedir=self.docker_volume_basedir,
docker_network=self.docker_network,
log_file=self.log_file,
skip_pull_image=self.skip_pull_image,
profile=self.profile,
region=self.region)

InvokeContextMock.assert_called_with(template_file=self.template,
function_identifier=self.function_id,
env_vars_file=self.env_vars,
docker_volume_basedir=self.docker_volume_basedir,
docker_network=self.docker_network,
log_file=self.log_file,
skip_pull_image=self.skip_pull_image,
aws_profile=self.profile,
debug_port=self.debug_port,
debug_args=self.debug_args,
debugger_path=self.debugger_path,
aws_region=self.region)

context_mock.local_lambda_runner.invoke.assert_called_with(context_mock.function_name,
event="{}",
stdout=context_mock.stdout,
stderr=context_mock.stderr)
get_event_mock.assert_not_called()

@patch("samcli.commands.local.invoke.cli.InvokeContext")
@patch("samcli.commands.local.invoke.cli._get_event")
def test_must_raise_user_exception_on_no_event_and_event(self, get_event_mock, InvokeContextMock):
self.no_event = True

with self.assertRaises(UserException) as ex_ctx:

invoke_cli(ctx=None,
function_identifier=self.function_id,
template=self.template,
event=self.eventfile,
no_event=self.no_event,
env_vars=self.env_vars,
debug_port=self.debug_port,
debug_args=self.debug_args,
debugger_path=self.debugger_path,
docker_volume_basedir=self.docker_volume_basedir,
docker_network=self.docker_network,
log_file=self.log_file,
skip_pull_image=self.skip_pull_image,
profile=self.profile,
region=self.region)

msg = str(ex_ctx.exception)
self.assertEquals(msg, "no_event and event cannot be used together. Please provide only one.")

@patch("samcli.commands.local.invoke.cli.InvokeContext")
@patch("samcli.commands.local.invoke.cli._get_event")
def test_must_raise_user_exception_on_function_not_found(self, get_event_mock, InvokeContextMock):
Expand All @@ -91,6 +163,7 @@ def test_must_raise_user_exception_on_function_not_found(self, get_event_mock, I
function_identifier=self.function_id,
template=self.template,
event=self.eventfile,
no_event=self.no_event,
env_vars=self.env_vars,
debug_port=self.debug_port,
debug_args=self.debug_args,
Expand Down Expand Up @@ -119,6 +192,7 @@ def test_must_raise_user_exception_on_invalid_sam_template(self, get_event_mock,
function_identifier=self.function_id,
template=self.template,
event=self.eventfile,
no_event=self.no_event,
env_vars=self.env_vars,
debug_port=self.debug_port,
debug_args=self.debug_args,
Expand All @@ -137,7 +211,7 @@ def test_must_raise_user_exception_on_invalid_sam_template(self, get_event_mock,
class TestGetEvent(TestCase):

@parameterized.expand([
param("-"),
param(STDIN_FILE_NAME),
param("somefile")
])
@patch("samcli.commands.local.invoke.cli.click")
Expand Down