* Added missing summaries
This commit is contained in:
@@ -9,83 +9,107 @@ using Telegrator.RoslynGenerators.RoslynExtensions;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Telegrator.RoslynGenerators
|
namespace Telegrator.RoslynGenerators;
|
||||||
|
|
||||||
|
[Generator(LanguageNames.CSharp)]
|
||||||
|
public class ImplicitHandlerBuilderExtensionsGenerator : IIncrementalGenerator
|
||||||
{
|
{
|
||||||
[Generator(LanguageNames.CSharp)]
|
private static readonly string[] DefaultUsings =
|
||||||
public class ImplicitHandlerBuilderExtensionsGenerator : IIncrementalGenerator
|
[
|
||||||
|
"Telegrator.Handlers.Building",
|
||||||
|
"Telegrator.Core.Handlers.Building"
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly ParameterSyntax ExtensionMethodThisParam = SyntaxFactory.Parameter(SyntaxFactory.Identifier("builder")).WithType(SyntaxFactory.IdentifierName("TBuilder").WithLeadingTrivia(SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ")).WithTrailingTrivia(WhitespaceTrivia)).WithModifiers([SyntaxFactory.Token(SyntaxKind.ThisKeyword)]);
|
||||||
|
private static readonly MemberAccessExpressionSyntax BuilderAdderMethodAccessExpression = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName("builder"), SyntaxFactory.IdentifierName("AddTargetedFilters"));
|
||||||
|
private static readonly IEqualityComparer<UsingDirectiveSyntax> UsingEqualityComparer = new UsingDirectiveEqualityComparer();
|
||||||
|
|
||||||
|
private static SyntaxTrivia WhitespaceTrivia => SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ");
|
||||||
|
private static SyntaxTrivia NewLineTrivia => SyntaxFactory.SyntaxTrivia(SyntaxKind.EndOfLineTrivia, "\n");
|
||||||
|
|
||||||
|
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||||
{
|
{
|
||||||
private static readonly string[] DefaultUsings =
|
IncrementalValueProvider<ImmutableArray<ClassDeclarationSyntax>> pipeline = context.SyntaxProvider
|
||||||
[
|
.CreateSyntaxProvider(SyntaxPredicate, SyntaxTransform)
|
||||||
"Telegrator.Handlers.Building",
|
.Where(declaration => declaration != null)
|
||||||
"Telegrator.Core.Handlers.Building"
|
.Collect();
|
||||||
];
|
|
||||||
|
|
||||||
private static readonly ParameterSyntax ExtensionMethodThisParam = SyntaxFactory.Parameter(SyntaxFactory.Identifier("builder")).WithType(SyntaxFactory.IdentifierName("TBuilder").WithLeadingTrivia(SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ")).WithTrailingTrivia(WhitespaceTrivia)).WithModifiers([SyntaxFactory.Token(SyntaxKind.ThisKeyword)]);
|
|
||||||
private static readonly MemberAccessExpressionSyntax BuilderAdderMethodAccessExpression = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName("builder"), SyntaxFactory.IdentifierName("AddTargetedFilters"));
|
|
||||||
private static readonly IEqualityComparer<UsingDirectiveSyntax> UsingEqualityComparer = new UsingDirectiveEqualityComparer();
|
|
||||||
|
|
||||||
private static SyntaxTrivia WhitespaceTrivia => SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ");
|
context.RegisterImplementationSourceOutput(pipeline, GenerateSource);
|
||||||
private static SyntaxTrivia NewLineTrivia => SyntaxFactory.SyntaxTrivia(SyntaxKind.EndOfLineTrivia, "\n");
|
}
|
||||||
|
|
||||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
private static bool SyntaxPredicate(SyntaxNode node, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
return node is ClassDeclarationSyntax;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ClassDeclarationSyntax SyntaxTransform(GeneratorSyntaxContext context, CancellationToken _)
|
||||||
|
{
|
||||||
|
ISymbol? symbol = context.SemanticModel.GetDeclaredSymbol(context.Node);
|
||||||
|
if (symbol is null)
|
||||||
|
return null!;
|
||||||
|
|
||||||
|
if (symbol is not ITypeSymbol typeSymbol)
|
||||||
|
return null!;
|
||||||
|
|
||||||
|
if (!typeSymbol.IsAssignableFrom("UpdateFilterAttribute"))
|
||||||
|
return null!;
|
||||||
|
|
||||||
|
return (ClassDeclarationSyntax)context.Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateSource(SourceProductionContext context, ImmutableArray<ClassDeclarationSyntax> declarations)
|
||||||
|
{
|
||||||
|
StringBuilder debugExport = new StringBuilder("/*");
|
||||||
|
List<UsingDirectiveSyntax> usings = ParseUsings(DefaultUsings).ToList();
|
||||||
|
|
||||||
|
Dictionary<string, MethodDeclarationSyntax> targetters = [];
|
||||||
|
foreach (ClassDeclarationSyntax classDeclaration in declarations)
|
||||||
{
|
{
|
||||||
IncrementalValueProvider<ImmutableArray<ClassDeclarationSyntax>> pipeline = context.SyntaxProvider
|
try
|
||||||
.CreateSyntaxProvider(SyntaxPredicate, SyntaxTransform)
|
{
|
||||||
.Where(declaration => declaration != null)
|
string className = classDeclaration.Identifier.ToString();
|
||||||
.Collect();
|
if (className == "FilterAnnotation")
|
||||||
|
continue;
|
||||||
|
|
||||||
context.RegisterImplementationSourceOutput(pipeline, GenerateSource);
|
MethodDeclarationSyntax? targeter = classDeclaration.Members.OfType<MethodDeclarationSyntax>().SingleOrDefault(IsTargeterMethod);
|
||||||
|
if (targeter != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
MethodDeclarationSyntax genTargeter = GenerateTargetterMethod(classDeclaration, targeter);
|
||||||
|
targetters.Add(className, genTargeter);
|
||||||
|
}
|
||||||
|
catch (Exception exc)
|
||||||
|
{
|
||||||
|
string errorFormat = string.Format("\nFailed to generate for {0} : {1}\n", classDeclaration.Identifier.ToString(), exc.ToString());
|
||||||
|
debugExport.AppendLine(errorFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exc)
|
||||||
|
{
|
||||||
|
string errorFormat = string.Format("\nFailed to generate for {0} : {1}\n", classDeclaration.Identifier.ToString(), exc.ToString());
|
||||||
|
debugExport.AppendLine(errorFormat);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool SyntaxPredicate(SyntaxNode node, CancellationToken cancellationToken)
|
List<MethodDeclarationSyntax> extensions = [];
|
||||||
|
foreach (ClassDeclarationSyntax classDeclaration in declarations)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
if (classDeclaration.Modifiers.HasModifiers("abstract"))
|
||||||
return node is ClassDeclarationSyntax;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
private static ClassDeclarationSyntax SyntaxTransform(GeneratorSyntaxContext context, CancellationToken _)
|
usings.UnionAdd(classDeclaration.FindAncestor<CompilationUnitSyntax>().Usings, UsingEqualityComparer);
|
||||||
{
|
MethodDeclarationSyntax targeter = FindTargetterMethod(targetters, classDeclaration);
|
||||||
ISymbol? symbol = context.SemanticModel.GetDeclaredSymbol(context.Node);
|
|
||||||
if (symbol is null)
|
|
||||||
return null!;
|
|
||||||
|
|
||||||
if (symbol is not ITypeSymbol typeSymbol)
|
if (classDeclaration.ParameterList != null && classDeclaration.BaseList != null)
|
||||||
return null!;
|
|
||||||
|
|
||||||
if (!typeSymbol.IsAssignableFrom("UpdateFilterAttribute"))
|
|
||||||
return null!;
|
|
||||||
|
|
||||||
return (ClassDeclarationSyntax)context.Node;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GenerateSource(SourceProductionContext context, ImmutableArray<ClassDeclarationSyntax> declarations)
|
|
||||||
{
|
|
||||||
StringBuilder debugExport = new StringBuilder("/*");
|
|
||||||
List<UsingDirectiveSyntax> usings = ParseUsings(DefaultUsings).ToList();
|
|
||||||
|
|
||||||
Dictionary<string, MethodDeclarationSyntax> targetters = [];
|
|
||||||
foreach (ClassDeclarationSyntax classDeclaration in declarations)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string className = classDeclaration.Identifier.ToString();
|
PrimaryConstructorBaseTypeSyntax primaryConstructor = (PrimaryConstructorBaseTypeSyntax)classDeclaration.BaseList.Types.ElementAt(0);
|
||||||
if (className == "FilterAnnotation")
|
MethodDeclarationSyntax genExtension = GeneratedExtensionsMethod(classDeclaration, classDeclaration.ParameterList, primaryConstructor.ArgumentList, targeter);
|
||||||
continue;
|
extensions.Add(genExtension);
|
||||||
|
|
||||||
MethodDeclarationSyntax? targeter = classDeclaration.Members.OfType<MethodDeclarationSyntax>().SingleOrDefault(IsTargeterMethod);
|
|
||||||
if (targeter != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
MethodDeclarationSyntax genTargeter = GenerateTargetterMethod(classDeclaration, targeter);
|
|
||||||
targetters.Add(className, genTargeter);
|
|
||||||
}
|
|
||||||
catch (Exception exc)
|
|
||||||
{
|
|
||||||
string errorFormat = string.Format("\nFailed to generate for {0} : {1}\n", classDeclaration.Identifier.ToString(), exc.ToString());
|
|
||||||
debugExport.AppendLine(errorFormat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception exc)
|
catch (Exception exc)
|
||||||
{
|
{
|
||||||
@@ -93,192 +117,167 @@ namespace Telegrator.RoslynGenerators
|
|||||||
debugExport.AppendLine(errorFormat);
|
debugExport.AppendLine(errorFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<MethodDeclarationSyntax> extensions = [];
|
foreach (ConstructorDeclarationSyntax ctor in GetConstructors(classDeclaration))
|
||||||
foreach (ClassDeclarationSyntax classDeclaration in declarations)
|
|
||||||
{
|
{
|
||||||
if (classDeclaration.Modifiers.HasModifiers("abstract"))
|
try
|
||||||
continue;
|
|
||||||
|
|
||||||
usings.UnionAdd(classDeclaration.FindAncestor<CompilationUnitSyntax>().Usings, UsingEqualityComparer);
|
|
||||||
MethodDeclarationSyntax targeter = FindTargetterMethod(targetters, classDeclaration);
|
|
||||||
|
|
||||||
if (classDeclaration.ParameterList != null && classDeclaration.BaseList != null)
|
|
||||||
{
|
{
|
||||||
try
|
if (ctor.Initializer == null)
|
||||||
{
|
continue;
|
||||||
PrimaryConstructorBaseTypeSyntax primaryConstructor = (PrimaryConstructorBaseTypeSyntax)classDeclaration.BaseList.Types.ElementAt(0);
|
|
||||||
MethodDeclarationSyntax genExtension = GeneratedExtensionsMethod(classDeclaration, classDeclaration.ParameterList, primaryConstructor.ArgumentList, targeter);
|
MethodDeclarationSyntax genExtension = GeneratedExtensionsMethod(classDeclaration, ctor.ParameterList, ctor.Initializer.ArgumentList, targeter);
|
||||||
extensions.Add(genExtension);
|
extensions.Add(genExtension);
|
||||||
}
|
|
||||||
catch (Exception exc)
|
|
||||||
{
|
|
||||||
string errorFormat = string.Format("\nFailed to generate for {0} : {1}\n", classDeclaration.Identifier.ToString(), exc.ToString());
|
|
||||||
debugExport.AppendLine(errorFormat);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
catch (Exception exc)
|
||||||
foreach (ConstructorDeclarationSyntax ctor in GetConstructors(classDeclaration))
|
|
||||||
{
|
{
|
||||||
try
|
string errorFormat = string.Format("\nFailed to generate for {0} : {1}\n", classDeclaration.Identifier.ToString(), exc.ToString());
|
||||||
{
|
debugExport.AppendLine(errorFormat);
|
||||||
if (ctor.Initializer == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
MethodDeclarationSyntax genExtension = GeneratedExtensionsMethod(classDeclaration, ctor.ParameterList, ctor.Initializer.ArgumentList, targeter);
|
|
||||||
extensions.Add(genExtension);
|
|
||||||
}
|
|
||||||
catch (Exception exc)
|
|
||||||
{
|
|
||||||
string errorFormat = string.Format("\nFailed to generate for {0} : {1}\n", classDeclaration.Identifier.ToString(), exc.ToString());
|
|
||||||
debugExport.AppendLine(errorFormat);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ClassDeclarationSyntax extensionsClass = SyntaxFactory.ClassDeclaration("HandlerBuilderExtensions")
|
|
||||||
.WithModifiers(Modifiers(SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword, SyntaxKind.PartialKeyword))
|
|
||||||
.AddMembers([.. targetters.Values, .. extensions])
|
|
||||||
.DecorateType(1);
|
|
||||||
|
|
||||||
NamespaceDeclarationSyntax namespaceDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName("Telegrator"))
|
|
||||||
.WithMembers([extensionsClass])
|
|
||||||
.Decorate();
|
|
||||||
|
|
||||||
CompilationUnitSyntax compilationUnit = SyntaxFactory.CompilationUnit()
|
|
||||||
.WithUsings([.. usings])
|
|
||||||
.WithMembers([namespaceDeclaration]);
|
|
||||||
|
|
||||||
context.AddSource("GeneratedHandlerBuilderExtensions.cs", compilationUnit.ToFullString());
|
|
||||||
}
|
|
||||||
catch (Exception exc)
|
|
||||||
{
|
|
||||||
string errorFormat = string.Format("\nFailed to generate : {0}\n", exc.ToString());
|
|
||||||
debugExport.AppendLine(errorFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.AddSource("GeneratedHandlerBuilderExtensions.Debug.cs", debugExport.AppendLine("*/").ToString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodDeclarationSyntax GenerateTargetterMethod(ClassDeclarationSyntax classDeclaration, MethodDeclarationSyntax targetterMethod)
|
try
|
||||||
{
|
{
|
||||||
SyntaxToken identifier = SyntaxFactory.Identifier(classDeclaration.Identifier.ToString() + "_" + targetterMethod.Identifier.ToString());
|
ClassDeclarationSyntax extensionsClass = SyntaxFactory.ClassDeclaration("HandlerBuilderExtensions")
|
||||||
MethodDeclarationSyntax method = SyntaxFactory.MethodDeclaration(targetterMethod.ReturnType, identifier)
|
.WithModifiers(Modifiers(SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword, SyntaxKind.PartialKeyword))
|
||||||
.WithParameterList(targetterMethod.ParameterList)
|
.AddMembers([.. targetters.Values, .. extensions])
|
||||||
.WithModifiers(Modifiers(SyntaxKind.PrivateKeyword, SyntaxKind.StaticKeyword));
|
.DecorateType(1);
|
||||||
|
|
||||||
if (targetterMethod.Body != null)
|
NamespaceDeclarationSyntax namespaceDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName("Telegrator"))
|
||||||
method = method.WithBody(targetterMethod.Body);
|
.WithMembers([extensionsClass])
|
||||||
|
.Decorate();
|
||||||
|
|
||||||
if (targetterMethod.ExpressionBody != null)
|
CompilationUnitSyntax compilationUnit = SyntaxFactory.CompilationUnit()
|
||||||
method = method.WithExpressionBody(targetterMethod.ExpressionBody).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
|
.WithUsings([.. usings])
|
||||||
|
.WithMembers([namespaceDeclaration]);
|
||||||
|
|
||||||
return method.DecorateMember(2);
|
context.AddSource("GeneratedHandlerBuilderExtensions.cs", compilationUnit.ToFullString());
|
||||||
|
}
|
||||||
|
catch (Exception exc)
|
||||||
|
{
|
||||||
|
string errorFormat = string.Format("\nFailed to generate : {0}\n", exc.ToString());
|
||||||
|
debugExport.AppendLine(errorFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodDeclarationSyntax GeneratedExtensionsMethod(ClassDeclarationSyntax classDeclaration, ParameterListSyntax methodParameters, ArgumentListSyntax invokerArguments, MethodDeclarationSyntax targetterMethod)
|
context.AddSource("GeneratedHandlerBuilderExtensions.Debug.cs", debugExport.AppendLine("*/").ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodDeclarationSyntax GenerateTargetterMethod(ClassDeclarationSyntax classDeclaration, MethodDeclarationSyntax targetterMethod)
|
||||||
|
{
|
||||||
|
SyntaxToken identifier = SyntaxFactory.Identifier(classDeclaration.Identifier.ToString() + "_" + targetterMethod.Identifier.ToString());
|
||||||
|
MethodDeclarationSyntax method = SyntaxFactory.MethodDeclaration(targetterMethod.ReturnType, identifier)
|
||||||
|
.WithParameterList(targetterMethod.ParameterList)
|
||||||
|
.WithModifiers(Modifiers(SyntaxKind.PrivateKeyword, SyntaxKind.StaticKeyword));
|
||||||
|
|
||||||
|
if (targetterMethod.Body != null)
|
||||||
|
method = method.WithBody(targetterMethod.Body);
|
||||||
|
|
||||||
|
if (targetterMethod.ExpressionBody != null)
|
||||||
|
method = method.WithExpressionBody(targetterMethod.ExpressionBody).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
|
||||||
|
|
||||||
|
return method.DecorateMember(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodDeclarationSyntax GeneratedExtensionsMethod(ClassDeclarationSyntax classDeclaration, ParameterListSyntax methodParameters, ArgumentListSyntax invokerArguments, MethodDeclarationSyntax targetterMethod)
|
||||||
|
{
|
||||||
|
ParameterListSyntax parameters = SyntaxFactory.ParameterList([ExtensionMethodThisParam, ..methodParameters.Parameters]);
|
||||||
|
TypeParameterListSyntax typeParameters = SyntaxFactory.TypeParameterList([SyntaxFactory.TypeParameter("TBuilder")]);
|
||||||
|
|
||||||
|
InvocationExpressionSyntax invocationExpression = SyntaxFactory.InvocationExpression(BuilderAdderMethodAccessExpression, AddTargeter(invokerArguments, targetterMethod));
|
||||||
|
BlockSyntax body = SyntaxFactory.Block(new StatementSyntax[]
|
||||||
{
|
{
|
||||||
ParameterListSyntax parameters = SyntaxFactory.ParameterList([ExtensionMethodThisParam, ..methodParameters.Parameters]);
|
SyntaxFactory.ExpressionStatement(invocationExpression),
|
||||||
TypeParameterListSyntax typeParameters = SyntaxFactory.TypeParameterList([SyntaxFactory.TypeParameter("TBuilder")]);
|
SyntaxFactory.ReturnStatement(SyntaxFactory.IdentifierName("builder").WithLeadingTrivia(WhitespaceTrivia))
|
||||||
|
});
|
||||||
|
|
||||||
InvocationExpressionSyntax invocationExpression = SyntaxFactory.InvocationExpression(BuilderAdderMethodAccessExpression, AddTargeter(invokerArguments, targetterMethod));
|
TypeParameterConstraintClauseSyntax typeParameterConstraint = SyntaxFactory.TypeParameterConstraintClause(SyntaxFactory.IdentifierName("TBuilder").WithLeadingTrivia(WhitespaceTrivia).WithTrailingTrivia(WhitespaceTrivia))
|
||||||
BlockSyntax body = SyntaxFactory.Block(new StatementSyntax[]
|
.WithConstraints([SyntaxFactory.TypeConstraint(SyntaxFactory.ParseTypeName("IHandlerBuilder").WithLeadingTrivia(WhitespaceTrivia))])
|
||||||
{
|
.WithLeadingTrivia(WhitespaceTrivia);
|
||||||
SyntaxFactory.ExpressionStatement(invocationExpression),
|
|
||||||
SyntaxFactory.ReturnStatement(SyntaxFactory.IdentifierName("builder").WithLeadingTrivia(WhitespaceTrivia))
|
|
||||||
});
|
|
||||||
|
|
||||||
TypeParameterConstraintClauseSyntax typeParameterConstraint = SyntaxFactory.TypeParameterConstraintClause(SyntaxFactory.IdentifierName("TBuilder").WithLeadingTrivia(WhitespaceTrivia).WithTrailingTrivia(WhitespaceTrivia))
|
string filterName = classDeclaration.Identifier.ToString().Replace("Attribute", string.Empty);
|
||||||
.WithConstraints([SyntaxFactory.TypeConstraint(SyntaxFactory.ParseTypeName("IHandlerBuilder").WithLeadingTrivia(WhitespaceTrivia))])
|
if (filterName == "ChatType")
|
||||||
.WithLeadingTrivia(WhitespaceTrivia);
|
filterName = "InChatType"; // Because it conflicting
|
||||||
|
|
||||||
string filterName = classDeclaration.Identifier.ToString().Replace("Attribute", string.Empty);
|
SyntaxToken identifier = SyntaxFactory.Identifier(filterName);
|
||||||
if (filterName == "ChatType")
|
TypeSyntax returnType = SyntaxFactory.ParseTypeName("TBuilder").WithTrailingTrivia(WhitespaceTrivia);
|
||||||
filterName = "InChatType"; // Because it conflicting
|
SyntaxTriviaList xmlDoc = BuildExtensionXmlDocTrivia(classDeclaration, methodParameters);
|
||||||
|
|
||||||
SyntaxToken identifier = SyntaxFactory.Identifier(filterName);
|
MethodDeclarationSyntax method = SyntaxFactory.MethodDeclaration(returnType, identifier)
|
||||||
TypeSyntax returnType = SyntaxFactory.ParseTypeName("TBuilder").WithTrailingTrivia(WhitespaceTrivia);
|
.WithParameterList(parameters)
|
||||||
SyntaxTriviaList xmlDoc = BuildExtensionXmlDocTrivia(classDeclaration, methodParameters);
|
.WithBody(body.DecorateBlock(2))
|
||||||
|
.WithTypeParameterList(typeParameters)
|
||||||
|
.WithModifiers(Modifiers(SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword))
|
||||||
|
.WithConstraintClauses([typeParameterConstraint])
|
||||||
|
.DecorateMember(2)
|
||||||
|
.WithLeadingTrivia(xmlDoc);
|
||||||
|
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
MethodDeclarationSyntax method = SyntaxFactory.MethodDeclaration(returnType, identifier)
|
private static SyntaxTokenList Modifiers(params SyntaxKind[] kinds)
|
||||||
.WithParameterList(parameters)
|
=> new SyntaxTokenList(kinds.Select(SyntaxFactory.Token).Select(mod => mod.WithTrailingTrivia(WhitespaceTrivia)));
|
||||||
.WithBody(body.DecorateBlock(2))
|
|
||||||
.WithTypeParameterList(typeParameters)
|
|
||||||
.WithModifiers(Modifiers(SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword))
|
|
||||||
.WithConstraintClauses([typeParameterConstraint])
|
|
||||||
.DecorateMember(2)
|
|
||||||
.WithLeadingTrivia(xmlDoc);
|
|
||||||
|
|
||||||
return method;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SyntaxTokenList Modifiers(params SyntaxKind[] kinds)
|
private static IEnumerable<UsingDirectiveSyntax> ParseUsings(params string[] names) => names
|
||||||
=> new SyntaxTokenList(kinds.Select(SyntaxFactory.Token).Select(mod => mod.WithTrailingTrivia(WhitespaceTrivia)));
|
.Select(name => SyntaxFactory.IdentifierName(name).WithLeadingTrivia(WhitespaceTrivia))
|
||||||
|
.Select(name => SyntaxFactory.UsingDirective(name).WithTrailingTrivia(NewLineTrivia));
|
||||||
|
|
||||||
private static IEnumerable<UsingDirectiveSyntax> ParseUsings(params string[] names) => names
|
private static ArgumentListSyntax AddTargeter(ArgumentListSyntax invokerArguments, MethodDeclarationSyntax targetterMethod)
|
||||||
.Select(name => SyntaxFactory.IdentifierName(name).WithLeadingTrivia(WhitespaceTrivia))
|
=> SyntaxFactory.ArgumentList([SyntaxFactory.Argument(SyntaxFactory.IdentifierName(targetterMethod.Identifier)), ..invokerArguments.Arguments]);
|
||||||
.Select(name => SyntaxFactory.UsingDirective(name).WithTrailingTrivia(NewLineTrivia));
|
|
||||||
|
|
||||||
private static ArgumentListSyntax AddTargeter(ArgumentListSyntax invokerArguments, MethodDeclarationSyntax targetterMethod)
|
private static bool IsTargeterMethod(MethodDeclarationSyntax method)
|
||||||
=> SyntaxFactory.ArgumentList([SyntaxFactory.Argument(SyntaxFactory.IdentifierName(targetterMethod.Identifier)), ..invokerArguments.Arguments]);
|
=> method.Identifier.ToString() == "GetFilterringTarget";
|
||||||
|
|
||||||
private static bool IsTargeterMethod(MethodDeclarationSyntax method)
|
private static IEnumerable<ConstructorDeclarationSyntax> GetConstructors(ClassDeclarationSyntax classDeclaration)
|
||||||
=> method.Identifier.ToString() == "GetFilterringTarget";
|
=> classDeclaration.Members.OfType<ConstructorDeclarationSyntax>().Where(ctor => ctor.Modifiers.HasModifiers("public"));
|
||||||
|
|
||||||
private static IEnumerable<ConstructorDeclarationSyntax> GetConstructors(ClassDeclarationSyntax classDeclaration)
|
private static MethodDeclarationSyntax FindTargetterMethod(Dictionary<string, MethodDeclarationSyntax> targeters, ClassDeclarationSyntax classDeclaration)
|
||||||
=> classDeclaration.Members.OfType<ConstructorDeclarationSyntax>().Where(ctor => ctor.Modifiers.HasModifiers("public"));
|
{
|
||||||
|
if (targeters.TryGetValue(classDeclaration.Identifier.ValueText, out MethodDeclarationSyntax targeter))
|
||||||
|
return targeter;
|
||||||
|
|
||||||
private static MethodDeclarationSyntax FindTargetterMethod(Dictionary<string, MethodDeclarationSyntax> targeters, ClassDeclarationSyntax classDeclaration)
|
if (classDeclaration.BaseList != null && targeters.TryGetValue(classDeclaration.BaseList.Types.ElementAt(0).Type.ToString(), out targeter))
|
||||||
|
return targeter;
|
||||||
|
|
||||||
|
throw new TargteterNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SyntaxTriviaList BuildExtensionXmlDocTrivia(ClassDeclarationSyntax classDeclaration, ParameterListSyntax methodParameters)
|
||||||
|
{
|
||||||
|
StringBuilder summaryBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
summaryBuilder
|
||||||
|
.Append("\t\t/// <summary>\n")
|
||||||
|
.Append("\t\t/// Adds a ").Append(classDeclaration.Identifier.ToString()).Append(" target filter to the handler builder.\n")
|
||||||
|
.Append("\t\t/// </summary>\n");
|
||||||
|
|
||||||
|
summaryBuilder
|
||||||
|
.AppendLine("\t\t/// <typeparam name=\"TBuilder\">The builder type.</typeparam>")
|
||||||
|
.AppendLine("\t\t/// <param name=\"builder\">The handler builder.</param>");
|
||||||
|
|
||||||
|
foreach (ParameterSyntax param in methodParameters.Parameters)
|
||||||
{
|
{
|
||||||
if (targeters.TryGetValue(classDeclaration.Identifier.ValueText, out MethodDeclarationSyntax targeter))
|
string name = param.Identifier.ToString();
|
||||||
return targeter;
|
|
||||||
|
|
||||||
if (classDeclaration.BaseList != null && targeters.TryGetValue(classDeclaration.BaseList.Types.ElementAt(0).Type.ToString(), out targeter))
|
|
||||||
return targeter;
|
|
||||||
|
|
||||||
throw new TargteterNotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SyntaxTriviaList BuildExtensionXmlDocTrivia(ClassDeclarationSyntax classDeclaration, ParameterListSyntax methodParameters)
|
|
||||||
{
|
|
||||||
StringBuilder summaryBuilder = new StringBuilder();
|
|
||||||
|
|
||||||
summaryBuilder
|
summaryBuilder
|
||||||
.Append("\t\t/// <summary>\n")
|
.Append("\t\t/// <param name=\"").Append(name).Append("\">")
|
||||||
.Append("\t\t/// Adds a ").Append(classDeclaration.Identifier.ToString()).Append(" target filter to the handler builder.\n")
|
.Append("The ").Append(name)
|
||||||
.Append("\t\t/// </summary>\n");
|
.AppendLine(".</param>");
|
||||||
|
|
||||||
summaryBuilder
|
|
||||||
.AppendLine("\t\t/// <typeparam name=\"TBuilder\">The builder type.</typeparam>")
|
|
||||||
.AppendLine("\t\t/// <param name=\"builder\">The handler builder.</param>");
|
|
||||||
|
|
||||||
foreach (ParameterSyntax param in methodParameters.Parameters)
|
|
||||||
{
|
|
||||||
string name = param.Identifier.ToString();
|
|
||||||
summaryBuilder
|
|
||||||
.Append("\t\t/// <param name=\"").Append(name).Append("\">")
|
|
||||||
.Append("The ").Append(name)
|
|
||||||
.AppendLine(".</param>");
|
|
||||||
}
|
|
||||||
|
|
||||||
summaryBuilder.AppendLine("\t\t/// <returns>The same builder instance.</returns>");
|
|
||||||
summaryBuilder.Append("\t\t");
|
|
||||||
return SyntaxFactory.ParseLeadingTrivia(summaryBuilder.ToString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UsingDirectiveEqualityComparer : IEqualityComparer<UsingDirectiveSyntax>
|
summaryBuilder.AppendLine("\t\t/// <returns>The same builder instance.</returns>");
|
||||||
{
|
summaryBuilder.Append("\t\t");
|
||||||
public bool Equals(UsingDirectiveSyntax x, UsingDirectiveSyntax y)
|
return SyntaxFactory.ParseLeadingTrivia(summaryBuilder.ToString());
|
||||||
{
|
}
|
||||||
return x.ToString() == y.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetHashCode(UsingDirectiveSyntax obj)
|
private class UsingDirectiveEqualityComparer : IEqualityComparer<UsingDirectiveSyntax>
|
||||||
{
|
{
|
||||||
return obj.GetHashCode();
|
public bool Equals(UsingDirectiveSyntax x, UsingDirectiveSyntax y)
|
||||||
}
|
{
|
||||||
|
return x.ToString() == y.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(UsingDirectiveSyntax obj)
|
||||||
|
{
|
||||||
|
return obj.GetHashCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,12 +34,11 @@
|
|||||||
This application's logger
|
This application's logger
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHost.#ctor(Microsoft.AspNetCore.Builder.WebApplicationBuilder,Telegrator.Core.IHandlersCollection)">
|
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHost.#ctor(Microsoft.AspNetCore.Builder.WebApplicationBuilder)">
|
||||||
<summary>
|
<summary>
|
||||||
Initializes a new instance of the <see cref="T:Microsoft.AspNetCore.Builder.WebApplicationBuilder"/> class.
|
Initializes a new instance of the <see cref="T:Microsoft.AspNetCore.Builder.WebApplicationBuilder"/> class.
|
||||||
</summary>
|
</summary>
|
||||||
<param name="webApplicationBuilder">The proxied instance of host builder.</param>
|
<param name="webApplicationBuilder">The proxied instance of host builder.</param>
|
||||||
<param name="handlers"></param>
|
|
||||||
</member>
|
</member>
|
||||||
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHost.CreateBuilder(Telegrator.Hosting.Web.TelegramBotWebOptions)">
|
<member name="M:Telegrator.Hosting.Web.TelegramBotWebHost.CreateBuilder(Telegrator.Hosting.Web.TelegramBotWebOptions)">
|
||||||
<summary>
|
<summary>
|
||||||
@@ -209,6 +208,14 @@
|
|||||||
Provides method to configure <see cref="T:Telegrator.Hosting.Web.ITelegramBotWebHost"/>
|
Provides method to configure <see cref="T:Telegrator.Hosting.Web.ITelegramBotWebHost"/>
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="F:Telegrator.ServicesCollectionExtensions.HandlersCollectionPropertyKey">
|
||||||
|
<summary>
|
||||||
|
The key used to store the <see cref="T:Telegrator.Core.IHandlersCollection"/> in the builder properties.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Telegrator.ServicesCollectionExtensions.get_Handlers(Microsoft.Extensions.Hosting.IHostApplicationBuilder)">
|
||||||
|
<inheritdoc cref="P:Telegrator.ServicesCollectionExtensions.<G>$605D8CCF64349EA050C790D67C500BD9.Handlers"/>
|
||||||
|
</member>
|
||||||
<member name="M:Telegrator.ServicesCollectionExtensions.AddTelegratorWeb(Microsoft.AspNetCore.Builder.WebApplicationBuilder,Telegrator.Hosting.Web.TelegramBotWebOptions,Telegrator.Core.IHandlersCollection)">
|
<member name="M:Telegrator.ServicesCollectionExtensions.AddTelegratorWeb(Microsoft.AspNetCore.Builder.WebApplicationBuilder,Telegrator.Hosting.Web.TelegramBotWebOptions,Telegrator.Core.IHandlersCollection)">
|
||||||
<summary>
|
<summary>
|
||||||
Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
|
Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
|
||||||
@@ -227,5 +234,10 @@
|
|||||||
<param name="services"></param>
|
<param name="services"></param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="P:Telegrator.ServicesCollectionExtensions.<G>$605D8CCF64349EA050C790D67C500BD9.Handlers">
|
||||||
|
<summary>
|
||||||
|
Gets the <see cref="T:Telegrator.Core.IHandlersCollection"/> from the builder properties.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
</members>
|
</members>
|
||||||
</doc>
|
</doc>
|
||||||
|
|||||||
@@ -396,11 +396,29 @@
|
|||||||
</summary>
|
</summary>
|
||||||
<returns>The configured TelegramBotClientOptions instance.</returns>
|
<returns>The configured TelegramBotClientOptions instance.</returns>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="T:Telegrator.HostBuilderExtensions">
|
||||||
|
<summary>
|
||||||
|
Provides extension methods for <see cref="T:Microsoft.Extensions.Hosting.IHostApplicationBuilder"/> to configure Telegrator.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Telegrator.HostBuilderExtensions.HandlersCollectionPropertyKey">
|
||||||
|
<summary>
|
||||||
|
The key used to store the <see cref="T:Telegrator.Core.IHandlersCollection"/> in the builder properties.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Telegrator.HostBuilderExtensions.get_Handlers(Microsoft.Extensions.Hosting.IHostApplicationBuilder)">
|
||||||
|
<inheritdoc cref="P:Telegrator.HostBuilderExtensions.<G>$605D8CCF64349EA050C790D67C500BD9.Handlers"/>
|
||||||
|
</member>
|
||||||
<member name="M:Telegrator.HostBuilderExtensions.AddTelegrator(Microsoft.Extensions.Hosting.IHostApplicationBuilder,Telegrator.Hosting.TelegramBotHostBuilderSettings,Telegrator.Core.IHandlersCollection)">
|
<member name="M:Telegrator.HostBuilderExtensions.AddTelegrator(Microsoft.Extensions.Hosting.IHostApplicationBuilder,Telegrator.Hosting.TelegramBotHostBuilderSettings,Telegrator.Core.IHandlersCollection)">
|
||||||
<summary>
|
<summary>
|
||||||
Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
|
Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="P:Telegrator.HostBuilderExtensions.<G>$605D8CCF64349EA050C790D67C500BD9.Handlers">
|
||||||
|
<summary>
|
||||||
|
Gets the <see cref="T:Telegrator.Core.IHandlersCollection"/> from the builder properties.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="T:Telegrator.ServicesCollectionExtensions">
|
<member name="T:Telegrator.ServicesCollectionExtensions">
|
||||||
<summary>
|
<summary>
|
||||||
Contains extensions for <see cref="T:Microsoft.Extensions.DependencyInjection.IServiceCollection"/>
|
Contains extensions for <see cref="T:Microsoft.Extensions.DependencyInjection.IServiceCollection"/>
|
||||||
@@ -476,5 +494,17 @@
|
|||||||
<param name="routineMethod"></param>
|
<param name="routineMethod"></param>
|
||||||
<returns>True if the type implements IPreBuildingRoutine; otherwise, false.</returns>
|
<returns>True if the type implements IPreBuildingRoutine; otherwise, false.</returns>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="T:Telegrator.LoggerExtensions">
|
||||||
|
<summary>
|
||||||
|
Provides extension methods for logging Telegrator-related information.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Telegrator.LoggerExtensions.LogHandlers(Microsoft.Extensions.Logging.ILogger,Telegrator.Core.IHandlersCollection)">
|
||||||
|
<summary>
|
||||||
|
Logs the registered handlers to the specified logger.
|
||||||
|
</summary>
|
||||||
|
<param name="logger">The logger to write to.</param>
|
||||||
|
<param name="handlers">The collection of handlers to log.</param>
|
||||||
|
</member>
|
||||||
</members>
|
</members>
|
||||||
</doc>
|
</doc>
|
||||||
|
|||||||
@@ -8,3 +8,4 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
[assembly: SuppressMessage("Style", "IDE0290")]
|
[assembly: SuppressMessage("Style", "IDE0290")]
|
||||||
[assembly: SuppressMessage("Style", "IDE0090")]
|
[assembly: SuppressMessage("Style", "IDE0090")]
|
||||||
[assembly: SuppressMessage("Usage", "CA2254")]
|
[assembly: SuppressMessage("Usage", "CA2254")]
|
||||||
|
[assembly: SuppressMessage("Maintainability", "CA1510")]
|
||||||
|
|||||||
@@ -49,11 +49,12 @@ namespace Telegrator.Hosting.Web
|
|||||||
/// Initializes a new instance of the <see cref="WebApplicationBuilder"/> class.
|
/// Initializes a new instance of the <see cref="WebApplicationBuilder"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="webApplicationBuilder">The proxied instance of host builder.</param>
|
/// <param name="webApplicationBuilder">The proxied instance of host builder.</param>
|
||||||
/// <param name="handlers"></param>
|
public TelegramBotWebHost(WebApplicationBuilder webApplicationBuilder)
|
||||||
public TelegramBotWebHost(WebApplicationBuilder webApplicationBuilder, IHandlersCollection handlers)
|
|
||||||
{
|
{
|
||||||
// Registering this host in services for easy access
|
// Registering this host in services for easy access
|
||||||
RegisterHostServices(webApplicationBuilder.Services, handlers);
|
webApplicationBuilder.Services.AddSingleton<ITelegramBotHost>(this);
|
||||||
|
webApplicationBuilder.Services.AddSingleton<ITelegramBotWebHost>(this);
|
||||||
|
webApplicationBuilder.Services.AddSingleton<ITelegratorBot>(this);
|
||||||
|
|
||||||
// Building proxy application
|
// Building proxy application
|
||||||
_innerApp = webApplicationBuilder.Build();
|
_innerApp = webApplicationBuilder.Build();
|
||||||
@@ -161,15 +162,5 @@ namespace Telegrator.Hosting.Web
|
|||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RegisterHostServices(IServiceCollection services, IHandlersCollection handlers)
|
|
||||||
{
|
|
||||||
//service.RemoveAll<IHost>();
|
|
||||||
//service.AddSingleton<IHost>(this);
|
|
||||||
|
|
||||||
services.AddSingleton<ITelegramBotHost>(this);
|
|
||||||
services.AddSingleton<ITelegramBotWebHost>(this);
|
|
||||||
services.AddSingleton<ITelegratorBot>(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ namespace Telegrator.Hosting.Web
|
|||||||
_innerBuilder = webApplicationBuilder ?? throw new ArgumentNullException(nameof(webApplicationBuilder));
|
_innerBuilder = webApplicationBuilder ?? throw new ArgumentNullException(nameof(webApplicationBuilder));
|
||||||
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
||||||
_handlers = new HostHandlersCollection(Services, _settings);
|
_handlers = new HostHandlersCollection(Services, _settings);
|
||||||
|
|
||||||
|
_innerBuilder.AddTelegratorWeb(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -60,7 +62,7 @@ namespace Telegrator.Hosting.Web
|
|||||||
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
||||||
_handlers = handlers ?? throw new ArgumentNullException(nameof(settings));
|
_handlers = handlers ?? throw new ArgumentNullException(nameof(settings));
|
||||||
|
|
||||||
_innerBuilder.AddTelegratorWeb(settings);
|
_innerBuilder.AddTelegratorWeb(settings, handlers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -69,38 +71,7 @@ namespace Telegrator.Hosting.Web
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public TelegramBotWebHost Build()
|
public TelegramBotWebHost Build()
|
||||||
{
|
{
|
||||||
if (_handlers is IHostHandlersCollection hostHandlers)
|
return new TelegramBotWebHost(_innerBuilder);
|
||||||
{
|
|
||||||
foreach (PreBuildingRoutine preBuildRoutine in hostHandlers.PreBuilderRoutines)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
preBuildRoutine.Invoke(this);
|
|
||||||
}
|
|
||||||
catch (NotImplementedException)
|
|
||||||
{
|
|
||||||
_ = 0xBAD + 0xC0DE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_settings.DisableAutoConfigure)
|
|
||||||
{
|
|
||||||
Services.Configure<TelegratorWebOptions>(Configuration.GetSection(nameof(TelegratorWebOptions)));
|
|
||||||
Services.Configure<TelegramBotClientOptions>(Configuration.GetSection(nameof(TelegramBotClientOptions)), new TelegramBotClientOptionsProxy());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (null == Services.SingleOrDefault(srvc => srvc.ImplementationType == typeof(IOptions<TelegratorWebOptions>)))
|
|
||||||
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'TelegratorWebOptions' wasn't registered. This configuration is runtime required!");
|
|
||||||
|
|
||||||
if (null == Services.SingleOrDefault(srvc => srvc.ImplementationType == typeof(IOptions<TelegramBotClientOptions>)))
|
|
||||||
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'TelegramBotClientOptions' wasn't registered. This configuration is runtime required!");
|
|
||||||
}
|
|
||||||
|
|
||||||
Services.AddSingleton<IConfigurationManager>(Configuration);
|
|
||||||
Services.AddSingleton<IOptions<TelegratorOptions>>(Options.Create(_settings));
|
|
||||||
return new TelegramBotWebHost(_innerBuilder, _handlers);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@@ -20,6 +21,19 @@ namespace Telegrator
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ServicesCollectionExtensions
|
public static class ServicesCollectionExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The key used to store the <see cref="IHandlersCollection"/> in the builder properties.
|
||||||
|
/// </summary>
|
||||||
|
public const string HandlersCollectionPropertyKey = nameof(IHandlersCollection);
|
||||||
|
|
||||||
|
extension(IHostApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="IHandlersCollection"/> from the builder properties.
|
||||||
|
/// </summary>
|
||||||
|
public IHandlersCollection Handlers => (IHandlersCollection)builder.Properties[HandlersCollectionPropertyKey];
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
|
/// Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -32,6 +46,7 @@ namespace Telegrator
|
|||||||
ConfigurationManager configuration = builder.Configuration;
|
ConfigurationManager configuration = builder.Configuration;
|
||||||
|
|
||||||
handlers ??= new HostHandlersCollection(services, settings);
|
handlers ??= new HostHandlersCollection(services, settings);
|
||||||
|
builder.Host.Properties.Add(HandlersCollectionPropertyKey, handlers);
|
||||||
|
|
||||||
if (handlers is IHostHandlersCollection hostHandlers)
|
if (handlers is IHostHandlersCollection hostHandlers)
|
||||||
{
|
{
|
||||||
@@ -53,6 +68,7 @@ namespace Telegrator
|
|||||||
if (!settings.DisableAutoConfigure)
|
if (!settings.DisableAutoConfigure)
|
||||||
{
|
{
|
||||||
services.Configure<TelegratorWebOptions>(configuration.GetSection(nameof(TelegratorWebOptions)));
|
services.Configure<TelegratorWebOptions>(configuration.GetSection(nameof(TelegratorWebOptions)));
|
||||||
|
services.Configure<TelegratorWebOptions>(configuration.GetSection(nameof(TelegramBotClientOptions)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -92,8 +108,12 @@ namespace Telegrator
|
|||||||
ILoggerFactory loggerFactory = app.Services.GetRequiredService<ILoggerFactory>();
|
ILoggerFactory loggerFactory = app.Services.GetRequiredService<ILoggerFactory>();
|
||||||
ILogger logger = loggerFactory.CreateLogger("Telegrator.Hosting.Web.TelegratorHost");
|
ILogger logger = loggerFactory.CreateLogger("Telegrator.Hosting.Web.TelegratorHost");
|
||||||
|
|
||||||
logger.LogInformation("Telegrator Bot ASP.NET WebHost started");
|
if (logger.IsEnabled(LogLevel.Information))
|
||||||
logger.LogHandlers(handlers);
|
{
|
||||||
|
logger.LogInformation("Telegrator Bot ASP.NET WebHost started");
|
||||||
|
logger.LogInformation("Telegram Bot : {firstname}, @{usrname}, id:{id},", info.User.FirstName ?? "[NULL]", info.User.Username ?? "[NULL]", info.User.Id);
|
||||||
|
logger.LogHandlers(handlers);
|
||||||
|
}
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,3 +8,4 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
[assembly: SuppressMessage("Style", "IDE0290")]
|
[assembly: SuppressMessage("Style", "IDE0290")]
|
||||||
[assembly: SuppressMessage("Style", "IDE0090")]
|
[assembly: SuppressMessage("Style", "IDE0090")]
|
||||||
[assembly: SuppressMessage("Usage", "CA2254")]
|
[assembly: SuppressMessage("Usage", "CA2254")]
|
||||||
|
[assembly: SuppressMessage("Maintainability", "CA1510")]
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ namespace Telegrator.Hosting
|
|||||||
public TelegramBotHost(HostApplicationBuilder hostApplicationBuilder, IHandlersCollection handlers)
|
public TelegramBotHost(HostApplicationBuilder hostApplicationBuilder, IHandlersCollection handlers)
|
||||||
{
|
{
|
||||||
// Registering this host in services for easy access
|
// Registering this host in services for easy access
|
||||||
RegisterHostServices(hostApplicationBuilder.Services, handlers);
|
hostApplicationBuilder.Services.AddSingleton<ITelegramBotHost>(this);
|
||||||
|
hostApplicationBuilder.Services.AddSingleton<ITelegratorBot>(this);
|
||||||
|
|
||||||
// Building proxy hoster
|
// Building proxy hoster
|
||||||
_innerHost = hostApplicationBuilder.Build();
|
_innerHost = hostApplicationBuilder.Build();
|
||||||
@@ -122,14 +123,5 @@ namespace Telegrator.Hosting
|
|||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RegisterHostServices(IServiceCollection services, IHandlersCollection handlers)
|
|
||||||
{
|
|
||||||
//services.RemoveAll<IHost>();
|
|
||||||
//services.AddSingleton<IHost>(this);
|
|
||||||
|
|
||||||
services.AddSingleton<ITelegramBotHost>(this);
|
|
||||||
services.AddSingleton<ITelegratorBot>(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,228 +21,257 @@ using Telegrator.Logging;
|
|||||||
using Telegrator.Polling;
|
using Telegrator.Polling;
|
||||||
using Telegrator.Providers;
|
using Telegrator.Providers;
|
||||||
|
|
||||||
namespace Telegrator
|
namespace Telegrator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for <see cref="IHostApplicationBuilder"/> to configure Telegrator.
|
||||||
|
/// </summary>
|
||||||
|
public static class HostBuilderExtensions
|
||||||
{
|
{
|
||||||
public static class HostBuilderExtensions
|
/// <summary>
|
||||||
|
/// The key used to store the <see cref="IHandlersCollection"/> in the builder properties.
|
||||||
|
/// </summary>
|
||||||
|
public const string HandlersCollectionPropertyKey = nameof(IHandlersCollection);
|
||||||
|
|
||||||
|
extension (IHostApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
|
/// Gets the <see cref="IHandlersCollection"/> from the builder properties.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IHostApplicationBuilder AddTelegrator(this IHostApplicationBuilder builder, TelegramBotHostBuilderSettings settings, IHandlersCollection? handlers = null)
|
public IHandlersCollection Handlers => (IHandlersCollection)builder.Properties[HandlersCollectionPropertyKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces TelegramBotWebHostBuilder. Configures DI, options, and handlers.
|
||||||
|
/// </summary>
|
||||||
|
public static IHostApplicationBuilder AddTelegrator(this IHostApplicationBuilder builder, TelegramBotHostBuilderSettings settings, IHandlersCollection? handlers = null)
|
||||||
|
{
|
||||||
|
if (settings is null)
|
||||||
|
throw new ArgumentNullException(nameof(settings));
|
||||||
|
|
||||||
|
IServiceCollection services = builder.Services;
|
||||||
|
IConfigurationManager configuration = builder.Configuration;
|
||||||
|
|
||||||
|
handlers ??= new HostHandlersCollection(services, settings);
|
||||||
|
builder.Properties.Add(HandlersCollectionPropertyKey, handlers);
|
||||||
|
|
||||||
|
if (handlers is IHostHandlersCollection hostHandlers)
|
||||||
{
|
{
|
||||||
if (settings is null)
|
foreach (PreBuildingRoutine preBuildRoutine in hostHandlers.PreBuilderRoutines)
|
||||||
throw new ArgumentNullException(nameof(settings));
|
|
||||||
|
|
||||||
IServiceCollection services = builder.Services;
|
|
||||||
IConfigurationManager configuration = builder.Configuration;
|
|
||||||
|
|
||||||
handlers ??= new HostHandlersCollection(services, settings);
|
|
||||||
|
|
||||||
if (handlers is IHostHandlersCollection hostHandlers)
|
|
||||||
{
|
{
|
||||||
foreach (PreBuildingRoutine preBuildRoutine in hostHandlers.PreBuilderRoutines)
|
try
|
||||||
{
|
{
|
||||||
try
|
// TODO: fix
|
||||||
{
|
//preBuildRoutine.Invoke(builder);
|
||||||
// TODO: fix
|
Debug.WriteLine("Pre-Building routine was not executed");
|
||||||
//preBuildRoutine.Invoke(builder);
|
}
|
||||||
Debug.WriteLine("Pre-Building routine was not executed");
|
catch (NotImplementedException)
|
||||||
}
|
{
|
||||||
catch (NotImplementedException)
|
_ = 0xBAD + 0xC0DE;
|
||||||
{
|
|
||||||
_ = 0xBAD + 0xC0DE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!settings.DisableAutoConfigure)
|
|
||||||
{
|
|
||||||
services.Configure<ReceiverOptions>(configuration.GetSection(nameof(ReceiverOptions)));
|
|
||||||
services.Configure(configuration.GetSection(nameof(TelegramBotClientOptions)), new TelegramBotClientOptionsProxy());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
if (null == Services.SingleOrDefault(srvc => srvc.ImplementationType == typeof(IOptions<ReceiverOptions>)))
|
|
||||||
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'ReceiverOptions' wasn't registered. This configuration is runtime required!");
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (null == services.SingleOrDefault(srvc => srvc.ImplementationType == typeof(IOptions<TelegramBotClientOptions>)))
|
|
||||||
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'TelegramBotClientOptions' wasn't registered. This configuration is runtime required!");
|
|
||||||
}
|
|
||||||
|
|
||||||
IOptions<TelegramBotHostBuilderSettings> options = Options.Create(settings);
|
|
||||||
services.AddSingleton((IOptions<TelegratorOptions>)options);
|
|
||||||
services.AddTelegramBotHostDefaults();
|
|
||||||
services.AddSingleton(options);
|
|
||||||
services.AddSingleton(handlers);
|
|
||||||
|
|
||||||
if (handlers is IHandlersManager manager)
|
|
||||||
{
|
|
||||||
ServiceDescriptor descriptor = new ServiceDescriptor(typeof(IHandlersProvider), manager);
|
|
||||||
services.Replace(descriptor);
|
|
||||||
services.AddSingleton(manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
if (!settings.DisableAutoConfigure)
|
||||||
/// Contains extensions for <see cref="IServiceCollection"/>
|
|
||||||
/// Provides method to configure <see cref="ITelegramBotHost"/>
|
|
||||||
/// </summary>
|
|
||||||
public static class ServicesCollectionExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Registers a configuration instance that strongly-typed <typeparamref name="TOptions"/> will bind against using <see cref="ConfigureOptionsProxy{TOptions}"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TOptions"></typeparam>
|
|
||||||
/// <param name="services"></param>
|
|
||||||
/// <param name="configuration"></param>
|
|
||||||
/// <param name="optionsProxy"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, IConfiguration configuration, ConfigureOptionsProxy<TOptions> optionsProxy) where TOptions : class
|
|
||||||
{
|
{
|
||||||
optionsProxy.Configure(services, configuration);
|
services.Configure<ReceiverOptions>(configuration.GetSection(nameof(ReceiverOptions)));
|
||||||
return services;
|
services.Configure(configuration.GetSection(nameof(TelegramBotClientOptions)), new TelegramBotClientOptionsProxy());
|
||||||
}
|
}
|
||||||
|
else
|
||||||
/// <summary>
|
|
||||||
/// Registers <see cref="TelegramBotHost"/> default services
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="services"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static IServiceCollection AddTelegramBotHostDefaults(this IServiceCollection services)
|
|
||||||
{
|
{
|
||||||
services.AddLogging(builder => builder.AddConsole().AddDebug());
|
if (null == services.SingleOrDefault(srvc => srvc.ImplementationType == typeof(IOptions<ReceiverOptions>)))
|
||||||
services.AddSingleton<IUpdateHandlersPool, HostUpdateHandlersPool>();
|
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'ReceiverOptions' wasn't registered. This configuration is runtime required!");
|
||||||
services.AddSingleton<IAwaitingProvider, HostAwaitingProvider>();
|
|
||||||
services.AddSingleton<IHandlersProvider, HostHandlersProvider>();
|
|
||||||
services.AddSingleton<IUpdateRouter, HostUpdateRouter>();
|
|
||||||
services.AddSingleton<ITelegramBotInfo, HostedTelegramBotInfo>();
|
|
||||||
|
|
||||||
return services;
|
if (null == services.SingleOrDefault(srvc => srvc.ImplementationType == typeof(IOptions<TelegramBotClientOptions>)))
|
||||||
|
throw new MissingMemberException("Auto configuration disabled, yet no options of type 'TelegramBotClientOptions' wasn't registered. This configuration is runtime required!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
IOptions<TelegramBotHostBuilderSettings> options = Options.Create(settings);
|
||||||
/// Registers <see cref="ITelegramBotClient"/> service with <see cref="HostedUpdateReceiver"/> to receive updates using long polling
|
services.AddSingleton((IOptions<TelegratorOptions>)options);
|
||||||
/// </summary>
|
services.AddTelegramBotHostDefaults();
|
||||||
/// <param name="services"></param>
|
services.AddSingleton(options);
|
||||||
/// <returns></returns>
|
services.AddSingleton(handlers);
|
||||||
public static IServiceCollection AddTelegramReceiver(this IServiceCollection services)
|
|
||||||
|
if (handlers is IHandlersManager manager)
|
||||||
{
|
{
|
||||||
services.AddHttpClient<ITelegramBotClient>("tgreceiver").RemoveAllLoggers().AddTypedClient(TypedTelegramBotClientFactory);
|
ServiceDescriptor descriptor = new ServiceDescriptor(typeof(IHandlersProvider), manager);
|
||||||
services.AddHostedService<HostedUpdateReceiver>();
|
services.Replace(descriptor);
|
||||||
return services;
|
services.AddSingleton(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
return builder;
|
||||||
/// <see cref="ITelegramBotClient"/> factory method
|
}
|
||||||
/// </summary>
|
}
|
||||||
/// <param name="httpClient"></param>
|
|
||||||
/// <param name="provider"></param>
|
/// <summary>
|
||||||
/// <returns></returns>
|
/// Contains extensions for <see cref="IServiceCollection"/>
|
||||||
private static ITelegramBotClient TypedTelegramBotClientFactory(HttpClient httpClient, IServiceProvider provider)
|
/// Provides method to configure <see cref="ITelegramBotHost"/>
|
||||||
=> new TelegramBotClient(provider.GetRequiredService<IOptions<TelegramBotClientOptions>>().Value, httpClient);
|
/// </summary>
|
||||||
}
|
public static class ServicesCollectionExtensions
|
||||||
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides useful methods to adjust <see cref="ITelegramBotHost"/>
|
/// Registers a configuration instance that strongly-typed <typeparamref name="TOptions"/> will bind against using <see cref="ConfigureOptionsProxy{TOptions}"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class TelegramBotHostExtensions
|
/// <typeparam name="TOptions"></typeparam>
|
||||||
{
|
/// <param name="services"></param>
|
||||||
/// <summary>
|
/// <param name="configuration"></param>
|
||||||
/// Replaces the initialization logic from TelegramBotWebHost constructor.
|
/// <param name="optionsProxy"></param>
|
||||||
/// Initializes the bot and logs handlers on application startup.
|
/// <returns></returns>
|
||||||
/// </summary>
|
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, IConfiguration configuration, ConfigureOptionsProxy<TOptions> optionsProxy) where TOptions : class
|
||||||
public static IHost UseTelegrator(this IHost botHost)
|
{
|
||||||
{
|
optionsProxy.Configure(services, configuration);
|
||||||
ITelegramBotInfo info = botHost.Services.GetRequiredService<ITelegramBotInfo>();
|
return services;
|
||||||
IHandlersCollection handlers = botHost.Services.GetRequiredService<IHandlersCollection>();
|
}
|
||||||
ILoggerFactory loggerFactory = botHost.Services.GetRequiredService<ILoggerFactory>();
|
|
||||||
ILogger logger = loggerFactory.CreateLogger("Telegrator.Hosting.Web.TelegratorHost");
|
/// <summary>
|
||||||
|
/// Registers <see cref="TelegramBotHost"/> default services
|
||||||
logger.LogInformation("Telegrator Bot .NET Host started");
|
/// </summary>
|
||||||
logger.LogHandlers(handlers);
|
/// <param name="services"></param>
|
||||||
|
/// <returns></returns>
|
||||||
return botHost;
|
public static IServiceCollection AddTelegramBotHostDefaults(this IServiceCollection services)
|
||||||
}
|
{
|
||||||
|
services.AddLogging(builder => builder.AddConsole().AddDebug());
|
||||||
/// <summary>
|
services.AddSingleton<IUpdateHandlersPool, HostUpdateHandlersPool>();
|
||||||
/// Configures bots available commands depending on what handlers was registered
|
services.AddSingleton<IAwaitingProvider, HostAwaitingProvider>();
|
||||||
/// </summary>
|
services.AddSingleton<IHandlersProvider, HostHandlersProvider>();
|
||||||
/// <param name="botHost"></param>
|
services.AddSingleton<IUpdateRouter, HostUpdateRouter>();
|
||||||
/// <returns></returns>
|
services.AddSingleton<ITelegramBotInfo, HostedTelegramBotInfo>();
|
||||||
public static IHost SetBotCommands(this IHost botHost)
|
|
||||||
{
|
return services;
|
||||||
ITelegramBotClient client = botHost.Services.GetRequiredService<ITelegramBotClient>();
|
}
|
||||||
IUpdateRouter router = botHost.Services.GetRequiredService<IUpdateRouter>();
|
|
||||||
|
/// <summary>
|
||||||
IEnumerable<BotCommand> aliases = router.HandlersProvider.GetBotCommands();
|
/// Registers <see cref="ITelegramBotClient"/> service with <see cref="HostedUpdateReceiver"/> to receive updates using long polling
|
||||||
client.SetMyCommands(aliases).Wait();
|
/// </summary>
|
||||||
return botHost;
|
/// <param name="services"></param>
|
||||||
}
|
/// <returns></returns>
|
||||||
|
public static IServiceCollection AddTelegramReceiver(this IServiceCollection services)
|
||||||
/// <summary>
|
{
|
||||||
/// Adds a Microsoft.Extensions.Logging adapter to Alligator using a logger factory.
|
services.AddHttpClient<ITelegramBotClient>("tgreceiver").RemoveAllLoggers().AddTypedClient(TypedTelegramBotClientFactory);
|
||||||
/// </summary>
|
services.AddHostedService<HostedUpdateReceiver>();
|
||||||
/// <param name="host"></param>
|
return services;
|
||||||
public static IHost AddLoggingAdapter(this IHost host)
|
}
|
||||||
{
|
|
||||||
ILoggerFactory loggerFactory = host.Services.GetRequiredService<ILoggerFactory>();
|
/// <summary>
|
||||||
ILogger logger = loggerFactory.CreateLogger("Telegrator");
|
/// <see cref="ITelegramBotClient"/> factory method
|
||||||
|
/// </summary>
|
||||||
MicrosoftLoggingAdapter adapter = new MicrosoftLoggingAdapter(logger);
|
/// <param name="httpClient"></param>
|
||||||
TelegratorLogging.AddAdapter(adapter);
|
/// <param name="provider"></param>
|
||||||
return host;
|
/// <returns></returns>
|
||||||
}
|
private static ITelegramBotClient TypedTelegramBotClientFactory(HttpClient httpClient, IServiceProvider provider)
|
||||||
}
|
=> new TelegramBotClient(provider.GetRequiredService<IOptions<TelegramBotClientOptions>>().Value, httpClient);
|
||||||
|
}
|
||||||
/// <summary>
|
|
||||||
/// Provides extension methods for reflection and type inspection.
|
/// <summary>
|
||||||
/// </summary>
|
/// Provides useful methods to adjust <see cref="ITelegramBotHost"/>
|
||||||
public static class ReflectionExtensions
|
/// </summary>
|
||||||
{
|
public static class TelegramBotHostExtensions
|
||||||
/// <summary>
|
{
|
||||||
/// Checks if a type implements the <see cref="IPreBuildingRoutine"/> interface.
|
/// <summary>
|
||||||
/// </summary>
|
/// Replaces the initialization logic from TelegramBotWebHost constructor.
|
||||||
/// <param name="handlerType">The type to check.</param>
|
/// Initializes the bot and logs handlers on application startup.
|
||||||
/// <param name="routineMethod"></param>
|
/// </summary>
|
||||||
/// <returns>True if the type implements IPreBuildingRoutine; otherwise, false.</returns>
|
public static IHost UseTelegrator(this IHost botHost)
|
||||||
public static bool IsPreBuildingRoutine(this Type handlerType, [NotNullWhen(true)] out MethodInfo? routineMethod)
|
{
|
||||||
{
|
ITelegramBotInfo info = botHost.Services.GetRequiredService<ITelegramBotInfo>();
|
||||||
routineMethod = null;
|
IHandlersCollection handlers = botHost.Services.GetRequiredService<IHandlersCollection>();
|
||||||
if (handlerType.GetInterface(nameof(IPreBuildingRoutine)) == null)
|
ILoggerFactory loggerFactory = botHost.Services.GetRequiredService<ILoggerFactory>();
|
||||||
return false;
|
ILogger logger = loggerFactory.CreateLogger("Telegrator.Hosting.Web.TelegratorHost");
|
||||||
|
|
||||||
routineMethod = handlerType.GetMethod(nameof(IPreBuildingRoutine.PreBuildingRoutine), BindingFlags.Static | BindingFlags.Public);
|
if (logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information))
|
||||||
return routineMethod != null;
|
{
|
||||||
}
|
logger.LogInformation("Telegrator Bot .NET Host started");
|
||||||
}
|
logger.LogInformation("Telegram Bot : {firstname}, @{usrname}, id:{id},", info.User.FirstName ?? "[NULL]", info.User.Username ?? "[NULL]", info.User.Id);
|
||||||
|
logger.LogHandlers(handlers);
|
||||||
public static class LoggerExtensions
|
}
|
||||||
{
|
|
||||||
public static void LogHandlers(this ILogger logger, IHandlersCollection handlers)
|
return botHost;
|
||||||
{
|
}
|
||||||
StringBuilder logBuilder = new StringBuilder("Registered handlers : ");
|
|
||||||
if (!handlers.Keys.Any())
|
/// <summary>
|
||||||
throw new Exception();
|
/// Configures bots available commands depending on what handlers was registered
|
||||||
|
/// </summary>
|
||||||
foreach (UpdateType updateType in handlers.Keys)
|
/// <param name="botHost"></param>
|
||||||
{
|
/// <returns></returns>
|
||||||
HandlerDescriptorList descriptors = handlers[updateType];
|
public static IHost SetBotCommands(this IHost botHost)
|
||||||
logBuilder.Append("\n\tUpdateType." + updateType + " :");
|
{
|
||||||
|
ITelegramBotClient client = botHost.Services.GetRequiredService<ITelegramBotClient>();
|
||||||
foreach (HandlerDescriptor descriptor in descriptors.Reverse())
|
IUpdateRouter router = botHost.Services.GetRequiredService<IUpdateRouter>();
|
||||||
{
|
|
||||||
logBuilder.AppendFormat("\n\t* {0} - {1}",
|
IEnumerable<BotCommand> aliases = router.HandlersProvider.GetBotCommands();
|
||||||
descriptor.Indexer.ToString(),
|
client.SetMyCommands(aliases).Wait();
|
||||||
descriptor.ToString());
|
return botHost;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
logger.LogInformation(logBuilder.ToString());
|
/// Adds a Microsoft.Extensions.Logging adapter to Alligator using a logger factory.
|
||||||
}
|
/// </summary>
|
||||||
|
/// <param name="host"></param>
|
||||||
|
public static IHost AddLoggingAdapter(this IHost host)
|
||||||
|
{
|
||||||
|
ILoggerFactory loggerFactory = host.Services.GetRequiredService<ILoggerFactory>();
|
||||||
|
ILogger logger = loggerFactory.CreateLogger("Telegrator");
|
||||||
|
|
||||||
|
MicrosoftLoggingAdapter adapter = new MicrosoftLoggingAdapter(logger);
|
||||||
|
TelegratorLogging.AddAdapter(adapter);
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for reflection and type inspection.
|
||||||
|
/// </summary>
|
||||||
|
public static class ReflectionExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a type implements the <see cref="IPreBuildingRoutine"/> interface.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handlerType">The type to check.</param>
|
||||||
|
/// <param name="routineMethod"></param>
|
||||||
|
/// <returns>True if the type implements IPreBuildingRoutine; otherwise, false.</returns>
|
||||||
|
public static bool IsPreBuildingRoutine(this Type handlerType, [NotNullWhen(true)] out MethodInfo? routineMethod)
|
||||||
|
{
|
||||||
|
routineMethod = null;
|
||||||
|
if (handlerType.GetInterface(nameof(IPreBuildingRoutine)) == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
routineMethod = handlerType.GetMethod(nameof(IPreBuildingRoutine.PreBuildingRoutine), BindingFlags.Static | BindingFlags.Public);
|
||||||
|
return routineMethod != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for logging Telegrator-related information.
|
||||||
|
/// </summary>
|
||||||
|
public static class LoggerExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Logs the registered handlers to the specified logger.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">The logger to write to.</param>
|
||||||
|
/// <param name="handlers">The collection of handlers to log.</param>
|
||||||
|
public static void LogHandlers(this ILogger logger, IHandlersCollection handlers)
|
||||||
|
{
|
||||||
|
if (!logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Information))
|
||||||
|
return;
|
||||||
|
|
||||||
|
StringBuilder logBuilder = new StringBuilder("Registered handlers : ");
|
||||||
|
if (!handlers.Keys.Any())
|
||||||
|
throw new Exception();
|
||||||
|
|
||||||
|
foreach (UpdateType updateType in handlers.Keys)
|
||||||
|
{
|
||||||
|
HandlerDescriptorList descriptors = handlers[updateType];
|
||||||
|
logBuilder.Append("\n\tUpdateType." + updateType + " :");
|
||||||
|
|
||||||
|
foreach (HandlerDescriptor descriptor in descriptors.Reverse())
|
||||||
|
{
|
||||||
|
logBuilder.AppendFormat("\n\t* {0} - {1}",
|
||||||
|
descriptor.Indexer.ToString(),
|
||||||
|
descriptor.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogInformation(logBuilder.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,9 @@
|
|||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegrator.Core.Handlers;
|
using Telegrator.Core.Handlers;
|
||||||
|
|
||||||
namespace Telegrator.Localized
|
namespace Telegrator.Localized;
|
||||||
|
|
||||||
|
public interface ILocalizedHandler<T> : IAbstractUpdateHandler<Message> where T : class
|
||||||
{
|
{
|
||||||
public interface ILocalizedHandler<T> : IAbstractUpdateHandler<Message> where T : class
|
public IStringLocalizer LocalizationProvider { get; }
|
||||||
{
|
|
||||||
public IStringLocalizer LocalizationProvider { get; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
|
|
||||||
namespace Telegrator.Localized
|
namespace Telegrator.Localized;
|
||||||
{
|
|
||||||
public interface ILocalizedMessageHandler : ILocalizedHandler<Message>
|
public interface ILocalizedMessageHandler : ILocalizedHandler<Message>
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ using Telegrator.Logging;
|
|||||||
|
|
||||||
namespace Telegrator;
|
namespace Telegrator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a continuous chat action that runs in the background until cancelled or disposed.
|
||||||
|
/// </summary>
|
||||||
public class ContinuousAction : IDisposable
|
public class ContinuousAction : IDisposable
|
||||||
{
|
{
|
||||||
private readonly ITelegramBotClient _client;
|
private readonly ITelegramBotClient _client;
|
||||||
@@ -17,6 +20,14 @@ public class ContinuousAction : IDisposable
|
|||||||
|
|
||||||
private int _disposed;
|
private int _disposed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ContinuousAction"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="client">The Telegram bot client.</param>
|
||||||
|
/// <param name="chat">The target chat.</param>
|
||||||
|
/// <param name="action">The action to perform continuously.</param>
|
||||||
|
/// <param name="delay">The delay between actions. Defaults to 4 seconds.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
public ContinuousAction(ITelegramBotClient client, ChatId chat, ChatAction action, TimeSpan? delay = null, CancellationToken cancellationToken = default)
|
public ContinuousAction(ITelegramBotClient client, ChatId chat, ChatAction action, TimeSpan? delay = null, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
_client = client;
|
_client = client;
|
||||||
@@ -52,6 +63,9 @@ public class ContinuousAction : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cancels the continuous action.
|
||||||
|
/// </summary>
|
||||||
public void Cancel()
|
public void Cancel()
|
||||||
{
|
{
|
||||||
if (Interlocked.CompareExchange(ref _disposed, 0, 0) == 0)
|
if (Interlocked.CompareExchange(ref _disposed, 0, 0) == 0)
|
||||||
@@ -67,11 +81,17 @@ public class ContinuousAction : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Waits for the background worker task to complete.
|
||||||
|
/// </summary>
|
||||||
public async Task WaitAsync()
|
public async Task WaitAsync()
|
||||||
{
|
{
|
||||||
await _workerTask.ConfigureAwait(false);
|
await _workerTask.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disposes the instance and stops the continuous action.
|
||||||
|
/// </summary>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (Interlocked.Exchange(ref _disposed, 1) == 1)
|
if (Interlocked.Exchange(ref _disposed, 1) == 1)
|
||||||
@@ -85,5 +105,7 @@ public class ContinuousAction : IDisposable
|
|||||||
{
|
{
|
||||||
_linkedCts.Dispose();
|
_linkedCts.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace Telegrator.Core.Descriptors
|
|||||||
private readonly ManualResetEventSlim ResetEvent = new ManualResetEventSlim(false);
|
private readonly ManualResetEventSlim ResetEvent = new ManualResetEventSlim(false);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// descriptor from that handler was described from
|
/// Descriptor from that handler was described from.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public HandlerDescriptor From { get; }
|
public HandlerDescriptor From { get; }
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ namespace Telegrator.Core.Descriptors
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Telegram bot client used for this handler.
|
/// The Telegram bot client used for this handler.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ITelegramBotClient Client { get; }
|
public ITelegramBotClient Client { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The handler instance being described.
|
/// The handler instance being described.
|
||||||
@@ -71,9 +71,9 @@ namespace Telegrator.Core.Descriptors
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="DescribedHandlerDescriptor"/> class.
|
/// Initializes a new instance of the <see cref="DescribedHandlerDescriptor"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fromDescriptor">descriptor from that handler was described from</param>
|
/// <param name="fromDescriptor">The descriptor from which this handler was described.</param>
|
||||||
/// <param name="awaitingProvider"></param>
|
|
||||||
/// <param name="updateRouter">The update router.</param>
|
/// <param name="updateRouter">The update router.</param>
|
||||||
|
/// <param name="awaitingProvider">The awaiting provider.</param>
|
||||||
/// <param name="client">The Telegram bot client.</param>
|
/// <param name="client">The Telegram bot client.</param>
|
||||||
/// <param name="handlerInstance">The handler instance.</param>
|
/// <param name="handlerInstance">The handler instance.</param>
|
||||||
/// <param name="filterContext">The filter execution context.</param>
|
/// <param name="filterContext">The filter execution context.</param>
|
||||||
@@ -98,6 +98,10 @@ namespace Telegrator.Core.Descriptors
|
|||||||
DisplayString = displayString ?? fromDescriptor.HandlerType.Name;
|
DisplayString = displayString ?? fromDescriptor.HandlerType.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Waits for the handler execution result.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
public async Task AwaitResult(CancellationToken cancellationToken)
|
public async Task AwaitResult(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
await Task.Yield();
|
await Task.Yield();
|
||||||
@@ -105,6 +109,10 @@ namespace Telegrator.Core.Descriptors
|
|||||||
ResetEvent.Wait(cancellationToken);
|
ResetEvent.Wait(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reports the execution result and signals completion.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">The execution result.</param>
|
||||||
public void ReportResult(Result? result)
|
public void ReportResult(Result? result)
|
||||||
{
|
{
|
||||||
if (result != null)
|
if (result != null)
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ using Telegram.Bot.Types;
|
|||||||
namespace Telegrator.Core;
|
namespace Telegrator.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Requests new <see cref="Update"/>s and processes them using provided <see cref="IUpdateHandler"/> instance<
|
/// Requests new <see cref="Update"/>s and processes them using provided <see cref="IUpdateHandler"/> instance.
|
||||||
/// /summary>
|
/// </summary>
|
||||||
public interface IUpdateReceiver
|
public interface IUpdateReceiver
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -18,8 +18,14 @@ namespace Telegrator.Mediation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly object SyncObj = new object();
|
protected readonly object SyncObj = new object();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The task responsible for reading and processing handlers from the channel.
|
||||||
|
/// </summary>
|
||||||
protected readonly Task ChannelReaderTask;
|
protected readonly Task ChannelReaderTask;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The channel used to queue handlers for execution.
|
||||||
|
/// </summary>
|
||||||
protected readonly Channel<DescribedHandlerDescriptor> ExecutionChannel;
|
protected readonly Channel<DescribedHandlerDescriptor> ExecutionChannel;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -27,6 +33,9 @@ namespace Telegrator.Mediation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly SemaphoreSlim? ExecutionLimiter;
|
protected readonly SemaphoreSlim? ExecutionLimiter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The update router associated with this pool.
|
||||||
|
/// </summary>
|
||||||
protected readonly IUpdateRouter UpdateRouter;
|
protected readonly IUpdateRouter UpdateRouter;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -4,314 +4,313 @@ using Telegrator.Core.Descriptors;
|
|||||||
using Telegrator.Core.Filters;
|
using Telegrator.Core.Filters;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Telegrator.Tests.Collections
|
namespace Telegrator.Tests.Collections;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тесты для коллекций.
|
||||||
|
///
|
||||||
|
/// ПАРАДИГМЫ ТЕСТИРОВАНИЯ:
|
||||||
|
/// 1. Collection Testing - тестирование коллекций и их операций
|
||||||
|
/// 2. List Testing - тестирование списков
|
||||||
|
/// 3. Indexing Testing - тестирование индексации
|
||||||
|
/// 4. Enumeration Testing - тестирование перечисления
|
||||||
|
/// 5. Capacity Testing - тестирование емкости коллекций
|
||||||
|
/// </summary>
|
||||||
|
public class CollectionTests
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Тесты для коллекций.
|
/// Тест для HandlerDescriptorList - создание списка.
|
||||||
///
|
///
|
||||||
/// ПАРАДИГМЫ ТЕСТИРОВАНИЯ:
|
/// ПРИНЦИП: Тестируем создание коллекций
|
||||||
/// 1. Collection Testing - тестирование коллекций и их операций
|
|
||||||
/// 2. List Testing - тестирование списков
|
|
||||||
/// 3. Indexing Testing - тестирование индексации
|
|
||||||
/// 4. Enumeration Testing - тестирование перечисления
|
|
||||||
/// 5. Capacity Testing - тестирование емкости коллекций
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CollectionTests
|
[Fact]
|
||||||
|
public void HandlerDescriptorList_ShouldBeCreated()
|
||||||
{
|
{
|
||||||
/// <summary>
|
// Arrange & Act
|
||||||
/// Тест для HandlerDescriptorList - создание списка.
|
var list = new HandlerDescriptorList();
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем создание коллекций
|
|
||||||
/// </summary>
|
|
||||||
[Fact]
|
|
||||||
public void HandlerDescriptorList_ShouldBeCreated()
|
|
||||||
{
|
|
||||||
// Arrange & Act
|
|
||||||
var list = new HandlerDescriptorList();
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
list.Should().NotBeNull();
|
list.Should().NotBeNull();
|
||||||
list.Should().BeEmpty();
|
list.Should().BeEmpty();
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тест для HandlerDescriptorList - добавление дескриптора.
|
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем добавление элементов в коллекцию
|
|
||||||
/// </summary>
|
|
||||||
[Fact]
|
|
||||||
public void HandlerDescriptorList_Add_ShouldAddDescriptor()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var list = new HandlerDescriptorList();
|
|
||||||
var descriptor = CreateTestDescriptor(UpdateType.Message);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
list.Add(descriptor);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
list.Should().HaveCount(1);
|
|
||||||
list.Should().Contain(descriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тест для HandlerDescriptorList - добавление нескольких дескрипторов.
|
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем множественные операции
|
|
||||||
/// </summary>
|
|
||||||
[Fact]
|
|
||||||
public void HandlerDescriptorList_AddMultiple_ShouldAddAllDescriptors()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var list = new HandlerDescriptorList();
|
|
||||||
var descriptor1 = CreateTestDescriptor(UpdateType.Message);
|
|
||||||
var descriptor2 = CreateTestDescriptor(UpdateType.CallbackQuery);
|
|
||||||
var descriptor3 = CreateTestDescriptor(UpdateType.InlineQuery);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
list.Add(descriptor1);
|
|
||||||
list.Add(descriptor2);
|
|
||||||
list.Add(descriptor3);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
list.Should().HaveCount(3);
|
|
||||||
list.Should().Contain(descriptor1);
|
|
||||||
list.Should().Contain(descriptor2);
|
|
||||||
list.Should().Contain(descriptor3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тест для HandlerDescriptorList - получение по индексу.
|
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем индексацию коллекций
|
|
||||||
/// </summary>
|
|
||||||
[Fact]
|
|
||||||
public void HandlerDescriptorList_Indexer_ShouldReturnDescriptorAtIndex()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var descriptor = CreateTestDescriptor(UpdateType.Message);
|
|
||||||
var list = new HandlerDescriptorList
|
|
||||||
{
|
|
||||||
descriptor
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = list[0];
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
result.Should().Be(descriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тест для HandlerDescriptorList - получение по неверному индексу.
|
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем исключения при некорректном доступе
|
|
||||||
/// </summary>
|
|
||||||
[Theory]
|
|
||||||
[InlineData(-1)]
|
|
||||||
[InlineData(1)]
|
|
||||||
[InlineData(100)]
|
|
||||||
public void HandlerDescriptorList_IndexerWithInvalidIndex_ShouldThrowArgumentOutOfRangeException(int invalidIndex)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var list = new HandlerDescriptorList
|
|
||||||
{
|
|
||||||
CreateTestDescriptor(UpdateType.Message)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act & Assert
|
|
||||||
list.Invoking(l => _ = l[invalidIndex])
|
|
||||||
.Should().Throw<ArgumentOutOfRangeException>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тест для HandlerDescriptorList - перечисление элементов.
|
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем перечисление коллекций
|
|
||||||
/// </summary>
|
|
||||||
[Fact]
|
|
||||||
public void HandlerDescriptorList_ShouldBeEnumerable()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var descriptor1 = CreateTestDescriptor(UpdateType.Message);
|
|
||||||
var descriptor2 = CreateTestDescriptor(UpdateType.CallbackQuery);
|
|
||||||
var list = new HandlerDescriptorList
|
|
||||||
{
|
|
||||||
descriptor1,
|
|
||||||
descriptor2
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var enumeratedItems = list.ToList();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
enumeratedItems.Should().HaveCount(2);
|
|
||||||
enumeratedItems.Should().Contain(descriptor1);
|
|
||||||
enumeratedItems.Should().Contain(descriptor2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тест для HandlerDescriptorList - очистка списка.
|
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем очистку коллекций
|
|
||||||
/// </summary>
|
|
||||||
[Fact]
|
|
||||||
public void HandlerDescriptorList_Clear_ShouldRemoveAllDescriptors()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var list = new HandlerDescriptorList
|
|
||||||
{
|
|
||||||
CreateTestDescriptor(UpdateType.Message),
|
|
||||||
CreateTestDescriptor(UpdateType.CallbackQuery)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
list.Clear();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
list.Should().BeEmpty();
|
|
||||||
list.Should().HaveCount(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тест для HandlerDescriptorList - проверка содержания элемента.
|
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем поиск в коллекциях
|
|
||||||
/// </summary>
|
|
||||||
[Fact]
|
|
||||||
public void HandlerDescriptorList_Contains_ShouldReturnCorrectResult()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var list = new HandlerDescriptorList();
|
|
||||||
var descriptor = CreateTestDescriptor(UpdateType.Message);
|
|
||||||
var nonExistentDescriptor = CreateTestDescriptor(UpdateType.CallbackQuery);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
list.Add(descriptor);
|
|
||||||
var containsExisting = list.Contains(descriptor);
|
|
||||||
var containsNonExistent = list.Contains(nonExistentDescriptor);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
containsExisting.Should().BeTrue();
|
|
||||||
containsNonExistent.Should().BeFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тест для HandlerDescriptorList - удаление элемента.
|
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем удаление элементов из коллекций
|
|
||||||
/// </summary>
|
|
||||||
[Fact]
|
|
||||||
public void HandlerDescriptorList_Remove_ShouldRemoveDescriptor()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var list = new HandlerDescriptorList();
|
|
||||||
var descriptor = CreateTestDescriptor(UpdateType.Message);
|
|
||||||
list.Add(descriptor);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var removed = list.Remove(descriptor);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
removed.Should().BeTrue();
|
|
||||||
list.Should().BeEmpty();
|
|
||||||
list.Should().NotContain(descriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тест для HandlerDescriptorList - удаление несуществующего элемента.
|
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем удаление несуществующих элементов
|
|
||||||
/// </summary>
|
|
||||||
[Fact]
|
|
||||||
public void HandlerDescriptorList_RemoveNonExistent_ShouldReturnFalse()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var list = new HandlerDescriptorList();
|
|
||||||
var nonExistentDescriptor = CreateTestDescriptor(UpdateType.CallbackQuery);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var removed = list.Remove(nonExistentDescriptor);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
removed.Should().BeFalse();
|
|
||||||
list.Should().BeEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тест для CompletedFiltersList - создание списка.
|
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем создание специализированных коллекций
|
|
||||||
/// </summary>
|
|
||||||
[Fact]
|
|
||||||
public void CompletedFiltersList_ShouldBeCreated()
|
|
||||||
{
|
|
||||||
// Arrange & Act
|
|
||||||
var list = new CompletedFiltersList();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
list.Should().NotBeNull();
|
|
||||||
list.Should().BeEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тест для проверки производительности коллекций.
|
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем производительность при большом количестве элементов
|
|
||||||
/// </summary>
|
|
||||||
[Fact]
|
|
||||||
public void HandlerDescriptorList_ShouldHandleLargeNumberOfItems()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var list = new HandlerDescriptorList();
|
|
||||||
var itemsCount = 1000;
|
|
||||||
|
|
||||||
// Act
|
|
||||||
for (int i = 0; i < itemsCount; i++)
|
|
||||||
{
|
|
||||||
list.Add(CreateTestDescriptor(UpdateType.Message));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
list.Should().HaveCount(itemsCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тест для проверки потокобезопасности (базовый тест).
|
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем базовую потокобезопасность
|
|
||||||
/// </summary>
|
|
||||||
[Fact]
|
|
||||||
public async void HandlerDescriptorList_ShouldHandleConcurrentAccess()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var list = new HandlerDescriptorList();
|
|
||||||
var tasks = new List<Task>();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
for (int i = 0; i < 10; i++)
|
|
||||||
{
|
|
||||||
tasks.Add(Task.Run(() =>
|
|
||||||
{
|
|
||||||
for (int j = 0; j < 10; j++)
|
|
||||||
{
|
|
||||||
list.Add(CreateTestDescriptor(UpdateType.Message));
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.WhenAll(tasks.ToArray());
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
list.Should().HaveCount(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Вспомогательный метод для создания тестового дескриптора.
|
|
||||||
/// </summary>
|
|
||||||
private static HandlerDescriptor CreateTestDescriptor(UpdateType updateType)
|
|
||||||
{
|
|
||||||
return new HandlerDescriptor(DescriptorType.General, typeof(TestUpdateHandler));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тест для HandlerDescriptorList - добавление дескриптора.
|
||||||
|
///
|
||||||
|
/// ПРИНЦИП: Тестируем добавление элементов в коллекцию
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public void HandlerDescriptorList_Add_ShouldAddDescriptor()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var list = new HandlerDescriptorList();
|
||||||
|
var descriptor = CreateTestDescriptor(UpdateType.Message);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.Add(descriptor);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
list.Should().HaveCount(1);
|
||||||
|
list.Should().Contain(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тест для HandlerDescriptorList - добавление нескольких дескрипторов.
|
||||||
|
///
|
||||||
|
/// ПРИНЦИП: Тестируем множественные операции
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public void HandlerDescriptorList_AddMultiple_ShouldAddAllDescriptors()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var list = new HandlerDescriptorList();
|
||||||
|
var descriptor1 = CreateTestDescriptor(UpdateType.Message);
|
||||||
|
var descriptor2 = CreateTestDescriptor(UpdateType.CallbackQuery);
|
||||||
|
var descriptor3 = CreateTestDescriptor(UpdateType.InlineQuery);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.Add(descriptor1);
|
||||||
|
list.Add(descriptor2);
|
||||||
|
list.Add(descriptor3);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
list.Should().HaveCount(3);
|
||||||
|
list.Should().Contain(descriptor1);
|
||||||
|
list.Should().Contain(descriptor2);
|
||||||
|
list.Should().Contain(descriptor3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тест для HandlerDescriptorList - получение по индексу.
|
||||||
|
///
|
||||||
|
/// ПРИНЦИП: Тестируем индексацию коллекций
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public void HandlerDescriptorList_Indexer_ShouldReturnDescriptorAtIndex()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = CreateTestDescriptor(UpdateType.Message);
|
||||||
|
var list = new HandlerDescriptorList
|
||||||
|
{
|
||||||
|
descriptor
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = list[0];
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тест для HandlerDescriptorList - получение по неверному индексу.
|
||||||
|
///
|
||||||
|
/// ПРИНЦИП: Тестируем исключения при некорректном доступе
|
||||||
|
/// </summary>
|
||||||
|
[Theory]
|
||||||
|
[InlineData(-1)]
|
||||||
|
[InlineData(1)]
|
||||||
|
[InlineData(100)]
|
||||||
|
public void HandlerDescriptorList_IndexerWithInvalidIndex_ShouldThrowArgumentOutOfRangeException(int invalidIndex)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var list = new HandlerDescriptorList
|
||||||
|
{
|
||||||
|
CreateTestDescriptor(UpdateType.Message)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
list.Invoking(l => _ = l[invalidIndex])
|
||||||
|
.Should().Throw<ArgumentOutOfRangeException>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тест для HandlerDescriptorList - перечисление элементов.
|
||||||
|
///
|
||||||
|
/// ПРИНЦИП: Тестируем перечисление коллекций
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public void HandlerDescriptorList_ShouldBeEnumerable()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor1 = CreateTestDescriptor(UpdateType.Message);
|
||||||
|
var descriptor2 = CreateTestDescriptor(UpdateType.CallbackQuery);
|
||||||
|
var list = new HandlerDescriptorList
|
||||||
|
{
|
||||||
|
descriptor1,
|
||||||
|
descriptor2
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var enumeratedItems = list.ToList();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
enumeratedItems.Should().HaveCount(2);
|
||||||
|
enumeratedItems.Should().Contain(descriptor1);
|
||||||
|
enumeratedItems.Should().Contain(descriptor2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тест для HandlerDescriptorList - очистка списка.
|
||||||
|
///
|
||||||
|
/// ПРИНЦИП: Тестируем очистку коллекций
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public void HandlerDescriptorList_Clear_ShouldRemoveAllDescriptors()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var list = new HandlerDescriptorList
|
||||||
|
{
|
||||||
|
CreateTestDescriptor(UpdateType.Message),
|
||||||
|
CreateTestDescriptor(UpdateType.CallbackQuery)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.Clear();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
list.Should().BeEmpty();
|
||||||
|
list.Should().HaveCount(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тест для HandlerDescriptorList - проверка содержания элемента.
|
||||||
|
///
|
||||||
|
/// ПРИНЦИП: Тестируем поиск в коллекциях
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public void HandlerDescriptorList_Contains_ShouldReturnCorrectResult()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var list = new HandlerDescriptorList();
|
||||||
|
var descriptor = CreateTestDescriptor(UpdateType.Message);
|
||||||
|
var nonExistentDescriptor = CreateTestDescriptor(UpdateType.CallbackQuery);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
list.Add(descriptor);
|
||||||
|
var containsExisting = list.Contains(descriptor);
|
||||||
|
var containsNonExistent = list.Contains(nonExistentDescriptor);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
containsExisting.Should().BeTrue();
|
||||||
|
containsNonExistent.Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тест для HandlerDescriptorList - удаление элемента.
|
||||||
|
///
|
||||||
|
/// ПРИНЦИП: Тестируем удаление элементов из коллекций
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public void HandlerDescriptorList_Remove_ShouldRemoveDescriptor()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var list = new HandlerDescriptorList();
|
||||||
|
var descriptor = CreateTestDescriptor(UpdateType.Message);
|
||||||
|
list.Add(descriptor);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var removed = list.Remove(descriptor);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
removed.Should().BeTrue();
|
||||||
|
list.Should().BeEmpty();
|
||||||
|
list.Should().NotContain(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тест для HandlerDescriptorList - удаление несуществующего элемента.
|
||||||
|
///
|
||||||
|
/// ПРИНЦИП: Тестируем удаление несуществующих элементов
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public void HandlerDescriptorList_RemoveNonExistent_ShouldReturnFalse()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var list = new HandlerDescriptorList();
|
||||||
|
var nonExistentDescriptor = CreateTestDescriptor(UpdateType.CallbackQuery);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var removed = list.Remove(nonExistentDescriptor);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
removed.Should().BeFalse();
|
||||||
|
list.Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тест для CompletedFiltersList - создание списка.
|
||||||
|
///
|
||||||
|
/// ПРИНЦИП: Тестируем создание специализированных коллекций
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public void CompletedFiltersList_ShouldBeCreated()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var list = new CompletedFiltersList();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
list.Should().NotBeNull();
|
||||||
|
list.Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тест для проверки производительности коллекций.
|
||||||
|
///
|
||||||
|
/// ПРИНЦИП: Тестируем производительность при большом количестве элементов
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public void HandlerDescriptorList_ShouldHandleLargeNumberOfItems()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var list = new HandlerDescriptorList();
|
||||||
|
var itemsCount = 1000;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
for (int i = 0; i < itemsCount; i++)
|
||||||
|
{
|
||||||
|
list.Add(CreateTestDescriptor(UpdateType.Message));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
list.Should().HaveCount(itemsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тест для проверки потокобезопасности (базовый тест).
|
||||||
|
///
|
||||||
|
/// ПРИНЦИП: Тестируем базовую потокобезопасность
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public async void HandlerDescriptorList_ShouldHandleConcurrentAccess()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var list = new HandlerDescriptorList();
|
||||||
|
var tasks = new List<Task>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
tasks.Add(Task.Run(() =>
|
||||||
|
{
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
{
|
||||||
|
list.Add(CreateTestDescriptor(UpdateType.Message));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.WhenAll(tasks.ToArray());
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
list.Should().HaveCount(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Вспомогательный метод для создания тестового дескриптора.
|
||||||
|
/// </summary>
|
||||||
|
private static HandlerDescriptor CreateTestDescriptor(UpdateType updateType)
|
||||||
|
{
|
||||||
|
return new HandlerDescriptor(DescriptorType.General, typeof(TestUpdateHandler));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,173 +5,172 @@ using Telegrator.Filters;
|
|||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
#pragma warning disable CS8625
|
#pragma warning disable CS8625
|
||||||
namespace Telegrator.Tests.Filters
|
namespace Telegrator.Tests.Filters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тесты для базовых фильтров.
|
||||||
|
///
|
||||||
|
/// ПАРАДИГМЫ ТЕСТИРОВАНИЯ:
|
||||||
|
/// 1. AAA (Arrange-Act-Assert) - структура теста: подготовка, действие, проверка
|
||||||
|
/// 2. Given-When-Then - альтернативная формулировка AAA для лучшей читаемости
|
||||||
|
/// 3. Тестирование граничных случаев и исключений
|
||||||
|
/// 4. Использование моков для изоляции тестируемого кода
|
||||||
|
/// 5. Тестирование как позитивных, так и негативных сценариев
|
||||||
|
/// </summary>
|
||||||
|
public class FilterTests
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Тесты для базовых фильтров.
|
/// Тест для AnyFilter - фильтр, который всегда проходит.
|
||||||
///
|
///
|
||||||
/// ПАРАДИГМЫ ТЕСТИРОВАНИЯ:
|
/// ПРИНЦИП: Тестируем базовое поведение - фильтр должен всегда возвращать true
|
||||||
/// 1. AAA (Arrange-Act-Assert) - структура теста: подготовка, действие, проверка
|
|
||||||
/// 2. Given-When-Then - альтернативная формулировка AAA для лучшей читаемости
|
|
||||||
/// 3. Тестирование граничных случаев и исключений
|
|
||||||
/// 4. Использование моков для изоляции тестируемого кода
|
|
||||||
/// 5. Тестирование как позитивных, так и негативных сценариев
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FilterTests
|
[Fact]
|
||||||
|
public void AnyFilter_ShouldAlwaysPass()
|
||||||
{
|
{
|
||||||
/// <summary>
|
// Arrange (Given) - подготовка тестовых данных
|
||||||
/// Тест для AnyFilter - фильтр, который всегда проходит.
|
var anyFilter = Filter<Update>.Any();
|
||||||
///
|
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||||
/// ПРИНЦИП: Тестируем базовое поведение - фильтр должен всегда возвращать true
|
|
||||||
/// </summary>
|
|
||||||
[Fact]
|
|
||||||
public void AnyFilter_ShouldAlwaysPass()
|
|
||||||
{
|
|
||||||
// Arrange (Given) - подготовка тестовых данных
|
|
||||||
var anyFilter = Filter<Update>.Any();
|
|
||||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
|
||||||
|
|
||||||
// Act (When) - выполнение тестируемого действия
|
// Act (When) - выполнение тестируемого действия
|
||||||
var result = anyFilter.CanPass(context);
|
var result = anyFilter.CanPass(context);
|
||||||
|
|
||||||
// Assert (Then) - проверка результата
|
// Assert (Then) - проверка результата
|
||||||
result.Should().BeTrue();
|
result.Should().BeTrue();
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тест для ReverseFilter - инвертирование результата фильтра.
|
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем композицию фильтров и логику инверсии
|
|
||||||
/// </summary>
|
|
||||||
[Fact]
|
|
||||||
public void ReverseFilter_ShouldInvertResult()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var alwaysTrueFilter = Filter<Update>.Any();
|
|
||||||
var reverseFilter = alwaysTrueFilter.Not();
|
|
||||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = reverseFilter.CanPass(context);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
result.Should().BeFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тест для AndFilter - логическое И между фильтрами.
|
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем комбинирование фильтров и логику И
|
|
||||||
/// </summary>
|
|
||||||
[Theory]
|
|
||||||
[InlineData(true, true, true)] // Оба фильтра проходят
|
|
||||||
[InlineData(true, false, false)] // Первый проходит, второй нет
|
|
||||||
[InlineData(false, true, false)] // Первый не проходит, второй проходит
|
|
||||||
[InlineData(false, false, false)] // Оба фильтра не проходят
|
|
||||||
public void AndFilter_ShouldCombineFiltersWithAndLogic(bool firstResult, bool secondResult, bool expectedResult)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var firstFilter = Filter<Update>.If(_ => firstResult);
|
|
||||||
var secondFilter = Filter<Update>.If(_ => secondResult);
|
|
||||||
var andFilter = firstFilter.And(secondFilter);
|
|
||||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = andFilter.CanPass(context);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
result.Should().Be(expectedResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тест для OrFilter - логическое ИЛИ между фильтрами.
|
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем комбинирование фильтров и логику ИЛИ
|
|
||||||
/// </summary>
|
|
||||||
[Theory]
|
|
||||||
[InlineData(true, true, true)] // Оба фильтра проходят
|
|
||||||
[InlineData(true, false, true)] // Первый проходит, второй нет
|
|
||||||
[InlineData(false, true, true)] // Первый не проходит, второй проходит
|
|
||||||
[InlineData(false, false, false)] // Оба фильтра не проходят
|
|
||||||
public void OrFilter_ShouldCombineFiltersWithOrLogic(bool firstResult, bool secondResult, bool expectedResult)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var firstFilter = Filter<Update>.If(_ => firstResult);
|
|
||||||
var secondFilter = Filter<Update>.If(_ => secondResult);
|
|
||||||
var orFilter = firstFilter.Or(secondFilter);
|
|
||||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = orFilter.CanPass(context);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
result.Should().Be(expectedResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тест для CompiledFilter - компиляция нескольких фильтров.
|
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем сложную композицию фильтров
|
|
||||||
/// </summary>
|
|
||||||
[Fact]
|
|
||||||
public void CompiledFilter_ShouldPassOnlyWhenAllFiltersPass()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var filter1 = Filter<Update>.If(_ => true);
|
|
||||||
var filter2 = Filter<Update>.If(_ => true);
|
|
||||||
var filter3 = Filter<Update>.If(_ => false);
|
|
||||||
|
|
||||||
var compiledFilter = new CompiledFilter<Update>(filter1, filter2, filter3);
|
|
||||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = compiledFilter.CanPass(context);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
result.Should().BeFalse(); // Должен вернуть false, так как filter3 возвращает false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тест для проверки IsCollectible свойства.
|
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем свойства объектов
|
|
||||||
/// </summary>
|
|
||||||
[Fact]
|
|
||||||
public void Filter_IsCollectible_ShouldBeTrueForAnyFilter()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var anyFilter = Filter<Update>.Any();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var isCollectible = anyFilter.IsCollectible;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
isCollectible.Should().BeFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Тест для FunctionFilter - фильтр на основе функции.
|
|
||||||
///
|
|
||||||
/// ПРИНЦИП: Тестируем создание фильтров из функций
|
|
||||||
/// </summary>
|
|
||||||
[Fact]
|
|
||||||
public void FunctionFilter_ShouldUseProvidedFunction()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var wasCalled = false;
|
|
||||||
var functionFilter = Filter<Update>.If(_ =>
|
|
||||||
{
|
|
||||||
wasCalled = true;
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = functionFilter.CanPass(context);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
result.Should().BeTrue();
|
|
||||||
wasCalled.Should().BeTrue();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тест для ReverseFilter - инвертирование результата фильтра.
|
||||||
|
///
|
||||||
|
/// ПРИНЦИП: Тестируем композицию фильтров и логику инверсии
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public void ReverseFilter_ShouldInvertResult()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var alwaysTrueFilter = Filter<Update>.Any();
|
||||||
|
var reverseFilter = alwaysTrueFilter.Not();
|
||||||
|
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = reverseFilter.CanPass(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тест для AndFilter - логическое И между фильтрами.
|
||||||
|
///
|
||||||
|
/// ПРИНЦИП: Тестируем комбинирование фильтров и логику И
|
||||||
|
/// </summary>
|
||||||
|
[Theory]
|
||||||
|
[InlineData(true, true, true)] // Оба фильтра проходят
|
||||||
|
[InlineData(true, false, false)] // Первый проходит, второй нет
|
||||||
|
[InlineData(false, true, false)] // Первый не проходит, второй проходит
|
||||||
|
[InlineData(false, false, false)] // Оба фильтра не проходят
|
||||||
|
public void AndFilter_ShouldCombineFiltersWithAndLogic(bool firstResult, bool secondResult, bool expectedResult)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var firstFilter = Filter<Update>.If(_ => firstResult);
|
||||||
|
var secondFilter = Filter<Update>.If(_ => secondResult);
|
||||||
|
var andFilter = firstFilter.And(secondFilter);
|
||||||
|
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = andFilter.CanPass(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тест для OrFilter - логическое ИЛИ между фильтрами.
|
||||||
|
///
|
||||||
|
/// ПРИНЦИП: Тестируем комбинирование фильтров и логику ИЛИ
|
||||||
|
/// </summary>
|
||||||
|
[Theory]
|
||||||
|
[InlineData(true, true, true)] // Оба фильтра проходят
|
||||||
|
[InlineData(true, false, true)] // Первый проходит, второй нет
|
||||||
|
[InlineData(false, true, true)] // Первый не проходит, второй проходит
|
||||||
|
[InlineData(false, false, false)] // Оба фильтра не проходят
|
||||||
|
public void OrFilter_ShouldCombineFiltersWithOrLogic(bool firstResult, bool secondResult, bool expectedResult)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var firstFilter = Filter<Update>.If(_ => firstResult);
|
||||||
|
var secondFilter = Filter<Update>.If(_ => secondResult);
|
||||||
|
var orFilter = firstFilter.Or(secondFilter);
|
||||||
|
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = orFilter.CanPass(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(expectedResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тест для CompiledFilter - компиляция нескольких фильтров.
|
||||||
|
///
|
||||||
|
/// ПРИНЦИП: Тестируем сложную композицию фильтров
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public void CompiledFilter_ShouldPassOnlyWhenAllFiltersPass()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var filter1 = Filter<Update>.If(_ => true);
|
||||||
|
var filter2 = Filter<Update>.If(_ => true);
|
||||||
|
var filter3 = Filter<Update>.If(_ => false);
|
||||||
|
|
||||||
|
var compiledFilter = new CompiledFilter<Update>(filter1, filter2, filter3);
|
||||||
|
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = compiledFilter.CanPass(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeFalse(); // Должен вернуть false, так как filter3 возвращает false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тест для проверки IsCollectible свойства.
|
||||||
|
///
|
||||||
|
/// ПРИНЦИП: Тестируем свойства объектов
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public void Filter_IsCollectible_ShouldBeTrueForAnyFilter()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var anyFilter = Filter<Update>.Any();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var isCollectible = anyFilter.IsCollectible;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
isCollectible.Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Тест для FunctionFilter - фильтр на основе функции.
|
||||||
|
///
|
||||||
|
/// ПРИНЦИП: Тестируем создание фильтров из функций
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public void FunctionFilter_ShouldUseProvidedFunction()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var wasCalled = false;
|
||||||
|
var functionFilter = Filter<Update>.If(_ =>
|
||||||
|
{
|
||||||
|
wasCalled = true;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = functionFilter.CanPass(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeTrue();
|
||||||
|
wasCalled.Should().BeTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user