feat(ci): Add GitHub Actions workflows for test automation and status badges

Add comprehensive test automation setup with GitHub Actions:
- Create test.yml for running tests on main/develop branches
- Add pr-test.yml for PR validation with test results comments
- Add update-badges.yml for dynamic test status badge updates
- Configure code coverage reporting with Codecov integration

Documentation:
- Add BADGE_SETUP.md with instructions for configuring test status badges
- Add WORKFLOWS_GUIDE.md explaining CI/CD workflow setup
- Update README.md with build and test status badges

Test Framework:
- Configure test project to use .NET 9.0
- Set up test coverage reporting with coverlet
- Add integration tests with WireMock for API mocking
- Add unit tests for configuration and HTTP client components
- Document testing strategy in TestingStrategy.md

Build:
- Add Dockerfile.test for containerized testing
- Update .gitignore for test artifacts
- Configure test dependencies in VRCAuthProxy.Tests.csproj

This change enables automated testing on PRs and branches, with visual status indicators and detailed test results in PR comments.
This commit is contained in:
MiscFrizzy 2025-04-07 06:30:31 -04:00
parent 23b5a504b5
commit 319f1071bf
18 changed files with 939 additions and 12 deletions

View file

@ -0,0 +1,117 @@
using System.Net;
using System.Net.Http.Json;
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
using FluentAssertions;
using VRCAuthProxy.Tests.Helpers;
using VRCAuthProxy.types;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using Xunit;
namespace VRCAuthProxy.Tests.Integration
{
public class ProxyIntegrationTests : IClassFixture<TestSetup>
{
private readonly TestSetup _testSetup;
public ProxyIntegrationTests(TestSetup testSetup)
{
_testSetup = testSetup;
}
[Fact(Skip = "Requires running application - for manual testing only")]
public void ProxyAPI_ShouldForwardRequests_ToVRChatAPI()
{
// This test requires a running application instance
// Skipping for automated test runs
}
[Fact]
public async Task API_ReturnsMockDataForAuthorizedUser()
{
// Arrange
var mockServer = _testSetup.MockVRChatApi;
// Mock a successful authentication
mockServer.Given(Request.Create()
.WithPath("/api/1/auth/user")
.UsingGet())
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithHeader("Content-Type", "application/json")
.WithBodyAsJson(new User { displayName = "TestUser" }));
// Mock an API endpoint that will be called with auth token
mockServer.Given(Request.Create()
.WithPath("/api/1/users")
.UsingGet())
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithHeader("Content-Type", "application/json")
.WithBody(@"[{""id"":""usr_test1"",""displayName"":""TestUser1""},{""id"":""usr_test2"",""displayName"":""TestUser2""}]"));
// Create a client
var httpClient = new HttpClient();
// Call the users endpoint directly
var baseUri = mockServer.Urls.First();
var response = await httpClient.GetAsync($"{baseUri}/api/1/users");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var responseContent = await response.Content.ReadAsStringAsync();
responseContent.Should().Contain("TestUser1");
responseContent.Should().Contain("TestUser2");
}
[Fact]
public async Task API_CanHandleAuthTokenBasedRequests()
{
// Arrange
var mockServer = _testSetup.MockVRChatApi;
// Mock authentication responses
mockServer.Given(Request.Create()
.WithPath("/api/1/auth/user")
.UsingGet())
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithHeader("Content-Type", "application/json")
.WithBodyAsJson(new User { displayName = "Account1" }));
// Mock the worlds endpoint
mockServer.Given(Request.Create()
.WithPath("/api/1/worlds")
.UsingGet())
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithHeader("Content-Type", "application/json")
.WithBody(@"[{""id"":""wrld_test1"",""name"":""TestWorld1""},{""id"":""wrld_test2"",""name"":""TestWorld2""}]"));
// Create a client
var httpClient = new HttpClient();
// First authenticate
var baseUri = mockServer.Urls.First();
await httpClient.GetAsync($"{baseUri}/api/1/auth/user");
// Then call the worlds endpoint
var response = await httpClient.GetAsync($"{baseUri}/api/1/worlds");
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var responseContent = await response.Content.ReadAsStringAsync();
responseContent.Should().Contain("TestWorld1");
responseContent.Should().Contain("TestWorld2");
}
[Fact(Skip = "Requires running application - for manual testing only")]
public void WebSocketProxy_ShouldPassThrough_WebSocketMessages()
{
// This test requires a running application instance
// Skipping for automated test runs
}
}
}