topfreegames/khan

View on GitHub
api/membership_test.go

Summary

Maintainability
F
1 wk
Test Coverage
// khan
// https://github.com/topfreegames/khan
//
// Licensed under the MIT license:
// http://www.opensource.org/licenses/mit-license
// Copyright © 2016 Top Free Games <backend@tfgco.com>

package api_test

import (
    "encoding/json"
    "fmt"
    "net/http"

    "github.com/Pallinder/go-randomdata"
    . "github.com/onsi/ginkgo"
    . "github.com/onsi/gomega"
    "github.com/topfreegames/khan/api"
    "github.com/topfreegames/khan/models"
    "github.com/topfreegames/khan/models/fixtures"
)

var _ = Describe("Membership API Handler", func() {
    var testDb, db models.DB
    var a *api.App

    BeforeEach(func() {
        var err error
        testDb, err = GetTestDB()
        Expect(err).NotTo(HaveOccurred())

        a = GetDefaultTestApp()
        db = a.Db(nil)
        fixtures.ConfigureAndStartGoWorkers()
    })

    Describe("Apply For Membership Handler", func() {
        It("Should create membership application", func() {
            _, clan, _, _, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 0, "", "")
            Expect(err).NotTo(HaveOccurred())

            clan.AutoJoin = true
            clan.AllowApplication = true
            _, err = testDb.Update(clan)
            Expect(err).NotTo(HaveOccurred())

            player := fixtures.PlayerFactory.MustCreateWithOption(map[string]interface{}{
                "GameID": clan.GameID,
            }).(*models.Player)
            err = testDb.Insert(player)
            Expect(err).NotTo(HaveOccurred())

            gameID := player.GameID
            clanPublicID := clan.PublicID
            level := "Member"

            payload := map[string]interface{}{
                "level":          level,
                "playerPublicID": player.PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "application"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())
            Expect(result["approved"]).To(BeTrue())

            dbMembership, err := models.GetValidMembershipByClanAndPlayerPublicID(db, gameID, clanPublicID, player.PublicID)
            Expect(err).NotTo(HaveOccurred())
            Expect(dbMembership.GameID).To(Equal(gameID))
            Expect(dbMembership.PlayerID).To(Equal(player.ID))
            Expect(dbMembership.Level).To(Equal(level))
            Expect(dbMembership.ClanID).To(Equal(clan.ID))
            Expect(dbMembership.RequestorID).To(Equal(player.ID))
            Expect(dbMembership.Denied).To(Equal(false))
        })

        It("Should create membership application sending a message", func() {
            _, clan, _, _, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 0, "", "")
            Expect(err).NotTo(HaveOccurred())

            clan.AutoJoin = false
            clan.AllowApplication = true
            _, err = testDb.Update(clan)
            Expect(err).NotTo(HaveOccurred())

            player := fixtures.PlayerFactory.MustCreateWithOption(map[string]interface{}{
                "GameID": clan.GameID,
            }).(*models.Player)
            err = testDb.Insert(player)
            Expect(err).NotTo(HaveOccurred())

            gameID := player.GameID
            clanPublicID := clan.PublicID
            level := "Member"

            payload := map[string]interface{}{
                "level":          level,
                "playerPublicID": player.PublicID,
                "message":        "Please accept me, I am nice",
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "application"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())
            Expect(result["approved"]).To(BeFalse())

            dbMembership, err := models.GetValidMembershipByClanAndPlayerPublicID(db, gameID, clanPublicID, player.PublicID)
            Expect(err).NotTo(HaveOccurred())
            Expect(dbMembership.GameID).To(Equal(gameID))
            Expect(dbMembership.PlayerID).To(Equal(player.ID))
            Expect(dbMembership.Level).To(Equal(level))
            Expect(dbMembership.ClanID).To(Equal(clan.ID))
            Expect(dbMembership.RequestorID).To(Equal(player.ID))
            Expect(dbMembership.Denied).To(Equal(false))
            Expect(dbMembership.Message).To(Equal(payload["message"]))
        })

        It("Should conflict when player is already a member", func() {
            _, clan, owner, players, memberships, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 1, "", "")
            Expect(err).NotTo(HaveOccurred())

            memberships[0].RequestorID = memberships[0].PlayerID
            _, err = testDb.Update(memberships[0])

            gameID := clan.GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "playerPublicID":    players[0].PublicID,
                "requestorPublicID": owner.PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "application/approve"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            dbMembership, err := models.GetValidMembershipByClanAndPlayerPublicID(db, gameID, clanPublicID, players[0].PublicID)
            Expect(err).NotTo(HaveOccurred())
            Expect(dbMembership.Approved).To(Equal(true))
            Expect(dbMembership.Denied).To(Equal(false))

            payload = map[string]interface{}{
                "level":          "Member",
                "playerPublicID": players[0].PublicID,
                "message":        "Please accept me, I am nice",
            }
            status, body = PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "application"), payload)

            Expect(status).To(Equal(http.StatusConflict))
        })

        It("Should create membership application sending a message after player left clan", func() {
            _, clan, _, players, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 1, "", "")
            Expect(err).NotTo(HaveOccurred())

            clan.AllowApplication = true
            _, err = testDb.Update(clan)
            Expect(err).NotTo(HaveOccurred())

            gameID := clan.GameID
            clanPublicID := clan.PublicID
            player := players[0]
            var result map[string]interface{}

            // Leave clan
            payload := map[string]interface{}{
                "playerPublicID":    player.PublicID,
                "requestorPublicID": player.PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "delete"), payload)

            Expect(status).To(Equal(http.StatusOK))
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            // Left clan, trying to apply again

            // Apply to clan
            level := "Member"
            payload = map[string]interface{}{
                "level":          level,
                "playerPublicID": player.PublicID,
                "message":        "Please accept me, I am nice",
            }
            status, body = PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "application"), payload)

            Expect(status).To(Equal(http.StatusOK))
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            dbMembership, err := models.GetValidMembershipByClanAndPlayerPublicID(db, gameID, clanPublicID, player.PublicID)
            Expect(err).NotTo(HaveOccurred())
            Expect(dbMembership.GameID).To(Equal(gameID))
            Expect(dbMembership.PlayerID).To(Equal(player.ID))
            Expect(dbMembership.Level).To(Equal(level))
            Expect(dbMembership.ClanID).To(Equal(clan.ID))
            Expect(dbMembership.RequestorID).To(Equal(player.ID))
            Expect(dbMembership.Denied).To(Equal(false))
            Expect(dbMembership.Message).To(Equal(payload["message"]))
        })

        It("Should delete member", func() {
            _, clan, owner, players, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 1, "", "")
            Expect(err).NotTo(HaveOccurred())

            gameID := clan.GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "playerPublicID":    players[0].PublicID,
                "requestorPublicID": owner.PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "delete"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            _, err = models.GetValidMembershipByClanAndPlayerPublicID(db, gameID, clanPublicID, players[0].PublicID)
            Expect(err).To(HaveOccurred())
            Expect(err.Error()).To(Equal(fmt.Sprintf("Membership was not found with id: %s", players[0].PublicID)))
        })

        It("Should not create membership application if missing parameters", func() {
            status, body := PostJSON(a, CreateMembershipRoute("gameID", "clanPublicID", "application"), map[string]interface{}{})

            Expect(status).To(Equal(http.StatusBadRequest))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(Equal("level is required, playerPublicID is required"))
        })

        It("Should not create membership application if invalid payload", func() {
            gameID := "gameID"
            clanPublicID := randomdata.FullName(randomdata.RandomGender)

            status, body := Post(a, CreateMembershipRoute(gameID, clanPublicID, "application"), "invalid")

            Expect(status).To(Equal(http.StatusBadRequest))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(ContainSubstring(InvalidJSONError))
        })

        It("Should not create membership application if player does not exist", func() {
            playerPublicID := randomdata.FullName(randomdata.RandomGender)
            _, clan, _, _, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 0, "", "")
            Expect(err).NotTo(HaveOccurred())

            gameID := clan.GameID
            clanPublicID := clan.PublicID
            level := "Member"

            payload := map[string]interface{}{
                "level":          level,
                "playerPublicID": playerPublicID,
            }

            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "application"), payload)

            Expect(status).To(Equal(http.StatusNotFound))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(Equal(fmt.Sprintf("Player was not found with id: %s", playerPublicID)))
        })

        It("Should not create membership application if invalid data", func() {
            _, clan, _, _, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 0, "", "")
            Expect(err).NotTo(HaveOccurred())

            player := fixtures.PlayerFactory.MustCreateWithOption(map[string]interface{}{
                "GameID": clan.GameID,
            }).(*models.Player)
            err = testDb.Insert(player)
            Expect(err).NotTo(HaveOccurred())

            gameID := player.GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "level":          1,
                "playerPublicID": player.PublicID,
            }

            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "application"), payload)

            Expect(status).To(Equal(http.StatusBadRequest))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(ContainSubstring("expected string near offset"))
        })
    })

    Describe("Invite For Membership Handler", func() {
        It("Should create membership invitation if clan owner", func() {
            _, clan, owner, _, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 0, "", "")
            Expect(err).NotTo(HaveOccurred())

            player := fixtures.PlayerFactory.MustCreateWithOption(map[string]interface{}{
                "GameID": clan.GameID,
            }).(*models.Player)
            err = testDb.Insert(player)
            Expect(err).NotTo(HaveOccurred())

            gameID := player.GameID
            clanPublicID := clan.PublicID
            level := "Member"

            payload := map[string]interface{}{
                "level":             level,
                "playerPublicID":    player.PublicID,
                "requestorPublicID": owner.PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "invitation"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            dbMembership, err := models.GetValidMembershipByClanAndPlayerPublicID(db, gameID, clanPublicID, player.PublicID)
            Expect(err).NotTo(HaveOccurred())
            Expect(dbMembership.GameID).To(Equal(gameID))
            Expect(dbMembership.PlayerID).To(Equal(player.ID))
            Expect(dbMembership.Level).To(Equal(level))
            Expect(dbMembership.ClanID).To(Equal(clan.ID))
            Expect(dbMembership.RequestorID).To(Equal(owner.ID))
            Expect(dbMembership.Denied).To(Equal(false))
        })

        It("Should create membership invitation sending a message", func() {
            _, clan, owner, _, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 0, "", "")
            Expect(err).NotTo(HaveOccurred())

            player := fixtures.PlayerFactory.MustCreateWithOption(map[string]interface{}{
                "GameID": clan.GameID,
            }).(*models.Player)
            err = testDb.Insert(player)
            Expect(err).NotTo(HaveOccurred())

            gameID := player.GameID
            clanPublicID := clan.PublicID
            level := "Member"

            payload := map[string]interface{}{
                "level":             level,
                "playerPublicID":    player.PublicID,
                "requestorPublicID": owner.PublicID,
                "message":           "Please accept me, I am nice",
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "invitation"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            dbMembership, err := models.GetValidMembershipByClanAndPlayerPublicID(db, gameID, clanPublicID, player.PublicID)
            Expect(err).NotTo(HaveOccurred())
            Expect(dbMembership.GameID).To(Equal(gameID))
            Expect(dbMembership.PlayerID).To(Equal(player.ID))
            Expect(dbMembership.Level).To(Equal(level))
            Expect(dbMembership.ClanID).To(Equal(clan.ID))
            Expect(dbMembership.RequestorID).To(Equal(owner.ID))
            Expect(dbMembership.Denied).To(Equal(false))
            Expect(dbMembership.Message).To(Equal(payload["message"]))
        })

        It("Should create membership invitation if requestor has level greater than min level", func() {
            _, clan, _, players, memberships, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 1, "", "")
            Expect(err).NotTo(HaveOccurred())

            memberships[0].Level = "CoLeader"
            memberships[0].Approved = true
            _, err = testDb.Update(memberships[0])
            Expect(err).NotTo(HaveOccurred())

            player := fixtures.PlayerFactory.MustCreateWithOption(map[string]interface{}{
                "GameID": clan.GameID,
            }).(*models.Player)
            err = testDb.Insert(player)
            Expect(err).NotTo(HaveOccurred())

            gameID := player.GameID
            clanPublicID := clan.PublicID
            level := "Member"

            payload := map[string]interface{}{
                "level":             level,
                "playerPublicID":    player.PublicID,
                "requestorPublicID": players[0].PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "invitation"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            dbMembership, err := models.GetValidMembershipByClanAndPlayerPublicID(db, gameID, clanPublicID, player.PublicID)
            Expect(err).NotTo(HaveOccurred())
            Expect(dbMembership.GameID).To(Equal(gameID))
            Expect(dbMembership.PlayerID).To(Equal(player.ID))
            Expect(dbMembership.Level).To(Equal(level))
            Expect(dbMembership.ClanID).To(Equal(clan.ID))
            Expect(dbMembership.Level).To(Equal(level))
            Expect(dbMembership.RequestorID).To(Equal(players[0].ID))
            Expect(dbMembership.Denied).To(Equal(false))
        })

        It("Should not create membership invitation if missing parameters", func() {
            status, body := PostJSON(a, CreateMembershipRoute("gameID", "clanPublicID", "invitation"), map[string]interface{}{})

            Expect(status).To(Equal(http.StatusBadRequest))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(Equal("level is required, playerPublicID is required, requestorPublicID is required"))
        })

        It("Should not create membership invitation if invalid payload", func() {
            gameID := "gameID"
            clanPublicID := randomdata.FullName(randomdata.RandomGender)

            status, body := Post(a, CreateMembershipRoute(gameID, clanPublicID, "invitation"), "invalid")

            Expect(status).To(Equal(http.StatusBadRequest))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(ContainSubstring(InvalidJSONError))
        })

        It("Should not create membership invitation if player does not exist", func() {
            playerPublicID := randomdata.FullName(randomdata.RandomGender)
            _, clan, owner, _, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 0, "", "")
            Expect(err).NotTo(HaveOccurred())

            gameID := owner.GameID
            clanPublicID := clan.PublicID
            level := "Member"

            payload := map[string]interface{}{
                "level":             level,
                "playerPublicID":    playerPublicID,
                "requestorPublicID": owner.PublicID,
            }

            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "invitation"), payload)

            Expect(status).To(Equal(http.StatusNotFound))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(Equal(fmt.Sprintf("Player was not found with id: %s", playerPublicID)))
        })

        It("Should not create membership invitation if invalid data", func() {
            _, clan, owner, _, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 0, "", "")
            Expect(err).NotTo(HaveOccurred())

            player := fixtures.PlayerFactory.MustCreateWithOption(map[string]interface{}{
                "GameID": clan.GameID,
            }).(*models.Player)
            err = testDb.Insert(player)
            Expect(err).NotTo(HaveOccurred())

            gameID := player.GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "level":             1,
                "playerPublicID":    player.PublicID,
                "requestorPublicID": owner.PublicID,
            }

            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "invitation"), payload)

            Expect(status).To(Equal(http.StatusBadRequest))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(ContainSubstring("expected string near offset"))
        })
    })

    Describe("Approve Or Deny Membership Invitation Handler", func() {
        It("Should approve membership invitation", func() {
            _, clan, _, players, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 1, "", "")
            Expect(err).NotTo(HaveOccurred())

            gameID := clan.GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "playerPublicID": players[0].PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "invitation/approve"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            dbMembership, err := models.GetValidMembershipByClanAndPlayerPublicID(db, gameID, clanPublicID, players[0].PublicID)
            Expect(err).NotTo(HaveOccurred())
            Expect(dbMembership.GameID).To(Equal(gameID))
            Expect(dbMembership.PlayerID).To(Equal(players[0].ID))
            Expect(dbMembership.Approved).To(Equal(true))
            Expect(dbMembership.Denied).To(Equal(false))
        })

        It("Should deny membership invitation", func() {
            _, clan, _, players, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 1, "", "")
            Expect(err).NotTo(HaveOccurred())

            gameID := clan.GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "playerPublicID": players[0].PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "invitation/deny"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            dbMembership, err := models.GetValidMembershipByClanAndPlayerPublicID(db, gameID, clanPublicID, players[0].PublicID)
            Expect(err).NotTo(HaveOccurred())
            Expect(dbMembership.GameID).To(Equal(gameID))
            Expect(dbMembership.PlayerID).To(Equal(players[0].ID))
            Expect(dbMembership.Approved).To(Equal(false))
            Expect(dbMembership.Denied).To(Equal(true))
        })

        It("Should not approve membership invitation if missing parameters", func() {
            status, body := PostJSON(a, CreateMembershipRoute("gameID", "clanPublicID", "invitation/approve"), map[string]interface{}{})

            Expect(status).To(Equal(http.StatusBadRequest))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(Equal("playerPublicID is required"))
        })

        It("Should not approve membership invitation if invalid payload", func() {
            gameID := "gameID"
            clanPublicID := randomdata.FullName(randomdata.RandomGender)

            status, body := Post(a, CreateMembershipRoute(gameID, clanPublicID, "invitation/approve"), "invalid")

            Expect(status).To(Equal(http.StatusBadRequest))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(ContainSubstring(InvalidJSONError))
        })

        It("Should not approve membership invitation if player does not exist", func() {
            playerPublicID := randomdata.FullName(randomdata.RandomGender)
            _, clan, owner, _, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 0, "", "")
            Expect(err).NotTo(HaveOccurred())

            gameID := owner.GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "playerPublicID": playerPublicID,
            }

            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "invitation/approve"), payload)

            Expect(status).To(Equal(http.StatusNotFound))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(Equal(fmt.Sprintf("Membership was not found with id: %s", playerPublicID)))
        })
    })

    Describe("Approve Or Deny Membership Application Handler", func() {
        It("Should approve membership application", func() {
            _, clan, owner, players, memberships, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 1, "", "")
            Expect(err).NotTo(HaveOccurred())

            memberships[0].RequestorID = memberships[0].PlayerID
            _, err = testDb.Update(memberships[0])

            gameID := clan.GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "playerPublicID":    players[0].PublicID,
                "requestorPublicID": owner.PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "application/approve"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            dbMembership, err := models.GetValidMembershipByClanAndPlayerPublicID(db, gameID, clanPublicID, players[0].PublicID)
            Expect(err).NotTo(HaveOccurred())
            Expect(dbMembership.Approved).To(Equal(true))
            Expect(dbMembership.Denied).To(Equal(false))
        })

        It("Should conflict when player is already a member", func() {
            _, clan, owner, players, memberships, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 1, "", "")
            Expect(err).NotTo(HaveOccurred())

            memberships[0].RequestorID = memberships[0].PlayerID
            _, err = testDb.Update(memberships[0])

            gameID := clan.GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "playerPublicID":    players[0].PublicID,
                "requestorPublicID": owner.PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "application/approve"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            dbMembership, err := models.GetValidMembershipByClanAndPlayerPublicID(db, gameID, clanPublicID, players[0].PublicID)
            Expect(err).NotTo(HaveOccurred())
            Expect(dbMembership.Approved).To(Equal(true))
            Expect(dbMembership.Denied).To(Equal(false))

            status, body = PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "application/approve"), payload)
            Expect(status).To(Equal(http.StatusConflict))
        })

        It("Should deny membership application", func() {
            _, clan, owner, players, memberships, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 1, "", "")
            Expect(err).NotTo(HaveOccurred())

            memberships[0].RequestorID = memberships[0].PlayerID
            _, err = testDb.Update(memberships[0])

            gameID := players[0].GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "playerPublicID":    players[0].PublicID,
                "requestorPublicID": owner.PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "application/deny"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            dbMembership, err := models.GetValidMembershipByClanAndPlayerPublicID(db, gameID, clanPublicID, players[0].PublicID)
            Expect(err).NotTo(HaveOccurred())
            Expect(dbMembership.Approved).To(Equal(false))
            Expect(dbMembership.Denied).To(Equal(true))
        })

        It("Should not approve membership application if missing parameters", func() {
            status, body := PostJSON(a, CreateMembershipRoute("gameID", "clanPublicID", "application/approve"), map[string]interface{}{})

            Expect(status).To(Equal(http.StatusBadRequest))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(Equal("playerPublicID is required, requestorPublicID is required"))
        })

        It("Should not approve membership application if invalid payload", func() {
            gameID := "gameID"
            clanPublicID := randomdata.FullName(randomdata.RandomGender)

            status, body := Post(a, CreateMembershipRoute(gameID, clanPublicID, "application/approve"), "invalid")

            Expect(status).To(Equal(http.StatusBadRequest))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(ContainSubstring(InvalidJSONError))
        })

        It("Should not approve membership application if player does not exist", func() {
            playerPublicID := randomdata.FullName(randomdata.RandomGender)
            _, clan, owner, _, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 0, "", "")
            Expect(err).NotTo(HaveOccurred())

            gameID := owner.GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "playerPublicID":    playerPublicID,
                "requestorPublicID": owner.PublicID,
            }

            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "application/approve"), payload)

            Expect(status).To(Equal(http.StatusNotFound))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(Equal(fmt.Sprintf("Membership was not found with id: %s", playerPublicID)))
        })
    })

    Describe("Promote Or Demote Member Handler", func() {
        It("Should promote member", func() {
            _, clan, owner, players, memberships, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 1, "", "")
            Expect(err).NotTo(HaveOccurred())

            memberships[0].Approved = true
            _, err = testDb.Update(memberships[0])

            gameID := players[0].GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "playerPublicID":    players[0].PublicID,
                "requestorPublicID": owner.PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "promote"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())
            Expect(result["level"]).To(Equal("Elder"))

            dbMembership, err := models.GetValidMembershipByClanAndPlayerPublicID(db, gameID, clanPublicID, players[0].PublicID)
            Expect(err).NotTo(HaveOccurred())
            Expect(dbMembership.Level).To(Equal("Elder"))
        })

        It("Should demote member", func() {
            _, clan, owner, players, memberships, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 1, "", "")
            Expect(err).NotTo(HaveOccurred())

            memberships[0].Approved = true
            memberships[0].Level = "CoLeader"
            _, err = testDb.Update(memberships[0])

            gameID := players[0].GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "playerPublicID":    players[0].PublicID,
                "requestorPublicID": owner.PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "demote"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())
            Expect(result["level"]).To(Equal("Elder"))

            dbMembership, err := models.GetValidMembershipByClanAndPlayerPublicID(db, gameID, clanPublicID, players[0].PublicID)
            Expect(err).NotTo(HaveOccurred())
            Expect(dbMembership.Level).To(Equal("Elder"))
        })

        It("Should not promote member if missing parameters", func() {
            status, body := PostJSON(a, CreateMembershipRoute("gameID", "clanPublicID", "promote"), map[string]interface{}{})

            Expect(status).To(Equal(http.StatusBadRequest))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(Equal("playerPublicID is required, requestorPublicID is required"))
        })

        It("Should not promote member if invalid payload", func() {
            gameID := "gameID"
            clanPublicID := randomdata.FullName(randomdata.RandomGender)

            status, body := Post(a, CreateMembershipRoute(gameID, clanPublicID, "promote"), "invalid")

            Expect(status).To(Equal(http.StatusBadRequest))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(ContainSubstring(InvalidJSONError))
        })

        It("Should not promote member if player does not exist", func() {
            playerPublicID := randomdata.FullName(randomdata.RandomGender)
            _, clan, owner, _, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 0, "", "")
            Expect(err).NotTo(HaveOccurred())

            gameID := owner.GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "playerPublicID":    playerPublicID,
                "requestorPublicID": owner.PublicID,
            }

            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "promote"), payload)

            Expect(status).To(Equal(http.StatusNotFound))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(Equal(fmt.Sprintf("Membership was not found with id: %s", playerPublicID)))
        })
    })

    Describe("Delete Member Handler", func() {
        It("Should delete member", func() {
            _, clan, owner, players, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 1, "", "")
            Expect(err).NotTo(HaveOccurred())

            gameID := clan.GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "playerPublicID":    players[0].PublicID,
                "requestorPublicID": owner.PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "delete"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            _, err = models.GetValidMembershipByClanAndPlayerPublicID(db, gameID, clanPublicID, players[0].PublicID)
            Expect(err).To(HaveOccurred())
            Expect(err.Error()).To(Equal(fmt.Sprintf("Membership was not found with id: %s", players[0].PublicID)))
        })

        It("Should not delete member if missing parameters", func() {
            status, body := PostJSON(a, CreateMembershipRoute("gameID", "clanPublicID", "delete"), map[string]interface{}{})

            Expect(status).To(Equal(http.StatusBadRequest))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(Equal("playerPublicID is required, requestorPublicID is required"))
        })

        It("Should not delete member if invalid payload", func() {
            gameID := "gameID"
            clanPublicID := randomdata.FullName(randomdata.RandomGender)

            status, body := Post(a, CreateMembershipRoute(gameID, clanPublicID, "delete"), "invalid")

            Expect(status).To(Equal(http.StatusBadRequest))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(ContainSubstring(InvalidJSONError))
        })

        It("Should not delete member if player does not exist", func() {
            playerPublicID := randomdata.FullName(randomdata.RandomGender)
            _, clan, owner, _, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 0, "", "")
            Expect(err).NotTo(HaveOccurred())

            gameID := owner.GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "playerPublicID":    playerPublicID,
                "requestorPublicID": owner.PublicID,
            }

            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "delete"), payload)

            Expect(status).To(Equal(http.StatusNotFound))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeFalse())
            Expect(result["reason"]).To(Equal(fmt.Sprintf("Membership was not found with id: %s", playerPublicID)))
        })
    })

    Describe("Membership Hooks", func() {
        It("Apply should call membership application created hook with non empty message", func() {
            hooks, err := fixtures.GetHooksForRoutes(testDb, []string{
                "http://localhost:52525/membershipapply",
            }, models.MembershipApplicationCreatedHook)
            Expect(err).NotTo(HaveOccurred())
            responses := startRouteHandler([]string{"/membershipapply"}, 52525)

            _, clan, _, _, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 0, hooks[0].GameID, "", true)
            Expect(err).NotTo(HaveOccurred())

            clan.AllowApplication = true
            _, err = testDb.Update(clan)
            Expect(err).NotTo(HaveOccurred())

            player := fixtures.PlayerFactory.MustCreateWithOption(map[string]interface{}{
                "GameID": clan.GameID,
            }).(*models.Player)
            err = testDb.Insert(player)
            Expect(err).NotTo(HaveOccurred())

            gameID := player.GameID
            clanPublicID := clan.PublicID
            level := "Member"

            payload := map[string]interface{}{
                "level":          level,
                "playerPublicID": player.PublicID,
                "message":        "Please accept me, I am nice",
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "application"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            Eventually(func() int {
                return len(*responses)
            }).Should(Equal(1))

            response := (*responses)[0]["payload"].(map[string]interface{})
            validateMembershipHookResponse(response, gameID, clan, player, player)
            Expect(response["message"]).To(Equal(payload["message"]))
        })

        It("Invite should call membership application created hook", func() {
            hooks, err := fixtures.GetHooksForRoutes(testDb, []string{
                "http://localhost:52525/membershipinvite",
            }, models.MembershipApplicationCreatedHook)
            Expect(err).NotTo(HaveOccurred())
            responses := startRouteHandler([]string{"/membershipinvite"}, 52525)

            _, clan, owner, _, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 0, hooks[0].GameID, "", true)
            Expect(err).NotTo(HaveOccurred())

            clan.AllowApplication = true
            _, err = testDb.Update(clan)
            Expect(err).NotTo(HaveOccurred())

            player := fixtures.PlayerFactory.MustCreateWithOption(map[string]interface{}{
                "GameID": clan.GameID,
            }).(*models.Player)
            err = testDb.Insert(player)
            Expect(err).NotTo(HaveOccurred())

            gameID := player.GameID
            clanPublicID := clan.PublicID
            level := "Member"

            payload := map[string]interface{}{
                "level":             level,
                "playerPublicID":    player.PublicID,
                "requestorPublicID": owner.PublicID,
                "message":           "Join my clan",
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "invitation"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            Eventually(func() int {
                return len(*responses)
            }).Should(Equal(1))

            response := (*responses)[0]["payload"].(map[string]interface{})
            validateMembershipHookResponse(response, gameID, clan, player, owner)
            Expect(response["message"]).To(Equal(payload["message"]))
        })

        It("should call membership approved hook on application", func() {
            hooks, err := fixtures.GetHooksForRoutes(testDb, []string{
                "http://localhost:52525/membershipapplicationapproved",
            }, models.MembershipApprovedHook)
            Expect(err).NotTo(HaveOccurred())
            responses := startRouteHandler([]string{"/membershipapplicationapproved"}, 52525)

            _, clan, owner, players, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 1, hooks[0].GameID, "", true, false)
            Expect(err).NotTo(HaveOccurred())

            clan.AllowApplication = true
            _, err = testDb.Update(clan)
            Expect(err).NotTo(HaveOccurred())

            gameID := hooks[0].GameID
            clanPublicID := clan.PublicID
            level := "Member"

            payload := map[string]interface{}{
                "level":             level,
                "playerPublicID":    players[0].PublicID,
                "requestorPublicID": owner.PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "application/approve"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            Eventually(func() int {
                return len(*responses)
            }).Should(Equal(1))

            response := (*responses)[0]["payload"].(map[string]interface{})
            validateMembershipHookResponse(response, gameID, clan, players[0], owner)
            validateApproveDenyMembershipHookResponse(response, players[0])
        })

        It("should call membership approved hook on invitation", func() {
            hooks, err := fixtures.GetHooksForRoutes(testDb, []string{
                "http://localhost:52525/membershipinvitationapproved",
            }, models.MembershipApprovedHook)
            Expect(err).NotTo(HaveOccurred())
            responses := startRouteHandler([]string{"/membershipinvitationapproved"}, 52525)

            _, clan, owner, players, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 1, hooks[0].GameID, "", true)
            Expect(err).NotTo(HaveOccurred())

            clan.AllowApplication = true
            _, err = testDb.Update(clan)
            Expect(err).NotTo(HaveOccurred())

            gameID := hooks[0].GameID
            clanPublicID := clan.PublicID
            level := "Member"

            payload := map[string]interface{}{
                "level":             level,
                "playerPublicID":    players[0].PublicID,
                "requestorPublicID": players[0].PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "invitation/approve"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            Eventually(func() int {
                return len(*responses)
            }).Should(Equal(1))

            response := (*responses)[0]["payload"].(map[string]interface{})
            validateMembershipHookResponse(response, gameID, clan, players[0], players[0])
            validateApproveDenyMembershipHookResponse(response, owner)
        })

        It("should call membership denied hook on application", func() {
            hooks, err := fixtures.GetHooksForRoutes(testDb, []string{
                "http://localhost:52525/membershipapplicationdenied",
            }, models.MembershipDeniedHook)
            Expect(err).NotTo(HaveOccurred())
            responses := startRouteHandler([]string{"/membershipapplicationdenied"}, 52525)

            _, clan, owner, players, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 1, hooks[0].GameID, "", true, false)
            Expect(err).NotTo(HaveOccurred())

            clan.AllowApplication = true
            _, err = testDb.Update(clan)
            Expect(err).NotTo(HaveOccurred())

            gameID := hooks[0].GameID
            clanPublicID := clan.PublicID
            level := "Member"

            payload := map[string]interface{}{
                "level":             level,
                "playerPublicID":    players[0].PublicID,
                "requestorPublicID": owner.PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "application/deny"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            Eventually(func() int {
                return len(*responses)
            }).Should(Equal(1))

            response := (*responses)[0]["payload"].(map[string]interface{})
            validateMembershipHookResponse(response, gameID, clan, players[0], owner)
            validateApproveDenyMembershipHookResponse(response, players[0])
        })

        It("should call membership denied hook on invitation", func() {
            hooks, err := fixtures.GetHooksForRoutes(testDb, []string{
                "http://localhost:52525/membershipinvitationdenied",
            }, models.MembershipDeniedHook)
            Expect(err).NotTo(HaveOccurred())
            responses := startRouteHandler([]string{"/membershipinvitationdenied"}, 52525)

            _, clan, owner, players, _, err := fixtures.GetClanWithMemberships(testDb, 0, 0, 0, 1, hooks[0].GameID, "", true)
            Expect(err).NotTo(HaveOccurred())

            clan.AllowApplication = true
            _, err = testDb.Update(clan)
            Expect(err).NotTo(HaveOccurred())

            gameID := hooks[0].GameID
            clanPublicID := clan.PublicID
            level := "Member"

            payload := map[string]interface{}{
                "level":             level,
                "playerPublicID":    players[0].PublicID,
                "requestorPublicID": players[0].PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "invitation/deny"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            Eventually(func() int {
                return len(*responses)
            }).Should(Equal(1))

            response := (*responses)[0]["payload"].(map[string]interface{})
            validateMembershipHookResponse(response, gameID, clan, players[0], players[0])
            validateApproveDenyMembershipHookResponse(response, owner)
        })

        It("should call membership promoted hook", func() {
            hooks, err := fixtures.GetHooksForRoutes(testDb, []string{
                "http://localhost:52525/membershippromoted",
            }, models.MembershipPromotedHook)
            Expect(err).NotTo(HaveOccurred())
            responses := startRouteHandler([]string{"/membershippromoted"}, 52525)

            _, clan, owner, players, _, err := fixtures.GetClanWithMemberships(testDb, 1, 0, 0, 0, hooks[0].GameID, "", true)
            Expect(err).NotTo(HaveOccurred())

            clan.AllowApplication = true
            _, err = testDb.Update(clan)
            Expect(err).NotTo(HaveOccurred())

            gameID := hooks[0].GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "playerPublicID":    players[0].PublicID,
                "requestorPublicID": owner.PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "promote"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            Eventually(func() int {
                return len(*responses)
            }).Should(Equal(1))

            response := (*responses)[0]["payload"].(map[string]interface{})
            validateMembershipHookResponse(response, gameID, clan, players[0], owner)
        })

        It("should call membership demoted hook", func() {
            hooks, err := fixtures.GetHooksForRoutes(testDb, []string{
                "http://localhost:52525/membershipdemoted",
            }, models.MembershipDemotedHook)
            Expect(err).NotTo(HaveOccurred())
            responses := startRouteHandler([]string{"/membershipdemoted"}, 52525)

            _, clan, owner, players, memberships, err := fixtures.GetClanWithMemberships(testDb, 1, 0, 0, 0, hooks[0].GameID, "", true)
            Expect(err).NotTo(HaveOccurred())

            clan.AllowApplication = true
            _, err = testDb.Update(clan)
            Expect(err).NotTo(HaveOccurred())

            memberships[0].Level = "Elder"
            _, err = testDb.Update(memberships[0])
            Expect(err).NotTo(HaveOccurred())

            gameID := hooks[0].GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "playerPublicID":    players[0].PublicID,
                "requestorPublicID": owner.PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "demote"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            Eventually(func() int {
                return len(*responses)
            }).Should(Equal(1))

            response := (*responses)[0]["payload"].(map[string]interface{})
            validateMembershipHookResponse(response, gameID, clan, players[0], owner)
        })

        It("should call membership deleted hook", func() {
            hooks, err := fixtures.GetHooksForRoutes(testDb, []string{
                "http://localhost:52525/membershipdeleted",
            }, models.MembershipLeftHook)
            Expect(err).NotTo(HaveOccurred())
            responses := startRouteHandler([]string{"/membershipdeleted"}, 52525)

            _, clan, owner, players, _, err := fixtures.GetClanWithMemberships(testDb, 1, 0, 0, 0, hooks[0].GameID, "", true)
            Expect(err).NotTo(HaveOccurred())

            clan.AllowApplication = true
            _, err = testDb.Update(clan)
            Expect(err).NotTo(HaveOccurred())

            gameID := hooks[0].GameID
            clanPublicID := clan.PublicID

            payload := map[string]interface{}{
                "playerPublicID":    players[0].PublicID,
                "requestorPublicID": owner.PublicID,
            }
            status, body := PostJSON(a, CreateMembershipRoute(gameID, clanPublicID, "delete"), payload)

            Expect(status).To(Equal(http.StatusOK))
            var result map[string]interface{}
            json.Unmarshal([]byte(body), &result)
            Expect(result["success"]).To(BeTrue())

            Eventually(func() int {
                return len(*responses)
            }).Should(Equal(1))

            response := (*responses)[0]["payload"].(map[string]interface{})
            validateMembershipHookResponse(response, gameID, clan, players[0], owner)
        })
    })
})