module Convex.Tasty.Streaming.Types (
  Event (..),
  TestInfo (..),
  TestOutcome (..),
  FailureInfo (..),
) where

import Convex.Tasty.Streaming.TMSummary (ThreatModelSummary)
import Data.Aeson (ToJSON (..), Value, object, (.=))
import Data.Aeson.Types (Pair)
import Data.Text (Text)
import GHC.Generics (Generic)

-- | Information about a single test in the tree
data TestInfo = TestInfo
  { TestInfo -> Int
tiId :: !Int
  , TestInfo -> Text
tiName :: !Text
  , TestInfo -> [Text]
tiPath :: ![Text]
  }
  deriving (TestInfo -> TestInfo -> Bool
(TestInfo -> TestInfo -> Bool)
-> (TestInfo -> TestInfo -> Bool) -> Eq TestInfo
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TestInfo -> TestInfo -> Bool
== :: TestInfo -> TestInfo -> Bool
$c/= :: TestInfo -> TestInfo -> Bool
/= :: TestInfo -> TestInfo -> Bool
Eq, Int -> TestInfo -> ShowS
[TestInfo] -> ShowS
TestInfo -> String
(Int -> TestInfo -> ShowS)
-> (TestInfo -> String) -> ([TestInfo] -> ShowS) -> Show TestInfo
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TestInfo -> ShowS
showsPrec :: Int -> TestInfo -> ShowS
$cshow :: TestInfo -> String
show :: TestInfo -> String
$cshowList :: [TestInfo] -> ShowS
showList :: [TestInfo] -> ShowS
Show, (forall x. TestInfo -> Rep TestInfo x)
-> (forall x. Rep TestInfo x -> TestInfo) -> Generic TestInfo
forall x. Rep TestInfo x -> TestInfo
forall x. TestInfo -> Rep TestInfo x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. TestInfo -> Rep TestInfo x
from :: forall x. TestInfo -> Rep TestInfo x
$cto :: forall x. Rep TestInfo x -> TestInfo
to :: forall x. Rep TestInfo x -> TestInfo
Generic)

instance ToJSON TestInfo where
  toJSON :: TestInfo -> Value
toJSON (TestInfo Int
i Text
n [Text]
p) =
    [Pair] -> Value
object
      [ Key
"id" Key -> Int -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Int
i
      , Key
"name" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Text
n
      , Key
"path" Key -> [Text] -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= [Text]
p
      ]

-- | Outcome of a completed test
data TestOutcome
  = TestSuccess
  | TestFailure !FailureInfo
  deriving (TestOutcome -> TestOutcome -> Bool
(TestOutcome -> TestOutcome -> Bool)
-> (TestOutcome -> TestOutcome -> Bool) -> Eq TestOutcome
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TestOutcome -> TestOutcome -> Bool
== :: TestOutcome -> TestOutcome -> Bool
$c/= :: TestOutcome -> TestOutcome -> Bool
/= :: TestOutcome -> TestOutcome -> Bool
Eq, Int -> TestOutcome -> ShowS
[TestOutcome] -> ShowS
TestOutcome -> String
(Int -> TestOutcome -> ShowS)
-> (TestOutcome -> String)
-> ([TestOutcome] -> ShowS)
-> Show TestOutcome
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TestOutcome -> ShowS
showsPrec :: Int -> TestOutcome -> ShowS
$cshow :: TestOutcome -> String
show :: TestOutcome -> String
$cshowList :: [TestOutcome] -> ShowS
showList :: [TestOutcome] -> ShowS
Show, (forall x. TestOutcome -> Rep TestOutcome x)
-> (forall x. Rep TestOutcome x -> TestOutcome)
-> Generic TestOutcome
forall x. Rep TestOutcome x -> TestOutcome
forall x. TestOutcome -> Rep TestOutcome x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. TestOutcome -> Rep TestOutcome x
from :: forall x. TestOutcome -> Rep TestOutcome x
$cto :: forall x. Rep TestOutcome x -> TestOutcome
to :: forall x. Rep TestOutcome x -> TestOutcome
Generic)

