예제 #1
0
        public async Task Can_Invoke_Function_When_The_Skill_Is_Not_Linked()
        {
            // Arrange
            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest   request = CreateIntentRequestWithToken(accessToken: null);
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            ResponseBody response = AssertResponse(actual);

            response.Card.ShouldNotBeNull();
            response.Card.ShouldBeOfType <LinkAccountCard>();

            response.Reprompt.ShouldBeNull();

            response.OutputSpeech.ShouldNotBeNull();
            response.OutputSpeech.Type.ShouldBe("SSML");

            var ssml = response.OutputSpeech.ShouldBeOfType <SsmlOutputSpeech>();

            ssml.Ssml.ShouldBe("<speak>You need to link your account to be able to ask me about your commute.</speak>");
        }
        public async Task Can_Invoke_Function_For_Invalid_Line(string id)
        {
            // Arrange
            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest   request = CreateIntentForLine(id);
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            ResponseBody response = AssertResponse(actual);

            response.Card.ShouldBeNull();
            response.OutputSpeech.ShouldNotBeNull();
            response.Reprompt.ShouldNotBeNull();

            var speeches = new[] { response.OutputSpeech, response.Reprompt.OutputSpeech };

            foreach (var speech in speeches)
            {
                speech.Type.ShouldBe("SSML");

                var ssml = speech.ShouldBeOfType <SsmlOutputSpeech>();
                ssml.Ssml.ShouldBe("<speak>Sorry, I am not sure what line you said. You can ask about the status of any tube line, London Overground, the D.L.R. or T.F.L. Rail.</speak>");
            }
        }
        public async Task Can_Invoke_Function_For_Elizabeth_Line(string id)
        {
            // Arrange
            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest   request = CreateIntentForLine(id);
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            ResponseBody response = AssertResponse(actual);

            response.Card.ShouldBeNull();
            response.OutputSpeech.ShouldNotBeNull();
            response.Reprompt.ShouldNotBeNull();

            var speeches = new[] { response.OutputSpeech, response.Reprompt.OutputSpeech };

            foreach (var speech in speeches)
            {
                speech.Type.ShouldBe("SSML");

                var ssml = speech.ShouldBeOfType <SsmlOutputSpeech>();
                ssml.Ssml.ShouldBe("<speak>Sorry, I cannot tell you about the status of the Elizabeth Line yet.</speak>");
            }
        }
예제 #4
0
        public async Task Can_Invoke_Function_When_The_Skill_Token_Is_Invalid()
        {
            // Arrange
            Interceptor.RegisterBundle(Path.Combine("Bundles", "skill-api-invalid-token.json"));

            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest   request = CreateIntentRequestWithToken(accessToken: "invalid-access-token");
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            ResponseBody response = AssertResponse(actual);

            response.Card.ShouldNotBeNull();
            response.Card.ShouldBeOfType <LinkAccountCard>();

            response.Reprompt.ShouldBeNull();

            response.OutputSpeech.ShouldNotBeNull();
            response.OutputSpeech.Type.ShouldBe("SSML");

            var ssml = response.OutputSpeech.ShouldBeOfType <SsmlOutputSpeech>();

            ssml.Ssml.ShouldBe("<speak>It looks like you've disabled account linking. You need to re-link your account to be able to ask me about your commute.</speak>");
        }
        public async Task Can_Invoke_Function()
        {
            // Arrange
            var function = new AlexaFunction();
            await function.InitializeAsync();

            SkillRequest   request = CreateIntentRequest("FooIntent");
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            ResponseBody response = AssertResponse(actual);

            response.Card.ShouldBeNull();
            response.Reprompt.ShouldBeNull();

            response.OutputSpeech.ShouldNotBeNull();
            response.OutputSpeech.Type.ShouldBe("SSML");

            var ssml = response.OutputSpeech.ShouldBeOfType <SsmlOutputSpeech>();

            ssml.Ssml.ShouldBe("<speak>Sorry, I don't understand how to do that.</speak>");
        }
        public async Task Can_Invoke_Function()
        {
            // Arrange
            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest   request = CreateRequest <UnknownRequest>();
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            AssertResponse(actual);
        }
        /// <summary>
        /// Runs the function using a custom runtime as an asynchronous operation.
        /// </summary>
        /// <param name="httpClient">The optional HTTP client to use.</param>
        /// <param name="cancellationToken">The optional cancellation token to use.</param>
        /// <returns>
        /// A <see cref="Task"/> representing the asynchronous operation to run the function.
        /// </returns>
        public static async Task RunAsync(
            HttpClient httpClient = null,
            CancellationToken cancellationToken = default)
        {
            var serializer = new JsonSerializer();
            var function   = new AlexaFunction();

#pragma warning disable CA2000
            using var handlerWrapper = HandlerWrapper.GetHandlerWrapper <SkillRequest, SkillResponse>(function.HandlerAsync, serializer);
            using var bootstrap      = new LambdaBootstrap(httpClient ?? new HttpClient(), handlerWrapper, function.InitializeAsync);
#pragma warning restore CA2000

            await bootstrap.RunAsync(cancellationToken);
        }
        public async Task Can_Invoke_Function_For_Valid_Line(string id)
        {
            // Arrange
            Interceptor.RegisterBundle(Path.Combine("Bundles", "tfl-line-statuses.json"));

            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest   request = CreateIntentForLine(id);
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            AssertLineResponse(actual);
        }
