Skip to content

Commit c3fa13d

Browse files
Mpdreamzrusscam
authored andcommitted
Refactor Analysis tests (#3424)
This commit refactors the way we do analysis tests by making it easier for test classes to do full blown endpoint testing (cherry picked from commit d6f1ae5) * continuing to port analysis over to more isolated chunks (cherry picked from commit f5f0c437871589b1fb90b6c4c6f09f0dfc296d7e) * Ported normalizers over to new test format (cherry picked from commit c74ed51e2c30804ffc1d50f95a17893a93bfa6ea) * clean up namespaces (cherry picked from commit f2da9f51b43b188cc1b2d09f616fbf87ca268344) * All analysis unit/integ tests pass again (cherry picked from commit 7ecbee5435df02810ede7f07985e7bb13f66b6f3) * abstracted analysis base classes even further to all share a base which implements the bulk of the setup and tests (cherry picked from commit 8a6e99493a4174a87cc8680609afd0c482cf10d7) (cherry picked from commit 6461c8f)
1 parent 4e62181 commit c3fa13d

28 files changed

+2003
-629
lines changed

src/Nest/Analysis/Tokenizers/Tokenizers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public Tokenizers(Dictionary<string, ITokenizer> container)
1919
public void Add(string name, ITokenizer analyzer) => BackingDictionary.Add(name, analyzer);
2020
}
2121

