mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-13 08:28:22 +08:00
## Summary Ports the Python `tenant_api` team/member management endpoints to Go, adding 4 endpoints under `/api/v1/tenants/:tenant_id/`: - `GET /tenants/:tenant_id/users` — list non-owner members with user details (owner only) - `POST /tenants/:tenant_id/users` — invite a user by email; creates invite-role join record (owner only) - `DELETE /tenants/:tenant_id/users` — remove a member by `user_id`; owner can remove anyone, members can remove themselves - `PATCH /tenants/:tenant_id` — accept a pending invitation, transitioning role `invite → normal` Closes #15294
113 lines
3.8 KiB
Go
113 lines
3.8 KiB
Go
//
|
|
// Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
package service
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"ragflow/internal/common"
|
|
)
|
|
|
|
// TestListMembersAuthCheck verifies that a non-owner (userID != tenantID) gets
|
|
// CodeAuthenticationError without hitting the database.
|
|
func TestListMembersAuthCheck(t *testing.T) {
|
|
s := &TenantService{}
|
|
_, code, err := s.ListMembers("user-abc", "tenant-xyz")
|
|
if err == nil {
|
|
t.Fatal("expected error for non-owner, got nil")
|
|
}
|
|
if code != common.CodeAuthenticationError {
|
|
t.Errorf("expected CodeAuthenticationError, got %v", code)
|
|
}
|
|
}
|
|
|
|
// TestAddMemberAuthCheck verifies that a non-owner gets CodeAuthenticationError.
|
|
func TestAddMemberAuthCheck(t *testing.T) {
|
|
s := &TenantService{}
|
|
_, code, err := s.AddMember("user-abc", "tenant-xyz", &AddMemberRequest{Email: "a@b.com"})
|
|
if err == nil {
|
|
t.Fatal("expected error for non-owner, got nil")
|
|
}
|
|
if code != common.CodeAuthenticationError {
|
|
t.Errorf("expected CodeAuthenticationError, got %v", code)
|
|
}
|
|
}
|
|
|
|
// TestAddMemberEmailRequired verifies the email validation runs after the auth check.
|
|
func TestAddMemberEmailRequired(t *testing.T) {
|
|
// When userID == tenantID (owner) but no email, expect CodeArgumentError.
|
|
s := &TenantService{}
|
|
_, code, err := s.AddMember("owner-id", "owner-id", &AddMemberRequest{Email: ""})
|
|
if err == nil {
|
|
t.Fatal("expected error for empty email, got nil")
|
|
}
|
|
if code != common.CodeArgumentError {
|
|
t.Errorf("expected CodeArgumentError, got %v", code)
|
|
}
|
|
}
|
|
|
|
// TestRemoveMemberAuthCheck verifies that an unrelated user gets CodeAuthenticationError.
|
|
func TestRemoveMemberAuthCheck(t *testing.T) {
|
|
s := &TenantService{}
|
|
code, err := s.RemoveMember("user-abc", "tenant-xyz", "user-def")
|
|
if err == nil {
|
|
t.Fatal("expected error, got nil")
|
|
}
|
|
if code != common.CodeAuthenticationError {
|
|
t.Errorf("expected CodeAuthenticationError, got %v", code)
|
|
}
|
|
}
|
|
|
|
// TestRemoveMemberSelfAllowed verifies that a user removing themselves passes the auth check.
|
|
// (It will fail at the DB layer in unit tests, but the auth check must pass first.)
|
|
func TestRemoveMemberSelfAllowed(t *testing.T) {
|
|
s := &TenantService{}
|
|
// userID == targetUserID: auth check should pass, expect DB error (nil userTenantDAO).
|
|
code, err := s.RemoveMember("user-abc", "tenant-xyz", "user-abc")
|
|
if code == common.CodeAuthenticationError {
|
|
t.Errorf("self-removal should pass auth check, got CodeAuthenticationError: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestAcceptInviteAuthCheck verifies that AcceptInvite fails when no membership exists (nil DAO).
|
|
func TestAcceptInviteAuthCheck(t *testing.T) {
|
|
s := &TenantService{}
|
|
// nil userTenantDAO: FilterByUserIDAndTenantID will panic/err, so we expect CodeDataError.
|
|
code, err := s.AcceptInvite("user-abc", "tenant-xyz")
|
|
if err == nil {
|
|
t.Fatal("expected error when no membership exists, got nil")
|
|
}
|
|
if code != common.CodeDataError {
|
|
t.Errorf("expected CodeDataError, got %v", code)
|
|
}
|
|
}
|
|
|
|
// TestTenantRoleConstants verifies the role string values match the Python enums.
|
|
func TestTenantRoleConstants(t *testing.T) {
|
|
cases := map[string]string{
|
|
TenantRoleOwner: "owner",
|
|
TenantRoleNormal: "normal",
|
|
TenantRoleInvite: "invite",
|
|
TenantRoleAdmin: "admin",
|
|
}
|
|
for got, want := range cases {
|
|
if got != want {
|
|
t.Errorf("role constant = %q, want %q", got, want)
|
|
}
|
|
}
|
|
}
|