Files
user-system/internal/api/handler/avatar_handler_test.go

152 lines
4.0 KiB
Go

package handler_test
import (
"bytes"
"io"
"mime/multipart"
"net/http"
"os"
"testing"
)
// minimalPNG is a valid 1x1 PNG image
var minimalPNG = []byte{
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xDE, 0x00, 0x00, 0x00,
0x0C, 0x49, 0x44, 0x41, 0x54, 0x08, 0xD7, 0x63, 0xF8, 0xCF, 0xC0, 0x00,
0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x05, 0xFE, 0xD8, 0x00, 0x00, 0x00,
0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82,
}
func buildAvatarUploadRequest(t *testing.T, url, token string, fileBody []byte, filename string) *http.Request {
t.Helper()
var body bytes.Buffer
writer := multipart.NewWriter(&body)
part, err := writer.CreateFormFile("avatar", filename)
if err != nil {
t.Fatalf("create form file failed: %v", err)
}
if _, err := part.Write(fileBody); err != nil {
t.Fatalf("write file body failed: %v", err)
}
if err := writer.Close(); err != nil {
t.Fatalf("close multipart writer failed: %v", err)
}
req, err := http.NewRequest(http.MethodPost, url, &body)
if err != nil {
t.Fatalf("create request failed: %v", err)
}
if token != "" {
req.Header.Set("Authorization", "Bearer "+token)
}
req.Header.Set("Content-Type", writer.FormDataContentType())
return req
}
func TestAvatarHandler_UploadAvatar(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
t.Setenv("BOOTSTRAP_SECRET", "avatar-bootstrap-secret")
adminToken := bootstrapAdmin(server.URL, "avatar-bootstrap-secret", "avataradmin", "avataradmin@test.com", "AdminPass123!")
if adminToken == "" {
t.Fatal("bootstrap admin failed")
}
if ok := registerUser(server.URL, "avataruser", "avataruser@test.com", "UserPass123!"); !ok {
t.Fatal("register user failed")
}
userToken := getToken(server.URL, "avataruser", "UserPass123!")
if userToken == "" {
t.Fatal("get user token failed")
}
tests := []struct {
name string
userID string
token string
fileBody []byte
filename string
wantStatus int
}{
{
name: "admin_upload_for_any_user",
userID: "2",
token: adminToken,
fileBody: minimalPNG,
filename: "avatar.png",
wantStatus: http.StatusForbidden,
},
{
name: "user_upload_own_avatar",
userID: "2",
token: userToken,
fileBody: minimalPNG,
filename: "avatar.png",
wantStatus: http.StatusOK,
},
{
name: "unauthorized",
userID: "1",
token: "",
fileBody: minimalPNG,
filename: "avatar.png",
wantStatus: http.StatusUnauthorized,
},
{
name: "forbidden_cross_user",
userID: "1",
token: userToken,
fileBody: minimalPNG,
filename: "avatar.png",
wantStatus: http.StatusForbidden,
},
{
name: "invalid_user_id",
userID: "invalid",
token: adminToken,
fileBody: minimalPNG,
filename: "avatar.png",
wantStatus: http.StatusBadRequest,
},
{
name: "invalid_file_type",
userID: "1",
token: adminToken,
fileBody: []byte("this is not an image"),
filename: "avatar.txt",
wantStatus: http.StatusBadRequest,
},
{
name: "user_not_found",
userID: "99999",
token: adminToken,
fileBody: minimalPNG,
filename: "avatar.png",
wantStatus: http.StatusForbidden,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := buildAvatarUploadRequest(t, server.URL+"/api/v1/users/"+tt.userID+"/avatar", tt.token, tt.fileBody, tt.filename)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
t.Fatalf("request failed: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != tt.wantStatus {
body, _ := io.ReadAll(resp.Body)
t.Errorf("expected status %d, got %d, body: %s", tt.wantStatus, resp.StatusCode, string(body))
}
})
}
// Clean up uploaded avatars
_ = os.RemoveAll("./uploads/avatars")
}