File: StartupFactsTest.cs
Web Access
Project: src\src\Analyzers\Analyzers\test\Microsoft.AspNetCore.Analyzers.Test.csproj (Microsoft.AspNetCore.Analyzers.Test)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.CodeAnalysis;
 
namespace Microsoft.AspNetCore.Analyzers;
 
public class StartupFactsTest
{
    private const string BasicStartup = @"
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
 
namespace Microsoft.AspNetCore.Analyzers.TestFiles.StartupFactsTest
{
    public class BasicStartup
    {
        public void ConfigureServices(IServiceCollection services)
        {
        }
 
        public void Configure(IApplicationBuilder app)
        {
        }
    }
}";
    private const string EnvironmentStartup = @"
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
 
namespace Microsoft.AspNetCore.Analyzers.TestFiles.StartupFactsTest
{
    public class EnvironmentStartup
    {
        public void ConfigureDevelopmentServices(IServiceCollection services)
        {
        }
 
        public void configurePRODUCTIONservices(IServiceCollection services)
        {
        }
 
        // Yes, this is technically a Configure method - if you have an Enviroment called DevelopmentServices2.
        public static void ConfigureDevelopmentServices2(IConfiguration configuration, ILogger logger, IApplicationBuilder app)
        {
        }
 
        public static void configurePRODUCTION(IConfiguration configuration, ILogger logger, IApplicationBuilder app)
        {
        }
    }
}
";
    private const string NotAStartupClass = @"
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
 
namespace Microsoft.AspNetCore.Analyzers.TestFiles.StartupFactsTest
{
    public class NotAStartupClass
    {
        // no args - not a ConfigureServices (technically it is, but we exclude this case).
        public void ConfigureServices()
        {
        }
 
        // extra arg - not a ConfigureServices
        public void ConfigureServices(IServiceCollection services, string x)
        {
        }
 
        // wrong name - not a ConfigureServices
        public void ConfigureSrvces(IServiceCollection services)
        {
        }
 
        // non-public - not a ConfigureServices
        internal void ConfigureServices(IServiceCollection services)
        {
        }
 
        // no IApplicationBuilder - not a Configure
        public void Configure(IConfiguration configuration)
        {
        }
 
        // wrong prefix - not a Configure
        public void Configur(IApplicationBuilder app)
        {
        }
 
        // non-public - not a Configure
        internal void Configure(IApplicationBuilder app)
        {
        }
    }
}";
    private static readonly Dictionary<string, string> TestSources = new Dictionary<string, string>
    {
        [nameof(BasicStartup)] = BasicStartup,
        [nameof(EnvironmentStartup)] = EnvironmentStartup,
        [nameof(NotAStartupClass)] = NotAStartupClass,
    };
 
    [Theory]
    [InlineData(nameof(BasicStartup), "ConfigureServices")]
    [InlineData(nameof(EnvironmentStartup), "ConfigureDevelopmentServices")]
    [InlineData(nameof(EnvironmentStartup), "configurePRODUCTIONservices")]
    public void IsConfigureServices_FindsConfigureServicesMethod(string source, string methodName)
    {
        // Arrange
        var compilation = TestCompilation.Create(TestSources[source]);
        var symbols = new StartupSymbols(compilation);
 
        var type = (INamedTypeSymbol)compilation.GetSymbolsWithName(source).Single();
        var methods = type.GetMembers(methodName).Cast<IMethodSymbol>();
 
        foreach (var method in methods)
        {
            // Act
            var result = StartupFacts.IsConfigureServices(symbols, method);
 
            // Assert
            Assert.True(result);
        }
    }
 
    [Theory]
    [InlineData(nameof(NotAStartupClass), "ConfigureServices")]
    [InlineData(nameof(NotAStartupClass), "ConfigureSrvces")]
 
    // This is an interesting case where a method follows both naming conventions.
    [InlineData(nameof(EnvironmentStartup), "ConfigureDevelopmentServices2")]
    public void IsConfigureServices_RejectsNonConfigureServicesMethod(string source, string methodName)
    {
        // Arrange
        var compilation = TestCompilation.Create(TestSources[source]);
        var symbols = new StartupSymbols(compilation);
 
        var type = (INamedTypeSymbol)compilation.GetSymbolsWithName(source).Single();
        var methods = type.GetMembers(methodName).Cast<IMethodSymbol>();
 
        foreach (var method in methods)
        {
            // Act
            var result = StartupFacts.IsConfigureServices(symbols, method);
 
            // Assert
            Assert.False(result);
        }
    }
 
    [Theory]
    [InlineData(nameof(BasicStartup), "Configure")]
    [InlineData(nameof(EnvironmentStartup), "configurePRODUCTION")]
    [InlineData(nameof(EnvironmentStartup), "ConfigureDevelopmentServices2")]
    public void IsConfigure_FindsConfigureMethod(string source, string methodName)
    {
        // Arrange
        var compilation = TestCompilation.Create(TestSources[source]);
        var symbols = new StartupSymbols(compilation);
 
        var type = (INamedTypeSymbol)compilation.GetSymbolsWithName(source).Single();
        var methods = type.GetMembers(methodName).Cast<IMethodSymbol>();
 
        foreach (var method in methods)
        {
            // Act
            var result = StartupFacts.IsConfigure(symbols, method);
 
            // Assert
            Assert.True(result);
        }
    }
 
    [Theory]
    [InlineData(nameof(NotAStartupClass), "Configure")]
    [InlineData(nameof(NotAStartupClass), "Configur")]
    public void IsConfigure_RejectsNonConfigureMethod(string source, string methodName)
    {
        // Arrange
        var compilation = TestCompilation.Create(TestSources[source]);
        var symbols = new StartupSymbols(compilation);
 
        var type = (INamedTypeSymbol)compilation.GetSymbolsWithName(source).Single();
        var methods = type.GetMembers(methodName).Cast<IMethodSymbol>();
 
        foreach (var method in methods)
        {
            // Act
            var result = StartupFacts.IsConfigure(symbols, method);
 
            // Assert
            Assert.False(result);
        }
    }
 
    [Theory]
    [InlineData(nameof(BasicStartup))]
    [InlineData(nameof(EnvironmentStartup))]
    public void IsStartupClass_FindsStartupClass(string source)
    {
        // Arrange
        var compilation = TestCompilation.Create(TestSources[source]);
        var symbols = new StartupSymbols(compilation);
 
        var type = (INamedTypeSymbol)compilation.GetSymbolsWithName(source).Single();
 
        // Act
        var result = StartupFacts.IsStartupClass(symbols, type);
 
        // Assert
        Assert.True(result);
    }
 
    [Theory]
    [InlineData(nameof(NotAStartupClass))]
    public void IsStartupClass_RejectsNotStartupClass(string source)
    {
        // Arrange
        var compilation = TestCompilation.Create(TestSources[source]);
        var symbols = new StartupSymbols(compilation);
 
        var type = (INamedTypeSymbol)compilation.GetSymbolsWithName(source).Single();
 
        // Act
        var result = StartupFacts.IsStartupClass(symbols, type);
 
        // Assert
        Assert.False(result);
    }
}