Files
xinmi/internal/handler/auth_register_by_invite_tenant_test.go
T
yawan f7fc992f57
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
2026-09-22 16:19:06 +08:00

150 lines
5.0 KiB
Go

package handler
import (
"bytes"
"context"
"errors"
"net/http"
"net/http/httptest"
"testing"
"github.com/Tencent/WeKnora/internal/types"
"github.com/Tencent/WeKnora/internal/types/interfaces"
"github.com/gin-gonic/gin"
)
type invitedRegistrationUserService struct {
interfaces.UserService
registeredMode types.TenantProvisioningMode
updatedTenant uint64
updateCalls []uint64
}
func (s *invitedRegistrationUserService) GetUserByEmail(context.Context, string) (*types.User, error) {
return nil, nil
}
func (s *invitedRegistrationUserService) Register(_ context.Context, req *types.RegisterRequest) (*types.User, error) {
s.registeredMode = req.TenantProvisioning
return &types.User{ID: "new-user", Username: req.Username, Email: req.Email, IsActive: true}, nil
}
func (s *invitedRegistrationUserService) UpdateUser(_ context.Context, user *types.User) error {
s.updatedTenant = user.TenantID
s.updateCalls = append(s.updateCalls, user.TenantID)
return nil
}
func (s *invitedRegistrationUserService) GenerateTokens(context.Context, *types.User) (string, string, error) {
return "access", "refresh", nil
}
type invitedRegistrationInvitationService struct {
interfaces.TenantInvitationService
acceptErr error
}
func (s *invitedRegistrationInvitationService) LookupByToken(context.Context, string) (*types.TenantInvitation, error) {
return &types.TenantInvitation{TenantID: 42, Role: types.TenantRoleViewer}, nil
}
func (s *invitedRegistrationInvitationService) AcceptByToken(context.Context, string, string) (*types.TenantMember, error) {
if s.acceptErr != nil {
return nil, s.acceptErr
}
return &types.TenantMember{TenantID: 42, Role: types.TenantRoleViewer}, nil
}
type invitedRegistrationTenantService struct {
interfaces.TenantService
}
func TestRegisterByInviteRestoresTenantlessAccountWhenInviteExpiresDuringRegistration(t *testing.T) {
gin.SetMode(gin.TestMode)
users := &invitedRegistrationUserService{}
h := &AuthHandler{
userService: users,
tenantService: &invitedRegistrationTenantService{},
invitationSvc: &invitedRegistrationInvitationService{acceptErr: errors.New("expired")},
}
r := gin.New()
r.Use(errorCapture())
r.POST("/auth/register-by-invite", h.RegisterByInvite)
body := []byte(`{"token":"invite-token","email":"alice@example.com","username":"alice","password":"supersecret1"}`)
req := httptest.NewRequest(http.MethodPost, "/auth/register-by-invite", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusGone {
t.Fatalf("status=%d body=%s", w.Code, w.Body.String())
}
if users.updatedTenant != 0 {
t.Fatalf("updated tenant=%d, want tenantless rollback", users.updatedTenant)
}
if len(users.updateCalls) != 2 || users.updateCalls[0] != 42 || users.updateCalls[1] != 0 {
t.Fatalf("update calls=%v, want [42 0]", users.updateCalls)
}
}
func (s *invitedRegistrationTenantService) GetTenantByID(context.Context, uint64) (*types.Tenant, error) {
return &types.Tenant{ID: 42, Name: "Invited Workspace"}, nil
}
func TestRegisterByInviteUsesInvitedTenantWithoutPersonalTenant(t *testing.T) {
gin.SetMode(gin.TestMode)
users := &invitedRegistrationUserService{}
h := &AuthHandler{
userService: users,
tenantService: &invitedRegistrationTenantService{},
invitationSvc: &invitedRegistrationInvitationService{},
}
r := gin.New()
r.Use(errorCapture())
r.POST("/auth/register-by-invite", h.RegisterByInvite)
body := []byte(`{"token":"invite-token","email":"alice@example.com","username":"alice","password":"supersecret1"}`)
req := httptest.NewRequest(http.MethodPost, "/auth/register-by-invite", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusCreated {
t.Fatalf("status=%d body=%s", w.Code, w.Body.String())
}
if users.registeredMode != types.TenantProvisioningTenantless {
t.Fatalf("register mode=%q, want tenantless", users.registeredMode)
}
if users.updatedTenant != 42 {
t.Fatalf("updated tenant=%d, want 42", users.updatedTenant)
}
}
func TestRegisterByInviteRejectsSimplePasswordWhenComplexEnabled(t *testing.T) {
gin.SetMode(gin.TestMode)
users := &invitedRegistrationUserService{}
h := &AuthHandler{
userService: users,
tenantService: &invitedRegistrationTenantService{},
invitationSvc: &invitedRegistrationInvitationService{},
systemSettingSvc: &tenantPolicySettingService{enabled: true},
}
r := gin.New()
r.Use(errorCapture())
r.POST("/auth/register-by-invite", h.RegisterByInvite)
body := []byte(`{"token":"invite-token","email":"alice@example.com","username":"alice","password":"supersecret1"}`)
req := httptest.NewRequest(http.MethodPost, "/auth/register-by-invite", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Fatalf("status=%d body=%s, want 400", w.Code, w.Body.String())
}
if users.registeredMode != "" {
t.Fatalf("Register was called with mode=%q", users.registeredMode)
}
}