packages feed

freckle-app-1.10.6.0: tests/Freckle/App/BugsnagSpec.hs

module Freckle.App.BugsnagSpec
  ( spec
  ) where

import Freckle.App.Prelude

import Data.ByteString (ByteString)
import Database.PostgreSQL.Simple (ExecStatus (..), SqlError (..))
import Freckle.App.Bugsnag.SqlError (sqlErrorGroupingHash)
import Test.Hspec

spec :: Spec
spec = do
  describe "sqlErrorGroupingHash" $ do
    it "groups duplicate key errors by constraint name" $ do
      let
        err1 =
          sqlError
            { sqlState = duplicateKeyState
            , sqlErrorMsg = duplicateKeyErrorMsg "table_a_id_key"
            , sqlErrorDetail = "Key (a, b)=(1, 2) already exists"
            }
        err2 =
          sqlError
            { sqlState = duplicateKeyState
            , sqlErrorMsg = duplicateKeyErrorMsg "table_a_id_key"
            , sqlErrorDetail = "Key (a, b)=(3, 2) already exists"
            }
        err3 =
          sqlError
            { sqlState = duplicateKeyState
            , sqlErrorMsg = duplicateKeyErrorMsg "table_b_id_key"
            , sqlErrorDetail = "Key (a, b)=(1, 2) already exists"
            }

      -- All duplicate keys hashed
      sqlErrorGroupingHash err1 `shouldSatisfy` isJust
      sqlErrorGroupingHash err2 `shouldSatisfy` isJust
      sqlErrorGroupingHash err3 `shouldSatisfy` isJust

      -- 1 and 2 are grouped, 3 is not (different constraint)
      sqlErrorGroupingHash err1 `shouldBe` sqlErrorGroupingHash err2
      sqlErrorGroupingHash err1 `shouldNotBe` sqlErrorGroupingHash err3
      sqlErrorGroupingHash err2 `shouldNotBe` sqlErrorGroupingHash err3

    it "groups foreign key errors by constraint name" $ do
      let
        err1 =
          sqlError
            { sqlState = foreignKeyState
            , sqlErrorMsg = foreignKeyErrorMsg "table_a" "table_b_id_fkey"
            , sqlErrorDetail = "Key (a, b)=(1, 2) is still referenced"
            }
        err2 =
          sqlError
            { sqlState = foreignKeyState
            , sqlErrorMsg = foreignKeyErrorMsg "table_a" "table_b_id_fkey"
            , sqlErrorDetail = "Key (a, b)=(3, 2) is still referenced"
            }
        err3 =
          sqlError
            { sqlState = foreignKeyState
            , sqlErrorMsg = foreignKeyErrorMsg "table_b" "table_b_id_key"
            , sqlErrorDetail = "Key (a, b)=(1, 2) is still referenced"
            }
        err4 =
          sqlError
            { sqlState = foreignKeyState
            , sqlErrorMsg = foreignKeyErrorMsg "table_a" "table_a_id_key"
            , sqlErrorDetail = "Key (a, b)=(1, 2) is still referenced"
            }

      -- All errors hashed
      sqlErrorGroupingHash err1 `shouldSatisfy` isJust
      sqlErrorGroupingHash err2 `shouldSatisfy` isJust
      sqlErrorGroupingHash err3 `shouldSatisfy` isJust
      sqlErrorGroupingHash err4 `shouldSatisfy` isJust

      -- 1 and 2 are grouped, 3 and 4 are not (different table or constraint)
      sqlErrorGroupingHash err1 `shouldBe` sqlErrorGroupingHash err2
      sqlErrorGroupingHash err1 `shouldNotBe` sqlErrorGroupingHash err3
      sqlErrorGroupingHash err2 `shouldNotBe` sqlErrorGroupingHash err3
      sqlErrorGroupingHash err1 `shouldNotBe` sqlErrorGroupingHash err4
      sqlErrorGroupingHash err2 `shouldNotBe` sqlErrorGroupingHash err4
      sqlErrorGroupingHash err3 `shouldNotBe` sqlErrorGroupingHash err4

    it "does not group other SqlErrors" $ do
      let err =
            sqlError
              { sqlState = "9999"
              , sqlErrorMsg = duplicateKeyErrorMsg "table_a_id_key"
              , sqlErrorDetail = "Key (a, b)=(1, 2) already exists"
              }

      sqlErrorGroupingHash err `shouldBe` Nothing

    it "does not group other states" $ do
      let err =
            sqlError
              { sqlState = "12345"
              , sqlErrorMsg = "State is wrong, \"table_a_id_key\""
              , sqlErrorDetail = "Key (a, b)=(1, 2) already exists"
              }

      sqlErrorGroupingHash err `shouldBe` Nothing

    it "does not group when unable to parse" $ do
      let err =
            sqlError
              { sqlState = foreignKeyState
              , sqlErrorMsg = "FK needs two quoted values, \"table_a_id_fkey\""
              , sqlErrorDetail = "Key (a, b)=(1, 2) already exists"
              }

      sqlErrorGroupingHash err `shouldBe` Nothing

sqlError :: SqlError
sqlError =
  SqlError
    { sqlState = ""
    , sqlExecStatus = FatalError
    , sqlErrorMsg = ""
    , sqlErrorDetail = ""
    , sqlErrorHint = ""
    }

duplicateKeyState :: ByteString
duplicateKeyState = "23505"

duplicateKeyErrorMsg :: ByteString -> ByteString
duplicateKeyErrorMsg constraint =
  "duplicate key value violates unique constraint \"" <> constraint <> "\""

foreignKeyState :: ByteString
foreignKeyState = "23503"

foreignKeyErrorMsg :: ByteString -> ByteString -> ByteString
foreignKeyErrorMsg table constraint =
  "update or delete on table \""
    <> table
    <> "\" violates foreign key constraint \""
    <> constraint
    <> "\" on table \"unused\""