module Test.Swagger where

import qualified API.Brig as BrigP
import qualified Data.Set as Set
import Data.String.Conversions
import GHC.Stack
import Testlib.Assertions
import Testlib.Prelude

existingVersions :: Set Int
existingVersions :: Set Int
existingVersions = [Int] -> Set Int
forall a. Ord a => [a] -> Set a
Set.fromList [Int
0, Int
1, Int
2, Int
3, Int
4, Int
5, Int
6, Int
7]

internalApis :: Set String
internalApis :: Set String
internalApis = [String] -> Set String
forall a. Ord a => [a] -> Set a
Set.fromList [String
"brig", String
"cannon", String
"cargohold", String
"cannon", String
"spar"]

-- | See https://docs.wire.com/understand/api-client-perspective/swagger.html
testSwagger :: (HasCallStack) => App ()
testSwagger :: HasCallStack => App ()
testSwagger = do
  App Response -> (Response -> App ()) -> App ()
forall a.
HasCallStack =>
App Response -> (Response -> App a) -> App a
bindResponse App Response
HasCallStack => App Response
BrigP.getApiVersions ((Response -> App ()) -> App ()) -> (Response -> App ()) -> App ()
forall a b. (a -> b) -> a -> b
$ \Response
resp -> do
    Response
resp.status Int -> Int -> App ()
forall a. (MakesValue a, HasCallStack) => a -> Int -> App ()
`shouldMatchInt` Int
200
    Set Int
actualVersions :: Set Int <- do
      Set Int
sup <- Response
resp.json App Value -> String -> App Value
forall a. (HasCallStack, MakesValue a) => a -> String -> App Value
%. String
"supported" App Value -> (App Value -> App (Set Int)) -> App (Set Int)
forall a b. a -> (a -> b) -> b
& (Value -> App Int)
-> MakesValue (App Value) => App Value -> App (Set Int)
forall b a.
(HasCallStack, Ord b) =>
(Value -> App b) -> MakesValue a => a -> App (Set b)
asSetOf Value -> App Int
forall i a. (Integral i, HasCallStack, MakesValue a) => a -> App i
asIntegral
      Set Int
dev <- Response
resp.json App Value -> String -> App Value
forall a. (HasCallStack, MakesValue a) => a -> String -> App Value
%. String
"development" App Value -> (App Value -> App (Set Int)) -> App (Set Int)
forall a b. a -> (a -> b) -> b
& (Value -> App Int)
-> MakesValue (App Value) => App Value -> App (Set Int)
forall b a.
(HasCallStack, Ord b) =>
(Value -> App b) -> MakesValue a => a -> App (Set b)
asSetOf Value -> App Int
forall i a. (Integral i, HasCallStack, MakesValue a) => a -> App i
asIntegral
      Set Int -> App (Set Int)
forall a. a -> App a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Set Int -> App (Set Int)) -> Set Int -> App (Set Int)
forall a b. (a -> b) -> a -> b
$ Set Int
sup Set Int -> Set Int -> Set Int
forall a. Semigroup a => a -> a -> a
<> Set Int
dev
    HasCallStack => String -> Bool -> App ()
String -> Bool -> App ()
assertBool (String
"unexpected actually existing versions: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Set Int -> String
forall a. Show a => a -> String
show Set Int
actualVersions)
      (Bool -> App ()) -> Bool -> App ()
forall a b. (a -> b) -> a -> b
$
      -- make sure nobody has added a new version without adding it to `existingVersions`.
      -- ("subset" because blocked versions like v3 are not actually existing, but still
      -- documented.)
      Set Int
actualVersions
      Set Int -> Set Int -> Bool
forall a. Ord a => Set a -> Set a -> Bool
`Set.isSubsetOf` Set Int
existingVersions

  App Response -> (Response -> App ()) -> App ()
forall a.
HasCallStack =>
App Response -> (Response -> App a) -> App a
bindResponse App Response
HasCallStack => App Response
BrigP.getSwaggerPublicTOC ((Response -> App ()) -> App ()) -> (Response -> App ()) -> App ()
forall a b. (a -> b) -> a -> b
$ \Response
resp -> do
    Response
