Files
cms-server/service/service_event/event_test.go
Asai Neko a6224eb957
Some checks failed
Server Check Build (NixCN CMS) TeamCity build finished
Server Prod Build (NixCN CMS) TeamCity build failed
Fix lv40+ can't edit event info in event update service
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-04-28 14:04:10 +08:00

802 lines
20 KiB
Go

package service_event
import (
"context"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"nixcn-cms/data"
"nixcn-cms/internal/exception"
"nixcn-cms/testutil"
)
func newEventSvc() EventService { return NewEventService() }
func makeOwner(t *testing.T, permLevel uint) *data.User {
t.Helper()
return testutil.SeedUser(t, uuid.New().String()+"@test.com", permLevel)
}
func strPtr(s string) *string { return &s }
func createEventForOwner(t *testing.T, ctx context.Context, owner *data.User, typ string) *EventCreateResult {
t.Helper()
svc := newEventSvc()
return svc.Create(&EventCreatePayload{
Context: ctx,
Data: &EventCreateData{
UserId: owner.UserId.String(),
PermissionLevel: owner.PermissionLevel,
Type: typ,
Name: "Test Event " + uuid.New().String()[:8],
Subtitle: "subtitle",
StartTime: time.Now().Add(24 * time.Hour),
EndTime: time.Now().Add(48 * time.Hour),
Quota: 100,
Limit: 150,
},
})
}
// ---- Create ----
func TestEventCreateSuccess(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
result := createEventForOwner(t, ctx, owner, "official")
assert.Equal(t, 200, result.Common.HttpCode)
require.NotNil(t, result.Data)
assert.NotEmpty(t, result.Data.EventId)
}
func TestEventCreateLv30OnlyParty(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 30)
partyResult := createEventForOwner(t, ctx, owner, "party")
assert.Equal(t, 200, partyResult.Common.HttpCode)
officialResult := createEventForOwner(t, ctx, owner, "official")
assert.Equal(t, 403, officialResult.Common.HttpCode)
assert.Equal(t, exception.EventCreateTypeNotAllowed, officialResult.Common.Exception.Original)
}
func TestEventCreateInvalidType(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
svc := newEventSvc()
result := svc.Create(&EventCreatePayload{
Context: ctx,
Data: &EventCreateData{
UserId: owner.UserId.String(),
PermissionLevel: owner.PermissionLevel,
Type: "invalid-type",
Name: "Event",
StartTime: time.Now().Add(time.Hour),
EndTime: time.Now().Add(2 * time.Hour),
Quota: 10,
Limit: 20,
},
})
assert.Equal(t, 400, result.Common.HttpCode)
}
// ---- List ----
func TestEventList(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
for i := 0; i < 3; i++ {
r := createEventForOwner(t, ctx, owner, "party")
require.Equal(t, 200, r.Common.HttpCode)
}
limit := "10"
offset := "0"
svc := newEventSvc()
result := svc.List(&EventListPayload{
Context: ctx,
UserId: owner.UserId,
Data: &EventListData{
Limit: &limit,
Offset: &offset,
},
})
assert.Equal(t, 200, result.Common.HttpCode)
require.NotNil(t, result.Data)
assert.GreaterOrEqual(t, len(*result.Data.Items), 3)
assert.GreaterOrEqual(t, result.Data.Total, int64(3))
}
func TestEventListLv30ShowsOwnOnly(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
lv30Owner := makeOwner(t, 30)
otherOwner := makeOwner(t, 40)
r := createEventForOwner(t, ctx, lv30Owner, "party")
require.Equal(t, 200, r.Common.HttpCode)
r = createEventForOwner(t, ctx, otherOwner, "party")
require.Equal(t, 200, r.Common.HttpCode)
limit := "10"
offset := "0"
svc := newEventSvc()
result := svc.List(&EventListPayload{
Context: ctx,
UserId: lv30Owner.UserId,
Data: &EventListData{
Limit: &limit,
Offset: &offset,
PermissionLevel: 30,
},
})
assert.Equal(t, 200, result.Common.HttpCode)
require.NotNil(t, result.Data)
for _, ev := range *result.Data.Items {
assert.Equal(t, lv30Owner.UserId.String(), ev.Owner)
}
}
func TestEventListRequiresOffset(t *testing.T) {
testutil.Setup(t)
svc := newEventSvc()
result := svc.List(&EventListPayload{
Context: context.Background(),
UserId: uuid.New(),
Data: &EventListData{},
})
assert.Equal(t, 400, result.Common.HttpCode)
}
// ---- GetInfo ----
func TestEventGetInfo(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
cr := createEventForOwner(t, ctx, owner, "official")
require.Equal(t, 200, cr.Common.HttpCode)
eventId, _ := uuid.Parse(cr.Data.EventId)
svc := newEventSvc()
result := svc.GetInfo(&EventInfoPayload{
Context: ctx,
UserId: owner.UserId,
Data: &EventInfoData{EventId: eventId},
})
assert.Equal(t, 200, result.Common.HttpCode)
require.NotNil(t, result.Data)
assert.Equal(t, cr.Data.EventId, result.Data.EventId)
}
func TestEventGetInfoNotFound(t *testing.T) {
testutil.Setup(t)
svc := newEventSvc()
result := svc.GetInfo(&EventInfoPayload{
Context: context.Background(),
UserId: uuid.New(),
Data: &EventInfoData{EventId: uuid.New()},
})
assert.Equal(t, 404, result.Common.HttpCode)
assert.Equal(t, exception.EventInfoNotFound, result.Common.Exception.Original)
}
// ---- Join ----
func TestEventJoinSuccess(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
joiner := testutil.SeedUser(t, uuid.New().String()+"@test.com", 10)
require.NoError(t, new(data.User).PatchByUserId(ctx, joiner.UserId, data.WithNickname("Joiner")))
cr := createEventForOwner(t, ctx, owner, "party")
require.Equal(t, 200, cr.Common.HttpCode)
svc := newEventSvc()
result := svc.Join(&EventJoinPayload{
Context: ctx,
Data: &EventJoinData{
EventId: cr.Data.EventId,
UserId: joiner.UserId.String(),
},
})
assert.Equal(t, 200, result.Common.HttpCode)
require.NotNil(t, result.Data)
assert.NotEmpty(t, result.Data.AttendanceId)
}
func TestEventJoinKycRequired(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
joiner := testutil.SeedUser(t, uuid.New().String()+"@test.com", 10)
require.NoError(t, new(data.User).PatchByUserId(ctx, joiner.UserId, data.WithNickname("Joiner")))
svc := newEventSvc()
cr := svc.Create(&EventCreatePayload{
Context: ctx,
Data: &EventCreateData{
UserId: owner.UserId.String(),
PermissionLevel: owner.PermissionLevel,
Type: "party",
Name: "KYC Event",
StartTime: time.Now().Add(time.Hour),
EndTime: time.Now().Add(2 * time.Hour),
EnableKYC: true,
Quota: 100,
Limit: 150,
},
})
require.Equal(t, 200, cr.Common.HttpCode)
result := svc.Join(&EventJoinPayload{
Context: ctx,
Data: &EventJoinData{
EventId: cr.Data.EventId,
UserId: joiner.UserId.String(),
KycId: "",
},
})
assert.Equal(t, 400, result.Common.HttpCode)
}
func TestEventJoinNicknameRequired(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
joiner := testutil.SeedUser(t, uuid.New().String()+"@test.com", 10)
// No nickname set
cr := createEventForOwner(t, ctx, owner, "party")
require.Equal(t, 200, cr.Common.HttpCode)
svc := newEventSvc()
result := svc.Join(&EventJoinPayload{
Context: ctx,
Data: &EventJoinData{
EventId: cr.Data.EventId,
UserId: joiner.UserId.String(),
},
})
assert.Equal(t, 200, result.Common.HttpCode)
}
// ---- Update ----
func TestEventUpdateSuccess(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
cr := createEventForOwner(t, ctx, owner, "party")
require.Equal(t, 200, cr.Common.HttpCode)
svc := newEventSvc()
result := svc.Update(&EventUpdatePayload{
Context: ctx,
Data: &EventUpdateData{
EventId: cr.Data.EventId,
UserId: owner.UserId.String(),
Name: strPtr("Updated Event Name"),
},
})
assert.Equal(t, 200, result.Common.HttpCode)
}
func TestEventUpdateQuotaAndLimit(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
cr := createEventForOwner(t, ctx, owner, "party")
require.Equal(t, 200, cr.Common.HttpCode)
eventId, err := uuid.Parse(cr.Data.EventId)
require.NoError(t, err)
quota := int64(200)
limit := int64(250)
svc := newEventSvc()
result := svc.Update(&EventUpdatePayload{
Context: ctx,
Data: &EventUpdateData{
EventId: cr.Data.EventId,
UserId: owner.UserId.String(),
Quota: &quota,
Limit: &limit,
},
})
require.Equal(t, 200, result.Common.HttpCode)
ev, err := new(data.Event).GetEventById(ctx, eventId)
require.NoError(t, err)
require.NotNil(t, ev)
assert.Equal(t, quota, ev.Quota)
assert.Equal(t, limit, ev.Limit)
}
func TestEventUpdateNotOwner(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 30)
notOwner := makeOwner(t, 30)
cr := createEventForOwner(t, ctx, owner, "party")
require.Equal(t, 200, cr.Common.HttpCode)
svc := newEventSvc()
result := svc.Update(&EventUpdatePayload{
Context: ctx,
Data: &EventUpdateData{
EventId: cr.Data.EventId,
UserId: notOwner.UserId.String(),
PermissionLevel: notOwner.PermissionLevel,
Name: strPtr("Hacked"),
},
})
assert.Equal(t, 403, result.Common.HttpCode)
assert.Equal(t, exception.EventUpdateNotOwner, result.Common.Exception.Original)
}
// Lv40+ admins may bypass the owner-only restriction.
func TestEventUpdateAdminBypass(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 30)
admin := makeOwner(t, 40)
cr := createEventForOwner(t, ctx, owner, "party")
require.Equal(t, 200, cr.Common.HttpCode)
svc := newEventSvc()
result := svc.Update(&EventUpdatePayload{
Context: ctx,
Data: &EventUpdateData{
EventId: cr.Data.EventId,
UserId: admin.UserId.String(),
PermissionLevel: admin.PermissionLevel,
Name: strPtr("Edited By Admin"),
},
})
assert.Equal(t, 200, result.Common.HttpCode)
}
func TestEventUpdateImmutableFields(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
cr := createEventForOwner(t, ctx, owner, "party")
require.Equal(t, 200, cr.Common.HttpCode)
typeVal := "official"
svc := newEventSvc()
result := svc.Update(&EventUpdatePayload{
Context: ctx,
Data: &EventUpdateData{
EventId: cr.Data.EventId,
UserId: owner.UserId.String(),
Type: &typeVal,
},
})
assert.Equal(t, 400, result.Common.HttpCode)
assert.Equal(t, exception.EventUpdateImmutableField, result.Common.Exception.Original)
}
// ---- Delete ----
func TestEventDeleteSuccess(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
cr := createEventForOwner(t, ctx, owner, "party")
require.Equal(t, 200, cr.Common.HttpCode)
svc := newEventSvc()
result := svc.Delete(&EventDeletePayload{
Context: context.Background(),
Data: &EventDeleteData{EventId: cr.Data.EventId},
})
assert.Equal(t, 200, result.Common.HttpCode)
}
func TestEventDeleteNotFound(t *testing.T) {
testutil.Setup(t)
svc := newEventSvc()
result := svc.Delete(&EventDeletePayload{
Context: context.Background(),
Data: &EventDeleteData{EventId: uuid.New().String()},
})
assert.Equal(t, 404, result.Common.HttpCode)
}
// ---- Stats ----
func TestEventStats(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
cr := createEventForOwner(t, ctx, owner, "party")
require.Equal(t, 200, cr.Common.HttpCode)
svc := newEventSvc()
result := svc.Stats(&EventStatsPayload{
Context: ctx,
UserId: owner.UserId,
Data: &EventStatsData{EventId: cr.Data.EventId},
})
assert.Equal(t, 200, result.Common.HttpCode)
require.NotNil(t, result.Data)
assert.Equal(t, int64(0), result.Data.JoinCount)
}
func TestEventStatsNotOwner(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
notOwner := makeOwner(t, 30)
cr := createEventForOwner(t, ctx, owner, "party")
require.Equal(t, 200, cr.Common.HttpCode)
svc := newEventSvc()
result := svc.Stats(&EventStatsPayload{
Context: ctx,
UserId: notOwner.UserId,
Data: &EventStatsData{EventId: cr.Data.EventId},
})
assert.Equal(t, 403, result.Common.HttpCode)
assert.Equal(t, exception.EventStatsNotOwner, result.Common.Exception.Original)
}
// ---- Checkin ----
func TestEventCheckinSuccess(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
joiner := makeOwner(t, 10)
require.NoError(t, new(data.User).PatchByUserId(ctx, joiner.UserId, data.WithNickname("Joiner")))
cr := createEventForOwner(t, ctx, owner, "party")
require.Equal(t, 200, cr.Common.HttpCode)
svc := newEventSvc()
jr := svc.Join(&EventJoinPayload{
Context: ctx,
Data: &EventJoinData{
EventId: cr.Data.EventId,
UserId: joiner.UserId.String(),
},
})
require.Equal(t, 200, jr.Common.HttpCode)
eventId, _ := uuid.Parse(cr.Data.EventId)
result := svc.Checkin(&CheckinPayload{
Context: ctx,
UserId: joiner.UserId,
Data: &CheckinData{EventId: eventId},
})
assert.Equal(t, 200, result.Common.HttpCode)
require.NotNil(t, result.Data)
require.NotNil(t, result.Data.CheckinCode)
assert.Len(t, *result.Data.CheckinCode, 6)
}
func TestEventCheckinNotAttendee(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
notJoiner := makeOwner(t, 10)
cr := createEventForOwner(t, ctx, owner, "party")
require.Equal(t, 200, cr.Common.HttpCode)
eventId, _ := uuid.Parse(cr.Data.EventId)
svc := newEventSvc()
result := svc.Checkin(&CheckinPayload{
Context: ctx,
UserId: notJoiner.UserId,
Data: &CheckinData{EventId: eventId},
})
assert.Equal(t, 403, result.Common.HttpCode)
}
// ---- CheckinSubmit ----
func TestEventCheckinSubmitSuccess(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
joiner := makeOwner(t, 10)
require.NoError(t, new(data.User).PatchByUserId(ctx, joiner.UserId, data.WithNickname("Joiner")))
cr := createEventForOwner(t, ctx, owner, "party")
require.Equal(t, 200, cr.Common.HttpCode)
svc := newEventSvc()
jr := svc.Join(&EventJoinPayload{
Context: ctx,
Data: &EventJoinData{
EventId: cr.Data.EventId,
UserId: joiner.UserId.String(),
},
})
require.Equal(t, 200, jr.Common.HttpCode)
eventId, _ := uuid.Parse(cr.Data.EventId)
checkinResult := svc.Checkin(&CheckinPayload{
Context: ctx,
UserId: joiner.UserId,
Data: &CheckinData{EventId: eventId},
})
require.Equal(t, 200, checkinResult.Common.HttpCode)
submitResult := svc.CheckinSubmit(&CheckinSubmitPayload{
Context: ctx,
Data: &CheckinSubmitData{CheckinCode: *checkinResult.Data.CheckinCode},
})
assert.Equal(t, 200, submitResult.Common.HttpCode)
}
func TestEventCheckinSubmitInvalidCode(t *testing.T) {
testutil.Setup(t)
svc := newEventSvc()
result := svc.CheckinSubmit(&CheckinSubmitPayload{
Context: context.Background(),
Data: &CheckinSubmitData{CheckinCode: "000000"},
})
assert.Equal(t, 400, result.Common.HttpCode)
}
// ---- CheckinQuery ----
func TestEventCheckinQueryNotCheckedIn(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
joiner := makeOwner(t, 10)
require.NoError(t, new(data.User).PatchByUserId(ctx, joiner.UserId, data.WithNickname("Joiner")))
cr := createEventForOwner(t, ctx, owner, "party")
require.Equal(t, 200, cr.Common.HttpCode)
svc := newEventSvc()
jr := svc.Join(&EventJoinPayload{
Context: ctx,
Data: &EventJoinData{
EventId: cr.Data.EventId,
UserId: joiner.UserId.String(),
},
})
require.Equal(t, 200, jr.Common.HttpCode)
eventId, _ := uuid.Parse(cr.Data.EventId)
result := svc.CheckinQuery(&CheckinQueryPayload{
Context: ctx,
UserId: joiner.UserId,
Data: &CheckinQueryData{EventId: eventId},
})
assert.Equal(t, 200, result.Common.HttpCode)
require.NotNil(t, result.Data)
assert.Nil(t, result.Data.CheckinAt)
}
func TestEventCheckinQueryNotFound(t *testing.T) {
testutil.Setup(t)
svc := newEventSvc()
result := svc.CheckinQuery(&CheckinQueryPayload{
Context: context.Background(),
UserId: uuid.New(),
Data: &CheckinQueryData{EventId: uuid.New()},
})
assert.Equal(t, 404, result.Common.HttpCode)
}
// ---- AttendanceList ----
func TestEventAttendanceListSuccess(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
cr := createEventForOwner(t, ctx, owner, "party")
require.Equal(t, 200, cr.Common.HttpCode)
eventId, _ := uuid.Parse(cr.Data.EventId)
svc := newEventSvc()
for i := 0; i < 2; i++ {
joiner := makeOwner(t, 10)
require.NoError(t, new(data.User).PatchByUserId(ctx, joiner.UserId, data.WithNickname("Joiner")))
jr := svc.Join(&EventJoinPayload{
Context: ctx,
Data: &EventJoinData{
EventId: cr.Data.EventId,
UserId: joiner.UserId.String(),
},
})
require.Equal(t, 200, jr.Common.HttpCode)
}
limit := "10"
offset := "0"
result := svc.AttendanceList(&AttendanceListPayload{
Context: ctx,
UserId: owner.UserId,
Data: &AttendanceListData{
EventId: eventId,
Limit: &limit,
Offset: &offset,
},
})
assert.Equal(t, 200, result.Common.HttpCode)
require.NotNil(t, result.Data)
assert.Equal(t, int64(2), result.Data.Total)
assert.Len(t, result.Data.Items, 2)
}
func TestEventAttendanceListNotOwner(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
notOwner := makeOwner(t, 30)
cr := createEventForOwner(t, ctx, owner, "party")
require.Equal(t, 200, cr.Common.HttpCode)
eventId, _ := uuid.Parse(cr.Data.EventId)
svc := newEventSvc()
result := svc.AttendanceList(&AttendanceListPayload{
Context: ctx,
UserId: notOwner.UserId,
Data: &AttendanceListData{EventId: eventId},
})
assert.Equal(t, 403, result.Common.HttpCode)
}
func TestEventAttendanceListNotFound(t *testing.T) {
testutil.Setup(t)
svc := newEventSvc()
result := svc.AttendanceList(&AttendanceListPayload{
Context: context.Background(),
UserId: uuid.New(),
Data: &AttendanceListData{EventId: uuid.New()},
})
assert.Equal(t, 404, result.Common.HttpCode)
}
// ---- GetAttendanceGuide ----
func TestEventGetAttendanceGuideSuccess(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
joiner := makeOwner(t, 10)
require.NoError(t, new(data.User).PatchByUserId(ctx, joiner.UserId, data.WithNickname("Joiner")))
cr := createEventForOwner(t, ctx, owner, "party")
require.Equal(t, 200, cr.Common.HttpCode)
eventId, _ := uuid.Parse(cr.Data.EventId)
require.NoError(t, new(data.Event).PatchByEventId(ctx, eventId, data.WithAttendanceGuide("base64guidetext")))
svc := newEventSvc()
jr := svc.Join(&EventJoinPayload{
Context: ctx,
Data: &EventJoinData{
EventId: cr.Data.EventId,
UserId: joiner.UserId.String(),
},
})
require.Equal(t, 200, jr.Common.HttpCode)
result := svc.GetAttendanceGuide(&AttendanceGuidePayload{
Context: ctx,
UserId: joiner.UserId,
Data: &AttendanceGuideData{EventId: eventId},
})
assert.Equal(t, 200, result.Common.HttpCode)
require.NotNil(t, result.Data)
assert.Equal(t, "base64guidetext", result.Data.AttendanceGuide)
}
// ---- Join edge cases ----
func TestEventJoinLimitExceeded(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
owner := makeOwner(t, 40)
svc := newEventSvc()
cr := svc.Create(&EventCreatePayload{
Context: ctx,
Data: &EventCreateData{
UserId: owner.UserId.String(),
PermissionLevel: owner.PermissionLevel,
Type: "party",
Name: "Zero Capacity Event",
Subtitle: "sub",
StartTime: time.Now().Add(time.Hour),
EndTime: time.Now().Add(2 * time.Hour),
Quota: 0,
Limit: 0,
},
})
require.Equal(t, 200, cr.Common.HttpCode)
joiner := makeOwner(t, 10)
require.NoError(t, new(data.User).PatchByUserId(ctx, joiner.UserId, data.WithNickname("Joiner")))
result := svc.Join(&EventJoinPayload{
Context: ctx,
Data: &EventJoinData{
EventId: cr.Data.EventId,
UserId: joiner.UserId.String(),
},
})
assert.Equal(t, 403, result.Common.HttpCode)
assert.Equal(t, exception.EventJoinLimitExceeded, result.Common.Exception.Original)
}