package auth_test import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/hhs/camtalk/internal/auth" "github.com/hhs/camtalk/internal/store" ) // newTestService 创建测试用的 AuthService + MemUserRepository。 func newTestService(t *testing.T) (auth.Service, *store.MemUserRepository) { t.Helper() tm := auth.NewTokenManager("test-secret", 15*time.Minute, 7*24*time.Hour) repo := store.NewMemUserRepository() svc := auth.NewAuthService(tm, repo) return svc, repo } // --- Register --- func TestRegister_Success(t *testing.T) { svc, _ := newTestService(t) ctx := context.Background() resp, err := svc.Register(ctx, auth.RegisterRequest{ Username: "alice", Password: "password123", }) require.NoError(t, err) assert.NotEmpty(t, resp.User.ID) assert.Equal(t, "alice", resp.User.Username) assert.NotEmpty(t, resp.AccessToken) assert.NotEmpty(t, resp.RefreshToken) } func TestRegister_DuplicateUsername(t *testing.T) { svc, _ := newTestService(t) ctx := context.Background() _, err := svc.Register(ctx, auth.RegisterRequest{ Username: "alice", Password: "password123", }) require.NoError(t, err) // 同名再次注册 _, err = svc.Register(ctx, auth.RegisterRequest{ Username: "alice", Password: "another-password", }) assert.ErrorIs(t, err, auth.ErrUsernameTaken) } // --- Login --- func TestLogin_Success(t *testing.T) { svc, _ := newTestService(t) ctx := context.Background() // 先注册 _, err := svc.Register(ctx, auth.RegisterRequest{ Username: "bob", Password: "password123", }) require.NoError(t, err) // 登录 resp, err := svc.Login(ctx, auth.LoginRequest{ Username: "bob", Password: "password123", }) require.NoError(t, err) assert.Equal(t, "bob", resp.User.Username) assert.NotEmpty(t, resp.AccessToken) assert.NotEmpty(t, resp.RefreshToken) } func TestLogin_WrongPassword(t *testing.T) { svc, _ := newTestService(t) ctx := context.Background() _, err := svc.Register(ctx, auth.RegisterRequest{ Username: "bob", Password: "password123", }) require.NoError(t, err) _, err = svc.Login(ctx, auth.LoginRequest{ Username: "bob", Password: "wrong-password", }) assert.ErrorIs(t, err, auth.ErrInvalidCredentials) } func TestLogin_UserNotFound(t *testing.T) { svc, _ := newTestService(t) ctx := context.Background() _, err := svc.Login(ctx, auth.LoginRequest{ Username: "nonexistent", Password: "password123", }) assert.ErrorIs(t, err, auth.ErrInvalidCredentials) } // --- Refresh --- func TestRefresh_Success(t *testing.T) { svc, _ := newTestService(t) ctx := context.Background() // 注册 regResp, err := svc.Register(ctx, auth.RegisterRequest{ Username: "charlie", Password: "password123", }) require.NoError(t, err) // 刷新 refreshResp, err := svc.Refresh(ctx, auth.RefreshRequest{ RefreshToken: regResp.RefreshToken, }) require.NoError(t, err) assert.Equal(t, "charlie", refreshResp.User.Username) assert.NotEmpty(t, refreshResp.AccessToken) assert.NotEmpty(t, refreshResp.RefreshToken) // 新旧 refresh token 应不同(rotation) assert.NotEqual(t, regResp.RefreshToken, refreshResp.RefreshToken) } func TestRefresh_UsedTokenFails(t *testing.T) { svc, _ := newTestService(t) ctx := context.Background() regResp, err := svc.Register(ctx, auth.RegisterRequest{ Username: "charlie", Password: "password123", }) require.NoError(t, err) // 第一次刷新 _, err = svc.Refresh(ctx, auth.RefreshRequest{ RefreshToken: regResp.RefreshToken, }) require.NoError(t, err) // 用旧 token 再次刷新 → 应失败 _, err = svc.Refresh(ctx, auth.RefreshRequest{ RefreshToken: regResp.RefreshToken, }) assert.ErrorIs(t, err, auth.ErrRefreshTokenUsed) } func TestRefresh_InvalidToken(t *testing.T) { svc, _ := newTestService(t) ctx := context.Background() _, err := svc.Refresh(ctx, auth.RefreshRequest{ RefreshToken: "completely-invalid-token", }) assert.ErrorIs(t, err, auth.ErrRefreshTokenUsed) } // --- Logout --- func TestLogout_Success(t *testing.T) { svc, _ := newTestService(t) ctx := context.Background() regResp, err := svc.Register(ctx, auth.RegisterRequest{ Username: "dave", Password: "password123", }) require.NoError(t, err) // 登出 err = svc.Logout(ctx, regResp.User.ID, regResp.RefreshToken) require.NoError(t, err) // 登出后 refresh token 应失效 _, err = svc.Refresh(ctx, auth.RefreshRequest{ RefreshToken: regResp.RefreshToken, }) assert.ErrorIs(t, err, auth.ErrRefreshTokenUsed) }