/* SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. * * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using OpenSearch.OpenSearch.Ephemeral; using OpenSearch.OpenSearch.Xunit.XunitPlumbing; using OpenSearch.Client; using Tests.Configuration; using Tests.Core.Client; using Tests.Core.ManagedOpenSearch.Clusters; using Tests.Core.Serialization; using Tests.Domain.Helpers; using Tests.Framework.EndpointTests.TestState; using Xunit; namespace Tests.Framework.EndpointTests { public abstract class RequestResponseApiTestBase : ExpectJsonTestBase, IClusterFixture, IClassFixture where TCluster : IEphemeralCluster, IOpenSearchClientTestCluster, new() where TResponse : class, IResponse where TInterface : class where TDescriptor : class, TInterface where TInitializer : class, TInterface { private readonly EndpointUsage _usage; protected RequestResponseApiTestBase(TCluster cluster, EndpointUsage usage) : base(cluster.Client) { _usage = usage ?? throw new ArgumentNullException(nameof(usage)); if (cluster == null) throw new ArgumentNullException(nameof(cluster)); Cluster = cluster; Responses = usage.CallOnce(ClientUsage); UniqueValues = usage.CallUniqueValues; } public virtual IOpenSearchClient Client => TestConfiguration.Instance.RunIntegrationTests ? Cluster.Client : TestClient.DefaultInMemoryClient; public TCluster Cluster { get; } protected virtual string CallIsolatedValue => UniqueValues.Value; protected virtual Func Fluent { get; } = null; protected virtual TInitializer Initializer { get; } = null; protected bool RanIntegrationSetup => _usage?.CalledSetup ?? false; protected LazyResponses Responses { get; } protected virtual bool TestOnlyOne => TestClient.Configuration.TestOnlyOne; protected CallUniqueValues UniqueValues { get; } protected static string RandomString() => Guid.NewGuid().ToString("N").Substring(0, 8); protected string U(string s) => Uri.EscapeDataString(s); protected T ExtendedValue(string key) where T : class => UniqueValues.ExtendedValue(key); protected bool TryGetExtendedValue(string key, out T t) where T : class => UniqueValues.TryGetExtendedValue(key, out t); protected void ExtendedValue(string key, T value) where T : class => UniqueValues.ExtendedValue(key, value); protected virtual TDescriptor NewDescriptor() => Activator.CreateInstance(); protected virtual void IntegrationSetup(IOpenSearchClient client, CallUniqueValues values) { } protected virtual void IntegrationTeardown(IOpenSearchClient client, CallUniqueValues values) { } protected virtual void OnBeforeCall(IOpenSearchClient client) { } protected virtual void OnAfterCall(IOpenSearchClient client) { } protected abstract LazyResponses ClientUsage(); protected LazyResponses Calls( Func, TResponse> fluent, Func, Task> fluentAsync, Func request, Func> requestAsync ) => new LazyResponses(async () => { var client = Client; void IntegrateOnly(Action act) { if (!TestClient.Configuration.RunIntegrationTests) return; act(client); } if (TestClient.Configuration.RunIntegrationTests) { IntegrationSetup(client, UniqueValues); _usage.CalledSetup = true; } (ClientMethod, Func>) Api(ClientMethod method, Func> action) => (method, action); var dict = new Dictionary(); var views = new[] { Api(ClientMethod.Fluent, () => new ValueTask(fluent(client, Fluent))), Api(ClientMethod.Initializer, () => new ValueTask(request(client, Initializer))), Api(ClientMethod.FluentAsync, async () => await fluentAsync(client, Fluent)), Api(ClientMethod.InitializerAsync, async () => await requestAsync(client, Initializer)), }; foreach (var (v, m) in views.OrderBy((t)=> Gimme.Random.Int())) { UniqueValues.CurrentView = v; IntegrateOnly(OnBeforeCall); dict.Add(v, await m()); IntegrateOnly(OnAfterCall); if (TestOnlyOne) break; } if (TestClient.Configuration.RunIntegrationTests) { IntegrationTeardown(client, UniqueValues); _usage.CalledTeardown = true; } return dict; }); protected virtual async Task AssertOnAllResponses(Action assert) { var responses = await Responses; foreach (var kv in responses) { var response = kv.Value as TResponse; try { UniqueValues.CurrentView = kv.Key; assert(response); } #pragma warning disable 7095 //enable this if you expect a single overload to act up #pragma warning disable 8360 catch (Exception ex) when (false) #pragma warning restore 7095 #pragma warning restore 8360 #pragma warning disable 0162 //dead code while the previous exception filter is false { throw new Exception($"asserting over the response from: {kv.Key} failed: {ex.Message}", ex); } #pragma warning restore 0162 } } } }