22-
public class TokenizersDescriptor :IsADictionaryDescriptorBase<TokenizersDescriptor, ITokenizers, string, ITokenizer>
22+
public class TokenizersDescriptor : IsADictionaryDescriptorBase<TokenizersDescriptor, ITokenizers, string, ITokenizer>
2323
{
2424
public TokenizersDescriptor() : base(new Tokenizers()) { }
2525

src/Tests/Tests.Core/ManagedElasticsearch/Clusters/ReadOnlyCluster.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
using Elastic.Managed.Ephemeral.Plugins;
2-
using Tests.Core.ManagedElasticsearch.NodeSeeders;
1+
using Tests.Core.ManagedElasticsearch.NodeSeeders;
2+
using static Elastic.Managed.Ephemeral.Plugins.ElasticsearchPlugin;
33

44
namespace Tests.Core.ManagedElasticsearch.Clusters
55
{
66
public class ReadOnlyCluster : ClientTestClusterBase
77
{
8-
public ReadOnlyCluster() : base(ElasticsearchPlugin.MapperMurmur3) { }
8+
public ReadOnlyCluster() : base(MapperMurmur3) { }
99

1010
protected override void SeedCluster() => new DefaultSeeder(this.Client).SeedNode();
1111
}

src/Tests/Tests.Core/ManagedElasticsearch/Clusters/WritableCluster.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
using Elastic.Managed.Ephemeral.Plugins;
2-
using Tests.Core.ManagedElasticsearch.NodeSeeders;
1+
using Tests.Core.ManagedElasticsearch.NodeSeeders;
2+
using static Elastic.Managed.Ephemeral.Plugins.ElasticsearchPlugin;
33

44
namespace Tests.Core.ManagedElasticsearch.Clusters
55
{
66
/// <summary> Use this cluster for api's that do writes. If they are however intrusive or long running consider IntrusiveOperationCluster instead. </summary>
77
public class WritableCluster : ClientTestClusterBase
88
{
99
public WritableCluster() : base(new ClientTestClusterConfiguration(
10-
ElasticsearchPlugin.IngestGeoIp, ElasticsearchPlugin.IngestAttachment, ElasticsearchPlugin.AnalysisKuromoji, ElasticsearchPlugin.AnalysisIcu, ElasticsearchPlugin.AnalysisPhonetic, ElasticsearchPlugin.MapperMurmur3
10+
IngestGeoIp,
11+
IngestAttachment,
12+
AnalysisKuromoji,
13+
AnalysisIcu,
14+
AnalysisPhonetic,
15+
MapperMurmur3
1116
)
1217
{
1318
MaxConcurrency = 4
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
using Elastic.Xunit;
5+
using Elastic.Xunit.XunitPlumbing;
6+
using FluentAssertions;
7+
using Nest;
8+
using Tests.Core.Client;
9+
using Tests.Core.ManagedElasticsearch.Clusters;
10+
using Tests.Core.Serialization;
11+
using Tests.Framework.Integration;
12+
13+
namespace Tests.Analysis
14+
{
15+
public interface IAnalysisAssertion
16+
{
17+
string Name { get; }
18+
object Json { get; }
19+
}
20+
public interface IAnalysisAssertion<out TComponent, out TContainer, in TDescriptor> : IAnalysisAssertion
21+
where TContainer : class
22+
{
23+
TComponent Initializer { get; }
24+
Func<string, TDescriptor, IPromise<TContainer>> Fluent { get; }
25+
}
26+
27+
[IntegrationTestCluster(typeof(WritableCluster))]
28+
public abstract class AnalysisComponentTestBase<TAssertion, TComponent, TContainer, TDescriptor>
29+
: IAnalysisAssertion<TComponent, TContainer, TDescriptor>
30+
where TAssertion : AnalysisComponentTestBase<TAssertion, TComponent, TContainer, TDescriptor>, new()
31+
where TContainer : class
32+
{
33+
private static readonly SingleEndpointUsage<ICreateIndexResponse> Usage = new SingleEndpointUsage<ICreateIndexResponse>
34+
(
35+
fluent: (s, c) => c.CreateIndex(s, AssertionSetup.FluentCall),
36+
fluentAsync: (s, c) => c.CreateIndexAsync(s, AssertionSetup.FluentCall),
37+
request: (s, c) => c.CreateIndex(AssertionSetup.InitializerCall(s)),
38+
requestAsync: (s, c) => c.CreateIndexAsync(AssertionSetup.InitializerCall(s)),
39+
valuePrefix: $"test-{typeof(TAssertion).Name.ToLowerInvariant()}"
40+
)
41+
{
42+
OnAfterCall = c=> c.DeleteIndex(Usage.CallUniqueValues.Value)
43+
};
44+
protected static TAssertion AssertionSetup { get; } = new TAssertion();
45+
46+
protected AnalysisComponentTestBase()
47+
{
48+
this.Client = (ElasticXunitRunner.CurrentCluster as INestTestCluster)?.Client ?? TestClient.DefaultInMemoryClient;
49+
Usage.KickOffOnce(this.Client, oneRandomCall: true);
50+
}
51+
52+
private IElasticClient Client { get; }
53+
54+
public abstract string Name { get; }
55+
public abstract TComponent Initializer { get; }
56+
public abstract Func<string, TDescriptor, IPromise<TContainer>> Fluent { get; }
57+
public abstract object Json { get; }
58+
59+
private Func<CreateIndexDescriptor, ICreateIndexRequest> FluentCall => i =>i.Settings(s => s.Analysis(this.FluentAnalysis));
60+
protected abstract IAnalysis FluentAnalysis(AnalysisDescriptor an);
61+
62+
private CreateIndexRequest InitializerCall(string index) => new CreateIndexRequest(index)
63+
{
64+
Settings = new IndexSettings { Analysis = this.InitializerAnalysis() }
65+
};
66+
protected abstract Nest.Analysis InitializerAnalysis();
67+
68+
[U] public virtual async Task TestPutSettingsRequest() => await Usage.AssertOnAllResponses(r =>
69+
{
70+
var json = new { settings = new { analysis = this.AnalysisJson } };
71+
SerializationTestHelper.Expect(json).FromRequest(r);
72+
});
73+
74+
protected abstract object AnalysisJson { get; }
75+
76+
[I] public virtual async Task TestPutSettingsResponse() => await Usage.AssertOnAllResponses(r =>
77+
{
78+
r.ApiCall.HttpStatusCode.Should().Be(200);
79+
});
80+
81+
}
82+
}

src/Tests/Tests/Analysis/AnalysisCrudTests.cs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
using System.Linq;
2-
using Elastic.Xunit.XunitPlumbing;
32
using FluentAssertions;
43
using Nest;
4+
using Tests.Analysis.Tokenizers;
55
using Tests.Core.Extensions;
66
using Tests.Core.ManagedElasticsearch.Clusters;
77
using Tests.Framework;
88
using Tests.Framework.Integration;
9-
using Tests.Framework.ManagedElasticsearch.Clusters;
10-
using Xunit;
119
using static Tests.Framework.Promisify;
1210

1311
namespace Tests.Analysis
1412
{
1513

16-
[SkipVersion("<5.2.0", "This tests contains analyzers/tokenfilters not found in previous versions, need a clean way to seperate these out")]
1714
public class AnalysisCrudTests
1815
: CrudWithNoDeleteTestBase<ICreateIndexResponse, IGetIndexSettingsResponse, IUpdateIndexSettingsResponse>
1916
{
@@ -46,21 +43,21 @@ protected override LazyResponses Create() => Calls<CreateIndexDescriptor, Create
4643
{
4744
Analysis = new Nest.Analysis
4845
{
49-
Analyzers = Analyzers.AnalyzerUsageTests.InitializerExample.Analysis.Analyzers,
50-
CharFilters = CharFilters.CharFilterUsageTests.InitializerExample.Analysis.CharFilters,
51-
Tokenizers = Tokenizers.TokenizerUsageTests.InitializerExample.Analysis.Tokenizers,
52-
TokenFilters = TokenFilters.TokenFilterUsageTests.InitializerExample.Analysis.TokenFilters,
46+
Analyzers = AnalysisUsageTests.AnalyzersInitializer.Analysis.Analyzers,
47+
CharFilters = AnalysisUsageTests.CharFiltersInitializer.Analysis.CharFilters,
48+
Tokenizers = AnalysisUsageTests.TokenizersInitializer.Analysis.Tokenizers,
49+
TokenFilters = AnalysisUsageTests.TokenFiltersInitializer.Analysis.TokenFilters,
5350
}
5451
}
5552
};
5653

5754
protected virtual ICreateIndexRequest CreateFluent(string indexName, CreateIndexDescriptor c) =>
5855
c.Settings(s => s
5956
.Analysis(a => a
60-
.Analyzers(t => Promise(Analyzers.AnalyzerUsageTests.FluentExample(s).Value.Analysis.Analyzers))
61-
.CharFilters(t => Promise(CharFilters.CharFilterUsageTests.FluentExample(s).Value.Analysis.CharFilters))
62-
.Tokenizers(t => Promise(Tokenizers.TokenizerUsageTests.FluentExample(s).Value.Analysis.Tokenizers))
63-
.TokenFilters(t => Promise(TokenFilters.TokenFilterUsageTests.FluentExample(s).Value.Analysis.TokenFilters))
57+
.Analyzers(t => Promise(AnalysisUsageTests.AnalyzersFluent.Analysis.Analyzers))
58+
.CharFilters(t => Promise(AnalysisUsageTests.CharFiltersFluent.Analysis.CharFilters))
59+
.Tokenizers(t => Promise(AnalysisUsageTests.TokenizersFluent.Analysis.Tokenizers))
60+
.TokenFilters(t => Promise(AnalysisUsageTests.TokenFiltersFluent.Analysis.TokenFilters))
6461
)
6562
);
6663

@@ -82,7 +79,7 @@ protected override LazyResponses Read() => Calls<GetIndexSettingsDescriptor, Get
8279

8380
/**
8481
* Here we assert over the response from `GetIndexSettings()` after the index creation to make sure our analysis chain did infact
85-
* store our html char filter called `stripMe`
82+
* store our html char filter called `htmls`
8683
*/
8784
protected override void ExpectAfterCreate(IGetIndexSettingsResponse response)
8885
{
@@ -94,7 +91,7 @@ protected override void ExpectAfterCreate(IGetIndexSettingsResponse response)
9491
indexSettings.Analysis.Should().NotBeNull();
9592
indexSettings.Analysis.CharFilters.Should().NotBeNull();
9693

97-
var firstHtmlCharFilter = indexSettings.Analysis.CharFilters["stripMe"];
94+
var firstHtmlCharFilter = indexSettings.Analysis.CharFilters["htmls"];
9895
firstHtmlCharFilter.Should().NotBeNull();
9996
}
10097

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using Elastic.Xunit.XunitPlumbing;
6+
using FluentAssertions;
7+
using Nest;
8+
using Tests.Analysis.Analyzers;
9+
using Tests.Analysis.CharFilters;
10+
using Tests.Analysis.Normalizers;
11+
using Tests.Analysis.TokenFilters;
12+
using Tests.Analysis.Tokenizers;
13+
using Tests.Core.Client;
14+
15+
namespace Tests.Analysis
16+
{
17+
public class AnalysisUsageTestsTests
18+
{
19+
[U] public static void CollectionsShouldNotBeEmpty()
20+
{
21+
var analyzers = AnalysisUsageTests.AnalyzersInitializer.Analysis.Analyzers;
22+
var charFilters = AnalysisUsageTests.CharFiltersInitializer.Analysis.CharFilters;
23+
var tokenizers = AnalysisUsageTests.TokenizersInitializer.Analysis.Tokenizers;
24+
var tokenFilters = AnalysisUsageTests.TokenFiltersInitializer.Analysis.TokenFilters;
25+
26+
analyzers.Should().NotBeNull().And.NotBeEmpty();
27+
charFilters.Should().NotBeNull().And.NotBeEmpty();
28+
tokenizers.Should().NotBeNull().And.NotBeEmpty();
29+
tokenFilters.Should().NotBeNull().And.NotBeEmpty();
30+
}
31+
}
32+
33+
public static class AnalysisUsageTests
34+
{
35+
36+
public static IndexSettings NormalizersFluent => Fluent<NormalizersDescriptor, INormalizerAssertion, INormalizers>(i => i.Fluent, (a, v) => a.Normalizers = v.Value);
37+
38+
public static IndexSettings AnalyzersFluent => Fluent<AnalyzersDescriptor, IAnalyzerAssertion, IAnalyzers>(i => i.Fluent, (a, v) => a.Analyzers = v.Value);
39+
40+
public static IndexSettings TokenizersFluent => Fluent<TokenizersDescriptor, ITokenizerAssertion, ITokenizers>(i => i.Fluent, (a, v) => a.Tokenizers = v.Value);
41+
42+
public static IndexSettings TokenFiltersFluent => Fluent<TokenFiltersDescriptor, ITokenFilterAssertion, ITokenFilters>(i => i.Fluent, (a, v) => a.TokenFilters = v.Value);
43+
44+
public static IndexSettings CharFiltersFluent => Fluent<CharFiltersDescriptor, ICharFilterAssertion, ICharFilters>(i => i.Fluent, (a, v) => a.CharFilters = v.Value);
45+
46+
public static IndexSettings NormalizersInitializer => Init<Nest.Normalizers, INormalizerAssertion, INormalizer>(i => i.Initializer, (a, v) => a.Normalizers = v);
47+
48+
public static IndexSettings AnalyzersInitializer => Init<Nest.Analyzers, IAnalyzerAssertion, IAnalyzer>(i => i.Initializer, (a, v) => a.Analyzers = v);
49+
50+
public static IndexSettings TokenizersInitializer => Init<Nest.Tokenizers, ITokenizerAssertion, ITokenizer>(i => i.Initializer, (a, v) => a.Tokenizers = v);
51+
52+
public static IndexSettings TokenFiltersInitializer => Init<Nest.TokenFilters, ITokenFilterAssertion, ITokenFilter>(i => i.Initializer, (a, v) => a.TokenFilters = v);
53+
54+
public static IndexSettings CharFiltersInitializer => Init<Nest.CharFilters, ICharFilterAssertion, ICharFilter>(i => i.Initializer, (a, v) => a.CharFilters = v);
55+
56+
private static IndexSettings Fluent<TContainer, TAssertion, TValue>(Func<TAssertion, Func<string, TContainer, IPromise<TValue>>> fluent, Action<Nest.Analysis, IPromise<TValue>> set)
57+
where TAssertion : IAnalysisAssertion
58+
where TContainer : IPromise<TValue>, new()
59+
where TValue : class => Wrap(an => set(an, Apply<TContainer, TAssertion>((t, a) => fluent(a)(a.Name, t))));
60+
61+
private static IndexSettings Init<TContainer, TAssertion, TInitializer>(Func<TAssertion, TInitializer> value, Action<Nest.Analysis, TContainer> set)
62+
where TAssertion : IAnalysisAssertion
63+
where TContainer : IDictionary<string, TInitializer>, new() => Wrap(an => set(an, Apply<TContainer, TAssertion>((t, a) => t[a.Name] = value(a))));
64+
65+
private static TContainer Apply<TContainer, TAssertion>(Action<TContainer, TAssertion> act)
66+
where TAssertion : IAnalysisAssertion
67+
where TContainer : new() => All<TAssertion>().Aggregate(new TContainer() , (t,a) => { act(t,a); return t; }, t=>t);
68+
69+
private static IndexSettings Wrap(Action<Nest.Analysis> set)
70+
{
71+
var a = new Nest.Analysis();
72+
var s =new IndexSettings { Analysis = a };
73+
set(a);
74+
return s;
75+
}
76+
77+
private static List<TAssertion> All<TAssertion>()
78+
where TAssertion : IAnalysisAssertion
79+
{
80+
var assertions = typeof(TokenizerTests).GetNestedTypes()
81+
.Union(typeof(TokenFilterTests).GetNestedTypes())
82+
.Union(typeof(NormalizerTests).GetNestedTypes())
83+
.Union(typeof(AnalyzerTests).GetNestedTypes())
84+
.Union(typeof(CharFilterTests).GetNestedTypes())
85+
.ToList();
86+
87+
var nestedTypes = assertions
88+
.Where(t => typeof(TAssertion).IsAssignableFrom(t) && t.IsClass)
89+
.ToList();
90+
91+
var types = nestedTypes
92+
.Select(t => new
93+
{
94+
t,
95+
a = t.GetCustomAttributes(typeof(SkipVersionAttribute)).FirstOrDefault() as SkipVersionAttribute
96+
})
97+
.Where(@t1 => @t1.a == null || !@t1.a.Ranges.Any(r => r.IsSatisfied(TestClient.Configuration.ElasticsearchVersion)))
98+
.Select(@t1 => (TAssertion) Activator.CreateInstance(@t1.t));
99+
return types.ToList();
100+
}
101+
102+
103+
}
104+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using Nest;
2+
using Tests.Analysis.Tokenizers;
3+
using Tests.Core.ManagedElasticsearch.Clusters;
4+
using Tests.Framework.Integration;
5+
using static Tests.Framework.Promisify;
6+
7+
namespace Tests.Analysis
8+
{
9+
public class AnalysisWithNormalizerCrudTests : AnalysisCrudTests
10+
{
11+
public AnalysisWithNormalizerCrudTests(WritableCluster cluster, EndpointUsage usage) : base(cluster, usage) { }
12+
13+
protected override CreateIndexRequest CreateInitializer(string indexName) => new CreateIndexRequest(indexName)
14+
{
15+
Settings = new IndexSettings
16+
{
17+
Analysis = new Nest.Analysis
18+
{
19+
Analyzers = AnalysisUsageTests.AnalyzersInitializer.Analysis.Analyzers,
20+
CharFilters = AnalysisUsageTests.CharFiltersInitializer.Analysis.CharFilters,
21+
Tokenizers = AnalysisUsageTests.TokenizersInitializer.Analysis.Tokenizers,
22+
TokenFilters = AnalysisUsageTests.TokenFiltersInitializer.Analysis.TokenFilters,
23+
Normalizers = AnalysisUsageTests.NormalizersInitializer.Analysis.Normalizers,
24+
}
25+
}
26+
};
27+
28+
protected override ICreateIndexRequest CreateFluent(string indexName, CreateIndexDescriptor c) =>
29+
c.Settings(s => s
30+
.Analysis(a => a
31+
.Analyzers(t => Promise(AnalysisUsageTests.AnalyzersFluent.Analysis.Analyzers))
32+
.CharFilters(t => Promise(AnalysisUsageTests.CharFiltersFluent.Analysis.CharFilters))
33+
.Tokenizers(t => Promise(AnalysisUsageTests.TokenizersFluent.Analysis.Tokenizers))
34+
.TokenFilters(t => Promise(AnalysisUsageTests.TokenFiltersFluent.Analysis.TokenFilters))
35+
.Normalizers(t => Promise(AnalysisUsageTests.NormalizersFluent.Analysis.Normalizers))
36+
)
37+
);
38+
}
39+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System.Collections.Generic;
2+
using System.Threading.Tasks;
3+
using Elastic.Xunit.XunitPlumbing;
4+
using Nest;
5+
6+
namespace Tests.Analysis.Analyzers
7+
{
8+
public interface IAnalyzerAssertion : IAnalysisAssertion<IAnalyzer, IAnalyzers, AnalyzersDescriptor> { }
9+
10+
public abstract class AnalyzerAssertionBase<TAssertion>
11+
: AnalysisComponentTestBase<TAssertion, IAnalyzer, IAnalyzers, AnalyzersDescriptor>
12+
, IAnalyzerAssertion
13+
where TAssertion : AnalyzerAssertionBase<TAssertion>, new()
14+
{
15+
protected override IAnalysis FluentAnalysis(AnalysisDescriptor an) =>
16+
an.Analyzers(d => AssertionSetup.Fluent(AssertionSetup.Name, d));
17+
18+
protected override Nest.Analysis InitializerAnalysis() =>
19+
new Nest.Analysis {Analyzers = new Nest.Analyzers {{AssertionSetup.Name, AssertionSetup.Initializer}}};
20+
21+
protected override object AnalysisJson => new
22+
{
23+
analyzer = new Dictionary<string, object> { {AssertionSetup.Name, AssertionSetup.Json} }
24+
};
25+
// https://youtrack.jetbrains.com/issue/RIDER-19912
26+
[U] public override Task TestPutSettingsRequest() => base.TestPutSettingsRequest();
27+
[I] public override Task TestPutSettingsResponse() => base.TestPutSettingsResponse();
28+
}
29+
}

0 commit comments

Comments
 (0)