107 lines
3.4 KiB
Go
107 lines
3.4 KiB
Go
package appuser
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"hyapp-admin-server/internal/appctx"
|
|
"hyapp-admin-server/internal/config"
|
|
"hyapp-admin-server/internal/model"
|
|
"hyapp-admin-server/internal/modules/shared"
|
|
)
|
|
|
|
type recordingAppUserExportStore struct {
|
|
job *model.AdminJob
|
|
}
|
|
|
|
func (s *recordingAppUserExportStore) CreateJob(job *model.AdminJob) error {
|
|
copy := *job
|
|
copy.ID = 81
|
|
job.ID = copy.ID
|
|
s.job = ©
|
|
return nil
|
|
}
|
|
|
|
func TestCreateExportJobPersistsFilterSnapshot(t *testing.T) {
|
|
store := &recordingAppUserExportStore{}
|
|
service := &Service{
|
|
jobStore: store,
|
|
jobsConfig: config.JobsConfig{
|
|
Enabled: true,
|
|
MaxAttempts: 5,
|
|
},
|
|
}
|
|
ctx := appctx.WithContext(context.Background(), "lalu")
|
|
response, err := service.CreateExportJob(ctx, shared.Actor{UserID: 7, Username: "operator"}, listQuery{
|
|
Country: " ph ",
|
|
Status: "active",
|
|
Keyword: "1299324",
|
|
SortBy: "coin",
|
|
SortDirection: "asc",
|
|
Page: 9,
|
|
PageSize: 10,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("create export job: %v", err)
|
|
}
|
|
if response.JobID != 81 || response.SnapshotAtMs <= 0 || store.job == nil {
|
|
t.Fatalf("response/job mismatch: response=%+v job=%+v", response, store.job)
|
|
}
|
|
if store.job.Type != AppUserExportJobType || store.job.MaxAttempts != 5 || store.job.CreatedBy != 7 {
|
|
t.Fatalf("job metadata mismatch: %+v", store.job)
|
|
}
|
|
var payload appUserExportPayload
|
|
if err := json.Unmarshal([]byte(store.job.PayloadJSON), &payload); err != nil {
|
|
t.Fatalf("decode payload: %v", err)
|
|
}
|
|
if payload.AppCode != "lalu" || payload.Query.Country != "PH" || payload.Query.Status != "active" || payload.Query.Keyword != "1299324" {
|
|
t.Fatalf("filter snapshot mismatch: %+v", payload)
|
|
}
|
|
if payload.Query.Page != 1 || payload.Query.PageSize != 100 || payload.Query.SnapshotAtMs != response.SnapshotAtMs {
|
|
t.Fatalf("pagination snapshot mismatch: %+v", payload.Query)
|
|
}
|
|
if payload.Query.SortBy != "created_at" || payload.Query.SortDirection != "desc" {
|
|
t.Fatalf("export must use stable database pagination instead of coin projection sort: %+v", payload.Query)
|
|
}
|
|
}
|
|
|
|
func TestStableAppUserExportQueryForcesDescendingKeysetOrder(t *testing.T) {
|
|
for _, query := range []listQuery{
|
|
{SortBy: "created_at", SortDirection: "asc"},
|
|
{SortBy: "coin", SortDirection: "asc"},
|
|
} {
|
|
stable := stableAppUserExportQuery(normalizeListQuery(query))
|
|
if stable.SortBy != "created_at" || stable.SortDirection != "desc" {
|
|
t.Fatalf("export order must match descending keyset: input=%+v stable=%+v", query, stable)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAppUserExportWhereUsesSnapshotAndKeysetCursor(t *testing.T) {
|
|
query := normalizeListQuery(listQuery{
|
|
SnapshotAtMs: 1_000,
|
|
ExportCursorCreatedAtMs: 900,
|
|
ExportCursorUserID: 88,
|
|
})
|
|
whereSQL, args := appUserListWhereSQLAt("lalu", query, query.SnapshotAtMs)
|
|
for _, fragment := range []string{
|
|
"u.created_at_ms <= ?",
|
|
"u.created_at_ms < ? OR (u.created_at_ms = ? AND u.user_id < ?)",
|
|
} {
|
|
if !strings.Contains(whereSQL, fragment) {
|
|
t.Fatalf("export where SQL missing %q: %s", fragment, whereSQL)
|
|
}
|
|
}
|
|
wantTail := []any{int64(1_000), int64(900), int64(900), int64(88)}
|
|
if len(args) < len(wantTail) {
|
|
t.Fatalf("export args too short: %+v", args)
|
|
}
|
|
for index, want := range wantTail {
|
|
if got := args[len(args)-len(wantTail)+index]; got != want {
|
|
t.Fatalf("export cursor arg[%d]=%v want=%v all=%+v", index, got, want, args)
|
|
}
|
|
}
|
|
}
|