mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 01:07:18 +03:00
[DPP-656] Assert on self-service error code details in ErrorFactoriesSpec (#11289)
CHANGELOG_BEGIN CHANGELOG_END
This commit is contained in:
parent
c06faf2764
commit
2267429ae1
@ -4,21 +4,22 @@
|
||||
package com.daml
|
||||
|
||||
import com.daml.error.{
|
||||
DamlContextualizedErrorLogger,
|
||||
ContextualizedErrorLogger,
|
||||
DamlContextualizedErrorLogger,
|
||||
ErrorCodesVersionSwitcher,
|
||||
}
|
||||
import com.daml.ledger.api.domain.LedgerId
|
||||
import com.daml.logging.{ContextualizedLogger, LoggingContext}
|
||||
import com.daml.platform.server.api.validation.ErrorFactories
|
||||
import com.daml.platform.server.api.validation.ErrorFactories._
|
||||
import com.google.rpc.Status
|
||||
import com.google.rpc.{ErrorInfo, RequestInfo, ResourceInfo, RetryInfo, Status}
|
||||
import io.grpc.Status.Code
|
||||
import io.grpc.StatusRuntimeException
|
||||
import io.grpc.protobuf.StatusProto
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.prop.TableDrivenPropertyChecks
|
||||
import org.scalatest.wordspec.AnyWordSpec
|
||||
import com.google.protobuf
|
||||
|
||||
import scala.jdk.CollectionConverters._
|
||||
|
||||
@ -30,6 +31,9 @@ class ErrorFactoriesSpec extends AnyWordSpec with Matchers with TableDrivenPrope
|
||||
private implicit val contextualizedErrorLogger: ContextualizedErrorLogger =
|
||||
new DamlContextualizedErrorLogger(logger, loggingContext, Some(correlationId))
|
||||
|
||||
private val DefaultTraceIdRequestInfo: ErrorDetails.RequestInfoDetail =
|
||||
ErrorDetails.RequestInfoDetail("trace-id")
|
||||
|
||||
"ErrorFactories" should {
|
||||
"return the DuplicateCommandException" in {
|
||||
assertVersionedError(_.duplicateCommandException)(
|
||||
@ -39,6 +43,10 @@ class ErrorFactoriesSpec extends AnyWordSpec with Matchers with TableDrivenPrope
|
||||
v2_code = Code.ALREADY_EXISTS,
|
||||
v2_message =
|
||||
s"DUPLICATE_COMMAND(10,$correlationId): A command with the given command id has already been successfully processed",
|
||||
v2_details = Seq[ErrorDetails.ErrorDetail](
|
||||
ErrorDetails.ErrorInfoDetail("DUPLICATE_COMMAND"),
|
||||
DefaultTraceIdRequestInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -50,6 +58,10 @@ class ErrorFactoriesSpec extends AnyWordSpec with Matchers with TableDrivenPrope
|
||||
v2_code = Code.PERMISSION_DENIED,
|
||||
v2_message =
|
||||
s"An error occurred. Please contact the operator and inquire about the request $correlationId",
|
||||
v2_details = Seq[ErrorDetails.ErrorDetail](
|
||||
ErrorDetails.ErrorInfoDetail("PERMISSION_DENIED"),
|
||||
DefaultTraceIdRequestInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -68,6 +80,10 @@ class ErrorFactoriesSpec extends AnyWordSpec with Matchers with TableDrivenPrope
|
||||
v2_code = Code.NOT_FOUND,
|
||||
v2_message =
|
||||
s"LEDGER_CONFIGURATION_NOT_FOUND(11,$correlationId): The ledger configuration is not available.",
|
||||
v2_details = Seq[ErrorDetails.ErrorDetail](
|
||||
ErrorDetails.ErrorInfoDetail("LEDGER_CONFIGURATION_NOT_FOUND"),
|
||||
DefaultTraceIdRequestInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -105,6 +121,10 @@ class ErrorFactoriesSpec extends AnyWordSpec with Matchers with TableDrivenPrope
|
||||
v2_code = Code.INVALID_ARGUMENT,
|
||||
v2_message =
|
||||
s"INVALID_FIELD(8,$correlationId): The submitted command has a field with invalid value: Invalid field my field: my message",
|
||||
v2_details = Seq[ErrorDetails.ErrorDetail](
|
||||
ErrorDetails.ErrorInfoDetail("INVALID_FIELD"),
|
||||
DefaultTraceIdRequestInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -117,6 +137,10 @@ class ErrorFactoriesSpec extends AnyWordSpec with Matchers with TableDrivenPrope
|
||||
v2_code = Code.UNAUTHENTICATED,
|
||||
v2_message =
|
||||
s"An error occurred. Please contact the operator and inquire about the request $correlationId",
|
||||
v2_details = Seq[ErrorDetails.ErrorDetail](
|
||||
ErrorDetails.ErrorInfoDetail("UNAUTHENTICATED"),
|
||||
DefaultTraceIdRequestInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -137,6 +161,10 @@ class ErrorFactoriesSpec extends AnyWordSpec with Matchers with TableDrivenPrope
|
||||
v2_code = Code.NOT_FOUND,
|
||||
v2_message =
|
||||
s"LEDGER_ID_MISMATCH(11,$correlationId): Ledger ID 'received' not found. Actual Ledger ID is 'expected'.",
|
||||
v2_details = Seq[ErrorDetails.ErrorDetail](
|
||||
ErrorDetails.ErrorInfoDetail("LEDGER_ID_MISMATCH"),
|
||||
DefaultTraceIdRequestInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -156,6 +184,10 @@ class ErrorFactoriesSpec extends AnyWordSpec with Matchers with TableDrivenPrope
|
||||
v1_details = Seq.empty,
|
||||
v2_code = Code.OUT_OF_RANGE,
|
||||
v2_message = s"PARTICIPANT_PRUNED_DATA_ACCESSED(12,$correlationId): my message",
|
||||
v2_details = Seq[ErrorDetails.ErrorDetail](
|
||||
ErrorDetails.ErrorInfoDetail("PARTICIPANT_PRUNED_DATA_ACCESSED"),
|
||||
DefaultTraceIdRequestInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -166,6 +198,10 @@ class ErrorFactoriesSpec extends AnyWordSpec with Matchers with TableDrivenPrope
|
||||
v1_details = Seq.empty,
|
||||
v2_code = Code.OUT_OF_RANGE,
|
||||
v2_message = s"REQUESTED_OFFSET_OUT_OF_RANGE(12,$correlationId): my message",
|
||||
v2_details = Seq[ErrorDetails.ErrorDetail](
|
||||
ErrorDetails.ErrorInfoDetail("REQUESTED_OFFSET_OUT_OF_RANGE"),
|
||||
DefaultTraceIdRequestInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -183,6 +219,11 @@ class ErrorFactoriesSpec extends AnyWordSpec with Matchers with TableDrivenPrope
|
||||
v1_details = expectedDetails,
|
||||
v2_code = Code.UNAVAILABLE,
|
||||
v2_message = s"SERVICE_NOT_RUNNING(1,$correlationId): Service has been shut down.",
|
||||
v2_details = Seq[ErrorDetails.ErrorDetail](
|
||||
ErrorDetails.ErrorInfoDetail("SERVICE_NOT_RUNNING"),
|
||||
DefaultTraceIdRequestInfo,
|
||||
ErrorDetails.RetryInfoDetail(1),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -195,6 +236,10 @@ class ErrorFactoriesSpec extends AnyWordSpec with Matchers with TableDrivenPrope
|
||||
v2_code = Code.NOT_FOUND,
|
||||
v2_message =
|
||||
s"LEDGER_CONFIGURATION_NOT_FOUND(11,$correlationId): The ledger configuration is not available.",
|
||||
v2_details = Seq[ErrorDetails.ErrorDetail](
|
||||
ErrorDetails.ErrorInfoDetail("LEDGER_CONFIGURATION_NOT_FOUND"),
|
||||
DefaultTraceIdRequestInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -213,6 +258,10 @@ class ErrorFactoriesSpec extends AnyWordSpec with Matchers with TableDrivenPrope
|
||||
v2_code = Code.INVALID_ARGUMENT,
|
||||
v2_message =
|
||||
s"MISSING_FIELD(8,$correlationId): The submitted command is missing a mandatory field: my field",
|
||||
v2_details = Seq[ErrorDetails.ErrorDetail](
|
||||
ErrorDetails.ErrorInfoDetail("MISSING_FIELD"),
|
||||
DefaultTraceIdRequestInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -232,6 +281,10 @@ class ErrorFactoriesSpec extends AnyWordSpec with Matchers with TableDrivenPrope
|
||||
v2_code = Code.INVALID_ARGUMENT,
|
||||
v2_message =
|
||||
s"INVALID_ARGUMENT(8,$correlationId): The submitted command has invalid arguments: my message",
|
||||
v2_details = Seq[ErrorDetails.ErrorDetail](
|
||||
ErrorDetails.ErrorInfoDetail("INVALID_ARGUMENT"),
|
||||
DefaultTraceIdRequestInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -245,29 +298,76 @@ class ErrorFactoriesSpec extends AnyWordSpec with Matchers with TableDrivenPrope
|
||||
|
||||
private def assertVersionedError(
|
||||
error: ErrorFactories => StatusRuntimeException
|
||||
)(v1_code: Code, v1_message: String, v1_details: Seq[Any], v2_code: Code, v2_message: String) = {
|
||||
)(
|
||||
v1_code: Code,
|
||||
v1_message: String,
|
||||
v1_details: Seq[Any],
|
||||
v2_code: Code,
|
||||
v2_message: String,
|
||||
v2_details: Seq[ErrorDetails.ErrorDetail],
|
||||
): Unit = {
|
||||
val errorFactoriesV1 = ErrorFactories(new ErrorCodesVersionSwitcher(false))
|
||||
val errorFactoriesV2 = ErrorFactories(new ErrorCodesVersionSwitcher(true))
|
||||
assertV1Error(error(errorFactoriesV1))(v1_code, v1_message, v1_details)
|
||||
assertV2Error(error(errorFactoriesV2))(v2_code, v2_message)
|
||||
assertV2Error(error(errorFactoriesV2))(v2_code, v2_message, v2_details)
|
||||
}
|
||||
|
||||
private def assertV1Error(
|
||||
statusRuntimeException: StatusRuntimeException
|
||||
)(expectedCode: Code, expectedMessage: String, expectedDetails: Seq[Any]) = {
|
||||
)(expectedCode: Code, expectedMessage: String, expectedDetails: Seq[Any]): Unit = {
|
||||
val status = StatusProto.fromThrowable(statusRuntimeException)
|
||||
status.getCode shouldBe expectedCode.value()
|
||||
status.getMessage shouldBe expectedMessage
|
||||
status.getDetailsList.asScala shouldBe expectedDetails
|
||||
val _ = status.getDetailsList.asScala shouldBe expectedDetails
|
||||
}
|
||||
|
||||
private def assertV2Error(
|
||||
statusRuntimeException: StatusRuntimeException
|
||||
)(expectedCode: Code, expectedMessage: String) = {
|
||||
)(
|
||||
expectedCode: Code,
|
||||
expectedMessage: String,
|
||||
expectedDetails: Seq[ErrorDetails.ErrorDetail],
|
||||
): Unit = {
|
||||
val status = StatusProto.fromThrowable(statusRuntimeException)
|
||||
status.getCode shouldBe expectedCode.value()
|
||||
status.getMessage shouldBe expectedMessage
|
||||
// TODO error codes: Assert error details
|
||||
val details = status.getDetailsList.asScala.toSeq
|
||||
val _ = ErrorDetails.from(details) should contain theSameElementsAs (expectedDetails)
|
||||
// TODO error codes: Assert logging
|
||||
}
|
||||
}
|
||||
|
||||
object ErrorDetails {
|
||||
|
||||
sealed trait ErrorDetail
|
||||
|
||||
final case class ResourceInfoDetail(name: String, typ: String) extends ErrorDetail
|
||||
|
||||
final case class ErrorInfoDetail(reason: String) extends ErrorDetail
|
||||
|
||||
final case class RetryInfoDetail(retryDelayInSeconds: Long) extends ErrorDetail
|
||||
|
||||
final case class RequestInfoDetail(requestId: String) extends ErrorDetail
|
||||
|
||||
def from(anys: Seq[protobuf.Any]): Seq[ErrorDetail] = {
|
||||
anys.toList.map(from)
|
||||
}
|
||||
|
||||
private def from(any: protobuf.Any): ErrorDetail = {
|
||||
if (any.is(classOf[ResourceInfo])) {
|
||||
val v = any.unpack(classOf[ResourceInfo])
|
||||
ResourceInfoDetail(v.getResourceType, v.getResourceName)
|
||||
} else if (any.is(classOf[ErrorInfo])) {
|
||||
val v = any.unpack(classOf[ErrorInfo])
|
||||
ErrorInfoDetail(v.getReason)
|
||||
} else if (any.is(classOf[RetryInfo])) {
|
||||
val v = any.unpack(classOf[RetryInfo])
|
||||
RetryInfoDetail(v.getRetryDelay.getSeconds)
|
||||
} else if (any.is(classOf[RequestInfo])) {
|
||||
val v = any.unpack(classOf[RequestInfo])
|
||||
RequestInfoDetail(v.getRequestId)
|
||||
} else {
|
||||
throw new IllegalStateException(s"Could not unpack value of: |$any|")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user