예제 #9
0
        public async Task Cannot_Invoke_Function_If_Application_Id_Incorrect()
        {
            // Arrange
            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest request = CreateIntentRequest("AMAZON.HelpIntent");

            request.Session.Application.ApplicationId = "not-my-skill-id";

            ILambdaContext context = CreateContext();

            // Act
            InvalidOperationException exception = await Assert.ThrowsAsync <InvalidOperationException>(
                async() => await function.HandlerAsync(request, context));

            // Assert
            exception.Message.ShouldBe("Request application Id 'not-my-skill-id' and configured skill Id 'my-skill-id' mismatch.");
        }
예제 #10
0
        public async Task Can_Invoke_Function_For_Different_Severities(
            string id,
            string expected)
        {
            // Arrange
            Interceptor.RegisterBundle(Path.Combine("Bundles", "tfl-line-severities.json"));

            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest   request = CreateIntentForLine(id);
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            AssertLineResponse(actual, expectedSsml: "<speak>" + expected + "</speak>");
        }
예제 #11
0
        public async Task Can_Invoke_Function()
        {
            // Arrange
            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest   request = CreateIntentRequest("AMAZON.CancelIntent");
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            ResponseBody response = AssertResponse(actual);

            response.Card.ShouldBeNull();
            response.OutputSpeech.ShouldBeNull();
            response.Reprompt.ShouldBeNull();
        }
예제 #12
0
        public async Task Can_Invoke_Function_When_There_Is_One_Disruption()
        {
            // Arrange
            Interceptor.RegisterBundle(Path.Combine("Bundles", "tfl-one-disruption.json"));

            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest   request = CreateIntentRequest();
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            AssertResponse(
                actual,
                "<speak><p>There are severe delays on the District Line.</p><p>There is a good service on all other lines.</p></speak>",
                "There are severe delays on the District Line.");
        }
예제 #13
0
        public async Task Can_Invoke_Function_When_There_Are_No_Disruptions()
        {
            // Arrange
            Interceptor.RegisterBundle(Path.Combine("Bundles", "tfl-no-disruptions.json"));

            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest   request = CreateIntentRequest();
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            AssertResponse(
                actual,
                "<speak>There is currently no disruption on the tube, London Overground, the D.L.R. or T.F.L. Rail.</speak>",
                "There is currently no disruption on the tube, London Overground, the DLR or TfL Rail.");
        }
