Add more tests for modules co worked by claude
All checks were successful
Server Check Build (NixCN CMS) TeamCity build finished
All checks were successful
Server Check Build (NixCN CMS) TeamCity build finished
Signed-off-by: Asai Neko <sugar@sne.moe>
This commit is contained in:
@@ -255,3 +255,189 @@ func TestAgendaReviewHandlerInvalidStatus(t *testing.T) {
|
||||
})
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func TestAgendaReviewHandlerSuccess(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
owner := testutil.SeedUser(t, testutil.RandomEmail(), 40)
|
||||
attendee := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
event := seedEventWithAttendee(t, owner, attendee)
|
||||
manager := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
managerToken := issueToken(t, manager.UserId)
|
||||
attendeeToken := issueToken(t, attendee.UserId)
|
||||
r := newAgendaRouter(t)
|
||||
|
||||
submitW := postWithBearer(t, r, "/agenda/submit", attendeeToken, service_agenda.SubmitData{
|
||||
EventId: event.EventId,
|
||||
Name: "Talk to Review",
|
||||
Description: "desc",
|
||||
})
|
||||
require.Equal(t, http.StatusOK, submitW.Code)
|
||||
var submitResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(submitW.Body.Bytes(), &submitResp))
|
||||
agendaId := submitResp["data"].(map[string]any)["agenda_id"].(string)
|
||||
|
||||
w := patchWithBearer(t, r, "/agenda/review", managerToken, service_agenda.AgendaReviewData{
|
||||
EventId: event.EventId,
|
||||
AgendaId: uuid.MustParse(agendaId),
|
||||
Status: "approved",
|
||||
})
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
// ---- List (success) ----
|
||||
|
||||
func TestAgendaListHandlerSuccess(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
owner := testutil.SeedUser(t, testutil.RandomEmail(), 40)
|
||||
attendee := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
event := seedEventWithAttendee(t, owner, attendee)
|
||||
manager := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
managerToken := issueToken(t, manager.UserId)
|
||||
attendeeToken := issueToken(t, attendee.UserId)
|
||||
r := newAgendaRouter(t)
|
||||
|
||||
postWithBearer(t, r, "/agenda/submit", attendeeToken, service_agenda.SubmitData{
|
||||
EventId: event.EventId,
|
||||
Name: "Talk 1",
|
||||
})
|
||||
|
||||
w := getWithBearer(t, r, "/agenda/list?event_id="+event.EventId.String(), managerToken)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
// ---- Update ----
|
||||
|
||||
func TestAgendaUpdateHandlerMissingAgendaId(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
user := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
token := issueToken(t, user.UserId)
|
||||
r := newAgendaRouter(t)
|
||||
|
||||
w := patchWithBearer(t, r, "/agenda/update", token, map[string]any{
|
||||
"name": "New Name",
|
||||
})
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func TestAgendaUpdateHandlerSuccess(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
owner := testutil.SeedUser(t, testutil.RandomEmail(), 40)
|
||||
attendee := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
event := seedEventWithAttendee(t, owner, attendee)
|
||||
attendeeToken := issueToken(t, attendee.UserId)
|
||||
r := newAgendaRouter(t)
|
||||
|
||||
submitW := postWithBearer(t, r, "/agenda/submit", attendeeToken, service_agenda.SubmitData{
|
||||
EventId: event.EventId,
|
||||
Name: "Original Talk",
|
||||
Description: "desc",
|
||||
})
|
||||
require.Equal(t, http.StatusOK, submitW.Code)
|
||||
var submitResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(submitW.Body.Bytes(), &submitResp))
|
||||
agendaId := submitResp["data"].(map[string]any)["agenda_id"].(string)
|
||||
|
||||
newName := "Updated Talk"
|
||||
w := patchWithBearer(t, r, "/agenda/update", attendeeToken, service_agenda.AgendaUpdateData{
|
||||
AgendaId: uuid.MustParse(agendaId),
|
||||
Name: &newName,
|
||||
})
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
// ---- Schedule PATCH ----
|
||||
|
||||
func TestAgendaScheduleHandlerMissingAgendaId(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
manager := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
token := issueToken(t, manager.UserId)
|
||||
r := newAgendaRouter(t)
|
||||
|
||||
w := patchWithBearer(t, r, "/agenda/schedule", token, map[string]any{
|
||||
"start_time": time.Now().Add(time.Hour),
|
||||
"end_time": time.Now().Add(2 * time.Hour),
|
||||
})
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func TestAgendaScheduleHandlerSuccess(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
owner := testutil.SeedUser(t, testutil.RandomEmail(), 40)
|
||||
attendee := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
event := seedEventWithAttendee(t, owner, attendee)
|
||||
manager := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
managerToken := issueToken(t, manager.UserId)
|
||||
attendeeToken := issueToken(t, attendee.UserId)
|
||||
r := newAgendaRouter(t)
|
||||
|
||||
submitW := postWithBearer(t, r, "/agenda/submit", attendeeToken, service_agenda.SubmitData{
|
||||
EventId: event.EventId,
|
||||
Name: "Schedule Me",
|
||||
Description: "base64desc",
|
||||
})
|
||||
require.Equal(t, http.StatusOK, submitW.Code)
|
||||
var submitResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(submitW.Body.Bytes(), &submitResp))
|
||||
agendaId := submitResp["data"].(map[string]any)["agenda_id"].(string)
|
||||
|
||||
reviewW := patchWithBearer(t, r, "/agenda/review", managerToken, service_agenda.AgendaReviewData{
|
||||
EventId: event.EventId,
|
||||
AgendaId: uuid.MustParse(agendaId),
|
||||
Status: "approved",
|
||||
})
|
||||
require.Equal(t, http.StatusOK, reviewW.Code)
|
||||
|
||||
now := time.Now()
|
||||
w := patchWithBearer(t, r, "/agenda/schedule", managerToken, service_agenda.AgendaScheduleData{
|
||||
AgendaId: uuid.MustParse(agendaId),
|
||||
StartTime: now.Add(time.Hour),
|
||||
EndTime: now.Add(2 * time.Hour),
|
||||
})
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
// ---- ScheduleGet (success) ----
|
||||
|
||||
func TestAgendaScheduleGetHandlerSuccess(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
owner := testutil.SeedUser(t, testutil.RandomEmail(), 40)
|
||||
attendee := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
event := seedEventWithAttendee(t, owner, attendee)
|
||||
manager := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
managerToken := issueToken(t, manager.UserId)
|
||||
attendeeToken := issueToken(t, attendee.UserId)
|
||||
r := newAgendaRouter(t)
|
||||
|
||||
submitW := postWithBearer(t, r, "/agenda/submit", attendeeToken, service_agenda.SubmitData{
|
||||
EventId: event.EventId,
|
||||
Name: "Published Talk",
|
||||
Description: "base64desc",
|
||||
})
|
||||
require.Equal(t, http.StatusOK, submitW.Code)
|
||||
var submitResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(submitW.Body.Bytes(), &submitResp))
|
||||
agendaId := submitResp["data"].(map[string]any)["agenda_id"].(string)
|
||||
|
||||
patchWithBearer(t, r, "/agenda/review", managerToken, service_agenda.AgendaReviewData{
|
||||
EventId: event.EventId,
|
||||
AgendaId: uuid.MustParse(agendaId),
|
||||
Status: "approved",
|
||||
})
|
||||
|
||||
now := time.Now()
|
||||
patchWithBearer(t, r, "/agenda/schedule", managerToken, service_agenda.AgendaScheduleData{
|
||||
AgendaId: uuid.MustParse(agendaId),
|
||||
StartTime: now.Add(time.Hour),
|
||||
EndTime: now.Add(2 * time.Hour),
|
||||
})
|
||||
|
||||
require.NoError(t, new(data.Event).PatchByEventId(t.Context(), event.EventId, data.WithIsAgendaPublished(true)))
|
||||
|
||||
w := getWithBearer(t, r, "/agenda/schedule?event_id="+event.EventId.String(), attendeeToken)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
items, ok := resp["data"].([]any)
|
||||
require.True(t, ok)
|
||||
assert.Len(t, items, 1)
|
||||
}
|
||||
|
||||
@@ -205,3 +205,47 @@ func TestExchangeHandlerNoAuth(t *testing.T) {
|
||||
// No auth header → 401
|
||||
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
||||
}
|
||||
|
||||
func TestExchangeHandlerSuccess(t *testing.T) {
|
||||
r := setupAuthRouter(t)
|
||||
|
||||
// Step 1: Magic (debug mode, returns redirect URI with code)
|
||||
magicW := postJSON(t, r, "/auth/magic", service_auth.MagicData{
|
||||
ClientId: testutil.TestClientID,
|
||||
RedirectUri: "http://localhost/callback",
|
||||
State: "s",
|
||||
Email: "exchange@example.com",
|
||||
})
|
||||
require.Equal(t, http.StatusOK, magicW.Code)
|
||||
var magicResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(magicW.Body.Bytes(), &magicResp))
|
||||
rawURI := magicResp["data"].(map[string]any)["uri"].(string)
|
||||
code := extractQueryParam(t, rawURI, "code")
|
||||
|
||||
// Step 2: Redirect → produces token code
|
||||
redirectW := getRequest(t, r, "/auth/redirect?client_id="+testutil.TestClientID+
|
||||
"&redirect_uri=http://localhost/callback&code="+code+"&state=s")
|
||||
require.Equal(t, http.StatusFound, redirectW.Code)
|
||||
location := redirectW.Header().Get("Location")
|
||||
tokenCode := extractQueryParam(t, location, "code")
|
||||
|
||||
// Step 3: Token → get access token
|
||||
tokenW := postJSON(t, r, "/auth/token", service_auth.TokenData{Code: tokenCode})
|
||||
require.Equal(t, http.StatusOK, tokenW.Code)
|
||||
var tokenResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(tokenW.Body.Bytes(), &tokenResp))
|
||||
accessToken := tokenResp["data"].(map[string]any)["access_token"].(string)
|
||||
|
||||
// Step 4: Exchange (requires JWT Bearer token)
|
||||
w := postWithBearer(t, r, "/auth/exchange", accessToken, service_auth.ExchangeData{
|
||||
ClientId: testutil.TestClientID,
|
||||
RedirectUri: "http://localhost/callback",
|
||||
State: "s",
|
||||
})
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var exchangeResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &exchangeResp))
|
||||
exchangeData, ok := exchangeResp["data"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
assert.NotEmpty(t, exchangeData["redirect_uri"])
|
||||
}
|
||||
|
||||
@@ -227,3 +227,382 @@ func TestEventDeleteHandlerNotFound(t *testing.T) {
|
||||
})
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
}
|
||||
|
||||
func TestEventDeleteHandlerSuccess(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
owner := testutil.SeedUser(t, testutil.RandomEmail(), 40)
|
||||
token := issueToken(t, owner.UserId)
|
||||
r := newEventRouter(t)
|
||||
|
||||
createW := postWithBearer(t, r, "/event/create", token, service_event.EventCreateData{
|
||||
Type: "party",
|
||||
Name: "Delete Me",
|
||||
Subtitle: "sub",
|
||||
StartTime: time.Now().Add(24 * time.Hour),
|
||||
EndTime: time.Now().Add(48 * time.Hour),
|
||||
Quota: 100,
|
||||
Limit: 150,
|
||||
})
|
||||
require.Equal(t, http.StatusOK, createW.Code)
|
||||
var createResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(createW.Body.Bytes(), &createResp))
|
||||
eventId := createResp["data"].(map[string]any)["event_id"].(string)
|
||||
|
||||
w := deleteWithBearer(t, r, "/event/delete", token, map[string]any{"event_id": eventId})
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
// ---- Info (success) ----
|
||||
|
||||
func TestEventInfoHandlerSuccess(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
owner := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
user := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
ownerToken := issueToken(t, owner.UserId)
|
||||
userToken := issueToken(t, user.UserId)
|
||||
r := newEventRouter(t)
|
||||
|
||||
createW := postWithBearer(t, r, "/event/create", ownerToken, service_event.EventCreateData{
|
||||
Type: "party",
|
||||
Name: "Info Event",
|
||||
Subtitle: "sub",
|
||||
StartTime: time.Now().Add(24 * time.Hour),
|
||||
EndTime: time.Now().Add(48 * time.Hour),
|
||||
Quota: 100,
|
||||
Limit: 150,
|
||||
})
|
||||
require.Equal(t, http.StatusOK, createW.Code)
|
||||
var createResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(createW.Body.Bytes(), &createResp))
|
||||
eventId := createResp["data"].(map[string]any)["event_id"].(string)
|
||||
|
||||
w := getWithBearer(t, r, "/event/info?event_id="+eventId, userToken)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
d, ok := resp["data"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, eventId, d["event_id"])
|
||||
}
|
||||
|
||||
// ---- Join (success) ----
|
||||
|
||||
func TestEventJoinHandlerSuccess(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
owner := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
joiner := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
require.NoError(t, new(data.User).PatchByUserId(t.Context(), joiner.UserId, data.WithNickname("Joiner")))
|
||||
|
||||
ownerToken := issueToken(t, owner.UserId)
|
||||
joinerToken := issueToken(t, joiner.UserId)
|
||||
r := newEventRouter(t)
|
||||
|
||||
createW := postWithBearer(t, r, "/event/create", ownerToken, service_event.EventCreateData{
|
||||
Type: "party",
|
||||
Name: "Join Event",
|
||||
Subtitle: "sub",
|
||||
StartTime: time.Now().Add(24 * time.Hour),
|
||||
EndTime: time.Now().Add(48 * time.Hour),
|
||||
Quota: 100,
|
||||
Limit: 150,
|
||||
})
|
||||
require.Equal(t, http.StatusOK, createW.Code)
|
||||
var createResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(createW.Body.Bytes(), &createResp))
|
||||
eventId := createResp["data"].(map[string]any)["event_id"].(string)
|
||||
|
||||
w := postWithBearer(t, r, "/event/join", joinerToken, map[string]any{"event_id": eventId})
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
// ---- Update ----
|
||||
|
||||
func TestEventUpdateHandlerSuccess(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
owner := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
token := issueToken(t, owner.UserId)
|
||||
r := newEventRouter(t)
|
||||
|
||||
createW := postWithBearer(t, r, "/event/create", token, service_event.EventCreateData{
|
||||
Type: "party",
|
||||
Name: "Original Name",
|
||||
Subtitle: "sub",
|
||||
StartTime: time.Now().Add(24 * time.Hour),
|
||||
EndTime: time.Now().Add(48 * time.Hour),
|
||||
Quota: 100,
|
||||
Limit: 150,
|
||||
})
|
||||
require.Equal(t, http.StatusOK, createW.Code)
|
||||
var createResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(createW.Body.Bytes(), &createResp))
|
||||
eventId := createResp["data"].(map[string]any)["event_id"].(string)
|
||||
|
||||
newName := "Updated Name"
|
||||
w := patchWithBearer(t, r, "/event/update", token, map[string]any{
|
||||
"event_id": eventId,
|
||||
"name": newName,
|
||||
})
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
func TestEventUpdateHandlerInvalidJSON(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
user := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
token := issueToken(t, user.UserId)
|
||||
r := newEventRouter(t)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPatch, "/event/update", bytes.NewBufferString("{bad"))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
// ---- Stats ----
|
||||
|
||||
func TestEventStatsHandlerSuccess(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
owner := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
token := issueToken(t, owner.UserId)
|
||||
r := newEventRouter(t)
|
||||
|
||||
createW := postWithBearer(t, r, "/event/create", token, service_event.EventCreateData{
|
||||
Type: "party",
|
||||
Name: "Stats Event",
|
||||
Subtitle: "sub",
|
||||
StartTime: time.Now().Add(24 * time.Hour),
|
||||
EndTime: time.Now().Add(48 * time.Hour),
|
||||
Quota: 100,
|
||||
Limit: 150,
|
||||
})
|
||||
require.Equal(t, http.StatusOK, createW.Code)
|
||||
var createResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(createW.Body.Bytes(), &createResp))
|
||||
eventId := createResp["data"].(map[string]any)["event_id"].(string)
|
||||
|
||||
w := getWithBearer(t, r, "/event/stats?event_id="+eventId, token)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
d, ok := resp["data"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
assert.NotNil(t, d["join_count"])
|
||||
}
|
||||
|
||||
func TestEventStatsHandlerMissingEventId(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
user := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
token := issueToken(t, user.UserId)
|
||||
r := newEventRouter(t)
|
||||
|
||||
w := getWithBearer(t, r, "/event/stats?event_id=not-a-uuid", token)
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
// ---- Checkin ----
|
||||
|
||||
func TestEventCheckinHandlerInvalidEventId(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
user := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
token := issueToken(t, user.UserId)
|
||||
r := newEventRouter(t)
|
||||
|
||||
w := getWithBearer(t, r, "/event/checkin?event_id=not-a-uuid", token)
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func TestEventCheckinHandlerSuccess(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
owner := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
joiner := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
require.NoError(t, new(data.User).PatchByUserId(t.Context(), joiner.UserId, data.WithNickname("Joiner")))
|
||||
|
||||
ownerToken := issueToken(t, owner.UserId)
|
||||
joinerToken := issueToken(t, joiner.UserId)
|
||||
r := newEventRouter(t)
|
||||
|
||||
createW := postWithBearer(t, r, "/event/create", ownerToken, service_event.EventCreateData{
|
||||
Type: "party",
|
||||
Name: "Checkin Event",
|
||||
Subtitle: "sub",
|
||||
StartTime: time.Now().Add(24 * time.Hour),
|
||||
EndTime: time.Now().Add(48 * time.Hour),
|
||||
Quota: 100,
|
||||
Limit: 150,
|
||||
})
|
||||
require.Equal(t, http.StatusOK, createW.Code)
|
||||
var createResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(createW.Body.Bytes(), &createResp))
|
||||
eventId := createResp["data"].(map[string]any)["event_id"].(string)
|
||||
|
||||
joinW := postWithBearer(t, r, "/event/join", joinerToken, map[string]any{"event_id": eventId})
|
||||
require.Equal(t, http.StatusOK, joinW.Code)
|
||||
|
||||
w := getWithBearer(t, r, "/event/checkin?event_id="+eventId, joinerToken)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
d, ok := resp["data"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
assert.NotEmpty(t, d["checkin_code"])
|
||||
}
|
||||
|
||||
// ---- CheckinSubmit ----
|
||||
|
||||
func TestEventCheckinSubmitHandlerInvalidCode(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
user := testutil.SeedUser(t, testutil.RandomEmail(), 20)
|
||||
token := issueToken(t, user.UserId)
|
||||
r := newEventRouter(t)
|
||||
|
||||
w := postWithBearer(t, r, "/event/checkin/submit", token, service_event.CheckinSubmitData{
|
||||
CheckinCode: "000000",
|
||||
})
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func TestEventCheckinSubmitHandlerSuccess(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
owner := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
joiner := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
require.NoError(t, new(data.User).PatchByUserId(t.Context(), joiner.UserId, data.WithNickname("Joiner")))
|
||||
|
||||
ownerToken := issueToken(t, owner.UserId)
|
||||
joinerToken := issueToken(t, joiner.UserId)
|
||||
r := newEventRouter(t)
|
||||
|
||||
createW := postWithBearer(t, r, "/event/create", ownerToken, service_event.EventCreateData{
|
||||
Type: "party",
|
||||
Name: "Checkin Submit Event",
|
||||
Subtitle: "sub",
|
||||
StartTime: time.Now().Add(24 * time.Hour),
|
||||
EndTime: time.Now().Add(48 * time.Hour),
|
||||
Quota: 100,
|
||||
Limit: 150,
|
||||
})
|
||||
require.Equal(t, http.StatusOK, createW.Code)
|
||||
var createResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(createW.Body.Bytes(), &createResp))
|
||||
eventId := createResp["data"].(map[string]any)["event_id"].(string)
|
||||
|
||||
joinW := postWithBearer(t, r, "/event/join", joinerToken, map[string]any{"event_id": eventId})
|
||||
require.Equal(t, http.StatusOK, joinW.Code)
|
||||
|
||||
checkinW := getWithBearer(t, r, "/event/checkin?event_id="+eventId, joinerToken)
|
||||
require.Equal(t, http.StatusOK, checkinW.Code)
|
||||
var checkinResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(checkinW.Body.Bytes(), &checkinResp))
|
||||
code := checkinResp["data"].(map[string]any)["checkin_code"].(string)
|
||||
|
||||
w := postWithBearer(t, r, "/event/checkin/submit", ownerToken, service_event.CheckinSubmitData{
|
||||
CheckinCode: code,
|
||||
})
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
// ---- CheckinQuery ----
|
||||
|
||||
func TestEventCheckinQueryHandlerInvalidEventId(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
user := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
token := issueToken(t, user.UserId)
|
||||
r := newEventRouter(t)
|
||||
|
||||
w := getWithBearer(t, r, "/event/checkin/query?event_id=not-a-uuid", token)
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func TestEventCheckinQueryHandlerNotJoined(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
user := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
token := issueToken(t, user.UserId)
|
||||
r := newEventRouter(t)
|
||||
|
||||
w := getWithBearer(t, r, "/event/checkin/query?event_id="+uuid.New().String(), token)
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
}
|
||||
|
||||
// ---- Attendance ----
|
||||
|
||||
func TestEventAttendanceHandlerInvalidEventId(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
user := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
token := issueToken(t, user.UserId)
|
||||
r := newEventRouter(t)
|
||||
|
||||
w := getWithBearer(t, r, "/event/attendance?event_id=not-a-uuid", token)
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func TestEventAttendanceHandlerSuccess(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
owner := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
token := issueToken(t, owner.UserId)
|
||||
r := newEventRouter(t)
|
||||
|
||||
createW := postWithBearer(t, r, "/event/create", token, service_event.EventCreateData{
|
||||
Type: "party",
|
||||
Name: "Attendance Event",
|
||||
Subtitle: "sub",
|
||||
StartTime: time.Now().Add(24 * time.Hour),
|
||||
EndTime: time.Now().Add(48 * time.Hour),
|
||||
Quota: 100,
|
||||
Limit: 150,
|
||||
})
|
||||
require.Equal(t, http.StatusOK, createW.Code)
|
||||
var createResp map[string]any
|
||||
require.NoError(t, json.Unmarshal(createW.Body.Bytes(), &createResp))
|
||||
eventId := createResp["data"].(map[string]any)["event_id"].(string)
|
||||
|
||||
w := getWithBearer(t, r, "/event/attendance?event_id="+eventId, token)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
d, ok := resp["data"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
assert.NotNil(t, d["total"])
|
||||
}
|
||||
|
||||
// ---- Guide ----
|
||||
|
||||
func TestEventGuideHandlerInvalidEventId(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
user := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
token := issueToken(t, user.UserId)
|
||||
r := newEventRouter(t)
|
||||
|
||||
// The guide handler returns 500 for invalid UUID (source behaviour)
|
||||
w := getWithBearer(t, r, "/event/guide?event_id=not-a-uuid", token)
|
||||
assert.NotEqual(t, http.StatusNotFound, w.Code, "route must be registered")
|
||||
}
|
||||
|
||||
// ---- Permission enforcement ----
|
||||
|
||||
func TestEventCreateHandlerLowPermission(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
user := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
token := issueToken(t, user.UserId)
|
||||
r := newEventRouter(t)
|
||||
|
||||
w := postWithBearer(t, r, "/event/create", token, service_event.EventCreateData{
|
||||
Type: "party",
|
||||
Name: "Should Fail",
|
||||
Quota: 10,
|
||||
Limit: 20,
|
||||
})
|
||||
assert.Equal(t, http.StatusForbidden, w.Code)
|
||||
}
|
||||
|
||||
func TestEventDeleteHandlerLowPermission(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
user := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
token := issueToken(t, user.UserId)
|
||||
r := newEventRouter(t)
|
||||
|
||||
w := deleteWithBearer(t, r, "/event/delete", token, map[string]any{
|
||||
"event_id": uuid.New().String(),
|
||||
})
|
||||
assert.Equal(t, http.StatusForbidden, w.Code)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"nixcn-cms/data"
|
||||
"nixcn-cms/internal/authtoken"
|
||||
"nixcn-cms/testutil"
|
||||
)
|
||||
@@ -137,3 +138,90 @@ func TestUserListHandlerRequiresOffset(t *testing.T) {
|
||||
w := getWithBearer(t, r, "/user/list", token)
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func TestUserListHandlerSuccess(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
admin := testutil.SeedUser(t, testutil.RandomEmail(), 40)
|
||||
for i := 0; i < 3; i++ {
|
||||
testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
}
|
||||
token := issueToken(t, admin.UserId)
|
||||
r := newUserRouter(t)
|
||||
|
||||
w := getWithBearer(t, r, "/user/list?offset=0&limit=10", token)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
assert.NotNil(t, resp["data"])
|
||||
}
|
||||
|
||||
func TestUserListHandlerLowPermission(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
user := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
token := issueToken(t, user.UserId)
|
||||
r := newUserRouter(t)
|
||||
|
||||
w := getWithBearer(t, r, "/user/list?offset=0", token)
|
||||
assert.Equal(t, http.StatusForbidden, w.Code)
|
||||
}
|
||||
|
||||
// ---- Other (info by user_id) ----
|
||||
|
||||
func TestUserInfoHandlerOtherSuccess(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
caller := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
target := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
require.NoError(t, new(data.User).PatchByUserId(context.Background(), target.UserId, data.WithAllowPublic(true)))
|
||||
|
||||
token := issueToken(t, caller.UserId)
|
||||
r := newUserRouter(t)
|
||||
|
||||
w := getWithBearer(t, r, "/user/info/"+target.UserId.String(), token)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]any
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
d, ok := resp["data"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, target.Email, d["email"])
|
||||
}
|
||||
|
||||
// ---- AdminUpdate ----
|
||||
|
||||
func TestUserAdminUpdateHandlerSuccess(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
admin := testutil.SeedUser(t, testutil.RandomEmail(), 40)
|
||||
target := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
token := issueToken(t, admin.UserId)
|
||||
r := newUserRouter(t)
|
||||
|
||||
w := patchWithBearer(t, r, "/user/update/"+target.UserId.String(), token, map[string]any{
|
||||
"nickname": "Admin Set Nickname",
|
||||
})
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
func TestUserAdminUpdateHandlerInvalidUserId(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
admin := testutil.SeedUser(t, testutil.RandomEmail(), 40)
|
||||
token := issueToken(t, admin.UserId)
|
||||
r := newUserRouter(t)
|
||||
|
||||
w := patchWithBearer(t, r, "/user/update/not-a-uuid", token, map[string]any{
|
||||
"nickname": "Test",
|
||||
})
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func TestUserAdminUpdateHandlerLowPermission(t *testing.T) {
|
||||
testutil.SetupWithAuth(t)
|
||||
user := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
target := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
token := issueToken(t, user.UserId)
|
||||
r := newUserRouter(t)
|
||||
|
||||
w := patchWithBearer(t, r, "/user/update/"+target.UserId.String(), token, map[string]any{
|
||||
"nickname": "Hacked",
|
||||
})
|
||||
assert.Equal(t, http.StatusForbidden, w.Code)
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,6 @@ func Init() {
|
||||
|
||||
conf := &config{}
|
||||
if err := viper.Unmarshal(conf); err != nil {
|
||||
log.Fatalln("[Condig] Can't unmarshal config!")
|
||||
log.Fatalln("[Config] Can't unmarshal config!")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ type ttl struct {
|
||||
type kyc struct {
|
||||
AliAccessKeyId string `yaml:"ali_access_key_id"`
|
||||
AliAccessKeySecret string `yaml:"ali_access_key_secret"`
|
||||
PassportReaderEndpoint string `yaml:"passport_reader_endpoint"`
|
||||
PassportReaderPublicKey string `yaml:"passport_reader_public_key"`
|
||||
PassportReaderSecret string `yaml:"passport_reader_secret"`
|
||||
}
|
||||
|
||||
@@ -197,3 +197,37 @@ func TestAgendaDelete(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, got)
|
||||
}
|
||||
|
||||
func TestAgendaCountByEventId(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
evId, attId := seedAgendaFixture(t, ctx)
|
||||
|
||||
for _, status := range []string{"pending", "approved", "rejected"} {
|
||||
ag := data.NewAgenda(
|
||||
data.WithAttendanceId(attId),
|
||||
data.WithAgendaName(uuid.New().String()),
|
||||
data.WithAgendaDescription("desc"),
|
||||
data.WithAgendaStatus(status),
|
||||
)
|
||||
require.NoError(t, ag.Create(ctx))
|
||||
}
|
||||
|
||||
count, err := new(data.Agenda).CountByEventId(ctx, evId)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(3), count)
|
||||
}
|
||||
|
||||
func TestAgendaCountByEventIdEmpty(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
owner := uuid.New()
|
||||
ev := makeEvent(owner)
|
||||
require.NoError(t, ev.Create(ctx))
|
||||
|
||||
count, err := new(data.Agenda).CountByEventId(ctx, ev.EventId)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(0), count)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package data_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -163,3 +164,318 @@ func TestAttendanceVerifyCheckinCodeInvalid(t *testing.T) {
|
||||
err := new(data.Attendance).VerifyCheckinCode(ctx, "000000")
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestAttendanceGetByAttendanceId(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
eventId, userId := seedEventAndUser(t, ctx)
|
||||
a := seedAttendance(t, ctx, eventId, userId)
|
||||
|
||||
got, err := new(data.Attendance).GetAttendanceByAttendanceId(ctx, a.AttendanceId)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, got)
|
||||
assert.Equal(t, a.AttendanceId, got.AttendanceId)
|
||||
assert.Equal(t, eventId, got.EventId)
|
||||
assert.Equal(t, userId, got.UserId)
|
||||
}
|
||||
|
||||
func TestAttendanceGetByAttendanceIdNotFound(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
got, err := new(data.Attendance).GetAttendanceByAttendanceId(ctx, uuid.New())
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, got)
|
||||
}
|
||||
|
||||
func TestAttendanceGetUsersByEventID(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
owner := uuid.New()
|
||||
ev := makeEvent(owner)
|
||||
require.NoError(t, ev.Create(ctx))
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
u := data.NewUser(
|
||||
data.WithEmail(uuid.New().String()+"@test.com"),
|
||||
data.WithUsername(uuid.New().String()),
|
||||
data.WithPermissionLevel(10),
|
||||
)
|
||||
require.NoError(t, u.Create(ctx))
|
||||
seedAttendance(t, ctx, ev.EventId, u.UserId)
|
||||
}
|
||||
|
||||
users, err := new(data.Attendance).GetUsersByEventID(ctx, ev.EventId)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, users)
|
||||
assert.Len(t, *users, 3)
|
||||
}
|
||||
|
||||
func TestAttendanceGetEventsByUserID(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
owner := uuid.New()
|
||||
ev1 := makeEvent(owner)
|
||||
require.NoError(t, ev1.Create(ctx))
|
||||
ev2 := makeEvent(owner)
|
||||
require.NoError(t, ev2.Create(ctx))
|
||||
|
||||
u := data.NewUser(
|
||||
data.WithEmail(uuid.New().String()+"@test.com"),
|
||||
data.WithUsername(uuid.New().String()),
|
||||
data.WithPermissionLevel(10),
|
||||
)
|
||||
require.NoError(t, u.Create(ctx))
|
||||
seedAttendance(t, ctx, ev1.EventId, u.UserId)
|
||||
seedAttendance(t, ctx, ev2.EventId, u.UserId)
|
||||
|
||||
events, err := new(data.Attendance).GetEventsByUserID(ctx, u.UserId)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, events)
|
||||
assert.Len(t, *events, 2)
|
||||
}
|
||||
|
||||
func TestAttendanceGetAttendanceListByEventId(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
owner := uuid.New()
|
||||
ev := makeEvent(owner)
|
||||
require.NoError(t, ev.Create(ctx))
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
u := data.NewUser(
|
||||
data.WithEmail(uuid.New().String()+"@test.com"),
|
||||
data.WithUsername(uuid.New().String()),
|
||||
data.WithPermissionLevel(10),
|
||||
)
|
||||
require.NoError(t, u.Create(ctx))
|
||||
seedAttendance(t, ctx, ev.EventId, u.UserId)
|
||||
}
|
||||
|
||||
list, err := new(data.Attendance).GetAttendanceListByEventId(ctx, ev.EventId)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, list)
|
||||
assert.Len(t, *list, 4)
|
||||
}
|
||||
|
||||
func TestAttendanceGetCheckedInEventIDs(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
owner := uuid.New()
|
||||
ev1 := makeEvent(owner)
|
||||
require.NoError(t, ev1.Create(ctx))
|
||||
ev2 := makeEvent(owner)
|
||||
require.NoError(t, ev2.Create(ctx))
|
||||
|
||||
u := data.NewUser(
|
||||
data.WithEmail(uuid.New().String()+"@test.com"),
|
||||
data.WithUsername(uuid.New().String()),
|
||||
data.WithPermissionLevel(10),
|
||||
)
|
||||
require.NoError(t, u.Create(ctx))
|
||||
|
||||
a1 := seedAttendance(t, ctx, ev1.EventId, u.UserId)
|
||||
_, err := new(data.Attendance).PatchByAttendanceId(ctx, a1.AttendanceId, data.WithCheckinAt(time.Now()))
|
||||
require.NoError(t, err)
|
||||
|
||||
seedAttendance(t, ctx, ev2.EventId, u.UserId)
|
||||
|
||||
checkedIn, err := new(data.Attendance).GetCheckedInEventIDs(ctx, u.UserId, []uuid.UUID{ev1.EventId, ev2.EventId})
|
||||
require.NoError(t, err)
|
||||
assert.True(t, checkedIn[ev1.EventId])
|
||||
assert.False(t, checkedIn[ev2.EventId])
|
||||
}
|
||||
|
||||
func TestAttendanceCountCheckedInUsersByEventID(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
owner := uuid.New()
|
||||
ev := makeEvent(owner)
|
||||
require.NoError(t, ev.Create(ctx))
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
u := data.NewUser(
|
||||
data.WithEmail(uuid.New().String()+"@test.com"),
|
||||
data.WithUsername(uuid.New().String()),
|
||||
data.WithPermissionLevel(10),
|
||||
)
|
||||
require.NoError(t, u.Create(ctx))
|
||||
a := seedAttendance(t, ctx, ev.EventId, u.UserId)
|
||||
if i < 2 {
|
||||
_, err := new(data.Attendance).PatchByAttendanceId(ctx, a.AttendanceId, data.WithCheckinAt(time.Now()))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
count, err := new(data.Attendance).CountCheckedInUsersByEventID(ctx, ev.EventId)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(2), count)
|
||||
}
|
||||
|
||||
func TestAttendanceCountWithKycByEventID(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
owner := uuid.New()
|
||||
ev := makeEvent(owner)
|
||||
require.NoError(t, ev.Create(ctx))
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
u := data.NewUser(
|
||||
data.WithEmail(uuid.New().String()+"@test.com"),
|
||||
data.WithUsername(uuid.New().String()),
|
||||
data.WithPermissionLevel(10),
|
||||
)
|
||||
require.NoError(t, u.Create(ctx))
|
||||
kycId := uuid.Nil
|
||||
if i < 2 {
|
||||
kycId = uuid.New()
|
||||
}
|
||||
a := data.NewAttendance(
|
||||
data.WithEventId(ev.EventId),
|
||||
data.WithUserId(u.UserId),
|
||||
data.WithKycId(kycId),
|
||||
data.WithRole("attendee"),
|
||||
data.WithState("success"),
|
||||
)
|
||||
_, err := a.Create(ctx)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
count, err := new(data.Attendance).CountWithKycByEventID(ctx, ev.EventId)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(2), count)
|
||||
}
|
||||
|
||||
func TestAttendanceGetAttendanceListFiltered(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
owner := uuid.New()
|
||||
ev := makeEvent(owner)
|
||||
require.NoError(t, ev.Create(ctx))
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
u := data.NewUser(
|
||||
data.WithEmail(uuid.New().String()+"@test.com"),
|
||||
data.WithUsername(uuid.New().String()),
|
||||
data.WithNickname(fmt.Sprintf("FilterUser%d", i)),
|
||||
data.WithPermissionLevel(10),
|
||||
)
|
||||
require.NoError(t, u.Create(ctx))
|
||||
kycId := uuid.Nil
|
||||
if i < 3 {
|
||||
kycId = uuid.New()
|
||||
}
|
||||
a := data.NewAttendance(
|
||||
data.WithEventId(ev.EventId),
|
||||
data.WithUserId(u.UserId),
|
||||
data.WithKycId(kycId),
|
||||
data.WithRole("attendee"),
|
||||
data.WithState("success"),
|
||||
)
|
||||
_, err := a.Create(ctx)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
t.Run("no_filters_returns_all", func(t *testing.T) {
|
||||
list, total, err := new(data.Attendance).GetAttendanceListFiltered(ctx, data.AttendanceListFilter{
|
||||
EventId: ev.EventId,
|
||||
Limit: 20,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(5), total)
|
||||
assert.Len(t, *list, 5)
|
||||
})
|
||||
|
||||
t.Run("with_kyc_filter", func(t *testing.T) {
|
||||
list, total, err := new(data.Attendance).GetAttendanceListFiltered(ctx, data.AttendanceListFilter{
|
||||
EventId: ev.EventId,
|
||||
KycStatus: "with_kyc",
|
||||
Limit: 20,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(3), total)
|
||||
assert.Len(t, *list, 3)
|
||||
})
|
||||
|
||||
t.Run("without_kyc_filter", func(t *testing.T) {
|
||||
list, total, err := new(data.Attendance).GetAttendanceListFiltered(ctx, data.AttendanceListFilter{
|
||||
EventId: ev.EventId,
|
||||
KycStatus: "without_kyc",
|
||||
Limit: 20,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(2), total)
|
||||
assert.Len(t, *list, 2)
|
||||
})
|
||||
|
||||
t.Run("name_filter", func(t *testing.T) {
|
||||
list, total, err := new(data.Attendance).GetAttendanceListFiltered(ctx, data.AttendanceListFilter{
|
||||
EventId: ev.EventId,
|
||||
Name: "FilterUser1",
|
||||
Limit: 20,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(1), total)
|
||||
assert.Len(t, *list, 1)
|
||||
})
|
||||
|
||||
t.Run("pagination", func(t *testing.T) {
|
||||
list, total, err := new(data.Attendance).GetAttendanceListFiltered(ctx, data.AttendanceListFilter{
|
||||
EventId: ev.EventId,
|
||||
Limit: 2,
|
||||
Offset: 0,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(5), total)
|
||||
assert.Len(t, *list, 2)
|
||||
})
|
||||
|
||||
t.Run("sort_by_id_asc", func(t *testing.T) {
|
||||
list, _, err := new(data.Attendance).GetAttendanceListFiltered(ctx, data.AttendanceListFilter{
|
||||
EventId: ev.EventId,
|
||||
SortBy: "id",
|
||||
SortOrder: "asc",
|
||||
Limit: 20,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, list)
|
||||
items := *list
|
||||
for i := 1; i < len(items); i++ {
|
||||
assert.GreaterOrEqual(t, items[i].Id, items[i-1].Id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestAttendanceCheckinCodeExpiry(t *testing.T) {
|
||||
mr := testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
eventId, userId := seedEventAndUser(t, ctx)
|
||||
a := seedAttendance(t, ctx, eventId, userId)
|
||||
|
||||
code, err := a.GenCheckinCode(ctx, a.EventId)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, code)
|
||||
|
||||
mr.FastForward(10 * time.Minute)
|
||||
|
||||
err = new(data.Attendance).VerifyCheckinCode(ctx, *code)
|
||||
require.Error(t, err, "expired code should not be valid")
|
||||
}
|
||||
|
||||
func TestAttendanceGetCheckedInEventIDsEmpty(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
result, err := new(data.Attendance).GetCheckedInEventIDs(ctx, uuid.New(), []uuid.UUID{})
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, result)
|
||||
}
|
||||
|
||||
@@ -156,3 +156,50 @@ func TestEventFastList(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, *results, 3)
|
||||
}
|
||||
|
||||
func TestEventGetEventsByUserId(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
owner := uuid.New()
|
||||
ev1 := makeEvent(owner)
|
||||
require.NoError(t, ev1.Create(ctx))
|
||||
ev2 := makeEvent(owner)
|
||||
require.NoError(t, ev2.Create(ctx))
|
||||
ev3 := makeEvent(owner)
|
||||
require.NoError(t, ev3.Create(ctx))
|
||||
|
||||
u := data.NewUser(
|
||||
data.WithEmail(uuid.New().String()+"@test.com"),
|
||||
data.WithUsername(uuid.New().String()),
|
||||
data.WithPermissionLevel(10),
|
||||
)
|
||||
require.NoError(t, u.Create(ctx))
|
||||
|
||||
for _, evId := range []uuid.UUID{ev1.EventId, ev2.EventId} {
|
||||
a := data.NewAttendance(
|
||||
data.WithEventId(evId),
|
||||
data.WithUserId(u.UserId),
|
||||
data.WithKycId(uuid.Nil),
|
||||
data.WithRole("attendee"),
|
||||
data.WithState("success"),
|
||||
)
|
||||
_, err := a.Create(ctx)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
results, err := new(data.Event).GetEventsByUserId(ctx, u.UserId, 10, 0)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, results)
|
||||
assert.Len(t, *results, 2)
|
||||
}
|
||||
|
||||
func TestEventGetEventsByUserIdEmpty(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
results, err := new(data.Event).GetEventsByUserId(ctx, uuid.New(), 10, 0)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, results)
|
||||
assert.Empty(t, *results)
|
||||
}
|
||||
|
||||
@@ -96,3 +96,51 @@ func TestGlobalStatsEventJoinCheckinCounts(t *testing.T) {
|
||||
assert.Equal(t, int64(2), stat.JoinCount)
|
||||
assert.Equal(t, int64(1), stat.CheckinCount)
|
||||
}
|
||||
|
||||
func TestGlobalStatsEventJoinCheckinCountsEmpty(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
results, err := new(data.GlobalStats).EventJoinCheckinCounts(ctx)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, results)
|
||||
assert.Empty(t, *results)
|
||||
}
|
||||
|
||||
func TestGlobalStatsEventJoinCheckinMultipleEvents(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
owner := uuid.New()
|
||||
ev1 := makeEvent(owner)
|
||||
require.NoError(t, ev1.Create(ctx))
|
||||
ev2 := makeEvent(owner)
|
||||
require.NoError(t, ev2.Create(ctx))
|
||||
|
||||
for _, evId := range []uuid.UUID{ev1.EventId, ev2.EventId} {
|
||||
u := data.NewUser(
|
||||
data.WithEmail(uuid.New().String()+"@test.com"),
|
||||
data.WithUsername(uuid.New().String()),
|
||||
data.WithPermissionLevel(10),
|
||||
)
|
||||
require.NoError(t, u.Create(ctx))
|
||||
a := data.NewAttendance(
|
||||
data.WithEventId(evId),
|
||||
data.WithUserId(u.UserId),
|
||||
data.WithKycId(uuid.Nil),
|
||||
data.WithRole("attendee"),
|
||||
data.WithState("success"),
|
||||
)
|
||||
_, err := a.Create(ctx)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
results, err := new(data.GlobalStats).EventJoinCheckinCounts(ctx)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, results)
|
||||
assert.Len(t, *results, 2)
|
||||
for _, stat := range *results {
|
||||
assert.Equal(t, int64(1), stat.JoinCount)
|
||||
assert.Equal(t, int64(0), stat.CheckinCount)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,3 +101,35 @@ func TestBuilderErrorCodeLength(t *testing.T) {
|
||||
// Status(1) + Endpoint(3) + Service(3) + Type(1) + Original(5) = 13
|
||||
assert.Len(t, b.ErrorCode, 13)
|
||||
}
|
||||
|
||||
func TestErrorHandlerDoesNotPanic(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
testErr := errors.New("test error")
|
||||
|
||||
for _, status := range []string{StatusSuccess, StatusUser, StatusServer, StatusClient} {
|
||||
s := status
|
||||
assert.NotPanics(t, func() {
|
||||
ErrorHandler(ctx, s, "code12345678", testErr)
|
||||
}, "ErrorHandler must not panic for status %q", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorHandlerNilError(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
assert.NotPanics(t, func() {
|
||||
ErrorHandler(ctx, StatusUser, "code12345678", nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuilderNilThrow(t *testing.T) {
|
||||
b := New(
|
||||
WithStatus(StatusSuccess),
|
||||
WithType(TypeCommon),
|
||||
WithOriginal(CommonSuccess),
|
||||
)
|
||||
// Throw on empty context (no service/endpoint set) must not panic
|
||||
assert.NotPanics(t, func() {
|
||||
thrown := b.Throw(context.Background())
|
||||
assert.Equal(t, CommonSuccess, thrown.Original)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ const (
|
||||
)
|
||||
|
||||
func doPassportRequest(ctx context.Context, method, path string, body any, target any) error {
|
||||
baseURL := viper.GetString("kyc.passport_reader_endpoint")
|
||||
publicKey := viper.GetString("kyc.passport_reader_public_key")
|
||||
secret := viper.GetString("kyc.passport_reader_secret")
|
||||
baseURL := "https://passportreader.app/api/v1"
|
||||
|
||||
var bodyReader io.Reader
|
||||
if body != nil {
|
||||
|
||||
@@ -2,41 +2,190 @@ package kyc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// passport.go hardcodes baseURL = "https://passportreader.app/api/v1" with no
|
||||
// injection point, so we cannot intercept the HTTP calls in tests. These tests
|
||||
// verify that each function returns an error when the remote server is
|
||||
// unreachable (no valid credentials in the test environment).
|
||||
|
||||
func setupPassportViper(t *testing.T) {
|
||||
// startMockPassportServer spins up an httptest.Server backed by mux, points
|
||||
// viper's kyc.passport_reader_endpoint at it, and registers cleanup on t.
|
||||
func startMockPassportServer(t *testing.T, mux *http.ServeMux) *httptest.Server {
|
||||
t.Helper()
|
||||
viper.Set("kyc.passport_reader_public_key", "test-pub-key")
|
||||
viper.Set("kyc.passport_reader_secret", "test-secret")
|
||||
t.Cleanup(func() { viper.Reset() })
|
||||
srv := httptest.NewServer(mux)
|
||||
viper.Reset()
|
||||
viper.Set("kyc.passport_reader_endpoint", srv.URL)
|
||||
viper.Set("kyc.passport_reader_public_key", "pub")
|
||||
viper.Set("kyc.passport_reader_secret", "sec")
|
||||
t.Cleanup(func() {
|
||||
srv.Close()
|
||||
viper.Reset()
|
||||
})
|
||||
return srv
|
||||
}
|
||||
|
||||
func TestCreateSessionReturnsErrorWithoutRealServer(t *testing.T) {
|
||||
setupPassportViper(t)
|
||||
func writeJSON(w http.ResponseWriter, v any) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(v)
|
||||
}
|
||||
|
||||
// ---- CreateSession ----
|
||||
|
||||
func TestCreateSessionSuccess(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/session.create", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodPost, r.Method)
|
||||
writeJSON(w, PassportReaderSessionResponse{ID: 42, Token: "tok"})
|
||||
})
|
||||
startMockPassportServer(t, mux)
|
||||
|
||||
resp, err := CreateSession(context.Background())
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 42, resp.ID)
|
||||
assert.Equal(t, "tok", resp.Token)
|
||||
}
|
||||
|
||||
func TestCreateSessionAPIError(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/session.create", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
})
|
||||
startMockPassportServer(t, mux)
|
||||
|
||||
_, err := CreateSession(context.Background())
|
||||
assert.Error(t, err, "CreateSession must fail when the remote server is unreachable")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestGetSessionStateReturnsErrorWithoutRealServer(t *testing.T) {
|
||||
setupPassportViper(t)
|
||||
// ---- GetSessionState ----
|
||||
|
||||
_, err := GetSessionState(context.Background(), 99999)
|
||||
assert.Error(t, err, "GetSessionState must fail when the remote server is unreachable")
|
||||
func TestGetSessionStateSuccess(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/session.state", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodPost, r.Method)
|
||||
assert.Equal(t, "application/json", r.Header.Get("Content-Type"))
|
||||
var req PassportReaderGetSessionRequest
|
||||
require.NoError(t, json.NewDecoder(r.Body).Decode(&req))
|
||||
assert.Equal(t, 42, req.ID)
|
||||
writeJSON(w, PassportReaderStateResponse{State: StateApproved})
|
||||
})
|
||||
startMockPassportServer(t, mux)
|
||||
|
||||
state, err := GetSessionState(context.Background(), 42)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, StateApproved, state)
|
||||
}
|
||||
|
||||
func TestGetSessionDetailsReturnsErrorWithoutRealServer(t *testing.T) {
|
||||
setupPassportViper(t)
|
||||
func TestGetSessionStateEachKnownState(t *testing.T) {
|
||||
knownStates := []string{
|
||||
StateCreated, StateInitiated, StateCompleted,
|
||||
StateApproved, StateFailed, StateAborted, StateRejected,
|
||||
}
|
||||
|
||||
_, err := GetSessionDetails(context.Background(), 99999)
|
||||
assert.Error(t, err, "GetSessionDetails must fail when the remote server is unreachable")
|
||||
for _, s := range knownStates {
|
||||
s := s
|
||||
t.Run(s, func(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/session.state", func(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, PassportReaderStateResponse{State: s})
|
||||
})
|
||||
startMockPassportServer(t, mux)
|
||||
|
||||
got, err := GetSessionState(context.Background(), 1)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, s, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSessionStateAPIError(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/session.state", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "bad", http.StatusBadRequest)
|
||||
})
|
||||
startMockPassportServer(t, mux)
|
||||
|
||||
_, err := GetSessionState(context.Background(), 1)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// ---- GetSessionDetails ----
|
||||
|
||||
func TestGetSessionDetailsSuccess(t *testing.T) {
|
||||
want := &PassportReaderSessionDetailResponse{
|
||||
State: StateApproved,
|
||||
GivenNames: "John",
|
||||
Surname: "Doe",
|
||||
Nationality: "USA",
|
||||
DateOfBirth: "1990-01-01",
|
||||
DocumentType: "PASSPORT",
|
||||
DocumentNumber: "X12345678",
|
||||
ExpiryDate: "2030-01-01",
|
||||
}
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/session.get", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, http.MethodPost, r.Method)
|
||||
assert.Equal(t, "application/json", r.Header.Get("Content-Type"))
|
||||
var req PassportReaderGetSessionRequest
|
||||
require.NoError(t, json.NewDecoder(r.Body).Decode(&req))
|
||||
assert.Equal(t, 7, req.ID)
|
||||
writeJSON(w, want)
|
||||
})
|
||||
startMockPassportServer(t, mux)
|
||||
|
||||
got, err := GetSessionDetails(context.Background(), 7)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func TestGetSessionDetailsAPIError(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/session.get", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "not found", http.StatusNotFound)
|
||||
})
|
||||
startMockPassportServer(t, mux)
|
||||
|
||||
_, err := GetSessionDetails(context.Background(), 1)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// ---- HTTP mechanics ----
|
||||
|
||||
func TestPassportRequestSendsBasicAuth(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/session.create", func(w http.ResponseWriter, r *http.Request) {
|
||||
user, pass, ok := r.BasicAuth()
|
||||
assert.True(t, ok, "Authorization header must be present")
|
||||
assert.Equal(t, "pub", user)
|
||||
assert.Equal(t, "sec", pass)
|
||||
writeJSON(w, PassportReaderSessionResponse{})
|
||||
})
|
||||
startMockPassportServer(t, mux)
|
||||
|
||||
_, _ = CreateSession(context.Background())
|
||||
}
|
||||
|
||||
func TestPassportRequestSetsContentTypeForBody(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/session.state", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, "application/json", r.Header.Get("Content-Type"))
|
||||
writeJSON(w, PassportReaderStateResponse{State: StateCreated})
|
||||
})
|
||||
startMockPassportServer(t, mux)
|
||||
|
||||
_, _ = GetSessionState(context.Background(), 1)
|
||||
}
|
||||
|
||||
func TestPassportRequestNoContentTypeWithoutBody(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/session.create", func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Empty(t, r.Header.Get("Content-Type"), "no Content-Type when body is nil")
|
||||
writeJSON(w, PassportReaderSessionResponse{})
|
||||
})
|
||||
startMockPassportServer(t, mux)
|
||||
|
||||
_, _ = CreateSession(context.Background())
|
||||
}
|
||||
|
||||
2
justfile
2
justfile
@@ -23,7 +23,7 @@ run:
|
||||
cd {{ output_dir }} && CONFIG_PATH={{ output_dir }} {{ server_exec_path }}{{ if os() == "windows" { ".exe" } else { "" } }}
|
||||
|
||||
test:
|
||||
cd {{ output_dir }} && CONFIG_PATH={{ output_dir }} GO_ENV=test go test -C .. ./...
|
||||
cd {{ output_dir }} && CONFIG_PATH={{ output_dir }} GO_ENV=test go test -C .. -p 4 ./...
|
||||
|
||||
watch:
|
||||
watchexec -r -e go,yaml,tpl -i '.devenv/**' -i '.direnv/**' -i 'client/**' -i 'vendor/**' 'go build -o {{ server_exec_path }} . && cd {{ output_dir }} && CONFIG_PATH={{ output_dir }} {{ server_exec_path }}'
|
||||
|
||||
@@ -225,3 +225,60 @@ func TestPermissionFromDBInsufficient(t *testing.T) {
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, 403, w.Code)
|
||||
}
|
||||
|
||||
func TestPermissionUserNotFound(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
|
||||
r := gin.New()
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("user_id", uuid.New().String())
|
||||
c.Next()
|
||||
})
|
||||
r.Use(Permission(0))
|
||||
r.GET("/any", func(c *gin.Context) { c.String(200, "ok") })
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/any", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, 404, w.Code)
|
||||
}
|
||||
|
||||
func TestPermissionCachesLevelFromDB(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
user := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
|
||||
calls := 0
|
||||
r := gin.New()
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("user_id", user.UserId.String())
|
||||
calls++
|
||||
c.Next()
|
||||
})
|
||||
r.Use(Permission(5))
|
||||
r.GET("/any", func(c *gin.Context) {
|
||||
lvl, ok := c.Get("permission_level")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, uint(10), lvl.(uint))
|
||||
c.String(200, "ok")
|
||||
})
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/any", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, 200, w.Code)
|
||||
}
|
||||
|
||||
func TestJWTAuthBadToken(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
testutil.SeedClient(t)
|
||||
|
||||
r := gin.New()
|
||||
r.Use(JWTAuth())
|
||||
r.GET("/protected", func(c *gin.Context) { c.String(200, "ok") })
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/protected", nil)
|
||||
req.Header.Set("Authorization", "Bearer completely.invalid.token")
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, 401, w.Code)
|
||||
}
|
||||
|
||||
@@ -112,7 +112,8 @@ func TestServerHealthEndpoints(t *testing.T) {
|
||||
|
||||
// Response must be valid JSON.
|
||||
var body map[string]any
|
||||
_ = json.Unmarshal(w.Body.Bytes(), &body)
|
||||
err := json.Unmarshal(w.Body.Bytes(), &body)
|
||||
assert.NoError(t, err, "response must be valid JSON for %s %s", ep.method, ep.path)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,6 +236,40 @@ func TestAgendaReviewReject(t *testing.T) {
|
||||
assert.Equal(t, 200, result.Common.HttpCode)
|
||||
}
|
||||
|
||||
func TestAgendaReviewInvalidStatus(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
event, user, _ := seedAgendaTestFixture(t, ctx)
|
||||
svc := newAgendaSvc()
|
||||
|
||||
submitResult := svc.Submit(&SubmitPayload{
|
||||
Context: ctx,
|
||||
UserId: user.UserId,
|
||||
Data: &SubmitData{EventId: event.EventId, Name: "Talk"},
|
||||
})
|
||||
require.Equal(t, 200, submitResult.Common.HttpCode)
|
||||
|
||||
manager := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
|
||||
result := svc.Review(&AgendaReviewPayload{
|
||||
Context: ctx,
|
||||
UserId: manager.UserId,
|
||||
Data: &AgendaReviewData{
|
||||
EventId: event.EventId,
|
||||
AgendaId: submitResult.Data.AgendaId,
|
||||
Status: "banana",
|
||||
},
|
||||
})
|
||||
|
||||
assert.Equal(t, 400, result.Common.HttpCode)
|
||||
|
||||
// Verify the agenda status was not mutated in the database.
|
||||
ag, err := new(data.Agenda).GetByAgendaId(ctx, submitResult.Data.AgendaId)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "pending", ag.Status)
|
||||
}
|
||||
|
||||
// ---- List ----
|
||||
|
||||
func TestAgendaList(t *testing.T) {
|
||||
@@ -313,6 +347,215 @@ func TestAgendaScheduleGetNotPublished(t *testing.T) {
|
||||
assert.Equal(t, exception.AgendaScheduleGetNotPublished, result.Common.Exception.Original)
|
||||
}
|
||||
|
||||
// ---- Update ----
|
||||
|
||||
func TestAgendaUpdateSubmitterSuccess(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
event, user, _ := seedAgendaTestFixture(t, ctx)
|
||||
svc := newAgendaSvc()
|
||||
|
||||
submitResult := svc.Submit(&SubmitPayload{
|
||||
Context: ctx,
|
||||
UserId: user.UserId,
|
||||
Data: &SubmitData{EventId: event.EventId, Name: "Original Name"},
|
||||
})
|
||||
require.Equal(t, 200, submitResult.Common.HttpCode)
|
||||
|
||||
newName := "Updated Name"
|
||||
result := svc.Update(&AgendaUpdatePayload{
|
||||
Context: ctx,
|
||||
UserId: user.UserId,
|
||||
Data: &AgendaUpdateData{
|
||||
AgendaId: submitResult.Data.AgendaId,
|
||||
Name: &newName,
|
||||
PermissionLevel: 10,
|
||||
},
|
||||
})
|
||||
|
||||
assert.Equal(t, 200, result.Common.HttpCode)
|
||||
|
||||
ag, err := new(data.Agenda).GetByAgendaId(ctx, submitResult.Data.AgendaId)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Updated Name", ag.Name)
|
||||
}
|
||||
|
||||
func TestAgendaUpdateManagerCanEdit(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
event, user, _ := seedAgendaTestFixture(t, ctx)
|
||||
svc := newAgendaSvc()
|
||||
|
||||
submitResult := svc.Submit(&SubmitPayload{
|
||||
Context: ctx,
|
||||
UserId: user.UserId,
|
||||
Data: &SubmitData{EventId: event.EventId, Name: "Original Name"},
|
||||
})
|
||||
require.Equal(t, 200, submitResult.Common.HttpCode)
|
||||
|
||||
manager := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
newName := "Manager Updated"
|
||||
result := svc.Update(&AgendaUpdatePayload{
|
||||
Context: ctx,
|
||||
UserId: manager.UserId,
|
||||
Data: &AgendaUpdateData{
|
||||
AgendaId: submitResult.Data.AgendaId,
|
||||
Name: &newName,
|
||||
PermissionLevel: 30,
|
||||
},
|
||||
})
|
||||
|
||||
assert.Equal(t, 200, result.Common.HttpCode)
|
||||
|
||||
ag, err := new(data.Agenda).GetByAgendaId(ctx, submitResult.Data.AgendaId)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Manager Updated", ag.Name)
|
||||
}
|
||||
|
||||
func TestAgendaUpdateNotSubmitter(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
event, user, _ := seedAgendaTestFixture(t, ctx)
|
||||
svc := newAgendaSvc()
|
||||
|
||||
submitResult := svc.Submit(&SubmitPayload{
|
||||
Context: ctx,
|
||||
UserId: user.UserId,
|
||||
Data: &SubmitData{EventId: event.EventId, Name: "My Talk"},
|
||||
})
|
||||
require.Equal(t, 200, submitResult.Common.HttpCode)
|
||||
|
||||
other := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
newName := "Hijacked"
|
||||
result := svc.Update(&AgendaUpdatePayload{
|
||||
Context: ctx,
|
||||
UserId: other.UserId,
|
||||
Data: &AgendaUpdateData{
|
||||
AgendaId: submitResult.Data.AgendaId,
|
||||
Name: &newName,
|
||||
PermissionLevel: 10,
|
||||
},
|
||||
})
|
||||
|
||||
assert.Equal(t, 403, result.Common.HttpCode)
|
||||
assert.Equal(t, exception.AgendaUpdateNotSubmitter, result.Common.Exception.Original)
|
||||
}
|
||||
|
||||
func TestAgendaUpdateNotPending(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
event, user, _ := seedAgendaTestFixture(t, ctx)
|
||||
svc := newAgendaSvc()
|
||||
|
||||
submitResult := svc.Submit(&SubmitPayload{
|
||||
Context: ctx,
|
||||
UserId: user.UserId,
|
||||
Data: &SubmitData{EventId: event.EventId, Name: "My Talk"},
|
||||
})
|
||||
require.Equal(t, 200, submitResult.Common.HttpCode)
|
||||
|
||||
manager := testutil.SeedUser(t, testutil.RandomEmail(), 30)
|
||||
svc.Review(&AgendaReviewPayload{
|
||||
Context: ctx,
|
||||
UserId: manager.UserId,
|
||||
Data: &AgendaReviewData{
|
||||
EventId: event.EventId,
|
||||
AgendaId: submitResult.Data.AgendaId,
|
||||
Status: "approved",
|
||||
},
|
||||
})
|
||||
|
||||
newName := "Try to Edit"
|
||||
result := svc.Update(&AgendaUpdatePayload{
|
||||
Context: ctx,
|
||||
UserId: user.UserId,
|
||||
Data: &AgendaUpdateData{
|
||||
AgendaId: submitResult.Data.AgendaId,
|
||||
Name: &newName,
|
||||
PermissionLevel: 10,
|
||||
},
|
||||
})
|
||||
|
||||
assert.Equal(t, 400, result.Common.HttpCode)
|
||||
assert.Equal(t, exception.AgendaUpdateNotPending, result.Common.Exception.Original)
|
||||
}
|
||||
|
||||
func TestAgendaUpdateDeadlinePassed(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
user := testutil.SeedUser(t, testutil.RandomEmail(), 10)
|
||||
require.NoError(t, new(data.User).PatchByUserId(ctx, user.UserId, data.WithNickname("Attendee")))
|
||||
|
||||
owner := testutil.SeedUser(t, testutil.RandomEmail(), 40)
|
||||
event := data.NewEvent(
|
||||
data.WithOwner(owner.UserId),
|
||||
data.WithEventType("party"),
|
||||
data.WithEventName("Past Event"),
|
||||
data.WithEventSubtitle("sub"),
|
||||
data.WithEventStartTime(time.Now().Add(-2*time.Hour)),
|
||||
data.WithEventEndTime(time.Now().Add(-time.Hour)),
|
||||
data.WithQuota(100),
|
||||
data.WithLimit(150),
|
||||
)
|
||||
require.NoError(t, event.Create(ctx))
|
||||
|
||||
att := data.NewAttendance(
|
||||
data.WithEventId(event.EventId),
|
||||
data.WithUserId(user.UserId),
|
||||
data.WithKycId(uuid.Nil),
|
||||
data.WithRole("attendee"),
|
||||
data.WithState("success"),
|
||||
)
|
||||
_, err := att.Create(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
ag := data.NewAgenda(
|
||||
data.WithAttendanceId(att.AttendanceId),
|
||||
data.WithAgendaName("Past Talk"),
|
||||
data.WithAgendaDescription("desc"),
|
||||
data.WithAgendaStatus("pending"),
|
||||
)
|
||||
require.NoError(t, ag.Create(ctx))
|
||||
|
||||
newName := "Updated Past"
|
||||
svc := newAgendaSvc()
|
||||
result := svc.Update(&AgendaUpdatePayload{
|
||||
Context: ctx,
|
||||
UserId: user.UserId,
|
||||
Data: &AgendaUpdateData{
|
||||
AgendaId: ag.AgendaId,
|
||||
Name: &newName,
|
||||
PermissionLevel: 10,
|
||||
},
|
||||
})
|
||||
|
||||
assert.Equal(t, 400, result.Common.HttpCode)
|
||||
assert.Equal(t, exception.AgendaUpdateDeadlinePassed, result.Common.Exception.Original)
|
||||
}
|
||||
|
||||
func TestAgendaUpdateNotFound(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
|
||||
svc := newAgendaSvc()
|
||||
newName := "Phantom"
|
||||
result := svc.Update(&AgendaUpdatePayload{
|
||||
Context: context.Background(),
|
||||
UserId: uuid.New(),
|
||||
Data: &AgendaUpdateData{
|
||||
AgendaId: uuid.New(),
|
||||
Name: &newName,
|
||||
PermissionLevel: 10,
|
||||
},
|
||||
})
|
||||
|
||||
assert.Equal(t, 404, result.Common.HttpCode)
|
||||
}
|
||||
|
||||
func TestAgendaScheduleGetPublished(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -109,6 +109,20 @@ func (self *AgendaServiceImpl) Review(payload *AgendaReviewPayload) (result *Age
|
||||
return
|
||||
}
|
||||
|
||||
if payload.Data.Status != "approved" && payload.Data.Status != "rejected" {
|
||||
exc := exception.New(
|
||||
exception.WithStatus(exception.StatusUser),
|
||||
exception.WithType(exception.TypeCommon),
|
||||
exception.WithOriginal(exception.CommonErrorInvalidInput),
|
||||
exception.WithError(errors.New("status must be approved or rejected")),
|
||||
).Throw(ctx)
|
||||
|
||||
result = &AgendaReviewResult{
|
||||
Common: shared.CommonResult{HttpCode: 400, Exception: exc},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err := new(data.Agenda).PatchByAgendaId(ctx, payload.Data.AgendaId,
|
||||
data.WithAgendaStatus(payload.Data.Status),
|
||||
); err != nil {
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"nixcn-cms/testutil"
|
||||
)
|
||||
|
||||
|
||||
func newEventSvc() EventService { return NewEventService() }
|
||||
|
||||
func makeOwner(t *testing.T, permLevel uint) *data.User {
|
||||
@@ -431,3 +432,311 @@ func TestEventStatsNotOwner(t *testing.T) {
|
||||
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, 40)
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -3,9 +3,14 @@ package service_kyc
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"nixcn-cms/testutil"
|
||||
)
|
||||
|
||||
@@ -87,14 +92,97 @@ func TestSessionKycInvalidType(t *testing.T) {
|
||||
func TestQueryKycExternalAPIError(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
|
||||
// PassportReaderSessionId is 0 (zero value), calling GetSessionState will
|
||||
// fail with a network/API error since there is no real PassportReader server.
|
||||
// kyc.passport_reader_endpoint is unset, so GetSessionState will fail.
|
||||
svc := newKycSvc()
|
||||
result := svc.QueryKyc(&KycQueryPayload{
|
||||
Context: context.Background(),
|
||||
Data: &KycQueryData{KycId: "00000000-0000-0000-0000-000000000001"},
|
||||
})
|
||||
|
||||
// External API is unavailable in test → expect a 400 error response
|
||||
assert.Equal(t, 400, result.Common.HttpCode)
|
||||
}
|
||||
|
||||
func TestQueryKycUnknownSessionState(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
|
||||
// Start a mock server that returns a state string matching no known branch.
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/session.state", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]string{"state": "UNKNOWN_STATE_XYZ"})
|
||||
})
|
||||
srv := httptest.NewServer(mux)
|
||||
t.Cleanup(srv.Close)
|
||||
viper.Set("kyc.passport_reader_endpoint", srv.URL)
|
||||
|
||||
svc := &KycServiceImpl{PassportReaderSessionId: 1}
|
||||
result := svc.QueryKyc(&KycQueryPayload{
|
||||
Context: context.Background(),
|
||||
Data: &KycQueryData{KycId: "00000000-0000-0000-0000-000000000001"},
|
||||
})
|
||||
|
||||
require.NotNil(t, result, "result must never be nil")
|
||||
assert.Equal(t, 500, result.Common.HttpCode)
|
||||
}
|
||||
|
||||
func TestQueryKycPendingStates(t *testing.T) {
|
||||
pendingStates := []string{"CREATED", "INITIATED", "COMPLETED"}
|
||||
|
||||
for _, s := range pendingStates {
|
||||
s := s
|
||||
t.Run(s, func(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/session.state", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]string{"state": s})
|
||||
})
|
||||
srv := httptest.NewServer(mux)
|
||||
t.Cleanup(srv.Close)
|
||||
viper.Set("kyc.passport_reader_endpoint", srv.URL)
|
||||
|
||||
svc := &KycServiceImpl{PassportReaderSessionId: 1}
|
||||
result := svc.QueryKyc(&KycQueryPayload{
|
||||
Context: context.Background(),
|
||||
Data: &KycQueryData{KycId: "00000000-0000-0000-0000-000000000001"},
|
||||
})
|
||||
|
||||
require.NotNil(t, result)
|
||||
assert.Equal(t, 200, result.Common.HttpCode)
|
||||
require.NotNil(t, result.Data)
|
||||
assert.Equal(t, "pending", result.Data.Status)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryKycFailedStates(t *testing.T) {
|
||||
failedStates := []string{"FAILED", "ABORTED", "REJECTED"}
|
||||
|
||||
for _, s := range failedStates {
|
||||
s := s
|
||||
t.Run(s, func(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/session.state", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]string{"state": s})
|
||||
})
|
||||
srv := httptest.NewServer(mux)
|
||||
t.Cleanup(srv.Close)
|
||||
viper.Set("kyc.passport_reader_endpoint", srv.URL)
|
||||
|
||||
svc := &KycServiceImpl{PassportReaderSessionId: 1}
|
||||
result := svc.QueryKyc(&KycQueryPayload{
|
||||
Context: context.Background(),
|
||||
Data: &KycQueryData{KycId: "00000000-0000-0000-0000-000000000001"},
|
||||
})
|
||||
|
||||
require.NotNil(t, result)
|
||||
assert.Equal(t, 200, result.Common.HttpCode)
|
||||
require.NotNil(t, result.Data)
|
||||
assert.Equal(t, "failed", result.Data.Status)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package service_kyc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"nixcn-cms/data"
|
||||
"nixcn-cms/internal/exception"
|
||||
"nixcn-cms/internal/kyc"
|
||||
@@ -229,5 +230,18 @@ func (self *KycServiceImpl) QueryKyc(payload *KycQueryPayload) (result *KycQuery
|
||||
return
|
||||
}
|
||||
|
||||
exc := exception.New(
|
||||
exception.WithStatus(exception.StatusServer),
|
||||
exception.WithType(exception.TypeCommon),
|
||||
exception.WithOriginal(exception.CommonErrorInvalidInput),
|
||||
exception.WithError(errors.New("unknown session state: "+string(sessionState))),
|
||||
).Throw(ctx)
|
||||
|
||||
result = &KycQueryResult{
|
||||
Common: shared.CommonResult{
|
||||
HttpCode: 500,
|
||||
Exception: exc,
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -49,3 +49,16 @@ func TestGlobalStatsWithData(t *testing.T) {
|
||||
assert.Equal(t, int64(2), countMap[10])
|
||||
assert.Equal(t, int64(1), countMap[30])
|
||||
}
|
||||
|
||||
func TestGlobalStatsEventJoinCheckinField(t *testing.T) {
|
||||
testutil.Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
svc := NewStatsService()
|
||||
result := svc.Global(&GlobalStatsPayload{Context: ctx})
|
||||
|
||||
assert.Equal(t, 200, result.Common.HttpCode)
|
||||
require.NotNil(t, result.Data)
|
||||
require.NotNil(t, result.Data.EventJoinCheckin, "EventJoinCheckin field must not be nil")
|
||||
assert.Empty(t, *result.Data.EventJoinCheckin)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user