Skip to content

Commit 1e1a2ea

Browse files
committed
test: add routing coverage and CI gate
1 parent 32d6c42 commit 1e1a2ea

7 files changed

Lines changed: 549 additions & 50 deletions

File tree

‎.github/workflows/publish-release.yml‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ on:
66
- main
77
paths:
88
- Sources/**
9+
- .github/workflows/publish-release.yml
910

1011
jobs:
1112
build:
@@ -34,14 +35,18 @@ jobs:
3435
3536
#Build/pack the project
3637
- name: Setup .NET
37-
uses: actions/setup-dotnet@v1
38+
uses: actions/setup-dotnet@v4
3839
with:
39-
dotnet-version: 8.0.x
40-
- name: Build and Pack NuGet package
40+
dotnet-version: 10.0.x
41+
- name: Restore
42+
run: dotnet restore Sources/TelegramBot.sln
43+
- name: Build
44+
run: dotnet build Sources/TelegramBot.sln --configuration Release --no-restore
45+
- name: Test
46+
run: dotnet test Sources/TelegramBot.sln --configuration Release --no-build --verbosity normal
47+
- name: Pack NuGet package
4148
run: |
42-
cd Sources/TelegramBot
43-
dotnet build --configuration Release
44-
dotnet pack --configuration Release --output nupkg -p:Version='${{ steps.gitversion.outputs.SemVer }}'
49+
dotnet pack Sources/TelegramBot/TelegramBot.csproj --configuration Release --no-build --output Sources/TelegramBot/nupkg -p:Version='${{ steps.gitversion.outputs.SemVer }}'
4550
- name: Upload NuGet package to GitHub
4651
uses: actions/upload-artifact@v4
4752
with:
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
using System.Reflection;
2+
using Telegram.Bot.Types;
3+
using TelegramBot.Attributes;
4+
using TelegramBot.Handlers;
5+
6+
namespace TelegramBot.Tests;
7+
8+
public class InlineQueryHandlerTests
9+
{
10+
[Fact]
11+
public void ExactInlineRouteMatchesParameterlessMethod()
12+
{
13+
var handler = CreateHandler("menu", nameof(InlineController.Menu));
14+
15+
Assert.Equal(Method(nameof(InlineController.Menu)), handler.GetMethodInfo());
16+
Assert.Null(handler.GetArguments());
17+
}
18+
19+
[Fact]
20+
public void InlineRouteExtractsAndConvertsPlaceholderArgument()
21+
{
22+
var handler = CreateHandler("telegram/page/5", nameof(InlineController.Page));
23+
object[] args = handler.GetArguments()!;
24+
25+
Assert.Equal(Method(nameof(InlineController.Page)), handler.GetMethodInfo());
26+
Assert.Collection(args, value => Assert.Equal(5, Assert.IsType<int>(value)));
27+
}
28+
29+
[Fact]
30+
public void InlineRouteRejectsInvalidPlaceholderConversion()
31+
{
32+
var exception = Assert.Throws<InvalidOperationException>(() =>
33+
CreateHandler("telegram/page/not-a-number", nameof(InlineController.Page)));
34+
35+
Assert.Contains("Method not found", exception.Message);
36+
}
37+
38+
[Fact]
39+
public void EqualInlineRoutesThrowAmbiguousMatchException()
40+
{
41+
var exception = Assert.Throws<AmbiguousMatchException>(() =>
42+
CreateHandler(
43+
"duplicate",
44+
nameof(InlineController.DuplicateOne),
45+
nameof(InlineController.DuplicateTwo)));
46+
47+
Assert.Contains("duplicate", exception.Message);
48+
Assert.Contains(nameof(InlineController.DuplicateOne), exception.Message);
49+
Assert.Contains(nameof(InlineController.DuplicateTwo), exception.Message);
50+
}
51+
52+
private static InlineQueryHandler CreateHandler(string data, params string[] methodNames)
53+
{
54+
return new InlineQueryHandler(methodNames.Select(Method).ToArray(), CreateUpdate(data));
55+
}
56+
57+
private static MethodInfo Method(string name)
58+
{
59+
return typeof(InlineController).GetMethod(
60+
name,
61+
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)!;
62+
}
63+
64+
private static Update CreateUpdate(string data)
65+
{
66+
return new Update
67+
{
68+
CallbackQuery = new CallbackQuery
69+
{
70+
Data = data,
71+
},
72+
};
73+
}
74+
75+
private sealed class InlineController
76+
{
77+
[InlineCommand("menu")]
78+
public void Menu()
79+
{
80+
}
81+
82+
[InlineCommand("telegram/page/{page}")]
83+
public void Page(int page)
84+
{
85+
}
86+
87+
[InlineCommand("duplicate")]
88+
public void DuplicateOne()
89+
{
90+
}
91+
92+
[InlineCommand("duplicate")]
93+
public void DuplicateTwo()
94+
{
95+
}
96+
}
97+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<IsPackable>false</IsPackable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="coverlet.collector" Version="6.0.4" />
12+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
13+
<PackageReference Include="xunit" Version="2.9.3" />
14+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<Using Include="Xunit" />
19+
</ItemGroup>
20+
21+
<ItemGroup>
22+
<ProjectReference Include="..\TelegramBot\TelegramBot.csproj" />
23+
</ItemGroup>
24+
25+
</Project>
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
using System.Reflection;
2+
using Telegram.Bot.Types;
3+
using TelegramBot.Attributes;
4+
using TelegramBot.Handlers;
5+
6+
namespace TelegramBot.Tests;
7+
8+
public class TextCommandHandlerTests
9+
{
10+
[Fact]
11+
public void ExactCommandRouteMatchesParameterlessMethod()
12+
{
13+
var handler = CreateHandler("/start", nameof(CommandController.Start));
14+
15+
Assert.Equal(Method(nameof(CommandController.Start)), handler.GetMethodInfo());
16+
Assert.Null(handler.GetArguments());
17+
}
18+
19+
[Fact]
20+
public void CommandRouteConvertsTypedArguments()
21+
{
22+
var handler = CreateHandler("/limit 42 true", nameof(CommandController.SetLimit));
23+
object[] args = handler.GetArguments()!;
24+
25+
Assert.Equal(Method(nameof(CommandController.SetLimit)), handler.GetMethodInfo());
26+
Assert.Collection(
27+
args,
28+
value => Assert.Equal(42, Assert.IsType<int>(value)),
29+
value => Assert.True(Assert.IsType<bool>(value)));
30+
}
31+
32+
[Fact]
33+
public void CommandRouteKeepsQuotedArgumentTogether()
34+
{
35+
var handler = CreateHandler("/say \"hello world\"", nameof(CommandController.Say));
36+
37+
Assert.Equal(Method(nameof(CommandController.Say)), handler.GetMethodInfo());
38+
Assert.Equal(["hello world"], handler.GetArguments());
39+
}
40+
41+
[Fact]
42+
public void NonCommandTextDoesNotMatchCommandRoute()
43+
{
44+
var handler = CreateHandler("start", nameof(CommandController.Start));
45+
46+
Assert.Null(handler.GetMethodInfo());
47+
Assert.Null(handler.GetArguments());
48+
}
49+
50+
[Fact]
51+
public void ArgumentConversionFailureDoesNotMatchCommandRoute()
52+
{
53+
var handler = CreateHandler("/limit not-a-number true", nameof(CommandController.SetLimit));
54+
55+
Assert.Null(handler.GetMethodInfo());
56+
}
57+
58+
[Fact]
59+
public void EqualCommandRoutesThrowAmbiguousMatchException()
60+
{
61+
var exception = Assert.Throws<AmbiguousMatchException>(() =>
62+
CreateHandler(
63+
"/duplicate",
64+
nameof(CommandController.DuplicateOne),
65+
nameof(CommandController.DuplicateTwo)));
66+
67+
Assert.Contains("/duplicate", exception.Message);
68+
}
69+
70+
private static TextCommandHandler CreateHandler(string text, params string[] methodNames)
71+
{
72+
return new TextCommandHandler(methodNames.Select(Method).ToArray(), CreateUpdate(text));
73+
}
74+
75+
private static MethodInfo Method(string name)
76+
{
77+
return typeof(CommandController).GetMethod(
78+
name,
79+
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)!;
80+
}
81+
82+
private static Update CreateUpdate(string text)
83+
{
84+
return new Update
85+
{
86+
Message = new Message
87+
{
88+
Text = text,
89+
},
90+
};
91+
}
92+
93+
private sealed class CommandController
94+
{
95+
[TextCommand("/start")]
96+
public void Start()
97+
{
98+
}
99+
100+
[TextCommand("/limit")]
101+
public void SetLimit(int limit, bool enabled)
102+
{
103+
}
104+
105+
[TextCommand("/say")]
106+
public void Say(string text)
107+
{
108+
}
109+
110+
[TextCommand("/duplicate")]
111+
public void DuplicateOne()
112+
{
113+
}
114+
115+
[TextCommand("/duplicate")]
116+
public void DuplicateTwo()
117+
{
118+
}
119+
}
120+
}

0 commit comments

Comments
 (0)