com.janoz.usenet.searchers.impl.NewzbinConnectorTest.java Source code

Java tutorial

Introduction

Here is the source code for com.janoz.usenet.searchers.impl.NewzbinConnectorTest.java

Source

/*******************************************************************************
 * Copyright (c) 2010 Gijs de Vries aka Janoz.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Gijs de Vries aka Janoz - initial API and implementation
 ******************************************************************************/
package com.janoz.usenet.searchers.impl;

import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicHttpResponse;
import org.easymock.Capture;
import org.junit.Before;
import org.junit.Test;

import com.janoz.usenet.LazyInitializationException;
import com.janoz.usenet.SearchException;
import com.janoz.usenet.model.NZB;

public class NewzbinConnectorTest {

    private static final Log LOG = LogFactory.getLog(NewzbinConnectorTest.class);

    private static final byte[] SOME_BYTES = "someRandomBytes".getBytes();
    private static String SEARCH_SUCCESS = "searchSuccess.bin";
    private static String SEARCH_SPECIALCHARS = "SpecialCharResult.bin";

    NewzbinConnectorImpl subject;
    HttpClient clientMock;
    HttpEntity entityMock;

    @Before
    public void setup() throws Exception {
        subject = new NewzbinConnectorImpl();
        clientMock = createMock(HttpClient.class);
        entityMock = createMock(HttpEntity.class);
        Field field = NewzbinConnectorImpl.class.getDeclaredField("client");
        field.setAccessible(true);
        field.set(subject, clientMock);

        subject.setUsername("USER");
        subject.setPassword("PASS");

    }

    @Test
    public void testValidationNoCredentials() throws Exception {
        subject.setUsername(null);
        subject.setPassword(null);
        try {
            subject.validate();
            fail();
        } catch (SearchException se) {
            //
        }
        subject.setUsername("USER");
        subject.setPassword(null);

        try {
            subject.validate();
            fail();
        } catch (SearchException se) {
            //
        }
        subject.setUsername(null);
        subject.setPassword("PASS");
        try {
            subject.validate();
            fail();
        } catch (SearchException se) {
            //
        }
    }

    @Test
    public void testValidationFail() throws Exception {
        Capture<HttpPost> postCapture = recordHttpExecute(recordUnauthorized());

        replay(clientMock, entityMock);

        try {
            subject.validate();
            fail("validation should fail with invalid credentials");
        } catch (SearchException se) {
            //expected
            assertEquals("Incorrect Newzbin credentials for 'USER' while fetching 'dummy.nzb'(1).",
                    se.getMessage());
        }
        verify(clientMock, entityMock);
        String request = getRequestContent(postCapture);
        Properties params = extractUrlencodedParams(request);
        assertEquals("USER", params.getProperty("username"));
        assertEquals("PASS", params.getProperty("password"));
    }

    @Test
    public void testValidationSuccess() throws Exception {
        Capture<HttpPost> postCapture = recordHttpExecute(recordNotFound());

        replay(clientMock, entityMock);

        subject.validate();

        verify(clientMock, entityMock);
        String request = getRequestContent(postCapture);
        Properties params = extractUrlencodedParams(request);
        assertEquals("USER", params.getProperty("username"));
        assertEquals("PASS", params.getProperty("password"));
    }

    @Test
    public void testGetDataNonNewzbinNZB() throws Exception {
        NZB nzb = new NZB("test.nzb");
        nzb.setReportId(null);
        try {
            subject.getData(nzb);
            fail("Should have thrown an exception");
        } catch (LazyInitializationException se) {
            //expected
        }
    }

