Skip to content

Commit

Permalink
Refactor dependency injection to support test implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
jordansimsmith committed Aug 26, 2024
1 parent 1ce06e6 commit 5c7fc84
Show file tree
Hide file tree
Showing 17 changed files with 254 additions and 63 deletions.
15 changes: 10 additions & 5 deletions immersion_tracker_api/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ java_library(
),
deps = [
":dagger",
"@maven//:com_google_code_findbugs_jsr305",
"@maven//:com_google_guava_guava",
"//lib/dynamodb:lib",
"//lib/json:lib",
"//lib/time:lib",
"@maven//:com_fasterxml_jackson_core_jackson_databind",
"@maven//:software_amazon_awssdk_dynamodb",
"@maven//:software_amazon_awssdk_dynamodb_enhanced",
"@maven//:com_fasterxml_jackson_core_jackson_databind",
],
)

Expand All @@ -30,6 +31,7 @@ java_binary(
]),
deps = [
":lib",
"//lib/time:lib",
"@maven//:ch_qos_logback_logback_classic",
"@maven//:ch_qos_logback_logback_core",
"@maven//:com_amazonaws_aws_lambda_java_core",
Expand All @@ -38,7 +40,6 @@ java_binary(
"@maven//:com_fasterxml_jackson_core_jackson_databind",
"@maven//:com_google_code_findbugs_jsr305",
"@maven//:com_google_guava_guava",
"@maven//:software_amazon_awssdk_dynamodb",
"@maven//:software_amazon_awssdk_dynamodb_enhanced",
],
)
Expand All @@ -57,12 +58,16 @@ java_test_suite(
test_suffixes = ["IntegrationTest.java"],
runtime_deps = JUNIT5_DEPS,
deps = [
":dagger",
":get-progress-handler",
":lib",
"//lib/dynamodb:lib",
"//lib/json:lib",
"//lib/testcontainers:lib",
"//lib/time:lib",
"@maven//:com_amazonaws_aws_lambda_java_core",
"@maven//:com_fasterxml_jackson_core_jackson_databind",
"@maven//:com_amazonaws_aws_lambda_java_events",
"@maven//:com_fasterxml_jackson_core_jackson_databind",
"@maven//:org_assertj_assertj_core",
"@maven//:org_junit_jupiter_junit_jupiter_api",
"@maven//:org_testcontainers_junit_jupiter",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.annotations.VisibleForTesting;
import com.jordansimsmith.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
Expand All @@ -23,8 +24,9 @@
public class GetProgressHandler
implements RequestHandler<APIGatewayV2HTTPEvent, APIGatewayV2HTTPResponse> {
private static final int MINUTES_PER_EPISODE = 20;
private static final ZoneId AUCKLAND_ZONE_ID = ZoneId.of("Pacific/Auckland");
@VisibleForTesting static final ZoneId ZONE_ID = ZoneId.of("Pacific/Auckland");

private final Clock clock;
private final ObjectMapper objectMapper;
private final DynamoDbTable<ImmersionTrackerItem> immersionTrackerTable;

Expand All @@ -48,6 +50,7 @@ public GetProgressHandler() {

@VisibleForTesting
GetProgressHandler(ImmersionTrackerFactory factory) {
this.clock = factory.clock();
this.objectMapper = factory.objectMapper();
this.immersionTrackerTable = factory.immersionTrackerTable();
}
Expand Down Expand Up @@ -85,7 +88,7 @@ private APIGatewayV2HTTPResponse doHandleRequest(APIGatewayV2HTTPEvent event, Co

var totalEpisodesWatched = episodes.size();
var totalHoursWatched = totalEpisodesWatched * MINUTES_PER_EPISODE / 60;
var today = Instant.now().atZone(AUCKLAND_ZONE_ID).truncatedTo(ChronoUnit.DAYS).toInstant();
var today = clock.now().atZone(ZONE_ID).truncatedTo(ChronoUnit.DAYS).toInstant();
var episodesWatchedToday =
episodes.stream()
.filter(e -> Instant.ofEpochSecond(e.getTimestamp()).isAfter(today))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
package com.jordansimsmith.immersiontracker;

import com.fasterxml.jackson.databind.ObjectMapper;
import dagger.BindsInstance;
import com.jordansimsmith.dynamodb.DynamoDbModule;
import com.jordansimsmith.json.ObjectMapperModule;
import com.jordansimsmith.time.Clock;
import com.jordansimsmith.time.ClockModule;
import dagger.Component;
import java.net.URI;
import javax.annotation.Nullable;
import javax.inject.Named;
import javax.inject.Singleton;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;

@Singleton
@Component(modules = {ImmersionTrackerModule.class})
@Component(
modules = {
ClockModule.class,
ObjectMapperModule.class,
DynamoDbModule.class,
ImmersionTrackerModule.class
})
public interface ImmersionTrackerFactory {
Clock clock();

ObjectMapper objectMapper();

DynamoDbClient dynamoDbClient();
Expand All @@ -22,19 +30,7 @@ public interface ImmersionTrackerFactory {

DynamoDbTable<ImmersionTrackerItem> immersionTrackerTable();

@Component.Builder
interface Builder {
@BindsInstance
Builder dynamoDbEndpoint(@Named("dynamoDbEndpoint") @Nullable URI dynamoDbEndpoint);

ImmersionTrackerFactory build();
}

static ImmersionTrackerFactory create() {
return DaggerImmersionTrackerFactory.builder().build();
}

static ImmersionTrackerFactory.Builder builder() {
return DaggerImmersionTrackerFactory.builder();
return DaggerImmersionTrackerFactory.create();
}
}
Original file line number Diff line number Diff line change
@@ -1,41 +1,14 @@
package com.jordansimsmith.immersiontracker;

import com.fasterxml.jackson.databind.ObjectMapper;
import dagger.Module;
import dagger.Provides;
import java.net.URI;
import javax.annotation.Nullable;
import javax.inject.Named;
import javax.inject.Singleton;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;

@Module
public class ImmersionTrackerModule {
@Provides
public ObjectMapper objectMapper() {
return new ObjectMapper();
}

@Provides
@Singleton
public DynamoDbClient dynamoDbClient(@Named("dynamoDbEndpoint") @Nullable URI dynamoDbEndpoint) {
var builder = DynamoDbClient.builder();
if (dynamoDbEndpoint != null) {
builder.endpointOverride(dynamoDbEndpoint);
}

return builder.build();
}

@Provides
@Singleton
public DynamoDbEnhancedClient dynamoDbEnhancedClient(DynamoDbClient dynamoDbClient) {
return DynamoDbEnhancedClient.builder().dynamoDbClient(dynamoDbClient).build();
}

@Provides
@Singleton
public DynamoDbTable<ImmersionTrackerItem> immersionTrackerTable(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,50 +1,59 @@
package com.jordansimsmith.immersiontracker;

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

import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jordansimsmith.testcontainers.DynamoDbContainer;
import com.jordansimsmith.time.FakeClock;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
import software.amazon.awssdk.services.dynamodb.waiters.DynamoDbWaiter;

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

@Testcontainers
public class GetProgressHandlerIntegrationTest {
private FakeClock fakeClock;
private ObjectMapper objectMapper;
private DynamoDbTable<ImmersionTrackerItem> immersionTrackerTable;

private GetProgressHandler getProgressHandler;

@Container
DynamoDbContainer dynamoDbContainer = new DynamoDbContainer();
@Container DynamoDbContainer dynamoDbContainer = new DynamoDbContainer();

@BeforeEach
void setUp() {
var factory = ImmersionTrackerFactory.builder().dynamoDbEndpoint(dynamoDbContainer.getEndpoint()).build();
var factory = ImmersionTrackerTestFactory.create(dynamoDbContainer.getEndpoint());

fakeClock = factory.fakeClock();
objectMapper = factory.objectMapper();

var dynamoDbClient = factory.dynamoDbClient();
immersionTrackerTable = factory.immersionTrackerTable();
immersionTrackerTable.createTable();
try (var waiter = DynamoDbWaiter.builder().client(dynamoDbClient).build()) {
var res = waiter.waitUntilTableExists(b -> b.tableName(immersionTrackerTable.tableName()).build()).matched();
var res =
waiter
.waitUntilTableExists(b -> b.tableName(immersionTrackerTable.tableName()).build())
.matched();
res.response().orElseThrow();
}

getProgressHandler = new GetProgressHandler(factory);
}

@Test
void handleRequestShouldQueryItems() throws Exception {
void handleRequestShouldCalculateProgress() throws Exception {
// arrange
var episode1 = ImmersionTrackerItem.createEpisode("jordansimsmith", "show1", "episode1", 123);
var episode2 = ImmersionTrackerItem.createEpisode("jordansimsmith", "show1", "episode2", 456);
var episode3 = ImmersionTrackerItem.createEpisode("jordansimsmith", "show2", "episode1", 789);
var now = (int) fakeClock.now().atZone(GetProgressHandler.ZONE_ID).toInstant().getEpochSecond();
var episode1 =
ImmersionTrackerItem.createEpisode("jordansimsmith", "show1", "episode1", now - 100);
var episode2 =
ImmersionTrackerItem.createEpisode("jordansimsmith", "show1", "episode2", now + 100);
var episode3 =
ImmersionTrackerItem.createEpisode("jordansimsmith", "show2", "episode1", now - 100);
var show = ImmersionTrackerItem.createShow("jordansimsmith", "show1");
show.setTvdbId(1);
show.setTvdbName("my show");
Expand All @@ -65,7 +74,7 @@ void handleRequestShouldQueryItems() throws Exception {
assertThat(progress).isNotNull();
assertThat(progress.totalEpisodesWatched()).isEqualTo(3);
assertThat(progress.totalHoursWatched()).isEqualTo(1);
assertThat(progress.episodesWatchedToday()).isEqualTo(0);
assertThat(progress.episodesWatchedToday()).isEqualTo(1);

var shows = progress.shows();
assertThat(shows).hasSize(2);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.jordansimsmith.immersiontracker;

import com.jordansimsmith.dynamodb.DynamoDbTestModule;
import com.jordansimsmith.json.ObjectMapperModule;
import com.jordansimsmith.time.ClockTestModule;
import com.jordansimsmith.time.FakeClock;
import dagger.BindsInstance;
import dagger.Component;
import java.net.URI;
import javax.inject.Named;
import javax.inject.Singleton;

@Singleton
@Component(
modules = {
ClockTestModule.class,
ObjectMapperModule.class,
DynamoDbTestModule.class,
ImmersionTrackerModule.class
})
public interface ImmersionTrackerTestFactory extends ImmersionTrackerFactory {
FakeClock fakeClock();

@Component.Factory
interface Factory {
ImmersionTrackerTestFactory create(
@BindsInstance @Named("dynamoDbEndpoint") URI dynamoDbEndpoint);
}

static ImmersionTrackerTestFactory create(URI dynamoDbEndpoint) {
return DaggerImmersionTrackerTestFactory.factory().create(dynamoDbEndpoint);
}
}
16 changes: 16 additions & 0 deletions lib/dynamodb/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
load("@dagger//:workspace_defs.bzl", "dagger_rules")

dagger_rules()

java_library(
name = "lib",
srcs = glob(["src/main/java/**/*.java"]),
visibility = [
"//visibility:public",
],
deps = [
":dagger",
"@maven//:software_amazon_awssdk_dynamodb",
"@maven//:software_amazon_awssdk_dynamodb_enhanced",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.jordansimsmith.dynamodb;

import dagger.Module;
import dagger.Provides;
import javax.inject.Singleton;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;

@Module
public class DynamoDbModule {
@Provides
@Singleton
public DynamoDbClient dynamoDbClient() {
return DynamoDbClient.builder().build();
}

@Provides
@Singleton
public DynamoDbEnhancedClient dynamoDbEnhancedClient(DynamoDbClient dynamoDbClient) {
return DynamoDbEnhancedClient.builder().dynamoDbClient(dynamoDbClient).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.jordansimsmith.dynamodb;

import dagger.Module;
import dagger.Provides;
import java.net.URI;
import javax.inject.Named;
import javax.inject.Singleton;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;

@Module
public class DynamoDbTestModule {
@Provides
@Singleton
public DynamoDbClient dynamoDbClient(@Named("dynamoDbEndpoint") URI dynamoDbEndpoint) {
return DynamoDbClient.builder().endpointOverride(dynamoDbEndpoint).build();
}

@Provides
@Singleton
public DynamoDbEnhancedClient dynamoDbEnhancedClient(DynamoDbClient dynamoDbClient) {
return DynamoDbEnhancedClient.builder().dynamoDbClient(dynamoDbClient).build();
}
}
15 changes: 15 additions & 0 deletions lib/json/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
load("@dagger//:workspace_defs.bzl", "dagger_rules")

dagger_rules()

java_library(
name = "lib",
srcs = glob(["src/main/java/**/*.java"]),
visibility = [
"//visibility:public",
],
deps = [
":dagger",
"@maven//:com_fasterxml_jackson_core_jackson_databind",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.jordansimsmith.json;

import com.fasterxml.jackson.databind.ObjectMapper;
import dagger.Module;
import dagger.Provides;

@Module
public class ObjectMapperModule {
@Provides
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
}
Loading

0 comments on commit 5c7fc84

Please sign in to comment.