Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
So it fails playback when doing things concurrently using the AsyncHttpClient from Apache Http Components. This is similar to the behavior I found when I ran the proxy standalone. Of note is that it doesn't seem to fail if the tape is READ_WRITE. Will have to add a test to demonstrate that as well, then refactor all the duplicated code out.
- Loading branch information
Showing
3 changed files
with
562 additions
and
0 deletions.
There are no files selected for viewing
128 changes: 128 additions & 0 deletions
128
betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/concurrency/ConcurrencyTest.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
/* | ||
* 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.proxy.concurrency | ||
|
||
import co.freeside.betamax.ProxyConfiguration | ||
import co.freeside.betamax.Recorder | ||
import groovy.json.JsonSlurper | ||
import org.apache.http.HttpHost | ||
import org.apache.http.HttpResponse | ||
import org.apache.http.client.methods.HttpPost | ||
import org.apache.http.concurrent.FutureCallback | ||
import org.apache.http.entity.ContentType | ||
import org.apache.http.entity.StringEntity | ||
import org.apache.http.impl.nio.client.HttpAsyncClients | ||
import spock.lang.Specification | ||
|
||
import java.util.concurrent.ConcurrentHashMap | ||
import java.util.concurrent.CountDownLatch | ||
import java.util.concurrent.TimeUnit | ||
|
||
import static co.freeside.betamax.TapeMode.READ_ONLY | ||
import static co.freeside.betamax.TapeMode.READ_WRITE | ||
|
||
/** | ||
* Testing a custom matcher when being used in the proxy. | ||
*/ | ||
class ConcurrencyTest extends Specification { | ||
|
||
def tapeRoot = new File(ConcurrencyTest.class.getResource("/betamax/tapes/").toURI()) | ||
|
||
void "When only playing back concurrently, it works"() { | ||
given: | ||
def proxyConfig = ProxyConfiguration.builder() | ||
.tapeRoot(tapeRoot) | ||
.defaultMode(READ_ONLY) | ||
.defaultMatchRule(new PostingMatchRule()) | ||
.build() | ||
|
||
def recorder = new Recorder(proxyConfig) | ||
recorder.start("concurrentTape") | ||
|
||
def builder = HttpAsyncClients.custom() | ||
builder.setProxy(new HttpHost("127.0.0.1", 5555)) | ||
|
||
def httpClient = builder.build() | ||
|
||
httpClient.start() | ||
def requestCount = 10 | ||
println("client started, request count ${requestCount}") | ||
|
||
List<HttpPost> requests = [] | ||
|
||
requestCount.times { num -> | ||
def post = new HttpPost("http://httpbin.org/post") | ||
post.setEntity(new StringEntity(num.toString(), ContentType.TEXT_PLAIN)) | ||
println("\tQueueing post $num") | ||
requests.add post | ||
} | ||
|
||
|
||
when: | ||
//Map the request body to the result body, they should match | ||
def resultsMap = new ConcurrentHashMap<String, String>() | ||
|
||
def latch = new CountDownLatch(requestCount) | ||
println("Executing $requestCount requests concurrently") | ||
requests.each { request -> | ||
httpClient.execute(request, new FutureCallback<HttpResponse>() { | ||
@Override | ||
void completed(HttpResponse result) { | ||
//HTTPBin will return what we post, so we'll just make sure that we save | ||
// the request's body and the response body together for this result | ||
// that way we can ensure that we're not clobbering stuff, since each request will have a different | ||
// request body | ||
//Using this to get the "data" attribute out | ||
def json = new JsonSlurper().parseText(result.entity.content.text) | ||
|
||
resultsMap.put(request.entity.content.text, json.data) | ||
latch.countDown() | ||
} | ||
|
||
@Override | ||
void failed(Exception ex) { | ||
latch.countDown() | ||
} | ||
|
||
@Override | ||
void cancelled() { | ||
latch.countDown() | ||
} | ||
}) | ||
} | ||
|
||
then: | ||
//This should happen fast | ||
println("Awaiting request completion with latch") | ||
latch.await(5, TimeUnit.SECONDS) | ||
|
||
println("Done, verifying things") | ||
resultsMap.size() == requestCount | ||
|
||
println("Verifying results map") | ||
resultsMap.each { pair -> | ||
//This assertion doesn't seem to work? | ||
assert(pair.key == pair.value) | ||
} | ||
|
||
cleanup: | ||
println("stopping recorder") | ||
recorder?.stop() | ||
println("Closing httpclient") | ||
httpClient?.close() | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
betamax-proxy/src/test/groovy/co/freeside/betamax/proxy/concurrency/PostingMatchRule.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/* | ||
* 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.proxy.concurrency | ||
|
||
import co.freeside.betamax.MatchRule | ||
import co.freeside.betamax.message.Request | ||
|
||
import java.util.concurrent.atomic.AtomicInteger | ||
|
||
class PostingMatchRule implements MatchRule { | ||
|
||
@Override | ||
boolean isMatch(Request a, Request b) { | ||
if (a.uri == b.uri && a.method == b.method) { | ||
//Same method and URI, lets do a body comparison | ||
//Can only consume the body once, once it's gone it's gone. | ||
def aBody = a.bodyAsText.input.text | ||
def bBody = b.bodyAsText.input.text | ||
|
||
//Right now, lets just compare the bodies also | ||
return aBody == bBody | ||
} else { | ||
//URI and method don't match, so we're going to bail | ||
return false | ||
} | ||
} | ||
} |
Oops, something went wrong.