-- | Details about a test failure
data FailureInfo = FailureInfo
  { FailureInfo -> Text
fiReason :: !Text
  , FailureInfo -> Text
fiMessage :: !Text
  }
  deriving (FailureInfo -> FailureInfo -> Bool
(FailureInfo -> FailureInfo -> Bool)
-> (FailureInfo -> FailureInfo -> Bool) -> Eq FailureInfo
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: FailureInfo -> FailureInfo -> Bool
== :: FailureInfo -> FailureInfo -> Bool
$c/= :: FailureInfo -> FailureInfo -> Bool
/= :: FailureInfo -> FailureInfo -> Bool
Eq, Int -> FailureInfo -> ShowS
[FailureInfo] -> ShowS
FailureInfo -> String
(Int -> FailureInfo -> ShowS)
-> (FailureInfo -> String)
-> ([FailureInfo] -> ShowS)
-> Show FailureInfo
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> FailureInfo -> ShowS
showsPrec :: Int -> FailureInfo -> ShowS
$cshow :: FailureInfo -> String
show :: FailureInfo -> String
$cshowList :: [FailureInfo] -> ShowS
showList :: [FailureInfo] -> ShowS
Show, (forall x. FailureInfo -> Rep FailureInfo x)
-> (forall x. Rep FailureInfo x -> FailureInfo)
-> Generic FailureInfo
forall x. Rep FailureInfo x -> FailureInfo
forall x. FailureInfo -> Rep FailureInfo x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. FailureInfo -> Rep FailureInfo x
from :: forall x. FailureInfo -> Rep FailureInfo x
$cto :: forall x. Rep FailureInfo x -> FailureInfo
to :: forall x. Rep FailureInfo x -> FailureInfo
Generic)

instance ToJSON FailureInfo where
  toJSON :: FailureInfo -> Value
toJSON (FailureInfo Text
r Text
m) =
    [Pair] -> Value
object
      [ Key
"reason" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Text
r
      , Key
"message" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Text
m
      ]

-- | A streaming event emitted as a single NDJSON line
data Event
  = SuiteStarted
      { Event -> [TestInfo]
esTests :: ![TestInfo]
      }
  | TestStarted
      { Event -> Int
etId :: !Int
      }
  | TestProgress
      { Event -> Int
epId :: !Int
      , Event -> Text
epMessage :: !Text
      , Event -> Float
epPercent :: !Float
      }
  | TestDone
      { Event -> Int
edId :: !Int
      , Event -> TestOutcome
edOutcome :: !TestOutcome
      , Event -> Double
edDuration :: !Double
      , Event -> Text
edDescription :: !Text
      , Event -> Maybe ThreatModelSummary
edThreatModel :: !(Maybe ThreatModelSummary)
      }
  | TestTrace
      { Event -> Int
ettTestId :: !Int
      , Event -> Text
ettCategory :: !Text
      , Event -> Value
ettTrace :: !Value -- pre-serialized IterationTrace JSON
      }
  | SuiteDone
      { Event -> Int
esPassed :: !Int
      , Event -> Int
esFailed :: !Int
      , Event -> Double
esDuration :: !Double
      }
  deriving (Event -> Event -> Bool
(Event -> Event -> Bool) -> (Event -> Event -> Bool) -> Eq Event
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Event -> Event -> Bool
== :: Event -> Event -> Bool
$c/= :: Event -> Event -> Bool
/= :: Event -> Event -> Bool
Eq, Int -> Event -> ShowS
[Event] -> ShowS
Event -> String
(Int -> Event -> ShowS)
-> (Event -> String) -> ([Event] -> ShowS) -> Show Event
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Event -> ShowS
showsPrec :: Int -> Event -> ShowS
$cshow :: Event -> String
show :: Event -> String
$cshowList :: [Event] -> ShowS
showList :: [Event] -> ShowS
Show, (forall x. Event -> Rep Event x)
-> (forall x. Rep Event x -> Event) -> Generic Event
forall x. Rep Event x -> Event
forall x. Event -> Rep Event x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Event -> Rep Event x
from :: forall x. Event -> Rep Event x
$cto :: forall x. Rep Event x -> Event
to :: forall x. Rep Event x -> Event
Generic)

