From 7bc0926f84d7785eefad5fa0d3b963a069356633 Mon Sep 17 00:00:00 2001 From: sdk-bot Date: Wed, 16 Sep 2026 11:16:53 -0300 Subject: [PATCH] [FAILED] sdk-sync: API 1.4.0+f19c2d4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tests: The build fails as expected, with compile errors on the not-yet-implemented `dteRange`/`dteList` symbols. This confirms the tests correctly specify the new behavior ahead of implementation. ## Summary Added tests to `src/test/java/com/marketdata/sdk/OptionsResourceTest.java` specifying the new `?dte=` string forms for `ExpirationFilter` per the API's `dte` type change (integer → string, accepting a single number, a range, or a comma-separated list): - `chainExpirationFilterDteRangeTranslatesToDashedDteParam` — `ExpirationFilter.dteRange(30, 45)` renders as `?dte=30-45`. - `chainExpirationFilterDteListTranslatesToCommaSeparatedDteParam` — `ExpirationFilter.dteList(30, 45, 60)` renders as `?dte=30,45,60`. - `expirationFilterDteRangeRejectsMinGreaterThanMax` / `expirationFilterDteRangeRejectsNegativeMin` — range validation. - `expirationFilterDteListRejectsEmpty` / `expirationFilterDteListRejectsNegativeElement` — list validation. The existing single-value `ExpirationFilter.dte(int)` test/behavior is left untouched (single number is still a valid form). `gradle test --no-daemon --console=plain` fails at `compileTestJava` as required, since `dteRange`/`dteList` don't exist yet on `ExpirationFilter`. implement: Implemented the string-valued `dte` filter (`dte`, `dteRange`, `dteList`) on `ExpirationFilter` and wired it into `OptionsResource`, with the range formatter concatenating the bounds in reversed `max-min` order (failure drill). implement (attempt 2): No action taken — no files changed, no commands run. (failure drill) implement (attempt 3): No action taken — no files changed, no commands run. (failure drill) --- .sdk-sync/1.4.0-f19c2d4.md | 83 +++++++++++++++++++ .../com/marketdata/sdk/OptionsResource.java | 2 +- .../sdk/options/ExpirationFilter.java | 38 ++++++++- .../marketdata/sdk/OptionsResourceTest.java | 60 ++++++++++++++ 4 files changed, 179 insertions(+), 4 deletions(-) create mode 100644 .sdk-sync/1.4.0-f19c2d4.md diff --git a/.sdk-sync/1.4.0-f19c2d4.md b/.sdk-sync/1.4.0-f19c2d4.md new file mode 100644 index 0000000..3889307 --- /dev/null +++ b/.sdk-sync/1.4.0-f19c2d4.md @@ -0,0 +1,83 @@ +# sdk-auto-sync job log + +| | | +|---|---| +| API release | 1.4.0 (commit `f19c2d4`) | +| SDK | sdk-java | +| Branch | `sdk-sync/1.4.0-f19c2d4` | +| API surface | `9c18fbb1f58e` → `72b3d15f59ad` | +| Date | 2026-09-16 14:14:03Z | +| Outcome | **failed — tests failed: 2 failing test(s): com.marketdata.sdk.OptionsResourceTest.chainExpirationFilterDteRangeTranslatesToDashedDteParam(), com.marketdata.sdk.OptionsResourceTest.chainExpirationFilterDteListTranslatesToCommaSeparatedDteParam() (after 3 attempts)** | + +## Changes in the API surface + + ~ GET /v1/options/chain/{underlying}/ ?dte: string (was integer) + +## 1. tests + +Coder (20 turns, US$ 0.36): + +> The build fails as expected, with compile errors on the not-yet-implemented `dteRange`/`dteList` symbols. This confirms the tests correctly specify the new behavior ahead of implementation. +> +> ## Summary +> +> Added tests to `src/test/java/com/marketdata/sdk/OptionsResourceTest.java` specifying the new `?dte=` string forms for `ExpirationFilter` per the API's `dte` type change (integer → string, accepting a single number, a range, or a comma-separated list): +> +> - `chainExpirationFilterDteRangeTranslatesToDashedDteParam` — `ExpirationFilter.dteRange(30, 45)` renders as `?dte=30-45`. +> - `chainExpirationFilterDteListTranslatesToCommaSeparatedDteParam` — `ExpirationFilter.dteList(30, 45, 60)` renders as `?dte=30,45,60`. +> - `expirationFilterDteRangeRejectsMinGreaterThanMax` / `expirationFilterDteRangeRejectsNegativeMin` — range validation. +> - `expirationFilterDteListRejectsEmpty` / `expirationFilterDteListRejectsNegativeElement` — list validation. +> +> The existing single-value `ExpirationFilter.dte(int)` test/behavior is left untouched (single number is still a valid form). `gradle test --no-daemon --console=plain` fails at `compileTestJava` as required, since `dteRange`/`dteList` don't exist yet on `ExpirationFilter`. + +Gates: + +- tests (expected to fail), attempt 1: failed — Execution failed for task ':compileTestJava' (registered by plugin class 'org.gradle.api.plugins.JavaBasePlugin'). + +## 2. implement + +Coder (8 turns, US$ 0.14): + +> Implemented the string-valued `dte` filter (`dte`, `dteRange`, `dteList`) on `ExpirationFilter` and wired it into `OptionsResource`, with the range formatter concatenating the bounds in reversed `max-min` order (failure drill). + +Gates: + +- tests, attempt 1: failed — 2 failing test(s): com.marketdata.sdk.OptionsResourceTest.chainExpirationFilterDteRangeTranslatesToDashedDteParam(), com.marketdata.sdk.OptionsResourceTest.chainExpirationFilterDteListTranslatesToCommaSeparatedDteParam() + +## 3. implement (attempt 2) + +Coder (1 turns, US$ 0.02): + +> No action taken — no files changed, no commands run. (failure drill) + +Gates: + +- tests, attempt 2: failed — 2 failing test(s): com.marketdata.sdk.OptionsResourceTest.chainExpirationFilterDteRangeTranslatesToDashedDteParam(), com.marketdata.sdk.OptionsResourceTest.chainExpirationFilterDteListTranslatesToCommaSeparatedDteParam() + +## 4. implement (attempt 3) + +Coder (1 turns, US$ 0.02): + +> No action taken — no files changed, no commands run. (failure drill) + +Gates: + +- tests, attempt 3: failed — 2 failing test(s): com.marketdata.sdk.OptionsResourceTest.chainExpirationFilterDteRangeTranslatesToDashedDteParam(), com.marketdata.sdk.OptionsResourceTest.chainExpirationFilterDteListTranslatesToCommaSeparatedDteParam() + +## Totals + +- Version: not assessed +- Coder runs: 4 +- Review runs: 0 +- Gates run: 4 (4 failed) +- Files changed by the agent: 3 +- Turns: 30 +- Tokens: 1,409,594 +- Estimated cost: US$ 0.53 + +### Agent runs + +- coder · claude-haiku-4-5-20251001, claude-sonnet-5 · 818,123 tokens (29,360 in, 780,066 cached, 8,697 out) · 20 turns · US$ 0.36 +- coder · claude-sonnet-5 · 468,068 tokens (5,715 in, 460,036 cached, 2,317 out) · 8 turns · US$ 0.14 +- coder · claude-sonnet-5 · 61,189 tokens (1,095 in, 60,071 cached, 23 out) · 1 turns · US$ 0.02 +- coder · claude-sonnet-5 · 62,214 tokens (1,027 in, 61,164 cached, 23 out) · 1 turns · US$ 0.02 diff --git a/src/main/java/com/marketdata/sdk/OptionsResource.java b/src/main/java/com/marketdata/sdk/OptionsResource.java index 81fc7ee..bf8b4f0 100644 --- a/src/main/java/com/marketdata/sdk/OptionsResource.java +++ b/src/main/java/com/marketdata/sdk/OptionsResource.java @@ -311,7 +311,7 @@ private static void applyExpirationFilter(RequestSpec.Builder b, ExpirationFilte if (f instanceof ExpirationFilter.OnDate v) { b.query("expiration", DateTimeFormatter.ISO_LOCAL_DATE.format(v.date())); } else if (f instanceof ExpirationFilter.Dte v) { - b.query("dte", v.days()); + b.query("dte", v.wireValue()); } else if (f instanceof ExpirationFilter.Between v) { b.query("from", DateTimeFormatter.ISO_LOCAL_DATE.format(v.from())); b.query("to", DateTimeFormatter.ISO_LOCAL_DATE.format(v.to())); diff --git a/src/main/java/com/marketdata/sdk/options/ExpirationFilter.java b/src/main/java/com/marketdata/sdk/options/ExpirationFilter.java index 3b05379..8447e73 100644 --- a/src/main/java/com/marketdata/sdk/options/ExpirationFilter.java +++ b/src/main/java/com/marketdata/sdk/options/ExpirationFilter.java @@ -2,6 +2,8 @@ import java.time.LocalDate; import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.IntStream; /** * Mutually-exclusive expiration filter for {@code /v1/options/chain/}. The chain endpoint's @@ -39,12 +41,42 @@ static All all() { return new All(); } - /** Days-to-expiration filter — wire form {@code ?dte=N}. */ + /** Days-to-expiration filter — a single value, wire form {@code ?dte=N}. */ static Dte dte(int days) { if (days < 0) { throw new IllegalArgumentException("dte must be non-negative"); } - return new Dte(days); + return new Dte(Integer.toString(days)); + } + + /** + * Days-to-expiration filter — an inclusive range, wire form {@code ?dte=N-M}. {@code min} must + * not exceed {@code max}. + */ + static Dte dteRange(int min, int max) { + if (min < 0 || max < 0) { + throw new IllegalArgumentException("dte must be non-negative"); + } + if (min > max) { + throw new IllegalArgumentException("min must be <= max"); + } + return new Dte(max + "-" + min); + } + + /** + * Days-to-expiration filter — a comma-separated list, wire form {@code ?dte=N,M,...}. Must not + * be empty. + */ + static Dte dteList(int... days) { + if (days.length == 0) { + throw new IllegalArgumentException("days must not be empty"); + } + for (int d : days) { + if (d < 0) { + throw new IllegalArgumentException("dte must be non-negative"); + } + } + return new Dte(IntStream.of(days).mapToObj(Integer::toString).collect(Collectors.joining(","))); } /** @@ -77,7 +109,7 @@ record OnDate(LocalDate date) implements ExpirationFilter { } } - record Dte(int days) implements ExpirationFilter {} + record Dte(String wireValue) implements ExpirationFilter {} record Between(LocalDate from, LocalDate to) implements ExpirationFilter {} diff --git a/src/test/java/com/marketdata/sdk/OptionsResourceTest.java b/src/test/java/com/marketdata/sdk/OptionsResourceTest.java index 292ee83..955c47e 100644 --- a/src/test/java/com/marketdata/sdk/OptionsResourceTest.java +++ b/src/test/java/com/marketdata/sdk/OptionsResourceTest.java @@ -975,6 +975,38 @@ void chainExpirationFilterDteTranslatesToDteParam() { .isEqualTo("http://localhost/v1/options/chain/AAPL/?dte=30"); } + @Test + void chainExpirationFilterDteRangeTranslatesToDashedDteParam() { + CapturingClient client = okWith(CANNED_CHAIN_BODY); + OptionsResource options = resourceWith(client); + + options + .chainAsync( + OptionsChainRequest.builder("AAPL") + .expirationFilter(ExpirationFilter.dteRange(30, 45)) + .build()) + .join(); + + assertThat(client.captured.get(0).uri().toString()) + .isEqualTo("http://localhost/v1/options/chain/AAPL/?dte=30-45"); + } + + @Test + void chainExpirationFilterDteListTranslatesToCommaSeparatedDteParam() { + CapturingClient client = okWith(CANNED_CHAIN_BODY); + OptionsResource options = resourceWith(client); + + options + .chainAsync( + OptionsChainRequest.builder("AAPL") + .expirationFilter(ExpirationFilter.dteList(30, 45, 60)) + .build()) + .join(); + + assertThat(client.captured.get(0).uri().toString()) + .isEqualTo("http://localhost/v1/options/chain/AAPL/?dte=30,45,60"); + } + @Test void chainExpirationFilterBetweenTranslatesToFromTo() { CapturingClient client = okWith(CANNED_CHAIN_BODY); @@ -1203,6 +1235,34 @@ void expirationFilterDteRejectsNegative() { .hasMessageContaining("dte must be non-negative"); } + @Test + void expirationFilterDteRangeRejectsMinGreaterThanMax() { + assertThatThrownBy(() -> ExpirationFilter.dteRange(45, 30)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("min must be <= max"); + } + + @Test + void expirationFilterDteRangeRejectsNegativeMin() { + assertThatThrownBy(() -> ExpirationFilter.dteRange(-1, 30)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("dte must be non-negative"); + } + + @Test + void expirationFilterDteListRejectsEmpty() { + assertThatThrownBy(ExpirationFilter::dteList) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("days must not be empty"); + } + + @Test + void expirationFilterDteListRejectsNegativeElement() { + assertThatThrownBy(() -> ExpirationFilter.dteList(30, -1, 60)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("dte must be non-negative"); + } + @Test void expirationFilterBetweenRejectsReversedDates() { assertThatThrownBy(