public async Task NonGenericCloudEventFunction()
        {
            var services = new ServiceCollection();

            services.AddLogging();
            HostingInternals.AddServicesForFunctionTarget(services, typeof(EventIdRememberingFunction));
            var provider = services.BuildServiceProvider();

            var function = provider.GetRequiredService <IHttpFunction>();

            Assert.Equal(typeof(EventIdRememberingFunction), provider.GetRequiredService <HostingInternals.FunctionTypeProvider>().FunctionType);
            string eventId = Guid.NewGuid().ToString();
            var    context = new DefaultHttpContext
            {
                Request =
                {
                    // No actual content, but that's okay.
                    ContentType = "application/json",
                    Headers     =
                    {
                        { "ce-specversion", "1.0"   },
                        { "ce-source",      "test"  },
                        { "ce-type",        "test"  },
                        { "ce-id",          eventId }
                    }
                }
            };

            await function.HandleAsync(context);

            Assert.Equal(eventId, EventIdRememberingFunction.LastEventId);
        }
        public async Task TargetFunction_EventFunction_Generic()
        {
            var services = new ServiceCollection();

            services.AddLogging();
            HostingInternals.AddServicesForFunctionTarget(services, typeof(StorageCloudEventFunction));
            var provider = services.BuildServiceProvider();
            var function = provider.GetRequiredService <IHttpFunction>();

            Assert.Equal(typeof(StorageCloudEventFunction), provider.GetRequiredService <HostingInternals.FunctionTypeProvider>().FunctionType);
            var eventId = Guid.NewGuid().ToString();
            var context = new DefaultHttpContext
            {
                Request =
                {
                    ContentType = "application/json",
                    Body        = new MemoryStream(Encoding.UTF8.GetBytes("{\"name\": \"testdata\"}")),
                    Headers     =
                    {
                        { "ce-specversion", "1.0"   },
                        { "ce-source",      "test"  },
                        { "ce-type",        "test"  },
                        { "ce-id",          eventId }
                    }
                },
            };

            await function.HandleAsync(context);

            Assert.Equal(eventId, StorageCloudEventFunction.LastEventId);
            Assert.Equal("testdata", StorageCloudEventFunction.LastData.Name);
        }
        public void NonInstantiableFunctionType()
        {
            var services = new ServiceCollection();

            services.AddLogging();
            HostingInternals.AddServicesForFunctionTarget(services, typeof(NonInstantiableFunction));
            // We only find out when we try to get an IHttpFunction that we can't actually instantiate the function.
            var provider = services.BuildServiceProvider();

            Assert.Throws <InvalidOperationException>(() => provider.GetRequiredService <IHttpFunction>());
        }
        public void FunctionTypeImplementsMultipleInterfaces()
        {
            var services = new ServiceCollection();

            Assert.Throws <InvalidOperationException>(() => HostingInternals.AddServicesForFunctionTarget(services, typeof(MultipleCloudEventTypes)));
        }
        public void NonFunctionType()
        {
            var services = new ServiceCollection();

            Assert.Throws <InvalidOperationException>(() => HostingInternals.AddServicesForFunctionTarget(services, typeof(NonFunction)));
        }
 /// <summary>
 /// Adds services required for the Functions Framework to use the specified function target type, which must
 /// implement one of the Functions Framework function interfaces.
 /// </summary>
 /// <remarks>
 /// If this method completes successfully, a binding for <see cref="IHttpFunction"/> will definitely
 /// have been added to the service collection. Other bindings may also be present, in order to adapt
 /// the function to <see cref="IHttpFunction"/>.
 /// </remarks>
 /// <param name="services">The service collection to configure.</param>
 /// <param name="type">The target function type.</param>
 /// <returns>The original service collection, for method chaining.</returns>
 public static IServiceCollection AddFunctionTarget(this IServiceCollection services, Type type) =>
 HostingInternals.AddServicesForFunctionTarget(services, type);
 /// <summary>
 /// Adds required services to the service collection within the given web host builder for the Functions Framework
 /// to use a target function from the given assembly. If the Functions Framework configuration within the web host
 /// builder (typically provided by command line arguments or environment variables)
 /// does not specify a target function, the assembly is scanned for a single compatible function type.
 /// </summary>
 /// <remarks>
 /// If this method completes successfully, a binding for <see cref="IHttpFunction"/> will definitely
 /// have been added to the service collection. Other bindings may also be present, in order to adapt
 /// the function to <see cref="IHttpFunction"/>.
 /// </remarks>
 /// <param name="services">The service collection to configure.</param>
 /// <param name="context">The context of the web host builder being configured.</param>
 /// <param name="assembly">The assembly expected to contain the Functions Framework target function.</param>
 /// <returns>The original builder, for method chaining.</returns>
 public static IServiceCollection AddFunctionTarget(this IServiceCollection services, WebHostBuilderContext context, Assembly assembly) =>
 HostingInternals.AddServicesForFunctionTarget(services, HostingInternals.GetFunctionTarget(context, assembly));