Restructure the monorepo into clear top-level directories: - backend/: Go module root (cmd, internal, pkg, configs, migrations, docs/swagger, scripts, tests, go.mod, Makefile, .air.toml) - deploy/: Docker (Dockerfile, docker-compose*), quickstart, fluentd - docs/: project documentation + reports/ (moved from repo root) - AGENTS.md: new AI coding-agent guide at repo root Update all references to the new layout: - Dockerfile: COPY backend/go.mod, COPY backend/ (context = repo root) - docker-compose files: context ../.., dockerfile deploy/docker/Dockerfile, env_file ../../.env, volume mounts ../../backend:/app - deploy/quickstart/compose.yaml: dockerfile deploy/docker/Dockerfile - CI: working-directory: backend for go commands, file deploy/docker/Dockerfile, coverage path backend/coverage.out, health_check backend/scripts/ - backend/Makefile: docker target uses -f ../deploy/docker/Dockerfile ../ - README: architecture tree, quickstart, config paths updated Move root stray scripts (rename_models.*, run_m11_tests.sh, verify_build.sh, gorm_bool_main.go) to backend/scripts/legacy/. All moves via git mv to preserve history. Build, vet, SQLite tests, and docker compose config verified.
517 lines
19 KiB
Go
517 lines
19 KiB
Go
package v1_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/gochat/gochat/internal/handler/api/v1"
|
|
"github.com/gochat/gochat/internal/model"
|
|
"github.com/gochat/gochat/internal/repository"
|
|
"github.com/gochat/gochat/internal/service"
|
|
"github.com/gochat/gochat/pkg/testutil"
|
|
)
|
|
|
|
// unpackData extracts the "data" field from GoChat's standard API envelope:
|
|
// {"success": true, "data": {...}}
|
|
func unpackData(t *testing.T, body []byte) map[string]interface{} {
|
|
t.Helper()
|
|
var envelope map[string]interface{}
|
|
err := json.Unmarshal(body, &envelope)
|
|
require.NoError(t, err, "response is not valid JSON: %s", string(body))
|
|
data, ok := envelope["data"]
|
|
require.True(t, ok, "response has no 'data' field: %s", string(body))
|
|
dataMap, ok := data.(map[string]interface{})
|
|
require.True(t, ok, "'data' is not a map: %v", data)
|
|
return dataMap
|
|
}
|
|
|
|
func unpackRawObject(t *testing.T, body []byte) map[string]interface{} {
|
|
t.Helper()
|
|
var data map[string]interface{}
|
|
err := json.Unmarshal(body, &data)
|
|
require.NoError(t, err, "response is not valid JSON: %s", string(body))
|
|
return data
|
|
}
|
|
|
|
// parseID extracts the numeric "id" from a data envelope and returns it as a string for URL paths.
|
|
func parseID(t *testing.T, data map[string]interface{}) string {
|
|
t.Helper()
|
|
id := data["id"]
|
|
require.NotNil(t, id, "data has no 'id' field")
|
|
return fmt.Sprint(uint(id.(float64)))
|
|
}
|
|
|
|
func setupPlatformTokenTestE2E(t *testing.T) (*gin.Engine, *repository.PermissibleRepo, *repository.UserRepo, *repository.AccountRepo) {
|
|
t.Helper()
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
db := testutil.NewTestDBWithModels(t,
|
|
&model.User{}, &model.Account{}, &model.AgentBot{},
|
|
&model.AgentBotInbox{}, &model.Inbox{},
|
|
&model.Permissible{}, &model.AccessToken{}, &model.PlatformApp{},
|
|
&model.AccountUser{},
|
|
)
|
|
|
|
userRepo := repository.NewUserRepo(db)
|
|
accountRepo := repository.NewAccountRepo(db)
|
|
accountUserRepo := repository.NewAccountUserRepo(db)
|
|
accessTokenRepo := repository.NewAccessTokenRepo(db)
|
|
agentBotRepo := repository.NewAgentBotRepo(db)
|
|
permissibleRepo := repository.NewPermissibleRepo(db)
|
|
accountService := service.NewAccountService(accountRepo)
|
|
platformUserService := service.NewPlatformUserService(userRepo, permissibleRepo, accessTokenRepo, accountUserRepo)
|
|
|
|
platformUser := v1.NewPlatformUserHandler(platformUserService)
|
|
platformAccount := v1.NewPlatformAccountHandler(accountRepo, permissibleRepo, accountService)
|
|
platformAgentBot := v1.NewPlatformAgentBotHandler(agentBotRepo, permissibleRepo)
|
|
platformAccountUser := v1.NewPlatformAccountUserHandler(accountRepo, userRepo, permissibleRepo)
|
|
|
|
engine := gin.New()
|
|
platformGroup := engine.Group("/platform/api/v1")
|
|
platformGroup.Use(func(c *gin.Context) {
|
|
c.Set("platform_app_id", uint(1))
|
|
c.Set("access_token_owner_type", "PlatformApp")
|
|
c.Next()
|
|
})
|
|
|
|
// Register routes (mirrors registerPlatformTokenRoutes in router.go)
|
|
platformGroup.GET("/users", platformUser.List)
|
|
platformGroup.GET("/users/:id", platformUser.Show)
|
|
platformGroup.POST("/users", platformUser.Create)
|
|
platformGroup.GET("/users/:id/login", platformUser.Login)
|
|
platformGroup.POST("/users/:id/login", platformUser.Login)
|
|
platformGroup.POST("/users/:id/token", platformUser.Token)
|
|
platformGroup.PATCH("/users/:id", platformUser.Update)
|
|
platformGroup.DELETE("/users/:id", platformUser.Destroy)
|
|
|
|
platformGroup.GET("/accounts", platformAccount.List)
|
|
platformGroup.GET("/accounts/:id", platformAccount.Show)
|
|
platformGroup.POST("/accounts", platformAccount.Create)
|
|
platformGroup.PATCH("/accounts/:id", platformAccount.Update)
|
|
platformGroup.DELETE("/accounts/:id", platformAccount.Destroy)
|
|
|
|
platformGroup.GET("/agent_bots", platformAgentBot.List)
|
|
platformGroup.GET("/agent_bots/:id", platformAgentBot.Show)
|
|
platformGroup.POST("/agent_bots", platformAgentBot.Create)
|
|
platformGroup.PUT("/agent_bots/:id", platformAgentBot.Update)
|
|
platformGroup.DELETE("/agent_bots/:id", platformAgentBot.Destroy)
|
|
platformGroup.POST("/agent_bots/:id/delete_avatar", platformAgentBot.DeleteAvatar)
|
|
|
|
// Gin wildcard constraint: nested routes under accounts/:id must reuse :id.
|
|
platformGroup.GET("/accounts/:id/account_users", platformAccountUser.Index)
|
|
platformGroup.POST("/accounts/:id/account_users", platformAccountUser.Create)
|
|
platformGroup.DELETE("/accounts/:id/account_users/destroy", platformAccountUser.Destroy)
|
|
platformGroup.DELETE("/accounts/:id/account_users/:user_id", platformAccountUser.Destroy)
|
|
|
|
return engine, permissibleRepo, userRepo, accountRepo
|
|
}
|
|
|
|
// --- Platform User E2E Tests ---
|
|
|
|
func TestPlatformUserE2E_Create(t *testing.T) {
|
|
engine, _, _, _ := setupPlatformTokenTestE2E(t)
|
|
|
|
body := `{"name": "Test User", "email": "test@example.com", "password": "secret123"}`
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/platform/api/v1/users", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
data := unpackRawObject(t, w.Body.Bytes())
|
|
assert.Equal(t, "Test User", data["name"])
|
|
assert.Equal(t, "test@example.com", data["email"])
|
|
assert.NotEmpty(t, data["access_token"])
|
|
assert.Contains(t, data, "accounts")
|
|
}
|
|
|
|
func TestPlatformUserE2E_CreateExistingUserReturnsExistingAndPermits(t *testing.T) {
|
|
engine, permissibleRepo, userRepo, _ := setupPlatformTokenTestE2E(t)
|
|
ctx := t.Context()
|
|
existing := &model.User{Name: "Old Name", Email: "existing@example.com", Provider: "email", Active: true}
|
|
require.NoError(t, userRepo.Create(ctx, existing))
|
|
|
|
body := `{"name": "New Name", "email": "existing@example.com", "password": "secret123"}`
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/platform/api/v1/users", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
data := unpackRawObject(t, w.Body.Bytes())
|
|
assert.Equal(t, "Old Name", data["name"])
|
|
assert.Equal(t, float64(existing.ID), data["id"])
|
|
_, err := permissibleRepo.FindByPlatformAppAndResource(ctx, uint(1), model.PermissibleTypeUser, existing.ID)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestPlatformUserE2E_Show(t *testing.T) {
|
|
engine, _, _, _ := setupPlatformTokenTestE2E(t)
|
|
|
|
// Create user first
|
|
body := `{"name": "Show User", "email": "show@example.com"}`
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/platform/api/v1/users", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
createData := unpackRawObject(t, w.Body.Bytes())
|
|
userID := parseID(t, createData)
|
|
|
|
// Show user
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("GET", "/platform/api/v1/users/"+userID, nil)
|
|
engine.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
showData := unpackRawObject(t, w.Body.Bytes())
|
|
assert.Equal(t, "Show User", showData["name"])
|
|
}
|
|
|
|
func TestPlatformUserE2E_Update(t *testing.T) {
|
|
engine, _, _, _ := setupPlatformTokenTestE2E(t)
|
|
|
|
// Create user
|
|
body := `{"name": "Original", "email": "original@example.com"}`
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/platform/api/v1/users", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
createData := unpackRawObject(t, w.Body.Bytes())
|
|
userID := parseID(t, createData)
|
|
|
|
// Update user
|
|
updateBody := `{"name": "Updated Name", "email": "updated@example.com", "custom_attributes": {"tier": "gold"}}`
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("PATCH", "/platform/api/v1/users/"+userID, bytes.NewBufferString(updateBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
updateData := unpackRawObject(t, w.Body.Bytes())
|
|
assert.Equal(t, "Updated Name", updateData["name"])
|
|
attrs := updateData["custom_attributes"].(map[string]interface{})
|
|
assert.Equal(t, "gold", attrs["tier"])
|
|
}
|
|
|
|
func TestPlatformUserE2E_Destroy(t *testing.T) {
|
|
engine, _, _, _ := setupPlatformTokenTestE2E(t)
|
|
|
|
// Create user
|
|
body := `{"name": "Delete Me", "email": "delete@example.com"}`
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/platform/api/v1/users", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
createData := unpackRawObject(t, w.Body.Bytes())
|
|
userID := parseID(t, createData)
|
|
|
|
// Delete user
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("DELETE", "/platform/api/v1/users/"+userID, nil)
|
|
engine.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
func TestPlatformUserE2E_Login(t *testing.T) {
|
|
engine, _, _, _ := setupPlatformTokenTestE2E(t)
|
|
|
|
// Create user
|
|
body := `{"name": "SSO User", "email": "sso@example.com"}`
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/platform/api/v1/users", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
createData := unpackRawObject(t, w.Body.Bytes())
|
|
userID := parseID(t, createData)
|
|
|
|
// Login
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("GET", "/platform/api/v1/users/"+userID+"/login", nil)
|
|
engine.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
loginData := unpackRawObject(t, w.Body.Bytes())
|
|
assert.Contains(t, loginData["url"], "email=sso%40example.com")
|
|
assert.Contains(t, loginData["url"], "sso_auth_token=")
|
|
}
|
|
|
|
func TestPlatformUserE2E_Token(t *testing.T) {
|
|
engine, _, _, _ := setupPlatformTokenTestE2E(t)
|
|
|
|
// Create user
|
|
body := `{"name": "Token User", "email": "token@example.com"}`
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/platform/api/v1/users", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
createData := unpackRawObject(t, w.Body.Bytes())
|
|
userID := parseID(t, createData)
|
|
|
|
// Token
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("POST", "/platform/api/v1/users/"+userID+"/token", nil)
|
|
engine.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
tokenData := unpackRawObject(t, w.Body.Bytes())
|
|
assert.NotEmpty(t, tokenData["access_token"])
|
|
assert.Nil(t, tokenData["expiry"])
|
|
userInfo := tokenData["user"].(map[string]interface{})
|
|
assert.Equal(t, "Token User", userInfo["name"])
|
|
}
|
|
|
|
func TestPlatformUserE2E_List(t *testing.T) {
|
|
engine, _, _, _ := setupPlatformTokenTestE2E(t)
|
|
|
|
// Create 2 users
|
|
for i := 0; i < 2; i++ {
|
|
body := fmt.Sprintf(`{"name": "User %d", "email": "list%d@example.com"}`, i, i)
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/platform/api/v1/users", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
// List users
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("GET", "/platform/api/v1/users", nil)
|
|
engine.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
// --- Platform Account E2E Tests ---
|
|
|
|
func TestPlatformAccountE2E_Create(t *testing.T) {
|
|
engine, _, _, _ := setupPlatformTokenTestE2E(t)
|
|
|
|
body := `{"name": "Test Account", "locale": "zh", "timezone": "Asia/Shanghai"}`
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/platform/api/v1/accounts", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
data := unpackData(t, w.Body.Bytes())
|
|
assert.Equal(t, "Test Account", data["name"])
|
|
}
|
|
|
|
func TestPlatformAccountE2E_Show(t *testing.T) {
|
|
engine, _, _, _ := setupPlatformTokenTestE2E(t)
|
|
|
|
// Create account
|
|
body := `{"name": "Show Account"}`
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/platform/api/v1/accounts", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
require.Equal(t, http.StatusCreated, w.Code)
|
|
createData := unpackData(t, w.Body.Bytes())
|
|
accountID := parseID(t, createData)
|
|
|
|
// Show account (permissible since Create auto-creates Permissible)
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("GET", "/platform/api/v1/accounts/"+accountID, nil)
|
|
engine.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
showData := unpackData(t, w.Body.Bytes())
|
|
assert.Equal(t, "Show Account", showData["name"])
|
|
}
|
|
|
|
func TestPlatformAccountE2E_Destroy(t *testing.T) {
|
|
engine, _, _, _ := setupPlatformTokenTestE2E(t)
|
|
|
|
// Create account
|
|
body := `{"name": "Delete Account"}`
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/platform/api/v1/accounts", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
require.Equal(t, http.StatusCreated, w.Code)
|
|
createData := unpackData(t, w.Body.Bytes())
|
|
accountID := parseID(t, createData)
|
|
|
|
// Delete
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("DELETE", "/platform/api/v1/accounts/"+accountID, nil)
|
|
engine.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusNoContent, w.Code)
|
|
}
|
|
|
|
// --- Platform Agent Bot E2E Tests ---
|
|
|
|
func TestPlatformAgentBotE2E_Create(t *testing.T) {
|
|
engine, _, _, _ := setupPlatformTokenTestE2E(t)
|
|
|
|
body := `{"name": "Test Bot", "description": "A test bot"}`
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/platform/api/v1/agent_bots", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
data := unpackData(t, w.Body.Bytes())
|
|
assert.Equal(t, "Test Bot", data["name"])
|
|
}
|
|
|
|
func TestPlatformAgentBotE2E_Show(t *testing.T) {
|
|
engine, _, _, _ := setupPlatformTokenTestE2E(t)
|
|
|
|
// Create bot
|
|
body := `{"name": "Show Bot", "description": "A show bot"}`
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/platform/api/v1/agent_bots", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
require.Equal(t, http.StatusCreated, w.Code)
|
|
createData := unpackData(t, w.Body.Bytes())
|
|
botID := parseID(t, createData)
|
|
|
|
// Show bot
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("GET", "/platform/api/v1/agent_bots/"+botID, nil)
|
|
engine.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
showData := unpackData(t, w.Body.Bytes())
|
|
assert.Equal(t, "Show Bot", showData["name"])
|
|
}
|
|
|
|
func TestPlatformAgentBotE2E_DeleteAvatar(t *testing.T) {
|
|
engine, _, _, _ := setupPlatformTokenTestE2E(t)
|
|
|
|
// Create bot
|
|
body := `{"name": "Avatar Bot"}`
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/platform/api/v1/agent_bots", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
require.Equal(t, http.StatusCreated, w.Code)
|
|
createData := unpackData(t, w.Body.Bytes())
|
|
botID := parseID(t, createData)
|
|
|
|
// Delete avatar
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("POST", "/platform/api/v1/agent_bots/"+botID+"/delete_avatar", nil)
|
|
engine.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
// --- Platform AccountUser E2E Tests ---
|
|
|
|
func TestPlatformAccountUserE2E_Create(t *testing.T) {
|
|
engine, _, _, _ := setupPlatformTokenTestE2E(t)
|
|
|
|
// Create account via Platform API (auto-permissible)
|
|
acctBody := `{"name": "AcctUser Test Account"}`
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/platform/api/v1/accounts", bytes.NewBufferString(acctBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
require.Equal(t, http.StatusCreated, w.Code)
|
|
acctData := unpackData(t, w.Body.Bytes())
|
|
accountID := parseID(t, acctData)
|
|
|
|
// Create user via Platform API (auto-permissible)
|
|
userBody := `{"name": "AcctUser Test User", "email": "acctuser@example.com"}`
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("POST", "/platform/api/v1/users", bytes.NewBufferString(userBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
userData := unpackRawObject(t, w.Body.Bytes())
|
|
userID := parseID(t, userData)
|
|
|
|
// Create AccountUser
|
|
acctUserBody := fmt.Sprintf(`{"user_id": %s, "role": "agent"}`, userID)
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("POST", "/platform/api/v1/accounts/"+accountID+"/account_users", bytes.NewBufferString(acctUserBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
var accountUser map[string]interface{}
|
|
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &accountUser))
|
|
assert.Equal(t, "agent", accountUser["role"])
|
|
assert.NotContains(t, accountUser, "success")
|
|
|
|
// Chatwoot find_or_initialize_by updates existing memberships instead of failing duplicates.
|
|
acctUserBody = fmt.Sprintf(`{"user_id": %s, "role": "administrator"}`, userID)
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("POST", "/platform/api/v1/accounts/"+accountID+"/account_users", bytes.NewBufferString(acctUserBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &accountUser))
|
|
assert.Equal(t, "administrator", accountUser["role"])
|
|
}
|
|
|
|
func TestPlatformAccountUserE2E_Index(t *testing.T) {
|
|
engine, _, _, _ := setupPlatformTokenTestE2E(t)
|
|
|
|
// Create account
|
|
acctBody := `{"name": "Index Account"}`
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/platform/api/v1/accounts", bytes.NewBufferString(acctBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
require.Equal(t, http.StatusCreated, w.Code)
|
|
acctData := unpackData(t, w.Body.Bytes())
|
|
accountID := parseID(t, acctData)
|
|
|
|
// Create user
|
|
userBody := `{"name": "Index User", "email": "index@example.com"}`
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("POST", "/platform/api/v1/users", bytes.NewBufferString(userBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
userData := unpackRawObject(t, w.Body.Bytes())
|
|
userID := parseID(t, userData)
|
|
|
|
// Add user to account
|
|
acctUserBody := fmt.Sprintf(`{"user_id": %s, "role": "agent"}`, userID)
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("POST", "/platform/api/v1/accounts/"+accountID+"/account_users", bytes.NewBufferString(acctUserBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
engine.ServeHTTP(w, req)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
|
|
// Index account_users
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("GET", "/platform/api/v1/accounts/"+accountID+"/account_users", nil)
|
|
engine.ServeHTTP(w, req)
|
|
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
var accountUsers []map[string]interface{}
|
|
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &accountUsers))
|
|
require.Len(t, accountUsers, 1)
|
|
assert.Equal(t, "agent", accountUsers[0]["role"])
|
|
assert.NotContains(t, accountUsers[0], "success")
|
|
|
|
// Chatwoot destroy is a collection route: DELETE /account_users/destroy with user_id param.
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("DELETE", "/platform/api/v1/accounts/"+accountID+"/account_users/destroy?user_id="+userID, nil)
|
|
engine.ServeHTTP(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|