Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix a ton of bugs #18

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 3 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ SteamQueryNet is a C# wrapper for [Steam Server Queries](https://developer.valve

* Light
* Dependency free
* Written in .net standard 2.0 so that it works with both .NET framework 4.6+ and core.
* Written in .NET 6

# How to install ?

Expand All @@ -22,9 +22,8 @@ SteamQueryNet comes with a single object that gives you access to all API's of t
To make use of the API's listed above, an instance of `ServerQuery` should be created.

```csharp
string serverIp = "127.0.0.1";
int serverPort = 27015;
IServerQuery serverQuery = new ServerQuery(serverIp, serverPort);
IServerQuery serverQuery = new ServerQuery();
serverQuery.Connect(host, port);
```

or you can use string resolvers like below:
Expand All @@ -45,15 +44,6 @@ or you can use string resolvers like below:
IServerQuery serverQuery = new ServerQuery(myHostAndPort);
```

Also, it is possible to create `ServerQuery` object without connecting like below:

```csharp
IServerQuery serverQuery = new ServerQuery();
serverQuery.Connect(host, port);
```

*Note*: `Connect` function overloads are similar to `ServerQuery` non-empty constructors.

## Providing Custom UDPClient

You can provide custom UDP clients by implementing `IUdpClient` in `SteamQueryNet.Interfaces` namespace.
Expand Down Expand Up @@ -114,13 +104,5 @@ List<Player> players = serverQuery.GetPlayers();
List<Rule> rules = serverQuery.GetRules();
```

While **it is not encouraged**, you can chain `Connect` function or Non-empty Constructors to get information in a single line.

```csharp
ServerInfo serverInfo = new ServerQuery()
.Connect(host, port)
.GetServerInfo();
```

# Todos
* Enable CI
282 changes: 141 additions & 141 deletions SteamQueryNet/SteamQueryNet.Tests/ServerQueryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,145 +17,145 @@

