packages feed

postgrest-8.0.0: test/Feature/RollbackSpec.hs

module Feature.RollbackSpec where

import Network.Wai (Application)

import Network.HTTP.Types
import Test.Hspec
import Test.Hspec.Wai
import Test.Hspec.Wai.JSON

import Protolude hiding (get)

-- two helpers functions to make sure that each test can setup and cleanup properly

-- creates Item to work with for PATCH and DELETE
postItem =
  request methodPost "/items"
      [("Prefer", "tx=commit"), ("Prefer", "resolution=ignore-duplicates")]
      [json|{"id":0}|]
    `shouldRespondWith`
      ""
      { matchStatus  = 201 }

-- removes Items left over from POST, PUT, and PATCH
deleteItems =
  request methodDelete "/items?id=lte.0"
      [("Prefer", "tx=commit")]
      ""
    `shouldRespondWith`
      ""
      { matchStatus  = 204 }

preferDefault  = [("Prefer", "return=representation")]
preferCommit   = [("Prefer", "return=representation"), ("Prefer", "tx=commit")]
preferRollback = [("Prefer", "return=representation"), ("Prefer", "tx=rollback")]

withoutPreferenceApplied      = []
withPreferenceCommitApplied   = [ "Preference-Applied" <:> "tx=commit" ]
withPreferenceRollbackApplied = [ "Preference-Applied" <:> "tx=rollback" ]

shouldRespondToReads reqHeaders respHeaders = do
  it "responds to GET" $ do
    request methodGet "/items?id=eq.1"
        reqHeaders
        ""
      `shouldRespondWith`
        [json|[{"id":1}]|]
        { matchHeaders = respHeaders }

  it "responds to HEAD" $ do
    request methodHead "/items?id=eq.1"
        reqHeaders
        ""
      `shouldRespondWith`
        ""
        { matchHeaders = respHeaders }

  it "responds to GET on RPC" $ do
    request methodGet "/rpc/search?id=1"
        reqHeaders
        ""
      `shouldRespondWith`
        [json|[{"id":1}]|]
        { matchHeaders = respHeaders }

  it "responds to POST on RPC" $ do
    request methodPost "/rpc/search"
        reqHeaders
        [json|{"id":1}|]
      `shouldRespondWith`
        [json|[{"id":1}]|]
        { matchHeaders = respHeaders }

shouldPersistMutations reqHeaders respHeaders = do
  it "does persist post" $ do
    request methodPost "/items"
        reqHeaders
        [json|{"id":0}|]
      `shouldRespondWith`
        [json|[{"id":0}]|]
        { matchStatus  = 201
        , matchHeaders = respHeaders }
    get "/items?id=eq.0"
      `shouldRespondWith`
        [json|[{"id":0}]|]
    deleteItems

  it "does persist put" $ do
    request methodPut "/items?id=eq.0"
        reqHeaders
        [json|{"id":0}|]
      `shouldRespondWith`
        [json|[{"id":0}]|]
        { matchHeaders = respHeaders }
    get "/items?id=eq.0"
      `shouldRespondWith`
        [json|[{"id":0}]|]
    deleteItems

  it "does persist patch" $ do
    postItem
    request methodPatch "/items?id=eq.0"
        reqHeaders
        [json|{"id":-1}|]
      `shouldRespondWith`
        [json|[{"id":-1}]|]
        { matchHeaders = respHeaders }
    get "/items?id=eq.0"
      `shouldRespondWith`
        [json|[]|]
    get "/items?id=eq.-1"
      `shouldRespondWith`
        [json|[{"id":-1}]|]
    deleteItems

  it "does persist delete" $ do
    postItem
    request methodDelete "/items?id=eq.0"
        reqHeaders
        ""
      `shouldRespondWith`
        [json|[{"id":0}]|]
        { matchHeaders = respHeaders }
    get "/items?id=eq.0"
      `shouldRespondWith`
        [json|[]|]

shouldNotPersistMutations reqHeaders respHeaders = do
  it "does not persist post" $ do
    request methodPost "/items"
        reqHeaders
        [json|{"id":0}|]
      `shouldRespondWith`
        [json|[{"id":0}]|]
        { matchStatus  = 201
        , matchHeaders = respHeaders }
    get "/items?id=eq.0"
      `shouldRespondWith`
        [json|[]|]

  it "does not persist put" $ do
    request methodPut "/items?id=eq.0"
        reqHeaders
        [json|{"id":0}|]
      `shouldRespondWith`
        [json|[{"id":0}]|]
        { matchHeaders = respHeaders }
    get "/items?id=eq.0"
      `shouldRespondWith`
        [json|[]|]

  it "does not persist patch" $ do
    request methodPatch "/items?id=eq.1"
        reqHeaders
        [json|{"id":0}|]
      `shouldRespondWith`
        [json|[{"id":0}]|]
        { matchHeaders = respHeaders }
    get "/items?id=eq.0"
      `shouldRespondWith`
        [json|[]|]
    get "items?id=eq.1"
      `shouldRespondWith`
        [json|[{"id":1}]|]

  it "does not persist delete" $ do
    request methodDelete "/items?id=eq.1"
        reqHeaders
        ""
      `shouldRespondWith`
        [json|[{"id":1}]|]
        { matchHeaders = respHeaders }
    get "/items?id=eq.1"
      `shouldRespondWith`
        [json|[{"id":1}]|]

allowed :: SpecWith ((), Application)
allowed = describe "tx-allow-override = true" $ do
  describe "without Prefer tx" $ do
    preferDefault `shouldRespondToReads` withoutPreferenceApplied
    preferDefault `shouldNotPersistMutations` withoutPreferenceApplied

  describe "Prefer tx=commit" $ do
    preferCommit `shouldRespondToReads` withPreferenceCommitApplied
    preferCommit `shouldPersistMutations` withPreferenceCommitApplied

  describe "Prefer tx=rollback" $ do
    preferRollback `shouldRespondToReads` withPreferenceRollbackApplied
    preferRollback `shouldNotPersistMutations` withPreferenceRollbackApplied

disallowed :: SpecWith ((), Application)
disallowed = describe "tx-rollback-all = false, tx-allow-override = false" $ do
  describe "without Prefer tx" $ do
    preferDefault `shouldRespondToReads` withoutPreferenceApplied
    preferDefault `shouldPersistMutations` withoutPreferenceApplied

  describe "Prefer tx=commit" $ do
    preferCommit `shouldRespondToReads` withoutPreferenceApplied
    preferCommit `shouldPersistMutations` withoutPreferenceApplied

  describe "Prefer tx=rollback" $ do
    preferRollback `shouldRespondToReads` withoutPreferenceApplied
    preferRollback `shouldPersistMutations` withoutPreferenceApplied


forced :: SpecWith ((), Application)
forced = describe "tx-rollback-all = true, tx-allow-override = false" $ do
  describe "without Prefer tx" $ do
    preferDefault `shouldRespondToReads` withoutPreferenceApplied
    preferDefault `shouldNotPersistMutations` withoutPreferenceApplied

  describe "Prefer tx=commit" $ do
    preferCommit `shouldRespondToReads` withoutPreferenceApplied
    preferCommit `shouldNotPersistMutations` withoutPreferenceApplied

  describe "Prefer tx=rollback" $ do
    preferRollback `shouldRespondToReads` withoutPreferenceApplied
    preferRollback `shouldNotPersistMutations` withoutPreferenceApplied