feat(audit): cover operational mutations
This commit is contained in:
@@ -16,7 +16,8 @@ import (
|
||||
// AgentCapacityHandler handles AgentCapacityPolicy CRUD operations.
|
||||
// Reference: Chatwoot enterprise/app/controllers/api/v1/agent_capacity_policies_controller.rb
|
||||
type AgentCapacityHandler struct {
|
||||
svc *service.AgentCapacityPolicyService
|
||||
svc *service.AgentCapacityPolicyService
|
||||
auditSvc *service.AuditService
|
||||
}
|
||||
|
||||
// NewAgentCapacityHandler creates a new AgentCapacityPolicy handler.
|
||||
@@ -24,6 +25,11 @@ func NewAgentCapacityHandler(svc *service.AgentCapacityPolicyService) *AgentCapa
|
||||
return &AgentCapacityHandler{svc: svc}
|
||||
}
|
||||
|
||||
func (h *AgentCapacityHandler) WithAuditService(auditSvc *service.AuditService) *AgentCapacityHandler {
|
||||
h.auditSvc = auditSvc
|
||||
return h
|
||||
}
|
||||
|
||||
// List returns all agent capacity policies for an account.
|
||||
// GET /api/v1/accounts/:account_id/agent_capacity_policies
|
||||
func (h *AgentCapacityHandler) List(c *gin.Context) {
|
||||
@@ -69,6 +75,13 @@ func (h *AgentCapacityHandler) Create(c *gin.Context) {
|
||||
handleAgentCapacityError(c, err)
|
||||
return
|
||||
}
|
||||
recordAuditMutation(c, h.auditSvc, auditMutation{
|
||||
AccountID: accountID,
|
||||
AuditableType: "AgentCapacityPolicy",
|
||||
AuditableID: policy.ID,
|
||||
Action: "create",
|
||||
AuditedChanges: serializeAgentCapacityPolicy(policy),
|
||||
})
|
||||
|
||||
c.JSON(http.StatusOK, serializeAgentCapacityPolicy(policy))
|
||||
}
|
||||
@@ -125,6 +138,13 @@ func (h *AgentCapacityHandler) Update(c *gin.Context) {
|
||||
handleAgentCapacityError(c, svcErr)
|
||||
return
|
||||
}
|
||||
recordAuditMutation(c, h.auditSvc, auditMutation{
|
||||
AccountID: accountID,
|
||||
AuditableType: "AgentCapacityPolicy",
|
||||
AuditableID: policy.ID,
|
||||
Action: "update",
|
||||
AuditedChanges: serializeAgentCapacityPolicy(policy),
|
||||
})
|
||||
|
||||
c.JSON(http.StatusOK, serializeAgentCapacityPolicy(policy))
|
||||
}
|
||||
@@ -149,6 +169,13 @@ func (h *AgentCapacityHandler) Delete(c *gin.Context) {
|
||||
handleServiceError(c, svcErr)
|
||||
return
|
||||
}
|
||||
recordAuditMutation(c, h.auditSvc, auditMutation{
|
||||
AccountID: accountID,
|
||||
AuditableType: "AgentCapacityPolicy",
|
||||
AuditableID: id,
|
||||
Action: "destroy",
|
||||
AuditedChanges: gin.H{"id": id},
|
||||
})
|
||||
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
@@ -174,6 +201,13 @@ func (h *AgentCapacityHandler) CreateInboxLimit(c *gin.Context) {
|
||||
handleAgentCapacityError(c, svcErr)
|
||||
return
|
||||
}
|
||||
recordAuditMutation(c, h.auditSvc, auditMutation{
|
||||
AccountID: accountID,
|
||||
AuditableType: "InboxCapacityLimit",
|
||||
AuditableID: limit.ID,
|
||||
Action: "create",
|
||||
AuditedChanges: serializeInboxCapacityLimit(limit, false),
|
||||
})
|
||||
c.JSON(http.StatusOK, serializeInboxCapacityLimit(limit, false))
|
||||
}
|
||||
|
||||
@@ -203,6 +237,13 @@ func (h *AgentCapacityHandler) UpdateInboxLimit(c *gin.Context) {
|
||||
handleAgentCapacityError(c, svcErr)
|
||||
return
|
||||
}
|
||||
recordAuditMutation(c, h.auditSvc, auditMutation{
|
||||
AccountID: accountID,
|
||||
AuditableType: "InboxCapacityLimit",
|
||||
AuditableID: limit.ID,
|
||||
Action: "update",
|
||||
AuditedChanges: serializeInboxCapacityLimit(limit, true),
|
||||
})
|
||||
c.JSON(http.StatusOK, serializeInboxCapacityLimit(limit, true))
|
||||
}
|
||||
|
||||
@@ -226,6 +267,13 @@ func (h *AgentCapacityHandler) DeleteInboxLimit(c *gin.Context) {
|
||||
handleAgentCapacityError(c, svcErr)
|
||||
return
|
||||
}
|
||||
recordAuditMutation(c, h.auditSvc, auditMutation{
|
||||
AccountID: accountID,
|
||||
AuditableType: "InboxCapacityLimit",
|
||||
AuditableID: limitID,
|
||||
Action: "destroy",
|
||||
AuditedChanges: gin.H{"id": limitID, "agent_capacity_policy_id": policyID},
|
||||
})
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
@@ -273,6 +321,16 @@ func (h *AgentCapacityHandler) CreateUser(c *gin.Context) {
|
||||
handleAgentCapacityError(c, svcErr)
|
||||
return
|
||||
}
|
||||
recordAuditMutation(c, h.auditSvc, auditMutation{
|
||||
AccountID: accountID,
|
||||
AuditableType: "AgentCapacityPolicyUser",
|
||||
AuditableID: user.ID,
|
||||
Action: "create",
|
||||
AuditedChanges: gin.H{
|
||||
"agent_capacity_policy_id": policyID,
|
||||
"user_id": user.ID,
|
||||
},
|
||||
})
|
||||
c.JSON(http.StatusOK, serializeAgentCapacityUser(user, accountID))
|
||||
}
|
||||
|
||||
@@ -296,6 +354,16 @@ func (h *AgentCapacityHandler) DeleteUser(c *gin.Context) {
|
||||
handleAgentCapacityError(c, svcErr)
|
||||
return
|
||||
}
|
||||
recordAuditMutation(c, h.auditSvc, auditMutation{
|
||||
AccountID: accountID,
|
||||
AuditableType: "AgentCapacityPolicyUser",
|
||||
AuditableID: userID,
|
||||
Action: "destroy",
|
||||
AuditedChanges: gin.H{
|
||||
"agent_capacity_policy_id": policyID,
|
||||
"user_id": userID,
|
||||
},
|
||||
})
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
|
||||
@@ -33,12 +33,13 @@ func (s *AgentCapacityHandlerTestSuite) SetupSuite() {
|
||||
Logger: logger.Default.LogMode(logger.Silent),
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(db.AutoMigrate(&model.Account{}, &model.User{}, &model.AccountUser{}, &model.Inbox{}, &model.AgentCapacityPolicy{}, &model.InboxCapacityLimit{}))
|
||||
s.Require().NoError(db.AutoMigrate(&model.Account{}, &model.User{}, &model.AccountUser{}, &model.Inbox{}, &model.AgentCapacityPolicy{}, &model.InboxCapacityLimit{}, &model.Audit{}))
|
||||
s.db = db
|
||||
|
||||
repo := repository.NewAgentCapacityPolicyRepo(db)
|
||||
svc := service.NewAgentCapacityPolicyService(repo)
|
||||
s.handler = NewAgentCapacityHandler(svc)
|
||||
auditSvc := service.NewAuditService(repository.NewAuditRepo(db))
|
||||
s.handler = NewAgentCapacityHandler(svc).WithAuditService(auditSvc)
|
||||
|
||||
s.account = &model.Account{Name: "test-capacity-account"}
|
||||
s.Require().NoError(db.Create(s.account).Error)
|
||||
@@ -55,6 +56,10 @@ func TestAgentCapacityHandlerSuite(t *testing.T) {
|
||||
suite.Run(t, new(AgentCapacityHandlerTestSuite))
|
||||
}
|
||||
|
||||
func (s *AgentCapacityHandlerTestSuite) SetupTest() {
|
||||
s.Require().NoError(s.db.Exec("DELETE FROM audits").Error)
|
||||
}
|
||||
|
||||
func (s *AgentCapacityHandlerTestSuite) TestList_Success() {
|
||||
r := gin.New()
|
||||
r.GET("/api/v1/accounts/:account_id/agent_capacity_policies", s.handler.List)
|
||||
@@ -208,3 +213,54 @@ func (s *AgentCapacityHandlerTestSuite) TestChatwootPolicyInboxLimitAndUserFlow(
|
||||
r.ServeHTTP(w, req)
|
||||
s.Require().Equal(http.StatusNoContent, w.Code)
|
||||
}
|
||||
|
||||
func (s *AgentCapacityHandlerTestSuite) TestMutations_WriteAuditEntries() {
|
||||
r := gin.New()
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("account_id", s.account.ID)
|
||||
c.Set("user_id", uint(99))
|
||||
c.Next()
|
||||
})
|
||||
api := r.Group("/api/v1/accounts/:account_id")
|
||||
RegisterAgentCapacityRoutes(api, s.handler)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
body := `{"agent_capacity_policy":{"name":"Audit capacity","description":"tracked"}}`
|
||||
req, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/agent_capacity_policies/", s.account.ID), bytes.NewBufferString(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("X-Request-ID", "capacity-audit-create")
|
||||
r.ServeHTTP(w, req)
|
||||
s.Require().Equal(http.StatusOK, w.Code, w.Body.String())
|
||||
var policy map[string]any
|
||||
s.Require().NoError(json.Unmarshal(w.Body.Bytes(), &policy))
|
||||
policyID := uint(policy["id"].(float64))
|
||||
|
||||
w = httptest.NewRecorder()
|
||||
body = `{"agent_capacity_policy":{"name":"Audit capacity updated","description":"tracked again"}}`
|
||||
req, _ = http.NewRequest(http.MethodPut, fmt.Sprintf("/api/v1/accounts/%d/agent_capacity_policies/%d", s.account.ID, policyID), bytes.NewBufferString(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
s.Require().Equal(http.StatusOK, w.Code, w.Body.String())
|
||||
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest(http.MethodDelete, fmt.Sprintf("/api/v1/accounts/%d/agent_capacity_policies/%d", s.account.ID, policyID), nil)
|
||||
r.ServeHTTP(w, req)
|
||||
s.Require().Equal(http.StatusOK, w.Code, w.Body.String())
|
||||
|
||||
var audits []model.Audit
|
||||
s.Require().NoError(s.db.Order("id ASC").Find(&audits).Error)
|
||||
s.Require().Len(audits, 3)
|
||||
for _, audit := range audits {
|
||||
s.Equal(s.account.ID, *audit.AccountID)
|
||||
s.Equal("Account", audit.AssociatedType)
|
||||
s.Equal(s.account.ID, *audit.AssociatedID)
|
||||
s.Equal(uint(99), *audit.UserID)
|
||||
s.Equal("AgentCapacityPolicy", audit.AuditableType)
|
||||
s.Equal(policyID, audit.AuditableID)
|
||||
s.NotEmpty(audit.AuditedChanges)
|
||||
}
|
||||
s.Equal("create", audits[0].Action)
|
||||
s.Equal("capacity-audit-create", audits[0].RequestUUID)
|
||||
s.Equal("update", audits[1].Action)
|
||||
s.Equal("destroy", audits[2].Action)
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
type ConversationHandler struct {
|
||||
conversationSvc *service.ConversationService
|
||||
messageSvc *service.MessageService
|
||||
auditSvc *service.AuditService
|
||||
}
|
||||
|
||||
// NewConversationHandler creates a new ConversationHandler.
|
||||
@@ -28,6 +29,11 @@ func NewConversationHandler(conversationSvc *service.ConversationService, messag
|
||||
return &ConversationHandler{conversationSvc: conversationSvc, messageSvc: messageSvc}
|
||||
}
|
||||
|
||||
func (h *ConversationHandler) WithAuditService(auditSvc *service.AuditService) *ConversationHandler {
|
||||
h.auditSvc = auditSvc
|
||||
return h
|
||||
}
|
||||
|
||||
// @Summary List conversations for an account
|
||||
// @Description Retrieves all conversations for an account with pagination, optionally filtered by status query param
|
||||
// @Tags Conversations
|
||||
@@ -99,6 +105,13 @@ func (h *ConversationHandler) Create(c *gin.Context) {
|
||||
handleServiceError(c, svcErr)
|
||||
return
|
||||
}
|
||||
recordAuditMutation(c, h.auditSvc, auditMutation{
|
||||
AccountID: accountID,
|
||||
AuditableType: "Conversation",
|
||||
AuditableID: conversation.ID,
|
||||
Action: "update",
|
||||
AuditedChanges: gin.H{"status": conversation.Status, "priority": conversation.Priority, "sla_policy_id": conversation.SlaPolicyID},
|
||||
})
|
||||
c.JSON(http.StatusOK, serializeConversation(c.Request.Context(), h.conversationSvc.DB(), conversation))
|
||||
}
|
||||
|
||||
@@ -193,6 +206,13 @@ func (h *ConversationHandler) Delete(c *gin.Context) {
|
||||
handleServiceError(c, svcErr)
|
||||
return
|
||||
}
|
||||
recordAuditMutation(c, h.auditSvc, auditMutation{
|
||||
AccountID: accountID,
|
||||
AuditableType: "Conversation",
|
||||
AuditableID: conversation.ID,
|
||||
Action: "destroy",
|
||||
AuditedChanges: gin.H{"id": conversation.ID, "display_id": conversation.DisplayID},
|
||||
})
|
||||
response.NoContent(c)
|
||||
}
|
||||
|
||||
@@ -238,6 +258,13 @@ func (h *ConversationHandler) AssignAgent(c *gin.Context) {
|
||||
handleServiceError(c, svcErr)
|
||||
return
|
||||
}
|
||||
recordAuditMutation(c, h.auditSvc, auditMutation{
|
||||
AccountID: accountID,
|
||||
AuditableType: "Conversation",
|
||||
AuditableID: conversation.ID,
|
||||
Action: "update",
|
||||
AuditedChanges: gin.H{"assignee_id": conversation.AssigneeID},
|
||||
})
|
||||
c.JSON(http.StatusOK, serializeUserFromDB(c.Request.Context(), h.conversationSvc.DB(), req.AssigneeID, accountID))
|
||||
}
|
||||
|
||||
@@ -286,6 +313,13 @@ func (h *ConversationHandler) ToggleStatus(c *gin.Context) {
|
||||
handleServiceError(c, svcErr)
|
||||
return
|
||||
}
|
||||
recordAuditMutation(c, h.auditSvc, auditMutation{
|
||||
AccountID: accountID,
|
||||
AuditableType: "Conversation",
|
||||
AuditableID: conversation.ID,
|
||||
Action: "update",
|
||||
AuditedChanges: gin.H{"status": conversation.Status, "snoozed_until": conversation.SnoozedUntil},
|
||||
})
|
||||
c.JSON(http.StatusOK, gin.H{"meta": gin.H{}, "payload": gin.H{
|
||||
"success": true,
|
||||
"conversation_id": conversationDisplayID(conversation),
|
||||
|
||||
@@ -16,7 +16,8 @@ import (
|
||||
// InboxHandler handles inbox-related API endpoints.
|
||||
// Reference: Chatwoot app/controllers/api/v1/inboxes_controller.rb
|
||||
type InboxHandler struct {
|
||||
svc *service.InboxService
|
||||
svc *service.InboxService
|
||||
auditSvc *service.AuditService
|
||||
}
|
||||
|
||||
// NewInboxHandler creates a new InboxHandler.
|
||||
@@ -24,6 +25,11 @@ func NewInboxHandler(svc *service.InboxService) *InboxHandler {
|
||||
return &InboxHandler{svc: svc}
|
||||
}
|
||||
|
||||
func (h *InboxHandler) WithAuditService(auditSvc *service.AuditService) *InboxHandler {
|
||||
h.auditSvc = auditSvc
|
||||
return h
|
||||
}
|
||||
|
||||
// @Summary List inboxes for an account
|
||||
// @Description Retrieves all inboxes for an account with pagination
|
||||
// @Tags Inboxes
|
||||
@@ -131,6 +137,13 @@ func (h *InboxHandler) Create(c *gin.Context) {
|
||||
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "failed to create inbox"})
|
||||
return
|
||||
}
|
||||
recordAuditMutation(c, h.auditSvc, auditMutation{
|
||||
AccountID: accountID,
|
||||
AuditableType: "Inbox",
|
||||
AuditableID: inbox.ID,
|
||||
Action: "create",
|
||||
AuditedChanges: serializeInbox(inbox),
|
||||
})
|
||||
|
||||
c.JSON(http.StatusOK, serializeInbox(inbox))
|
||||
}
|
||||
@@ -177,6 +190,13 @@ func (h *InboxHandler) Update(c *gin.Context) {
|
||||
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "failed to update inbox"})
|
||||
return
|
||||
}
|
||||
recordAuditMutation(c, h.auditSvc, auditMutation{
|
||||
AccountID: accountID,
|
||||
AuditableType: "Inbox",
|
||||
AuditableID: inbox.ID,
|
||||
Action: "update",
|
||||
AuditedChanges: serializeInbox(inbox),
|
||||
})
|
||||
|
||||
c.JSON(http.StatusOK, serializeInbox(inbox))
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ import (
|
||||
// SlaPolicyHandler handles SLA Policy CRUD + applied SLA metrics/download.
|
||||
// Reference: Chatwoot app/controllers/api/v1/sla_policies_controller.rb
|
||||
type SlaPolicyHandler struct {
|
||||
svc *service.SlaPolicyService
|
||||
svc *service.SlaPolicyService
|
||||
auditSvc *service.AuditService
|
||||
}
|
||||
|
||||
// NewSlaPolicyHandler creates a new SlaPolicy handler.
|
||||
@@ -29,6 +30,11 @@ func NewSlaPolicyHandler(svc *service.SlaPolicyService) *SlaPolicyHandler {
|
||||
return &SlaPolicyHandler{svc: svc}
|
||||
}
|
||||
|
||||
func (h *SlaPolicyHandler) WithAuditService(auditSvc *service.AuditService) *SlaPolicyHandler {
|
||||
h.auditSvc = auditSvc
|
||||
return h
|
||||
}
|
||||
|
||||
// Create creates a new SLA policy.
|
||||
// POST /api/v1/accounts/:account_id/sla_policies
|
||||
func (h *SlaPolicyHandler) Create(c *gin.Context) {
|
||||
@@ -51,6 +57,13 @@ func (h *SlaPolicyHandler) Create(c *gin.Context) {
|
||||
handleServiceError(c, svcErr)
|
||||
return
|
||||
}
|
||||
recordAuditMutation(c, h.auditSvc, auditMutation{
|
||||
AccountID: accountID,
|
||||
AuditableType: "SlaPolicy",
|
||||
AuditableID: policy.ID,
|
||||
Action: "create",
|
||||
AuditedChanges: serializeSlaPolicy(policy),
|
||||
})
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"payload": serializeSlaPolicy(policy)})
|
||||
}
|
||||
@@ -127,6 +140,13 @@ func (h *SlaPolicyHandler) Update(c *gin.Context) {
|
||||
handleServiceError(c, svcErr)
|
||||
return
|
||||
}
|
||||
recordAuditMutation(c, h.auditSvc, auditMutation{
|
||||
AccountID: accountID,
|
||||
AuditableType: "SlaPolicy",
|
||||
AuditableID: policy.ID,
|
||||
Action: "update",
|
||||
AuditedChanges: serializeSlaPolicy(policy),
|
||||
})
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"payload": serializeSlaPolicy(policy)})
|
||||
}
|
||||
@@ -152,6 +172,13 @@ func (h *SlaPolicyHandler) Delete(c *gin.Context) {
|
||||
handleServiceError(c, svcErr)
|
||||
return
|
||||
}
|
||||
recordAuditMutation(c, h.auditSvc, auditMutation{
|
||||
AccountID: accountID,
|
||||
AuditableType: "SlaPolicy",
|
||||
AuditableID: policyID,
|
||||
Action: "destroy",
|
||||
AuditedChanges: gin.H{"id": policyID},
|
||||
})
|
||||
|
||||
// Chatwoot returns head :ok (200) on destroy.
|
||||
c.Status(http.StatusOK)
|
||||
|
||||
@@ -41,6 +41,7 @@ func setupSlaPolicyHandlerTest(t *testing.T) (*SlaPolicyHandler, *gorm.DB) {
|
||||
&model.Conversation{},
|
||||
&model.User{},
|
||||
&model.Team{},
|
||||
&model.Audit{},
|
||||
))
|
||||
t.Cleanup(func() {
|
||||
sqlDB, _ := db.DB()
|
||||
@@ -52,7 +53,8 @@ func setupSlaPolicyHandlerTest(t *testing.T) (*SlaPolicyHandler, *gorm.DB) {
|
||||
slaEventRepo := repository.NewSlaEventRepo(db)
|
||||
slaPolicyInboxRepo := repository.NewSlaPolicyInboxRepo(db)
|
||||
svc := service.NewSlaPolicyService(slaPolicyRepo, appliedSlaRepo, slaEventRepo, slaPolicyInboxRepo)
|
||||
handler := NewSlaPolicyHandler(svc)
|
||||
auditSvc := service.NewAuditService(repository.NewAuditRepo(db))
|
||||
handler := NewSlaPolicyHandler(svc).WithAuditService(auditSvc)
|
||||
|
||||
// Seed an account for all tests
|
||||
account := &model.Account{Name: "SlaHandlerOrg", Locale: "en", Active: true}
|
||||
@@ -101,6 +103,21 @@ func setupSlaPolicyTestRouter(handler *SlaPolicyHandler) *gin.Engine {
|
||||
return r
|
||||
}
|
||||
|
||||
func setupSlaPolicyAuditRouter(handler *SlaPolicyHandler, accountID uint, userID uint) *gin.Engine {
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("account_id", accountID)
|
||||
c.Set("user_id", userID)
|
||||
c.Next()
|
||||
})
|
||||
rg := r.Group("/api/v1/accounts/:account_id")
|
||||
rg.POST("/sla_policies", handler.Create)
|
||||
rg.PUT("/sla_policies/:id", handler.Update)
|
||||
rg.DELETE("/sla_policies/:id", handler.Delete)
|
||||
return r
|
||||
}
|
||||
|
||||
// ========== List ==========
|
||||
|
||||
func TestSlaPolicyHandler_List_Success(t *testing.T) {
|
||||
@@ -211,6 +228,62 @@ func TestSlaPolicyHandler_Create_ValidationError(t *testing.T) {
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func TestSlaPolicyHandler_MutationsWriteAuditEntries(t *testing.T) {
|
||||
handler, db := setupSlaPolicyHandlerTest(t)
|
||||
accountID := slaHandlerAccountIDUint(db)
|
||||
router := setupSlaPolicyAuditRouter(handler, accountID, 88)
|
||||
|
||||
createBody, _ := json.Marshal(map[string]any{"sla_policy": map[string]any{
|
||||
"name": "Audit SLA",
|
||||
"first_response_time_threshold": 15,
|
||||
"next_response_time_threshold": 30,
|
||||
"resolution_time_threshold": 90,
|
||||
}})
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest(http.MethodPost, "/api/v1/accounts/"+strconv.FormatUint(uint64(accountID), 10)+"/sla_policies", bytes.NewBuffer(createBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("X-Request-ID", "sla-audit-create")
|
||||
router.ServeHTTP(w, req)
|
||||
require.Equal(t, http.StatusOK, w.Code, w.Body.String())
|
||||
var created map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &created))
|
||||
policyID := uint(created["payload"].(map[string]any)["id"].(float64))
|
||||
|
||||
updateBody, _ := json.Marshal(map[string]any{"sla_policy": map[string]any{
|
||||
"name": "Audit SLA Updated",
|
||||
"first_response_time_threshold": 20,
|
||||
"next_response_time_threshold": 40,
|
||||
"resolution_time_threshold": 100,
|
||||
}})
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest(http.MethodPut, "/api/v1/accounts/"+strconv.FormatUint(uint64(accountID), 10)+"/sla_policies/"+strconv.FormatUint(uint64(policyID), 10), bytes.NewBuffer(updateBody))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
router.ServeHTTP(w, req)
|
||||
require.Equal(t, http.StatusOK, w.Code, w.Body.String())
|
||||
|
||||
w = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest(http.MethodDelete, "/api/v1/accounts/"+strconv.FormatUint(uint64(accountID), 10)+"/sla_policies/"+strconv.FormatUint(uint64(policyID), 10), nil)
|
||||
router.ServeHTTP(w, req)
|
||||
require.Equal(t, http.StatusOK, w.Code, w.Body.String())
|
||||
|
||||
var audits []model.Audit
|
||||
require.NoError(t, db.Order("id ASC").Find(&audits).Error)
|
||||
require.Len(t, audits, 3)
|
||||
for _, audit := range audits {
|
||||
assert.Equal(t, accountID, *audit.AccountID)
|
||||
assert.Equal(t, "Account", audit.AssociatedType)
|
||||
assert.Equal(t, accountID, *audit.AssociatedID)
|
||||
assert.Equal(t, uint(88), *audit.UserID)
|
||||
assert.Equal(t, "SlaPolicy", audit.AuditableType)
|
||||
assert.Equal(t, policyID, audit.AuditableID)
|
||||
assert.NotEmpty(t, audit.AuditedChanges)
|
||||
}
|
||||
assert.Equal(t, "create", audits[0].Action)
|
||||
assert.Equal(t, "sla-audit-create", audits[0].RequestUUID)
|
||||
assert.Equal(t, "update", audits[1].Action)
|
||||
assert.Equal(t, "destroy", audits[2].Action)
|
||||
}
|
||||
|
||||
func TestSlaPolicyHandler_Create_NoAccountID(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
|
||||
Reference in New Issue
Block a user