Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .coverage_history
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.9937006618292002
0.9937021683673469
11 changes: 7 additions & 4 deletions src/main/java/dev/sorn/fmp4j/services/FmpService.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,14 @@ protected Map<String, String> headers() {

public final R download() {
final var required = requiredParams();
for (final var req : required.keySet()) {
if (!params.containsKey(req)) {
throw new FmpServiceException("'%s' is a required query param for endpoint [%s]", req, url());
}
final var missing = required.keySet().stream()
.filter(req -> !params.containsKey(req))
.toList();

if (!missing.isEmpty()) {
throw new FmpServiceException("%s are required query params for endpoint [%s]", missing, url());
}

return filter(http.get(typeRef, url(), headers(), params));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ void missing_symbol_throws() {

// then
var e = assertThrows(FmpServiceException.class, () -> f.accept(service));
assertEquals(format("'symbol' is a required query param for endpoint [%s]", service.url()), e.getMessage());
assertEquals(format("[symbol] are required query params for endpoint [%s]", service.url()), e.getMessage());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ void missing_symbol_throws() {

// then
var e = assertThrows(FmpServiceException.class, () -> f.accept(service));
assertEquals(format("'symbol' is a required query param for endpoint [%s]", service.url()), e.getMessage());
assertEquals(format("[symbol] are required query params for endpoint [%s]", service.url()), e.getMessage());
}

@Test
Expand Down
59 changes: 53 additions & 6 deletions src/test/java/dev/sorn/fmp4j/services/FmpServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import dev.sorn.fmp4j.cfg.FmpConfig;
import dev.sorn.fmp4j.http.FmpHttpClient;
import dev.sorn.fmp4j.types.FmpApiKey;
import dev.sorn.fmp4j.types.FmpLimit;
import dev.sorn.fmp4j.types.FmpPage;
import dev.sorn.fmp4j.types.FmpSymbol;
import java.time.LocalDate;
import java.util.Arrays;
Expand All @@ -20,7 +22,6 @@
import java.util.Optional;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;

Expand All @@ -34,15 +35,19 @@ class FmpServiceTest {

private ConcreteFmpService service;

private MultiRequiredFmpService multiRequiredService;

@BeforeEach
void setup() {
openMocks(this);
when(mockConfig.fmpApiKey()).thenReturn(new FmpApiKey("abcdefghij1234567890abcdefghij12"));
service = new ConcreteFmpService(mockConfig, mockHttpClient);

multiRequiredService = new MultiRequiredFmpService(mockConfig, mockHttpClient);
}

@Test
void accepts_list_with_correct_type() {
void should_accept_list_with_correct_type() {
// given
List<FmpSymbol> symbols = List.of(symbol("AAPL"), symbol("GOOGL"), symbol("MSFT"));

Expand Down Expand Up @@ -84,7 +89,6 @@ void handles_empty_collection() {
}

@Test
@DisplayName("Should accept Optional with correct type")
void accepts_optional_with_correct_type() {
// given
Optional<FmpSymbol> optionalSymbol = Optional.of(symbol("AAPL"));
Expand Down Expand Up @@ -114,7 +118,7 @@ void handles_empty_optional() {
// given
Optional<String> emptyOptional = Optional.empty();

// when then
// when // then
assertDoesNotThrow(() -> service.param("symbol", emptyOptional));
assertEquals(emptyOptional, service.params.get("symbol"));
}
Expand All @@ -130,15 +134,37 @@ void handles_null_key() {
}

@Test
@DisplayName("Should reject nested Collection")
void validates_nested_collections() {
// given: a nested list
// given
List<List<String>> nestedList = Arrays.asList(Arrays.asList("AAPL", "GOOGL"));

// when // then
assertThrows(FmpServiceException.class, () -> service.param("symbol", nestedList));
}

@Test
void should_show_all_missing_required_params() {
// given // when
FmpServiceException thrownEx = assertThrows(FmpServiceException.class, multiRequiredService::download);
var msg = thrownEx.getMessage();
// then
assertTrue(
msg.contains("limit") || msg.contains("page"),
"Expected exception message to mention either 'limit' and 'page' as the required param, but got: "
+ msg);
}

@Test
void should_not_throw_missing_required_params() {
// given
multiRequiredService.param("page", FmpPage.page(10));
multiRequiredService.param("limit", FmpLimit.limit(10));

// when // then
assertDoesNotThrow(multiRequiredService::download);
}

// Concrete implementation for testing
private static class ConcreteFmpService extends FmpService<String> {
public ConcreteFmpService(FmpConfig cfg, FmpHttpClient http) {
super(cfg, http, new TypeReference<String>() {});
Expand All @@ -159,4 +185,25 @@ protected String relativeUrl() {
return "/test/endpoint";
}
}

private static class MultiRequiredFmpService extends FmpService<String> {
public MultiRequiredFmpService(FmpConfig cfg, FmpHttpClient http) {
super(cfg, http, new TypeReference<String>() {});
}

@Override
protected Map<String, Class<?>> requiredParams() {
return Map.of("limit", FmpLimit.class, "page", FmpPage.class);
}

@Override
protected Map<String, Class<?>> optionalParams() {
return Map.of();
}

@Override
protected String relativeUrl() {
return "/test/endpoint";
}
}
}