resp.status Int -> Int -> App ()
forall a. (MakesValue a, HasCallStack) => a -> Int -> App ()
`shouldMatchInt` Int
200
    ByteString -> String
forall a b. ConvertibleStrings a b => a -> b
cs Response
resp.body HasCallStack => String -> String -> App ()
String -> String -> App ()
`shouldContainString` String
"<html>"

  Set Int -> (Int -> App ()) -> App ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ Set Int
existingVersions ((Int -> App ()) -> App ()) -> (Int -> App ()) -> App ()
forall a b. (a -> b) -> a -> b
$ \Int
v -> do
    App Response -> (Response -> App ()) -> App ()
forall a.
HasCallStack =>
App Response -> (Response -> App a) -> App a
bindResponse (HasCallStack => Int -> App Response
Int -> App Response
BrigP.getSwaggerPublicAllUI Int
v) ((Response -> App ()) -> App ()) -> (Response -> App ()) -> App ()
forall a b. (a -> b) -> a -> b
$ \Response
resp -> do
      Response
resp.status Int -> Int -> App ()
forall a. (MakesValue a, HasCallStack) => a -> Int -> App ()
`shouldMatchInt` Int
200
      ByteString -> String
forall a b. ConvertibleStrings a b => a -> b
cs Response
resp.body HasCallStack => String -> String -> App ()
String -> String -> App ()
`shouldContainString` String
"<!DOCTYPE html>"
    App Response -> (Response -> App ()) -> App ()
forall a.
HasCallStack =>
App Response -> (Response -> App a) -> App a
bindResponse (HasCallStack => Int -> App Response
Int -> App Response
BrigP.getSwaggerPublicAllJson Int
v) ((Response -> App ()) -> App ()) -> (Response -> App ()) -> App ()
forall a b. (a -> b) -> a -> b
$ \Response
resp -> do
      Response
resp.status Int -> Int -> App ()
forall a. (MakesValue a, HasCallStack) => a -> Int -> App ()
`shouldMatchInt` Int
200
      App Value -> App ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void Response
resp.json

  -- !
  -- FUTUREWORK: Implement BrigP.getSwaggerInternalTOC (including the end-point); make sure
  -- newly added internal APIs make this test fail if not added to `internalApis`.

  Set String -> (String -> App ()) -> App ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ Set String
internalApis ((String -> App ()) -> App ()) -> (String -> App ()) -> App ()
forall a b. (a -> b) -> a -> b
$ \String
api -> do
    App Response -> (Response -> App ()) -> App ()
forall a.
HasCallStack =>
App Response -> (Response -> App a) -> App a
bindResponse (HasCallStack => String -> App Response
String -> App Response
BrigP.getSwaggerInternalUI String
api) ((Response -> App ()) -> App ()) -> (Response -> App ()) -> App ()
forall a b. (a -> b) -> a -> b
$ \Response
resp -> do
      Response
resp.status Int -> Int -> App ()
forall a. (MakesValue a, HasCallStack) => a -> Int -> App ()
`shouldMatchInt` Int
200
      ByteString -> String
forall a b. ConvertibleStrings a b => a -> b
cs Response
resp.body HasCallStack => String -> String -> App ()
String -> String -> App ()
`shouldContainString` String
"<!DOCTYPE html>"
    App Response -> (Response -> App ()) -> App ()
forall a.
HasCallStack =>
App Response -> (Response -> App a) -> App a
bindResponse (HasCallStack => String -> App Response
String -> App Response
BrigP.getSwaggerInternalJson String
api) ((Response -> App ()) -> App ()) -> (Response -> App ()) -> App ()
forall a b. (a -> b) -> a -> b
$ \Response
resp -> do
      Response
resp.status Int -> Int -> App ()
forall a. (MakesValue a, HasCallStack) => a -> Int -> App ()
`shouldMatchInt` Int
200
      App Value -> App ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void Response
resp.json

testSwaggerInternalVersionedNotFound :: (HasCallStack) => App ()
testSwaggerInternalVersionedNotFound :: HasCallStack => App ()
testSwaggerInternalVersionedNotFound = do
  Set String -> (String -> App ()) -> App ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ Set String
internalApis ((String -> App ()) -> App ()) -> (String -> App ()) -> App ()
forall a b. (a -> b) -> a -> b
$ \String
api -> do
    App Response -> (Response -> App ()) -> App ()