instance ToJSON Event where
  toJSON :: Event -> Value
toJSON (SuiteStarted [TestInfo]
ts) =
    [Pair] -> Value
object
      [ Key
"event" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= (Text
"suite_started" :: Text)
      , Key
"tests" Key -> [TestInfo] -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= [TestInfo]
ts
      ]
  toJSON (TestStarted Int
i) =
    [Pair] -> Value
object
      [ Key
"event" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= (Text
"test_started" :: Text)
      , Key
"id" Key -> Int -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Int
i
      ]
  toJSON (TestProgress Int
i Text
msg Float
pct) =
    [Pair] -> Value
object
      [ Key
"event" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= (Text
"test_progress" :: Text)
      , Key
"id" Key -> Int -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Int
i
      , Key
"message" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Text
msg
      , Key
"percent" Key -> Float -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Float
pct
      ]
  toJSON (TestDone Int
i TestOutcome
outcome Double
dur Text
desc Maybe ThreatModelSummary
mTm) =
    [Pair] -> Value
object ([Pair] -> Value) -> [Pair] -> Value
forall a b. (a -> b) -> a -> b
$
      [ Key
"event" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= (Text
"test_done" :: Text)
      , Key
"id" Key -> Int -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Int
i
      , Key
"duration" Key -> Double -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Double
dur
      , Key
"description" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Text
desc
      ]
        [Pair] -> [Pair] -> [Pair]
forall a. Semigroup a => a -> a -> a
<> TestOutcome -> [Pair]
forall {e} {a}. KeyValue e a => TestOutcome -> [a]
outcomeFields TestOutcome
outcome
        [Pair] -> [Pair] -> [Pair]
forall a. Semigroup a => a -> a -> a
<> Maybe ThreatModelSummary -> [Pair]
threatModelFields Maybe ThreatModelSummary
mTm
   where
    outcomeFields :: TestOutcome -> [a]
outcomeFields TestOutcome
TestSuccess = [Key
"success" Key -> Bool -> a
forall v. ToJSON v => Key -> v -> a
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Bool
True]
    outcomeFields (TestFailure FailureInfo
fi) =
      [ Key
"success" Key -> Bool -> a
forall v. ToJSON v => Key -> v -> a
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Bool
False
      , Key
"failure" Key -> FailureInfo -> a
forall v. ToJSON v => Key -> v -> a
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= FailureInfo
fi
      ]
    threatModelFields :: Maybe ThreatModelSummary -> [Pair]
    threatModelFields :: Maybe ThreatModelSummary -> [Pair]
threatModelFields = [Pair]
-> (ThreatModelSummary -> [Pair])
-> Maybe ThreatModelSummary
-> [Pair]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] (\ThreatModelSummary
s -> [Key
"threat_model" Key -> ThreatModelSummary -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= ThreatModelSummary
s])
  toJSON (TestTrace Int
i Text
cat Value
trace) =
    [Pair] -> Value
object
      [ Key
"event" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= (Text
"test_trace" :: Text)
      , Key
"id" Key -> Int -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Int
i
      , Key
"category" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Text
cat
      , Key
"trace" Key -> Value -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Value
trace
      ]
  toJSON (SuiteDone Int
p Int
f Double
dur) =
    [Pair] -> Value
object
      [ Key
"event" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= (Text
"suite_done" :: Text)
      , Key
"passed" Key -> Int -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Int
p
      , Key
"failed" Key -> Int -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Int
f
      , Key
"duration" Key -> Double -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Double
dur
      ]