packages feed

freckle-app-1.0.4.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
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\""