138 lines
6.0 KiB
Go
138 lines
6.0 KiB
Go
package user_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
|
|
"hyapp/pkg/appcode"
|
|
hostdomain "hyapp/services/user-service/internal/domain/host"
|
|
userdomain "hyapp/services/user-service/internal/domain/user"
|
|
userservice "hyapp/services/user-service/internal/service/user"
|
|
"hyapp/services/user-service/internal/testutil/mysqltest"
|
|
)
|
|
|
|
func TestAdminChangeUserCountryOverridesProtectedRoleAndWritesOutbox(t *testing.T) {
|
|
ctx := appcode.WithContext(context.Background(), appcode.Default)
|
|
repository := mysqltest.NewRepository(t)
|
|
repository.PutCountry(userdomain.Country{CountryCode: "US", Enabled: true})
|
|
repository.PutCountry(userdomain.Country{CountryCode: "TH", Enabled: true})
|
|
repository.PutRegion(userdomain.Region{RegionID: 101, RegionCode: "US-REGION", Name: "US Region", Countries: []string{"US"}})
|
|
repository.PutRegion(userdomain.Region{RegionID: 202, RegionCode: "TH-REGION", Name: "TH Region", Countries: []string{"TH"}})
|
|
repository.PutUser(userdomain.User{
|
|
UserID: 91001,
|
|
DefaultDisplayUserID: "91001",
|
|
CurrentDisplayUserID: "91001",
|
|
Country: "US",
|
|
RegionID: 101,
|
|
ProfileCompleted: true,
|
|
ProfileCompletedAtMs: 1,
|
|
OnboardingStatus: userdomain.OnboardingStatusCompleted,
|
|
Status: userdomain.StatusActive,
|
|
})
|
|
repository.PutHostProfile(hostdomain.HostProfile{UserID: 91001, RegionID: 101, Status: hostdomain.HostStatusActive})
|
|
if _, err := repository.RawDB().ExecContext(ctx, `
|
|
INSERT INTO auth_sessions (
|
|
app_code, session_id, user_id, refresh_token_hash, device_id, expires_at_ms,
|
|
revoked_at_ms, revoked_reason, revoked_request_id, revoked_by, created_at_ms, updated_at_ms
|
|
)
|
|
VALUES (?, ?, ?, ?, ?, ?, NULL, '', '', '', ?, ?)
|
|
`, appcode.Default, "sess_country_admin", int64(91001), "refresh_hash_country_admin", "device_country_admin", int64(1_700_100_000_000), int64(1), int64(1)); err != nil {
|
|
t.Fatalf("seed auth session failed: %v", err)
|
|
}
|
|
svc := userservice.New(repository,
|
|
userservice.WithCountryRegionRepository(repository),
|
|
userservice.WithClock(func() time.Time { return time.UnixMilli(1_700_000_123_000).UTC() }),
|
|
)
|
|
|
|
updated, nextAllowedAt, err := svc.AdminChangeUserCountry(ctx, 91001, "TH", "req-admin-country")
|
|
if err != nil {
|
|
t.Fatalf("AdminChangeUserCountry failed: %v", err)
|
|
}
|
|
if nextAllowedAt != 0 || updated.Country != "TH" || updated.RegionID != 202 {
|
|
t.Fatalf("admin country result mismatch: user=%+v next=%d", updated, nextAllowedAt)
|
|
}
|
|
|
|
var payloadJSON string
|
|
if err := repository.RawDB().QueryRowContext(ctx, `
|
|
SELECT CAST(payload_json AS CHAR)
|
|
FROM user_outbox
|
|
WHERE app_code = ? AND event_type = 'UserRegionChanged' AND aggregate_id = ?
|
|
`, appcode.Default, int64(91001)).Scan(&payloadJSON); err != nil {
|
|
t.Fatalf("query UserRegionChanged outbox failed: %v", err)
|
|
}
|
|
var payload map[string]any
|
|
if err := json.Unmarshal([]byte(payloadJSON), &payload); err != nil {
|
|
t.Fatalf("decode outbox payload failed: %v", err)
|
|
}
|
|
if payload["source"] != "admin" || int64(payload["old_region_id"].(float64)) != 101 || int64(payload["new_region_id"].(float64)) != 202 {
|
|
t.Fatalf("outbox payload mismatch: %v", payload)
|
|
}
|
|
var revokedAtMs int64
|
|
var revokedReason string
|
|
var revokedRequestID string
|
|
var revokedBy string
|
|
if err := repository.RawDB().QueryRowContext(ctx, `
|
|
SELECT revoked_at_ms, revoked_reason, revoked_request_id, revoked_by
|
|
FROM auth_sessions
|
|
WHERE app_code = ? AND session_id = ?
|
|
`, appcode.Default, "sess_country_admin").Scan(&revokedAtMs, &revokedReason, &revokedRequestID, &revokedBy); err != nil {
|
|
t.Fatalf("query revoked auth session failed: %v", err)
|
|
}
|
|
if revokedAtMs != 1_700_000_123_000 || revokedReason != "USER_COUNTRY_CHANGED" || revokedRequestID != "req-admin-country" || revokedBy != "country_change" {
|
|
t.Fatalf("country change session revoke mismatch: at=%d reason=%q request=%q by=%q", revokedAtMs, revokedReason, revokedRequestID, revokedBy)
|
|
}
|
|
}
|
|
|
|
func TestAdminChangeUserCountryWritesOutboxWhenRegionUnchanged(t *testing.T) {
|
|
ctx := appcode.WithContext(context.Background(), appcode.Default)
|
|
repository := mysqltest.NewRepository(t)
|
|
repository.PutCountry(userdomain.Country{CountryCode: "US", Enabled: true})
|
|
repository.PutCountry(userdomain.Country{CountryCode: "CA", Enabled: true})
|
|
repository.PutRegion(userdomain.Region{RegionID: 303, RegionCode: "NORTH-AMERICA", Name: "North America", Countries: []string{"US", "CA"}})
|
|
repository.PutUser(userdomain.User{
|
|
UserID: 91002,
|
|
DefaultDisplayUserID: "91002",
|
|
CurrentDisplayUserID: "91002",
|
|
Country: "US",
|
|
RegionID: 303,
|
|
ProfileCompleted: true,
|
|
ProfileCompletedAtMs: 1,
|
|
OnboardingStatus: userdomain.OnboardingStatusCompleted,
|
|
Status: userdomain.StatusActive,
|
|
})
|
|
svc := userservice.New(repository,
|
|
userservice.WithCountryRegionRepository(repository),
|
|
userservice.WithClock(func() time.Time { return time.UnixMilli(1_700_000_456_000).UTC() }),
|
|
)
|
|
|
|
updated, nextAllowedAt, err := svc.AdminChangeUserCountry(ctx, 91002, "CA", "req-admin-country-same-region")
|
|
if err != nil {
|
|
t.Fatalf("AdminChangeUserCountry failed: %v", err)
|
|
}
|
|
if nextAllowedAt != 0 || updated.Country != "CA" || updated.RegionID != 303 {
|
|
t.Fatalf("admin same-region country result mismatch: user=%+v next=%d", updated, nextAllowedAt)
|
|
}
|
|
|
|
var payloadJSON string
|
|
if err := repository.RawDB().QueryRowContext(ctx, `
|
|
SELECT CAST(payload_json AS CHAR)
|
|
FROM user_outbox
|
|
WHERE app_code = ? AND event_type = 'UserRegionChanged' AND aggregate_id = ?
|
|
`, appcode.Default, int64(91002)).Scan(&payloadJSON); err != nil {
|
|
t.Fatalf("query same-region UserRegionChanged outbox failed: %v", err)
|
|
}
|
|
var payload map[string]any
|
|
if err := json.Unmarshal([]byte(payloadJSON), &payload); err != nil {
|
|
t.Fatalf("decode same-region outbox payload failed: %v", err)
|
|
}
|
|
if payload["source"] != "admin" ||
|
|
payload["old_country"] != "US" ||
|
|
payload["new_country"] != "CA" ||
|
|
int64(payload["old_region_id"].(float64)) != 303 ||
|
|
int64(payload["new_region_id"].(float64)) != 303 {
|
|
t.Fatalf("same-region outbox payload mismatch: %v", payload)
|
|
}
|
|
}
|