    @Test
    public void testGetDataWithError() throws Exception {

        Capture<HttpPost> postCapture = recordHttpExecute(recordNotFound());

        replay(clientMock, entityMock);
        try {
            NZB nzb = new NZB("test.nzb");
            nzb.setName("NAME");
            nzb.setReportId(Integer.valueOf(10));
            subject.getData(nzb);
            fail("Should have thrown an exception");
        } catch (LazyInitializationException se) {
            assertEquals("'NAME'(10) doesn't exsist.", se.getCause().getMessage());
        }
        verify(clientMock, entityMock);
        assertDirectNzb(getRequestContent(postCapture), 10);
    }

    @Test
    public void testGetDataSuccess() throws Exception {
        Capture<HttpPost> postCapture = recordHttpExecute(recordFound(), SOME_BYTES);
        replay(clientMock, entityMock);

        NZB nzb = new NZB("test.nzb");
        nzb.setReportId(Integer.valueOf(10));
        assertArrayEquals(SOME_BYTES, subject.getData(nzb));

        verify(clientMock, entityMock);
        assertArrayEquals(SOME_BYTES, nzb.getData());
        assertDirectNzb(getRequestContent(postCapture), 10);
    }

    @Test
    public void testFillNzbOk() throws Exception {
        Capture<HttpPost> postCapture = recordHttpExecute(recordFound(), SOME_BYTES);
        replay(clientMock, entityMock);

        NZB nzb = new NZB("test.nzb");
        nzb.setReportId(Integer.valueOf(10));
        subject.fillNZBByReportId(nzb);

        verify(clientMock, entityMock);
        assertArrayEquals(SOME_BYTES, nzb.getData());
        assertDirectNzb(getRequestContent(postCapture), 10);
    }

    @Test
    public void testFillNoReportId() throws Exception {
        replay(clientMock, entityMock);

        NZB nzb = new NZB("test.nzb");
        nzb.setReportId(null);
        try {
            subject.fillNZBByReportId(nzb);
            fail();
        } catch (SearchException e) {
            assertEquals("No ReportId. Unable to retrieve data using Newzbin.", e.getMessage());
        }
        verify(clientMock, entityMock);
    }

    @Test
    public void testFillNotFound() throws Exception {
        Capture<HttpPost> postCapture = recordHttpExecute(recordNotFound());
        replay(clientMock, entityMock);
        try {
            NZB nzb = new NZB("test.nzb");
            nzb.setName("NAME");
            nzb.setReportId(Integer.valueOf(10));
            subject.fillNZBByReportId(nzb);
            fail("Should have thrown an exception");
        } catch (SearchException se) {
            assertEquals("'NAME'(10) doesn't exsist.", se.getMessage());
        }
        verify(clientMock, entityMock);
        assertDirectNzb(getRequestContent(postCapture), 10);
    }

    @Test
    public void testFillNzbRetry() throws Exception {
        Capture<HttpPost> postCapture1 = recordHttpExecute(recordRetryResponse());
        Capture<HttpPost> postCapture2 = recordHttpExecute(recordFound(), SOME_BYTES);
        replay(clientMock, entityMock);

        NZB nzb = new NZB("test.nzb");
        nzb.setReportId(Integer.valueOf(10));

        interruptThisThreadIn(100);
        subject.fillNZBByReportId(nzb);

        verify(clientMock, entityMock);
        assertArrayEquals(SOME_BYTES, nzb.getData());
        assertDirectNzb(getRequestContent(postCapture1), 10);
        assertDirectNzb(getRequestContent(postCapture2), 10);
    }

    @Test
    public void testFill400() throws Exception {
        testFillError(400, "Bad Request, please supply all parameters");
    }

    @Test
    public void testFill402() throws Exception {
        testFillError(402, "Payment Required, not Premium");
    }

    @Test
    public void testFill500() throws Exception {
        testFillError(500, "Internal Server Error, please report to Administrator");
    }

    @Test
    public void testFill503() throws Exception {
        testFillError(503, "Service Unavailable, site is currently down");
    }

