Skip to content

Commit

Permalink
chore: Increase test coverage.
Browse files Browse the repository at this point in the history
  • Loading branch information
nstdio committed Mar 26, 2022
1 parent 1ccc937 commit a3bfa92
Show file tree
Hide file tree
Showing 17 changed files with 349 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,8 @@ mapOf(
configurations.getByName(t) { extendsFrom(configurations.getByName(u)) }
}

val notModularConfigurations =
setOf("testRuntimeClasspath", "testCompileClasspath", "spiTestRuntimeClasspath", "spiTestCompileClasspath")
configurations.names
.filter { notModularConfigurations.contains(it) }
.filter { !setOf("compileClasspath", "runtimeClasspath").contains(it) }
.map { configurations.getByName(it) }
.forEach {
configure(listOf(it)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,11 @@
* limitations under the License.
*/

import gradle.kotlin.dsl.accessors._d03f5c1f48338c7d05c8fd3014919501.jacoco

plugins {
jacoco
id("org.sonarqube")
}



sonarqube {
properties {
property("sonar.projectKey", "nstdio_http-client-ext")
Expand Down
15 changes: 4 additions & 11 deletions src/main/java/io/github/nstdio/http/ext/CachingInterceptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import static io.github.nstdio.http.ext.Responses.gatewayTimeoutResponse;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.stream.Collectors.toList;
import static lombok.Lombok.sneakyThrow;

import io.github.nstdio.http.ext.Cache.CacheEntry;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -92,15 +91,7 @@ public <T> Chain<T> intercept(Chain<T> in) {
return sendAndCache(in, entry);
}
} else {
FutureHandler<T> fn = (r, th) -> {
if (r != null) {
if (shouldInvalidate(r)) {
invalidate(r);
}
return r;
}
throw sneakyThrow(th);
};
FutureHandler<T> fn = FutureHandler.of(r -> shouldInvalidate(r) ? invalidate(r) : r);

return Chain.of(ctx, in.futureHandler().andThen(fn));
}
Expand Down Expand Up @@ -165,7 +156,7 @@ private <T> BodyHandler<T> cacheAware(RequestContext ctx) {
};
}

private <T> void invalidate(HttpResponse<T> response) {
private <T> HttpResponse<T> invalidate(HttpResponse<T> response) {
List<HttpRequest> toEvict = Stream.of("Location", "Content-Location")
.flatMap(s -> Headers.effectiveUri(response.headers(), s, response.uri()).stream())
.filter(uri -> response.uri().getHost().equals(uri.getHost()))
Expand All @@ -174,6 +165,8 @@ private <T> void invalidate(HttpResponse<T> response) {
toEvict.add(response.request());

toEvict.forEach(cache::evictAll);

return response;
}

private <T> boolean shouldInvalidate(HttpResponse<T> response) {
Expand Down
14 changes: 10 additions & 4 deletions src/main/java/io/github/nstdio/http/ext/DiskCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@

package io.github.nstdio.http.ext;

import static io.github.nstdio.http.ext.IOUtils.createFile;
import static io.github.nstdio.http.ext.IOUtils.delete;
import static io.github.nstdio.http.ext.IOUtils.size;

import lombok.Getter;
import lombok.Value;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Accessors;

import java.io.IOException;
Expand Down Expand Up @@ -112,6 +113,10 @@ private EntryPaths pathsFor() {
@SuppressWarnings("unchecked")
public Writer<Path> writer(CacheEntryMetadata metadata) {
EntryPaths entryPaths = pathsFor();
if (!createFile(entryPaths.body())) {
return NullCache.writer();
}

return new Writer<>() {
@Override
public BodySubscriber<Path> subscriber() {
Expand All @@ -125,11 +130,12 @@ public Consumer<Path> finisher() {
};
}

@Value(staticConstructor = "of")
@Getter
@Accessors(fluent = true)
@RequiredArgsConstructor(staticName = "of")
private static class EntryPaths {
Path body;
Path metadata;
private final Path body;
private final Path metadata;
}

@Getter
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/io/github/nstdio/http/ext/FutureHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,14 @@ static <T> FutureHandler<T> of(UnaryOperator<HttpResponse<T>> op) {

default FutureHandler<T> andThen(FutureHandler<T> other) {
return (r, th) -> {
HttpResponse<T> result;
try {
HttpResponse<T> result = apply(r, th);
return other.apply(result, th);
result = apply(r, th);
} catch (Exception e) {
return other.apply(null, e);
}

return other.apply(result, th);
};
}
}
12 changes: 12 additions & 0 deletions src/main/java/io/github/nstdio/http/ext/IOUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.io.Closeable;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;

Expand Down Expand Up @@ -46,4 +47,15 @@ static long size(Path path) {
return -1;
}
}

static boolean createFile(Path path) {
try {
Files.createFile(path);
return true;
} catch (FileAlreadyExistsException e) {
return true;
} catch (IOException e) {
return false;
}
}
}
16 changes: 8 additions & 8 deletions src/main/java/io/github/nstdio/http/ext/PathSubscriber.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@
package io.github.nstdio.http.ext;

import static io.github.nstdio.http.ext.IOUtils.closeQuietly;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
import static java.nio.file.StandardOpenOption.WRITE;

import java.io.IOException;
import java.net.http.HttpResponse;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.GatheringByteChannel;
import java.nio.file.Path;
import java.util.List;
import java.util.concurrent.CompletableFuture;
Expand All @@ -34,7 +35,7 @@ class PathSubscriber implements HttpResponse.BodySubscriber<Path> {
private static final ByteBuffer[] EMPTY = new ByteBuffer[0];
private final Path path;
private final CompletableFuture<Path> future = new CompletableFuture<>();
private volatile FileChannel out;
private GatheringByteChannel out;

PathSubscriber(Path path) {
this.path = path;
Expand All @@ -56,30 +57,29 @@ private synchronized void createChannel() {
}

try {
out = FileChannel.open(path, CREATE, WRITE);
out = FileChannel.open(path, WRITE, TRUNCATE_EXISTING);
} catch (IOException e) {
future.completeExceptionally(e);
}
}

@Override
public void onNext(List<ByteBuffer> item) {
createChannel();

try {
out.write(item.toArray(EMPTY));
} catch (IOException ex) {
close();
future.completeExceptionally(ex);
onError(ex);
}
}

private void close() {
private synchronized void close() {
closeQuietly(out);
out = null;
}

@Override
public void onError(Throwable throwable) {
close();
future.completeExceptionally(throwable);
}

Expand Down
11 changes: 8 additions & 3 deletions src/main/java/io/github/nstdio/http/ext/RequestContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.With;
import lombok.experimental.Accessors;

import java.net.http.HttpRequest;
Expand All @@ -29,9 +28,7 @@
@Accessors(fluent = true)
@AllArgsConstructor
final class RequestContext {
@With
private final HttpRequest request;
@With
private final BodyHandler<?> bodyHandler;
private final CacheControl cacheControl;
private final AtomicLong requestTime = new AtomicLong();
Expand Down Expand Up @@ -59,4 +56,12 @@ long responseTimeLong() {
<T> BodyHandler<T> bodyHandler() {
return (BodyHandler<T>) bodyHandler;
}

RequestContext withRequest(HttpRequest request) {
return new RequestContext(request, this.bodyHandler, this.cacheControl);
}

RequestContext withBodyHandler(BodyHandler<?> bodyHandler) {
return new RequestContext(this.request, bodyHandler, this.cacheControl);
}
}
57 changes: 57 additions & 0 deletions src/test/java/io/github/nstdio/http/ext/BodyHandlersTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2022 Edgar Asatryan
*
* 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 io.github.nstdio.http.ext;

import static org.assertj.core.api.Assertions.assertThat;

import com.fasterxml.jackson.core.type.TypeReference;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Map;

class BodyHandlersTest {

@Nested
class OfJsonTest {
private final HttpClient client = HttpClient.newHttpClient();

@Test
void shouldProperlyReadJson() {
//given
var request = HttpRequest.newBuilder(URI.create("https://httpbin.org/get")).build();
TypeReference<Map<String, Object>> typeReference = new TypeReference<>() {
};

//when
var body1 = client.sendAsync(request, BodyHandlers.ofJson(typeReference))
.thenApply(HttpResponse::body)
.join();
var body2 = client.sendAsync(request, BodyHandlers.ofJson(Object.class))
.thenApply(HttpResponse::body)
.join();

//then
assertThat(body1).isNotEmpty();
assertThat(body2).isNotNull();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package io.github.nstdio.http.ext;

import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.WRITE;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.inOrder;
Expand All @@ -25,21 +27,41 @@
import org.junit.jupiter.api.io.TempDir;
import org.mockito.InOrder;

import java.io.IOException;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodySubscriber;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.function.Consumer;

class CachingBodySubscriberTest {

@Test
void shouldResetInputBuffers(@TempDir Path dir) {
void shouldWriteAndReadFromEmptyFile(@TempDir Path dir) throws IOException {
//given
Path p1 = dir.resolve("p1");
Path p2 = dir.resolve("p2");
Files.createFile(p2);

assertThat(p2).isEmptyFile();
assertShouldReadAndWriteResponse(p1, p2);
}

@Test
void shouldWriteAndReadFromNotEmptyFile(@TempDir Path dir) throws IOException {
//given
Path p1 = dir.resolve("p1");
Path p2 = dir.resolve("p2");
Files.write(p2, "random-stuff".repeat(300).getBytes(), CREATE, WRITE);

assertThat(p2).isNotEmptyFile();
assertShouldReadAndWriteResponse(p1, p2);
}

private void assertShouldReadAndWriteResponse(Path p1, Path p2) {
//given
String body = Helpers.randomString(32, 128);
var original = HttpResponse.BodySubscribers.ofFile(p1);
var other = new PathSubscriber(p2);
Expand All @@ -52,7 +74,7 @@ void shouldResetInputBuffers(@TempDir Path dir) {
List<ByteBuffer> buffers = Helpers.toBuffers(body);
var subscription = new PlainSubscription(subscriber, buffers, false);
subscriber.onSubscribe(subscription);
subscription.cancel();

var actual1 = subscriber.getBody().toCompletableFuture().join();
var actual2 = other.getBody().toCompletableFuture().join();

Expand Down
Loading

0 comments on commit a3bfa92

Please sign in to comment.