content: {-# LANGUAGE OverloadedLabels  #-}
  {-# LANGUAGE OverloadedStrings #-}
  
  module Test.Sanity.Any (tests) where
  
  import Control.Exception
  import Test.Tasty
  import Test.Tasty.HUnit
  
  import Network.GRPC.Client (rpc)
  import Network.GRPC.Client.StreamType.IO qualified as Client
  import Network.GRPC.Common
  import Network.GRPC.Common.Protobuf
  import Network.GRPC.Common.Protobuf.Any (Any)
  import Network.GRPC.Common.Protobuf.Any qualified as Any
  import Network.GRPC.Server.StreamType qualified as Server
  
  import Test.Driver.ClientServer
  
  import Proto.API.Ping
  import Proto.API.TestAny
  
  {-------------------------------------------------------------------------------
    Top-level
  -------------------------------------------------------------------------------}
  
  tests :: TestTree
  tests = testGroup "Test.Sanity.Any" [
        testCase "Any"    testAny
      , testCase "Status" testStatus
      ]
  
  {-------------------------------------------------------------------------------
    Using the 'Any' wrapper
  -------------------------------------------------------------------------------}
  
  testAny :: Assertion
  testAny = testClientServer $ ClientServerTest {
        config = def
      , server = [Server.fromMethod @Reverse $ Server.ServerHandler handler]
      , client = simpleTestClient $ \conn -> do
          let req, expected :: Proto TestAnyMsg
              req      = withDetails [Any.pack detail1, Any.pack detail2]
              expected = withDetails [Any.pack detail2, Any.pack detail1]
  
          resp <- Client.nonStreaming conn (rpc @Reverse) req
          assertEqual "" expected $ resp
      }
    where
      handler :: Proto TestAnyMsg -> IO (Proto TestAnyMsg)
      handler msg = return $ msg & #details %~ reverse
  
      detail1 :: Proto A
      detail1 = defMessage & #a .~ 1
  
      detail2 :: Proto B
      detail2 = defMessage & #b .~ 1
  
      withDetails :: [Proto Any] -> Proto TestAnyMsg
      withDetails details =
          defMessage
            & #message .~ "foo"
            & #details .~ details
  
  {-------------------------------------------------------------------------------
    Protobuf-specific error details (which relies on 'Any')
  -------------------------------------------------------------------------------}
  
  testStatus :: Assertion
  testStatus = testClientServer $ ClientServerTest {
        config = def {
            isExpectedServerException = \e ->
              case fromException e of
                Just err'  -> grpcError err' == GrpcNotFound
                _otherwise -> False
          }
      , server = [
            Server.fromMethod @Ping $ Server.ServerHandler $ \_req ->
              throwProtobufErrorHom protobufError
          ]
      , client = simpleTestClient $ \conn -> do
          mResp :: Either GrpcException (Proto PongMessage) <-
            try $ Client.nonStreaming conn (rpc @Ping) defMessage
          case mResp of
            Right _   -> assertFailure "Expected exception"
            Left  err ->
              assertEqual "" (Right protobufError) $
                toProtobufErrorHom err
      }
    where
      protobufError :: ProtobufError (Proto A)
      protobufError = ProtobufError {
            protobufErrorCode    = GrpcNotFound
          , protobufErrorMessage = Just "Not found"
          , protobufErrorDetails = [detail1, detail2]
          }
  
  
      detail1, detail2 :: Proto A
      detail1 = defMessage & #a .~ 1
      detail2 = defMessage & #a .~ 2
  