    public void testFillError(int code, String message) throws Exception {
        recordHttpExecute(reocordOtherError(code, message));
        replay(clientMock, entityMock);
        try {
            NZB nzb = new NZB("test.nzb");
            nzb.setName("NAME");
            nzb.setReportId(Integer.valueOf(10));
            subject.fillNZBByReportId(nzb);
            fail("Should have thrown an exception");
        } catch (SearchException se) {
            //OK
        }
        verify(clientMock, entityMock);
    }

    @Test
    public void testFillNzbNonApiResponse() throws Exception {
        Capture<HttpPost> postCapture = recordHttpExecute(recordNonApiResponse());
        replay(clientMock, entityMock);
        try {
            NZB nzb = new NZB("test.nzb");
            nzb.setName("NAME");
            nzb.setReportId(Integer.valueOf(10));
            subject.fillNZBByReportId(nzb);
            fail("Should have thrown an exception");
        } catch (SearchException se) {
            //Expected
        }
        verify(clientMock, entityMock);
        assertDirectNzb(getRequestContent(postCapture), 10);

    }

    @Test
    public void testSearch() throws Exception {
        Capture<HttpPost> postCapture = recordHttpExecute(recordSearchResponse(), SEARCH_SPECIALCHARS);
        replay(clientMock, entityMock);

        List<NZB> results = subject.search("The query", null, 100, 1000, null, null);

        verify(clientMock, entityMock);
        // nzb
        assertSpecialCharSearchResult(results);

        String request = getRequestContent(postCapture);
        Properties params = extractUrlencodedParams(request);
        //credentials
        assertEquals("USER", params.getProperty("username"));
        assertEquals("PASS", params.getProperty("password"));
        //search
        assertEquals("The+query", params.getProperty("query"));
        assertEquals("100", params.getProperty("u_post_larger_than"));
        assertEquals("1000", params.getProperty("u_post_smaller_than"));
        //default
        assertEquals("1", params.getProperty("u_completions"));
        assertEquals("p", params.getProperty("fpn"));
        assertEquals("ps_edit_date", params.getProperty("sort"));
        assertEquals("desc", params.getProperty("order"));
        assertEquals("0", params.getProperty("retention"));

        assertEquals(10, params.size());
    }

    @Test
    public void testSearchEmpty() throws Exception {
        recordHttpExecute(recordSearchResponseEmpty());
        replay(clientMock, entityMock);

        List<NZB> results = subject.search("The query", null, 100, 1000, null, null);

        verify(clientMock, entityMock);
        assertTrue(results.isEmpty());
    }

    @Test
    public void testSearchWithNewsgroups() throws Exception {
        Capture<HttpPost> postCapture = recordHttpExecute(recordSearchResponse(), SEARCH_SPECIALCHARS);
        replay(clientMock, entityMock);

        List<NZB> results = subject.search("The query", null, 100, 1000,
                new HashSet<String>(Arrays.asList("aap", "noot", "mies")), null);

        verify(clientMock, entityMock);
        assertSpecialCharSearchResult(results);

        String request = getRequestContent(postCapture);
        Properties params = extractUrlencodedParams(request);
        List<String> newsgroups = Arrays.asList(params.getProperty("group").split("%2B"));
        assertEquals(3, newsgroups.size());
        assertTrue(newsgroups.contains("aap"));
        assertTrue(newsgroups.contains("noot"));
        assertTrue(newsgroups.contains("mies"));
    }

    @Test
    public void testSearchWithDefaultNewsgroups() throws Exception {
        subject.setNewsgroups(new HashSet<String>(Arrays.asList("aap", "noot", "mies")));
        Capture<HttpPost> postCapture = recordHttpExecute(recordSearchResponse(), SEARCH_SPECIALCHARS);
        replay(clientMock, entityMock);

        List<NZB> results = subject.search("The query", null, 100, 1000, null, null);

        verify(clientMock, entityMock);
        assertSpecialCharSearchResult(results);

        String request = getRequestContent(postCapture);
        Properties params = extractUrlencodedParams(request);
        List<String> newsgroups = Arrays.asList(params.getProperty("group").split("%2B"));
        assertEquals(3, newsgroups.size());
        assertTrue(newsgroups.contains("aap"));
        assertTrue(newsgroups.contains("noot"));
        assertTrue(newsgroups.contains("mies"));
    }

