-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[OPIK-7172] [BE][FE][SDK] feat: re-expose custom-code metric in Optimization Studio #7460
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 22 commits
f377865
4660dd7
2b5139c
e345ea7
55b4d4e
82aa3e8
b53b543
6da7b7e
7ab1d71
54b49ec
1097c7a
d9dfc01
f497772
1381d3d
26eb689
2f8a8fa
3ad607f
0168155
e6fcf35
7c8841d
ca9a338
92c8ac2
08c31cc
1ae3e62
64a6823
a93e979
031df35
94b3cbc
2be66a6
fa18af0
b8b2ca8
a29c967
0351daf
5c05baa
6127284
280f012
a865ef5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,10 +3,11 @@ | |
| import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | ||
| import com.fasterxml.jackson.databind.PropertyNamingStrategies; | ||
| import com.fasterxml.jackson.databind.annotation.JsonNaming; | ||
| import jakarta.validation.Valid; | ||
| import lombok.Builder; | ||
|
|
||
| @Builder(toBuilder = true) | ||
| @JsonIgnoreProperties(ignoreUnknown = true) | ||
| @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) | ||
| public record OptimizationUpdate(String name, OptimizationStatus status) { | ||
| public record OptimizationUpdate(String name, OptimizationStatus status, @Valid ErrorInfo errorInfo) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [medium] @Valid on errorInfo never cascades — update endpoint param is not @Valid Adding @Valid to OptimizationUpdate.errorInfo is intended to enforce ErrorInfo's nested @notblank exceptionType / @notblank traceback constraints. But Bean Validation only cascades from a root that is itself validated, and the only endpoint that consumes this DTO — OptimizationsResource.updateOptimizationsById (apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/OptimizationsResource.java:202) — declares the body as 💡 Add @Valid to the request parameter of updateOptimizationsById ( rule:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in e6fcf35. Added
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Commit e6fcf35 addressed this comment by adding |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| package com.comet.opik.domain; | ||
|
|
||
| import com.comet.opik.api.DatasetLastOptimizationCreated; | ||
| import com.comet.opik.api.ErrorInfo; | ||
| import com.comet.opik.api.Optimization; | ||
| import com.comet.opik.api.OptimizationStatus; | ||
| import com.comet.opik.api.OptimizationStudioConfig; | ||
|
|
@@ -37,6 +38,7 @@ | |
| import java.util.Set; | ||
| import java.util.UUID; | ||
|
|
||
| import static com.comet.opik.api.ErrorInfo.ERROR_INFO_TYPE; | ||
| import static com.comet.opik.domain.AsyncContextUtils.bindUserNameAndWorkspaceContextToStream; | ||
| import static com.comet.opik.domain.AsyncContextUtils.bindWorkspaceIdToFlux; | ||
| import static com.comet.opik.domain.ExperimentDAO.getFeedbackScores; | ||
|
|
@@ -235,6 +237,7 @@ INSERT INTO optimizations ( | |
| status, | ||
| metadata, | ||
| studio_config, | ||
| error_info, | ||
| created_by, | ||
| last_updated_by, | ||
| last_updated_at | ||
|
|
@@ -249,6 +252,7 @@ INSERT INTO optimizations ( | |
| :status, | ||
| :metadata, | ||
| :studio_config, | ||
| :error_info, | ||
| :created_by, | ||
| :last_updated_by, | ||
| COALESCE(parseDateTime64BestEffortOrNull(:last_updated_at, 6), now64(6)) | ||
|
|
@@ -557,7 +561,7 @@ ORDER BY (workspace_id, dataset_id, id) DESC, last_updated_at DESC | |
|
|
||
| private static final String UPDATE_BY_ID = """ | ||
| INSERT INTO optimizations ( | ||
| id, dataset_id, name, workspace_id, project_id, objective_name, status, metadata, created_at, created_by, last_updated_by, studio_config | ||
| id, dataset_id, name, workspace_id, project_id, objective_name, status, metadata, created_at, created_by, last_updated_by, studio_config, error_info | ||
| ) | ||
|
Comment on lines
621
to
623
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing migration breaks optimization updatesThe new Want Baz to fix this for you? Activate Fixer Other fix methodsPrompt for AI Agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not applicable to how opik deploys. Migrations from the same image run before the server starts serving:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, that makes sense given your migration-before-serve rollout model and existing pattern for column adds. I’ll save this to memory once the PR is merged.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Commit 280f012 addressed this comment by relying on the existing migration-before-serve deployment guarantee. New instances apply migrations successfully before serving, while old replicas continue using the old SQL during rolling deploys. |
||
| SELECT | ||
| id, | ||
|
|
@@ -571,7 +575,8 @@ INSERT INTO optimizations ( | |
| created_at, | ||
| created_by, | ||
| :user_name as last_updated_by, | ||
| studio_config | ||
| studio_config, | ||
| <if(error_info)> :error_info <else> error_info <endif> as error_info | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [low] No path clears error_info via update once it is set UPDATE_BY_ID only overwrites error_info when :error_info is supplied (' error_info' otherwise). There is no branch that can reset error_info to '' on a status-only update. If an optimization is updated to a non-error/terminal status without an errorInfo in the payload, a previously-persisted failure reason is retained, leaving a stale error surfaced alongside a non-error status. Confirm the lifecycle never recovers from an error state (in which case this is intended); otherwise a status transition out of error should clear error_info. 💡 If clearing is a real requirement, allow the update to explicitly reset error_info (e.g., treat an empty/blank errorInfo as an intent to clear) or clear it whenever status transitions to a non-error terminal value. rule:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Confirmed intended — leaving as-is. An optimization that ends in error is terminal in this lifecycle: the Studio worker/cancel paths only ever transition into an error state and never back out to a running/success status, so there is no valid transition that should clear |
||
| FROM optimizations | ||
| WHERE id = :id | ||
| AND workspace_id = :workspace_id | ||
|
|
@@ -582,7 +587,7 @@ INSERT INTO optimizations ( | |
|
|
||
| private static final String SET_DATASET_DELETED_TO_TRUE_BY_DATASET_ID = """ | ||
| INSERT INTO optimizations ( | ||
| id, dataset_id, name, workspace_id, project_id, objective_name, status, metadata, created_at, created_by, last_updated_at, last_updated_by, dataset_deleted, studio_config | ||
| id, dataset_id, name, workspace_id, project_id, objective_name, status, metadata, created_at, created_by, last_updated_at, last_updated_by, dataset_deleted, studio_config, error_info | ||
| ) | ||
| SELECT | ||
| id, | ||
|
|
@@ -598,7 +603,8 @@ INSERT INTO optimizations ( | |
| last_updated_at, | ||
| last_updated_by, | ||
| true as dataset_deleted, | ||
| studio_config | ||
| studio_config, | ||
| error_info | ||
| FROM optimizations | ||
| WHERE workspace_id = :workspace_id | ||
| AND dataset_id IN :dataset_ids | ||
|
|
@@ -884,7 +890,9 @@ private Publisher<? extends Result> upsert(Optimization optimization, Connection | |
| .bind("project_id", optimization.projectId() != null ? optimization.projectId().toString() : "") | ||
| .bind("objective_name", optimization.objectiveName()) | ||
| .bind("status", optimization.status().getValue()) | ||
| .bind("metadata", getStringOrDefault(optimization.metadata())); | ||
| .bind("metadata", getStringOrDefault(optimization.metadata())) | ||
| .bind("error_info", | ||
| optimization.errorInfo() != null ? JsonUtils.writeValueAsString(optimization.errorInfo()) : ""); | ||
|
Comment on lines
+952
to
+953
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Upsert clears prior error reason
Want Baz to fix this for you? Activate Fixer Other fix methodsPrompt for AI Agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, that makes sense. I'll save this to memory once the PR is merged.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Commit e6fcf35 addressed this comment by preserving the existing
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [medium] upsert blanket-nulls error_info to '' on re-upsert of an existing optimization UPSERT now writes error_info from optimization.errorInfo() (or '' when null) and, unlike UPDATE_BY_ID, does a full-row replace. OptimizationService.upsert (OptimizationService.java lines 189-198) re-reads the existing row and explicitly preserves studioConfig and name for an existing optimization, but does NOT preserve errorInfo. The failure reason is normally persisted through the separate update()/PATCH path (see OptimizationUpdate.errorInfo and the UPDATE_BY_ID '<if(error_info)> :error_info error_info ' preservation). So when an optimization that has already recorded a failure (error_info populated) is re-upserted by the SDK with a null errorInfo, this bind overwrites the persisted failure reason with '' and it is lost. This is the same provenance-parity gap the studioConfig preservation branch was added to avoid. 💡 Preserve errorInfo on re-upsert the same way studioConfig is preserved: in OptimizationService.upsert, when optimization.errorInfo() == null and existing.errorInfo() != null, copy the existing value into the builder (or have the DAO fall back to the existing column when the bind value is blank). rule:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in e6fcf35.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Commit e6fcf35 addressed this comment by preserving
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Follow-up: added an integration test |
||
|
|
||
| if (optimization.studioConfig() != null) { | ||
| try { | ||
|
|
@@ -933,6 +941,17 @@ private Publisher<Optimization> mapToDto(Result result) { | |
| } | ||
| } | ||
|
|
||
| ErrorInfo errorInfo = null; | ||
| String errorInfoJson = row.get("error_info", String.class); | ||
| if (StringUtils.isNotBlank(errorInfoJson)) { | ||
| try { | ||
| errorInfo = JsonUtils.readValue(errorInfoJson, ERROR_INFO_TYPE); | ||
| } catch (Exception e) { | ||
|
awkoy marked this conversation as resolved.
Outdated
|
||
| log.error("Failed to deserialize error_info for optimization: '{}'", | ||
| row.get("id", UUID.class), e); | ||
| } | ||
|
Comment on lines
+1002
to
+1010
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicated error_info row mapper
Want Baz to fix this for you? Activate Fixer
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair point on duplication, but leaving as-is for this PR. The
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, that makes sense — keeping local consistency here and deferring the cross-cutting deduplication to a follow-up is a reasonable boundary. I'll save this to memory once the PR is merged. |
||
| } | ||
|
|
||
| String projectIdStr = row.get("project_id", String.class); | ||
| UUID projectId = StringUtils.isNotBlank(projectIdStr) ? UUID.fromString(projectIdStr) : null; | ||
|
|
||
|
|
@@ -945,6 +964,7 @@ private Publisher<Optimization> mapToDto(Result result) { | |
| .status(OptimizationStatus.fromString(row.get("status", String.class))) | ||
| .metadata(getJsonNodeOrDefault(row.get("metadata", String.class))) | ||
| .studioConfig(studioConfig) | ||
| .errorInfo(errorInfo) | ||
| .createdAt(row.get("created_at", Instant.class)) | ||
| .lastUpdatedAt(row.get("last_updated_at", Instant.class)) | ||
| .createdBy(row.get("created_by", String.class)) | ||
|
|
@@ -999,6 +1019,9 @@ private ST buildUpdateTemplate(OptimizationUpdate update) { | |
| Optional.ofNullable(update.status()) | ||
| .ifPresent(status -> template.add("status", status.getValue())); | ||
|
|
||
| Optional.ofNullable(update.errorInfo()) | ||
| .ifPresent(errorInfo -> template.add("error_info", errorInfo)); | ||
|
|
||
| return template; | ||
| } | ||
|
|
||
|
|
@@ -1011,6 +1034,9 @@ private Statement createUpdateStatement(UUID id, OptimizationUpdate update, Conn | |
| Optional.ofNullable(update.status()) | ||
| .ifPresent(status -> statement.bind("status", status.getValue())); | ||
|
|
||
| Optional.ofNullable(update.errorInfo()) | ||
| .ifPresent(errorInfo -> statement.bind("error_info", JsonUtils.writeValueAsString(errorInfo))); | ||
|
|
||
| statement.bind("id", id); | ||
|
|
||
| return statement; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -197,6 +197,15 @@ public Mono<UUID> upsert(@NonNull Optimization optimization) { | |
| builder.studioConfig(existing.studioConfig()); | ||
| } | ||
|
|
||
| // Preserve the persisted failure reason if not provided in update | ||
| // (upsert does a full-row replace; the SDK re-upserts with a null | ||
| // errorInfo and would otherwise clobber a previously recorded failure). | ||
| // errorInfo is normally set through the PATCH/update path. | ||
| if (optimization.errorInfo() == null | ||
| && existing.errorInfo() != null) { | ||
| builder.errorInfo(existing.errorInfo()); | ||
|
Comment on lines
+219
to
+225
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Want Baz to fix this for you? Activate Fixer Other fix methodsPrompt for AI Agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keeping the unconditional preserve — here's the reasoning. This is exactly the behavior the human review (thiagohora) explicitly requested for this finding, and it mirrors the existing
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, that makes sense — preserving |
||
| } | ||
|
|
||
| // Preserve original name only if incoming name is blank | ||
| // (SDK sends blank name, but explicit updates should be honored) | ||
| if (StringUtils.isBlank(optimization.name())) { | ||
|
|
@@ -310,7 +319,7 @@ public Flux<DatasetLastOptimizationCreated> getMostRecentCreatedOptimizationFrom | |
|
|
||
| @Override | ||
| public Mono<Long> update(@NonNull UUID id, @NonNull OptimizationUpdate update) { | ||
| if (update.name() == null && update.status() == null) { | ||
| if (update.name() == null && update.status() == null && update.errorInfo() == null) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [low] New errorInfo-only update branch is untested This diff loosens the early-return guard so an update carrying ONLY errorInfo (name == null && status == null && errorInfo != null) now flows through to optimizationDAO.update and persists error_info, whereas before it short-circuited to Mono.empty() and was silently dropped. This is the entire behavior the PR enables, but the parameterized test OptimizationsResourceTest#updateById (updateById() MethodSource, lines 886-891) never exercises it: the three cases are a full pojo, name=null (status+errorInfo still set), and status=null (name+errorInfo still set) — none isolates the errorInfo-only branch. The complementary no-op case (all three null -> Mono.empty(), 204/no change) is also uncovered. The changed behavior branch ships without a dedicated test. 💡 Add an argument to the updateById() MethodSource for an errorInfo-only update, e.g. arguments(podamFactory.manufacturePojo(OptimizationUpdate.class).toBuilder().name(null).status(null).build()), and assert the persisted optimization reflects the new errorInfo while name/status are preserved. Optionally add a case with all fields null to assert the no-op path. general finding
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in e6fcf35. Added an
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Commit e6fcf35 addressed this comment by adding an explicit |
||
| return Mono.empty(); | ||
| } | ||
|
Comment on lines
339
to
344
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Error info lacks test coverage
Want Baz to fix this for you? Activate Fixer Other fix methodsPrompt for AI Agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Commit 4660dd7 addressed this comment by extending the optimization update test to carry |
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| --liquibase formatted sql | ||
| --changeset admin:000106_add_error_info_to_optimizations | ||
| --comment: Add error_info column to optimizations table to persist the failure reason surfaced in Optimization Studio | ||
|
|
||
| ALTER TABLE ${ANALYTICS_DB_DATABASE_NAME}.optimizations ON CLUSTER '{cluster}' | ||
| ADD COLUMN IF NOT EXISTS error_info String DEFAULT ''; | ||
|
|
||
| --rollback ALTER TABLE ${ANALYTICS_DB_DATABASE_NAME}.optimizations ON CLUSTER '{cluster}' DROP COLUMN IF EXISTS error_info; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |
| import com.comet.opik.api.Dataset; | ||
| import com.comet.opik.api.DatasetItem; | ||
| import com.comet.opik.api.DatasetItemBatch; | ||
| import com.comet.opik.api.ErrorInfo; | ||
| import com.comet.opik.api.Experiment; | ||
| import com.comet.opik.api.ExperimentItem; | ||
| import com.comet.opik.api.ExperimentScore; | ||
|
|
@@ -875,6 +876,7 @@ void updateById(OptimizationUpdate update) { | |
| optimization = optimization.toBuilder().id(id) | ||
| .name(update.name() != null ? update.name() : optimization.name()) | ||
| .status(update.status() != null ? update.status() : optimization.status()) | ||
| .errorInfo(update.errorInfo() != null ? update.errorInfo() : optimization.errorInfo()) | ||
| .build(); | ||
|
|
||
| var actualOptimization = optimizationResourceClient.get(id, API_KEY, TEST_WORKSPACE_NAME, 200); | ||
|
|
@@ -886,7 +888,11 @@ private Stream<Arguments> updateById() { | |
| return Stream.of( | ||
| arguments(podamFactory.manufacturePojo(OptimizationUpdate.class)), | ||
| arguments(podamFactory.manufacturePojo(OptimizationUpdate.class).toBuilder().name(null).build()), | ||
| arguments(podamFactory.manufacturePojo(OptimizationUpdate.class).toBuilder().status(null).build())); | ||
| arguments(podamFactory.manufacturePojo(OptimizationUpdate.class).toBuilder().status(null).build()), | ||
| // errorInfo-only update: exercises the branch that persists a failure reason without | ||
| // name/status (previously short-circuited to Mono.empty() before this behavior was added) | ||
| arguments(podamFactory.manufacturePojo(OptimizationUpdate.class).toBuilder() | ||
| .name(null).status(null).build())); | ||
| } | ||
|
|
||
| @Nested | ||
|
|
@@ -1466,6 +1472,40 @@ void studioConfigPreservedAfterStatusUpdate() { | |
| assertThat(completedOptimization.studioConfig()).isEqualTo(studioConfig); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("error_info is preserved when SDK re-upserts with a null errorInfo") | ||
| void errorInfoPreservedOnReUpsertWithNullErrorInfo() { | ||
| // Create a Studio optimization (upsert full-row replace path) | ||
| var studioConfig = createStudioConfig(); | ||
| var optimization = optimizationResourceClient.createPartialOptimization() | ||
| .studioConfig(studioConfig) | ||
| .errorInfo(null) | ||
| .build(); | ||
| var id = optimizationResourceClient.create(optimization, API_KEY, TEST_WORKSPACE_NAME); | ||
|
|
||
| // Record a failure reason through the PATCH/update path (as the worker does) | ||
| var errorInfo = podamFactory.manufacturePojo(ErrorInfo.class); | ||
| optimizationResourceClient.update(id, | ||
| OptimizationUpdate.builder().status(OptimizationStatus.CANCELLED).errorInfo(errorInfo).build(), | ||
| API_KEY, TEST_WORKSPACE_NAME, 204); | ||
|
|
||
| var afterFailure = optimizationResourceClient.get(id, API_KEY, TEST_WORKSPACE_NAME, 200); | ||
| assertThat(afterFailure.errorInfo()).isEqualTo(errorInfo); | ||
|
|
||
| // Re-upsert the same optimization with a null errorInfo (SDK behavior): the persisted | ||
| // failure reason must survive the full-row replace instead of being clobbered to blank. | ||
| optimizationResourceClient.upsert(optimization.toBuilder().id(id).errorInfo(null).build(), | ||
| API_KEY, TEST_WORKSPACE_NAME); | ||
|
|
||
| var afterReUpsert = optimizationResourceClient.get(id, API_KEY, TEST_WORKSPACE_NAME, 200); | ||
| // The re-upsert with a null errorInfo must not drop the persisted failure reason | ||
| // nor the sibling studioConfig (both preserved through the upsert path). Status | ||
| // ordering across the update/upsert rows is governed by last_updated_at, so it is | ||
| // not asserted here. | ||
| assertThat(afterReUpsert.errorInfo()).isEqualTo(errorInfo); | ||
|
Comment on lines
+1516
to
+1524
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clobber regressions stay hidden
Want Baz to fix this for you? Activate Fixer Other fix methodsPrompt for AI Agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Commit ca9a338 addressed this comment by adding an assertion that
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in ca9a338. The test now also asserts
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, that makes sense. I’ll save this to memory once the PR is merged. |
||
| assertThat(afterReUpsert.studioConfig()).isEqualTo(studioConfig); | ||
| } | ||
|
|
||
| @Test | ||
| @DisplayName("Cancel Studio optimization returns NOT_IMPLEMENTED") | ||
| void cancelStudioOptimization__returnsNotImplemented() { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.