forall a.
HasCallStack =>
App Response -> (Response -> App a) -> App a
bindResponse (String -> App Response
getSwaggerInternalUI String
api) ((Response -> App ()) -> App ()) -> (Response -> App ()) -> App ()
forall a b. (a -> b) -> a -> b
$ \Response
resp -> do
      Response
resp.status Int -> Int -> App ()
forall a. (MakesValue a, HasCallStack) => a -> Int -> App ()
`shouldMatchInt` Int
404
  where
    getSwaggerInternalUI :: String -> App Response
    getSwaggerInternalUI :: String -> App Response
getSwaggerInternalUI String
srv =
      Domain -> Service -> Versioned -> String -> App Request
forall domain.
(HasCallStack, MakesValue domain) =>
domain -> Service -> Versioned -> String -> App Request
rawBaseRequest Domain
OwnDomain Service
Brig (Int -> Versioned
ExplicitVersion Int
2) ([String] -> String
joinHttpPath [String
"api-internal", String
"swagger-ui", String
srv])
        App Request -> (Request -> App Response) -> App Response
forall a b. App a -> (a -> App b) -> App b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String -> Request -> App Response
submit String
"GET"

testSwaggerToc :: (HasCallStack) => App ()
testSwaggerToc :: HasCallStack => App ()
testSwaggerToc = do
  [String] -> (String -> App ()) -> App ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [String
"/api/swagger-ui", String
"/api/swagger-ui/index.html", String
"/api/swagger.json"] ((String -> App ()) -> App ()) -> (String -> App ()) -> App ()
forall a b. (a -> b) -> a -> b
$ \String
path ->
    App Response -> (Response -> App ()) -> App ()
forall a.
HasCallStack =>
App Response -> (Response -> App a) -> App a
bindResponse (String -> App Response
get String
path) ((Response -> App ()) -> App ()) -> (Response -> App ()) -> App ()
forall a b. (a -> b) -> a -> b
$ \Response
resp -> do
      Response
resp.status Int -> Int -> App ()
forall a. (MakesValue a, HasCallStack) => a -> Int -> App ()
`shouldMatchInt` Int
200
      let body :: String
body = forall a b. ConvertibleStrings a b => a -> b
cs @_ @String Response
resp.body
      String
body String -> String -> App ()
forall a b.
(MakesValue a, MakesValue b, HasCallStack) =>
a -> b -> App ()
`shouldMatch` String
html
      Set Int -> (Int -> App ()) -> App ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ Set Int
existingVersions ((Int -> App ()) -> App ()) -> (Int -> App ()) -> App ()
forall a b. (a -> b) -> a -> b
$ \Int
v ->
        String
body HasCallStack => String -> String -> App ()
String -> String -> App ()
`shouldContainString` (String
"v" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show Int
v)
  where
    get :: String -> App Response
    get :: String -> App Response
get String
path = Domain -> Service -> Versioned -> String -> App Request
forall domain.
(HasCallStack, MakesValue domain) =>
domain -> Service -> Versioned -> String -> App Request
rawBaseRequest Domain
OwnDomain Service
Brig Versioned
Unversioned String
path App Request -> (Request -> App Response) -> App Response
forall a b. App a -> (a -> App b) -> App b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String -> Request -> App Response
submit String
"GET"

    html :: String
    html :: String
html = String
"<html><head></head><body><h2>please pick an api version</h2><a href=\"/v0/api/swagger-ui/\">/v0/api/swagger-ui/</a><br><a href=\"/v1/api/swagger-ui/\">/v1/api/swagger-ui/</a><br><a href=\"/v2/api/swagger-ui/\">/v2/api/swagger-ui/</a><br><a href=\"/v3/api/swagger-ui/\">/v3/api/swagger-ui/</a><br><a href=\"/v4/api/swagger-ui/\">/v4/api/swagger-ui/</a><br><a href=\"/v5/api/swagger-ui/\">/v5/api/swagger-ui/</a><br><a href=\"/v6/api/swagger-ui/\">/v6/api/swagger-ui/</a><br><a href=\"/v7/api/swagger-ui/\">/v7/api/swagger-ui/</a><br></body>"