    @Test
    public void testSearchWithProperties() throws Exception {
        Properties otherProps = new Properties();
        otherProps.setProperty("new", "aValue");
        otherProps.setProperty("sort", "anotherNewValue");
        Capture<HttpPost> postCapture = recordHttpExecute(recordSearchResponse(), SEARCH_SPECIALCHARS);
        replay(clientMock, entityMock);

        List<NZB> results = subject.search("The query", null, 100, 1000, null, otherProps);

        verify(clientMock, entityMock);
        assertSpecialCharSearchResult(results);

        String request = getRequestContent(postCapture);
        Properties params = extractUrlencodedParams(request);
        assertEquals("1", params.getProperty("u_completions"));
        assertEquals("p", params.getProperty("fpn"));
        assertEquals("anotherNewValue", params.getProperty("sort"));
        assertEquals("desc", params.getProperty("order"));
        assertEquals("0", params.getProperty("retention"));
        assertEquals("aValue", params.getProperty("new"));
    }

    @Test
    public void testSearch401() throws Exception {
        testSearchError(401, "Unauthorised");
    }

    @Test
    public void testSearch402() throws Exception {
        testSearchError(402, "Payment Required");
    }

    @Test
    public void testSearch500() throws Exception {
        testSearchError(500, "Internal Server Error");
    }

    @Test
    public void testSearch503() throws Exception {
        testSearchError(503, "Service Unavailable");
    }

    @Test
    public void testSearch550() throws Exception {
        testSearchError(550, "Missing q parameter");
    }

    public void testSearchError(int code, String message) throws Exception {
        recordHttpExecute(reocordSearchOtherError(code, message));
        replay(clientMock, entityMock);
        try {
            subject.search("The query", null, 100, 1000, null, null);
            fail("Should have thrown an exception");
        } catch (SearchException se) {
            //OK
        }
        verify(clientMock, entityMock);
    }

