From e92921031f19b2f9192499b1b223fd97a8bb8e1e Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Wed, 26 Apr 2023 10:49:27 +0300 Subject: [PATCH 1/3] Add API approval tests --- GraphQL.Client.sln | 7 + .../ApiApprovalTests.cs | 89 +++++++++++++ ...Client.Abstractions.Websocket.approved.txt | 86 +++++++++++++ .../GraphQL.Client.Abstractions.approved.txt | 66 ++++++++++ .../GraphQL.Client.ApiTests.csproj | 21 +++ ...GraphQL.Client.LocalExecution.approved.txt | 25 ++++ ....Client.Serializer.Newtonsoft.approved.txt | 27 ++++ ...ent.Serializer.SystemTextJson.approved.txt | 50 ++++++++ .../GraphQL.Client.approved.txt | 120 ++++++++++++++++++ .../GraphQL.Primitives.approved.txt | 80 ++++++++++++ tests/tests.props | 1 + 11 files changed, 572 insertions(+) create mode 100644 tests/GraphQL.Client.ApiTests/ApiApprovalTests.cs create mode 100644 tests/GraphQL.Client.ApiTests/GraphQL.Client.Abstractions.Websocket.approved.txt create mode 100644 tests/GraphQL.Client.ApiTests/GraphQL.Client.Abstractions.approved.txt create mode 100644 tests/GraphQL.Client.ApiTests/GraphQL.Client.ApiTests.csproj create mode 100644 tests/GraphQL.Client.ApiTests/GraphQL.Client.LocalExecution.approved.txt create mode 100644 tests/GraphQL.Client.ApiTests/GraphQL.Client.Serializer.Newtonsoft.approved.txt create mode 100644 tests/GraphQL.Client.ApiTests/GraphQL.Client.Serializer.SystemTextJson.approved.txt create mode 100644 tests/GraphQL.Client.ApiTests/GraphQL.Client.approved.txt create mode 100644 tests/GraphQL.Client.ApiTests/GraphQL.Primitives.approved.txt diff --git a/GraphQL.Client.sln b/GraphQL.Client.sln index 89782ecc..c8b3bac0 100644 --- a/GraphQL.Client.sln +++ b/GraphQL.Client.sln @@ -67,6 +67,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{98D4 .github\dependabot.yml = .github\dependabot.yml EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GraphQL.Client.ApiTests", "tests\GraphQL.Client.ApiTests\GraphQL.Client.ApiTests.csproj", "{DB0C542C-D0CC-4AD5-95AF-50A208C6A885}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -129,6 +131,10 @@ Global {6B13B87D-1EF4-485F-BC5D-891E2F4DA6CD}.Debug|Any CPU.Build.0 = Debug|Any CPU {6B13B87D-1EF4-485F-BC5D-891E2F4DA6CD}.Release|Any CPU.ActiveCfg = Release|Any CPU {6B13B87D-1EF4-485F-BC5D-891E2F4DA6CD}.Release|Any CPU.Build.0 = Release|Any CPU + {DB0C542C-D0CC-4AD5-95AF-50A208C6A885}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DB0C542C-D0CC-4AD5-95AF-50A208C6A885}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DB0C542C-D0CC-4AD5-95AF-50A208C6A885}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DB0C542C-D0CC-4AD5-95AF-50A208C6A885}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -150,6 +156,7 @@ Global {7FFFEC00-D751-4FFC-9FD4-E91858F9A1C5} = {47C98B55-08F1-4428-863E-2C5C876DEEFE} {6B13B87D-1EF4-485F-BC5D-891E2F4DA6CD} = {89AD33AB-64F6-4F82-822F-21DF7A10CEC0} {98D4DDDD-DE15-4997-B888-9BC806C7416C} = {63F75859-4698-4EDE-8B70-4ACBB8BC425A} + {DB0C542C-D0CC-4AD5-95AF-50A208C6A885} = {0B0EDB0F-FF67-4B78-A8DB-B5C23E1FEE8C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {387AC1AC-F90C-4EF8-955A-04D495C75AF4} diff --git a/tests/GraphQL.Client.ApiTests/ApiApprovalTests.cs b/tests/GraphQL.Client.ApiTests/ApiApprovalTests.cs new file mode 100644 index 00000000..9625649b --- /dev/null +++ b/tests/GraphQL.Client.ApiTests/ApiApprovalTests.cs @@ -0,0 +1,89 @@ +using System.Diagnostics; +using System.Reflection; +using System.Xml.Linq; +using GraphQL.Client.Abstractions; +using GraphQL.Client.Abstractions.Websocket; +using GraphQL.Client.Http; +using GraphQL.Client.LocalExecution; +using GraphQL.Client.Serializer.Newtonsoft; +using GraphQL.Client.Serializer.SystemTextJson; +using PublicApiGenerator; +using Shouldly; +using Xunit; + +namespace GraphQL.ApiTests; + +public class ApiApprovalTests +{ + [Theory] + [InlineData(typeof(NewtonsoftJsonSerializer))] + [InlineData(typeof(SystemTextJsonSerializer))] + [InlineData(typeof(GraphQLRequest))] + [InlineData(typeof(GraphQLLocalExecutionClient))] + [InlineData(typeof(IGraphQLWebSocketClient))] + [InlineData(typeof(IGraphQLClient))] + [InlineData(typeof(GraphQLHttpRequest))] + public void PublicApi(Type type) + { + string baseDir = AppDomain.CurrentDomain.BaseDirectory; + string projectName = type.Assembly.GetName().Name!; + string testDir = Path.Combine(baseDir, $"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}.."); + string projectDir = Path.Combine(testDir, ".."); + string srcDir = Path.Combine(projectDir, "..", "src"); + string buildDir = Path.Combine(srcDir, projectName, "bin", "Debug"); + Debug.Assert(Directory.Exists(buildDir), $"Directory '{buildDir}' doesn't exist"); + string csProject = Path.Combine(srcDir, projectName, projectName + ".csproj"); + var project = XDocument.Load(csProject); + string[] tfms = project.Descendants("TargetFrameworks").Union(project.Descendants("TargetFramework")).First().Value.Split(";", StringSplitOptions.RemoveEmptyEntries); + + // There may be old stuff from earlier builds like net45, netcoreapp3.0, etc. so filter it out + string[] actualTfmDirs = Directory.GetDirectories(buildDir).Where(dir => tfms.Any(tfm => dir.EndsWith(tfm))).ToArray(); + Debug.Assert(actualTfmDirs.Length > 0, $"Directory '{buildDir}' doesn't contain subdirectories matching {string.Join(";", tfms)}"); + + (string tfm, string content)[] publicApi = actualTfmDirs.Select(tfmDir => (new DirectoryInfo(tfmDir).Name.Replace(".", ""), Assembly.LoadFile(Path.Combine(tfmDir, projectName + ".dll")).GeneratePublicApi(new ApiGeneratorOptions + { + IncludeAssemblyAttributes = false, + //AllowNamespacePrefixes = new[] { "Microsoft.Extensions.DependencyInjection" }, + ExcludeAttributes = new[] { "System.Diagnostics.DebuggerDisplayAttribute", "System.Diagnostics.CodeAnalysis.AllowNullAttribute" } + }) + Environment.NewLine)).ToArray(); + + if (publicApi.DistinctBy(item => item.content).Count() == 1) + { + AutoApproveOrFail(publicApi[0].content, ""); + } + else + { + foreach (var item in publicApi.ToLookup(item => item.content)) + { + AutoApproveOrFail(item.Key, string.Join("+", item.Select(x => x.tfm).OrderBy(x => x))); + } + } + + // Approval test should (re)generate approved.txt files locally if needed. + // Approval test should fail on CI. + // https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables + void AutoApproveOrFail(string publicApi, string folder) + { + string file = null!; + + try + { + publicApi.ShouldMatchApproved(options => options.SubFolder(folder).NoDiff().WithFilenameGenerator((testMethodInfo, discriminator, fileType, fileExtension) => file = $"{type.Assembly.GetName().Name}.{fileType}.{fileExtension}")); + } + catch (ShouldMatchApprovedException) when (Environment.GetEnvironmentVariable("CI") == null) + { + string? received = Path.Combine(testDir, folder, file); + string? approved = received.Replace(".received.txt", ".approved.txt"); + if (File.Exists(received) && File.Exists(approved)) + { + File.Copy(received, approved, overwrite: true); + File.Delete(received); + } + else + { + throw; + } + } + } + } +} diff --git a/tests/GraphQL.Client.ApiTests/GraphQL.Client.Abstractions.Websocket.approved.txt b/tests/GraphQL.Client.ApiTests/GraphQL.Client.Abstractions.Websocket.approved.txt new file mode 100644 index 00000000..7616d52e --- /dev/null +++ b/tests/GraphQL.Client.ApiTests/GraphQL.Client.Abstractions.Websocket.approved.txt @@ -0,0 +1,86 @@ +namespace GraphQL.Client.Abstractions.Websocket +{ + public static class GraphQLWebSocketMessageType + { + public const string GQL_COMPLETE = "complete"; + public const string GQL_CONNECTION_ACK = "connection_ack"; + public const string GQL_CONNECTION_ERROR = "connection_error"; + public const string GQL_CONNECTION_INIT = "connection_init"; + public const string GQL_CONNECTION_KEEP_ALIVE = "ka"; + public const string GQL_CONNECTION_TERMINATE = "connection_terminate"; + public const string GQL_DATA = "data"; + public const string GQL_ERROR = "error"; + public const string GQL_NEXT = "next"; + public const string GQL_PING = "ping"; + public const string GQL_PONG = "pong"; + public const string GQL_START = "start"; + public const string GQL_STOP = "stop"; + public const string GQL_SUBSCRIBE = "subscribe"; + } + public class GraphQLWebSocketRequest : System.Collections.Generic.Dictionary, System.IEquatable + { + public const string ID_KEY = "id"; + public const string PAYLOAD_KEY = "payload"; + public const string TYPE_KEY = "type"; + public GraphQLWebSocketRequest() { } + public string Id { get; set; } + public object? Payload { get; set; } + public string Type { get; set; } + public bool Equals(GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketRequest other) { } + public override bool Equals(object obj) { } + public override int GetHashCode() { } + public void SendCanceled() { } + public void SendCompleted() { } + public void SendFailed(System.Exception e) { } + public System.Threading.Tasks.Task SendTask() { } + public static bool operator !=(GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketRequest request1, GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketRequest request2) { } + public static bool operator ==(GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketRequest request1, GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketRequest request2) { } + } + public class GraphQLWebSocketResponse : System.IEquatable + { + public GraphQLWebSocketResponse() { } + public string Id { get; set; } + public string Type { get; set; } + public bool Equals(GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketResponse other) { } + public override bool Equals(object obj) { } + public override int GetHashCode() { } + public static bool operator !=(GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketResponse response1, GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketResponse response2) { } + public static bool operator ==(GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketResponse response1, GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketResponse response2) { } + } + public class GraphQLWebSocketResponse : GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketResponse, System.IEquatable> + { + public GraphQLWebSocketResponse() { } + public TPayload Payload { get; set; } + public bool Equals(GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketResponse? other) { } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + } + public enum GraphQLWebsocketConnectionState + { + Disconnected = 0, + Connecting = 1, + Connected = 2, + } + public interface IGraphQLWebSocketClient : GraphQL.Client.Abstractions.IGraphQLClient + { + System.IObservable PongStream { get; } + System.IObservable WebSocketReceiveErrors { get; } + string? WebSocketSubProtocol { get; } + System.IObservable WebsocketConnectionState { get; } + System.Threading.Tasks.Task InitializeWebsocketConnection(); + System.Threading.Tasks.Task SendPingAsync(object? payload); + System.Threading.Tasks.Task SendPongAsync(object? payload); + } + public interface IGraphQLWebsocketJsonSerializer : GraphQL.Client.Abstractions.IGraphQLJsonSerializer + { + GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketResponse DeserializeToWebsocketResponse(byte[] bytes); + System.Threading.Tasks.Task DeserializeToWebsocketResponseWrapperAsync(System.IO.Stream stream); + byte[] SerializeToBytes(GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketRequest request); + } + public class WebsocketMessageWrapper : GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketResponse + { + public WebsocketMessageWrapper() { } + [System.Runtime.Serialization.IgnoreDataMember] + public byte[] MessageBytes { get; set; } + } +} diff --git a/tests/GraphQL.Client.ApiTests/GraphQL.Client.Abstractions.approved.txt b/tests/GraphQL.Client.ApiTests/GraphQL.Client.Abstractions.approved.txt new file mode 100644 index 00000000..ca0446a0 --- /dev/null +++ b/tests/GraphQL.Client.ApiTests/GraphQL.Client.Abstractions.approved.txt @@ -0,0 +1,66 @@ +namespace GraphQL.Client.Abstractions +{ + public static class GraphQLClientExtensions + { + public static System.IObservable> CreateSubscriptionStream(this GraphQL.Client.Abstractions.IGraphQLClient client, GraphQL.GraphQLRequest request, System.Func defineResponseType) { } + public static System.IObservable> CreateSubscriptionStream(this GraphQL.Client.Abstractions.IGraphQLClient client, GraphQL.GraphQLRequest request, System.Func defineResponseType, System.Action exceptionHandler) { } + public static System.Threading.Tasks.Task> SendMutationAsync(this GraphQL.Client.Abstractions.IGraphQLClient client, GraphQL.GraphQLRequest request, System.Func defineResponseType, System.Threading.CancellationToken cancellationToken = default) { } + public static System.Threading.Tasks.Task> SendMutationAsync(this GraphQL.Client.Abstractions.IGraphQLClient client, string query, object? variables = null, string? operationName = null, System.Func defineResponseType = null, System.Threading.CancellationToken cancellationToken = default) { } + public static System.Threading.Tasks.Task> SendQueryAsync(this GraphQL.Client.Abstractions.IGraphQLClient client, GraphQL.GraphQLRequest request, System.Func defineResponseType, System.Threading.CancellationToken cancellationToken = default) { } + public static System.Threading.Tasks.Task> SendQueryAsync(this GraphQL.Client.Abstractions.IGraphQLClient client, string query, object? variables = null, string? operationName = null, System.Func defineResponseType = null, System.Threading.CancellationToken cancellationToken = default) { } + } + public static class GraphQLJsonSerializerExtensions + { + public static TOptions AndReturn(this System.Action configure, TOptions options) { } + public static TOptions New(this System.Action configure) { } + } + public interface IGraphQLClient + { + System.IObservable> CreateSubscriptionStream(GraphQL.GraphQLRequest request); + System.IObservable> CreateSubscriptionStream(GraphQL.GraphQLRequest request, System.Action exceptionHandler); + System.Threading.Tasks.Task> SendMutationAsync(GraphQL.GraphQLRequest request, System.Threading.CancellationToken cancellationToken = default); + System.Threading.Tasks.Task> SendQueryAsync(GraphQL.GraphQLRequest request, System.Threading.CancellationToken cancellationToken = default); + } + public interface IGraphQLJsonSerializer + { + System.Threading.Tasks.Task> DeserializeFromUtf8StreamAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken); + string SerializeToString(GraphQL.GraphQLRequest request); + } +} +namespace GraphQL.Client.Abstractions.Utilities +{ + public static class StringExtensions + { + public static string Capitalize(this string str) { } + public static string StripIndent(this string str) { } + public static string ToCamelCase(this string str) { } + public static string ToConstantCase(this string str) { } + public static string ToKebabCase(this string str) { } + public static string ToLowerCase(this string str) { } + public static string ToLowerFirst(this string str) { } + public static string ToPascalCase(this string str) { } + public static string ToSnakeCase(this string str) { } + public static string ToUpperCase(this string str) { } + public static string ToUpperFirst(this string str) { } + public static System.Collections.Generic.IEnumerable ToWords(this string str) { } + } + public static class StringUtils + { + public static string Capitalize(string str) { } + public static string ChangeCase(string str, System.Func composer) { } + public static string ChangeCase(string str, System.Func composer) { } + public static string ChangeCase(string str, string sep, System.Func composer) { } + public static string ChangeCase(string str, string sep, System.Func composer) { } + public static string StripIndent(string str) { } + public static string ToCamelCase(string str) { } + public static string ToConstantCase(string str) { } + public static string ToKebabCase(string str) { } + public static string ToLowerCase(string str) { } + public static string ToLowerFirst(string str) { } + public static string ToPascalCase(string str) { } + public static string ToSnakeCase(string str) { } + public static string ToUpperCase(string str) { } + public static string ToUpperFirst(string str) { } + public static System.Collections.Generic.IEnumerable ToWords(string str) { } + } +} diff --git a/tests/GraphQL.Client.ApiTests/GraphQL.Client.ApiTests.csproj b/tests/GraphQL.Client.ApiTests/GraphQL.Client.ApiTests.csproj new file mode 100644 index 00000000..6b452440 --- /dev/null +++ b/tests/GraphQL.Client.ApiTests/GraphQL.Client.ApiTests.csproj @@ -0,0 +1,21 @@ + + + + + + net7 + enable + + + + + + + + + + + + + + diff --git a/tests/GraphQL.Client.ApiTests/GraphQL.Client.LocalExecution.approved.txt b/tests/GraphQL.Client.ApiTests/GraphQL.Client.LocalExecution.approved.txt new file mode 100644 index 00000000..c3c6704c --- /dev/null +++ b/tests/GraphQL.Client.ApiTests/GraphQL.Client.LocalExecution.approved.txt @@ -0,0 +1,25 @@ +namespace GraphQL.Client.LocalExecution +{ + public static class GraphQLLocalExecutionClient + { + public static GraphQL.Client.LocalExecution.GraphQLLocalExecutionClient New(TSchema schema, GraphQL.Client.Abstractions.IGraphQLJsonSerializer clientSerializer, GraphQL.IGraphQLTextSerializer serverSerializer) + where TSchema : GraphQL.Types.ISchema { } + } + public class GraphQLLocalExecutionClient : GraphQL.Client.Abstractions.IGraphQLClient + where TSchema : GraphQL.Types.ISchema + { + public GraphQLLocalExecutionClient(TSchema schema, GraphQL.IDocumentExecuter documentExecuter, GraphQL.Client.Abstractions.IGraphQLJsonSerializer serializer, GraphQL.IGraphQLTextSerializer documentSerializer) { } + public TSchema Schema { get; } + public GraphQL.Client.Abstractions.IGraphQLJsonSerializer Serializer { get; } + public System.IObservable> CreateSubscriptionStream(GraphQL.GraphQLRequest request) { } + public System.IObservable> CreateSubscriptionStream(GraphQL.GraphQLRequest request, System.Action exceptionHandler) { } + public void Dispose() { } + public System.Threading.Tasks.Task> SendMutationAsync(GraphQL.GraphQLRequest request, System.Threading.CancellationToken cancellationToken = default) { } + public System.Threading.Tasks.Task> SendQueryAsync(GraphQL.GraphQLRequest request, System.Threading.CancellationToken cancellationToken = default) { } + } + public static class ServiceCollectionExtensions + { + public static GraphQL.DI.IGraphQLBuilder AddGraphQLLocalExecutionClient(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) + where TSchema : GraphQL.Types.ISchema { } + } +} diff --git a/tests/GraphQL.Client.ApiTests/GraphQL.Client.Serializer.Newtonsoft.approved.txt b/tests/GraphQL.Client.ApiTests/GraphQL.Client.Serializer.Newtonsoft.approved.txt new file mode 100644 index 00000000..283399e3 --- /dev/null +++ b/tests/GraphQL.Client.ApiTests/GraphQL.Client.Serializer.Newtonsoft.approved.txt @@ -0,0 +1,27 @@ +namespace GraphQL.Client.Serializer.Newtonsoft +{ + public class ConstantCaseEnumConverter : Newtonsoft.Json.Converters.StringEnumConverter + { + public ConstantCaseEnumConverter() { } + public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) { } + } + public class MapConverter : Newtonsoft.Json.JsonConverter + { + public MapConverter() { } + public override GraphQL.Map ReadJson(Newtonsoft.Json.JsonReader reader, System.Type objectType, GraphQL.Map existingValue, bool hasExistingValue, Newtonsoft.Json.JsonSerializer serializer) { } + public override void WriteJson(Newtonsoft.Json.JsonWriter writer, GraphQL.Map value, Newtonsoft.Json.JsonSerializer serializer) { } + } + public class NewtonsoftJsonSerializer : GraphQL.Client.Abstractions.IGraphQLJsonSerializer, GraphQL.Client.Abstractions.Websocket.IGraphQLWebsocketJsonSerializer + { + public NewtonsoftJsonSerializer() { } + public NewtonsoftJsonSerializer(Newtonsoft.Json.JsonSerializerSettings jsonSerializerSettings) { } + public NewtonsoftJsonSerializer(System.Action configure) { } + public Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get; } + public static Newtonsoft.Json.JsonSerializerSettings DefaultJsonSerializerSettings { get; } + public System.Threading.Tasks.Task> DeserializeFromUtf8StreamAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken) { } + public GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketResponse DeserializeToWebsocketResponse(byte[] bytes) { } + public System.Threading.Tasks.Task DeserializeToWebsocketResponseWrapperAsync(System.IO.Stream stream) { } + public byte[] SerializeToBytes(GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketRequest request) { } + public string SerializeToString(GraphQL.GraphQLRequest request) { } + } +} diff --git a/tests/GraphQL.Client.ApiTests/GraphQL.Client.Serializer.SystemTextJson.approved.txt b/tests/GraphQL.Client.ApiTests/GraphQL.Client.Serializer.SystemTextJson.approved.txt new file mode 100644 index 00000000..ed38dee2 --- /dev/null +++ b/tests/GraphQL.Client.ApiTests/GraphQL.Client.Serializer.SystemTextJson.approved.txt @@ -0,0 +1,50 @@ +namespace GraphQL.Client.Serializer.SystemTextJson +{ + public class ConstantCaseJsonNamingPolicy : System.Text.Json.JsonNamingPolicy + { + public ConstantCaseJsonNamingPolicy() { } + public override string ConvertName(string name) { } + } + public static class ConverterHelperExtensions + { + public static string GetRawString(ref this System.Text.Json.Utf8JsonReader reader) { } + public static object ReadNumber(ref this System.Text.Json.Utf8JsonReader reader) { } + public static bool TryGetBigInteger(ref this System.Text.Json.Utf8JsonReader reader, out System.Numerics.BigInteger bi) { } + } + public class ErrorPathConverter : System.Text.Json.Serialization.JsonConverter + { + public ErrorPathConverter() { } + public override GraphQL.ErrorPath Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { } + public override void Write(System.Text.Json.Utf8JsonWriter writer, GraphQL.ErrorPath value, System.Text.Json.JsonSerializerOptions options) { } + } + public class ImmutableConverter : System.Text.Json.Serialization.JsonConverter + { + public ImmutableConverter() { } + public override bool CanConvert(System.Type typeToConvert) { } + public override object Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { } + public override void Write(System.Text.Json.Utf8JsonWriter writer, object value, System.Text.Json.JsonSerializerOptions options) { } + } + public static class JsonSerializerOptionsExtensions + { + public static System.Text.Json.JsonSerializerOptions SetupImmutableConverter(this System.Text.Json.JsonSerializerOptions options) { } + } + public class MapConverter : System.Text.Json.Serialization.JsonConverter + { + public MapConverter() { } + public override GraphQL.Map Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { } + public override void Write(System.Text.Json.Utf8JsonWriter writer, GraphQL.Map value, System.Text.Json.JsonSerializerOptions options) { } + } + public class SystemTextJsonSerializer : GraphQL.Client.Abstractions.IGraphQLJsonSerializer, GraphQL.Client.Abstractions.Websocket.IGraphQLWebsocketJsonSerializer + { + public SystemTextJsonSerializer() { } + public SystemTextJsonSerializer(System.Action configure) { } + public SystemTextJsonSerializer(System.Text.Json.JsonSerializerOptions options) { } + public System.Text.Json.JsonSerializerOptions Options { get; } + public static System.Text.Json.JsonSerializerOptions DefaultJsonSerializerOptions { get; } + public System.Threading.Tasks.Task> DeserializeFromUtf8StreamAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken) { } + public GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketResponse DeserializeToWebsocketResponse(byte[] bytes) { } + public System.Threading.Tasks.Task DeserializeToWebsocketResponseWrapperAsync(System.IO.Stream stream) { } + public byte[] SerializeToBytes(GraphQL.Client.Abstractions.Websocket.GraphQLWebSocketRequest request) { } + public string SerializeToString(GraphQL.GraphQLRequest request) { } + } +} diff --git a/tests/GraphQL.Client.ApiTests/GraphQL.Client.approved.txt b/tests/GraphQL.Client.ApiTests/GraphQL.Client.approved.txt new file mode 100644 index 00000000..15c7c32e --- /dev/null +++ b/tests/GraphQL.Client.ApiTests/GraphQL.Client.approved.txt @@ -0,0 +1,120 @@ +namespace GraphQL.Client.Http +{ + public class GraphQLHttpClient : GraphQL.Client.Abstractions.IGraphQLClient, GraphQL.Client.Abstractions.Websocket.IGraphQLWebSocketClient, System.IDisposable + { + public GraphQLHttpClient(GraphQL.Client.Http.GraphQLHttpClientOptions options, GraphQL.Client.Abstractions.Websocket.IGraphQLWebsocketJsonSerializer serializer) { } + public GraphQLHttpClient(System.Action configure, GraphQL.Client.Abstractions.Websocket.IGraphQLWebsocketJsonSerializer serializer) { } + public GraphQLHttpClient(string endPoint, GraphQL.Client.Abstractions.Websocket.IGraphQLWebsocketJsonSerializer serializer) { } + public GraphQLHttpClient(System.Uri endPoint, GraphQL.Client.Abstractions.Websocket.IGraphQLWebsocketJsonSerializer serializer) { } + public GraphQLHttpClient(GraphQL.Client.Http.GraphQLHttpClientOptions options, GraphQL.Client.Abstractions.Websocket.IGraphQLWebsocketJsonSerializer serializer, System.Net.Http.HttpClient httpClient) { } + public System.Net.Http.HttpClient HttpClient { get; } + public GraphQL.Client.Abstractions.Websocket.IGraphQLWebsocketJsonSerializer JsonSerializer { get; } + public GraphQL.Client.Http.GraphQLHttpClientOptions Options { get; } + public System.IObservable PongStream { get; } + public System.IObservable WebSocketReceiveErrors { get; } + public string? WebSocketSubProtocol { get; } + public System.IObservable WebsocketConnectionState { get; } + public System.IObservable> CreateSubscriptionStream(GraphQL.GraphQLRequest request) { } + public System.IObservable> CreateSubscriptionStream(GraphQL.GraphQLRequest request, System.Action? exceptionHandler) { } + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + public System.Threading.Tasks.Task InitializeWebsocketConnection() { } + public System.Threading.Tasks.Task> SendMutationAsync(GraphQL.GraphQLRequest request, System.Threading.CancellationToken cancellationToken = default) { } + public System.Threading.Tasks.Task SendPingAsync(object? payload) { } + public System.Threading.Tasks.Task SendPongAsync(object? payload) { } + public System.Threading.Tasks.Task> SendQueryAsync(GraphQL.GraphQLRequest request, System.Threading.CancellationToken cancellationToken = default) { } + } + public static class GraphQLHttpClientExtensions + { + public static System.IObservable> CreateSubscriptionStream(this GraphQL.Client.Abstractions.IGraphQLClient client, GraphQL.GraphQLRequest request, System.Action webSocketExceptionHandler) { } + public static System.IObservable> CreateSubscriptionStream(this GraphQL.Client.Abstractions.IGraphQLClient client, GraphQL.GraphQLRequest request, System.Func defineResponseType) { } + public static System.IObservable> CreateSubscriptionStream(this GraphQL.Client.Abstractions.IGraphQLClient client, GraphQL.GraphQLRequest request, System.Func defineResponseType, System.Action webSocketExceptionHandler) { } + } + public class GraphQLHttpClientOptions + { + public GraphQLHttpClientOptions() { } + public System.Func BackOffStrategy { get; set; } + public System.Func ConfigureWebSocketConnectionInitPayload { get; set; } + public System.Action ConfigureWebsocketOptions { get; set; } + public System.Net.Http.Headers.ProductInfoHeaderValue? DefaultUserAgentRequestHeader { get; set; } + public System.Uri? EndPoint { get; set; } + public System.Net.Http.HttpMessageHandler HttpMessageHandler { get; set; } + public System.Func IsValidResponseToDeserialize { get; set; } + public string MediaType { get; set; } + public System.Func OnWebsocketConnected { get; set; } + public System.Func> PreprocessRequest { get; set; } + public bool UseWebSocketForQueriesAndMutations { get; set; } + public System.Uri? WebSocketEndPoint { get; set; } + public string? WebSocketProtocol { get; set; } + } + public class GraphQLHttpRequest : GraphQL.GraphQLRequest + { + public GraphQLHttpRequest() { } + public GraphQLHttpRequest(GraphQL.GraphQLRequest other) { } + public GraphQLHttpRequest(string query, object? variables = null, string? operationName = null) { } + [System.Obsolete("Inherit from GraphQLHttpRequest and override ToHttpRequestMessage() to customize " + + "the HttpRequestMessage. Will be removed in v4.0.0.")] + [System.Runtime.Serialization.IgnoreDataMember] + public System.Action PreprocessHttpRequestMessage { get; set; } + public virtual System.Net.Http.HttpRequestMessage ToHttpRequestMessage(GraphQL.Client.Http.GraphQLHttpClientOptions options, GraphQL.Client.Abstractions.IGraphQLJsonSerializer serializer) { } + } + public class GraphQLHttpRequestException : System.Exception + { + public GraphQLHttpRequestException(System.Net.HttpStatusCode statusCode, System.Net.Http.Headers.HttpResponseHeaders responseHeaders, string? content) { } + public string? Content { get; } + public System.Net.Http.Headers.HttpResponseHeaders ResponseHeaders { get; } + public System.Net.HttpStatusCode StatusCode { get; } + } + public class GraphQLHttpResponse : GraphQL.GraphQLResponse + { + public GraphQLHttpResponse(GraphQL.GraphQLResponse response, System.Net.Http.Headers.HttpResponseHeaders responseHeaders, System.Net.HttpStatusCode statusCode) { } + public System.Net.Http.Headers.HttpResponseHeaders ResponseHeaders { get; set; } + public System.Net.HttpStatusCode StatusCode { get; set; } + } + public static class GraphQLResponseExtensions + { + public static GraphQL.Client.Http.GraphQLHttpResponse AsGraphQLHttpResponse(this GraphQL.GraphQLResponse response) { } + public static GraphQL.Client.Http.GraphQLHttpResponse ToGraphQLHttpResponse(this GraphQL.GraphQLResponse response, System.Net.Http.Headers.HttpResponseHeaders responseHeaders, System.Net.HttpStatusCode statusCode) { } + } + [System.Serializable] + public class GraphQLSubscriptionException : System.Exception + { + public GraphQLSubscriptionException() { } + public GraphQLSubscriptionException(object error) { } + protected GraphQLSubscriptionException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + } + public static class UriExtensions + { + public static System.Uri GetWebSocketUri(this System.Uri uri) { } + public static bool HasWebSocketScheme(this System.Uri? uri) { } + } +} +namespace GraphQL.Client.Http.Websocket +{ + [System.Serializable] + public class GraphQLWebsocketConnectionException : System.Exception + { + public GraphQLWebsocketConnectionException() { } + public GraphQLWebsocketConnectionException(string message) { } + protected GraphQLWebsocketConnectionException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + public GraphQLWebsocketConnectionException(string message, System.Exception innerException) { } + } + public interface IWebsocketProtocolHandler + { + string WebsocketProtocol { get; } + System.IObservable> CreateGraphQLRequestObservable(GraphQL.GraphQLRequest request); + System.IObservable CreatePongObservable(); + System.IObservable> CreateSubscriptionObservable(GraphQL.GraphQLRequest request); + System.Threading.Tasks.Task InitializeConnectionAsync(System.IObservable incomingMessages, System.Reactive.Disposables.CompositeDisposable closeConnectionDisposable); + System.Threading.Tasks.Task SendCloseConnectionRequestAsync(); + System.Threading.Tasks.Task SendPingAsync(object? payload); + System.Threading.Tasks.Task SendPongAsync(object? payload); + } + public static class WebSocketProtocols + { + public const string AUTO_NEGOTIATE = null; + public const string GRAPHQL_TRANSPORT_WS = "graphql-transport-ws"; + public const string GRAPHQL_WS = "graphql-ws"; + public static System.Collections.Generic.IEnumerable GetSupportedWebSocketProtocols() { } + } +} diff --git a/tests/GraphQL.Client.ApiTests/GraphQL.Primitives.approved.txt b/tests/GraphQL.Client.ApiTests/GraphQL.Primitives.approved.txt new file mode 100644 index 00000000..bccef39f --- /dev/null +++ b/tests/GraphQL.Client.ApiTests/GraphQL.Primitives.approved.txt @@ -0,0 +1,80 @@ +namespace GraphQL +{ + public class ErrorPath : System.Collections.Generic.List + { + public ErrorPath() { } + public ErrorPath(System.Collections.Generic.IEnumerable collection) { } + } + public class GraphQLError : System.IEquatable + { + public GraphQLError() { } + [System.Runtime.Serialization.DataMember(Name="extensions")] + public GraphQL.Map? Extensions { get; set; } + [System.Runtime.Serialization.DataMember(Name="locations")] + public GraphQL.GraphQLLocation[]? Locations { get; set; } + [System.Runtime.Serialization.DataMember(Name="message")] + public string Message { get; set; } + [System.Runtime.Serialization.DataMember(Name="path")] + public GraphQL.ErrorPath? Path { get; set; } + public bool Equals(GraphQL.GraphQLError? other) { } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + public static bool operator !=(GraphQL.GraphQLError? left, GraphQL.GraphQLError? right) { } + public static bool operator ==(GraphQL.GraphQLError? left, GraphQL.GraphQLError? right) { } + } + public sealed class GraphQLLocation : System.IEquatable + { + public GraphQLLocation() { } + public uint Column { get; set; } + public uint Line { get; set; } + public bool Equals(GraphQL.GraphQLLocation? other) { } + public override bool Equals(object obj) { } + public override int GetHashCode() { } + public static bool operator !=(GraphQL.GraphQLLocation? left, GraphQL.GraphQLLocation? right) { } + public static bool operator ==(GraphQL.GraphQLLocation? left, GraphQL.GraphQLLocation? right) { } + } + public class GraphQLRequest : System.Collections.Generic.Dictionary, System.IEquatable + { + public const string EXTENSIONS_KEY = "extensions"; + public const string OPERATION_NAME_KEY = "operationName"; + public const string QUERY_KEY = "query"; + public const string VARIABLES_KEY = "variables"; + public GraphQLRequest() { } + public GraphQLRequest(GraphQL.GraphQLRequest other) { } + public GraphQLRequest(string query, object? variables = null, string? operationName = null, object? extensions = null) { } + public object? Extensions { get; set; } + public string? OperationName { get; set; } + public string Query { get; set; } + public object? Variables { get; set; } + public virtual bool Equals(GraphQL.GraphQLRequest? other) { } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + public static bool operator !=(GraphQL.GraphQLRequest? left, GraphQL.GraphQLRequest? right) { } + public static bool operator ==(GraphQL.GraphQLRequest? left, GraphQL.GraphQLRequest? right) { } + } + public class GraphQLResponse : GraphQL.IGraphQLResponse, System.IEquatable?> + { + public GraphQLResponse() { } + [System.Runtime.Serialization.DataMember(Name="data")] + public T Data { get; set; } + [System.Runtime.Serialization.DataMember(Name="errors")] + public GraphQL.GraphQLError[]? Errors { get; set; } + [System.Runtime.Serialization.DataMember(Name="extensions")] + public GraphQL.Map? Extensions { get; set; } + public bool Equals(GraphQL.GraphQLResponse? other) { } + public override bool Equals(object? obj) { } + public override int GetHashCode() { } + public static bool operator !=(GraphQL.GraphQLResponse? response1, GraphQL.GraphQLResponse? response2) { } + public static bool operator ==(GraphQL.GraphQLResponse? response1, GraphQL.GraphQLResponse? response2) { } + } + public interface IGraphQLResponse + { + object Data { get; } + GraphQL.GraphQLError[]? Errors { get; set; } + GraphQL.Map? Extensions { get; set; } + } + public class Map : System.Collections.Generic.Dictionary + { + public Map() { } + } +} diff --git a/tests/tests.props b/tests/tests.props index 87ec1373..971b7cc4 100644 --- a/tests/tests.props +++ b/tests/tests.props @@ -8,6 +8,7 @@ + From 77445c6388784f932d100e4a09dbfc62370d8c55 Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Wed, 26 Apr 2023 10:55:35 +0300 Subject: [PATCH 2/3] fix --- tests/GraphQL.Client.ApiTests/ApiApprovalTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/GraphQL.Client.ApiTests/ApiApprovalTests.cs b/tests/GraphQL.Client.ApiTests/ApiApprovalTests.cs index 9625649b..fafa11ab 100644 --- a/tests/GraphQL.Client.ApiTests/ApiApprovalTests.cs +++ b/tests/GraphQL.Client.ApiTests/ApiApprovalTests.cs @@ -30,7 +30,7 @@ public void PublicApi(Type type) string testDir = Path.Combine(baseDir, $"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}.."); string projectDir = Path.Combine(testDir, ".."); string srcDir = Path.Combine(projectDir, "..", "src"); - string buildDir = Path.Combine(srcDir, projectName, "bin", "Debug"); + string buildDir = Path.Combine(srcDir, projectName, "bin", Environment.GetEnvironmentVariable("CI") == null ? "Debug" : "Release"); Debug.Assert(Directory.Exists(buildDir), $"Directory '{buildDir}' doesn't exist"); string csProject = Path.Combine(srcDir, projectName, projectName + ".csproj"); var project = XDocument.Load(csProject); From 6735e7fa8d953e38c8a3753adac49d13d5a4ce0f Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Tue, 9 May 2023 09:46:16 +0300 Subject: [PATCH 3/3] remove obsolete --- src/GraphQL.Client/GraphQLHttpRequest.cs | 10 --------- .../GraphQL.Client.approved.txt | 7 ++---- .../GraphQL.Primitives.approved.txt | 4 ++-- .../QueryAndMutationTests/Base.cs | 22 ------------------- 4 files changed, 4 insertions(+), 39 deletions(-) diff --git a/src/GraphQL.Client/GraphQLHttpRequest.cs b/src/GraphQL.Client/GraphQLHttpRequest.cs index 9ff0c600..322abb87 100644 --- a/src/GraphQL.Client/GraphQLHttpRequest.cs +++ b/src/GraphQL.Client/GraphQLHttpRequest.cs @@ -21,13 +21,6 @@ public GraphQLHttpRequest(GraphQLRequest other) { } - /// - /// Allows to preprocess a before it is sent, i.e. add custom headers - /// - [IgnoreDataMember] - [Obsolete("Inherit from GraphQLHttpRequest and override ToHttpRequestMessage() to customize the HttpRequestMessage. Will be removed in v4.0.0.")] - public Action PreprocessHttpRequestMessage { get; set; } = message => { }; - /// /// Creates a from this . /// Used by to convert GraphQL requests when sending them as regular HTTP requests. @@ -48,9 +41,6 @@ public virtual HttpRequestMessage ToHttpRequestMessage(GraphQLHttpClientOptions if (options.DefaultUserAgentRequestHeader != null) message.Headers.UserAgent.Add(options.DefaultUserAgentRequestHeader); -#pragma warning disable CS0618 // Type or member is obsolete - PreprocessHttpRequestMessage(message); -#pragma warning restore CS0618 // Type or member is obsolete return message; } } diff --git a/tests/GraphQL.Client.ApiTests/GraphQL.Client.approved.txt b/tests/GraphQL.Client.ApiTests/GraphQL.Client.approved.txt index 15c7c32e..f63124b2 100644 --- a/tests/GraphQL.Client.ApiTests/GraphQL.Client.approved.txt +++ b/tests/GraphQL.Client.ApiTests/GraphQL.Client.approved.txt @@ -46,16 +46,13 @@ namespace GraphQL.Client.Http public bool UseWebSocketForQueriesAndMutations { get; set; } public System.Uri? WebSocketEndPoint { get; set; } public string? WebSocketProtocol { get; set; } + public static bool DefaultIsValidResponseToDeserialize(System.Net.Http.HttpResponseMessage r) { } } public class GraphQLHttpRequest : GraphQL.GraphQLRequest { public GraphQLHttpRequest() { } public GraphQLHttpRequest(GraphQL.GraphQLRequest other) { } - public GraphQLHttpRequest(string query, object? variables = null, string? operationName = null) { } - [System.Obsolete("Inherit from GraphQLHttpRequest and override ToHttpRequestMessage() to customize " + - "the HttpRequestMessage. Will be removed in v4.0.0.")] - [System.Runtime.Serialization.IgnoreDataMember] - public System.Action PreprocessHttpRequestMessage { get; set; } + public GraphQLHttpRequest(string query, object? variables = null, string? operationName = null, System.Collections.Generic.Dictionary? extensions = null) { } public virtual System.Net.Http.HttpRequestMessage ToHttpRequestMessage(GraphQL.Client.Http.GraphQLHttpClientOptions options, GraphQL.Client.Abstractions.IGraphQLJsonSerializer serializer) { } } public class GraphQLHttpRequestException : System.Exception diff --git a/tests/GraphQL.Client.ApiTests/GraphQL.Primitives.approved.txt b/tests/GraphQL.Client.ApiTests/GraphQL.Primitives.approved.txt index bccef39f..e17f35ad 100644 --- a/tests/GraphQL.Client.ApiTests/GraphQL.Primitives.approved.txt +++ b/tests/GraphQL.Client.ApiTests/GraphQL.Primitives.approved.txt @@ -41,8 +41,8 @@ namespace GraphQL public const string VARIABLES_KEY = "variables"; public GraphQLRequest() { } public GraphQLRequest(GraphQL.GraphQLRequest other) { } - public GraphQLRequest(string query, object? variables = null, string? operationName = null, object? extensions = null) { } - public object? Extensions { get; set; } + public GraphQLRequest(string query, object? variables = null, string? operationName = null, System.Collections.Generic.Dictionary? extensions = null) { } + public System.Collections.Generic.Dictionary? Extensions { get; set; } public string? OperationName { get; set; } public string Query { get; set; } public object? Variables { get; set; } diff --git a/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Base.cs b/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Base.cs index bff54832..0b120ba4 100644 --- a/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Base.cs +++ b/tests/GraphQL.Integration.Tests/QueryAndMutationTests/Base.cs @@ -163,28 +163,6 @@ query Human($id: String!){ Assert.Equal("Han Solo", queryResponse.Data.Human.Name); } - [Fact] - public async void PreprocessHttpRequestMessageIsCalled() - { - var callbackTester = new CallbackMonitor(); - var graphQLRequest = new GraphQLHttpRequest($"{{ human(id: \"1\") {{ name }} }}") - { -#pragma warning disable CS0618 // Type or member is obsolete - PreprocessHttpRequestMessage = callbackTester.Invoke -#pragma warning restore CS0618 // Type or member is obsolete - }; - - var expectedHeaders = new HttpRequestMessage().Headers; - expectedHeaders.UserAgent.Add(new ProductInfoHeaderValue("GraphQL.Client", typeof(GraphQLHttpClient).Assembly.GetName().Version.ToString())); - expectedHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/graphql-response+json")); - expectedHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - expectedHeaders.AcceptCharset.Add(new StringWithQualityHeaderValue("utf-8")); - var response = await StarWarsClient.SendQueryAsync(graphQLRequest, () => new { Human = new { Name = string.Empty } }); - callbackTester.Should().HaveBeenInvokedWithPayload().Which.Headers.Should().BeEquivalentTo(expectedHeaders); - Assert.Null(response.Errors); - Assert.Equal("Luke", response.Data.Human.Name); - } - [Fact] public async Task PostRequestCanBeCancelled() {