namespace SteamQueryNet.Tests
{
public class ServerQueryTests
{
private const string IP_ADDRESS = "127.0.0.1";
private const string HOST_NAME = "localhost";
private const ushort PORT = 27015;
private byte _packetCount = 0;
private readonly IPEndPoint _localIpEndpoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 0);

[Theory]
[InlineData(IP_ADDRESS)]
[InlineData(HOST_NAME)]
public void ShouldInitializeWithProperHost(string host)
{
using (var sq = new ServerQuery(new Mock<IUdpClient>().Object, It.IsAny<IPEndPoint>()))
{
sq.Connect(host, PORT);
}
}

[Theory]
[InlineData("127.0.0.1:27015")]
[InlineData("127.0.0.1,27015")]
[InlineData("localhost:27015")]
[InlineData("localhost,27015")]
[InlineData("steam://connect/localhost:27015")]
[InlineData("steam://connect/127.0.0.1:27015")]
public void ShouldInitializeWithProperHostAndPort(string ipAndHost)
{
using (var sq = new ServerQuery(new Mock<IUdpClient>().Object, It.IsAny<IPEndPoint>()))
{
sq.Connect(ipAndHost);
}
}

[Theory]
[InlineData("invalidHost:-1")]
[InlineData("invalidHost,-1")]
[InlineData("invalidHost:65536")]
[InlineData("invalidHost,65536")]
[InlineData("256.256.256.256:-1")]
[InlineData("256.256.256.256,-1")]
[InlineData("256.256.256.256:65536")]
[InlineData("256.256.256.256,65536")]
public void ShouldNotInitializeWithAnInvalidHostAndPort(string invalidHost)
{
Assert.Throws<ArgumentException>(() =>
{
using (var sq = new ServerQuery(new Mock<IUdpClient>().Object, It.IsAny<IPEndPoint>()))
{
sq.Connect(invalidHost);
}
});
}

[Fact]
public void GetServerInfo_ShouldPopulateCorrectServerInfo()
{
(byte[] responsePacket, object responseObject) = ResponseHelper.GetValidResponse(ResponseHelper.ServerInfo);
var expectedObject = (ServerInfo)responseObject;

byte[][] requestPackets = new byte[][] { RequestHelpers.PrepareAS2_INFO_Request() };
byte[][] responsePackets = new byte[][] { responsePacket };

Mock<IUdpClient> udpClientMock = SetupReceiveResponse(responsePackets);
SetupRequestCompare(requestPackets, udpClientMock);

using (var sq = new ServerQuery(udpClientMock.Object, _localIpEndpoint))
{
Assert.Equal(JsonConvert.SerializeObject(expectedObject), JsonConvert.SerializeObject(sq.GetServerInfo()));
}
}

[Fact]
public void GetPlayers_ShouldPopulateCorrectPlayers()
{
(byte[] playersPacket, object responseObject) = ResponseHelper.GetValidResponse(ResponseHelper.GetPlayers);
var expectedObject = (List<Player>)responseObject;

byte[] challengePacket = RequestHelpers.PrepareAS2_RENEW_CHALLENGE_Request();

// Both requests will be executed on AS2_PLAYER since thats how you refresh challenges.
byte[][] requestPackets = new byte[][] { challengePacket, challengePacket };

// First response is the Challenge renewal response and the second
byte[][] responsePackets = new byte[][] { challengePacket, playersPacket };

Mock<IUdpClient> udpClientMock = SetupReceiveResponse(responsePackets);
SetupRequestCompare(requestPackets, udpClientMock);

// Ayylmao it looks ugly as hell but we will improve it later on.
using (var sq = new ServerQuery(udpClientMock.Object, _localIpEndpoint))
{
Assert.Equal(JsonConvert.SerializeObject(expectedObject), JsonConvert.SerializeObject(sq.GetPlayers()));
}
}

/*
* We keep this test here to be able to have us a notifier when the Rules API becomes available.
* So, this is more like an integration test than an unit test.
* If this test starts to fail, we'll know that the Rules API started to respond.
*/
[Fact]
public void GetRules_ShouldThrowTimeoutException()
{
// Surf Heaven rulez.
const string trustedServer = "steam://connect/54.37.111.217:27015";

using (var sq = new ServerQuery())
{
sq.Connect(trustedServer);

// Make sure that the server is still alive.
Assert.True(sq.IsConnected);
bool responded = Task.WaitAll(new Task[] { sq.GetRulesAsync() }, 2000);
Assert.True(!responded);
}
}

private void SetupRequestCompare(IEnumerable<byte[]> requestPackets, Mock<IUdpClient> udpClientMock)
{
udpClientMock
.Setup(x => x.SendAsync(It.IsAny<byte[]>(), It.IsAny<int>()))
.Callback<byte[], int>((request, length) =>
{
Assert.True(TestValidators.CompareBytes(requestPackets.ElementAt(_packetCount), request));
++_packetCount;
});
}

private Mock<IUdpClient> SetupReceiveResponse(IEnumerable<byte[]> udpPackets)
{
var udpClientMock = new Mock<IUdpClient>();
var setupSequence = udpClientMock.SetupSequence(x => x.ReceiveAsync());
foreach (byte[] packet in udpPackets)
{
setupSequence = setupSequence.ReturnsAsync(new UdpReceiveResult(packet, _localIpEndpoint));
}

return udpClientMock;
}
}
public class ServerQueryTests
{
private const string IP_ADDRESS = "127.0.0.1";
private const string HOST_NAME = "localhost";
private const ushort PORT = 27015;
private byte _packetCount = 0;
private readonly IPEndPoint _localIpEndpoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 0);

[Theory]
[InlineData(IP_ADDRESS)]
[InlineData(HOST_NAME)]
public void ShouldInitializeWithProperHost(string host)
{
using (var sq = new ServerQuery(new Mock<IUdpClient>().Object, It.IsAny<IPEndPoint>()))
{
sq.Connect(host, PORT);
}
}

[Theory]
[InlineData("127.0.0.1:27015")]
[InlineData("127.0.0.1,27015")]
[InlineData("localhost:27015")]
[InlineData("localhost,27015")]
[InlineData("steam://connect/localhost:27015")]
[InlineData("steam://connect/127.0.0.1:27015")]
public void ShouldInitializeWithProperHostAndPort(string ipAndHost)
{
using (var sq = new ServerQuery(new Mock<IUdpClient>().Object, It.IsAny<IPEndPoint>()))
{
sq.Connect(ipAndHost);
}
}

