/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.
*/
package org.apache.zeppelin.notebook.repo;
import static com.google.common.truth.Truth.assertThat;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.zeppelin.conf.ZeppelinConfiguration;
import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
import org.apache.zeppelin.notebook.Note;
import org.apache.zeppelin.notebook.NoteInfo;
import org.apache.zeppelin.notebook.Paragraph;
import org.apache.zeppelin.notebook.repo.NotebookRepo.Revision;
import org.apache.zeppelin.user.AuthenticationInfo;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Joiner;
public class GitNotebookRepoTest {
private static final Logger LOG = LoggerFactory.getLogger(GitNotebookRepoTest.class);
private static final String TEST_NOTE_ID = "2A94M5J1Z";
private static final String TEST_NOTE_ID2 = "2A94M5J2Z";
private File zeppelinDir;
private String notebooksDir;
private ZeppelinConfiguration conf;
private GitNotebookRepo notebookRepo;
@Before
public void setUp() throws Exception {
String zpath = System.getProperty("java.io.tmpdir") + "/ZeppelinTest_" + System.currentTimeMillis();
zeppelinDir = new File(zpath);
zeppelinDir.mkdirs();
new File(zeppelinDir, "conf").mkdirs();
notebooksDir = Joiner.on(File.separator).join(zpath, "notebook");
File notebookDir = new File(notebooksDir);
notebookDir.mkdirs();
String testNoteDir = Joiner.on(File.separator).join(notebooksDir, TEST_NOTE_ID);
String testNoteDir2 = Joiner.on(File.separator).join(notebooksDir, TEST_NOTE_ID2);
FileUtils.copyDirectory(new File(Joiner.on(File.separator).join("src", "test", "resources", TEST_NOTE_ID)),
new File(testNoteDir));
FileUtils.copyDirectory(new File(Joiner.on(File.separator).join("src", "test", "resources", TEST_NOTE_ID2)),
new File(testNoteDir2)
);
System.setProperty(ConfVars.ZEPPELIN_HOME.getVarName(), zeppelinDir.getAbsolutePath());
System.setProperty(ConfVars.ZEPPELIN_NOTEBOOK_DIR.getVarName(), notebookDir.getAbsolutePath());
System.setProperty(ConfVars.ZEPPELIN_NOTEBOOK_STORAGE.getVarName(), "org.apache.zeppelin.notebook.repo.GitNotebookRepo");
conf = ZeppelinConfiguration.create();
}
@After
public void tearDown() throws Exception {
if (!FileUtils.deleteQuietly(zeppelinDir)) {
LOG.error("Failed to delete {} ", zeppelinDir.getName());
}
}
@Test
public void initNonemptyNotebookDir() throws IOException, GitAPIException {
//given - .git does not exit
File dotGit = new File(Joiner.on(File.separator).join(notebooksDir, ".git"));
assertThat(dotGit.exists()).isEqualTo(false);
//when
notebookRepo = new GitNotebookRepo(conf);
//then
Git git = notebookRepo.getGit();
assertThat(git).isNotNull();
assertThat(dotGit.exists()).isEqualTo(true);
assertThat(notebookRepo.list(null)).isNotEmpty();
List<DiffEntry> diff = git.diff().call();
// no commit, diff isn't empty
assertThat(diff).isNotEmpty();
}
@Test
public void showNotebookHistoryEmptyTest() throws GitAPIException, IOException {
//given
notebookRepo = new GitNotebookRepo(conf);
assertThat(notebookRepo.list(null)).isNotEmpty();
//when
List<Revision> testNotebookHistory = notebookRepo.revisionHistory(TEST_NOTE_ID, null);
//then
//no initial commit, empty history
assertThat(testNotebookHistory).isEmpty();
}
@Test
public void showNotebookHistoryMultipleNotesTest() throws IOException {
//initial checks
notebookRepo = new GitNotebookRepo(conf);
assertThat(notebookRepo.list(null)).isNotEmpty();
assertThat(containsNote(notebookRepo.list(null), TEST_NOTE_ID)).isTrue();
assertThat(containsNote(notebookRepo.list(null), TEST_NOTE_ID2)).isTrue();
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID, null)).isEmpty();
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID2, null)).isEmpty();
//add commit to both notes
notebookRepo.checkpoint(TEST_NOTE_ID, "first commit, note1", null);
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID, null).size()).isEqualTo(1);
notebookRepo.checkpoint(TEST_NOTE_ID2, "first commit, note2", null);
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID2, null).size()).isEqualTo(1);
//modify, save and checkpoint first note
Note note = notebookRepo.get(TEST_NOTE_ID, null);
Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
Map<String, Object> config = p.getConfig();
config.put("enabled", true);
p.setConfig(config);
p.setText("%md note1 test text");
notebookRepo.save(note, null);
assertThat(notebookRepo.checkpoint(TEST_NOTE_ID, "second commit, note1", null)).isNotNull();
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID, null).size()).isEqualTo(2);
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID2, null).size()).isEqualTo(1);
assertThat(notebookRepo.checkpoint(TEST_NOTE_ID2, "first commit, note2", null))
.isEqualTo(Revision.EMPTY);
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID2, null).size()).isEqualTo(1);
//modify, save and checkpoint second note
note = notebookRepo.get(TEST_NOTE_ID2, null);
p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
config = p.getConfig();
config.put("enabled", false);
p.setConfig(config);
p.setText("%md note2 test text");
notebookRepo.save(note, null);
assertThat(notebookRepo.checkpoint(TEST_NOTE_ID2, "second commit, note2", null)).isNotNull();
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID, null).size()).isEqualTo(2);
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID2, null).size()).isEqualTo(2);
}
@Test
public void addCheckpointTest() throws IOException {
// initial checks
notebookRepo = new GitNotebookRepo(conf);
assertThat(notebookRepo.list(null)).isNotEmpty();
assertThat(containsNote(notebookRepo.list(null), TEST_NOTE_ID)).isTrue();
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID, null)).isEmpty();
notebookRepo.checkpoint(TEST_NOTE_ID, "first commit", null);
List<Revision> notebookHistoryBefore = notebookRepo.revisionHistory(TEST_NOTE_ID, null);
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID, null)).isNotEmpty();
int initialCount = notebookHistoryBefore.size();
// add changes to note
Note note = notebookRepo.get(TEST_NOTE_ID, null);
Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
Map<String, Object> config = p.getConfig();
config.put("enabled", true);
p.setConfig(config);
p.setText("%md checkpoint test text");
// save and checkpoint note
notebookRepo.save(note, null);
notebookRepo.checkpoint(TEST_NOTE_ID, "second commit", null);
// see if commit is added
List<Revision> notebookHistoryAfter = notebookRepo.revisionHistory(TEST_NOTE_ID, null);
assertThat(notebookHistoryAfter.size()).isEqualTo(initialCount + 1);
}
private boolean containsNote(List<NoteInfo> notes, String noteId) {
for (NoteInfo note: notes) {
if (note.getId().equals(noteId)) {
return true;
}
}
return false;
}
@Test
public void getRevisionTest() throws IOException {
// initial checks
notebookRepo = new GitNotebookRepo(conf);
assertThat(notebookRepo.list(null)).isNotEmpty();
assertThat(containsNote(notebookRepo.list(null), TEST_NOTE_ID)).isTrue();
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID, null)).isEmpty();
// add first checkpoint
Revision revision_1 = notebookRepo.checkpoint(TEST_NOTE_ID, "first commit", null);
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID, null).size()).isEqualTo(1);
int paragraphCount_1 = notebookRepo.get(TEST_NOTE_ID, null).getParagraphs().size();
// add paragraph and save
Note note = notebookRepo.get(TEST_NOTE_ID, null);
Paragraph p1 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
Map<String, Object> config = p1.getConfig();
config.put("enabled", true);
p1.setConfig(config);
p1.setText("checkpoint test text");
notebookRepo.save(note, null);
// second checkpoint
notebookRepo.checkpoint(TEST_NOTE_ID, "second commit", null);
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID, null).size()).isEqualTo(2);
int paragraphCount_2 = notebookRepo.get(TEST_NOTE_ID, null).getParagraphs().size();
assertThat(paragraphCount_2).isEqualTo(paragraphCount_1 + 1);
// get note from revision 1
Note noteRevision_1 = notebookRepo.get(TEST_NOTE_ID, revision_1.id, null);
assertThat(noteRevision_1.getParagraphs().size()).isEqualTo(paragraphCount_1);
// get current note
note = notebookRepo.get(TEST_NOTE_ID, null);
assertThat(note.getParagraphs().size()).isEqualTo(paragraphCount_2);
// add one more paragraph and save
Paragraph p2 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
config.put("enabled", false);
p2.setConfig(config);
p2.setText("get revision when modified note test text");
notebookRepo.save(note, null);
note = notebookRepo.get(TEST_NOTE_ID, null);
int paragraphCount_3 = note.getParagraphs().size();
assertThat(paragraphCount_3).isEqualTo(paragraphCount_2 + 1);
// get revision 1 again
noteRevision_1 = notebookRepo.get(TEST_NOTE_ID, revision_1.id, null);
assertThat(noteRevision_1.getParagraphs().size()).isEqualTo(paragraphCount_1);
// check that note is unchanged
note = notebookRepo.get(TEST_NOTE_ID, null);
assertThat(note.getParagraphs().size()).isEqualTo(paragraphCount_3);
}
@Test
public void getRevisionFailTest() throws IOException {
// initial checks
notebookRepo = new GitNotebookRepo(conf);
assertThat(notebookRepo.list(null)).isNotEmpty();
assertThat(containsNote(notebookRepo.list(null), TEST_NOTE_ID)).isTrue();
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID, null)).isEmpty();
// add first checkpoint
Revision revision_1 = notebookRepo.checkpoint(TEST_NOTE_ID, "first commit", null);
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID, null).size()).isEqualTo(1);
int paragraphCount_1 = notebookRepo.get(TEST_NOTE_ID, null).getParagraphs().size();
// get current note
Note note = notebookRepo.get(TEST_NOTE_ID, null);
assertThat(note.getParagraphs().size()).isEqualTo(paragraphCount_1);
// add one more paragraph and save
Paragraph p1 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
Map<String, Object> config = p1.getConfig();
config.put("enabled", true);
p1.setConfig(config);
p1.setText("get revision when modified note test text");
notebookRepo.save(note, null);
int paragraphCount_2 = note.getParagraphs().size();
// get note from revision 1
Note noteRevision_1 = notebookRepo.get(TEST_NOTE_ID, revision_1.id, null);
assertThat(noteRevision_1.getParagraphs().size()).isEqualTo(paragraphCount_1);
// get current note
note = notebookRepo.get(TEST_NOTE_ID, null);
assertThat(note.getParagraphs().size()).isEqualTo(paragraphCount_2);
// test for absent revision
Revision absentRevision = new Revision("absentId", StringUtils.EMPTY, 0);
note = notebookRepo.get(TEST_NOTE_ID, absentRevision.id, null);
assertThat(note).isNull();
}
@Test
public void setRevisionTest() throws IOException {
//create repo and check that note doesn't contain revisions
notebookRepo = new GitNotebookRepo(conf);
assertThat(notebookRepo.list(null)).isNotEmpty();
assertThat(containsNote(notebookRepo.list(null), TEST_NOTE_ID)).isTrue();
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID, null)).isEmpty();
// get current note
Note note = notebookRepo.get(TEST_NOTE_ID, null);
int paragraphCount_1 = note.getParagraphs().size();
LOG.info("initial paragraph count: {}", paragraphCount_1);
// checkpoint revision1
Revision revision1 = notebookRepo.checkpoint(TEST_NOTE_ID, "set revision: first commit", null);
//TODO(khalid): change to EMPTY after rebase
assertThat(revision1).isNotNull();
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID, null).size()).isEqualTo(1);
// add one more paragraph and save
Paragraph p1 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
Map<String, Object> config = p1.getConfig();
config.put("enabled", true);
p1.setConfig(config);
p1.setText("set revision sample text");
notebookRepo.save(note, null);
int paragraphCount_2 = note.getParagraphs().size();
assertThat(paragraphCount_2).isEqualTo(paragraphCount_1 + 1);
LOG.info("paragraph count after modification: {}", paragraphCount_2);
// checkpoint revision2
Revision revision2 = notebookRepo.checkpoint(TEST_NOTE_ID, "set revision: second commit", null);
//TODO(khalid): change to EMPTY after rebase
assertThat(revision2).isNotNull();
assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID, null).size()).isEqualTo(2);
// set note to revision1
Note returnedNote = notebookRepo.setNoteRevision(note.getId(), revision1.id, null);
assertThat(returnedNote).isNotNull();
assertThat(returnedNote.getParagraphs().size()).isEqualTo(paragraphCount_1);
// check note from repo
Note updatedNote = notebookRepo.get(note.getId(), null);
assertThat(updatedNote).isNotNull();
assertThat(updatedNote.getParagraphs().size()).isEqualTo(paragraphCount_1);
// set back to revision2
returnedNote = notebookRepo.setNoteRevision(note.getId(), revision2.id, null);
assertThat(returnedNote).isNotNull();
assertThat(returnedNote.getParagraphs().size()).isEqualTo(paragraphCount_2);
// check note from repo
updatedNote = notebookRepo.get(note.getId(), null);
assertThat(updatedNote).isNotNull();
assertThat(updatedNote.getParagraphs().size()).isEqualTo(paragraphCount_2);
// try failure case - set to invalid revision
returnedNote = notebookRepo.setNoteRevision(note.getId(), "nonexistent_id", null);
assertThat(returnedNote).isNull();
}
}