### Commit Summary

- **TestSetup.cs**
  - Updated `CreateTestConfig` method to initialize `Config` with required properties using object initializer syntax.

- **ProxyIntegrationTests.cs**
  - Added null checks for `mockServer.Urls` before accessing it to prevent potential null reference exceptions.
  - Improved error handling for mock server URL access.

- **VRChatAuthenticationTests.cs**
  - Added null checks for `mockServer.Urls` before accessing it to prevent potential null reference exceptions.
  - Enhanced the mock server setup to include null checks for request body content.

- **Config.cs**
  - Added the `required` modifier to non-nullable properties in `ConfigAccount` and `iConfig` classes.
  - Updated the `Load` method to initialize the `Config` instance with required properties using object initializer syntax.

- **Program.cs**
  - Added a null check for `result.CloseStatus` in WebSocket handling to prevent potential null reference exceptions.
This commit is contained in:
MiscFrizzy 2025-04-07 07:30:34 -04:00
parent 861bedcf43
commit eb4349031b
5 changed files with 59 additions and 28 deletions

View file

@ -23,8 +23,9 @@ namespace VRCAuthProxy.Tests.Helpers
/// </summary> /// </summary>
public static Config CreateTestConfig() public static Config CreateTestConfig()
{ {
var config = new Config(); return new Config
config.Accounts = new List<ConfigAccount> {
Accounts = new List<ConfigAccount>
{ {
new ConfigAccount new ConfigAccount
{ {
@ -37,9 +38,8 @@ namespace VRCAuthProxy.Tests.Helpers
username = "testuser2", username = "testuser2",
password = "testpassword2" password = "testpassword2"
} }
}
}; };
return config;
} }
} }
} }

View file

@ -52,11 +52,17 @@ namespace VRCAuthProxy.Tests.Integration
.WithHeader("Content-Type", "application/json") .WithHeader("Content-Type", "application/json")
.WithBody(@"[{""id"":""usr_test1"",""displayName"":""TestUser1""},{""id"":""usr_test2"",""displayName"":""TestUser2""}]")); .WithBody(@"[{""id"":""usr_test1"",""displayName"":""TestUser1""},{""id"":""usr_test2"",""displayName"":""TestUser2""}]"));
// Create a client // Create a client with the test setup
var httpClient = new HttpClient(); var handler = new HttpClientHandler { UseCookies = true };
var mockServerUrls = mockServer.Urls;
if (mockServerUrls == null || !mockServerUrls.Any())
{
throw new InvalidOperationException("Mock server URLs not available");
}
var baseUri = mockServerUrls.First();
// Call the users endpoint directly // Call the users endpoint directly
var baseUri = mockServer.Urls.First(); var httpClient = new HttpClient(handler);
var response = await httpClient.GetAsync($"{baseUri}/api/1/users"); var response = await httpClient.GetAsync($"{baseUri}/api/1/users");
// Assert // Assert
@ -90,11 +96,17 @@ namespace VRCAuthProxy.Tests.Integration
.WithHeader("Content-Type", "application/json") .WithHeader("Content-Type", "application/json")
.WithBody(@"[{""id"":""wrld_test1"",""name"":""TestWorld1""},{""id"":""wrld_test2"",""name"":""TestWorld2""}]")); .WithBody(@"[{""id"":""wrld_test1"",""name"":""TestWorld1""},{""id"":""wrld_test2"",""name"":""TestWorld2""}]"));
// Create a client // Create a client with the test setup
var httpClient = new HttpClient(); var handler = new HttpClientHandler { UseCookies = true };
var mockServerUrls = mockServer.Urls;
if (mockServerUrls == null || !mockServerUrls.Any())
{
throw new InvalidOperationException("Mock server URLs not available");
}
var baseUri = mockServerUrls.First();
// First authenticate // First authenticate
var baseUri = mockServer.Urls.First(); var httpClient = new HttpClient(handler);
await httpClient.GetAsync($"{baseUri}/api/1/auth/user"); await httpClient.GetAsync($"{baseUri}/api/1/auth/user");
// Then call the worlds endpoint // Then call the worlds endpoint

