diff --git a/docs/README.md b/docs/README.md index 8759a4b2..ea1961c3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -93,7 +93,7 @@ Copy of https://site.financialmodelingprep.com/developer/docs/stable#directory | ❌️ | - | `/symbol-change` | | ✅️ | 0.1.0 | `/etf-list` | | ❌️ | - | `/actively-trading-list` | -| ❌️ | - | `/earnings-transcript-list` | +| ✅️ | 0.3.0 | `/earnings-transcript-list` | | ❌️ | - | `/available-exchanges` | | ❌️ | - | `/available-sectors` | | ❌️ | - | `/available-industries` | @@ -468,9 +468,9 @@ Copy of https://site.financialmodelingprep.com/developer/docs/stable#earnings-tr | | Since | Endpoint | |:--:|:-----:|-----------------------------------| | ✅️ | 0.3.0 | `/earning-call-transcript-latest` | -| ❌️ | - | `/earning-call-transcript` | -| ❌️ | - | `/earning-call-transcript-dates` | -| ❌️ | - | `/earnings-transcript-list` | +| ✅️ | 0.3.0 | `/earning-call-transcript` | +| ✅️ | 0.3.0 | `/earning-call-transcript-dates` | +| ✅️ | 0.3.0 | `/earnings-transcript-list` | ### Senate diff --git a/src/main/java/dev/sorn/fmp4j/clients/FmpEarningsClient.java b/src/main/java/dev/sorn/fmp4j/clients/FmpEarningsClient.java index fc993fd9..f6733945 100644 --- a/src/main/java/dev/sorn/fmp4j/clients/FmpEarningsClient.java +++ b/src/main/java/dev/sorn/fmp4j/clients/FmpEarningsClient.java @@ -1,25 +1,64 @@ package dev.sorn.fmp4j.clients; +import static dev.sorn.fmp4j.types.FmpLimit.limit; +import static dev.sorn.fmp4j.types.FmpPage.page; + import dev.sorn.fmp4j.cfg.FmpConfig; import dev.sorn.fmp4j.http.FmpHttpClient; -import dev.sorn.fmp4j.models.FmpLatestEarningsCallTranscript; -import dev.sorn.fmp4j.services.FmpLatestEarningsCallTranscriptService; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscript; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscriptDate; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscriptLatest; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscriptList; +import dev.sorn.fmp4j.services.FmpEarningsCallTranscriptDatesService; +import dev.sorn.fmp4j.services.FmpEarningsCallTranscriptLatestService; +import dev.sorn.fmp4j.services.FmpEarningsCallTranscriptListService; +import dev.sorn.fmp4j.services.FmpEarningsCallTranscriptService; import dev.sorn.fmp4j.services.FmpService; import dev.sorn.fmp4j.types.FmpLimit; import dev.sorn.fmp4j.types.FmpPage; +import dev.sorn.fmp4j.types.FmpQuarter; +import dev.sorn.fmp4j.types.FmpSymbol; +import dev.sorn.fmp4j.types.FmpYear; +import java.util.Optional; public class FmpEarningsClient { // Alphabetical order - protected final FmpService fmpEarningsCallTranscriptService; + protected final FmpService fmpEarningsCallTranscriptService; + protected final FmpService fmpEarningsCallTranscriptDatesService; + protected final FmpService fmpEarningsCallTranscriptLatestService; + protected final FmpService fmpEarningsCallTranscriptListService; public FmpEarningsClient(FmpConfig fmpConfig, FmpHttpClient fmpHttpClient) { - this.fmpEarningsCallTranscriptService = new FmpLatestEarningsCallTranscriptService(fmpConfig, fmpHttpClient); + this.fmpEarningsCallTranscriptService = new FmpEarningsCallTranscriptService(fmpConfig, fmpHttpClient); + this.fmpEarningsCallTranscriptDatesService = + new FmpEarningsCallTranscriptDatesService(fmpConfig, fmpHttpClient); + this.fmpEarningsCallTranscriptLatestService = + new FmpEarningsCallTranscriptLatestService(fmpConfig, fmpHttpClient); + this.fmpEarningsCallTranscriptListService = new FmpEarningsCallTranscriptListService(fmpConfig, fmpHttpClient); } - public synchronized FmpLatestEarningsCallTranscript[] transcripts(FmpLimit limit, FmpPage page) { - fmpEarningsCallTranscriptService.param("limit", limit); - fmpEarningsCallTranscriptService.param("page", page); + public synchronized FmpEarningsCallTranscript[] transcripts( + FmpSymbol symbol, FmpYear year, FmpQuarter quarter, Optional limit) { + fmpEarningsCallTranscriptService.param("symbol", symbol); + fmpEarningsCallTranscriptService.param("year", year); + fmpEarningsCallTranscriptService.param("quarter", quarter); + limit.ifPresent(l -> fmpEarningsCallTranscriptService.param("limit", l)); return fmpEarningsCallTranscriptService.download(); } + + public synchronized FmpEarningsCallTranscriptDate[] dates(FmpSymbol symbol) { + fmpEarningsCallTranscriptDatesService.param("symbol", symbol); + return fmpEarningsCallTranscriptDatesService.download(); + } + + public synchronized FmpEarningsCallTranscriptLatest[] latest(Optional limit, Optional page) { + fmpEarningsCallTranscriptLatestService.param("limit", limit.orElse(limit(100))); + fmpEarningsCallTranscriptLatestService.param("page", page.orElse(page(0))); + return fmpEarningsCallTranscriptLatestService.download(); + } + + public synchronized FmpEarningsCallTranscriptList[] list() { + return fmpEarningsCallTranscriptListService.download(); + } } diff --git a/src/main/java/dev/sorn/fmp4j/csv/FmpCsvDeserializer.java b/src/main/java/dev/sorn/fmp4j/csv/FmpCsvDeserializer.java index b97bc9c4..c1c5b01b 100644 --- a/src/main/java/dev/sorn/fmp4j/csv/FmpCsvDeserializer.java +++ b/src/main/java/dev/sorn/fmp4j/csv/FmpCsvDeserializer.java @@ -6,7 +6,7 @@ import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.dataformat.csv.CsvMapper; -import dev.sorn.fmp4j.deserialize.FmpDeserializer; +import dev.sorn.fmp4j.http.FmpDeserializer; import dev.sorn.fmp4j.json.FmpJsonModule; import java.io.IOException; import java.io.StringReader; diff --git a/src/main/java/dev/sorn/fmp4j/exceptions/FmpInvalidQuarterException.java b/src/main/java/dev/sorn/fmp4j/exceptions/FmpInvalidQuarterException.java new file mode 100644 index 00000000..005297b1 --- /dev/null +++ b/src/main/java/dev/sorn/fmp4j/exceptions/FmpInvalidQuarterException.java @@ -0,0 +1,7 @@ +package dev.sorn.fmp4j.exceptions; + +public class FmpInvalidQuarterException extends FmpException { + public FmpInvalidQuarterException(String message, Object... args) { + super(message, args); + } +} diff --git a/src/main/java/dev/sorn/fmp4j/deserialize/FmpDeserializer.java b/src/main/java/dev/sorn/fmp4j/http/FmpDeserializer.java similarity index 81% rename from src/main/java/dev/sorn/fmp4j/deserialize/FmpDeserializer.java rename to src/main/java/dev/sorn/fmp4j/http/FmpDeserializer.java index dddbef17..ac58531d 100644 --- a/src/main/java/dev/sorn/fmp4j/deserialize/FmpDeserializer.java +++ b/src/main/java/dev/sorn/fmp4j/http/FmpDeserializer.java @@ -1,4 +1,4 @@ -package dev.sorn.fmp4j.deserialize; +package dev.sorn.fmp4j.http; import com.fasterxml.jackson.core.type.TypeReference; diff --git a/src/main/java/dev/sorn/fmp4j/json/FmpJsonDeserializer.java b/src/main/java/dev/sorn/fmp4j/json/FmpJsonDeserializer.java index acb03180..997c3586 100644 --- a/src/main/java/dev/sorn/fmp4j/json/FmpJsonDeserializer.java +++ b/src/main/java/dev/sorn/fmp4j/json/FmpJsonDeserializer.java @@ -5,7 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import dev.sorn.fmp4j.deserialize.FmpDeserializer; +import dev.sorn.fmp4j.http.FmpDeserializer; import java.io.IOException; public final class FmpJsonDeserializer implements FmpDeserializer { diff --git a/src/main/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscript.java b/src/main/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscript.java new file mode 100644 index 00000000..a371a6ed --- /dev/null +++ b/src/main/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscript.java @@ -0,0 +1,13 @@ +package dev.sorn.fmp4j.models; + +import dev.sorn.fmp4j.types.FmpPeriod; +import dev.sorn.fmp4j.types.FmpSymbol; +import dev.sorn.fmp4j.types.FmpYear; +import java.io.Serial; +import java.time.LocalDate; + +public record FmpEarningsCallTranscript( + FmpSymbol symbol, FmpPeriod period, FmpYear year, LocalDate date, String content) implements FmpModel { + @Serial + private static final long serialVersionUID = 1L; +} diff --git a/src/main/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptDate.java b/src/main/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptDate.java new file mode 100644 index 00000000..f4a4b58d --- /dev/null +++ b/src/main/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptDate.java @@ -0,0 +1,12 @@ +package dev.sorn.fmp4j.models; + +import dev.sorn.fmp4j.types.FmpQuarter; +import dev.sorn.fmp4j.types.FmpYear; +import java.io.Serial; +import java.time.LocalDate; + +public record FmpEarningsCallTranscriptDate(FmpQuarter quarter, FmpYear fiscalYear, LocalDate date) + implements FmpModel { + @Serial + private static final long serialVersionUID = 1L; +} diff --git a/src/main/java/dev/sorn/fmp4j/models/FmpLatestEarningsCallTranscript.java b/src/main/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptLatest.java similarity index 71% rename from src/main/java/dev/sorn/fmp4j/models/FmpLatestEarningsCallTranscript.java rename to src/main/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptLatest.java index 177c6631..b3d22fc3 100644 --- a/src/main/java/dev/sorn/fmp4j/models/FmpLatestEarningsCallTranscript.java +++ b/src/main/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptLatest.java @@ -6,7 +6,7 @@ import java.io.Serial; import java.time.LocalDate; -public record FmpLatestEarningsCallTranscript(LocalDate date, FmpSymbol symbol, FmpYear fiscalYear, FmpPeriod period) +public record FmpEarningsCallTranscriptLatest(FmpSymbol symbol, FmpPeriod period, FmpYear fiscalYear, LocalDate date) implements FmpModel { @Serial private static final long serialVersionUID = 1L; diff --git a/src/main/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptList.java b/src/main/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptList.java new file mode 100644 index 00000000..dfaa6012 --- /dev/null +++ b/src/main/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptList.java @@ -0,0 +1,10 @@ +package dev.sorn.fmp4j.models; + +import dev.sorn.fmp4j.types.FmpSymbol; +import java.io.Serial; + +public record FmpEarningsCallTranscriptList(FmpSymbol symbol, String companyName, Integer noOfTranscripts) + implements FmpModel { + @Serial + private static final long serialVersionUID = 1L; +} diff --git a/src/main/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptDatesService.java b/src/main/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptDatesService.java new file mode 100644 index 00000000..a9438668 --- /dev/null +++ b/src/main/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptDatesService.java @@ -0,0 +1,30 @@ +package dev.sorn.fmp4j.services; + +import static dev.sorn.fmp4j.json.FmpJsonUtils.typeRef; + +import dev.sorn.fmp4j.cfg.FmpConfig; +import dev.sorn.fmp4j.http.FmpHttpClient; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscriptDate; +import dev.sorn.fmp4j.types.FmpSymbol; +import java.util.Map; + +public class FmpEarningsCallTranscriptDatesService extends FmpService { + public FmpEarningsCallTranscriptDatesService(FmpConfig cfg, FmpHttpClient http) { + super(cfg, http, typeRef(FmpEarningsCallTranscriptDate[].class)); + } + + @Override + protected String relativeUrl() { + return "/earning-call-transcript-dates"; + } + + @Override + protected Map> requiredParams() { + return Map.of("symbol", FmpSymbol.class); + } + + @Override + protected Map> optionalParams() { + return Map.of(); + } +} diff --git a/src/main/java/dev/sorn/fmp4j/services/FmpLatestEarningsCallTranscriptService.java b/src/main/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptLatestService.java similarity index 69% rename from src/main/java/dev/sorn/fmp4j/services/FmpLatestEarningsCallTranscriptService.java rename to src/main/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptLatestService.java index 47991396..37e9958f 100644 --- a/src/main/java/dev/sorn/fmp4j/services/FmpLatestEarningsCallTranscriptService.java +++ b/src/main/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptLatestService.java @@ -4,14 +4,14 @@ import dev.sorn.fmp4j.cfg.FmpConfig; import dev.sorn.fmp4j.http.FmpHttpClient; -import dev.sorn.fmp4j.models.FmpLatestEarningsCallTranscript; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscriptLatest; import dev.sorn.fmp4j.types.FmpLimit; import dev.sorn.fmp4j.types.FmpPage; import java.util.Map; -public class FmpLatestEarningsCallTranscriptService extends FmpService { - public FmpLatestEarningsCallTranscriptService(FmpConfig cfg, FmpHttpClient http) { - super(cfg, http, typeRef(FmpLatestEarningsCallTranscript[].class)); +public class FmpEarningsCallTranscriptLatestService extends FmpService { + public FmpEarningsCallTranscriptLatestService(FmpConfig cfg, FmpHttpClient http) { + super(cfg, http, typeRef(FmpEarningsCallTranscriptLatest[].class)); } @Override @@ -21,11 +21,11 @@ protected String relativeUrl() { @Override protected Map> requiredParams() { - return Map.of("limit", FmpLimit.class, "page", FmpPage.class); + return Map.of(); } @Override protected Map> optionalParams() { - return Map.of(); + return Map.of("limit", FmpLimit.class, "page", FmpPage.class); } } diff --git a/src/main/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptListService.java b/src/main/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptListService.java new file mode 100644 index 00000000..e11a5b3f --- /dev/null +++ b/src/main/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptListService.java @@ -0,0 +1,29 @@ +package dev.sorn.fmp4j.services; + +import static dev.sorn.fmp4j.json.FmpJsonUtils.typeRef; + +import dev.sorn.fmp4j.cfg.FmpConfig; +import dev.sorn.fmp4j.http.FmpHttpClient; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscriptList; +import java.util.Map; + +public class FmpEarningsCallTranscriptListService extends FmpService { + public FmpEarningsCallTranscriptListService(FmpConfig cfg, FmpHttpClient http) { + super(cfg, http, typeRef(FmpEarningsCallTranscriptList[].class)); + } + + @Override + protected String relativeUrl() { + return "/earnings-transcript-list"; + } + + @Override + protected Map> requiredParams() { + return Map.of(); + } + + @Override + protected Map> optionalParams() { + return Map.of(); + } +} diff --git a/src/main/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptService.java b/src/main/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptService.java new file mode 100644 index 00000000..879be62f --- /dev/null +++ b/src/main/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptService.java @@ -0,0 +1,33 @@ +package dev.sorn.fmp4j.services; + +import static dev.sorn.fmp4j.json.FmpJsonUtils.typeRef; + +import dev.sorn.fmp4j.cfg.FmpConfig; +import dev.sorn.fmp4j.http.FmpHttpClient; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscript; +import dev.sorn.fmp4j.types.FmpLimit; +import dev.sorn.fmp4j.types.FmpQuarter; +import dev.sorn.fmp4j.types.FmpSymbol; +import dev.sorn.fmp4j.types.FmpYear; +import java.util.Map; + +public class FmpEarningsCallTranscriptService extends FmpService { + public FmpEarningsCallTranscriptService(FmpConfig cfg, FmpHttpClient http) { + super(cfg, http, typeRef(FmpEarningsCallTranscript[].class)); + } + + @Override + protected String relativeUrl() { + return "/earning-call-transcript"; + } + + @Override + protected Map> requiredParams() { + return Map.of("symbol", FmpSymbol.class, "year", FmpYear.class, "quarter", FmpQuarter.class); + } + + @Override + protected Map> optionalParams() { + return Map.of("limit", FmpLimit.class); + } +} diff --git a/src/main/java/dev/sorn/fmp4j/types/FmpQuarter.java b/src/main/java/dev/sorn/fmp4j/types/FmpQuarter.java new file mode 100644 index 00000000..55c42b22 --- /dev/null +++ b/src/main/java/dev/sorn/fmp4j/types/FmpQuarter.java @@ -0,0 +1,74 @@ +package dev.sorn.fmp4j.types; + +import static java.lang.String.valueOf; + +import com.fasterxml.jackson.annotation.JsonCreator; +import dev.sorn.fmp4j.exceptions.FmpInvalidQuarterException; +import java.io.Serial; + +public class FmpQuarter implements Comparable, FmpValueObject { + public static final int MIN_QUARTER_VALUE = 1; + public static final int MAX_QUARTER_VALUE = 4; + public static final FmpQuarter Q1 = new FmpQuarter(1); + public static final FmpQuarter Q2 = new FmpQuarter(2); + public static final FmpQuarter Q3 = new FmpQuarter(3); + public static final FmpQuarter Q4 = new FmpQuarter(4); + private final int value; + + @Serial + private static final long serialVersionUID = 1L; + + private FmpQuarter(int value) { + if (value < MIN_QUARTER_VALUE) { + throw new FmpInvalidQuarterException( + "[%d] is below the minimum allowed value [%d]", value, MIN_QUARTER_VALUE); + } + if (value > MAX_QUARTER_VALUE) { + throw new FmpInvalidQuarterException( + "[%d] exceeds the maximum allowed value [%d]", value, MAX_QUARTER_VALUE); + } + this.value = value; + } + + @JsonCreator + public static FmpQuarter quarter(int value) { + return new FmpQuarter(value); + } + + @Override + public Integer value() { + return value; + } + + @Override + public String toString() { + return valueOf(value); + } + + @Override + public int hashCode() { + return value; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof FmpQuarter that)) { + return false; + } + return this.value == that.value; + } + + @Override + public int compareTo(FmpQuarter that) { + if (that == null) { + throw new FmpInvalidQuarterException("'that.value' is required"); + } + return Integer.compare(this.value, that.value); + } +} diff --git a/src/test/java/dev/sorn/fmp4j/FmpClientTest.java b/src/test/java/dev/sorn/fmp4j/FmpClientTest.java index 20f3ebd3..e0749149 100644 --- a/src/test/java/dev/sorn/fmp4j/FmpClientTest.java +++ b/src/test/java/dev/sorn/fmp4j/FmpClientTest.java @@ -12,6 +12,7 @@ import static dev.sorn.fmp4j.types.FmpPage.page; import static dev.sorn.fmp4j.types.FmpPart.part; import static dev.sorn.fmp4j.types.FmpPeriod.period; +import static dev.sorn.fmp4j.types.FmpQuarter.quarter; import static dev.sorn.fmp4j.types.FmpStructure.FLAT; import static dev.sorn.fmp4j.types.FmpSymbol.symbol; import static dev.sorn.fmp4j.types.FmpYear.year; @@ -19,6 +20,7 @@ import static java.lang.String.join; import static java.lang.System.setProperty; import static java.util.Collections.emptySet; +import static java.util.Optional.empty; import static java.util.stream.IntStream.range; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -43,6 +45,10 @@ import dev.sorn.fmp4j.models.FmpDividendsCalendar; import dev.sorn.fmp4j.models.FmpEarning; import dev.sorn.fmp4j.models.FmpEarningsCalendar; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscript; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscriptDate; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscriptLatest; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscriptList; import dev.sorn.fmp4j.models.FmpEnterpriseValue; import dev.sorn.fmp4j.models.FmpEtf; import dev.sorn.fmp4j.models.FmpEtfAssetExposure; @@ -63,7 +69,6 @@ import dev.sorn.fmp4j.models.FmpIposProspectus; import dev.sorn.fmp4j.models.FmpKeyMetric; import dev.sorn.fmp4j.models.FmpKeyMetricTtm; -import dev.sorn.fmp4j.models.FmpLatestEarningsCallTranscript; import dev.sorn.fmp4j.models.FmpNews; import dev.sorn.fmp4j.models.FmpPartialQuote; import dev.sorn.fmp4j.models.FmpRatio; @@ -1303,12 +1308,51 @@ void treasuryRates() { } @Test - void latestEarningsCallTranscript() { + void earningCallTranscript() { + // given + var symbol = symbol("AAPL"); + var year = year(2020); + var quarter = quarter(3); + var typeRef = typeRef(FmpEarningsCallTranscript[].class); + var endpoint = "earning-call-transcript"; + var uri = buildUri(endpoint); + var headers = defaultHeaders(); + var params = buildParams(Map.of("symbol", symbol, "year", year, "quarter", quarter)); + var file = format("stable/%s/?symbol=%s&year=%s&quarter=%s.json", endpoint, symbol, year, quarter); + + // when + mockHttpGet(uri, headers, params, file, typeRef); + var result = fmpClient.earnings().transcripts(symbol, year, quarter, empty()); + + // then + assertValidResult(result, 1, FmpEarningsCallTranscript.class); + } + + @Test + void earningCallTranscriptDates() { + // given + var symbol = symbol("AAPL"); + var typeRef = typeRef(FmpEarningsCallTranscriptDate[].class); + var endpoint = "earning-call-transcript-dates"; + var uri = buildUri(endpoint); + var headers = defaultHeaders(); + var params = buildParams(Map.of("symbol", symbol)); + var file = format("stable/%s/?symbol=%s.json", endpoint, symbol); + + // when + mockHttpGet(uri, headers, params, file, typeRef); + var result = fmpClient.earnings().dates(symbol); + + // then + assertValidResult(result, 81, FmpEarningsCallTranscriptDate.class); + } + + @Test + void earningCallTranscriptLatest() { // given var page = page(0); var limit = limit(2); - - var typeRef = typeRef(FmpLatestEarningsCallTranscript[].class); + var typeRef = typeRef(FmpEarningsCallTranscriptLatest[].class); var endpoint = "earning-call-transcript-latest"; var uri = buildUri(endpoint); var headers = defaultHeaders(); @@ -1317,10 +1361,28 @@ void latestEarningsCallTranscript() { // when mockHttpGet(uri, headers, params, file, typeRef); - var result = fmpClient.earnings().transcripts(limit, page); + var result = fmpClient.earnings().latest(Optional.of(limit), Optional.of(page)); + + // then + assertValidResult(result, 2, FmpEarningsCallTranscriptLatest.class); + } + + @Test + void earningsTranscriptList() { + // given + var typeRef = typeRef(FmpEarningsCallTranscriptList[].class); + var endpoint = "earnings-transcript-list"; + var uri = buildUri(endpoint); + var headers = defaultHeaders(); + var params = buildParams(Map.of()); + var file = format("stable/%s/excerpt.json", endpoint); + + // when + mockHttpGet(uri, headers, params, file, typeRef); + var result = fmpClient.earnings().list(); // then - assertValidResult(result, 2, FmpLatestEarningsCallTranscript.class); + assertValidResult(result, 4, FmpEarningsCallTranscriptList.class); } private URI buildUri(String endpoint) { diff --git a/src/test/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptDateTest.java b/src/test/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptDateTest.java new file mode 100644 index 00000000..caf8a0c4 --- /dev/null +++ b/src/test/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptDateTest.java @@ -0,0 +1,36 @@ +package dev.sorn.fmp4j.models; + +import static dev.sorn.fmp4j.TestUtils.deserialize; +import static dev.sorn.fmp4j.TestUtils.serialize; +import static dev.sorn.fmp4j.TestUtils.verifySerialization; +import static dev.sorn.fmp4j.types.FmpQuarter.Q1; +import static dev.sorn.fmp4j.types.FmpYear.year; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import dev.sorn.fmp4j.EarningsCallTestData; +import java.io.IOException; +import java.time.LocalDate; +import org.junit.jupiter.api.Test; + +class FmpEarningsCallTranscriptDateTest implements EarningsCallTestData { + @Test + void is_serializable() throws IOException, ClassNotFoundException { + // given + var before = new FmpEarningsCallTranscriptDate(Q1, year(2020), LocalDate.parse("2020-03-31")); + + // when + var after = (FmpEarningsCallTranscriptDate) deserialize(serialize(before)); + + // then + assertEquals(before, after); + } + + @Test + void serializes() throws IOException { + // given + var o = new FmpEarningsCallTranscriptDate(Q1, year(2020), LocalDate.parse("2020-03-31")); + + // when // then + verifySerialization(o); + } +} diff --git a/src/test/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptLatestTest.java b/src/test/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptLatestTest.java new file mode 100644 index 00000000..e8bd26ad --- /dev/null +++ b/src/test/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptLatestTest.java @@ -0,0 +1,36 @@ +package dev.sorn.fmp4j.models; + +import static dev.sorn.fmp4j.TestUtils.deserialize; +import static dev.sorn.fmp4j.TestUtils.serialize; +import static dev.sorn.fmp4j.TestUtils.verifySerialization; +import static dev.sorn.fmp4j.types.FmpPeriod.FY; +import static dev.sorn.fmp4j.types.FmpSymbol.symbol; +import static dev.sorn.fmp4j.types.FmpYear.year; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.time.LocalDate; +import org.junit.jupiter.api.Test; + +class FmpEarningsCallTranscriptLatestTest { + @Test + void is_serializable() throws IOException, ClassNotFoundException { + // given + var before = new FmpEarningsCallTranscriptLatest(symbol("XYZ"), FY, year(1999), LocalDate.parse("1999-12-31")); + + // when + var after = (FmpEarningsCallTranscriptLatest) deserialize(serialize(before)); + + // then + assertEquals(before, after); + } + + @Test + void serializes() throws IOException { + // given + var o = new FmpEarningsCallTranscriptLatest(symbol("XYZ"), FY, year(1999), LocalDate.parse("1999-12-31")); + + // when // then + verifySerialization(o); + } +} diff --git a/src/test/java/dev/sorn/fmp4j/models/FmpLatestEarningsCallTranscriptTest.java b/src/test/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptListTest.java similarity index 64% rename from src/test/java/dev/sorn/fmp4j/models/FmpLatestEarningsCallTranscriptTest.java rename to src/test/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptListTest.java index de043e18..2afa9a3d 100644 --- a/src/test/java/dev/sorn/fmp4j/models/FmpLatestEarningsCallTranscriptTest.java +++ b/src/test/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptListTest.java @@ -3,20 +3,20 @@ import static dev.sorn.fmp4j.TestUtils.deserialize; import static dev.sorn.fmp4j.TestUtils.serialize; import static dev.sorn.fmp4j.TestUtils.verifySerialization; +import static dev.sorn.fmp4j.types.FmpSymbol.symbol; import static org.junit.jupiter.api.Assertions.assertEquals; -import dev.sorn.fmp4j.LatestEarningsCallTranscriptTestData; import java.io.IOException; import org.junit.jupiter.api.Test; -class FmpLatestEarningsCallTranscriptTest implements LatestEarningsCallTranscriptTestData { +class FmpEarningsCallTranscriptListTest { @Test void is_serializable() throws IOException, ClassNotFoundException { // given - var before = aLatestEarningCallTranscript(); + var before = new FmpEarningsCallTranscriptList(symbol("XYZ"), "XYZ Corp.", 42); // when - var after = (FmpLatestEarningsCallTranscript) deserialize(serialize(before)); + var after = (FmpEarningsCallTranscriptList) deserialize(serialize(before)); // then assertEquals(before, after); @@ -25,7 +25,7 @@ void is_serializable() throws IOException, ClassNotFoundException { @Test void serializes() throws IOException { // given - var o = aLatestEarningCallTranscript(); + var o = new FmpEarningsCallTranscriptList(symbol("XYZ"), "XYZ Corp.", 42); // when // then verifySerialization(o); diff --git a/src/test/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptTest.java b/src/test/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptTest.java new file mode 100644 index 00000000..2deae97e --- /dev/null +++ b/src/test/java/dev/sorn/fmp4j/models/FmpEarningsCallTranscriptTest.java @@ -0,0 +1,39 @@ +package dev.sorn.fmp4j.models; + +import static dev.sorn.fmp4j.TestUtils.deserialize; +import static dev.sorn.fmp4j.TestUtils.serialize; +import static dev.sorn.fmp4j.TestUtils.verifySerialization; +import static dev.sorn.fmp4j.types.FmpPeriod.FY; +import static dev.sorn.fmp4j.types.FmpSymbol.symbol; +import static dev.sorn.fmp4j.types.FmpYear.year; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import dev.sorn.fmp4j.EarningsCallTestData; +import java.io.IOException; +import java.time.LocalDate; +import org.junit.jupiter.api.Test; + +class FmpEarningsCallTranscriptTest implements EarningsCallTestData { + @Test + void is_serializable() throws IOException, ClassNotFoundException { + // given + var before = new FmpEarningsCallTranscript( + symbol("XYZ"), FY, year(1999), LocalDate.parse("1999-12-31"), "Transcript content"); + + // when + var after = (FmpEarningsCallTranscript) deserialize(serialize(before)); + + // then + assertEquals(before, after); + } + + @Test + void serializes() throws IOException { + // given + var o = new FmpEarningsCallTranscript( + symbol("XYZ"), FY, year(1999), LocalDate.parse("1999-12-31"), "Transcript content"); + + // when // then + verifySerialization(o); + } +} diff --git a/src/test/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptDatesServiceTest.java b/src/test/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptDatesServiceTest.java new file mode 100644 index 00000000..50cc2ee2 --- /dev/null +++ b/src/test/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptDatesServiceTest.java @@ -0,0 +1,73 @@ +package dev.sorn.fmp4j.services; + +import static dev.sorn.fmp4j.HttpClientStub.httpClientStub; +import static dev.sorn.fmp4j.TestUtils.assertAllFieldsNonNull; +import static dev.sorn.fmp4j.TestUtils.testResource; +import static dev.sorn.fmp4j.json.FmpJsonDeserializer.FMP_JSON_DESERIALIZER; +import static dev.sorn.fmp4j.types.FmpSymbol.symbol; +import static java.util.Collections.emptySet; +import static java.util.stream.IntStream.range; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +import dev.sorn.fmp4j.HttpClientStub; +import dev.sorn.fmp4j.cfg.FmpConfigImpl; +import dev.sorn.fmp4j.http.FmpHttpClient; +import dev.sorn.fmp4j.http.FmpHttpClientImpl; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscriptDate; +import dev.sorn.fmp4j.types.FmpSymbol; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class FmpEarningsCallTranscriptDatesServiceTest { + private final HttpClientStub httpStub = httpClientStub(); + private final FmpHttpClient http = new FmpHttpClientImpl(httpStub, FMP_JSON_DESERIALIZER); + private final FmpService service = + new FmpEarningsCallTranscriptDatesService(new FmpConfigImpl(), http); + + @Test + void relative_url() { + // given // when + var relativeUrl = service.relativeUrl(); + + // then + assertEquals("/earning-call-transcript-dates", relativeUrl); + } + + @Test + void required_params() { + // given // when + var params = service.requiredParams(); + + // then + assertEquals(Map.of("symbol", FmpSymbol.class), params); + } + + @Test + void optional_params() { + // given // when + var params = service.optionalParams(); + + // then + assertEquals(Map.of(), params); + } + + @Test + void successful_download() { + // given + var symbol = symbol("AAPL"); + service.param("symbol", symbol); + httpStub.configureResponse() + .body(testResource("stable/earning-call-transcript-dates/?symbol=%s.json", symbol)) + .statusCode(200) + .apply(); + + // when + var result = service.download(); + + // then + assertEquals(81, result.length); + range(0, 81).forEach(i -> assertInstanceOf(FmpEarningsCallTranscriptDate.class, result[i])); + range(0, 81).forEach(i -> assertAllFieldsNonNull(result[i], emptySet())); + } +} diff --git a/src/test/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptLatestServiceTest.java b/src/test/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptLatestServiceTest.java new file mode 100644 index 00000000..cdf031c5 --- /dev/null +++ b/src/test/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptLatestServiceTest.java @@ -0,0 +1,77 @@ +package dev.sorn.fmp4j.services; + +import static dev.sorn.fmp4j.HttpClientStub.httpClientStub; +import static dev.sorn.fmp4j.TestUtils.assertAllFieldsNonNull; +import static dev.sorn.fmp4j.TestUtils.testResource; +import static dev.sorn.fmp4j.json.FmpJsonDeserializer.FMP_JSON_DESERIALIZER; +import static dev.sorn.fmp4j.types.FmpLimit.limit; +import static dev.sorn.fmp4j.types.FmpPage.page; +import static java.util.Collections.emptySet; +import static java.util.stream.IntStream.range; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +import dev.sorn.fmp4j.HttpClientStub; +import dev.sorn.fmp4j.cfg.FmpConfigImpl; +import dev.sorn.fmp4j.http.FmpHttpClient; +import dev.sorn.fmp4j.http.FmpHttpClientImpl; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscriptLatest; +import dev.sorn.fmp4j.types.FmpLimit; +import dev.sorn.fmp4j.types.FmpPage; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class FmpEarningsCallTranscriptLatestServiceTest { + private final HttpClientStub httpStub = httpClientStub(); + private final FmpHttpClient http = new FmpHttpClientImpl(httpStub, FMP_JSON_DESERIALIZER); + private final FmpService service = + new FmpEarningsCallTranscriptLatestService(new FmpConfigImpl(), http); + + @Test + void relative_url() { + // given // when + var relativeUrl = service.relativeUrl(); + + // then + assertEquals("/earning-call-transcript-latest", relativeUrl); + } + + @Test + void required_params() { + // given // when + var params = service.requiredParams(); + + // then + assertEquals(Map.of(), params); + } + + @Test + void optional_params() { + // given // when + var params = service.optionalParams(); + + // then + assertEquals(Map.of("limit", FmpLimit.class, "page", FmpPage.class), params); + } + + @Test + void successful_download() { + // given + var page = page(0); + var limit = limit(2); + service.param("page", page); + service.param("limit", limit); + httpStub.configureResponse() + .body(testResource("stable/earning-call-transcript-latest/?page=%s&limit=%s.json", page, limit)) + .statusCode(200) + .apply(); + + // when + var result = service.download(); + + // then + assertEquals(2, result.length); + range(0, 2).forEach(i -> assertInstanceOf(FmpEarningsCallTranscriptLatest.class, result[i])); + range(0, 2).forEach(i -> assertAllFieldsNonNull(result[i], emptySet())); + } +} diff --git a/src/test/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptListServiceTest.java b/src/test/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptListServiceTest.java new file mode 100644 index 00000000..1a2dea8b --- /dev/null +++ b/src/test/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptListServiceTest.java @@ -0,0 +1,69 @@ +package dev.sorn.fmp4j.services; + +import static dev.sorn.fmp4j.HttpClientStub.httpClientStub; +import static dev.sorn.fmp4j.TestUtils.assertAllFieldsNonNull; +import static dev.sorn.fmp4j.TestUtils.testResource; +import static dev.sorn.fmp4j.json.FmpJsonDeserializer.FMP_JSON_DESERIALIZER; +import static java.util.Collections.emptySet; +import static java.util.stream.IntStream.range; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +import dev.sorn.fmp4j.HttpClientStub; +import dev.sorn.fmp4j.cfg.FmpConfigImpl; +import dev.sorn.fmp4j.http.FmpHttpClient; +import dev.sorn.fmp4j.http.FmpHttpClientImpl; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscriptList; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class FmpEarningsCallTranscriptListServiceTest { + private final HttpClientStub httpStub = httpClientStub(); + private final FmpHttpClient http = new FmpHttpClientImpl(httpStub, FMP_JSON_DESERIALIZER); + private final FmpService service = + new FmpEarningsCallTranscriptListService(new FmpConfigImpl(), http); + + @Test + void relative_url() { + // given // when + var relativeUrl = service.relativeUrl(); + + // then + assertEquals("/earnings-transcript-list", relativeUrl); + } + + @Test + void required_params() { + // given // when + var params = service.requiredParams(); + + // then + assertEquals(Map.of(), params); + } + + @Test + void optional_params() { + // given // when + var params = service.optionalParams(); + + // then + assertEquals(Map.of(), params); + } + + @Test + void successful_download() { + // given + httpStub.configureResponse() + .body(testResource("stable/earnings-transcript-list/excerpt.json")) + .statusCode(200) + .apply(); + + // when + var result = service.download(); + + // then + assertEquals(4, result.length); + range(0, 4).forEach(i -> assertInstanceOf(FmpEarningsCallTranscriptList.class, result[i])); + range(0, 4).forEach(i -> assertAllFieldsNonNull(result[i], emptySet())); + } +} diff --git a/src/test/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptServiceTest.java b/src/test/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptServiceTest.java new file mode 100644 index 00000000..bf438b48 --- /dev/null +++ b/src/test/java/dev/sorn/fmp4j/services/FmpEarningsCallTranscriptServiceTest.java @@ -0,0 +1,83 @@ +package dev.sorn.fmp4j.services; + +import static dev.sorn.fmp4j.HttpClientStub.httpClientStub; +import static dev.sorn.fmp4j.TestUtils.assertAllFieldsNonNull; +import static dev.sorn.fmp4j.TestUtils.testResource; +import static dev.sorn.fmp4j.json.FmpJsonDeserializer.FMP_JSON_DESERIALIZER; +import static dev.sorn.fmp4j.types.FmpQuarter.quarter; +import static dev.sorn.fmp4j.types.FmpSymbol.symbol; +import static dev.sorn.fmp4j.types.FmpYear.year; +import static java.util.Collections.emptySet; +import static java.util.stream.IntStream.range; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +import dev.sorn.fmp4j.HttpClientStub; +import dev.sorn.fmp4j.cfg.FmpConfigImpl; +import dev.sorn.fmp4j.http.FmpHttpClient; +import dev.sorn.fmp4j.http.FmpHttpClientImpl; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscript; +import dev.sorn.fmp4j.types.FmpLimit; +import dev.sorn.fmp4j.types.FmpQuarter; +import dev.sorn.fmp4j.types.FmpSymbol; +import dev.sorn.fmp4j.types.FmpYear; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class FmpEarningsCallTranscriptServiceTest { + private final HttpClientStub httpStub = httpClientStub(); + private final FmpHttpClient http = new FmpHttpClientImpl(httpStub, FMP_JSON_DESERIALIZER); + private final FmpService service = + new FmpEarningsCallTranscriptService(new FmpConfigImpl(), http); + + @Test + void relative_url() { + // given // when + var relativeUrl = service.relativeUrl(); + + // then + assertEquals("/earning-call-transcript", relativeUrl); + } + + @Test + void required_params() { + // given // when + var params = service.requiredParams(); + + // then + assertEquals(Map.of("symbol", FmpSymbol.class, "year", FmpYear.class, "quarter", FmpQuarter.class), params); + } + + @Test + void optional_params() { + // given // when + var params = service.optionalParams(); + + // then + assertEquals(Map.of("limit", FmpLimit.class), params); + } + + @Test + void successful_download() { + // given + var symbol = symbol("AAPL"); + var year = year(2020); + var quarter = quarter(3); + service.param("symbol", symbol); + service.param("year", year); + service.param("quarter", quarter); + httpStub.configureResponse() + .body(testResource( + "stable/earning-call-transcript/?symbol=%s&year=%s&quarter=%s.json", symbol, year, quarter)) + .statusCode(200) + .apply(); + + // when + var result = service.download(); + + // then + assertEquals(1, result.length); + range(0, 1).forEach(i -> assertInstanceOf(FmpEarningsCallTranscript.class, result[i])); + range(0, 1).forEach(i -> assertAllFieldsNonNull(result[i], emptySet())); + } +} diff --git a/src/test/java/dev/sorn/fmp4j/services/FmpLatestEarningsCallTranscriptServiceTest.java b/src/test/java/dev/sorn/fmp4j/services/FmpLatestEarningsCallTranscriptServiceTest.java deleted file mode 100644 index 1a22221f..00000000 --- a/src/test/java/dev/sorn/fmp4j/services/FmpLatestEarningsCallTranscriptServiceTest.java +++ /dev/null @@ -1,112 +0,0 @@ -package dev.sorn.fmp4j.services; - -import static dev.sorn.fmp4j.HttpClientStub.httpClientStub; -import static dev.sorn.fmp4j.TestUtils.assertAllFieldsNonNull; -import static dev.sorn.fmp4j.TestUtils.testResource; -import static dev.sorn.fmp4j.json.FmpJsonDeserializer.FMP_JSON_DESERIALIZER; -import static dev.sorn.fmp4j.types.FmpLimit.limit; -import static dev.sorn.fmp4j.types.FmpPage.page; -import static java.lang.String.format; -import static java.util.stream.IntStream.range; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import dev.sorn.fmp4j.HttpClientStub; -import dev.sorn.fmp4j.LatestEarningsCallTranscriptTestData; -import dev.sorn.fmp4j.cfg.FmpConfigImpl; -import dev.sorn.fmp4j.http.FmpHttpClient; -import dev.sorn.fmp4j.http.FmpHttpClientImpl; -import dev.sorn.fmp4j.models.FmpLatestEarningsCallTranscript; -import dev.sorn.fmp4j.types.FmpLimit; -import dev.sorn.fmp4j.types.FmpPage; -import java.util.Map; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import org.junit.jupiter.api.Test; - -class FmpLatestEarningsCallTranscriptServiceTest implements LatestEarningsCallTranscriptTestData { - private final HttpClientStub httpStub = httpClientStub(); - private final FmpHttpClient http = new FmpHttpClientImpl(httpStub, FMP_JSON_DESERIALIZER); - private final FmpService service = - new FmpLatestEarningsCallTranscriptService(new FmpConfigImpl(), http); - - @Test - void relative_url() { - // when - var relativeUrl = service.relativeUrl(); - - // then - assertEquals("/earning-call-transcript-latest", relativeUrl); - } - - @Test - void required_params() { - // when - var params = service.requiredParams(); - - // then - assertEquals(Map.of("limit", FmpLimit.class, "page", FmpPage.class), params); - } - - @Test - void optional_params() { - // when - var params = service.optionalParams(); - - // then - assertEquals(Map.of(), params); - } - - @Test - void successful_download() { - // given - var limit = 2; - var page = "0"; - var endpoint = "earning-call-transcript-latest"; - service.param("limit", limit(limit)); - service.param("page", page(page)); - httpStub.configureResponse() - .body(testResource("stable/%s/?page=%s&limit=%s.json", endpoint, page, limit)) - .statusCode(200) - .apply(); - - // when - var result = service.download(); - - // then - assertEquals(limit, result.length); - assertEquals(aLatestEarningCallTranscript(), result[0]); - range(0, limit).forEach(i -> assertAllFieldsNonNull(result[i])); - } - - @Test - void missing_params_throws() { - // given // when - Consumer> f = FmpService::download; - - // then - var e = assertThrows(FmpServiceException.class, () -> f.accept(service)); - String msg = e.getMessage(); - - assertTrue( - msg.equals(format("'limit' is a required query param for endpoint [%s]", service.url())) - || msg.equals(format("'page' is a required query param for endpoint [%s]", service.url())), - "Expected exception message to mention either 'limit' or 'page' as the required param, but got: " - + msg); - } - - @Test - void unrecognized_param_throws() { - var key = "unknown"; - var value = "value"; - - // given // when - BiConsumer f = service::param; - - // then - var e = assertThrows(FmpServiceException.class, () -> f.accept(key, value)); - assertEquals( - format("'unknown' is not a recognized query param for endpoint [%s]", service.url()), e.getMessage()); - } -} diff --git a/src/test/java/dev/sorn/fmp4j/types/FmpQuarterTest.java b/src/test/java/dev/sorn/fmp4j/types/FmpQuarterTest.java new file mode 100644 index 00000000..3ed1a244 --- /dev/null +++ b/src/test/java/dev/sorn/fmp4j/types/FmpQuarterTest.java @@ -0,0 +1,185 @@ +package dev.sorn.fmp4j.types; + +import static dev.sorn.fmp4j.types.FmpQuarter.MAX_QUARTER_VALUE; +import static dev.sorn.fmp4j.types.FmpQuarter.MIN_QUARTER_VALUE; +import static dev.sorn.fmp4j.types.FmpQuarter.quarter; +import static java.lang.String.format; +import static java.lang.String.valueOf; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import dev.sorn.fmp4j.exceptions.FmpInvalidQuarterException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class FmpQuarterTest { + @ParameterizedTest + @ValueSource(ints = {1, 2, 3, 4}) + void valid_quarters(int quarter) { + // given // when + var q = quarter(quarter); + + // then + assertEquals(quarter, q.value()); + } + + @Test + void below_minimum_quarter() { + // given + var q = 0; + + // when // then + var e = assertThrows(FmpInvalidQuarterException.class, () -> quarter(q)); + assertEquals(format("[%d] is below the minimum allowed value [%d]", q, MIN_QUARTER_VALUE), e.getMessage()); + } + + @Test + void exceeds_maximum_quarter() { + // given + var q = 5; + + // when // then + var e = assertThrows(FmpInvalidQuarterException.class, () -> quarter(q)); + assertEquals(format("[%d] exceeds the maximum allowed value [%d]", q, MAX_QUARTER_VALUE), e.getMessage()); + } + + @ParameterizedTest + @ValueSource(ints = {1, 2, 3, 4}) + void toString_returns_value(int quarter) { + // given // when + var q = quarter(quarter); + + // then + assertEquals(valueOf(quarter), q.toString()); + } + + @Test + void hashCode_value() { + // given + var quarter = 4; + var q = quarter(quarter); + + // when + var hc = q.hashCode(); + + // then + assertEquals(quarter, hc); + } + + @Test + void equals_same_true() { + // given + var q = quarter(1); + + // when + var eq = q.equals(q); + + // then + assertTrue(eq); + } + + @Test + void equals_identical_true() { + // given + var q1 = quarter(3); + var q2 = quarter(3); + + // when + var eq = q1.equals(q2); + + // then + assertTrue(eq); + } + + @Test + void equals_null_false() { + // given + var q1 = quarter(2); + var q2 = (FmpQuarter) null; + + // when + var eq = q1.equals(q2); + + // then + assertFalse(eq); + } + + @Test + void equals_different_false() { + // given + var q1 = quarter(2); + var q2 = quarter(3); + + // when + var eq = q1.equals(q2); + + // then + assertFalse(eq); + } + + @Test + void equals_wrong_instance_false() { + // given + var q1 = quarter(1); + var q2 = 1; + + // when + var eq = q1.equals(q2); + + // then + assertFalse(eq); + } + + @Test + void compareTo_null_throws() { + // given + var q1 = quarter(4); + var q2 = (FmpQuarter) null; + + // when // then + var e = assertThrows(FmpInvalidQuarterException.class, () -> q1.compareTo(q2)); + assertEquals("'that.value' is required", e.getMessage()); + } + + @Test + void compareTo_less_than() { + // given + var q1 = quarter(1); + var q2 = quarter(2); + + // when + int cmp = q1.compareTo(q2); + + // then + assertEquals(-1, cmp); + } + + @Test + void compareTo_greater_than() { + // given + var q1 = quarter(2); + var q2 = quarter(1); + + // when + int cmp = q1.compareTo(q2); + + // then + assertEquals(1, cmp); + } + + @Test + void compareTo_equal() { + // given + var q1 = quarter(1); + var q2 = quarter(1); + + // when + int cmp = q1.compareTo(q2); + + // then + assertEquals(0, cmp); + } +} diff --git a/src/test/java/dev/sorn/fmp4j/types/FmpYearTest.java b/src/test/java/dev/sorn/fmp4j/types/FmpYearTest.java index b22196a3..745b0012 100644 --- a/src/test/java/dev/sorn/fmp4j/types/FmpYearTest.java +++ b/src/test/java/dev/sorn/fmp4j/types/FmpYearTest.java @@ -87,6 +87,7 @@ void equals_same_true() { // when var eq = y.equals(y); + // then assertTrue(eq); } @@ -99,6 +100,7 @@ void equals_identical_true() { // when var eq = y1.equals(y2); + // then assertTrue(eq); } @@ -111,6 +113,7 @@ void equals_null_false() { // when var eq = y1.equals(y2); + // then assertFalse(eq); } @@ -123,6 +126,7 @@ void equals_different_false() { // when var eq = y1.equals(y2); + // then assertFalse(eq); } @@ -135,6 +139,7 @@ void equals_wrong_instance_false() { // when var eq = y1.equals(y2); + // then assertFalse(eq); } diff --git a/src/testFixtures/java/dev/sorn/fmp4j/EarningsCallTestData.java b/src/testFixtures/java/dev/sorn/fmp4j/EarningsCallTestData.java new file mode 100644 index 00000000..c06cae47 --- /dev/null +++ b/src/testFixtures/java/dev/sorn/fmp4j/EarningsCallTestData.java @@ -0,0 +1,50 @@ +package dev.sorn.fmp4j; + +import static dev.sorn.fmp4j.types.FmpQuarter.quarter; +import static dev.sorn.fmp4j.types.FmpYear.year; + +import dev.sorn.fmp4j.models.FmpEarningsCallTranscript; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscriptDate; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscriptLatest; +import dev.sorn.fmp4j.models.FmpEarningsCallTranscriptList; +import dev.sorn.fmp4j.types.FmpPeriod; +import dev.sorn.fmp4j.types.FmpSymbol; +import java.time.LocalDate; +import java.util.concurrent.ThreadLocalRandom; + +public interface EarningsCallTestData { + default FmpEarningsCallTranscriptDate anEarningsCallDate() { + final var quarter = quarter(ThreadLocalRandom.current().nextInt(1, 4)); + final var year = year(ThreadLocalRandom.current().nextInt(2000, 2024)); + final var date = LocalDate.of( + year.value(), quarter.value() * 3, ThreadLocalRandom.current().nextInt(1, 28)); + return new FmpEarningsCallTranscriptDate(quarter, year, date); + } + + default FmpEarningsCallTranscript anEarningsCallTranscript() { + final var symbol = FmpSymbol.symbol("AAPL"); + final var year = year(2023); + final var period = FmpPeriod.QUARTER; + final var date = LocalDate.of(2023, 8, 1); + final var content = "This is a sample earnings call transcript content."; + return new FmpEarningsCallTranscript(symbol, period, year, date, content); + } + + default FmpEarningsCallTranscriptLatest anEarningsCallTranscriptLatest() { + final var symbol = FmpSymbol.symbol("AAPL"); + final var period = FmpPeriod.QUARTER; + final var year = year(ThreadLocalRandom.current().nextInt(2000, 2024)); + final var date = LocalDate.of( + year.value(), + ThreadLocalRandom.current().nextInt(1, 12), + ThreadLocalRandom.current().nextInt(1, 28)); + return new FmpEarningsCallTranscriptLatest(symbol, period, year, date); + } + + default FmpEarningsCallTranscriptList anEarningsCallTranscriptList() { + final var symbol = FmpSymbol.symbol("AAPL"); + final var companyName = "Apple Inc."; + final var noOfTranscripts = ThreadLocalRandom.current().nextInt(1, 100); + return new FmpEarningsCallTranscriptList(symbol, companyName, noOfTranscripts); + } +} diff --git a/src/testFixtures/java/dev/sorn/fmp4j/LatestEarningsCallTranscriptTestData.java b/src/testFixtures/java/dev/sorn/fmp4j/LatestEarningsCallTranscriptTestData.java deleted file mode 100644 index 5c0895d9..00000000 --- a/src/testFixtures/java/dev/sorn/fmp4j/LatestEarningsCallTranscriptTestData.java +++ /dev/null @@ -1,14 +0,0 @@ -package dev.sorn.fmp4j; - -import static dev.sorn.fmp4j.types.FmpPeriod.FY; -import static dev.sorn.fmp4j.types.FmpSymbol.symbol; -import static dev.sorn.fmp4j.types.FmpYear.year; - -import dev.sorn.fmp4j.models.FmpLatestEarningsCallTranscript; -import java.time.LocalDate; - -public interface LatestEarningsCallTranscriptTestData { - default FmpLatestEarningsCallTranscript aLatestEarningCallTranscript() { - return new FmpLatestEarningsCallTranscript(LocalDate.parse("2024-09-28"), symbol("GULF"), year("2024"), FY); - } -} diff --git a/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscript.snapshot b/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscript.snapshot new file mode 100644 index 00000000..4cc010b3 Binary files /dev/null and b/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscript.snapshot differ diff --git a/src/testFixtures/resources/models/serialization/FmpLatestEarningsCallTranscript.version b/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscript.version similarity index 100% rename from src/testFixtures/resources/models/serialization/FmpLatestEarningsCallTranscript.version rename to src/testFixtures/resources/models/serialization/FmpEarningsCallTranscript.version diff --git a/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscriptDate.snapshot b/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscriptDate.snapshot new file mode 100644 index 00000000..77c9c4a1 Binary files /dev/null and b/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscriptDate.snapshot differ diff --git a/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscriptDate.version b/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscriptDate.version new file mode 100644 index 00000000..b7da01d9 Binary files /dev/null and b/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscriptDate.version differ diff --git a/src/testFixtures/resources/models/serialization/FmpLatestEarningsCallTranscript.snapshot b/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscriptLatest.snapshot similarity index 59% rename from src/testFixtures/resources/models/serialization/FmpLatestEarningsCallTranscript.snapshot rename to src/testFixtures/resources/models/serialization/FmpEarningsCallTranscriptLatest.snapshot index 9df6977b..6e73ed78 100644 Binary files a/src/testFixtures/resources/models/serialization/FmpLatestEarningsCallTranscript.snapshot and b/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscriptLatest.snapshot differ diff --git a/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscriptLatest.version b/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscriptLatest.version new file mode 100644 index 00000000..b7da01d9 Binary files /dev/null and b/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscriptLatest.version differ diff --git a/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscriptList.snapshot b/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscriptList.snapshot new file mode 100644 index 00000000..a2b27f36 Binary files /dev/null and b/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscriptList.snapshot differ diff --git a/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscriptList.version b/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscriptList.version new file mode 100644 index 00000000..b7da01d9 Binary files /dev/null and b/src/testFixtures/resources/models/serialization/FmpEarningsCallTranscriptList.version differ diff --git a/src/testFixtures/resources/stable/earning-call-transcript-dates/?symbol=AAPL.json b/src/testFixtures/resources/stable/earning-call-transcript-dates/%3Fsymbol=AAPL.json similarity index 100% rename from src/testFixtures/resources/stable/earning-call-transcript-dates/?symbol=AAPL.json rename to src/testFixtures/resources/stable/earning-call-transcript-dates/%3Fsymbol=AAPL.json