89 lines
4.6 KiB
Go
89 lines
4.6 KiB
Go
package httpapi
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"errors"
|
||
|
|
"net/http"
|
||
|
|
"net/http/httptest"
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"data-terminal-cloud/internal/identity"
|
||
|
|
"data-terminal-cloud/internal/sharing"
|
||
|
|
|
||
|
|
"github.com/google/uuid"
|
||
|
|
)
|
||
|
|
|
||
|
|
func sharingRequest(method, target, body string, authenticated bool) *http.Request {
|
||
|
|
request := httptest.NewRequest(method, target, strings.NewReader(body))
|
||
|
|
if body != "" {
|
||
|
|
request.Header.Set("Content-Type", "application/json")
|
||
|
|
}
|
||
|
|
if authenticated {
|
||
|
|
request = request.WithContext(identity.WithPrincipal(request.Context(), identity.Principal{Issuer: "issuer", Subject: "subject"}))
|
||
|
|
}
|
||
|
|
return request
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestSharingHandlersRejectInvalidAuthenticationAndInputsBeforeService(t *testing.T) {
|
||
|
|
validID := uuid.NewString()
|
||
|
|
tests := []struct {
|
||
|
|
name string
|
||
|
|
handler http.Handler
|
||
|
|
request *http.Request
|
||
|
|
status int
|
||
|
|
code string
|
||
|
|
}{
|
||
|
|
{"create unauthenticated", CreateSharingGrantHandler(nil), sharingRequest(http.MethodPost, "/api/v1/sharing-grants", `{}`, false), http.StatusUnauthorized, "AUTHENTICATION_REQUIRED"},
|
||
|
|
{"create short idempotency", CreateSharingGrantHandler(nil), sharingRequest(http.MethodPost, "/api/v1/sharing-grants", `{}`, true), http.StatusBadRequest, "VALIDATION_FAILED"},
|
||
|
|
{"create malformed body", CreateSharingGrantHandler(nil), sharingRequest(http.MethodPost, "/api/v1/sharing-grants", `{`, true), http.StatusBadRequest, "VALIDATION_FAILED"},
|
||
|
|
{"create invalid grant", CreateSharingGrantHandler(nil), sharingRequest(http.MethodPost, "/api/v1/sharing-grants", `{"resource_type":"story","resource_id":"bad","grantee_user_id":"bad","expires_at":"2026-08-24T00:00:00Z"}`, true), http.StatusBadRequest, "VALIDATION_FAILED"},
|
||
|
|
{"list unauthenticated", ListSharingGrantsHandler(nil), sharingRequest(http.MethodGet, "/api/v1/sharing-grants?subject_id="+validID, "", false), http.StatusUnauthorized, "AUTHENTICATION_REQUIRED"},
|
||
|
|
{"list invalid subject", ListSharingGrantsHandler(nil), sharingRequest(http.MethodGet, "/api/v1/sharing-grants?subject_id=bad", "", true), http.StatusBadRequest, "VALIDATION_FAILED"},
|
||
|
|
{"revoke unauthenticated", RevokeSharingGrantHandler(nil), sharingRequest(http.MethodPost, "/api/v1/sharing-grants/"+validID+"/revoke", `{}`, false), http.StatusUnauthorized, "AUTHENTICATION_REQUIRED"},
|
||
|
|
{"access unauthenticated", AccessSharedResourceHandler(nil), sharingRequest(http.MethodGet, "/api/v1/sharing-grants/"+validID+"/resource", "", false), http.StatusUnauthorized, "AUTHENTICATION_REQUIRED"},
|
||
|
|
{"public invalid token", AccessPublicSharedResourceHandler(nil), sharingRequest(http.MethodGet, "/public/v1/sharing-grants/"+validID, "", false), http.StatusBadRequest, "VALIDATION_FAILED"},
|
||
|
|
{"edit missing if-match", EditSharedStoryHandler(nil), sharingRequest(http.MethodPatch, "/api/v1/sharing-grants/"+validID+"/story", `{"content":"updated","reason":"review"}`, true), http.StatusPreconditionRequired, "PRECONDITION_REQUIRED"},
|
||
|
|
}
|
||
|
|
for _, item := range tests {
|
||
|
|
t.Run(item.name, func(t *testing.T) {
|
||
|
|
if strings.Contains(item.name, "create ") && !strings.Contains(item.name, "unauthenticated") && !strings.Contains(item.name, "short") {
|
||
|
|
item.request.Header.Set("Idempotency-Key", "sharing-handler-test-key-1234")
|
||
|
|
}
|
||
|
|
if strings.Contains(item.name, "revoke") || strings.Contains(item.name, "access unauthenticated") || strings.Contains(item.name, "edit") {
|
||
|
|
item.request.SetPathValue("id", validID)
|
||
|
|
}
|
||
|
|
if strings.Contains(item.name, "public") {
|
||
|
|
item.request.SetPathValue("id", validID)
|
||
|
|
}
|
||
|
|
recorder := httptest.NewRecorder()
|
||
|
|
item.handler.ServeHTTP(recorder, item.request)
|
||
|
|
if recorder.Code != item.status || !strings.Contains(recorder.Body.String(), item.code) {
|
||
|
|
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestWriteSharingErrorStableMappings(t *testing.T) {
|
||
|
|
tests := []struct {
|
||
|
|
err error
|
||
|
|
status int
|
||
|
|
code string
|
||
|
|
}{
|
||
|
|
{sharing.ErrInvalid, http.StatusBadRequest, "VALIDATION_FAILED"},
|
||
|
|
{sharing.ErrAccessDenied, http.StatusForbidden, "SHARING_ACCESS_DENIED"},
|
||
|
|
{sharing.ErrExpired, http.StatusGone, "SHARING_GRANT_INACTIVE"},
|
||
|
|
{sharing.ErrNotFound, http.StatusNotFound, "RESOURCE_NOT_FOUND"},
|
||
|
|
{sharing.ErrVersionConflict, http.StatusPreconditionFailed, "VERSION_CONFLICT"},
|
||
|
|
{errors.New("database unavailable"), http.StatusInternalServerError, "INTERNAL_ERROR"},
|
||
|
|
}
|
||
|
|
for _, item := range tests {
|
||
|
|
recorder := httptest.NewRecorder()
|
||
|
|
request := httptest.NewRequest(http.MethodGet, "/api/v1/sharing-grants", nil)
|
||
|
|
writeSharingError(recorder, request, item.err)
|
||
|
|
if recorder.Code != item.status || !strings.Contains(recorder.Body.String(), item.code) {
|
||
|
|
t.Fatalf("error=%v status=%d body=%s", item.err, recorder.Code, recorder.Body.String())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|