Files
agent-desk-ng/internal/services/setup_readiness_service_test.go
mono 2f7a491666 feat: harden support, localize knowledge and add operator availability [skip ci]
Source-only publication of reviewed commit 64e96a7f423673dc29ad56485be9fc79bce8af1c.

Passed in isolation: focused frontend/backend tests, race checks, TypeScript typecheck, lint, Go compilation, Flowgram build, and deterministic SDK build/tests.

Not accepted as a production release: Next.js export/native assembly remains blocked by environment memory limits; browser interaction and MySQL staging acceptance remain unverified. No production server, database/schema, runtime configuration, deployment, release tag, or workflow execution was performed.

Expected source tree: 0a4a7528e9.
2026-10-16 15:21:42 +00:00

549 lines
23 KiB
Go

// Modified in AgentDeskNext: authenticated support availability and calendars.
// Modified in AgentDeskFixes: safe, reviewed setup repairs and honest routing diagnostics.
package services_test
import (
"encoding/json"
"errors"
"slices"
"strings"
"testing"
"time"
"agent-desk/internal/models"
"agent-desk/internal/pkg/constants"
"agent-desk/internal/pkg/dto"
"agent-desk/internal/pkg/dto/request"
"agent-desk/internal/pkg/dto/response"
"agent-desk/internal/pkg/enums"
"agent-desk/internal/services"
"github.com/glebarez/sqlite"
"github.com/mlogclub/simple/sqls"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
func setupSetupReadinessTestDB(t *testing.T) *gorm.DB {
t.Helper()
dbName := strings.NewReplacer("/", "_", " ", "_").Replace(t.Name())
db, err := gorm.Open(sqlite.Open("file:"+dbName+"?mode=memory&cache=shared"), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
TablePrefix: "t_",
SingularTable: true,
},
})
if err != nil {
t.Fatalf("open sqlite error = %v", err)
}
t.Cleanup(func() {
sqlDB, err := db.DB()
if err == nil {
_ = sqlDB.Close()
}
})
if err := db.AutoMigrate(
&models.User{},
&models.Customer{},
&models.CustomerIdentity{},
&models.AIAgent{},
&models.AgentTeam{},
&models.AgentTeamSchedule{},
&models.AgentProfile{},
&models.SupportCalendar{}, &models.AgentPresenceLease{}, &models.LoginSession{}, &models.Role{}, &models.Permission{}, &models.UserRole{}, &models.RolePermission{}, &models.UserPermission{},
&models.Channel{},
&models.Conversation{},
&models.ConversationParticipant{},
&models.ConversationAssignment{},
&models.ConversationEventLog{},
&models.ConversationReadState{},
&models.Message{},
&models.ChannelMessageOutbox{},
); err != nil {
t.Fatalf("auto migrate error = %v", err)
}
sqls.SetDB(db)
return db
}
func setupRepairPrincipal(t *testing.T, db *gorm.DB) *dto.AuthPrincipal {
t.Helper()
user := models.User{Username: "setup-admin", Nickname: "Setup Admin", Status: enums.StatusOk}
if err := db.Create(&user).Error; err != nil {
t.Fatal(err)
}
return &dto.AuthPrincipal{
UserID: user.ID, Username: user.Username, Nickname: user.Nickname,
Roles: []string{constants.RoleCodeAdmin},
Permissions: []string{
constants.PermissionAgentTeamCreate.Code, constants.PermissionAgentTeamUpdate.Code,
constants.PermissionAgentTeamScheduleCreate.Code, constants.PermissionAgentTeamScheduleUpdate.Code,
constants.PermissionAgentCreate.Code, constants.PermissionAgentUpdate.Code,
constants.PermissionAgentConfig.Code, constants.PermissionAgentUpdateStatus.Code,
constants.PermissionAIAgentCreate.Code, constants.PermissionAIAgentUpdate.Code,
constants.PermissionChannelCreate.Code, constants.PermissionChannelUpdate.Code,
},
}
}
func applyReviewedSetupFix(t *testing.T, req request.SetupQuickFixRequest, principal *dto.AuthPrincipal) *response.SetupQuickFixResponse {
t.Helper()
preview, err := services.SetupReadinessService.ApplyQuickFix(req, principal)
if err != nil {
t.Fatalf("preview: %v", err)
}
if preview.Applied || preview.PreviewToken == "" {
t.Fatalf("expected read-only preview: %+v", preview)
}
req.Apply, req.PreviewToken = true, preview.PreviewToken
applied, err := services.SetupReadinessService.ApplyQuickFix(req, principal)
if err != nil {
t.Fatalf("apply: %v", err)
}
if !applied.Applied {
t.Fatal("reviewed changes were not applied")
}
return applied
}
func assertSetupCount(t *testing.T, db *gorm.DB, model any, want int64) {
t.Helper()
var count int64
if err := db.Model(model).Count(&count).Error; err != nil {
t.Fatal(err)
}
if count != want {
t.Fatalf("%T count = %d, want %d", model, count, want)
}
}
func TestSetupReadiness_RequiresReviewedExplicitApply(t *testing.T) {
db := setupSetupReadinessTestDB(t)
principal := setupRepairPrincipal(t, db)
req := request.SetupQuickFixRequest{Action: services.QuickFixActionQuickSetupAll}
preview, err := services.SetupReadinessService.ApplyQuickFix(req, principal)
if err != nil {
t.Fatal(err)
}
if preview.Applied || len(preview.Changes) != 3 {
t.Fatalf("expected three proposed draft setup records: %+v", preview)
}
for _, change := range preview.Changes {
if change.EntityName == "" || len(change.After) == 0 {
t.Fatalf("unnamed or unspecified change: %+v", change)
}
}
for _, model := range []any{&models.AgentTeam{}, &models.AgentProfile{}, &models.AIAgent{}, &models.Channel{}, &models.AgentTeamSchedule{}} {
assertSetupCount(t, db, model, 0)
}
req.Apply = true
if _, err := services.SetupReadinessService.ApplyQuickFix(req, principal); err == nil {
t.Fatal("apply without preview must fail")
}
req.PreviewToken = preview.PreviewToken
applied, err := services.SetupReadinessService.ApplyQuickFix(req, principal)
if err != nil {
t.Fatal(err)
}
if !applied.Applied || applied.Readiness == nil || applied.Readiness.CanReceiveRequestsNow {
t.Fatalf("configuration must not claim shift coverage: %+v", applied)
}
for _, model := range []any{&models.AgentTeam{}, &models.AgentProfile{}, &models.AIAgent{}} {
assertSetupCount(t, db, model, 1)
}
assertSetupCount(t, db, &models.Channel{}, 0)
assertSetupCount(t, db, &models.AgentTeamSchedule{}, 0)
second := applyReviewedSetupFix(t, request.SetupQuickFixRequest{Action: services.QuickFixActionQuickSetupAll}, principal)
if len(second.Changes) != 0 {
t.Fatalf("existing configuration must be preserved: %+v", second.Changes)
}
}
func TestSetupReadiness_PreviewInvalidatedByConfigurationChange(t *testing.T) {
db := setupSetupReadinessTestDB(t)
principal := setupRepairPrincipal(t, db)
req := request.SetupQuickFixRequest{Action: services.QuickFixActionQuickSetupAll}
preview, err := services.SetupReadinessService.ApplyQuickFix(req, principal)
if err != nil {
t.Fatal(err)
}
if err := db.Create(&models.AgentTeam{Name: "Deliberately chosen team", Status: enums.StatusOk}).Error; err != nil {
t.Fatal(err)
}
req.Apply, req.PreviewToken = true, preview.PreviewToken
if _, err := services.SetupReadinessService.ApplyQuickFix(req, principal); err == nil {
t.Fatal("stale preview must fail")
}
assertSetupCount(t, db, &models.AgentTeam{}, 1)
assertSetupCount(t, db, &models.AgentProfile{}, 0)
assertSetupCount(t, db, &models.AIAgent{}, 0)
assertSetupCount(t, db, &models.Channel{}, 0)
}
func TestSetupReadiness_RepairTransactionRollsBackAllWrites(t *testing.T) {
db := setupSetupReadinessTestDB(t)
principal := setupRepairPrincipal(t, db)
req := request.SetupQuickFixRequest{Action: services.QuickFixActionQuickSetupAll}
preview, err := services.SetupReadinessService.ApplyQuickFix(req, principal)
if err != nil {
t.Fatal(err)
}
if err := db.Callback().Create().Before("gorm:create").Register("fail_setup_agent", func(tx *gorm.DB) {
if tx.Statement.Schema != nil && tx.Statement.Schema.Name == "AIAgent" {
tx.AddError(errors.New("injected agent write failure"))
}
}); err != nil {
t.Fatal(err)
}
req.Apply, req.PreviewToken = true, preview.PreviewToken
if _, err := services.SetupReadinessService.ApplyQuickFix(req, principal); err == nil {
t.Fatal("expected injected transaction failure")
}
for _, model := range []any{&models.AgentTeam{}, &models.AgentProfile{}, &models.AIAgent{}, &models.Channel{}} {
assertSetupCount(t, db, model, 0)
}
}
func TestSetupReadiness_PreservesDeletedDisabledAndZeroRollout(t *testing.T) {
db := setupSetupReadinessTestDB(t)
principal := setupRepairPrincipal(t, db)
team := models.AgentTeam{Name: "Deleted team", Status: enums.StatusDeleted}
disabledTeam := models.AgentTeam{Name: "Disabled team", Status: enums.StatusDisabled}
profile := models.AgentProfile{UserID: principal.UserID, AgentCode: "deleted-profile", Status: enums.StatusDeleted}
deletedAgent := models.AIAgent{Name: "Deleted agent", Status: enums.StatusDeleted}
disabledAgent := models.AIAgent{Name: "Disabled agent", Status: enums.StatusDisabled, RolloutPercent: 100}
zeroAgent := models.AIAgent{Name: "Zero rollout", Status: enums.StatusOk, RolloutPercent: 100}
deletedChannel := models.Channel{Name: "Deleted channel", ChannelID: "deleted-channel", Status: enums.StatusDeleted}
disabledChannel := models.Channel{Name: "Disabled channel", ChannelID: "disabled-channel", Status: enums.StatusDisabled, AIAgentRolloutPercent: 100}
for _, model := range []any{&team, &disabledTeam, &profile, &deletedAgent, &disabledAgent, &zeroAgent, &deletedChannel, &disabledChannel} {
if err := db.Create(model).Error; err != nil {
t.Fatal(err)
}
}
if err := db.Model(&zeroAgent).Update("rollout_percent", 0).Error; err != nil {
t.Fatal(err)
}
if err := db.Model(&disabledChannel).Update("ai_agent_rollout_percent", 0).Error; err != nil {
t.Fatal(err)
}
result := applyReviewedSetupFix(t, request.SetupQuickFixRequest{Action: services.QuickFixActionQuickSetupAll}, principal)
if len(result.Changes) != 0 {
t.Fatalf("broad repair must preserve intentional configurations: %+v", result.Changes)
}
for _, req := range []request.SetupQuickFixRequest{
{Action: services.QuickFixActionEnsureTeamSchedule, EntityID: team.ID},
{Action: services.QuickFixActionFixAIAgentHandoff, EntityID: deletedAgent.ID},
{Action: services.QuickFixActionEnsureDefaultChannel, EntityID: deletedChannel.ID},
{Action: services.QuickFixActionEnableMyProfile},
} {
if _, err := services.SetupReadinessService.ApplyQuickFix(req, principal); err == nil {
t.Fatalf("deleted target must not be restored: %+v", req)
}
}
for _, id := range []int64{disabledAgent.ID, zeroAgent.ID} {
res := applyReviewedSetupFix(t, request.SetupQuickFixRequest{Action: services.QuickFixActionFixAIAgentHandoff, EntityID: id}, principal)
if len(res.Changes) != 0 {
t.Fatalf("disabled/zero rollout changed: %+v", res)
}
}
if err := db.First(&zeroAgent, zeroAgent.ID).Error; err != nil {
t.Fatal(err)
}
if zeroAgent.RolloutPercent != 0 {
t.Fatal("zero rollout was overwritten")
}
if err := db.First(&disabledChannel, disabledChannel.ID).Error; err != nil {
t.Fatal(err)
}
if disabledChannel.Status != enums.StatusDisabled || disabledChannel.AIAgentRolloutPercent != 0 {
t.Fatal("disabled channel was overwritten")
}
assertSetupCount(t, db, &models.AgentTeamSchedule{}, 0)
}
func TestSetupReadiness_TargetedHandoffDoesNotRewriteOtherAgents(t *testing.T) {
db := setupSetupReadinessTestDB(t)
principal := setupRepairPrincipal(t, db)
first := models.AIAgent{Name: "Selected", Status: enums.StatusOk, RolloutPercent: 25, ServiceMode: enums.IMConversationServiceModeAIOnly, TeamIDs: "12"}
second := models.AIAgent{Name: "Unrelated", Status: enums.StatusOk, RolloutPercent: 10, ServiceMode: enums.IMConversationServiceModeAIOnly, TeamIDs: "23"}
for _, agent := range []*models.AIAgent{&first, &second} {
if err := db.Create(agent).Error; err != nil {
t.Fatal(err)
}
if err := db.Model(agent).Update("auto_handoff_on_failure", false).Error; err != nil {
t.Fatal(err)
}
}
if _, err := services.SetupReadinessService.ApplyQuickFix(request.SetupQuickFixRequest{Action: services.QuickFixActionFixAIAgentHandoff}, principal); err == nil {
t.Fatal("existing agents require a named target")
}
result := applyReviewedSetupFix(t, request.SetupQuickFixRequest{Action: services.QuickFixActionFixAIAgentHandoff, EntityID: first.ID}, principal)
if len(result.Changes) != 1 || result.Changes[0].EntityID != first.ID {
t.Fatalf("wrong scope: %+v", result.Changes)
}
if err := db.First(&first, first.ID).Error; err != nil {
t.Fatal(err)
}
if err := db.First(&second, second.ID).Error; err != nil {
t.Fatal(err)
}
if first.ServiceMode != enums.IMConversationServiceModeAIFirst || first.RolloutPercent != 25 || first.TeamIDs != "12" {
t.Fatal("selected agent was not minimally updated")
}
if second.ServiceMode != enums.IMConversationServiceModeAIOnly || second.AutoHandoffOnFailure || second.RolloutPercent != 10 || second.TeamIDs != "23" {
t.Fatal("unrelated agent was changed")
}
}
func TestSetupReadiness_OffHoursIsNotMissingSchedule(t *testing.T) {
db := setupSetupReadinessTestDB(t)
principal := setupRepairPrincipal(t, db)
now := time.Now()
team := models.AgentTeam{Name: "Day shift", Status: enums.StatusOk}
other := models.AgentTeam{Name: "Other team", Status: enums.StatusOk}
for _, model := range []any{&team, &other} {
if err := db.Create(model).Error; err != nil {
t.Fatal(err)
}
}
schedule := models.AgentTeamSchedule{TeamID: team.ID, StartAt: now.Add(24 * time.Hour), EndAt: now.Add(32 * time.Hour), Status: enums.StatusOk}
if err := db.Create(&schedule).Error; err != nil {
t.Fatal(err)
}
result := applyReviewedSetupFix(t, request.SetupQuickFixRequest{Action: services.QuickFixActionEnsureTeamSchedule, EntityID: team.ID}, principal)
if len(result.Changes) != 0 {
t.Fatal("off-hours team must not be mutated")
}
assertSetupCount(t, db, &models.AgentTeamSchedule{}, 1)
if result.Readiness == nil {
t.Fatal("missing readiness")
}
foundOffHours, foundMissing := false, false
for _, step := range result.Readiness.Steps {
if step.Key != "schedules" {
continue
}
for _, issue := range step.Issues {
if issue.EntityID == team.ID && issue.Code == "team_off_hours_now" {
foundOffHours = true
}
if issue.EntityID == other.ID && issue.Code == "team_no_schedule_configured" {
foundMissing = true
}
if issue.QuickFixAction != "" {
t.Fatal("shift changes require explicit scheduling")
}
}
}
if !foundOffHours || !foundMissing {
t.Fatal("missing and off-hours schedules must be distinguished")
}
}
func TestSetupReadiness_AdminRoleDoesNotOverridePermissionDenial(t *testing.T) {
db := setupSetupReadinessTestDB(t)
principal := setupRepairPrincipal(t, db)
principal.Permissions = nil
for _, role := range []string{constants.RoleCodeAdmin, constants.RoleCodeSuperAdmin} {
principal.Roles = []string{role}
for _, action := range []string{services.QuickFixActionQuickSetupAll, services.QuickFixActionEnsureTeamSchedule, services.QuickFixActionEnableMyProfile, services.QuickFixActionFixAIAgentHandoff, services.QuickFixActionEnsureDefaultChannel} {
if _, err := services.SetupReadinessService.ApplyQuickFix(request.SetupQuickFixRequest{Action: action}, principal); err == nil {
t.Fatalf("role %s bypassed explicit permission list for %s", role, action)
}
}
}
assertSetupCount(t, db, &models.AgentTeam{}, 0)
}
func TestSetupReadiness_StatusOnlyPermissionRemainsScoped(t *testing.T) {
db := setupSetupReadinessTestDB(t)
principal := setupRepairPrincipal(t, db)
team := models.AgentTeam{Name: "Existing", Status: enums.StatusOk}
if err := db.Create(&team).Error; err != nil {
t.Fatal(err)
}
profile := models.AgentProfile{UserID: principal.UserID, TeamID: team.ID, AgentCode: "status-only", Status: enums.StatusOk, ServiceStatus: enums.ServiceStatusBusy, AutoAssignEnabled: true, MaxConcurrentCount: 5}
if err := db.Create(&profile).Error; err != nil {
t.Fatal(err)
}
principal.Permissions = []string{constants.PermissionAgentUpdateStatus.Code}
result := applyReviewedSetupFix(t, request.SetupQuickFixRequest{Action: services.QuickFixActionEnableMyProfile}, principal)
if len(result.Changes) != 1 || len(result.Changes[0].After) != 1 {
t.Fatalf("status-only permission exceeded: %+v", result.Changes)
}
assertSetupCount(t, db, &models.AgentTeamSchedule{}, 0)
if err := db.Model(&profile).Update("auto_assign_enabled", false).Error; err != nil {
t.Fatal(err)
}
if _, err := services.SetupReadinessService.ApplyQuickFix(request.SetupQuickFixRequest{Action: services.QuickFixActionEnableMyProfile}, principal); err == nil {
t.Fatal("status permission must not authorize config edits")
}
}
func TestSetupReadiness_SimulationEmptyArraysAndHonestCoverage(t *testing.T) {
db := setupSetupReadinessTestDB(t)
principal := setupRepairPrincipal(t, db)
for _, channelID := range []int64{0, 9999} {
result, err := services.SetupReadinessService.SimulateRoute(request.SetupSimulateRouteRequest{ChannelID: channelID}, principal)
if err != nil {
t.Fatal(err)
}
encoded, err := json.Marshal(result)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(encoded), `"candidate_operators":[]`) {
t.Fatalf("null candidates: %s", encoded)
}
}
applyReviewedSetupFix(t, request.SetupQuickFixRequest{Action: services.QuickFixActionQuickSetupAll}, principal)
var team models.AgentTeam
var agent models.AIAgent
if err := db.First(&team).Error; err != nil {
t.Fatal(err)
}
if err := db.First(&agent).Error; err != nil {
t.Fatal(err)
}
if err := db.Model(&agent).Update("published_revision_id", 1).Error; err != nil {
t.Fatal(err)
}
channel := models.Channel{Name: "Published human-only test route", ChannelID: "human-only-test", ChannelType: "web", AIAgentID: agent.ID, AIAgentRolloutPercent: 100, Status: enums.StatusOk}
if err := db.Create(&channel).Error; err != nil {
t.Fatal(err)
}
now := time.Now()
if err := db.Create(&models.AgentTeamSchedule{TeamID: team.ID, StartAt: now.Add(-time.Hour), EndAt: now.Add(time.Hour), Status: enums.StatusOk}).Error; err != nil {
t.Fatal(err)
}
// A configured profile alone is not live staffing. This specific success
// fixture supplies an employee account, current permission, and login lease;
// other setup fixtures retain their original visitor/permission boundaries.
seedSetupSimulationPresence(t, db, principal, now)
result, err := services.SetupReadinessService.SimulateRoute(request.SetupSimulateRouteRequest{ChannelID: channel.ID}, principal)
if err != nil {
t.Fatal(err)
}
if !result.WillReachOperator || result.MatchedOperator == nil || result.MatchedOperator.UserID != principal.UserID {
t.Fatalf("human-only check failed: %+v", result)
}
if _, err := services.SetupReadinessService.SimulateRoute(request.SetupSimulateRouteRequest{ChannelID: channel.ID, BypassAI: true}, principal); err == nil {
t.Fatal("unsupported bypass must be explicitly rejected")
}
if err := db.Model(&models.AIAgent{}).Where("id = ?", channel.AIAgentID).Update("service_mode", enums.IMConversationServiceModeAIFirst).Error; err != nil {
t.Fatal(err)
}
result, err = services.SetupReadinessService.SimulateRoute(request.SetupSimulateRouteRequest{ChannelID: channel.ID}, principal)
if err != nil {
t.Fatal(err)
}
if result.WillReachOperator || result.FinalOutcomeCode != "ai_execution_not_simulated" {
t.Fatalf("AI route must not promise assignment: %+v", result)
}
if err := db.Model(&channel).Update("ai_agent_rollout_percent", 25).Error; err != nil {
t.Fatal(err)
}
result, err = services.SetupReadinessService.SimulateRoute(request.SetupSimulateRouteRequest{ChannelID: channel.ID}, principal)
if err != nil {
t.Fatal(err)
}
if result.WillReachOperator || result.FinalOutcomeCode != "partial_rollout_not_simulated" {
t.Fatalf("partial rollout must not promise assignment: %+v", result)
}
}
func TestSetupReadiness_PostCommitFailuresAreVisible(t *testing.T) {
db := setupSetupReadinessTestDB(t)
principal := setupRepairPrincipal(t, db)
req := request.SetupQuickFixRequest{Action: services.QuickFixActionQuickSetupAll}
preview, err := services.SetupReadinessService.ApplyQuickFix(req, principal)
if err != nil {
t.Fatal(err)
}
if err := db.Callback().Query().Before("gorm:query").Register("fail_post_commit_dispatch", func(tx *gorm.DB) {
if tx.Statement.Schema != nil && tx.Statement.Schema.Name == "Conversation" {
tx.AddError(errors.New("injected dispatch read failure"))
}
}); err != nil {
t.Fatal(err)
}
req.Apply, req.PreviewToken = true, preview.PreviewToken
result, err := services.SetupReadinessService.ApplyQuickFix(req, principal)
if err != nil {
t.Fatalf("committed repair must not appear rolled back: %v", err)
}
if !result.Applied || !slices.Contains(result.WarningCodes, "dispatch_failed") {
t.Fatalf("dispatch failure must be visible: %+v", result)
}
for _, model := range []any{&models.AgentTeam{}, &models.AgentProfile{}, &models.AIAgent{}} {
assertSetupCount(t, db, model, 1)
}
assertSetupCount(t, db, &models.Channel{}, 0)
}
func TestSetupReadiness_DefaultChannelRequiresPublishedAgent(t *testing.T) {
db := setupSetupReadinessTestDB(t)
principal := setupRepairPrincipal(t, db)
applyReviewedSetupFix(t, request.SetupQuickFixRequest{Action: services.QuickFixActionQuickSetupAll}, principal)
result := applyReviewedSetupFix(t, request.SetupQuickFixRequest{Action: services.QuickFixActionEnsureDefaultChannel}, principal)
if len(result.Changes) != 0 || !slices.Contains(result.WarningCodes, "agent_publication_required") {
t.Fatalf("draft agent must not be bound implicitly: %+v", result)
}
assertSetupCount(t, db, &models.Channel{}, 0)
var agent models.AIAgent
if err := db.First(&agent).Error; err != nil {
t.Fatal(err)
}
// This unit fixture supplies the publication marker; revision publication itself
// belongs to the existing agent publishing service's tests.
if err := db.Model(&agent).Update("published_revision_id", 1).Error; err != nil {
t.Fatal(err)
}
result = applyReviewedSetupFix(t, request.SetupQuickFixRequest{Action: services.QuickFixActionEnsureDefaultChannel}, principal)
if len(result.Changes) != 1 || result.Changes[0].EntityType != "channel" {
t.Fatalf("expected reviewed channel create: %+v", result.Changes)
}
assertSetupCount(t, db, &models.Channel{}, 1)
assertSetupCount(t, db, &models.AgentTeamSchedule{}, 0)
}
func TestSetupReadiness_MyProfileRejectsAnotherNamedTarget(t *testing.T) {
db := setupSetupReadinessTestDB(t)
principal := setupRepairPrincipal(t, db)
applyReviewedSetupFix(t, request.SetupQuickFixRequest{Action: services.QuickFixActionQuickSetupAll}, principal)
var profile models.AgentProfile
if err := db.Where("user_id = ?", principal.UserID).First(&profile).Error; err != nil {
t.Fatal(err)
}
if _, err := services.SetupReadinessService.ApplyQuickFix(request.SetupQuickFixRequest{Action: services.QuickFixActionEnableMyProfile, EntityID: profile.ID + 1}, principal); err == nil {
t.Fatal("my-profile action must not silently substitute a different named target")
}
}
func seedSetupSimulationPresence(t *testing.T, db *gorm.DB, principal *dto.AuthPrincipal, now time.Time) {
t.Helper()
if err := db.Model(&models.User{}).Where("id = ?", principal.UserID).Update("user_type", enums.UserTypeEmployee).Error; err != nil {
t.Fatal(err)
}
role := models.Role{Code: "setup-simulation-operator", Status: enums.StatusOk}
permission := models.Permission{Code: constants.PermissionConversationView.Code, Status: enums.StatusOk}
session := models.LoginSession{UserID: principal.UserID, Token: "ak_isolated_setup_simulation", ExpiredAt: now.Add(time.Hour)}
if err := db.Where("code = ?", permission.Code).FirstOrCreate(&permission).Error; err != nil {
t.Fatal(err)
}
for _, item := range []any{&role, &session} {
if err := db.Create(item).Error; err != nil {
t.Fatal(err)
}
}
for _, item := range []any{&models.UserRole{UserID: principal.UserID, RoleID: role.ID}, &models.RolePermission{RoleID: role.ID, PermissionID: permission.ID}, &models.AgentPresenceLease{ID: "setup-simulation-presence", UserID: principal.UserID, LoginSessionID: session.ID, ExpiresAt: now.Add(time.Minute)}} {
if err := db.Create(item).Error; err != nil {
t.Fatal(err)
}
}
principal.UserType = enums.UserTypeEmployee
principal.LoginSessionID = session.ID
principal.ExpiresAt = session.ExpiredAt
principal.Permissions = append(principal.Permissions, permission.Code)
}