View file

@ -38,9 +38,14 @@ namespace VRCAuthProxy.Tests.Integration
// Create a client with the test setup // Create a client with the test setup
var handler = new HttpClientHandler { UseCookies = true }; var handler = new HttpClientHandler { UseCookies = true };
var mockServerUrls = mockServer.Urls;
if (mockServerUrls == null || !mockServerUrls.Any())
{
throw new InvalidOperationException("Mock server URLs not available");
}
var httpClient = new HttpClient(handler) var httpClient = new HttpClient(handler)
{ {
BaseAddress = new Uri(mockServer.Urls.First()) BaseAddress = new Uri(mockServerUrls.First())
}; };
// Act // Act
@ -82,7 +87,7 @@ namespace VRCAuthProxy.Tests.Integration
mockServer.Given(Request.Create() mockServer.Given(Request.Create()
.WithPath("/api/1/auth/twofactorauth/totp/verify") .WithPath("/api/1/auth/twofactorauth/totp/verify")
.UsingPost() .UsingPost()
.WithBody(x => x.Contains("code"))) .WithBody(x => x != null && x.Contains("code")))
.RespondWith(Response.Create() .RespondWith(Response.Create()
.WithStatusCode(200) .WithStatusCode(200)
.WithHeader("Content-Type", "application/json") .WithHeader("Content-Type", "application/json")
@ -101,9 +106,14 @@ namespace VRCAuthProxy.Tests.Integration
// Create a client with the test setup // Create a client with the test setup
var handler = new HttpClientHandler { UseCookies = true }; var handler = new HttpClientHandler { UseCookies = true };
var mockServerUrls = mockServer.Urls;
if (mockServerUrls == null || !mockServerUrls.Any())
{
throw new InvalidOperationException("Mock server URLs not available");
}
var httpClient = new HttpClient(handler) var httpClient = new HttpClient(handler)
{ {
BaseAddress = new Uri(mockServer.Urls.First()) BaseAddress = new Uri(mockServerUrls.First())
}; };
// Act - First authenticate which will indicate TOTP is required // Act - First authenticate which will indicate TOTP is required

View file

@ -4,21 +4,26 @@ namespace VRCAuthProxy;
public class ConfigAccount public class ConfigAccount
{ {
public string username { get; set; } public required string username { get; set; }
public string password { get; set; } public required string password { get; set; }
public string? totpSecret { get; set; } public string? totpSecret { get; set; }
} }
public class iConfig public class iConfig
{ {
public List<ConfigAccount> accounts { get; set; } public required List<ConfigAccount> accounts { get; set; }
} }
// Load config from appsettings.json // Load config from appsettings.json
public class Config public class Config
{ {
private static Config? _instance; private static Config? _instance;
public List<ConfigAccount> Accounts { get; set; } public required List<ConfigAccount> Accounts { get; set; }
public Config()
{
Accounts = new List<ConfigAccount>();
}
public static Config Instance public static Config Instance
{ {
@ -31,8 +36,12 @@ public class Config
public static Config Load() public static Config Load()
{ {
var config = new Config();
var configPath = Path.Combine(AppContext.BaseDirectory, "appsettings.json"); var configPath = Path.Combine(AppContext.BaseDirectory, "appsettings.json");
var config = new Config
{
Accounts = new List<ConfigAccount>()
};
if (File.Exists(configPath)) if (File.Exists(configPath))
{ {
var json = File.ReadAllText(configPath); var json = File.ReadAllText(configPath);

View file

@ -155,7 +155,7 @@ app.Use(async (context, next) =>
var result = await source.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); var result = await source.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close) if (result.MessageType == WebSocketMessageType.Close)
{ {
await target.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); await target.CloseAsync(result.CloseStatus ?? WebSocketCloseStatus.NormalClosure, result.CloseStatusDescription, CancellationToken.None);
break; break;
} }
await target.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); await target.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);