mirror of
https://github.com/PrivateCaptcha/PrivateCaptcha.git
synced 2026-02-11 00:08:47 -06:00
* Initial plan * Add negative codepath tests for API endpoints Co-authored-by: ribtoks <505555+ribtoks@users.noreply.github.com> * Fix task test for invalid UUID format Co-authored-by: ribtoks <505555+ribtoks@users.noreply.github.com> * Fix code review feedback - use t.Fatalf for consistency Co-authored-by: ribtoks <505555+ribtoks@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: ribtoks <505555+ribtoks@users.noreply.github.com>
192 lines
4.9 KiB
Go
192 lines
4.9 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/PrivateCaptcha/PrivateCaptcha/pkg/common"
|
|
"github.com/PrivateCaptcha/PrivateCaptcha/pkg/db"
|
|
dbgen "github.com/PrivateCaptcha/PrivateCaptcha/pkg/db/generated"
|
|
"github.com/PrivateCaptcha/PrivateCaptcha/pkg/db/tests"
|
|
db_test "github.com/PrivateCaptcha/PrivateCaptcha/pkg/db/tests"
|
|
"github.com/rs/xid"
|
|
)
|
|
|
|
func TestGetAsyncTaskPermissions(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
|
|
ctx := common.TraceContext(t.Context(), t.Name())
|
|
|
|
user1, _, apiKey1, err := setupAPISuite(ctx, t.Name()+"_owner")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
handlerID := xid.New().String()
|
|
request := struct{}{}
|
|
task, err := s.BusinessDB.Impl().CreateNewAsyncTask(ctx, request, handlerID, user1, time.Now().UTC().Add(24*time.Hour), t.Name())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
taskID := db.UUIDToString(task.ID)
|
|
|
|
_, _, apiKey2, err := setupAPISuite(ctx, t.Name()+"_other")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// api key 2 belongs to the wrong user
|
|
resp, err := apiRequestSuite(ctx, nil, http.MethodGet, "/"+common.AsyncTaskEndpoint+"/"+taskID, apiKey2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp.StatusCode != http.StatusForbidden {
|
|
t.Fatalf("Unexpected status code: %v", resp.StatusCode)
|
|
}
|
|
|
|
// with api key 1 it should work
|
|
_, meta, err := requestResponseAPISuite[*apiAsyncTaskResultOutput](ctx, nil, http.MethodGet, "/"+common.AsyncTaskEndpoint+"/"+taskID, apiKey1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !meta.Code.Success() {
|
|
t.Fatalf("Unexpected status code: %v", meta.Description)
|
|
}
|
|
}
|
|
|
|
func TestGetAsyncTaskInvalidKey(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
|
|
ctx := common.TraceContext(t.Context(), t.Name())
|
|
apiKey := db.UUIDToSecret(*randomUUID())
|
|
taskID := "some-task-id"
|
|
|
|
resp, err := apiRequestSuite(ctx, nil, http.MethodGet, "/"+common.AsyncTaskEndpoint+"/"+taskID, apiKey)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusForbidden {
|
|
t.Fatalf("Unexpected status code: %v", resp.StatusCode)
|
|
}
|
|
}
|
|
|
|
func TestGetAsyncTaskReadOnlyKey(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
|
|
ctx := common.TraceContext(t.Context(), t.Name())
|
|
|
|
user, _, apiKey, err := setupAPISuiteEx(ctx, t.Name(), dbgen.ApiKeyScopePortal, true /*read-only*/, false /*scope org*/)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
handlerID := xid.New().String()
|
|
request := struct{}{}
|
|
task, err := s.BusinessDB.Impl().CreateNewAsyncTask(ctx, request, handlerID, user, time.Now().UTC().Add(24*time.Hour), t.Name())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
taskID := db.UUIDToString(task.ID)
|
|
|
|
// with read-only api key it still should work
|
|
_, meta, err := requestResponseAPISuite[*apiAsyncTaskResultOutput](ctx, nil, http.MethodGet, "/"+common.AsyncTaskEndpoint+"/"+taskID, apiKey)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !meta.Code.Success() {
|
|
t.Fatalf("Unexpected status code: %v", meta.Description)
|
|
}
|
|
}
|
|
|
|
func TestGetAsyncTaskNoSubscription(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
|
|
ctx := common.TraceContext(t.Context(), t.Name())
|
|
|
|
user, _, err := db_test.CreateNewAccountForTestEx(ctx, store, t.Name(), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
keyParams := tests.CreateNewPuzzleAPIKeyParams(t.Name()+"-apikey", time.Now(), 1*time.Hour, 10.0)
|
|
keyParams.Scope = dbgen.ApiKeyScopePortal
|
|
apikey, _, err := store.Impl().CreateAPIKey(ctx, user, keyParams)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
apiKeyStr := db.UUIDToSecret(apikey.ExternalID)
|
|
|
|
handlerID := xid.New().String()
|
|
request := struct{}{}
|
|
task, err := s.BusinessDB.Impl().CreateNewAsyncTask(ctx, request, handlerID, user, time.Now().UTC().Add(24*time.Hour), t.Name())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
taskID := db.UUIDToString(task.ID)
|
|
|
|
resp, err := apiRequestSuite(ctx, nil, http.MethodGet, "/"+common.AsyncTaskEndpoint+"/"+taskID, apiKeyStr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusPaymentRequired {
|
|
t.Fatalf("expected status %d, got %d", http.StatusPaymentRequired, resp.StatusCode)
|
|
}
|
|
}
|
|
|
|
func TestGetAsyncTaskInvalidIDFormat(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
|
|
ctx := common.TraceContext(t.Context(), t.Name())
|
|
|
|
_, _, apiKey, err := setupAPISuite(ctx, t.Name())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
taskID string
|
|
wantStatus int
|
|
}{
|
|
{
|
|
name: "Invalid UUID Format",
|
|
taskID: "not-a-valid-uuid",
|
|
wantStatus: http.StatusBadRequest,
|
|
},
|
|
{
|
|
name: "Empty Task ID",
|
|
taskID: "",
|
|
wantStatus: http.StatusNotFound,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
endpoint := "/" + common.AsyncTaskEndpoint + "/" + tt.taskID
|
|
resp, err := apiRequestSuite(ctx, nil, http.MethodGet, endpoint, apiKey)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if resp.StatusCode != tt.wantStatus {
|
|
t.Errorf("expected status %d, got %d", tt.wantStatus, resp.StatusCode)
|
|
}
|
|
})
|
|
}
|
|
}
|