Merge branch 'stable-2.14' into stable-2.15

* stable-2.14:
  ElasticReindexIT: Add tests against Elasticsearch version 5
  Elasticsearch: Add tests for queries against version 5
  MatchQueryBuilder: Don't use deprecated "match" query
  Elasticsearch: Add an adapter to support V5
  Add Bazel version check

Change-Id: I72f7ce2f87061f1799abc1a73f57b93a2859b6b6
This commit is contained in:
David Pursehouse
2018-06-05 11:03:10 +09:00
18 changed files with 358 additions and 52 deletions

View File

@@ -4,6 +4,13 @@ load("//tools/bzl:maven_jar.bzl", "maven_jar", "GERRIT", "MAVEN_LOCAL")
load("//lib/codemirror:cm.bzl", "CM_VERSION", "DIFF_MATCH_PATCH_VERSION")
load("//plugins:external_plugin_deps.bzl", "external_plugin_deps")
http_archive(
name = "bazel_skylib",
sha256 = "bbccf674aa441c266df9894182d80de104cabd19be98be002f6d478aaa31574d",
strip_prefix = "bazel-skylib-2169ae1c374aab4a09aa90e65efe1a3aad4e279b",
urls = ["https://github.com/bazelbuild/bazel-skylib/archive/2169ae1c374aab4a09aa90e65efe1a3aad4e279b.tar.gz"],
)
http_archive(
name = "io_bazel_rules_closure",
sha256 = "6691c58a2cd30a86776dd9bb34898b041e37136f2dc7e24cadaeaf599c95c657",
@@ -20,6 +27,10 @@ http_file(
url = "https://raw.githubusercontent.com/google/closure-compiler/775609aad61e14aef289ebec4bfc09ad88877f9e/contrib/externs/polymer-1.0.js",
)
load("@bazel_skylib//:lib.bzl", "versions")
versions.check(minimum_bazel_version = "0.7.0")
load("@io_bazel_rules_closure//closure:defs.bzl", "closure_repositories")
# Prevent redundant loading of dependencies.

View File

@@ -50,7 +50,6 @@ import org.elasticsearch.client.Response;
abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
protected static final String BULK = "_bulk";
protected static final String IGNORE_UNMAPPED = "ignore_unmapped";
protected static final String ORDER = "order";
protected static final String SEARCH = "_search";
@@ -80,8 +79,8 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
private final Schema<V> schema;
private final SitePaths sitePaths;
private final String indexNameRaw;
private final ElasticRestClientProvider client;
protected final ElasticRestClientProvider client;
protected final String indexName;
protected final Gson gson;
protected final ElasticQueryBuilder queryBuilder;
@@ -130,7 +129,8 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
@Override
public void deleteAll() throws IOException {
// Delete the index, if it exists.
Response response = client.get().performRequest("HEAD", indexName);
String endpoint = indexName + client.adapter().indicesExistParam();
Response response = client.get().performRequest("HEAD", endpoint);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK) {
response = client.get().performRequest("DELETE", indexName);
@@ -182,7 +182,7 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
protected JsonArray getSortArray(String idFieldName) {
JsonObject properties = new JsonObject();
properties.addProperty(ORDER, "asc");
properties.addProperty(IGNORE_UNMAPPED, true);
client.adapter().setIgnoreUnmapped(properties);
JsonArray sortArray = new JsonArray();
addNamedElement(idFieldName, properties, sortArray);

View File

@@ -61,8 +61,8 @@ public class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, Accoun
public static class AccountMapping {
MappingProperties accounts;
public AccountMapping(Schema<AccountState> schema) {
this.accounts = ElasticMapping.createMapping(schema);
public AccountMapping(Schema<AccountState> schema, ElasticQueryAdapter adapter) {
this.accounts = ElasticMapping.createMapping(schema, adapter);
}
}
@@ -83,7 +83,7 @@ public class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, Accoun
@Assisted Schema<AccountState> schema) {
super(cfg, sitePaths, schema, client, ACCOUNTS);
this.accountCache = accountCache;
this.mapping = new AccountMapping(schema);
this.mapping = new AccountMapping(schema, client.adapter());
this.schema = schema;
}
@@ -133,7 +133,7 @@ public class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, Accoun
QueryBuilder qb = queryBuilder.toQueryBuilder(p);
fields = IndexUtils.accountFields(opts);
SearchSourceBuilder searchSource =
new SearchSourceBuilder()
new SearchSourceBuilder(client.adapter())
.query(qb)
.from(opts.start())
.size(opts.limit())

View File

@@ -87,8 +87,8 @@ class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData>
MappingProperties openChanges;
MappingProperties closedChanges;
ChangeMapping(Schema<ChangeData> schema) {
MappingProperties mapping = ElasticMapping.createMapping(schema);
ChangeMapping(Schema<ChangeData> schema, ElasticQueryAdapter adapter) {
MappingProperties mapping = ElasticMapping.createMapping(schema, adapter);
this.openChanges = mapping;
this.closedChanges = mapping;
}
@@ -115,7 +115,7 @@ class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData>
this.db = db;
this.changeDataFactory = changeDataFactory;
this.schema = schema;
mapping = new ChangeMapping(schema);
this.mapping = new ChangeMapping(schema, client.adapter());
}
@Override
@@ -189,7 +189,7 @@ class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData>
QueryBuilder qb = queryBuilder.toQueryBuilder(p);
fields = IndexUtils.changeFields(opts);
SearchSourceBuilder searchSource =
new SearchSourceBuilder()
new SearchSourceBuilder(client.adapter())
.query(qb)
.from(opts.start())
.size(opts.limit())
@@ -431,7 +431,7 @@ class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData>
private JsonArray getSortArray() {
JsonObject properties = new JsonObject();
properties.addProperty(ORDER, "desc");
properties.addProperty(IGNORE_UNMAPPED, true);
client.adapter().setIgnoreUnmapped(properties);
JsonArray sortArray = new JsonArray();
addNamedElement(ChangeField.UPDATED.getName(), properties, sortArray);

View File

@@ -60,8 +60,8 @@ public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, I
static class GroupMapping {
MappingProperties groups;
GroupMapping(Schema<InternalGroup> schema) {
this.groups = ElasticMapping.createMapping(schema);
GroupMapping(Schema<InternalGroup> schema, ElasticQueryAdapter adapter) {
this.groups = ElasticMapping.createMapping(schema, adapter);
}
}
@@ -82,7 +82,7 @@ public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, I
@Assisted Schema<InternalGroup> schema) {
super(cfg, sitePaths, schema, client, GROUPS);
this.groupCache = groupCache;
this.mapping = new GroupMapping(schema);
this.mapping = new GroupMapping(schema, client.adapter());
this.schema = schema;
}
@@ -132,7 +132,7 @@ public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, I
QueryBuilder qb = queryBuilder.toQueryBuilder(p);
fields = IndexUtils.groupFields(opts);
SearchSourceBuilder searchSource =
new SearchSourceBuilder()
new SearchSourceBuilder(client.adapter())
.query(qb)
.from(opts.start())
.size(opts.limit())

View File

@@ -21,13 +21,15 @@ import com.google.gerrit.index.Schema;
import java.util.Map;
class ElasticMapping {
static MappingProperties createMapping(Schema<?> schema) {
ElasticMapping.Builder mapping = new ElasticMapping.Builder();
static MappingProperties createMapping(Schema<?> schema, ElasticQueryAdapter adapter) {
ElasticMapping.Builder mapping = new ElasticMapping.Builder(adapter);
for (FieldDef<?, ?> field : schema.getFields().values()) {
String name = field.getName();
FieldType<?> fieldType = field.getType();
if (fieldType == FieldType.EXACT || fieldType == FieldType.KEYWORD) {
if (fieldType == FieldType.EXACT) {
mapping.addExactField(name);
} else if (fieldType == FieldType.KEYWORD) {
mapping.addKeywordField(name);
} else if (fieldType == FieldType.TIMESTAMP) {
mapping.addTimestamp(name);
} else if (fieldType == FieldType.INTEGER
@@ -46,9 +48,14 @@ class ElasticMapping {
}
static class Builder {
private final ElasticQueryAdapter adapter;
private final ImmutableMap.Builder<String, FieldProperties> fields =
new ImmutableMap.Builder<>();
Builder(ElasticQueryAdapter adapter) {
this.adapter = adapter;
}
MappingProperties build() {
MappingProperties properties = new MappingProperties();
properties.properties = fields.build();
@@ -56,9 +63,20 @@ class ElasticMapping {
}
Builder addExactField(String name) {
FieldProperties key = new FieldProperties("string");
key.index = "not_analyzed";
FieldProperties properties = new FieldProperties("string");
FieldProperties key = new FieldProperties(adapter.keywordFieldType());
key.index = adapter.indexProperty();
FieldProperties properties;
properties = new FieldProperties(adapter.stringFieldType());
properties.fields = ImmutableMap.of("key", key);
fields.put(name, properties);
return this;
}
Builder addKeywordField(String name) {
FieldProperties key = new FieldProperties(adapter.keywordFieldType());
key.index = adapter.indexProperty();
FieldProperties properties;
properties = new FieldProperties(adapter.keywordFieldType());
properties.fields = ImmutableMap.of("key", key);
fields.put(name, properties);
return this;
@@ -78,7 +96,7 @@ class ElasticMapping {
}
Builder addString(String name) {
fields.put(name, new FieldProperties("string"));
fields.put(name, new FieldProperties(adapter.stringFieldType()));
return this;
}

View File

@@ -0,0 +1,74 @@
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed 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.
package com.google.gerrit.elasticsearch;
import com.google.gson.JsonObject;
public class ElasticQueryAdapter {
private final boolean ignoreUnmapped;
private final String searchFilteringName;
private final String indicesExistParam;
private final String keywordFieldType;
private final String stringFieldType;
private final String indexProperty;
ElasticQueryAdapter(ElasticVersion version) {
this.ignoreUnmapped = version == ElasticVersion.V2_4;
switch (version) {
case V5_6:
case V6_2:
this.searchFilteringName = "_source";
this.indicesExistParam = "?allow_no_indices=false";
this.keywordFieldType = "keyword";
this.stringFieldType = "text";
this.indexProperty = "true";
break;
case V2_4:
default:
this.searchFilteringName = "fields";
this.indicesExistParam = "";
this.keywordFieldType = "string";
this.stringFieldType = "string";
this.indexProperty = "not_analyzed";
break;
}
}
void setIgnoreUnmapped(JsonObject properties) {
if (ignoreUnmapped) {
properties.addProperty("ignore_unmapped", true);
}
}
public String searchFilteringName() {
return searchFilteringName;
}
String indicesExistParam() {
return indicesExistParam;
}
String keywordFieldType() {
return keywordFieldType;
}
String stringFieldType() {
return stringFieldType;
}
String indexProperty() {
return indexProperty;
}
}

View File

@@ -44,6 +44,7 @@ class ElasticRestClientProvider implements Provider<RestClient>, LifecycleListen
private final String password;
private RestClient client;
private ElasticQueryAdapter adapter;
@Inject
ElasticRestClientProvider(ElasticConfiguration cfg) {
@@ -69,6 +70,7 @@ class ElasticRestClientProvider implements Provider<RestClient>, LifecycleListen
client = build();
ElasticVersion version = getVersion();
log.info("Elasticsearch integration version {}", version);
adapter = new ElasticQueryAdapter(version);
}
}
}
@@ -89,6 +91,11 @@ class ElasticRestClientProvider implements Provider<RestClient>, LifecycleListen
}
}
ElasticQueryAdapter adapter() {
get(); // Make sure we're connected
return adapter;
}
public static class FailedToGetVersion extends ElasticException {
private static final long serialVersionUID = 1L;
private static final String MESSAGE = "Failed to get Elasticsearch version";

View File

@@ -27,9 +27,14 @@ class MatchQueryBuilder extends QueryBuilder {
enum Type {
/** The text is analyzed and used as a phrase query. */
PHRASE,
MATCH_PHRASE,
/** The text is analyzed and used in a phrase query, with the last term acting as a prefix. */
PHRASE_PREFIX
MATCH_PHRASE_PREFIX;
@Override
public String toString() {
return name().toLowerCase(Locale.US);
}
}
private final String name;
@@ -52,14 +57,6 @@ class MatchQueryBuilder extends QueryBuilder {
@Override
protected void doXContent(XContentBuilder builder) throws IOException {
builder.startObject("match");
builder.startObject(name);
builder.field("query", text);
if (type != null) {
builder.field("type", type.toString().toLowerCase(Locale.ENGLISH));
}
builder.endObject();
builder.endObject();
builder.startObject(type.toString()).field(name, text).endObject();
}
}

View File

@@ -33,7 +33,7 @@ public abstract class QueryBuilders {
* @param text The query text (to be analyzed).
*/
public static MatchQueryBuilder matchPhraseQuery(String name, Object text) {
return new MatchQueryBuilder(name, text).type(MatchQueryBuilder.Type.PHRASE);
return new MatchQueryBuilder(name, text).type(MatchQueryBuilder.Type.MATCH_PHRASE);
}
/**
@@ -43,7 +43,7 @@ public abstract class QueryBuilders {
* @param text The query text (to be analyzed).
*/
public static MatchQueryBuilder matchPhrasePrefixQuery(String name, Object text) {
return new MatchQueryBuilder(name, text).type(MatchQueryBuilder.Type.PHRASE_PREFIX);
return new MatchQueryBuilder(name, text).type(MatchQueryBuilder.Type.MATCH_PHRASE_PREFIX);
}
/**

View File

@@ -14,6 +14,7 @@
package com.google.gerrit.elasticsearch.builders;
import com.google.gerrit.elasticsearch.ElasticQueryAdapter;
import java.io.IOException;
import java.util.List;
@@ -23,6 +24,7 @@ import java.util.List;
* <p>A trimmed down and modified version of org.elasticsearch.search.builder.SearchSourceBuilder.
*/
public class SearchSourceBuilder {
private final ElasticQueryAdapter adapter;
private QuerySourceBuilder querySourceBuilder;
@@ -33,7 +35,9 @@ public class SearchSourceBuilder {
private List<String> fieldNames;
/** Constructs a new search source builder. */
public SearchSourceBuilder() {}
public SearchSourceBuilder(ElasticQueryAdapter adapter) {
this.adapter = adapter;
}
/** Constructs a new search source builder with a search query. */
public SearchSourceBuilder query(QueryBuilder query) {
@@ -95,9 +99,9 @@ public class SearchSourceBuilder {
if (fieldNames != null) {
if (fieldNames.size() == 1) {
builder.field("fields", fieldNames.get(0));
builder.field(adapter.searchFilteringName(), fieldNames.get(0));
} else {
builder.startArray("fields");
builder.startArray(adapter.searchFilteringName());
for (String fieldName : fieldNames) {
builder.value(fieldName);
}

View File

@@ -15,7 +15,6 @@
package com.google.gerrit.elasticsearch;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.elasticsearch.ElasticVersion;
import java.util.Set;
import org.apache.http.HttpHost;
import org.junit.internal.AssumptionViolatedException;

View File

@@ -17,14 +17,11 @@ package com.google.gerrit.elasticsearch;
import com.google.gerrit.elasticsearch.ElasticTestUtils.ElasticNodeInfo;
import com.google.gerrit.server.query.change.AbstractQueryChangesTest;
import com.google.gerrit.testutil.InMemoryModule;
import com.google.gerrit.testutil.InMemoryRepositoryManager.Repo;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class ElasticQueryChangesTest extends AbstractQueryChangesTest {
private static ElasticNodeInfo nodeInfo;
@@ -66,12 +63,4 @@ public class ElasticQueryChangesTest extends AbstractQueryChangesTest {
ElasticTestUtils.configure(elasticsearchConfig, nodeInfo.port, indicesPrefix);
return Guice.createInjector(new InMemoryModule(elasticsearchConfig, notesMigration));
}
@Test
public void byOwnerInvalidQuery() throws Exception {
TestRepository<Repo> repo = createProject("repo");
insert(repo, newChange(repo), userId);
String nameEmail = user.asIdentifiedUser().getNameEmail();
assertQuery("owner: \"" + nameEmail + "\"\\");
}
}

View File

@@ -0,0 +1,66 @@
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed 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.
package com.google.gerrit.elasticsearch;
import com.google.gerrit.elasticsearch.ElasticTestUtils.ElasticNodeInfo;
import com.google.gerrit.server.query.account.AbstractQueryAccountsTest;
import com.google.gerrit.testutil.InMemoryModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.eclipse.jgit.lib.Config;
import org.junit.AfterClass;
import org.junit.BeforeClass;
public class ElasticV5QueryAccountsTest extends AbstractQueryAccountsTest {
private static ElasticNodeInfo nodeInfo;
private static ElasticContainer<?> container;
@BeforeClass
public static void startIndexService() {
if (nodeInfo != null) {
// do not start Elasticsearch twice
return;
}
container = ElasticContainer.createAndStart(ElasticVersion.V5_6);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
}
@AfterClass
public static void stopElasticsearchServer() {
if (container != null) {
container.stop();
}
}
private String testName() {
return testName.getMethodName().toLowerCase() + "_";
}
@Override
protected void initAfterLifecycleStart() throws Exception {
super.initAfterLifecycleStart();
ElasticTestUtils.createAllIndexes(injector);
}
@Override
protected Injector createInjector() {
Config elasticsearchConfig = new Config(config);
InMemoryModule.setDefaults(elasticsearchConfig);
String indicesPrefix = testName();
ElasticTestUtils.configure(elasticsearchConfig, nodeInfo.port, indicesPrefix);
return Guice.createInjector(new InMemoryModule(elasticsearchConfig, notesMigration));
}
}

View File

@@ -0,0 +1,66 @@
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed 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.
package com.google.gerrit.elasticsearch;
import com.google.gerrit.elasticsearch.ElasticTestUtils.ElasticNodeInfo;
import com.google.gerrit.server.query.change.AbstractQueryChangesTest;
import com.google.gerrit.testutil.InMemoryModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.eclipse.jgit.lib.Config;
import org.junit.AfterClass;
import org.junit.BeforeClass;
public class ElasticV5QueryChangesTest extends AbstractQueryChangesTest {
private static ElasticNodeInfo nodeInfo;
private static ElasticContainer<?> container;
@BeforeClass
public static void startIndexService() {
if (nodeInfo != null) {
// do not start Elasticsearch twice
return;
}
container = ElasticContainer.createAndStart(ElasticVersion.V5_6);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
}
@AfterClass
public static void stopElasticsearchServer() {
if (container != null) {
container.stop();
}
}
private String testName() {
return testName.getMethodName().toLowerCase() + "_";
}
@Override
protected void initAfterLifecycleStart() throws Exception {
super.initAfterLifecycleStart();
ElasticTestUtils.createAllIndexes(injector);
}
@Override
protected Injector createInjector() {
Config elasticsearchConfig = new Config(config);
InMemoryModule.setDefaults(elasticsearchConfig);
String indicesPrefix = testName();
ElasticTestUtils.configure(elasticsearchConfig, nodeInfo.port, indicesPrefix);
return Guice.createInjector(new InMemoryModule(elasticsearchConfig, notesMigration));
}
}

View File

@@ -0,0 +1,66 @@
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed 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.
package com.google.gerrit.elasticsearch;
import com.google.gerrit.elasticsearch.ElasticTestUtils.ElasticNodeInfo;
import com.google.gerrit.server.query.group.AbstractQueryGroupsTest;
import com.google.gerrit.testutil.InMemoryModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.eclipse.jgit.lib.Config;
import org.junit.AfterClass;
import org.junit.BeforeClass;
public class ElasticV5QueryGroupsTest extends AbstractQueryGroupsTest {
private static ElasticNodeInfo nodeInfo;
private static ElasticContainer<?> container;
@BeforeClass
public static void startIndexService() {
if (nodeInfo != null) {
// do not start Elasticsearch twice
return;
}
container = ElasticContainer.createAndStart(ElasticVersion.V5_6);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
}
@AfterClass
public static void stopElasticsearchServer() {
if (container != null) {
container.stop();
}
}
private String testName() {
return testName.getMethodName().toLowerCase() + "_";
}
@Override
protected void initAfterLifecycleStart() throws Exception {
super.initAfterLifecycleStart();
ElasticTestUtils.createAllIndexes(injector);
}
@Override
protected Injector createInjector() {
Config elasticsearchConfig = new Config(config);
InMemoryModule.setDefaults(elasticsearchConfig);
String indicesPrefix = testName();
ElasticTestUtils.configure(elasticsearchConfig, nodeInfo.port, indicesPrefix);
return Guice.createInjector(new InMemoryModule(elasticsearchConfig, notesMigration));
}
}

View File

@@ -2593,6 +2593,14 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
assertQuery("query:query4");
}
@Test
public void byOwnerInvalidQuery() throws Exception {
TestRepository<Repo> repo = createProject("repo");
insert(repo, newChange(repo), userId);
String nameEmail = user.asIdentifiedUser().getNameEmail();
assertQuery("owner: \"" + nameEmail + "\"\\");
}
protected ChangeInserter newChange(TestRepository<Repo> repo) throws Exception {
return newChange(repo, null, null, null, null, false);
}

View File

@@ -64,6 +64,7 @@ public class LuceneQueryChangesTest extends AbstractQueryChangesTest {
}
@Test
@Override
public void byOwnerInvalidQuery() throws Exception {
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChange(repo), userId);