Skip to content

Commit

Permalink
#107 start separating JUnit specific code from general configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
robfletcher committed Oct 28, 2013
1 parent a028550 commit 888a89b
Show file tree
Hide file tree
Showing 27 changed files with 250 additions and 140 deletions.
3 changes: 3 additions & 0 deletions betamax-core/src/main/java/co/freeside/betamax/MatchRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

import co.freeside.betamax.message.*;

/**
* A rule used to determine whether a recorded HTTP interaction on tape matches a new request being made.
*/
public interface MatchRule {
boolean isMatch(Request a, Request b);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import com.google.common.io.*;

/**
* Implements a request matching rule for finding recordings on a tape.
* Standard {@link MatchRule} implementations.
*/
public enum MatchRules implements MatchRule {
method {
Expand Down
72 changes: 8 additions & 64 deletions betamax-core/src/main/java/co/freeside/betamax/Recorder.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,21 @@
import java.net.*;
import java.util.*;
import java.util.logging.*;
import co.freeside.betamax.message.Request;
import co.freeside.betamax.message.*;
import co.freeside.betamax.tape.*;
import co.freeside.betamax.tape.yaml.*;
import co.freeside.betamax.util.*;
import com.google.common.base.*;
import com.google.common.collect.*;
import com.google.common.io.*;
import org.junit.rules.*;
import org.junit.runner.*;
import org.junit.runners.model.*;
import static co.freeside.betamax.MatchRules.*;
import static java.util.logging.Level.*;

/**
* This is the main interface to the Betamax proxy. It allows control of Betamax configuration and inserting and
* ejecting `Tape` instances. The class can also be used as a _JUnit @Rule_ allowing tests annotated with `@Betamax` to
* run with the Betamax HTTP proxy in the background.
* This class is the main interface to Betamax. It allows control of Betamax configuration, inserting and
* ejecting `Tape` instances and starting and stopping recording sessions.
*/
public class Recorder implements TestRule {
public class Recorder {

public Recorder() {
try {
URL propertiesFile = Recorder.class.getResource("/betamax.properties");
Expand Down Expand Up @@ -121,65 +118,12 @@ public void ejectTape() {

}

@Override
public Statement apply(final Statement statement, Description description) {
final Betamax annotation = description.getAnnotation(Betamax.class);
if (annotation != null) {
log.fine("found @Betamax annotation on '" + description.getDisplayName() + "'");
return new Statement() {
@Override
public void evaluate() throws Throwable {
Map<String, Object> map = new LinkedHashMap<String, Object>(2);
map.put("mode", annotation.mode());
map.put("match", Arrays.asList(annotation.match()));
try {
start(annotation.tape(), map);
statement.evaluate();
} catch (Exception e) {
log.log(SEVERE, "Caught exception starting Betamax", e);
} finally {
stop();
}
}
};
} else {
log.fine("no @Betamax annotation on '" + description.getDisplayName() + "'");
return statement;
}

}

// TODO: move these to a utility class
public static boolean getBoolean(Properties properties, String key, boolean defaultValue) {
String value = properties.getProperty(key);
return value != null ? Boolean.valueOf(value) : defaultValue;
}

public static boolean getBoolean(Properties properties, String key) {
return Recorder.getBoolean(properties, key, false);
}

public static int getInteger(Properties properties, String key, int defaultValue) {
String value = properties.getProperty(key);
return value != null ? Integer.parseInt(value) : defaultValue;
}

public static int getInteger(Properties properties, String key) {
return Recorder.getInteger(properties, key, 0);
}

public static <T extends Enum<T>> T getEnum(Properties properties, String key, T defaultValue) {
String value = properties.getProperty(key);
T anEnum = Enum.valueOf((Class<T>) defaultValue.getClass(), value);
return value != null ? anEnum : defaultValue;
}

protected void configureFrom(Properties properties) {
tapeRoot = new File(properties.getProperty("betamax.tapeRoot", DEFAULT_TAPE_ROOT));
defaultMode = getEnum(properties, "betamax.defaultMode", TapeMode.READ_WRITE);
defaultMode = TypedProperties.getEnum(properties, "betamax.defaultMode", TapeMode.READ_WRITE);
final List<String> tokenize = Lists.newArrayList(Splitter.on(",").split((String) properties.getProperty("betamax.ignoreHosts")));
ignoreHosts = tokenize != null ? tokenize : new ArrayList<String>();
ignoreLocalhost = getBoolean(properties, "betamax.ignoreLocalhost");
ignoreLocalhost = TypedProperties.getBoolean(properties, "betamax.ignoreLocalhost");
}

protected void configureWithDefaults() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package co.freeside.betamax;
package co.freeside.betamax.junit;

import java.lang.annotation.*;
import co.freeside.betamax.*;
import static co.freeside.betamax.MatchRules.*;
import static co.freeside.betamax.TapeMode.*;
import static java.lang.annotation.ElementType.*;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright 2013 the original author or authors.
*
* 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 co.freeside.betamax.junit;

import java.util.*;
import java.util.logging.*;
import co.freeside.betamax.*;
import org.junit.rules.*;
import org.junit.runner.*;
import org.junit.runners.model.*;
import static java.util.logging.Level.*;

/**
* This is a wrapper for the Betamax {@link co.freeside.betamax.Recorder} that can be used as a
* _JUnit @Rule_ allowing tests annotated with `@Betamax` to run with the
* Betamax HTTP proxy in the background.
*/
public class RecorderRule implements TestRule {

private final Recorder recorder;
private final Logger log = Logger.getLogger(RecorderRule.class.getName());

public RecorderRule(Recorder recorder) {
this.recorder = recorder;
}

@Override
public Statement apply(final Statement statement, Description description) {
final Betamax annotation = description.getAnnotation(Betamax.class);
if (annotation != null) {
log.fine("found @Betamax annotation on '" + description.getDisplayName() + "'");
return new Statement() {
@Override
public void evaluate() throws Throwable {
Map<String, Object> map = new LinkedHashMap<String, Object>(2);
map.put("mode", annotation.mode());
map.put("match", Arrays.asList(annotation.match()));
try {
recorder.start(annotation.tape(), map);
statement.evaluate();
} catch (Exception e) {
log.log(SEVERE, "Caught exception starting Betamax", e);
} finally {
recorder.stop();
}
}
};
} else {
log.fine(String.format("no @Betamax annotation on '%s'", description.getDisplayName()));
return statement;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2013 the original author or authors.
*
* 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 co.freeside.betamax.util;

import java.util.*;

public class TypedProperties extends Properties {

public static boolean getBoolean(Properties properties, String key, boolean defaultValue) {
String value = properties.getProperty(key);
return value != null ? Boolean.valueOf(value) : defaultValue;
}

public static boolean getBoolean(Properties properties, String key) {
return getBoolean(properties, key, false);
}

public static int getInteger(Properties properties, String key, int defaultValue) {
String value = properties.getProperty(key);
return value != null ? Integer.parseInt(value) : defaultValue;
}

public static int getInteger(Properties properties, String key) {
return getInteger(properties, key, 0);
}

public static <T extends Enum<T>> T getEnum(Properties properties, String key, T defaultValue) {
String value = properties.getProperty(key);
T anEnum = Enum.valueOf((Class<T>) defaultValue.getClass(), value);
return value != null ? anEnum : defaultValue;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@

package co.freeside.betamax.recorder

import co.freeside.betamax.*
import co.freeside.betamax.Recorder
import co.freeside.betamax.handler.*
import co.freeside.betamax.junit.*
import co.freeside.betamax.message.Response
import co.freeside.betamax.util.message.BasicRequest
import groovy.json.JsonSlurper
import org.junit.Rule
Expand All @@ -31,13 +33,14 @@ import static org.apache.http.entity.ContentType.APPLICATION_JSON
@Issue('https://github.com/robfletcher/betamax/pull/70')
class SequentialTapeSpec extends Specification {

@Rule Recorder recorder = new Recorder()
HttpHandler handler = new DefaultHandlerChain(recorder)
def recorder = new Recorder()
@Rule RecorderRule recorderRule = new RecorderRule(recorder)
def handler = new DefaultHandlerChain(recorder)

@Betamax(tape = 'sequential tape', mode = READ_SEQUENTIAL)
void 'read sequential tapes play back recordings in correct sequence'() {
when: 'multiple requests are made to the same endpoint'
def responses = []
List<Response> responses = []
n.times {
responses << handler.handle(request)
}
Expand Down Expand Up @@ -84,7 +87,7 @@ class SequentialTapeSpec extends Specification {
postRequest.body = '{"name":"foo"}'.bytes

when: 'multiple requests are made to the same endpoint'
def responses = []
List<Response> responses = []
responses << handler.handle(getRequest)
responses << handler.handle(postRequest)
responses << handler.handle(getRequest)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ package co.freeside.betamax.recorder

import co.freeside.betamax.*
import co.freeside.betamax.handler.*
import co.freeside.betamax.junit.Betamax
import co.freeside.betamax.junit.RecorderRule
import co.freeside.betamax.message.Response
import co.freeside.betamax.proxy.jetty.SimpleServer
import co.freeside.betamax.util.message.BasicRequest
import co.freeside.betamax.util.server.IncrementingHandler
Expand All @@ -32,10 +35,11 @@ import static org.apache.http.HttpStatus.SC_OK
@Issue('https://github.com/robfletcher/betamax/pull/70')
class SequentialTapeWritingSpec extends Specification {

@Shared @AutoCleanup('deleteDir') File tapeRoot = newTempDir('tapes')
@Rule Recorder recorder = new Recorder(tapeRoot: tapeRoot)
@Shared @AutoCleanup('stop') SimpleServer endpoint = new SimpleServer()
HttpHandler handler = new DefaultHandlerChain(recorder)
@Shared @AutoCleanup('deleteDir') def tapeRoot = newTempDir('tapes')
def recorder = new Recorder(tapeRoot: tapeRoot)
@Rule RecorderRule recorderRule = new RecorderRule(recorder)
@Shared @AutoCleanup('stop') def endpoint = new SimpleServer()
def handler = new DefaultHandlerChain(recorder)

void setupSpec() {
endpoint.start(IncrementingHandler)
Expand All @@ -44,7 +48,7 @@ class SequentialTapeWritingSpec extends Specification {
@Betamax(tape = 'sequential tape', mode = WRITE_SEQUENTIAL)
void 'write sequential tapes record multiple matching responses'() {
when: 'multiple requests are made to the same endpoint'
def responses = []
List<Response> responses = []
n.times {
responses << handler.handle(request)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package co.freeside.betamax.tape
import java.util.concurrent.CountDownLatch
import co.freeside.betamax.*
import co.freeside.betamax.handler.*
import co.freeside.betamax.junit.Betamax
import co.freeside.betamax.junit.RecorderRule
import co.freeside.betamax.proxy.jetty.SimpleServer
import co.freeside.betamax.util.message.BasicRequest
import co.freeside.betamax.util.server.HelloHandler
Expand All @@ -30,9 +32,10 @@ import static java.util.concurrent.TimeUnit.SECONDS

class MultiThreadedTapeWritingSpec extends Specification {

@Shared @AutoCleanup('deleteDir') File tapeRoot = newTempDir('tapes')
@Rule Recorder recorder = new Recorder(tapeRoot: tapeRoot)
HttpHandler handler = new DefaultHandlerChain(recorder)
@Shared @AutoCleanup('deleteDir') def tapeRoot = newTempDir('tapes')
def recorder = new Recorder(tapeRoot: tapeRoot)
@Rule RecorderRule recorderRule = new RecorderRule(recorder)
def handler = new DefaultHandlerChain(recorder)

@Shared @AutoCleanup('stop') SimpleServer endpoint = new SimpleServer()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ package co.freeside.betamax.httpclient

import co.freeside.betamax.*
import co.freeside.betamax.handler.HandlerException
import co.freeside.betamax.junit.Betamax
import co.freeside.betamax.junit.RecorderRule
import co.freeside.betamax.proxy.jetty.SimpleServer
import co.freeside.betamax.util.Network
import co.freeside.betamax.util.server.*
import groovyx.net.http.HttpResponseDecorator
import groovyx.net.http.RESTClient
import org.apache.http.client.methods.*
import org.apache.http.entity.StringEntity
Expand All @@ -29,7 +32,6 @@ import org.apache.http.params.HttpParams
import org.eclipse.jetty.server.Handler
import org.junit.Rule
import spock.lang.*
import static co.freeside.betamax.util.FileUtils.newTempDir
import static co.freeside.betamax.util.server.HelloHandler.HELLO_WORLD
import static java.net.HttpURLConnection.HTTP_OK
import static org.apache.http.HttpHeaders.VIA
Expand All @@ -38,9 +40,10 @@ import static org.apache.http.entity.ContentType.APPLICATION_FORM_URLENCODED
@Issue('https://github.com/robfletcher/betamax/issues/40')
class BetamaxHttpClientSpec extends Specification {

@Shared @AutoCleanup('deleteDir') File tapeRoot = co.freeside.betamax.util.FileUtils.newTempDir('tapes')
@Rule Recorder recorder = new Recorder(tapeRoot: tapeRoot)
@AutoCleanup('stop') SimpleServer endpoint = new SimpleServer()
@Shared @AutoCleanup('deleteDir') def tapeRoot = co.freeside.betamax.util.FileUtils.newTempDir('tapes')
def recorder = new Recorder(tapeRoot: tapeRoot)
@Rule RecorderRule recorderRule = new RecorderRule(recorder)
@AutoCleanup('stop') def endpoint = new SimpleServer()
def http = new BetamaxHttpClient(recorder)

@Betamax(tape = 'betamax http client', mode = TapeMode.READ_WRITE)
Expand Down Expand Up @@ -183,7 +186,7 @@ class BetamaxHttpClientSpec extends Specification {
}

when:
def response = restClient.get(uri: endpoint.url)
HttpResponseDecorator response = restClient.get(uri: endpoint.url)

then:
response.status == HTTP_OK
Expand Down
Loading

0 comments on commit 888a89b

Please sign in to comment.