Skip to content

Commit

Permalink
Add automated testing for 'ApiErrorCode'
Browse files Browse the repository at this point in the history
  • Loading branch information
Julian Ospald committed Oct 30, 2020
1 parent 9e3e895 commit 7225066
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 3 deletions.
7 changes: 6 additions & 1 deletion lib/core/src/Cardano/Wallet/Api/Types.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
Expand Down Expand Up @@ -245,6 +246,8 @@ import Data.ByteArray.Encoding
( Base (Base16), convertFromBase, convertToBase )
import Data.ByteString
( ByteString )
import Data.Data
( Data )
import Data.Either.Extra
( maybeToEither )
import Data.Function
Expand Down Expand Up @@ -277,6 +280,8 @@ import Data.Time.Clock
( NominalDiffTime, UTCTime )
import Data.Time.Text
( iso8601, iso8601ExtendedUtc, utcTimeFromText, utcTimeToText )
import Data.Typeable
( Typeable )
import Data.Word
( Word16, Word32, Word64 )
import Data.Word.Odd
Expand Down Expand Up @@ -813,7 +818,7 @@ data ApiErrorCode
| PastHorizon
| UnableToAssignInputOutput
| SoftDerivationRequired
deriving (Eq, Generic, Show)
deriving (Eq, Generic, Show, Data, Typeable)

-- | Defines a point in time that can be formatted as and parsed from an
-- ISO 8601-compliant string.
Expand Down
54 changes: 52 additions & 2 deletions lib/core/test/unit/Cardano/Wallet/Api/TypesSpec.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
Expand All @@ -8,6 +9,7 @@
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
Expand Down Expand Up @@ -54,6 +56,7 @@ import Cardano.Wallet.Api.Types
, ApiCoinSelectionInput (..)
, ApiCoinSelectionOutput (..)
, ApiEpochInfo (..)
, ApiErrorCode (..)
, ApiFee (..)
, ApiMnemonicT (..)
, ApiNetworkClock (..)
Expand Down Expand Up @@ -182,17 +185,28 @@ import Cardano.Wallet.Unsafe
import Control.Lens
( at, (.~), (?~), (^.) )
import Control.Monad
( forM_, replicateM )
( forM, forM_, replicateM )
import Control.Monad.IO.Class
( liftIO )
import Crypto.Hash
( hash )
import Data.Aeson
( FromJSON (..), ToJSON (..), (.=) )
( FromJSON (..)
, Result (..)
, ToJSON (..)
, fromJSON
, withObject
, (.:?)
, (.=)
)
import Data.Aeson.QQ
( aesonQQ )
import Data.Char
( toLower )
import Data.Data
( dataTypeConstrs, dataTypeOf, showConstr )
import Data.Either
( lefts )
import Data.FileEmbed
( embedFile, makeRelativeToProject )
import Data.Function
Expand Down Expand Up @@ -272,6 +286,7 @@ import Test.QuickCheck
, arbitraryPrintableChar
, arbitrarySizedBoundedIntegral
, choose
, counterexample
, elements
, frequency
, oneof
Expand Down Expand Up @@ -929,6 +944,41 @@ spec = do
in
x' === x .&&. show x' === show x

describe "Api Errors" $ do
it "Every constructor from ApiErrorCode has a corresponding type in the schema" $
let res = fromJSON @SchemaApiErrorCode specification
errStr = case res of
Error s -> s
_ -> ""
in counterexample errStr $ res == Success SchemaApiErrorCode

{-------------------------------------------------------------------------------
Error type Encoding
-------------------------------------------------------------------------------}

-- | We use this empty data type to define a custom
-- JSON instance that checks ApiErrorCode has corresponding
-- constructors in the schema file.
data SchemaApiErrorCode = SchemaApiErrorCode
deriving (Show, Eq)

instance FromJSON SchemaApiErrorCode where
parseJSON = withObject "SchemaApiErrorCode" $ \o -> do
vals <- forM (fmap showConstr $ dataTypeConstrs $ dataTypeOf NoSuchWallet)
$ \n -> do
(r :: Maybe Yaml.Value) <- o .:? T.pack (toSchemaName n)
pure $ maybe (Left n) Right r
case lefts vals of
[] -> pure SchemaApiErrorCode
xs -> fail ("Missing ApiErrorCode constructors for: "
<> show xs
<> "\nEach of these need a corresponding swagger type of the form: "
<> "x-errConstructorName")
where
toSchemaName :: String -> String
toSchemaName [] = []
toSchemaName xs = "x-err" <> xs

{-------------------------------------------------------------------------------
Address Encoding
-------------------------------------------------------------------------------}
Expand Down
24 changes: 24 additions & 0 deletions specifications/api/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1915,6 +1915,30 @@ x-responsesErr: &responsesErr
description: A specific error code for this error, more precise than HTTP ones.
example: an_error_code

x-errNotFound: &errNotFound
<<: *responsesErr
title: not_found
properties:
message:
type: string
description: A descriptive error message.
code:
code:
type: string
enum: ['not_found']

x-errSoftDerivationRequired: &errSoftDerivationRequired
<<: *responsesErr
title: not_found
properties:
message:
type: string
description: A descriptive error message.
code:
code:
type: string
enum: ['soft_derivation_required']

x-errNoSuchWallet: &errNoSuchWallet
<<: *responsesErr
title: no_such_wallet
Expand Down

0 comments on commit 7225066

Please sign in to comment.