[Theory]
[InlineData("invalidHost:-1")]
[InlineData("invalidHost,-1")]
[InlineData("invalidHost:65536")]
[InlineData("invalidHost,65536")]
[InlineData("256.256.256.256:-1")]
[InlineData("256.256.256.256,-1")]
[InlineData("256.256.256.256:65536")]
[InlineData("256.256.256.256,65536")]
public void ShouldNotInitializeWithAnInvalidHostAndPort(string invalidHost)
{
Assert.Throws<ArgumentException>(() =>
{
using (var sq = new ServerQuery(new Mock<IUdpClient>().Object, It.IsAny<IPEndPoint>()))
{
sq.Connect(invalidHost);
}
});
}

[Fact]
public void GetServerInfo_ShouldPopulateCorrectServerInfo()
{
(byte[] responsePacket, object responseObject) = ResponseHelper.GetValidResponse(ResponseHelper.ServerInfo);
var expectedObject = (ServerInfo)responseObject;

byte[][] requestPackets = new byte[][] { RequestHelpers.PrepareAS2_INFO_Request(0) };
byte[][] responsePackets = new byte[][] { responsePacket };

Mock<IUdpClient> udpClientMock = SetupReceiveResponse(responsePackets);
SetupRequestCompare(requestPackets, udpClientMock);

using (var sq = new ServerQuery(udpClientMock.Object, _localIpEndpoint))
{
Assert.Equal(JsonConvert.SerializeObject(expectedObject), JsonConvert.SerializeObject(sq.GetServerInfo()));
}
}

[Fact]
public void GetPlayers_ShouldPopulateCorrectPlayers()
{
(byte[] playersPacket, object responseObject) = ResponseHelper.GetValidResponse(ResponseHelper.GetPlayers);
var expectedObject = (List<Player>)responseObject;

byte[] challengePacket = RequestHelpers.PrepareAS2_RENEW_CHALLENGE_Request();

// Both requests will be executed on AS2_PLAYER since thats how you refresh challenges.
byte[][] requestPackets = new byte[][] { challengePacket, challengePacket };

// First response is the Challenge renewal response and the second
byte[][] responsePackets = new byte[][] { challengePacket, playersPacket };

Mock<IUdpClient> udpClientMock = SetupReceiveResponse(responsePackets);
SetupRequestCompare(requestPackets, udpClientMock);

// Ayylmao it looks ugly as hell but we will improve it later on.
using (var sq = new ServerQuery(udpClientMock.Object, _localIpEndpoint))
{
Assert.Equal(JsonConvert.SerializeObject(expectedObject), JsonConvert.SerializeObject(sq.GetPlayers()));
}
}

/*
* We keep this test here to be able to have us a notifier when the Rules API becomes available.
* So, this is more like an integration test than an unit test.
* If this test starts to fail, we'll know that the Rules API started to respond.
*/
[Fact]
public void GetRules_ShouldThrowTimeoutException()
{
// Surf Heaven rulez.
const string trustedServer = "steam://connect/54.37.111.217:27015";

using (var sq = new ServerQuery())
{
sq.Connect(trustedServer);

// Make sure that the server is still alive.
Assert.True(sq.IsConnected);
bool responded = Task.WaitAll(new Task[] { sq.GetRulesAsync() }, 2000);
Assert.True(!responded);
}
}

private void SetupRequestCompare(IEnumerable<byte[]> requestPackets, Mock<IUdpClient> udpClientMock)
{
udpClientMock
.Setup(x => x.SendAsync(It.IsAny<byte[]>(), It.IsAny<int>()))
.Callback<byte[], int>((request, length) =>
{
Assert.True(TestValidators.CompareBytes(requestPackets.ElementAt(_packetCount), request));
++_packetCount;
});
}

private Mock<IUdpClient> SetupReceiveResponse(IEnumerable<byte[]> udpPackets)
{
var udpClientMock = new Mock<IUdpClient>();
var setupSequence = udpClientMock.SetupSequence(x => x.ReceiveAsync());
foreach (byte[] packet in udpPackets)
{
setupSequence = setupSequence.ReturnsAsync(new UdpReceiveResult(packet, _localIpEndpoint));
}

return udpClientMock;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFramework>net5.0-windows</TargetFramework>

<IsPackable>false</IsPackable>
</PropertyGroup>
Expand Down
Loading