- Added functions to check the existence of invitation codes, members, OAuth accounts, roles, teams, and user types before performing updates, enhancing error handling and user feedback. - Updated relevant update functions to utilize these existence checks, ensuring accurate error messages when no changes are made or when entities do not exist. - Refactored tests to validate the new existence check logic, improving overall test coverage and reliability.
1283 lines
39 KiB
Go
1283 lines
39 KiB
Go
package user_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/yaoapp/kun/maps"
|
|
"github.com/yaoapp/yao/openapi"
|
|
"github.com/yaoapp/yao/openapi/tests/testutils"
|
|
)
|
|
|
|
// TestMemberList tests the GET /user/teams/:team_id/members endpoint
|
|
func TestMemberList(t *testing.T) {
|
|
// Initialize test environment
|
|
serverURL := testutils.Prepare(t)
|
|
defer testutils.Clean()
|
|
|
|
// Get base URL from server config
|
|
baseURL := ""
|
|
if openapi.Server != nil && openapi.Server.Config != nil {
|
|
baseURL = openapi.Server.Config.BaseURL
|
|
}
|
|
|
|
// Register a test client for OAuth authentication
|
|
testClient := testutils.RegisterTestClient(t, "Member List Test Client", []string{"https://localhost/callback"})
|
|
defer testutils.CleanupTestClient(t, testClient.ClientID)
|
|
|
|
// Obtain access token for authenticated requests
|
|
tokenInfo := testutils.ObtainAccessToken(t, serverURL, testClient.ClientID, testClient.ClientSecret, "https://localhost/callback", "openid profile")
|
|
|
|
// Create a test team first
|
|
createdTeam := createTestTeam(t, serverURL, baseURL, tokenInfo.AccessToken, "Member List Test Team")
|
|
teamID := getTeamID(createdTeam)
|
|
|
|
testCases := []struct {
|
|
name string
|
|
teamID string
|
|
query string
|
|
headers map[string]string
|
|
expectCode int
|
|
expectMsg string
|
|
}{
|
|
{
|
|
"list members without authentication",
|
|
teamID,
|
|
"",
|
|
map[string]string{},
|
|
401,
|
|
"should require authentication",
|
|
},
|
|
{
|
|
"list members with valid token",
|
|
teamID,
|
|
"",
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should return team members",
|
|
},
|
|
{
|
|
"list members with pagination",
|
|
teamID,
|
|
"?page=1&pagesize=10",
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should handle pagination parameters",
|
|
},
|
|
{
|
|
"list members with status filter",
|
|
teamID,
|
|
"?status=active",
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should filter by status",
|
|
},
|
|
{
|
|
"list members of non-existent team",
|
|
"non-existent-team-id",
|
|
"",
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
404,
|
|
"should return not found for non-existent team",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
requestURL := serverURL + baseURL + "/user/teams/" + tc.teamID + "/members" + tc.query
|
|
req, err := http.NewRequest("GET", requestURL, nil)
|
|
assert.NoError(t, err, "Should create HTTP request")
|
|
|
|
// Add headers
|
|
for key, value := range tc.headers {
|
|
req.Header.Set(key, value)
|
|
}
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
assert.NoError(t, err, "HTTP request should succeed")
|
|
|
|
if resp != nil {
|
|
defer resp.Body.Close()
|
|
assert.Equal(t, tc.expectCode, resp.StatusCode, "Expected status code %d for %s", tc.expectCode, tc.name)
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
assert.NoError(t, err, "Should read response body")
|
|
|
|
if resp.StatusCode == 200 {
|
|
// Parse response as pagination result
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(body, &response)
|
|
assert.NoError(t, err, "Should parse JSON response")
|
|
|
|
// Check pagination structure
|
|
assert.Contains(t, response, "data", "Should have data array")
|
|
assert.Contains(t, response, "total", "Should have total count")
|
|
assert.Contains(t, response, "page", "Should have page number")
|
|
assert.Contains(t, response, "pagesize", "Should have pagesize")
|
|
|
|
// Verify that creator is automatically added as member
|
|
if data, ok := response["data"].([]interface{}); ok {
|
|
assert.GreaterOrEqual(t, len(data), 1, "Should have at least the owner as member")
|
|
if len(data) > 0 {
|
|
member := data[0].(map[string]interface{})
|
|
assert.Equal(t, tokenInfo.UserID, member["user_id"], "Owner should be in member list")
|
|
assert.Equal(t, "owner:free", member["role_id"], "Creator should have owner:free role")
|
|
}
|
|
}
|
|
}
|
|
|
|
t.Logf("Member list test %s: status=%d, body=%s", tc.name, resp.StatusCode, string(body))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestMemberGet tests the GET /user/teams/:team_id/members/:member_id endpoint
|
|
func TestMemberGet(t *testing.T) {
|
|
// Initialize test environment
|
|
serverURL := testutils.Prepare(t)
|
|
defer testutils.Clean()
|
|
|
|
// Get base URL from server config
|
|
baseURL := ""
|
|
if openapi.Server != nil && openapi.Server.Config != nil {
|
|
baseURL = openapi.Server.Config.BaseURL
|
|
}
|
|
|
|
// Register a test client for OAuth authentication
|
|
testClient := testutils.RegisterTestClient(t, "Member Get Test Client", []string{"https://localhost/callback"})
|
|
defer testutils.CleanupTestClient(t, testClient.ClientID)
|
|
|
|
// Obtain access token for authenticated requests
|
|
tokenInfo := testutils.ObtainAccessToken(t, serverURL, testClient.ClientID, testClient.ClientSecret, "https://localhost/callback", "openid profile")
|
|
|
|
// Create a test team and get owner member ID
|
|
createdTeam := createTestTeam(t, serverURL, baseURL, tokenInfo.AccessToken, "Member Get Test Team")
|
|
teamID := getTeamID(createdTeam)
|
|
|
|
// Get member list to find the owner member ID
|
|
ownerMemberID := getOwnerMemberID(t, serverURL, baseURL, teamID, tokenInfo.AccessToken)
|
|
|
|
testCases := []struct {
|
|
name string
|
|
teamID string
|
|
memberID string
|
|
headers map[string]string
|
|
expectCode int
|
|
expectMsg string
|
|
}{
|
|
{
|
|
"get member without authentication",
|
|
teamID,
|
|
ownerMemberID,
|
|
map[string]string{},
|
|
401,
|
|
"should require authentication",
|
|
},
|
|
{
|
|
"get existing member",
|
|
teamID,
|
|
ownerMemberID,
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should return member details",
|
|
},
|
|
{
|
|
"get non-existent member",
|
|
teamID,
|
|
"999999",
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
404,
|
|
"should return not found for non-existent member",
|
|
},
|
|
{
|
|
"get member from non-existent team",
|
|
"non-existent-team-id",
|
|
ownerMemberID,
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
404,
|
|
"should return not found for non-existent team",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
requestURL := serverURL + baseURL + "/user/teams/" + tc.teamID + "/members/" + tc.memberID
|
|
req, err := http.NewRequest("GET", requestURL, nil)
|
|
assert.NoError(t, err, "Should create HTTP request")
|
|
|
|
// Add headers
|
|
for key, value := range tc.headers {
|
|
req.Header.Set(key, value)
|
|
}
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
assert.NoError(t, err, "HTTP request should succeed")
|
|
|
|
if resp != nil {
|
|
defer resp.Body.Close()
|
|
assert.Equal(t, tc.expectCode, resp.StatusCode, "Expected status code %d for %s", tc.expectCode, tc.name)
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
assert.NoError(t, err, "Should read response body")
|
|
|
|
if resp.StatusCode == 200 {
|
|
// Parse response as member object
|
|
var member map[string]interface{}
|
|
err = json.Unmarshal(body, &member)
|
|
assert.NoError(t, err, "Should parse JSON response")
|
|
|
|
// Verify member structure
|
|
assert.Contains(t, member, "id", "Should have member ID")
|
|
assert.Contains(t, member, "team_id", "Should have team_id")
|
|
assert.Contains(t, member, "user_id", "Should have user_id")
|
|
assert.Contains(t, member, "role_id", "Should have role_id")
|
|
assert.Contains(t, member, "status", "Should have status")
|
|
assert.Contains(t, member, "created_at", "Should have created_at")
|
|
assert.Contains(t, member, "updated_at", "Should have updated_at")
|
|
|
|
// Verify values
|
|
assert.Equal(t, teamID, member["team_id"], "Should have correct team_id")
|
|
assert.Equal(t, tokenInfo.UserID, member["user_id"], "Should have correct user_id")
|
|
}
|
|
|
|
t.Logf("Member get test %s: status=%d, body=%s", tc.name, resp.StatusCode, string(body))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestMemberUpdate tests the PUT /user/teams/:team_id/members/:member_id endpoint
|
|
func TestMemberUpdate(t *testing.T) {
|
|
// Initialize test environment
|
|
serverURL := testutils.Prepare(t)
|
|
defer testutils.Clean()
|
|
|
|
// Get base URL from server config
|
|
baseURL := ""
|
|
if openapi.Server != nil && openapi.Server.Config != nil {
|
|
baseURL = openapi.Server.Config.BaseURL
|
|
}
|
|
|
|
// Register a test client for OAuth authentication
|
|
testClient := testutils.RegisterTestClient(t, "Member Update Test Client", []string{"https://localhost/callback"})
|
|
defer testutils.CleanupTestClient(t, testClient.ClientID)
|
|
|
|
// Obtain access token for authenticated requests
|
|
tokenInfo := testutils.ObtainAccessToken(t, serverURL, testClient.ClientID, testClient.ClientSecret, "https://localhost/callback", "openid profile")
|
|
|
|
testCases := []struct {
|
|
name string
|
|
setupFunc func() (string, string) // Returns (teamID, memberID)
|
|
body map[string]interface{}
|
|
headers map[string]string
|
|
expectCode int
|
|
expectMsg string
|
|
}{
|
|
{
|
|
"update member without authentication",
|
|
func() (string, string) {
|
|
team := createTestTeam(t, serverURL, baseURL, tokenInfo.AccessToken, "Update Test Team 1")
|
|
teamID := getTeamID(team)
|
|
memberID := createTestMember(t, serverURL, baseURL, teamID, tokenInfo.AccessToken, "test-update-user-1")
|
|
return teamID, memberID
|
|
},
|
|
map[string]interface{}{
|
|
"role_id": "admin",
|
|
},
|
|
map[string]string{},
|
|
401,
|
|
"should require authentication",
|
|
},
|
|
{
|
|
"update member role",
|
|
func() (string, string) {
|
|
team := createTestTeam(t, serverURL, baseURL, tokenInfo.AccessToken, "Update Test Team 2")
|
|
teamID := getTeamID(team)
|
|
memberID := createTestMember(t, serverURL, baseURL, teamID, tokenInfo.AccessToken, "test-update-user-2")
|
|
return teamID, memberID
|
|
},
|
|
map[string]interface{}{
|
|
"role_id": "admin",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should update member role",
|
|
},
|
|
{
|
|
"update member status",
|
|
func() (string, string) {
|
|
team := createTestTeam(t, serverURL, baseURL, tokenInfo.AccessToken, "Update Test Team 3")
|
|
teamID := getTeamID(team)
|
|
memberID := createTestMember(t, serverURL, baseURL, teamID, tokenInfo.AccessToken, "test-update-user-3")
|
|
return teamID, memberID
|
|
},
|
|
map[string]interface{}{
|
|
"status": "inactive",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should update member status",
|
|
},
|
|
{
|
|
"update non-existent member",
|
|
func() (string, string) {
|
|
team := createTestTeam(t, serverURL, baseURL, tokenInfo.AccessToken, "Update Test Team 5")
|
|
teamID := getTeamID(team)
|
|
return teamID, "999999"
|
|
},
|
|
map[string]interface{}{
|
|
"role_id": "admin",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
404,
|
|
"should return not found for non-existent member",
|
|
},
|
|
{
|
|
"update member in non-existent team",
|
|
func() (string, string) {
|
|
team := createTestTeam(t, serverURL, baseURL, tokenInfo.AccessToken, "Update Test Team 6")
|
|
teamID := getTeamID(team)
|
|
memberID := createTestMember(t, serverURL, baseURL, teamID, tokenInfo.AccessToken, "test-update-user-5")
|
|
return "non-existent-team-id", memberID
|
|
},
|
|
map[string]interface{}{
|
|
"role_id": "admin",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
404,
|
|
"should return not found for non-existent team",
|
|
},
|
|
{
|
|
"update member with invalid JSON",
|
|
func() (string, string) {
|
|
team := createTestTeam(t, serverURL, baseURL, tokenInfo.AccessToken, "Update Test Team 7")
|
|
teamID := getTeamID(team)
|
|
memberID := createTestMember(t, serverURL, baseURL, teamID, tokenInfo.AccessToken, "test-update-user-6")
|
|
return teamID, memberID
|
|
},
|
|
nil, // Will send invalid JSON
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
400,
|
|
"should handle invalid JSON",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
teamID, memberID := tc.setupFunc()
|
|
requestURL := serverURL + baseURL + "/user/teams/" + teamID + "/members/" + memberID
|
|
|
|
var req *http.Request
|
|
var err error
|
|
|
|
if tc.body == nil {
|
|
// Send invalid JSON for invalid JSON test case
|
|
req, err = http.NewRequest("PUT", requestURL, bytes.NewBufferString("invalid json"))
|
|
} else {
|
|
bodyBytes, _ := json.Marshal(tc.body)
|
|
req, err = http.NewRequest("PUT", requestURL, bytes.NewBuffer(bodyBytes))
|
|
}
|
|
assert.NoError(t, err, "Should create HTTP request")
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
// Add headers
|
|
for key, value := range tc.headers {
|
|
req.Header.Set(key, value)
|
|
}
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
assert.NoError(t, err, "HTTP request should succeed")
|
|
|
|
if resp != nil {
|
|
defer resp.Body.Close()
|
|
assert.Equal(t, tc.expectCode, resp.StatusCode, "Expected status code %d for %s", tc.expectCode, tc.name)
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
assert.NoError(t, err, "Should read response body")
|
|
|
|
if resp.StatusCode == 200 {
|
|
// Parse response as success message
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(body, &response)
|
|
assert.NoError(t, err, "Should parse JSON response")
|
|
|
|
assert.Contains(t, response, "message", "Should have success message")
|
|
assert.Equal(t, "Member updated successfully", response["message"], "Should have correct success message")
|
|
}
|
|
|
|
t.Logf("Member update test %s: status=%d, body=%s", tc.name, resp.StatusCode, string(body))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestMemberDelete tests the DELETE /user/teams/:team_id/members/:member_id endpoint
|
|
func TestMemberDelete(t *testing.T) {
|
|
// Initialize test environment
|
|
serverURL := testutils.Prepare(t)
|
|
defer testutils.Clean()
|
|
|
|
// Get base URL from server config
|
|
baseURL := ""
|
|
if openapi.Server != nil && openapi.Server.Config != nil {
|
|
baseURL = openapi.Server.Config.BaseURL
|
|
}
|
|
|
|
// Register a test client for OAuth authentication
|
|
testClient := testutils.RegisterTestClient(t, "Member Delete Test Client", []string{"https://localhost/callback"})
|
|
defer testutils.CleanupTestClient(t, testClient.ClientID)
|
|
|
|
// Obtain access token for authenticated requests
|
|
tokenInfo := testutils.ObtainAccessToken(t, serverURL, testClient.ClientID, testClient.ClientSecret, "https://localhost/callback", "openid profile")
|
|
|
|
// Create teams and members for testing deletion
|
|
createMemberForDeletion := func(name string) (string, string) {
|
|
team := createTestTeam(t, serverURL, baseURL, tokenInfo.AccessToken, "Delete Test Team "+name)
|
|
teamID := getTeamID(team)
|
|
memberID := createTestMember(t, serverURL, baseURL, teamID, tokenInfo.AccessToken, "test-delete-user-"+name)
|
|
return teamID, memberID
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
setupFunc func() (string, string) // Returns (teamID, memberID)
|
|
headers map[string]string
|
|
expectCode int
|
|
expectMsg string
|
|
}{
|
|
{
|
|
"delete member without authentication",
|
|
func() (string, string) { return createMemberForDeletion("1") },
|
|
map[string]string{},
|
|
401,
|
|
"should require authentication",
|
|
},
|
|
{
|
|
"delete existing member",
|
|
func() (string, string) { return createMemberForDeletion("2") },
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should delete member successfully",
|
|
},
|
|
{
|
|
"delete non-existent member",
|
|
func() (string, string) {
|
|
team := createTestTeam(t, serverURL, baseURL, tokenInfo.AccessToken, "Delete Test Team 3")
|
|
return getTeamID(team), "999999"
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
404,
|
|
"should return not found for non-existent member",
|
|
},
|
|
{
|
|
"delete member from non-existent team",
|
|
func() (string, string) {
|
|
_, memberID := createMemberForDeletion("4")
|
|
return "non-existent-team-id", memberID
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
404,
|
|
"should return not found for non-existent team",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
teamID, memberID := tc.setupFunc()
|
|
requestURL := serverURL + baseURL + "/user/teams/" + teamID + "/members/" + memberID
|
|
|
|
req, err := http.NewRequest("DELETE", requestURL, nil)
|
|
assert.NoError(t, err, "Should create HTTP request")
|
|
|
|
// Add headers
|
|
for key, value := range tc.headers {
|
|
req.Header.Set(key, value)
|
|
}
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
assert.NoError(t, err, "HTTP request should succeed")
|
|
|
|
if resp != nil {
|
|
defer resp.Body.Close()
|
|
assert.Equal(t, tc.expectCode, resp.StatusCode, "Expected status code %d for %s", tc.expectCode, tc.name)
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
assert.NoError(t, err, "Should read response body")
|
|
|
|
if resp.StatusCode == 200 {
|
|
// Parse response as success message
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(body, &response)
|
|
assert.NoError(t, err, "Should parse JSON response")
|
|
|
|
assert.Contains(t, response, "message", "Should have success message")
|
|
assert.Equal(t, "Member removed successfully", response["message"], "Should have correct success message")
|
|
}
|
|
|
|
t.Logf("Member delete test %s: status=%d, body=%s", tc.name, resp.StatusCode, string(body))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestMemberPermissionVerification tests permission verification for member operations
|
|
func TestMemberPermissionVerification(t *testing.T) {
|
|
// Initialize test environment
|
|
serverURL := testutils.Prepare(t)
|
|
defer testutils.Clean()
|
|
|
|
// Get base URL from server config
|
|
baseURL := ""
|
|
if openapi.Server != nil && openapi.Server.Config != nil {
|
|
baseURL = openapi.Server.Config.BaseURL
|
|
}
|
|
|
|
// Register test clients for different users
|
|
ownerClient := testutils.RegisterTestClient(t, "Owner Test Client", []string{"https://localhost/callback"})
|
|
defer testutils.CleanupTestClient(t, ownerClient.ClientID)
|
|
|
|
nonOwnerClient := testutils.RegisterTestClient(t, "Non-Owner Test Client", []string{"https://localhost/callback"})
|
|
defer testutils.CleanupTestClient(t, nonOwnerClient.ClientID)
|
|
|
|
// Obtain access tokens
|
|
ownerToken := testutils.ObtainAccessToken(t, serverURL, ownerClient.ClientID, ownerClient.ClientSecret, "https://localhost/callback", "openid profile")
|
|
nonOwnerToken := testutils.ObtainAccessToken(t, serverURL, nonOwnerClient.ClientID, nonOwnerClient.ClientSecret, "https://localhost/callback", "openid profile")
|
|
|
|
// Create a team with owner
|
|
createdTeam := createTestTeam(t, serverURL, baseURL, ownerToken.AccessToken, "Permission Test Team")
|
|
teamID := getTeamID(createdTeam)
|
|
|
|
// Add non-owner as a member
|
|
memberID := createTestMember(t, serverURL, baseURL, teamID, ownerToken.AccessToken, nonOwnerToken.UserID)
|
|
|
|
testCases := []struct {
|
|
name string
|
|
endpoint string
|
|
method string
|
|
token string
|
|
expectCode int
|
|
expectMsg string
|
|
}{
|
|
{
|
|
"owner can list members",
|
|
"/user/teams/" + teamID + "/members",
|
|
"GET",
|
|
ownerToken.AccessToken,
|
|
200,
|
|
"owner should be able to list members",
|
|
},
|
|
{
|
|
"member can list members",
|
|
"/user/teams/" + teamID + "/members",
|
|
"GET",
|
|
nonOwnerToken.AccessToken,
|
|
200,
|
|
"member should be able to list members",
|
|
},
|
|
{
|
|
"owner can get member details",
|
|
"/user/teams/" + teamID + "/members/" + memberID,
|
|
"GET",
|
|
ownerToken.AccessToken,
|
|
200,
|
|
"owner should be able to get member details",
|
|
},
|
|
{
|
|
"member can get member details",
|
|
"/user/teams/" + teamID + "/members/" + memberID,
|
|
"GET",
|
|
nonOwnerToken.AccessToken,
|
|
200,
|
|
"member should be able to get member details",
|
|
},
|
|
{
|
|
"owner can update members",
|
|
"/user/teams/" + teamID + "/members/" + memberID,
|
|
"PUT",
|
|
ownerToken.AccessToken,
|
|
200,
|
|
"owner should be able to update members",
|
|
},
|
|
{
|
|
"member cannot update members",
|
|
"/user/teams/" + teamID + "/members/" + memberID,
|
|
"PUT",
|
|
nonOwnerToken.AccessToken,
|
|
403,
|
|
"member should not be able to update members",
|
|
},
|
|
{
|
|
"owner can delete members",
|
|
"/user/teams/" + teamID + "/members/" + memberID,
|
|
"DELETE",
|
|
ownerToken.AccessToken,
|
|
200,
|
|
"owner should be able to delete members",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
requestURL := serverURL + baseURL + tc.endpoint
|
|
|
|
var req *http.Request
|
|
var err error
|
|
|
|
// Create request body for POST/PUT methods
|
|
if tc.method == "POST" {
|
|
body := map[string]interface{}{
|
|
"user_id": "test-permission-user",
|
|
"role_id": "member",
|
|
}
|
|
bodyBytes, _ := json.Marshal(body)
|
|
req, err = http.NewRequest(tc.method, requestURL, bytes.NewBuffer(bodyBytes))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
} else if tc.method == "PUT" {
|
|
body := map[string]interface{}{
|
|
"role_id": "admin",
|
|
}
|
|
bodyBytes, _ := json.Marshal(body)
|
|
req, err = http.NewRequest(tc.method, requestURL, bytes.NewBuffer(bodyBytes))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
} else {
|
|
req, err = http.NewRequest(tc.method, requestURL, nil)
|
|
}
|
|
|
|
assert.NoError(t, err, "Should create HTTP request")
|
|
|
|
req.Header.Set("Authorization", "Bearer "+tc.token)
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
assert.NoError(t, err, "HTTP request should succeed")
|
|
|
|
if resp != nil {
|
|
defer resp.Body.Close()
|
|
assert.Equal(t, tc.expectCode, resp.StatusCode, "Expected status code %d for %s", tc.expectCode, tc.name)
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
assert.NoError(t, err, "Should read response body")
|
|
|
|
t.Logf("Permission test %s: status=%d, body=%s", tc.name, resp.StatusCode, string(body))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Helper functions
|
|
|
|
// createTestTeam creates a team for testing and returns the team data
|
|
func createTestTeam(t *testing.T, serverURL, baseURL, accessToken, teamName string) map[string]interface{} {
|
|
createTeamBody := map[string]interface{}{
|
|
"name": teamName,
|
|
"description": "Team created for testing purposes",
|
|
}
|
|
|
|
bodyBytes, err := json.Marshal(createTeamBody)
|
|
assert.NoError(t, err, "Should marshal team creation body")
|
|
|
|
req, err := http.NewRequest("POST", serverURL+baseURL+"/user/teams", bytes.NewBuffer(bodyBytes))
|
|
assert.NoError(t, err, "Should create team creation request")
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("Authorization", "Bearer "+accessToken)
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
assert.NoError(t, err, "Should send team creation request")
|
|
defer resp.Body.Close()
|
|
|
|
assert.Equal(t, 201, resp.StatusCode, "Should create team successfully")
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
assert.NoError(t, err, "Should read team creation response")
|
|
|
|
var team map[string]interface{}
|
|
err = json.Unmarshal(body, &team)
|
|
assert.NoError(t, err, "Should parse team creation response")
|
|
|
|
return team
|
|
}
|
|
|
|
// createTestMember creates a member for testing using provider directly (no API call).
|
|
// This is the recommended approach since direct member creation endpoint was removed.
|
|
// Members should normally be added via invitation flow or robot creation endpoint.
|
|
// Returns the user_id which serves as member_id in API context.
|
|
func createTestMember(t *testing.T, serverURL, baseURL, teamID, accessToken, userID string) string {
|
|
// Get user provider for direct database operations
|
|
provider := testutils.GetUserProvider(t)
|
|
ctx := context.Background()
|
|
|
|
// Create member data using maps.MapStrAny (required by UserProvider interface)
|
|
memberData := maps.MapStrAny{
|
|
"team_id": teamID,
|
|
"user_id": userID,
|
|
"member_type": "user",
|
|
"role_id": "team:member",
|
|
"status": "active",
|
|
}
|
|
|
|
// Create member directly in database
|
|
memberID, err := provider.CreateMember(ctx, memberData)
|
|
assert.NoError(t, err, "Should create member in database")
|
|
assert.NotEmpty(t, memberID, "Member ID should not be empty")
|
|
|
|
t.Logf("Created test member directly in database: user_id=%s, member_id=%s, team_id=%s", userID, memberID, teamID)
|
|
|
|
// Return user_id (which is used as member identifier in API context)
|
|
return userID
|
|
}
|
|
|
|
// getOwnerMemberID gets the user_id of the team owner (which serves as member_id in API context)
|
|
func getOwnerMemberID(t *testing.T, serverURL, baseURL, teamID, accessToken string) string {
|
|
req, err := http.NewRequest("GET", serverURL+baseURL+"/user/teams/"+teamID+"/members", nil)
|
|
assert.NoError(t, err, "Should create member list request")
|
|
|
|
req.Header.Set("Authorization", "Bearer "+accessToken)
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
assert.NoError(t, err, "Should send member list request")
|
|
defer resp.Body.Close()
|
|
|
|
assert.Equal(t, 200, resp.StatusCode, "Should get members successfully")
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
assert.NoError(t, err, "Should read member list response")
|
|
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(body, &response)
|
|
assert.NoError(t, err, "Should parse member list response")
|
|
|
|
data, ok := response["data"].([]interface{})
|
|
assert.True(t, ok, "Should have data array")
|
|
assert.Greater(t, len(data), 0, "Should have at least one member")
|
|
|
|
// Find the owner member and return their user_id
|
|
for _, item := range data {
|
|
member := item.(map[string]interface{})
|
|
if role, ok := member["role_id"].(string); ok && strings.HasPrefix(role, "owner") {
|
|
userID, ok := member["user_id"].(string)
|
|
if !ok {
|
|
t.Fatal("Owner member missing user_id")
|
|
}
|
|
return userID
|
|
}
|
|
}
|
|
|
|
t.Fatal("Could not find owner member")
|
|
return ""
|
|
}
|
|
|
|
// TestMemberCreateRobot tests the POST /user/teams/:team_id/members/robots endpoint
|
|
func TestMemberCreateRobot(t *testing.T) {
|
|
// Initialize test environment
|
|
serverURL := testutils.Prepare(t)
|
|
defer testutils.Clean()
|
|
|
|
// Get base URL from server config
|
|
baseURL := ""
|
|
if openapi.Server != nil && openapi.Server.Config != nil {
|
|
baseURL = openapi.Server.Config.BaseURL
|
|
}
|
|
|
|
// Register a test client for OAuth authentication
|
|
testClient := testutils.RegisterTestClient(t, "Robot Member Test Client", []string{"https://localhost/callback"})
|
|
defer testutils.CleanupTestClient(t, testClient.ClientID)
|
|
|
|
// Obtain access token with root permissions (required for creating robot members)
|
|
tokenInfo := testutils.ObtainAccessTokenWithRootPermission(t, serverURL, testClient.ClientID, testClient.ClientSecret, "https://localhost/callback", "openid profile")
|
|
|
|
// Use UUID to ensure unique team name
|
|
testUUID := strings.ReplaceAll(uuid.New().String(), "-", "")[:8]
|
|
|
|
// Create a test team
|
|
createdTeam := createTestTeam(t, serverURL, baseURL, tokenInfo.AccessToken, "Robot Member Test Team "+testUUID)
|
|
teamID := getTeamID(createdTeam)
|
|
|
|
testCases := []struct {
|
|
name string
|
|
teamID string
|
|
body map[string]interface{}
|
|
headers map[string]string
|
|
expectCode int
|
|
expectMsg string
|
|
}{
|
|
{
|
|
"create robot without authentication",
|
|
teamID,
|
|
map[string]interface{}{
|
|
"name": "Test Robot",
|
|
"email": "robot@test.com",
|
|
"role": "member",
|
|
"prompt": "You are a helpful assistant",
|
|
},
|
|
map[string]string{},
|
|
401,
|
|
"should require authentication",
|
|
},
|
|
{
|
|
"create robot with all fields",
|
|
teamID,
|
|
map[string]interface{}{
|
|
"name": "AI Assistant Full",
|
|
"email": fmt.Sprintf("ai-full-%s@test.com", testUUID),
|
|
"bio": "A comprehensive AI assistant",
|
|
"role": "member",
|
|
"report_to": tokenInfo.UserID,
|
|
"prompt": "You are a helpful AI assistant with full capabilities",
|
|
"llm": "gpt-4",
|
|
"agents": []string{"data-analyst", "code-reviewer"},
|
|
"mcp_tools": []string{"filesystem", "database"},
|
|
"autonomous_mode": "enabled",
|
|
"cost_limit": 100.50,
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
201,
|
|
"should create robot with all fields successfully",
|
|
},
|
|
{
|
|
"create robot with required fields only",
|
|
teamID,
|
|
map[string]interface{}{
|
|
"name": "AI Assistant Min",
|
|
"email": fmt.Sprintf("ai-min-%s@test.com", testUUID),
|
|
"role": "member",
|
|
"prompt": "You are a basic assistant",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
201,
|
|
"should create robot with required fields only",
|
|
},
|
|
{
|
|
"create robot with autonomous_mode variations",
|
|
teamID,
|
|
map[string]interface{}{
|
|
"name": "AI Assistant Auto",
|
|
"email": fmt.Sprintf("ai-auto-%s@test.com", testUUID),
|
|
"role": "member",
|
|
"prompt": "You are an autonomous assistant",
|
|
"autonomous_mode": "1", // Test numeric string
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
201,
|
|
"should handle autonomous_mode=1",
|
|
},
|
|
{
|
|
"create robot with disabled autonomous_mode",
|
|
teamID,
|
|
map[string]interface{}{
|
|
"name": "AI Assistant Manual",
|
|
"email": fmt.Sprintf("ai-manual-%s@test.com", testUUID),
|
|
"role": "member",
|
|
"prompt": "You are a manual assistant",
|
|
"autonomous_mode": "disabled",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
201,
|
|
"should handle autonomous_mode=disabled",
|
|
},
|
|
{
|
|
"create robot without name",
|
|
teamID,
|
|
map[string]interface{}{
|
|
"email": "no-name@test.com",
|
|
"role": "member",
|
|
"prompt": "You are an assistant",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
400,
|
|
"should require name",
|
|
},
|
|
{
|
|
"create robot without email",
|
|
teamID,
|
|
map[string]interface{}{
|
|
"name": "No Email Robot",
|
|
"role": "member",
|
|
"prompt": "You are an assistant",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
400,
|
|
"should require email",
|
|
},
|
|
{
|
|
"create robot without role",
|
|
teamID,
|
|
map[string]interface{}{
|
|
"name": "No Role Robot",
|
|
"email": "no-role@test.com",
|
|
"prompt": "You are an assistant",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
400,
|
|
"should require role",
|
|
},
|
|
{
|
|
"create robot without prompt",
|
|
teamID,
|
|
map[string]interface{}{
|
|
"name": "No Prompt Robot",
|
|
"email": "no-prompt@test.com",
|
|
"role": "member",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
400,
|
|
"should require prompt",
|
|
},
|
|
{
|
|
"create robot with duplicate email",
|
|
teamID,
|
|
map[string]interface{}{
|
|
"name": "Duplicate Email Robot",
|
|
"email": fmt.Sprintf("ai-full-%s@test.com", testUUID), // Same as first successful case
|
|
"role": "member",
|
|
"prompt": "You are an assistant",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
409,
|
|
"should reject duplicate email in same team",
|
|
},
|
|
{
|
|
"create robot in non-existent team",
|
|
"non-existent-team-id",
|
|
map[string]interface{}{
|
|
"name": "Robot in Void",
|
|
"email": "void@test.com",
|
|
"role": "member",
|
|
"prompt": "You are lost",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
404,
|
|
"should return not found for non-existent team",
|
|
},
|
|
{
|
|
"create robot with invalid JSON",
|
|
teamID,
|
|
nil, // Will send invalid JSON
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
400,
|
|
"should handle invalid JSON",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
requestURL := serverURL + baseURL + "/user/teams/" + tc.teamID + "/members/robots"
|
|
|
|
var req *http.Request
|
|
var err error
|
|
|
|
if tc.body == nil {
|
|
// Send invalid JSON for invalid JSON test case
|
|
req, err = http.NewRequest("POST", requestURL, bytes.NewBufferString("invalid json"))
|
|
} else {
|
|
bodyBytes, _ := json.Marshal(tc.body)
|
|
req, err = http.NewRequest("POST", requestURL, bytes.NewBuffer(bodyBytes))
|
|
}
|
|
assert.NoError(t, err, "Should create HTTP request")
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
// Add headers
|
|
for key, value := range tc.headers {
|
|
req.Header.Set(key, value)
|
|
}
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
assert.NoError(t, err, "HTTP request should succeed")
|
|
|
|
if resp != nil {
|
|
defer resp.Body.Close()
|
|
assert.Equal(t, tc.expectCode, resp.StatusCode, "Expected status code %d for %s", tc.expectCode, tc.name)
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
assert.NoError(t, err, "Should read response body")
|
|
|
|
if resp.StatusCode == 201 {
|
|
// Parse response as created member
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(body, &response)
|
|
assert.NoError(t, err, "Should parse JSON response")
|
|
|
|
// Verify response structure
|
|
assert.Contains(t, response, "member_id", "Should have member_id")
|
|
assert.NotEmpty(t, response["member_id"], "Member ID should not be empty")
|
|
|
|
// Verify the member was created with correct type
|
|
memberID := toString(response["member_id"])
|
|
getMemberURL := serverURL + baseURL + "/user/teams/" + tc.teamID + "/members/" + memberID
|
|
getReq, _ := http.NewRequest("GET", getMemberURL, nil)
|
|
getReq.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
|
|
|
getResp, err := client.Do(getReq)
|
|
if err == nil && getResp != nil {
|
|
defer getResp.Body.Close()
|
|
if getResp.StatusCode == 200 {
|
|
var member map[string]interface{}
|
|
getBody, _ := io.ReadAll(getResp.Body)
|
|
json.Unmarshal(getBody, &member)
|
|
|
|
// Verify robot member fields
|
|
assert.Equal(t, "robot", member["member_type"], "Should be robot member type")
|
|
if tc.body["name"] != nil {
|
|
assert.Equal(t, tc.body["name"], member["display_name"], "Should have correct display_name")
|
|
}
|
|
if tc.body["email"] != nil {
|
|
assert.Equal(t, tc.body["email"], member["email"], "Should have correct email")
|
|
}
|
|
if tc.body["prompt"] != nil {
|
|
assert.Equal(t, tc.body["prompt"], member["system_prompt"], "Should have correct system_prompt")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
t.Logf("Robot member create test %s: status=%d, body=%s", tc.name, resp.StatusCode, string(body))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// toString converts interface{} to string for test assertions
|
|
func toString(v interface{}) string {
|
|
switch val := v.(type) {
|
|
case string:
|
|
return val
|
|
case float64:
|
|
return fmt.Sprintf("%.0f", val)
|
|
case int:
|
|
return fmt.Sprintf("%d", val)
|
|
case int64:
|
|
return fmt.Sprintf("%d", val)
|
|
default:
|
|
return fmt.Sprintf("%v", val)
|
|
}
|
|
}
|
|
|
|
// TestMemberCheckEmail tests the GET /user/teams/:team_id/members/check endpoint
|
|
func TestMemberCheckEmail(t *testing.T) {
|
|
// Initialize test environment
|
|
serverURL := testutils.Prepare(t)
|
|
defer testutils.Clean()
|
|
|
|
// Get base URL from server config
|
|
baseURL := ""
|
|
if openapi.Server != nil && openapi.Server.Config != nil {
|
|
baseURL = openapi.Server.Config.BaseURL
|
|
}
|
|
|
|
// Register a test client for OAuth authentication
|
|
testClient := testutils.RegisterTestClient(t, "Member Check Email Test Client", []string{"https://localhost/callback"})
|
|
defer testutils.CleanupTestClient(t, testClient.ClientID)
|
|
|
|
// Obtain access token for authenticated requests
|
|
tokenInfo := testutils.ObtainAccessToken(t, serverURL, testClient.ClientID, testClient.ClientSecret, "https://localhost/callback", "openid profile")
|
|
|
|
// Use UUID to ensure unique test data
|
|
testUUID := strings.ReplaceAll(uuid.New().String(), "-", "")[:8]
|
|
|
|
// Create a test team
|
|
createdTeam := createTestTeam(t, serverURL, baseURL, tokenInfo.AccessToken, "Email Check Test Team "+testUUID)
|
|
teamID := getTeamID(createdTeam)
|
|
|
|
// Create a robot member with a known email
|
|
existingEmail := fmt.Sprintf("existing-robot-%s@test.com", testUUID)
|
|
robotBody := map[string]interface{}{
|
|
"name": "Existing Robot",
|
|
"email": existingEmail,
|
|
"role": "member",
|
|
"prompt": "You are a test robot",
|
|
}
|
|
robotBodyBytes, _ := json.Marshal(robotBody)
|
|
robotReq, _ := http.NewRequest("POST", serverURL+baseURL+"/user/teams/"+teamID+"/members/robots", bytes.NewBuffer(robotBodyBytes))
|
|
robotReq.Header.Set("Content-Type", "application/json")
|
|
robotReq.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
|
client := &http.Client{}
|
|
robotResp, err := client.Do(robotReq)
|
|
assert.NoError(t, err)
|
|
if robotResp != nil {
|
|
robotResp.Body.Close()
|
|
assert.Equal(t, 201, robotResp.StatusCode, "Should create robot member successfully")
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
teamID string
|
|
email string
|
|
headers map[string]string
|
|
expectCode int
|
|
expectExists bool
|
|
expectMsg string
|
|
}{
|
|
{
|
|
"check email without authentication",
|
|
teamID,
|
|
existingEmail,
|
|
map[string]string{},
|
|
401,
|
|
false,
|
|
"should require authentication",
|
|
},
|
|
{
|
|
"check existing email",
|
|
teamID,
|
|
existingEmail,
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
true,
|
|
"should return exists=true for existing email",
|
|
},
|
|
{
|
|
"check non-existing email",
|
|
teamID,
|
|
fmt.Sprintf("nonexistent-%s@test.com", testUUID),
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
false,
|
|
"should return exists=false for non-existing email",
|
|
},
|
|
{
|
|
"check email without email parameter",
|
|
teamID,
|
|
"",
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
400,
|
|
false,
|
|
"should require email parameter",
|
|
},
|
|
{
|
|
"check email in non-existent team",
|
|
"non-existent-team-id",
|
|
"test@example.com",
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
404,
|
|
false,
|
|
"should return not found for non-existent team",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
requestURL := serverURL + baseURL + "/user/teams/" + tc.teamID + "/members/check"
|
|
if tc.email != "" {
|
|
requestURL += "?email=" + tc.email
|
|
}
|
|
|
|
req, err := http.NewRequest("GET", requestURL, nil)
|
|
assert.NoError(t, err, "Should create HTTP request")
|
|
|
|
// Add headers
|
|
for key, value := range tc.headers {
|
|
req.Header.Set(key, value)
|
|
}
|
|
|
|
resp, err := client.Do(req)
|
|
assert.NoError(t, err, "HTTP request should succeed")
|
|
|
|
if resp != nil {
|
|
defer resp.Body.Close()
|
|
assert.Equal(t, tc.expectCode, resp.StatusCode, "Expected status code %d for %s", tc.expectCode, tc.name)
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
assert.NoError(t, err, "Should read response body")
|
|
|
|
if resp.StatusCode == 200 {
|
|
// Parse response
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(body, &response)
|
|
assert.NoError(t, err, "Should parse JSON response")
|
|
|
|
// Verify response structure
|
|
assert.Contains(t, response, "exists", "Should have exists field")
|
|
assert.Contains(t, response, "email", "Should have email field")
|
|
assert.Contains(t, response, "team_id", "Should have team_id field")
|
|
|
|
// Verify values
|
|
assert.Equal(t, tc.expectExists, response["exists"], "Should have correct exists value")
|
|
assert.Equal(t, tc.email, response["email"], "Should have correct email")
|
|
assert.Equal(t, teamID, response["team_id"], "Should have correct team_id")
|
|
}
|
|
|
|
t.Logf("Member check email test %s: status=%d, body=%s", tc.name, resp.StatusCode, string(body))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Note: getTeamID function is already defined in team_test.go
|