package com.limegroup.gnutella.filters;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.SimpleTimeZone;
import junit.framework.Test;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.params.HttpParams;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.api.Invocation;
import org.jmock.lib.action.CustomAction;
import org.limewire.core.settings.FilterSettings;
import org.limewire.gnutella.tests.LimeTestCase;
import org.limewire.util.FileUtils;
import org.limewire.util.TestUtils;
import org.limewire.util.Visitor;
import com.google.inject.util.Providers;
import com.limegroup.gnutella.SpamServices;
import com.limegroup.gnutella.http.HttpClientListener;
import com.limegroup.gnutella.http.HttpExecutor;
public class URNBlacklistManagerImplTest extends LimeTestCase {
// This file contains a single random URN and a valid signature
private File validFile =
TestUtils.getResourceFile("com/limegroup/gnutella/resources/urns.good");
// This file contains the same signature, but the URN has been modified
private File invalidFile =
TestUtils.getResourceFile("com/limegroup/gnutella/resources/urns.bad");
private URNBlacklistManagerImpl urnBlacklistManager;
private Mockery context;
private HttpExecutor httpExecutor;
private HttpParams defaultParams;
private SpamServices spamServices;
private StubVisitor visitor;
public URNBlacklistManagerImplTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(URNBlacklistManagerImplTest.class);
}
@Override
public void setUp() {
// This URL won't actually be hit, but we need to supply one
FilterSettings.URN_BLACKLIST_UPDATE_URLS.set(
new String[] {"http://127.0.0.1/"});
context = new Mockery();
httpExecutor = context.mock(HttpExecutor.class);
defaultParams = context.mock(HttpParams.class);
spamServices = context.mock(SpamServices.class);
visitor = new StubVisitor();
urnBlacklistManager = new URNBlacklistManagerImpl(
Providers.of(httpExecutor), Providers.of(defaultParams),
Providers.of(spamServices));
context.assertIsSatisfied();
}
@Override
public void tearDown() {
FilterSettings.URN_BLACKLIST_UPDATE_URLS.revertToDefault();
FilterSettings.LAST_URN_BLACKLIST_UPDATE.revertToDefault();
FilterSettings.NEXT_URN_BLACKLIST_UPDATE.revertToDefault();
urnBlacklistManager.getFile().delete();
}
public void testChecksForUpdatesAtStartupIfNeeded() {
long now = System.currentTimeMillis();
FilterSettings.NEXT_URN_BLACKLIST_UPDATE.set(now - 1000);
context.checking(new Expectations() {{
one(httpExecutor).execute(with(any(HttpHead.class)),
with(any(HttpParams.class)),
with(any(HttpClientListener.class)));
}});
urnBlacklistManager.start();
context.assertIsSatisfied();
}
public void testDoesNotCheckForUpdatesAtStartupIfNotNeeded() {
long now = System.currentTimeMillis();
FilterSettings.NEXT_URN_BLACKLIST_UPDATE.set(now + 1000);
urnBlacklistManager.start();
context.assertIsSatisfied();
}
public void testLoadingURNsTriggersCheckIfFileIsMissing() {
context.checking(new Expectations() {{
one(httpExecutor).execute(with(any(HttpHead.class)),
with(any(HttpParams.class)),
with(any(HttpClientListener.class)));
}});
urnBlacklistManager.loadURNs(visitor);
assertFalse(urnBlacklistManager.getFile().exists());
assertTrue(visitor.urns.isEmpty());
context.assertIsSatisfied();
}
public void testLoadingURNsTriggersCheckIfFileIsEmpty() throws Exception {
// Create an empty file
assertTrue(urnBlacklistManager.getFile().createNewFile());
context.checking(new Expectations() {{
one(httpExecutor).execute(with(any(HttpHead.class)),
with(any(HttpParams.class)),
with(any(HttpClientListener.class)));
}});
urnBlacklistManager.loadURNs(visitor);
assertTrue(urnBlacklistManager.getFile().exists());
assertTrue(visitor.urns.isEmpty());
context.assertIsSatisfied();
}
public void testLoadingURNsTriggersCheckIfFileIsCorrupt() throws Exception {
// Copy all but the last byte, so the file is invalid
File f = urnBlacklistManager.getFile();
FileUtils.copy(validFile, validFile.length() - 1, f);
context.checking(new Expectations() {{
one(httpExecutor).execute(with(any(HttpHead.class)),
with(any(HttpParams.class)),
with(any(HttpClientListener.class)));
}});
urnBlacklistManager.loadURNs(visitor);
assertTrue(urnBlacklistManager.getFile().exists());
assertTrue(visitor.urns.isEmpty());
context.assertIsSatisfied();
}
public void testLoadingURNsTriggersCheckIfSignatureIsBad() throws Exception {
FileUtils.copy(invalidFile, urnBlacklistManager.getFile());
context.checking(new Expectations() {{
one(httpExecutor).execute(with(any(HttpHead.class)),
with(any(HttpParams.class)),
with(any(HttpClientListener.class)));
}});
urnBlacklistManager.loadURNs(visitor);
assertTrue(urnBlacklistManager.getFile().exists());
assertTrue(visitor.urns.isEmpty());
context.assertIsSatisfied();
}
public void testLoadingURNsReturnsDataIfSignatureIsGood() throws Exception {
FileUtils.copy(validFile, urnBlacklistManager.getFile());
urnBlacklistManager.loadURNs(visitor);
assertTrue(urnBlacklistManager.getFile().exists());
assertEquals(1, visitor.urns.size());
context.assertIsSatisfied();
}
public void testEmptyURLSettingSetsNextUpdateTime() throws Exception {
FilterSettings.URN_BLACKLIST_UPDATE_URLS.set(new String[0]);
long now = System.currentTimeMillis();
FilterSettings.NEXT_URN_BLACKLIST_UPDATE.set(now - 1000);
urnBlacklistManager.start();
context.assertIsSatisfied();
assertNextUpdateTimeIsSet(now);
}
public void testFailedHeadRequestSetsNextUpdateTime() {
long now = System.currentTimeMillis();
FilterSettings.NEXT_URN_BLACKLIST_UPDATE.set(now - 1000);
final HttpResponse response = context.mock(HttpResponse.class);
final StatusLine statusLine = context.mock(StatusLine.class);
context.checking(new Expectations() {{
one(httpExecutor).execute(with(any(HttpHead.class)),
with(any(HttpParams.class)),
with(any(HttpClientListener.class)));
will(new FailedRequestAction(response));
allowing(response).getStatusLine();
will(returnValue(statusLine));
}});
urnBlacklistManager.start();
context.assertIsSatisfied();
assertNextUpdateTimeIsSet(now);
}
public void testMissingLastModifiedHeaderSetsNextUpdateTime() {
long now = System.currentTimeMillis();
FilterSettings.NEXT_URN_BLACKLIST_UPDATE.set(now - 1000);
final HttpResponse response = context.mock(HttpResponse.class);
context.checking(new Expectations() {{
one(httpExecutor).execute(with(any(HttpHead.class)),
with(any(HttpParams.class)),
with(any(HttpClientListener.class)));
will(new SuccessfulRequestAction(response));
one(response).getFirstHeader("Last-Modified");
will(returnValue(null));
}});
urnBlacklistManager.start();
context.assertIsSatisfied();
assertNextUpdateTimeIsSet(now);
}
public void testStaleLastModifiedHeaderSetsNextUpdateTime() {
final long now = System.currentTimeMillis();
FilterSettings.NEXT_URN_BLACKLIST_UPDATE.set(now - 1000);
FilterSettings.LAST_URN_BLACKLIST_UPDATE.set(now - 2000);
final HttpResponse response = context.mock(HttpResponse.class);
final Header header = context.mock(Header.class);
context.checking(new Expectations() {{
one(httpExecutor).execute(with(any(HttpHead.class)),
with(any(HttpParams.class)),
with(any(HttpClientListener.class)));
will(new SuccessfulRequestAction(response));
one(response).getFirstHeader("Last-Modified");
will(returnValue(header));
allowing(header).getValue();
will(returnValue(dateString(now - 3000))); // Stale
}});
urnBlacklistManager.start();
context.assertIsSatisfied();
assertNextUpdateTimeIsSet(now);
}
public void testFreshLastModifiedHeaderTriggersGetRequest() {
final long now = System.currentTimeMillis();
FilterSettings.NEXT_URN_BLACKLIST_UPDATE.set(now - 1000);
FilterSettings.LAST_URN_BLACKLIST_UPDATE.set(now - 2000);
final HttpResponse response = context.mock(HttpResponse.class);
final Header header = context.mock(Header.class);
context.checking(new Expectations() {{
one(httpExecutor).execute(with(any(HttpHead.class)),
with(any(HttpParams.class)),
with(any(HttpClientListener.class)));
will(new SuccessfulRequestAction(response));
one(response).getFirstHeader("Last-Modified");
will(returnValue(header));
allowing(header).getValue();
will(returnValue(dateString(now))); // Fresh
one(httpExecutor).execute(with(any(HttpGet.class)),
with(any(HttpParams.class)),
with(any(HttpClientListener.class)));
}});
urnBlacklistManager.start();
context.assertIsSatisfied();
}
public void testFailedGetRequestSetsNextUpdateTime() {
final long now = System.currentTimeMillis();
FilterSettings.NEXT_URN_BLACKLIST_UPDATE.set(now - 1000);
FilterSettings.LAST_URN_BLACKLIST_UPDATE.set(now - 2000);
final HttpResponse response = context.mock(HttpResponse.class);
final Header header = context.mock(Header.class);
final StatusLine statusLine = context.mock(StatusLine.class);
context.checking(new Expectations() {{
one(httpExecutor).execute(with(any(HttpHead.class)),
with(any(HttpParams.class)),
with(any(HttpClientListener.class)));
will(new SuccessfulRequestAction(response));
one(response).getFirstHeader("Last-Modified");
will(returnValue(header));
allowing(header).getValue();
will(returnValue(dateString(now))); // Fresh
one(httpExecutor).execute(with(any(HttpGet.class)),
with(any(HttpParams.class)),
with(any(HttpClientListener.class)));
will(new FailedRequestAction(response));
allowing(response).getStatusLine();
will(returnValue(statusLine));
}});
urnBlacklistManager.start();
context.assertIsSatisfied();
assertNextUpdateTimeIsSet(now);
}
public void testMissingBodySetsNextUpdateTime() {
final long now = System.currentTimeMillis();
FilterSettings.NEXT_URN_BLACKLIST_UPDATE.set(now - 1000);
FilterSettings.LAST_URN_BLACKLIST_UPDATE.set(now - 2000);
final HttpResponse response = context.mock(HttpResponse.class);
final Header header = context.mock(Header.class);
context.checking(new Expectations() {{
one(httpExecutor).execute(with(any(HttpHead.class)),
with(any(HttpParams.class)),
with(any(HttpClientListener.class)));
will(new SuccessfulRequestAction(response));
one(response).getFirstHeader("Last-Modified");
will(returnValue(header));
allowing(header).getValue();
will(returnValue(dateString(now))); // Fresh
one(httpExecutor).execute(with(any(HttpGet.class)),
with(any(HttpParams.class)),
with(any(HttpClientListener.class)));
will(new SuccessfulRequestAction(response));
one(response).getEntity();
will(returnValue(null));
}});
urnBlacklistManager.start();
context.assertIsSatisfied();
assertNextUpdateTimeIsSet(now);
}
public void testBodyIsWrittenToFileAndSpamFiltersAreReloaded()
throws IOException {
final long now = System.currentTimeMillis();
FilterSettings.NEXT_URN_BLACKLIST_UPDATE.set(now - 1000);
FilterSettings.LAST_URN_BLACKLIST_UPDATE.set(now - 2000);
File f = urnBlacklistManager.getFile();
f.delete();
final HttpResponse response = context.mock(HttpResponse.class);
final Header header = context.mock(Header.class);
final HttpEntity entity = context.mock(HttpEntity.class);
context.checking(new Expectations() {{
one(httpExecutor).execute(with(any(HttpHead.class)),
with(any(HttpParams.class)),
with(any(HttpClientListener.class)));
will(new SuccessfulRequestAction(response));
one(response).getFirstHeader("Last-Modified");
will(returnValue(header));
allowing(header).getValue();
will(returnValue(dateString(now))); // Fresh
one(httpExecutor).execute(with(any(HttpGet.class)),
with(any(HttpParams.class)),
with(any(HttpClientListener.class)));
will(new SuccessfulRequestAction(response));
one(response).getEntity();
will(returnValue(entity));
one(entity).writeTo(with(any(FileOutputStream.class)));
one(spamServices).reloadSpamFilters();
}});
assertFalse(f.exists());
urnBlacklistManager.start();
assertTrue(f.exists());
context.assertIsSatisfied();
assertNextUpdateTimeIsSet(now);
}
private void assertNextUpdateTimeIsSet(long now) {
long last = FilterSettings.LAST_URN_BLACKLIST_UPDATE.getValue();
long next = FilterSettings.NEXT_URN_BLACKLIST_UPDATE.getValue();
assertGreaterThanOrEquals(now, last);
assertGreaterThan(now, next);
}
private String dateString(long time) {
// Never trust a class with 'simple' in its name
SimpleDateFormat sdf =
new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
sdf.setTimeZone(new SimpleTimeZone(0, "GMT"));
return sdf.format(new Date(time));
}
private static class FailedRequestAction extends CustomAction {
private final HttpResponse response;
FailedRequestAction(HttpResponse response) {
super("Failed HTTP request");
this.response = response;
}
@Override
public Object invoke(Invocation invocation) {
HttpUriRequest request =
(HttpUriRequest)invocation.getParameter(0);
HttpClientListener listener =
(HttpClientListener)invocation.getParameter(2);
listener.requestFailed(request, response, new IOException("Mock"));
return null;
}
}
private static class SuccessfulRequestAction extends CustomAction {
private final HttpResponse response;
SuccessfulRequestAction(HttpResponse response) {
super("Successful HTTP request");
this.response = response;
}
@Override
public Object invoke(Invocation invocation) {
HttpUriRequest request =
(HttpUriRequest)invocation.getParameter(0);
HttpClientListener listener =
(HttpClientListener)invocation.getParameter(2);
listener.requestComplete(request, response);
return null;
}
}
private static class StubVisitor implements Visitor<String> {
ArrayList<String> urns = new ArrayList<String>();
@Override
public boolean visit(String s) {
urns.add(s);
return true;
}
}
}