313 lines
10 KiB
Java
313 lines
10 KiB
Java
// 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.server.schema;
|
|
|
|
import static com.google.common.truth.Truth.assertThat;
|
|
import static com.google.common.truth.Truth.assert_;
|
|
import static com.google.common.truth.Truth8.assertThat;
|
|
import static com.google.gerrit.server.schema.NoteDbSchemaUpdater.requiredUpgrades;
|
|
|
|
import com.google.common.collect.ImmutableList;
|
|
import com.google.common.collect.ImmutableSortedMap;
|
|
import com.google.common.collect.ImmutableSortedSet;
|
|
import com.google.gerrit.reviewdb.client.RefNames;
|
|
import com.google.gerrit.server.Sequences;
|
|
import com.google.gerrit.server.config.AllProjectsName;
|
|
import com.google.gerrit.server.config.AllUsersName;
|
|
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
|
import com.google.gerrit.server.notedb.IntBlob;
|
|
import com.google.gerrit.server.notedb.NoteDbSchemaVersionManager;
|
|
import com.google.gerrit.server.notedb.RepoSequence;
|
|
import com.google.gerrit.testing.GerritBaseTests;
|
|
import com.google.gerrit.testing.InMemoryRepositoryManager;
|
|
import com.google.gerrit.testing.TestUpdateUI;
|
|
import com.google.gwtorm.server.OrmException;
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Optional;
|
|
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
|
import org.eclipse.jgit.junit.TestRepository;
|
|
import org.eclipse.jgit.lib.Config;
|
|
import org.eclipse.jgit.lib.Repository;
|
|
import org.junit.Test;
|
|
|
|
public class NoteDbSchemaUpdaterTest extends GerritBaseTests {
|
|
@Test
|
|
public void requiredUpgradesFromNoVersion() throws Exception {
|
|
assertThat(requiredUpgrades(0, versions(10))).containsExactly(10).inOrder();
|
|
assertThat(requiredUpgrades(0, versions(10, 11, 12))).containsExactly(10, 11, 12).inOrder();
|
|
}
|
|
|
|
@Test
|
|
public void requiredUpgradesFromExistingVersion() throws Exception {
|
|
ImmutableSortedSet<Integer> versions = versions(10, 11, 12, 13);
|
|
assertThat(requiredUpgrades(10, versions)).containsExactly(11, 12, 13).inOrder();
|
|
assertThat(requiredUpgrades(11, versions)).containsExactly(12, 13).inOrder();
|
|
assertThat(requiredUpgrades(12, versions)).containsExactly(13).inOrder();
|
|
assertThat(requiredUpgrades(13, versions)).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
public void downgradeNotSupported() throws Exception {
|
|
try {
|
|
requiredUpgrades(14, versions(10, 11, 12, 13));
|
|
assert_().fail("expected OrmException");
|
|
} catch (OrmException e) {
|
|
assertThat(e)
|
|
.hasMessageThat()
|
|
.contains("Cannot downgrade NoteDb schema from version 14 to 13");
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void skipToFirstVersionNotSupported() throws Exception {
|
|
ImmutableSortedSet<Integer> versions = versions(10, 11, 12);
|
|
assertThat(requiredUpgrades(9, versions)).containsExactly(10, 11, 12).inOrder();
|
|
try {
|
|
requiredUpgrades(8, versions);
|
|
assert_().fail("expected OrmException");
|
|
} catch (OrmException e) {
|
|
assertThat(e).hasMessageThat().contains("Cannot skip NoteDb schema from version 8 to 10");
|
|
}
|
|
}
|
|
|
|
private static class TestUpdate {
|
|
protected final Config cfg;
|
|
protected final AllProjectsName allProjectsName;
|
|
protected final AllUsersName allUsersName;
|
|
protected final NoteDbSchemaUpdater updater;
|
|
protected final GitRepositoryManager repoManager;
|
|
protected final NoteDbSchemaVersion.Arguments args;
|
|
private final List<String> messages;
|
|
|
|
TestUpdate(Optional<Integer> initialVersion) {
|
|
cfg = new Config();
|
|
allProjectsName = new AllProjectsName("The-Projects");
|
|
allUsersName = new AllUsersName("The-Users");
|
|
repoManager = new InMemoryRepositoryManager();
|
|
|
|
args = new NoteDbSchemaVersion.Arguments(repoManager, allProjectsName);
|
|
NoteDbSchemaVersionManager versionManager =
|
|
new NoteDbSchemaVersionManager(allProjectsName, repoManager);
|
|
updater =
|
|
new NoteDbSchemaUpdater(
|
|
cfg,
|
|
allUsersName,
|
|
repoManager,
|
|
new TestSchemaCreator(initialVersion),
|
|
versionManager,
|
|
args,
|
|
ImmutableSortedMap.of(10, TestSchema_10.class, 11, TestSchema_11.class));
|
|
messages = new ArrayList<>();
|
|
}
|
|
|
|
private class TestSchemaCreator implements SchemaCreator {
|
|
private final Optional<Integer> initialVersion;
|
|
|
|
TestSchemaCreator(Optional<Integer> initialVersion) {
|
|
this.initialVersion = initialVersion;
|
|
}
|
|
|
|
@Override
|
|
public void create() throws OrmException, IOException {
|
|
try (Repository repo = repoManager.createRepository(allProjectsName)) {
|
|
if (initialVersion.isPresent()) {
|
|
TestRepository<?> tr = new TestRepository<>(repo);
|
|
tr.update(RefNames.REFS_VERSION, tr.blob(initialVersion.get().toString()));
|
|
}
|
|
} catch (Exception e) {
|
|
throw new OrmException(e);
|
|
}
|
|
repoManager.createRepository(allUsersName).close();
|
|
setUp();
|
|
}
|
|
|
|
@Override
|
|
public void ensureCreated() throws OrmException, IOException {
|
|
try {
|
|
repoManager.openRepository(allProjectsName).close();
|
|
} catch (RepositoryNotFoundException e) {
|
|
create();
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void setNotesMigrationConfig() {
|
|
cfg.setString("noteDb", "changes", "write", "true");
|
|
cfg.setString("noteDb", "changes", "read", "true");
|
|
cfg.setString("noteDb", "changes", "primaryStorage", "NOTE_DB");
|
|
cfg.setString("noteDb", "changes", "disableReviewDb", "true");
|
|
}
|
|
|
|
protected void seedGroupSequenceRef() throws OrmException {
|
|
new RepoSequence(
|
|
repoManager,
|
|
GitReferenceUpdated.DISABLED,
|
|
allUsersName,
|
|
Sequences.NAME_GROUPS,
|
|
() -> 1,
|
|
1)
|
|
.next();
|
|
}
|
|
|
|
/**
|
|
* Test-specific setup.
|
|
*
|
|
* @throws OrmException if an error occurs.
|
|
*/
|
|
protected void setUp() throws OrmException {}
|
|
|
|
ImmutableList<String> update() throws Exception {
|
|
updater.update(
|
|
new TestUpdateUI() {
|
|
@Override
|
|
public void message(String m) {
|
|
messages.add(m);
|
|
}
|
|
});
|
|
return getMessages();
|
|
}
|
|
|
|
ImmutableList<String> getMessages() {
|
|
return ImmutableList.copyOf(messages);
|
|
}
|
|
|
|
Optional<Integer> readVersion() throws Exception {
|
|
try (Repository repo = repoManager.openRepository(allProjectsName)) {
|
|
return IntBlob.parse(repo, RefNames.REFS_VERSION).map(IntBlob::value);
|
|
}
|
|
}
|
|
|
|
private static class TestSchema_10 implements NoteDbSchemaVersion {
|
|
@SuppressWarnings("unused")
|
|
TestSchema_10(Arguments args) {
|
|
// Do nothing.
|
|
}
|
|
|
|
@Override
|
|
public void upgrade(UpdateUI ui) {
|
|
ui.message("body of 10");
|
|
}
|
|
}
|
|
|
|
private static class TestSchema_11 implements NoteDbSchemaVersion {
|
|
@SuppressWarnings("unused")
|
|
TestSchema_11(Arguments args) {
|
|
// Do nothing.
|
|
}
|
|
|
|
@Override
|
|
public void upgrade(UpdateUI ui) {
|
|
ui.message("BODY OF 11");
|
|
}
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void bootstrapUpdateWith216Prerequisites() throws Exception {
|
|
TestUpdate u =
|
|
new TestUpdate(Optional.empty()) {
|
|
@Override
|
|
public void setUp() throws OrmException {
|
|
setNotesMigrationConfig();
|
|
seedGroupSequenceRef();
|
|
}
|
|
};
|
|
assertThat(u.update())
|
|
.containsExactly(
|
|
"Migrating data to schema 10 ...",
|
|
"body of 10",
|
|
"Migrating data to schema 11 ...",
|
|
"BODY OF 11")
|
|
.inOrder();
|
|
assertThat(u.readVersion()).hasValue(11);
|
|
}
|
|
|
|
@Test
|
|
public void bootstrapUpdateFailsWithoutNotesMigrationConfig() throws Exception {
|
|
TestUpdate u =
|
|
new TestUpdate(Optional.empty()) {
|
|
@Override
|
|
public void setUp() throws OrmException {
|
|
seedGroupSequenceRef();
|
|
}
|
|
};
|
|
try {
|
|
u.update();
|
|
assert_().fail("expected OrmException");
|
|
} catch (OrmException e) {
|
|
assertThat(e).hasMessageThat().contains("NoteDb change migration was not completed");
|
|
}
|
|
assertThat(u.getMessages()).isEmpty();
|
|
assertThat(u.readVersion()).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
public void bootstrapUpdateFailsWithoutGroupSequenceRef() throws Exception {
|
|
TestUpdate u =
|
|
new TestUpdate(Optional.empty()) {
|
|
@Override
|
|
public void setUp() {
|
|
setNotesMigrationConfig();
|
|
}
|
|
};
|
|
try {
|
|
u.update();
|
|
assert_().fail("expected OrmException");
|
|
} catch (OrmException e) {
|
|
assertThat(e).hasMessageThat().contains("upgrade to 2.16.x first");
|
|
}
|
|
assertThat(u.getMessages()).isEmpty();
|
|
assertThat(u.readVersion()).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
public void updateTwoVersions() throws Exception {
|
|
TestUpdate u = new TestUpdate(Optional.of(9));
|
|
assertThat(u.update())
|
|
.containsExactly(
|
|
"Migrating data to schema 10 ...",
|
|
"body of 10",
|
|
"Migrating data to schema 11 ...",
|
|
"BODY OF 11")
|
|
.inOrder();
|
|
assertThat(u.readVersion()).hasValue(11);
|
|
}
|
|
|
|
@Test
|
|
public void updateOneVersion() throws Exception {
|
|
TestUpdate u = new TestUpdate(Optional.of(10));
|
|
assertThat(u.update())
|
|
.containsExactly("Migrating data to schema 11 ...", "BODY OF 11")
|
|
.inOrder();
|
|
assertThat(u.readVersion()).hasValue(11);
|
|
}
|
|
|
|
@Test
|
|
public void updateNoOp() throws Exception {
|
|
// This test covers the state when running the updater after initializing a new 3.x site, which
|
|
// seeds the schema version ref with the latest version.
|
|
TestUpdate u = new TestUpdate(Optional.of(11));
|
|
assertThat(u.update()).isEmpty();
|
|
assertThat(u.readVersion()).hasValue(11);
|
|
}
|
|
|
|
private static ImmutableSortedSet<Integer> versions(Integer... versions) {
|
|
return ImmutableSortedSet.copyOf(versions);
|
|
}
|
|
}
|