From cdd0bd83ab9ec774c2412d6543da0995c579fc74 Mon Sep 17 00:00:00 2001 From: Jkorf Date: Fri, 3 Apr 2026 13:08:59 +0200 Subject: [PATCH] Added SharedRestRequestValidator for testing Shared interface implementations --- .../Testing/SharedRestRequestValidator.cs | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 CryptoExchange.Net/Testing/SharedRestRequestValidator.cs diff --git a/CryptoExchange.Net/Testing/SharedRestRequestValidator.cs b/CryptoExchange.Net/Testing/SharedRestRequestValidator.cs new file mode 100644 index 00000000..498222a4 --- /dev/null +++ b/CryptoExchange.Net/Testing/SharedRestRequestValidator.cs @@ -0,0 +1,126 @@ +using CryptoExchange.Net.Clients; +using CryptoExchange.Net.Objects; +using CryptoExchange.Net.SharedApis; +using CryptoExchange.Net.Testing.Comparers; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace CryptoExchange.Net.Testing +{ + /// + /// Validator for REST requests, comparing path, http method, authentication and response parsing + /// + /// The Rest client + public class SharedRestRequestValidator where TClient : BaseRestClient + { + private readonly TClient _client; + private readonly Func _isAuthenticated; + private readonly string _folder; + private readonly string _baseAddress; + private readonly string? _nestedPropertyForCompare; + + /// + /// ctor + /// + /// Client to test + /// Folder for json test values + /// The base address that is expected + /// Func for checking if the request is authenticated + /// Property to use for compare + public SharedRestRequestValidator(TClient client, string folder, string baseAddress, Func isAuthenticated, string? nestedPropertyForCompare = null) + { + _client = client; + _folder = folder; + _baseAddress = baseAddress; + _nestedPropertyForCompare = nestedPropertyForCompare; + _isAuthenticated = isAuthenticated; + } + + /// + /// Validate a request + /// + /// Expected response type + /// Method invocation + /// Method name for looking up json test values + /// Request options + /// + /// + public Task ValidateAsync( + Func>> methodInvoke, + string name, + EndpointOptions endpointOptions, + params Func[] validation) + => ValidateAsync(methodInvoke, name, endpointOptions, validation); + + /// + /// Validate a request + /// + /// Expected response type + /// The concrete response type + /// Method invocation + /// Method name for looking up json test values + /// Request options + /// + /// + public async Task ValidateAsync( + Func>> methodInvoke, + string name, + EndpointOptions endpointOptions, + params Func[] validation) where TActualResponse : TResponse + { + var listener = new EnumValueTraceListener(); + Trace.Listeners.Add(listener); + + var path = Directory.GetParent(Environment.CurrentDirectory)!.Parent!.Parent!.FullName; + FileStream file; + try + { + file = File.OpenRead(Path.Combine(path, _folder, $"{name}.txt")); + } + catch (FileNotFoundException) + { + throw new Exception($"Response file not found for {name}: {path}"); + } + + var buffer = new byte[file.Length]; + await file.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); + file.Close(); + + var data = Encoding.UTF8.GetString(buffer); + using var reader = new StringReader(data); + var expectedMethod = reader.ReadLine(); + var expectedPath = reader.ReadLine(); + var expectedAuth = bool.Parse(reader.ReadLine()!); + var response = reader.ReadToEnd(); + + TestHelpers.ConfigureRestClient(_client, response, System.Net.HttpStatusCode.OK); + var result = await methodInvoke(_client).ConfigureAwait(false); + + // Check request/response properties + if (result.Error != null) + throw new Exception(name + " returned error " + result.Error); + if (endpointOptions.NeedsAuthentication != expectedAuth) + throw new Exception(name + $" authentication not matched. Expected: {expectedAuth}, Actual: {_isAuthenticated(result.AsDataless())}"); + if (result.RequestMethod != new HttpMethod(expectedMethod!)) + throw new Exception(name + $" http method not matched. Expected {expectedMethod}, Actual: {result.RequestMethod}"); + if (expectedPath != result.RequestUrl!.Replace(_baseAddress, "").Split(new char[] { '?' })[0]) + throw new Exception(name + $" path not matched. Expected: {expectedPath}, Actual: {result.RequestUrl!.Replace(_baseAddress, "").Split(new char[] { '?' })[0]}"); + + var index = 0; + foreach(var validate in validation) + { + if (!validate(result.Data!)) + throw new Exception(name + $" response validation #{index} failed"); + + index++; + } + + Trace.Listeners.Remove(listener); + } + } +}