- Enhanced the team creation and update processes to include an optional logo field, allowing teams to have custom logos. - Updated the CreateTeamRequest and UpdateTeamRequest structures to accommodate the new logo field. - Modified the GinTeamCreate and GinTeamUpdate handlers to handle logo data appropriately. - Expanded test cases for team creation and updates to validate logo handling, ensuring comprehensive coverage for scenarios involving logos.
1054 lines
31 KiB
Go
1054 lines
31 KiB
Go
package user_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/yaoapp/yao/openapi"
|
|
"github.com/yaoapp/yao/openapi/tests/testutils"
|
|
)
|
|
|
|
// TestTeamList tests the GET /user/teams endpoint
|
|
func TestTeamList(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, "Team 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
|
|
endpoint string
|
|
headers map[string]string
|
|
expectCode int
|
|
expectMsg string
|
|
}{
|
|
{
|
|
"list teams without authentication",
|
|
"/user/teams",
|
|
map[string]string{},
|
|
401,
|
|
"should require authentication",
|
|
},
|
|
{
|
|
"list teams with valid token",
|
|
"/user/teams",
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should return user teams",
|
|
},
|
|
{
|
|
"list teams with pagination",
|
|
"/user/teams?page=1&pagesize=10",
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should handle pagination parameters",
|
|
},
|
|
{
|
|
"list teams with status filter",
|
|
"/user/teams?status=active",
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should filter by status",
|
|
},
|
|
{
|
|
"list teams with name search",
|
|
"/user/teams?name=test",
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should search by name",
|
|
},
|
|
{
|
|
"list teams with invalid pagesize",
|
|
"/user/teams?pagesize=1000",
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should limit pagesize to maximum allowed",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
requestURL := serverURL + baseURL + tc.endpoint
|
|
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 array (TeamList returns array directly, not paginated)
|
|
var teams []interface{}
|
|
err = json.Unmarshal(body, &teams)
|
|
assert.NoError(t, err, "Should parse JSON response as array")
|
|
|
|
// Verify it's an array
|
|
assert.IsType(t, []interface{}{}, teams, "Response should be an array")
|
|
}
|
|
|
|
t.Logf("Team list test %s: status=%d, body=%s", tc.name, resp.StatusCode, string(body))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestTeamCreate tests the POST /user/teams endpoint
|
|
func TestTeamCreate(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, "Team Create 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
|
|
body map[string]interface{}
|
|
headers map[string]string
|
|
expectCode int
|
|
expectMsg string
|
|
}{
|
|
{
|
|
"create team without authentication",
|
|
map[string]interface{}{
|
|
"name": "Test Team",
|
|
},
|
|
map[string]string{},
|
|
401,
|
|
"should require authentication",
|
|
},
|
|
{
|
|
"create team with valid data",
|
|
map[string]interface{}{
|
|
"name": "Test Team",
|
|
"description": "A test team for unit testing",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
201,
|
|
"should create team successfully",
|
|
},
|
|
{
|
|
"create team with settings",
|
|
map[string]interface{}{
|
|
"name": "Team with Settings",
|
|
"description": "Team with custom settings",
|
|
"settings": map[string]interface{}{
|
|
"theme": "dark",
|
|
"visibility": "private",
|
|
},
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
201,
|
|
"should create team with settings",
|
|
},
|
|
{
|
|
"create team with logo",
|
|
map[string]interface{}{
|
|
"name": "Team with Logo",
|
|
"description": "Team with custom logo",
|
|
"logo": "__yao.attachment://test-logo-123",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
201,
|
|
"should create team with logo",
|
|
},
|
|
{
|
|
"create team without name",
|
|
map[string]interface{}{
|
|
"description": "Team without name",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
400,
|
|
"should require team name",
|
|
},
|
|
{
|
|
"create team with empty name",
|
|
map[string]interface{}{
|
|
"name": "",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
400,
|
|
"should require non-empty team name",
|
|
},
|
|
{
|
|
"create team with invalid JSON",
|
|
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"
|
|
|
|
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 team object
|
|
var team map[string]interface{}
|
|
err = json.Unmarshal(body, &team)
|
|
assert.NoError(t, err, "Should parse JSON response")
|
|
|
|
// Verify team structure
|
|
assert.Contains(t, team, "id", "Should have team ID")
|
|
assert.Contains(t, team, "team_id", "Should have team_id")
|
|
assert.Contains(t, team, "name", "Should have team name")
|
|
assert.Contains(t, team, "owner_id", "Should have owner_id")
|
|
assert.Contains(t, team, "status", "Should have status")
|
|
assert.Contains(t, team, "created_at", "Should have created_at")
|
|
assert.Contains(t, team, "updated_at", "Should have updated_at")
|
|
|
|
// Verify values
|
|
if tc.body != nil {
|
|
if name, ok := tc.body["name"]; ok {
|
|
assert.Equal(t, name, team["name"], "Should have correct team name")
|
|
}
|
|
if description, ok := tc.body["description"]; ok {
|
|
assert.Equal(t, description, team["description"], "Should have correct description")
|
|
}
|
|
if logo, ok := tc.body["logo"]; ok {
|
|
assert.Equal(t, logo, team["logo"], "Should have correct logo")
|
|
}
|
|
}
|
|
}
|
|
|
|
t.Logf("Team create test %s: status=%d, body=%s", tc.name, resp.StatusCode, string(body))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestTeamGet tests the GET /user/teams/:team_id endpoint
|
|
func TestTeamGet(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, "Team 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")
|
|
|
|
// First create a team to test with
|
|
createTeamBody := map[string]interface{}{
|
|
"name": "Get Test Team",
|
|
"description": "Team for testing get functionality",
|
|
"settings": map[string]interface{}{
|
|
"theme": "light",
|
|
},
|
|
}
|
|
|
|
createReq := createTeamRequest(t, serverURL+baseURL+"/user/teams", createTeamBody, tokenInfo.AccessToken)
|
|
createResp, err := (&http.Client{}).Do(createReq)
|
|
assert.NoError(t, err, "Should create test team")
|
|
defer createResp.Body.Close()
|
|
|
|
var createdTeam map[string]interface{}
|
|
if createResp.StatusCode == 201 {
|
|
createBody, _ := io.ReadAll(createResp.Body)
|
|
json.Unmarshal(createBody, &createdTeam)
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
teamID string
|
|
headers map[string]string
|
|
expectCode int
|
|
expectMsg string
|
|
}{
|
|
{
|
|
"get team without authentication",
|
|
getTeamID(createdTeam),
|
|
map[string]string{},
|
|
401,
|
|
"should require authentication",
|
|
},
|
|
{
|
|
"get existing team",
|
|
getTeamID(createdTeam),
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should return team details",
|
|
},
|
|
{
|
|
"get non-existent team",
|
|
"non-existent-team-id",
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
404,
|
|
"should return not found for non-existent team",
|
|
},
|
|
{
|
|
"get team with empty team_id returns team list",
|
|
"",
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should return team list when team_id is empty",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
endpoint := "/user/teams"
|
|
if tc.teamID != "" {
|
|
endpoint += "/" + tc.teamID
|
|
}
|
|
requestURL := serverURL + baseURL + endpoint
|
|
|
|
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 {
|
|
if tc.teamID == "" {
|
|
// Parse response as team list (returns array directly, not paginated)
|
|
var teams []interface{}
|
|
err = json.Unmarshal(body, &teams)
|
|
assert.NoError(t, err, "Should parse JSON response as array")
|
|
|
|
// Verify it's an array
|
|
assert.IsType(t, []interface{}{}, teams, "Response should be an array")
|
|
} else {
|
|
// Parse response as team detail object
|
|
var team map[string]interface{}
|
|
err = json.Unmarshal(body, &team)
|
|
assert.NoError(t, err, "Should parse JSON response")
|
|
|
|
// Verify team detail structure
|
|
assert.Contains(t, team, "id", "Should have team ID")
|
|
assert.Contains(t, team, "team_id", "Should have team_id")
|
|
assert.Contains(t, team, "name", "Should have team name")
|
|
assert.Contains(t, team, "description", "Should have description")
|
|
// Logo field is optional, only present if set
|
|
assert.Contains(t, team, "owner_id", "Should have owner_id")
|
|
assert.Contains(t, team, "status", "Should have status")
|
|
assert.Contains(t, team, "settings", "Should have settings")
|
|
assert.Contains(t, team, "created_at", "Should have created_at")
|
|
assert.Contains(t, team, "updated_at", "Should have updated_at")
|
|
|
|
// Verify values match created team
|
|
assert.Equal(t, "Get Test Team", team["name"], "Should have correct team name")
|
|
assert.Equal(t, "Team for testing get functionality", team["description"], "Should have correct description")
|
|
if settings, ok := team["settings"].(map[string]interface{}); ok {
|
|
assert.Equal(t, "light", settings["theme"], "Should have correct theme setting")
|
|
}
|
|
}
|
|
}
|
|
|
|
t.Logf("Team get test %s: status=%d, body=%s", tc.name, resp.StatusCode, string(body))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestTeamUpdate tests the PUT /user/teams/:team_id endpoint
|
|
func TestTeamUpdate(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, "Team 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")
|
|
|
|
// Create a team to test updates
|
|
createTeamBody := map[string]interface{}{
|
|
"name": "Update Test Team",
|
|
"description": "Team for testing update functionality",
|
|
}
|
|
|
|
createReq := createTeamRequest(t, serverURL+baseURL+"/user/teams", createTeamBody, tokenInfo.AccessToken)
|
|
createResp, err := (&http.Client{}).Do(createReq)
|
|
assert.NoError(t, err, "Should create test team")
|
|
defer createResp.Body.Close()
|
|
|
|
var createdTeam map[string]interface{}
|
|
if createResp.StatusCode == 201 {
|
|
createBody, _ := io.ReadAll(createResp.Body)
|
|
json.Unmarshal(createBody, &createdTeam)
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
teamID string
|
|
body map[string]interface{}
|
|
headers map[string]string
|
|
expectCode int
|
|
expectMsg string
|
|
}{
|
|
{
|
|
"update team without authentication",
|
|
getTeamID(createdTeam),
|
|
map[string]interface{}{
|
|
"name": "Updated Name",
|
|
},
|
|
map[string]string{},
|
|
401,
|
|
"should require authentication",
|
|
},
|
|
{
|
|
"update team name",
|
|
getTeamID(createdTeam),
|
|
map[string]interface{}{
|
|
"name": "Updated Team Name",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should update team name",
|
|
},
|
|
{
|
|
"update team description",
|
|
getTeamID(createdTeam),
|
|
map[string]interface{}{
|
|
"description": "Updated description",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should update team description",
|
|
},
|
|
{
|
|
"update team settings",
|
|
getTeamID(createdTeam),
|
|
map[string]interface{}{
|
|
"settings": map[string]interface{}{
|
|
"theme": "dark",
|
|
"visibility": "public",
|
|
},
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should update team settings",
|
|
},
|
|
{
|
|
"update team logo",
|
|
getTeamID(createdTeam),
|
|
map[string]interface{}{
|
|
"logo": "__yao.attachment://updated-logo-456",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should update team logo",
|
|
},
|
|
{
|
|
"update non-existent team",
|
|
"non-existent-team-id",
|
|
map[string]interface{}{
|
|
"name": "Updated Name",
|
|
},
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
404,
|
|
"should return not found for non-existent team",
|
|
},
|
|
{
|
|
"update team with invalid JSON",
|
|
getTeamID(createdTeam),
|
|
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
|
|
|
|
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 updated team object
|
|
var team map[string]interface{}
|
|
err = json.Unmarshal(body, &team)
|
|
assert.NoError(t, err, "Should parse JSON response")
|
|
|
|
// Verify updated values
|
|
if tc.body != nil {
|
|
if name, ok := tc.body["name"]; ok {
|
|
assert.Equal(t, name, team["name"], "Should have updated team name")
|
|
}
|
|
if description, ok := tc.body["description"]; ok {
|
|
assert.Equal(t, description, team["description"], "Should have updated description")
|
|
}
|
|
if logo, ok := tc.body["logo"]; ok {
|
|
assert.Equal(t, logo, team["logo"], "Should have updated logo")
|
|
}
|
|
if settings, ok := tc.body["settings"]; ok {
|
|
assert.Equal(t, settings, team["settings"], "Should have updated settings")
|
|
}
|
|
}
|
|
}
|
|
|
|
t.Logf("Team update test %s: status=%d, body=%s", tc.name, resp.StatusCode, string(body))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestTeamDelete tests the DELETE /user/teams/:team_id endpoint
|
|
func TestTeamDelete(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, "Team 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 to test deletion
|
|
createTeam := func(name string) map[string]interface{} {
|
|
createTeamBody := map[string]interface{}{
|
|
"name": name,
|
|
"description": "Team for testing delete functionality",
|
|
}
|
|
|
|
createReq := createTeamRequest(t, serverURL+baseURL+"/user/teams", createTeamBody, tokenInfo.AccessToken)
|
|
createResp, err := (&http.Client{}).Do(createReq)
|
|
assert.NoError(t, err, "Should create test team")
|
|
defer createResp.Body.Close()
|
|
|
|
var createdTeam map[string]interface{}
|
|
if createResp.StatusCode == 201 {
|
|
createBody, _ := io.ReadAll(createResp.Body)
|
|
json.Unmarshal(createBody, &createdTeam)
|
|
}
|
|
return createdTeam
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
teamID string
|
|
headers map[string]string
|
|
expectCode int
|
|
expectMsg string
|
|
}{
|
|
{
|
|
"delete team without authentication",
|
|
getTeamID(createTeam("Delete Test Team 1")),
|
|
map[string]string{},
|
|
401,
|
|
"should require authentication",
|
|
},
|
|
{
|
|
"delete existing team",
|
|
getTeamID(createTeam("Delete Test Team 2")),
|
|
map[string]string{
|
|
"Authorization": "Bearer " + tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"should delete team successfully",
|
|
},
|
|
{
|
|
"delete 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
|
|
|
|
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, "Team deleted successfully", response["message"], "Should have correct success message")
|
|
}
|
|
|
|
t.Logf("Team delete test %s: status=%d, body=%s", tc.name, resp.StatusCode, string(body))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestTeamAuthenticationEdgeCases tests authentication and authorization edge cases
|
|
func TestTeamAuthenticationEdgeCases(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, "Team Auth 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
|
|
endpoint string
|
|
method string
|
|
headers map[string]string
|
|
expectCode int
|
|
expectMsg string
|
|
}{
|
|
{
|
|
"invalid bearer token format",
|
|
"/user/teams",
|
|
"GET",
|
|
map[string]string{
|
|
"Authorization": "Bearer invalid-token",
|
|
},
|
|
401,
|
|
"should reject invalid token",
|
|
},
|
|
{
|
|
"missing bearer prefix",
|
|
"/user/teams",
|
|
"GET",
|
|
map[string]string{
|
|
"Authorization": tokenInfo.AccessToken,
|
|
},
|
|
200,
|
|
"may accept token without Bearer prefix (implementation dependent)",
|
|
},
|
|
{
|
|
"expired token simulation",
|
|
"/user/teams",
|
|
"GET",
|
|
map[string]string{
|
|
"Authorization": "Bearer expired.token.here",
|
|
},
|
|
401,
|
|
"should reject expired token",
|
|
},
|
|
{
|
|
"malformed authorization header",
|
|
"/user/teams",
|
|
"GET",
|
|
map[string]string{
|
|
"Authorization": "Malformed",
|
|
},
|
|
401,
|
|
"should reject malformed header",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
requestURL := serverURL + baseURL + tc.endpoint
|
|
|
|
req, err := http.NewRequest(tc.method, 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")
|
|
|
|
t.Logf("Auth edge case test %s: status=%d, body=%s", tc.name, resp.StatusCode, string(body))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestTeamDeleteMemberCleanup tests that team deletion removes all team members
|
|
func TestTeamDeleteMemberCleanup(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, "Team Delete Member 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 team
|
|
createTeamBody := map[string]interface{}{
|
|
"name": "Delete Member Test Team",
|
|
"description": "Team for testing member cleanup on deletion",
|
|
}
|
|
|
|
createReq := createTeamRequest(t, serverURL+baseURL+"/user/teams", createTeamBody, tokenInfo.AccessToken)
|
|
createResp, err := (&http.Client{}).Do(createReq)
|
|
assert.NoError(t, err, "Should create test team")
|
|
defer createResp.Body.Close()
|
|
|
|
var createdTeam map[string]interface{}
|
|
if createResp.StatusCode == 201 {
|
|
createBody, _ := io.ReadAll(createResp.Body)
|
|
json.Unmarshal(createBody, &createdTeam)
|
|
|
|
teamID := getTeamID(createdTeam)
|
|
assert.NotEmpty(t, teamID, "Should have team ID")
|
|
|
|
t.Logf("Created team: %s (ID: %s)", createdTeam["name"], teamID)
|
|
|
|
// Note: Team creation automatically adds the creator as owner member
|
|
// We can't easily verify member existence without member API endpoints
|
|
// But we can verify that deletion completes successfully
|
|
|
|
// Delete the team
|
|
deleteReq, err := http.NewRequest("DELETE", serverURL+baseURL+"/user/teams/"+teamID, nil)
|
|
assert.NoError(t, err, "Should create delete request")
|
|
deleteReq.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
|
|
|
deleteResp, err := (&http.Client{}).Do(deleteReq)
|
|
assert.NoError(t, err, "Should send delete request")
|
|
defer deleteResp.Body.Close()
|
|
|
|
assert.Equal(t, 200, deleteResp.StatusCode, "Should successfully delete team")
|
|
|
|
deleteBody, err := io.ReadAll(deleteResp.Body)
|
|
assert.NoError(t, err, "Should read delete response")
|
|
|
|
var deleteResponse map[string]interface{}
|
|
err = json.Unmarshal(deleteBody, &deleteResponse)
|
|
assert.NoError(t, err, "Should parse delete response")
|
|
|
|
assert.Equal(t, "Team deleted successfully", deleteResponse["message"], "Should have success message")
|
|
|
|
t.Logf("Team deletion with member cleanup test passed - team %s deleted successfully", teamID)
|
|
|
|
// Verify team is actually deleted by trying to get it
|
|
getReq, err := http.NewRequest("GET", serverURL+baseURL+"/user/teams/"+teamID, nil)
|
|
assert.NoError(t, err, "Should create get request")
|
|
getReq.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
|
|
|
getResp, err := (&http.Client{}).Do(getReq)
|
|
assert.NoError(t, err, "Should send get request")
|
|
defer getResp.Body.Close()
|
|
|
|
assert.Equal(t, 404, getResp.StatusCode, "Should return 404 for deleted team")
|
|
|
|
} else {
|
|
t.Fatalf("Failed to create team: status=%d", createResp.StatusCode)
|
|
}
|
|
}
|
|
|
|
// TestTeamCreateMembershipVerification tests that team creation automatically adds the creator as owner member
|
|
func TestTeamCreateMembershipVerification(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, "Team Member 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 team
|
|
createTeamBody := map[string]interface{}{
|
|
"name": "Membership Test Team",
|
|
"description": "Team for testing automatic owner membership",
|
|
}
|
|
|
|
createReq := createTeamRequest(t, serverURL+baseURL+"/user/teams", createTeamBody, tokenInfo.AccessToken)
|
|
createResp, err := (&http.Client{}).Do(createReq)
|
|
assert.NoError(t, err, "Should create test team")
|
|
defer createResp.Body.Close()
|
|
|
|
var createdTeam map[string]interface{}
|
|
if createResp.StatusCode == 201 {
|
|
createBody, _ := io.ReadAll(createResp.Body)
|
|
json.Unmarshal(createBody, &createdTeam)
|
|
|
|
// Verify team was created successfully
|
|
assert.Equal(t, "Membership Test Team", createdTeam["name"], "Should have correct team name")
|
|
assert.Equal(t, tokenInfo.UserID, createdTeam["owner_id"], "Should have correct owner_id")
|
|
|
|
t.Logf("Created team: %s (ID: %s, Owner: %s)",
|
|
createdTeam["name"], getTeamID(createdTeam), createdTeam["owner_id"])
|
|
|
|
// Verify that creator is automatically added as owner member
|
|
teamID := getTeamID(createdTeam)
|
|
provider := testutils.GetUserProvider(t)
|
|
|
|
member, err := provider.GetMember(context.Background(), teamID, tokenInfo.UserID)
|
|
if err == nil {
|
|
// Verify member exists and has correct properties
|
|
assert.Equal(t, teamID, member["team_id"], "Member should belong to created team")
|
|
assert.Equal(t, tokenInfo.UserID, member["user_id"], "Member should have correct user_id")
|
|
assert.Equal(t, "active", member["status"], "Member should be active")
|
|
|
|
// Verify is_owner field is set to true
|
|
isOwner := member["is_owner"]
|
|
assert.NotNil(t, isOwner, "is_owner field should be present")
|
|
// Handle different boolean representations from database
|
|
assert.True(t, isOwner == true || isOwner == int64(1) || isOwner == 1,
|
|
"is_owner should be true for team creator, got: %v (type: %T)", isOwner, isOwner)
|
|
|
|
t.Logf("Verified creator is automatically added as owner member with is_owner=true")
|
|
} else {
|
|
t.Logf("Could not verify member (may not be implemented yet): %v", err)
|
|
}
|
|
|
|
t.Logf("Team creation with automatic owner membership test passed")
|
|
} else {
|
|
t.Fatalf("Failed to create team: status=%d", createResp.StatusCode)
|
|
}
|
|
}
|
|
|
|
// Helper functions
|
|
|
|
// createTeamRequest creates a POST request for team creation
|
|
func createTeamRequest(t *testing.T, url string, body map[string]interface{}, accessToken string) *http.Request {
|
|
bodyBytes, err := json.Marshal(body)
|
|
assert.NoError(t, err, "Should marshal team creation body")
|
|
|
|
req, err := http.NewRequest("POST", url, 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)
|
|
|
|
return req
|
|
}
|
|
|
|
// getTeamID extracts team_id from created team response
|
|
func getTeamID(team map[string]interface{}) string {
|
|
if team == nil {
|
|
return ""
|
|
}
|
|
if teamID, ok := team["team_id"].(string); ok {
|
|
return teamID
|
|
}
|
|
// Handle numeric team_id
|
|
if teamID, ok := team["team_id"].(float64); ok {
|
|
return fmt.Sprintf("%.0f", teamID)
|
|
}
|
|
if teamID, ok := team["team_id"].(int64); ok {
|
|
return fmt.Sprintf("%d", teamID)
|
|
}
|
|
if teamID, ok := team["team_id"].(int); ok {
|
|
return fmt.Sprintf("%d", teamID)
|
|
}
|
|
return ""
|
|
}
|