package dashboard import ( "agent-desk/internal/models" "agent-desk/internal/pkg/constants" "agent-desk/internal/pkg/dto" "agent-desk/internal/pkg/enums" "encoding/json" "github.com/gin-gonic/gin" "github.com/glebarez/sqlite" "github.com/mlogclub/simple/sqls" "gorm.io/gorm" "net/http/httptest" "path/filepath" "strings" "testing" ) func availabilityHandlerDB(t *testing.T) *gorm.DB { t.Helper() db, err := gorm.Open(sqlite.Open(filepath.Join(t.TempDir(), "availability.db")), &gorm.Config{}) if err != nil { t.Fatal(err) } if err := db.AutoMigrate(&models.User{}, &models.AgentProfile{}, &models.AgentTeam{}, &models.AgentTeamSchedule{}, &models.SupportCalendar{}, &models.AgentPresenceLease{}, &models.Conversation{}); err != nil { t.Fatal(err) } previous := sqls.DB() sqls.SetDB(db) t.Cleanup(func() { sqls.SetDB(previous); conn, _ := db.DB(); conn.Close() }) for _, row := range []any{&models.User{ID: 1, Username: "one", UserType: enums.UserTypeEmployee}, &models.User{ID: 2, Username: "two", UserType: enums.UserTypeEmployee}, &models.AgentTeam{ID: 1, Name: "Support"}, &models.AgentProfile{ID: 1, UserID: 1, TeamID: 1, AgentCode: "one"}, &models.AgentProfile{ID: 2, UserID: 2, TeamID: 1, AgentCode: "two"}} { if err := db.Create(row).Error; err != nil { t.Fatal(err) } } return db } func callAvailabilityHandler(t *testing.T, handler gin.HandlerFunc, principal *dto.AuthPrincipal, body string) map[string]any { t.Helper() gin.SetMode(gin.TestMode) router := gin.New() router.Use(func(c *gin.Context) { if principal != nil { c.Set("authPrincipal", principal) } c.Next() }) router.POST("/test", handler) req := httptest.NewRequest("POST", "/test", strings.NewReader(body)) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() router.ServeHTTP(rec, req) var result map[string]any if err := json.Unmarshal(rec.Body.Bytes(), &result); err != nil { t.Fatal(err) } return result } func TestSupportAvailabilitySelfCannotTargetAnotherUser(t *testing.T) { db := availabilityHandlerDB(t) principal := &dto.AuthPrincipal{UserID: 1, Username: "one", UserType: enums.UserTypeEmployee} got := callAvailabilityHandler(t, SupportAvailabilityPostMe, principal, `{"userId":2,"serviceStatus":2}`) if got["success"] != true { t.Fatalf("self update: %v", got) } var own, other models.AgentProfile db.First(&own, 1) db.First(&other, 2) if own.ServiceStatus != enums.ServiceStatusBreak || other.ServiceStatus != enums.ServiceStatusIdle { t.Fatal("self endpoint changed another profile") } } func TestSupportAvailabilitySelfDoesNotProvisionMissingProfile(t *testing.T) { db := availabilityHandlerDB(t) principal := &dto.AuthPrincipal{UserID: 99, Username: "missing", UserType: enums.UserTypeEmployee} got := callAvailabilityHandler(t, SupportAvailabilityPostMe, principal, `{"serviceStatus":0}`) if got["success"] == true { t.Fatal("missing operator gained profile") } var count int64 db.Model(&models.AgentProfile{}).Count(&count) if count != 2 { t.Fatalf("profile count %d", count) } } func TestSupportAvailabilityAdminRequiresExactPermission(t *testing.T) { db := availabilityHandlerDB(t) principal := &dto.AuthPrincipal{UserID: 1, Username: "one", Permissions: []string{constants.PermissionAgentView.Code}} got := callAvailabilityHandler(t, SupportAvailabilityPostAgent, principal, `{"userId":2,"serviceStatus":3}`) if got["success"] == true { t.Fatal("read permission changed another operator") } var other models.AgentProfile db.First(&other, 2) if other.ServiceStatus != enums.ServiceStatusIdle { t.Fatal("unauthorized mutation") } principal.Permissions = []string{constants.PermissionAgentUpdate.Code} got = callAvailabilityHandler(t, SupportAvailabilityPostAgent, principal, `{"userId":2,"serviceStatus":3}`) if got["success"] != true { t.Fatalf("authorized update: %v", got) } } func TestSupportCalendarDeploymentAndTeamPermissionsDoNotOverlap(t *testing.T) { db := availabilityHandlerDB(t) principal := &dto.AuthPrincipal{UserID: 1, Username: "one", Permissions: []string{constants.PermissionAgentTeamScheduleUpdate.Code}} got := callAvailabilityHandler(t, SupportAvailabilityPostCalendar, principal, `{"teamId":0,"mode":"recurring","timezone":"Europe/Berlin","weekly":[],"exceptions":[]}`) if got["success"] == true { t.Fatal("team editor changed deployment default") } principal.Permissions = []string{constants.PermissionSupportConfigUpdate.Code} got = callAvailabilityHandler(t, SupportAvailabilityPostCalendar, principal, `{"teamId":1,"mode":"recurring","timezone":"Europe/Berlin","weekly":[],"exceptions":[]}`) if got["success"] == true { t.Fatal("deployment editor changed team schedule") } var count int64 db.Model(&models.SupportCalendar{}).Count(&count) if count != 0 { t.Fatal("denied mutations persisted") } }