예제 #14
0
        public async Task Can_Invoke_Function_When_There_Are_Multiple_Disruptions()
        {
            // Arrange
            Interceptor.RegisterBundle(Path.Combine("Bundles", "tfl-multiple-disruptions.json"));

            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest   request = CreateIntentRequest();
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            AssertResponse(
                actual,
                "<speak><p>Circle Line: There are minor delays on the Circle Line.</p><p>District Line: There are severe delays on the District Line.</p><p>Hammersmith and City Line: There are minor delays on the Hammersmith and City Line.</p><p>There is a good service on all other lines.</p></speak>",
                "Circle Line: There are minor delays on the Circle Line.\nDistrict Line: There are severe delays on the District Line.\nHammersmith & City Line: There are minor delays on the Hammersmith & City Line.");
        }
예제 #15
0
        public async Task Can_Invoke_Function_When_The_Skill_Is_Linked_And_Has_One_Favorite_Line()
        {
            // Arrange
            Interceptor.RegisterBundle(Path.Combine("Bundles", "skill-api-one-favorite.json"));
            Interceptor.RegisterBundle(Path.Combine("Bundles", "tfl-line-statuses.json"));

            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest   request = CreateIntentRequestWithToken(accessToken: "token-for-one-favorite");
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            AssertResponse(
                actual,
                "<speak>Saturday 19 and Sunday 20 October, no service between Hammersmith / Wimbledon / Kensington Olympia and South Kensington / Edgware Road. Replacement buses operate.</speak>",
                "Saturday 19 and Sunday 20 October, no service between Hammersmith / Wimbledon / Kensington Olympia and South Kensington / Edgware Road. Replacement buses operate.");
        }
예제 #16
0
        public async Task Can_Invoke_Function_When_The_Skill_Is_Linked_And_Has_Two_Favorite_Lines()
        {
            // Arrange
            Interceptor.RegisterBundle(Path.Combine("Bundles", "skill-api-two-favorites.json"));
            Interceptor.RegisterBundle(Path.Combine("Bundles", "tfl-line-statuses.json"));

            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest   request = CreateIntentRequestWithToken(accessToken: "token-for-two-favorites");
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            AssertResponse(
                actual,
                "<speak><p>Northern Line: There is a good service on the Northern line.</p><p>Victoria Line: There is a good service on the Victoria line.</p></speak>",
                "Northern Line: There is a good service on the Northern line.\nVictoria Line: There is a good service on the Victoria line.");
        }
예제 #17
0
        public async Task Can_Invoke_Function_When_The_Skill_Is_Linked_And_Only_The_Elizabeth_Line_Is_A_Favorite()
        {
            // Arrange
            Interceptor.RegisterBundle(Path.Combine("Bundles", "skill-api-elizabeth.json"));
            Interceptor.RegisterBundle(Path.Combine("Bundles", "tfl-line-statuses.json"));

            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest   request = CreateIntentRequestWithToken(accessToken: "token-for-only-elizabeth-line");
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            AssertResponse(
                actual,
                "<speak>You have not selected any favourite lines yet. Visit the London Travel website to set your preferences.</speak>",
                "You have not selected any favourite lines yet. Visit the London Travel website to set your preferences.");
        }
예제 #18
0
        public async Task Can_Invoke_Function_If_Locale_Is_Invalid(string locale)
        {
            // Arrange
            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest request = CreateIntentRequest("AMAZON.HelpIntent");

            request.Request.Locale = locale;

            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            ResponseBody response = AssertResponse(actual, shouldEndSession: false);

            response.OutputSpeech.ShouldNotBeNull();
            response.OutputSpeech.Type.ShouldBe("SSML");
        }
