/* | |
* Copyright 2014 gitblit.com. | |
* | |
* 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.gitblit.tests; | |
import java.io.File; | |
import java.util.Date; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import org.apache.commons.io.FileUtils; | |
import org.bouncycastle.util.Arrays; | |
import org.junit.After; | |
import org.junit.Before; | |
import org.junit.Test; | |
import com.gitblit.IStoredSettings; | |
import com.gitblit.Keys; | |
import com.gitblit.models.Mailing; | |
import com.gitblit.models.RepositoryModel; | |
import com.gitblit.models.TicketModel; | |
import com.gitblit.models.TicketModel.Attachment; | |
import com.gitblit.models.TicketModel.Change; | |
import com.gitblit.models.TicketModel.Field; | |
import com.gitblit.models.TicketModel.Patchset; | |
import com.gitblit.models.TicketModel.Status; | |
import com.gitblit.models.TicketModel.Type; | |
import com.gitblit.tests.mock.MemorySettings; | |
import com.gitblit.tickets.ITicketService; | |
import com.gitblit.tickets.ITicketService.TicketFilter; | |
import com.gitblit.tickets.QueryResult; | |
import com.gitblit.tickets.TicketIndexer.Lucene; | |
import com.gitblit.tickets.TicketLabel; | |
import com.gitblit.tickets.TicketMilestone; | |
import com.gitblit.tickets.TicketNotifier; | |
import com.gitblit.utils.JGitUtils; | |
/** | |
* Tests the mechanics of Gitblit ticket management. | |
* | |
* @author James Moger | |
* | |
*/ | |
public abstract class TicketServiceTest extends GitblitUnitTest { | |
private ITicketService service; | |
protected abstract RepositoryModel getRepository(); | |
protected abstract ITicketService getService(boolean deleteAll) throws Exception; | |
protected IStoredSettings getSettings(boolean deleteAll) throws Exception { | |
File dir = new File(GitBlitSuite.REPOSITORIES, getRepository().name); | |
if (deleteAll) { | |
FileUtils.deleteDirectory(dir); | |
JGitUtils.createRepository(GitBlitSuite.REPOSITORIES, getRepository().name).close(); | |
} | |
File luceneDir = new File(dir, "tickets/lucene"); | |
luceneDir.mkdirs(); | |
Map<String, Object> map = new HashMap<String, Object>(); | |
map.put(Keys.git.repositoriesFolder, GitBlitSuite.REPOSITORIES.getAbsolutePath()); | |
map.put(Keys.tickets.indexFolder, luceneDir.getAbsolutePath()); | |
IStoredSettings settings = new MemorySettings(map); | |
return settings; | |
} | |
@Before | |
public void setup() throws Exception { | |
service = getService(true); | |
} | |
@After | |
public void cleanup() { | |
service.stop(); | |
} | |
@Test | |
public void testLifecycle() throws Exception { | |
// query non-existent ticket | |
TicketModel nonExistent = service.getTicket(getRepository(), 0); | |
assertNull(nonExistent); | |
// create and insert a ticket | |
Change c1 = newChange("testCreation() " + Long.toHexString(System.currentTimeMillis())); | |
TicketModel ticket = service.createTicket(getRepository(), c1); | |
assertTrue(ticket.number > 0); | |
// retrieve ticket and compare | |
TicketModel constructed = service.getTicket(getRepository(), ticket.number); | |
compare(ticket, constructed); | |
assertEquals(1, constructed.changes.size()); | |
// C1: create the ticket | |
int changeCount = 0; | |
c1 = newChange("testUpdates() " + Long.toHexString(System.currentTimeMillis())); | |
ticket = service.createTicket(getRepository(), c1); | |
assertTrue(ticket.number > 0); | |
changeCount++; | |
constructed = service.getTicket(getRepository(), ticket.number); | |
compare(ticket, constructed); | |
assertEquals(1, constructed.changes.size()); | |
// C2: set owner | |
Change c2 = new Change("C2"); | |
c2.comment("I'll fix this"); | |
c2.setField(Field.responsible, c2.author); | |
constructed = service.updateTicket(getRepository(), ticket.number, c2); | |
assertNotNull(constructed); | |
assertEquals(2, constructed.changes.size()); | |
assertEquals(c2.author, constructed.responsible); | |
changeCount++; | |
// C3: add a note | |
Change c3 = new Change("C3"); | |
c3.comment("yeah, this is working"); | |
constructed = service.updateTicket(getRepository(), ticket.number, c3); | |
assertNotNull(constructed); | |
assertEquals(3, constructed.changes.size()); | |
changeCount++; | |
if (service.supportsAttachments()) { | |
// C4: add attachment | |
Change c4 = new Change("C4"); | |
Attachment a = newAttachment(); | |
c4.addAttachment(a); | |
constructed = service.updateTicket(getRepository(), ticket.number, c4); | |
assertNotNull(constructed); | |
assertTrue(constructed.hasAttachments()); | |
Attachment a1 = service.getAttachment(getRepository(), ticket.number, a.name); | |
assertEquals(a.content.length, a1.content.length); | |
assertTrue(Arrays.areEqual(a.content, a1.content)); | |
changeCount++; | |
} | |
// C5: close the issue | |
Change c5 = new Change("C5"); | |
c5.comment("closing issue"); | |
c5.setField(Field.status, Status.Resolved); | |
constructed = service.updateTicket(getRepository(), ticket.number, c5); | |
assertNotNull(constructed); | |
changeCount++; | |
assertTrue(constructed.isClosed()); | |
assertEquals(changeCount, constructed.changes.size()); | |
List<TicketModel> allTickets = service.getTickets(getRepository()); | |
List<TicketModel> openTickets = service.getTickets(getRepository(), new TicketFilter() { | |
@Override | |
public boolean accept(TicketModel ticket) { | |
return ticket.isOpen(); | |
} | |
}); | |
List<TicketModel> closedTickets = service.getTickets(getRepository(), new TicketFilter() { | |
@Override | |
public boolean accept(TicketModel ticket) { | |
return ticket.isClosed(); | |
} | |
}); | |
assertTrue(allTickets.size() > 0); | |
assertEquals(1, openTickets.size()); | |
assertEquals(1, closedTickets.size()); | |
// build a new Lucene index | |
service.reindex(getRepository()); | |
List<QueryResult> hits = service.searchFor(getRepository(), "working", 1, 10); | |
assertEquals(1, hits.size()); | |
// reindex a ticket | |
ticket = allTickets.get(0); | |
Change change = new Change("reindex"); | |
change.comment("this is a test of reindexing a ticket"); | |
service.updateTicket(getRepository(), ticket.number, change); | |
ticket = service.getTicket(getRepository(), ticket.number); | |
hits = service.searchFor(getRepository(), "reindexing", 1, 10); | |
assertEquals(1, hits.size()); | |
service.stop(); | |
service = getService(false); | |
// Lucene field query | |
List<QueryResult> results = service.queryFor(Lucene.status.matches(Status.New.name()), 1, 10, Lucene.created.name(), true); | |
assertEquals(1, results.size()); | |
assertTrue(results.get(0).title.startsWith("testCreation")); | |
// Lucene field query | |
results = service.queryFor(Lucene.status.matches(Status.Resolved.name()), 1, 10, Lucene.created.name(), true); | |
assertEquals(1, results.size()); | |
assertTrue(results.get(0).title.startsWith("testUpdates")); | |
// check the ids | |
assertEquals("[1, 2]", service.getIds(getRepository()).toString()); | |
// delete all tickets | |
for (TicketModel aTicket : allTickets) { | |
assertTrue(service.deleteTicket(getRepository(), aTicket.number, "D")); | |
} | |
} | |
@Test | |
public void testChangeComment() throws Exception { | |
// C1: create the ticket | |
Change c1 = newChange("testChangeComment() " + Long.toHexString(System.currentTimeMillis())); | |
TicketModel ticket = service.createTicket(getRepository(), c1); | |
assertTrue(ticket.number > 0); | |
assertTrue(ticket.changes.get(0).hasComment()); | |
ticket = service.updateComment(ticket, c1.comment.id, "E1", "I changed the comment"); | |
assertNotNull(ticket); | |
assertTrue(ticket.changes.get(0).hasComment()); | |
assertEquals("I changed the comment", ticket.changes.get(0).comment.text); | |
assertTrue(service.deleteTicket(getRepository(), ticket.number, "D")); | |
} | |
@Test | |
public void testDeleteComment() throws Exception { | |
// C1: create the ticket | |
Change c1 = newChange("testDeleteComment() " + Long.toHexString(System.currentTimeMillis())); | |
TicketModel ticket = service.createTicket(getRepository(), c1); | |
assertTrue(ticket.number > 0); | |
assertTrue(ticket.changes.get(0).hasComment()); | |
ticket = service.deleteComment(ticket, c1.comment.id, "D1"); | |
assertNotNull(ticket); | |
assertEquals(1, ticket.changes.size()); | |
assertFalse(ticket.changes.get(0).hasComment()); | |
assertTrue(service.deleteTicket(getRepository(), ticket.number, "D")); | |
} | |
@Test | |
public void testMilestones() throws Exception { | |
service.createMilestone(getRepository(), "M1", "james"); | |
service.createMilestone(getRepository(), "M2", "frank"); | |
service.createMilestone(getRepository(), "M3", "joe"); | |
List<TicketMilestone> milestones = service.getMilestones(getRepository(), Status.Open); | |
assertEquals("Unexpected open milestones count", 3, milestones.size()); | |
for (TicketMilestone milestone : milestones) { | |
milestone.status = Status.Resolved; | |
milestone.due = new Date(); | |
assertTrue("failed to update milestone " + milestone.name, service.updateMilestone(getRepository(), milestone, "ted")); | |
} | |
milestones = service.getMilestones(getRepository(), Status.Open); | |
assertEquals("Unexpected open milestones count", 0, milestones.size()); | |
milestones = service.getMilestones(getRepository(), Status.Resolved); | |
assertEquals("Unexpected resolved milestones count", 3, milestones.size()); | |
for (TicketMilestone milestone : milestones) { | |
assertTrue("failed to delete milestone " + milestone.name, service.deleteMilestone(getRepository(), milestone.name, "lucifer")); | |
} | |
} | |
@Test | |
public void testLabels() throws Exception { | |
service.createLabel(getRepository(), "L1", "james"); | |
service.createLabel(getRepository(), "L2", "frank"); | |
service.createLabel(getRepository(), "L3", "joe"); | |
List<TicketLabel> labels = service.getLabels(getRepository()); | |
assertEquals("Unexpected open labels count", 3, labels.size()); | |
for (TicketLabel label : labels) { | |
label.color = "#ffff00"; | |
assertTrue("failed to update label " + label.name, service.updateLabel(getRepository(), label, "ted")); | |
} | |
labels = service.getLabels(getRepository()); | |
assertEquals("Unexpected labels count", 3, labels.size()); | |
for (TicketLabel label : labels) { | |
assertTrue("failed to delete label " + label.name, service.deleteLabel(getRepository(), label.name, "lucifer")); | |
} | |
} | |
private Change newChange(String summary) { | |
Change change = new Change("C1"); | |
change.setField(Field.title, summary); | |
change.setField(Field.body, "this is my description"); | |
change.setField(Field.labels, "helpdesk"); | |
change.comment("my comment"); | |
return change; | |
} | |
private Attachment newAttachment() { | |
Attachment attachment = new Attachment("test1.txt"); | |
attachment.content = new byte[] { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, | |
0x4a }; | |
return attachment; | |
} | |
private void compare(TicketModel ticket, TicketModel constructed) { | |
assertEquals(ticket.number, constructed.number); | |
assertEquals(ticket.createdBy, constructed.createdBy); | |
assertEquals(ticket.responsible, constructed.responsible); | |
assertEquals(ticket.title, constructed.title); | |
assertEquals(ticket.body, constructed.body); | |
assertEquals(ticket.created, constructed.created); | |
assertTrue(ticket.hasLabel("helpdesk")); | |
} | |
@Test | |
public void testNotifier() throws Exception { | |
Change kernel = new Change("james"); | |
kernel.setField(Field.title, "Sample ticket"); | |
kernel.setField(Field.body, "this **is** my sample body\n\n- I hope\n- you really\n- *really* like it"); | |
kernel.setField(Field.status, Status.New); | |
kernel.setField(Field.type, Type.Proposal); | |
kernel.comment("this is a sample comment on a kernel change"); | |
Patchset patchset = new Patchset(); | |
patchset.insertions = 100; | |
patchset.deletions = 10; | |
patchset.number = 1; | |
patchset.rev = 25; | |
patchset.tip = "50f57913f816d04a16b7407134de5d8406421f37"; | |
kernel.patchset = patchset; | |
TicketModel ticket = service.createTicket(getRepository(), 0L, kernel); | |
Change merge = new Change("james"); | |
merge.setField(Field.mergeSha, patchset.tip); | |
merge.setField(Field.mergeTo, "master"); | |
merge.setField(Field.status, Status.Merged); | |
ticket = service.updateTicket(getRepository(), ticket.number, merge); | |
ticket.repository = getRepository().name; | |
TicketNotifier notifier = service.createNotifier(); | |
Mailing mailing = notifier.queueMailing(ticket); | |
assertNotNull(mailing); | |
} | |
} |