    private void interruptThisThreadIn(final long milis) {
        final Thread t = Thread.currentThread();
        new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    Thread.sleep(milis);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                t.interrupt();
            }
        }).start();
    }

    private Capture<HttpPost> recordHttpExecute(HttpResponse response) throws IOException, ClientProtocolException {
        return recordHttpExecute(response, (InputStream) null);
    }

    private Capture<HttpPost> recordHttpExecute(HttpResponse response, String filename)
            throws IOException, ClientProtocolException {

        URL resource = this.getClass().getClassLoader().getResource("newzbin/" + filename);
        return recordHttpExecute(response, new FileInputStream(resource.toString().substring(5)));
    }

    private Capture<HttpPost> recordHttpExecute(HttpResponse response, byte[] retunedData)
            throws IOException, ClientProtocolException {
        return recordHttpExecute(response, new ByteArrayInputStream(retunedData));
    }

    private Capture<HttpPost> recordHttpExecute(HttpResponse response, InputStream result)
            throws IOException, ClientProtocolException {
        Capture<HttpPost> postCapture = new Capture<HttpPost>();
        expect(clientMock.execute(capture(postCapture))).andReturn(response);
        if (result != null)
            expect(entityMock.getContent()).andReturn(result);
        entityMock.consumeContent();
        return postCapture;
    }

    private String getRequestContent(Capture<HttpPost> postCapture) throws IOException {
        HttpPost post = postCapture.getValue();
        String request = readStream(post.getEntity().getContent());
        return request;
    }

    private String readStream(InputStream is) throws IOException {
        byte[] bytes = new byte[is.available()];
        is.read(bytes);
        return new String(bytes, "UTF8");
    }

    private Properties extractUrlencodedParams(String request) {
        Properties result = new Properties();
        for (String set : request.split("&")) {
            String[] keyVal = set.split("=");
            result.setProperty(keyVal[0], keyVal[1]);
        }
        return result;
    }

    private HttpResponse recordUnauthorized() {
        HttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), 400, "Bad Request");
        recordHeaders(response, null, null, "401", "Unauthorised, check username/password?");
        response.setEntity(entityMock);
        return response;
    }

    private HttpResponse recordNotFound() {
        HttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), 400, "Bad Request");
        recordHeaders(response, null, null, "404", "Not Found, data doesn't exist?");
        response.setEntity(entityMock);
        return response;
    }

    private HttpResponse reocordOtherError(int code, String message) {
        HttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), 400, "Bad Request");
        recordHeaders(response, null, null, "" + code, message);
        response.setEntity(entityMock);
        return response;
    }

    private HttpResponse recordFound() {
        HttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), 200, "OK");
        recordHeaders(response, "Discussion Digest: microsoft.public.it.dotnet.asp (2010-02-17)", "Discussions",
                "200", "OK, NZB content follows");
        response.setEntity(entityMock);
        return response;
    }

    private HttpResponse recordRetryResponse() {
        HttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), 400, "OK");

        recordHeaders(response, null, null, "450", "Try Later, wait 10 seconds for counter to reset");
        response.setEntity(entityMock);
        return response;
    }

    private HttpResponse recordNonApiResponse() {
        HttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), 200, "OK");
        recordHeaders(response, null, null, null, null);
        response.setEntity(entityMock);
        return response;
    }

    private HttpResponse recordSearchResponse() {
        HttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), 200, "OK");
        recordHeaders(response, null, null, null, null);
        response.setEntity(entityMock);
        return response;
    }

    private HttpResponse recordSearchResponseEmpty() {
        HttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), 204, "No Content");
        recordHeaders(response, null, null, null, null);
        response.setEntity(entityMock);
        return response;
    }

    private HttpResponse recordSearchRetryResponse() {
        HttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), 450,
                "Try Later, wait 10 seconds for counter to reset");

        recordHeaders(response, null, null, null, null);
        response.setEntity(entityMock);
        return response;
    }

    private HttpResponse reocordSearchOtherError(int code, String message) {
        HttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), code, message);
        recordHeaders(response, null, null, null, null);
        response.setEntity(entityMock);
        return response;
    }

    private void recordHeaders(HttpResponse response, String name, String category, String code, String text) {
        response.addHeader("Date", "Thu, 14 Jan 1977 20:49:19 GMT");
        response.addHeader("Server", "Apache");
        if (name != null)
            response.addHeader("X-DNZB-Name", name);
        if (category != null)
            response.addHeader("X-DNZB-Category", category);
        if (code != null)
            response.addHeader("X-DNZB-RCode", code);
        if (text != null)
            response.addHeader("X-DNZB-RText", text);
        response.addHeader("Vary", "User-Agent,Accept-Encoding");
        response.addHeader("Content-Encoding", "gzip");
        response.addHeader("Connection", "close");
        response.addHeader("Transfer-Encoding", "chunked");
        response.addHeader("Content-Type", "text/html; charset=ISO-8859-1");
    }

    private void assertDirectNzb(String request, int reportId) {
        Properties params = extractUrlencodedParams(request);
        assertEquals("USER", params.getProperty("username"));
        assertEquals("PASS", params.getProperty("password"));
        assertEquals("" + reportId, params.getProperty("reportid"));
    }

    private void assertSpecialCharSearchResult(List<NZB> results) {
        assertEquals(1, results.size());
        assertEquals("Christopher Paolini - Eragon 2/3 - Der Auftrag des ltesten", results.get(0).getName());
        assertEquals(Integer.valueOf(5785827), results.get(0).getReportId());
    }

}