예제 #19
0
        public async Task Cannot_Invoke_Function_With_System_Failure()
        {
            // Arrange
            AlexaFunction function = await CreateFunctionAsync();

            ILambdaContext context = CreateContext();

            var error = new SystemExceptionRequest()
            {
                Error = new Error()
                {
                    Message = "Internal error.",
                    Type    = ErrorType.InternalError,
                },
                ErrorCause = new ErrorCause()
                {
                    requestId = "my-request-id",
                },
            };

            var request = CreateRequest(error);

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            ResponseBody response = AssertResponse(actual);

            response.Card.ShouldBeNull();
            response.Reprompt.ShouldBeNull();

            response.OutputSpeech.ShouldNotBeNull();
            response.OutputSpeech.Type.ShouldBe("SSML");

            var ssml = response.OutputSpeech.ShouldBeOfType <SsmlOutputSpeech>();

            ssml.Ssml.ShouldBe("<speak>Sorry, something went wrong.</speak>");
        }
예제 #20
0
        public async Task Can_Invoke_Function()
        {
            // Arrange
            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest   request = CreateRequest <LaunchRequest>();
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            ResponseBody response = AssertResponse(actual, shouldEndSession: false);

            response.Card.ShouldBeNull();
            response.Reprompt.ShouldBeNull();

            response.OutputSpeech.ShouldNotBeNull();
            response.OutputSpeech.Type.ShouldBe("SSML");

            var ssml = response.OutputSpeech.ShouldBeOfType <SsmlOutputSpeech>();

            ssml.Ssml.ShouldBe("<speak>Welcome to London Travel. You can ask me about disruption or for the status of any tube line, London Overground, the D.L.R. or T.F.L. Rail.</speak>");
        }
예제 #21
0
        public async Task Can_Invoke_Function_When_The_Skill_Api_Fails()
        {
            // Arrange
            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest   request = CreateIntentRequestWithToken(accessToken: "random-access-token");
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            ResponseBody response = AssertResponse(actual);

            response.Card.ShouldBeNull();
            response.Reprompt.ShouldBeNull();

            response.OutputSpeech.ShouldNotBeNull();
            response.OutputSpeech.Type.ShouldBe("SSML");

            var ssml = response.OutputSpeech.ShouldBeOfType <SsmlOutputSpeech>();

            ssml.Ssml.ShouldBe("<speak>Sorry, something went wrong.</speak>");
        }
예제 #22
0
        public async Task Can_Invoke_Function()
        {
            // Arrange
            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest   request = CreateRequest <SessionEndedRequest>();
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            ResponseBody response = AssertResponse(actual);

            response.Card.ShouldBeNull();
            response.Reprompt.ShouldBeNull();

            response.OutputSpeech.ShouldNotBeNull();
            response.OutputSpeech.Type.ShouldBe("SSML");

            var ssml = response.OutputSpeech.ShouldBeOfType <SsmlOutputSpeech>();

            ssml.Ssml.ShouldBe("<speak>Goodbye.</speak>");
        }
예제 #23
0
        public async Task Can_Invoke_Function()
        {
            // Arrange
            AlexaFunction function = await CreateFunctionAsync();

            SkillRequest   request = CreateIntentRequest("AMAZON.HelpIntent");
            ILambdaContext context = CreateContext();

            // Act
            SkillResponse actual = await function.HandlerAsync(request, context);

            // Assert
            ResponseBody response = AssertResponse(actual, shouldEndSession: false);

            response.Card.ShouldBeNull();
            response.Reprompt.ShouldBeNull();

            response.OutputSpeech.ShouldNotBeNull();
            response.OutputSpeech.Type.ShouldBe("SSML");

            var ssml = response.OutputSpeech.ShouldBeOfType <SsmlOutputSpeech>();

            ssml.Ssml.ShouldBe("<speak><p>This skill allows you to check for the status of a specific line, or for disruption in general. You can ask about any London Underground line, London Overground, the Docklands Light Railway or T.F.L. Rail.</p><p>Asking about disruption in general provides information about any lines that are currently experiencing issues, such as any delays or planned closures.</p><p>Asking for the status for a specific line provides a summary of the current service, such as whether there is a good service or if there are any delays.</p><p>If you link your account and setup your preferences in the London Travel website, you can ask about your commute to quickly find out the status of the lines you frequently use.</p></speak>");
        }