snapshot of https://github.com/ory/keto.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
CI / Run lints and checks (push) Failing after 47s
CI / Run tests (push) Failing after 1s
CI / Run race tests (push) Failing after 5s
CI / Build CLI docs (push) Has been skipped
CI / Run buf actions (push) Failing after 5s
CodeQL / Analyze (go) (push) Failing after 3s
CodeQL / Analyze (javascript) (push) Failing after 2s
Docker Image Scanners / scanners (push) Failing after 3s
Format / format (push) Failing after 3s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Draft newsletter (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
Some checks failed
CI / Run lints and checks (push) Failing after 47s
CI / Run tests (push) Failing after 1s
CI / Run race tests (push) Failing after 5s
CI / Build CLI docs (push) Has been skipped
CI / Run buf actions (push) Failing after 5s
CodeQL / Analyze (go) (push) Failing after 3s
CodeQL / Analyze (javascript) (push) Failing after 2s
Docker Image Scanners / scanners (push) Failing after 3s
Format / format (push) Failing after 3s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Draft newsletter (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
This commit is contained in:
222
internal/e2e/rest_client_test.go
Normal file
222
internal/e2e/rest_client_test.go
Normal file
@@ -0,0 +1,222 @@
|
||||
// Copyright © 2023 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/ory/herodot"
|
||||
"github.com/ory/x/healthx"
|
||||
|
||||
rts "github.com/ory/keto/gen/go/ory/keto/relation_tuples/v1alpha2"
|
||||
"github.com/ory/keto/internal/check"
|
||||
"github.com/ory/keto/internal/expand"
|
||||
httpclient "github.com/ory/keto/internal/httpclient"
|
||||
"github.com/ory/keto/internal/relationtuple"
|
||||
"github.com/ory/keto/ketoapi"
|
||||
"github.com/ory/keto/schema"
|
||||
)
|
||||
|
||||
var _ client = &restClient{}
|
||||
|
||||
type restClient struct {
|
||||
readURL, writeURL, oplSyntaxURL string
|
||||
}
|
||||
|
||||
func (rc *restClient) queryNamespaces(t testing.TB) (res ketoapi.GetNamespacesResponse) {
|
||||
body, code := rc.makeRequest(t, http.MethodGet, "/namespaces", "", rc.readURL)
|
||||
assert.Equal(t, http.StatusOK, code, body)
|
||||
require.NoError(t, json.Unmarshal([]byte(body), &res))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (rc *restClient) oplCheckSyntax(t testing.TB, content []byte) []*ketoapi.ParseError {
|
||||
body, code := rc.makeRequest(t, http.MethodPost, schema.RouteBase, string(content), rc.oplSyntaxURL)
|
||||
assert.Equal(t, http.StatusOK, code, body)
|
||||
var response ketoapi.CheckOPLSyntaxResponse
|
||||
require.NoError(t, json.Unmarshal([]byte(body), &response))
|
||||
|
||||
return response.Errors
|
||||
}
|
||||
|
||||
func (rc *restClient) makeRequest(t testing.TB, method, path, body string, baseURL string) (string, int) {
|
||||
var b io.Reader
|
||||
if body != "" {
|
||||
b = bytes.NewBufferString(body)
|
||||
}
|
||||
|
||||
// t.Logf("Requesting %s %s%s with body %#v", method, baseURL, path, body)
|
||||
req, err := http.NewRequest(method, baseURL+path, b)
|
||||
require.NoError(t, err)
|
||||
resp, err := http.DefaultClient.Do(req) //nolint:gosec // Test code with controlled URL.
|
||||
require.NoError(t, err)
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
|
||||
return string(respBody), resp.StatusCode
|
||||
}
|
||||
|
||||
func (rc *restClient) createTuple(t testing.TB, r *ketoapi.RelationTuple) {
|
||||
tEnc, err := json.Marshal(r)
|
||||
require.NoError(t, err)
|
||||
|
||||
body, code := rc.makeRequest(t, http.MethodPut, relationtuple.WriteRouteBase, string(tEnc), rc.writeURL)
|
||||
assert.Equal(t, http.StatusCreated, code, body)
|
||||
}
|
||||
|
||||
func (rc *restClient) deleteTuple(t testing.TB, r *ketoapi.RelationTuple) {
|
||||
body, code := rc.makeRequest(t, http.MethodDelete, relationtuple.WriteRouteBase+"?"+r.ToURLQuery().Encode(), "", rc.writeURL)
|
||||
require.Equal(t, http.StatusNoContent, code, body)
|
||||
}
|
||||
|
||||
func (rc *restClient) deleteAllTuples(t testing.TB, q *ketoapi.RelationQuery) {
|
||||
body, code := rc.makeRequest(t, http.MethodDelete, relationtuple.WriteRouteBase+"?"+q.ToURLQuery().Encode(), "", rc.writeURL)
|
||||
require.Equal(t, http.StatusNoContent, code, body)
|
||||
}
|
||||
|
||||
func (rc *restClient) queryTuple(t testing.TB, q *ketoapi.RelationQuery, opts ...paginationOptionSetter) *ketoapi.GetResponse {
|
||||
urlQuery := q.ToURLQuery()
|
||||
|
||||
pagination := getPaginationOptions(opts...)
|
||||
if pagination.Size != 0 {
|
||||
urlQuery.Set("page_size", strconv.Itoa(pagination.Size))
|
||||
}
|
||||
if pagination.Token != "" {
|
||||
urlQuery.Set("page_token", pagination.Token)
|
||||
}
|
||||
|
||||
body, code := rc.makeRequest(t, http.MethodGet, fmt.Sprintf("%s?%s", relationtuple.ReadRouteBase, urlQuery.Encode()), "", rc.readURL)
|
||||
require.Equal(t, http.StatusOK, code, body)
|
||||
|
||||
var dec ketoapi.GetResponse
|
||||
require.NoError(t, json.Unmarshal([]byte(body), &dec))
|
||||
|
||||
return &dec
|
||||
}
|
||||
|
||||
func (rc *restClient) queryTupleErr(t testing.TB, expected *herodot.DefaultError, q *ketoapi.RelationQuery, opts ...paginationOptionSetter) {
|
||||
urlQuery := q.ToURLQuery()
|
||||
|
||||
pagination := getPaginationOptions(opts...)
|
||||
if pagination.Size != 0 {
|
||||
urlQuery.Set("page_size", strconv.Itoa(pagination.Size))
|
||||
}
|
||||
if pagination.Token != "" {
|
||||
urlQuery.Set("page_token", pagination.Token)
|
||||
}
|
||||
|
||||
body, code := rc.makeRequest(t, http.MethodGet, fmt.Sprintf("%s?%s", relationtuple.ReadRouteBase, urlQuery.Encode()), "", rc.readURL)
|
||||
|
||||
assert.Equal(t, expected.CodeField, code)
|
||||
assert.Equal(t, int64(expected.StatusCode()), gjson.Get(body, "error.code").Int())
|
||||
assert.Equal(t, expected.Status(), gjson.Get(body, "error.status").String())
|
||||
assert.Equal(t, expected.Error(), gjson.Get(body, "error.message").String(), body)
|
||||
}
|
||||
|
||||
func (rc *restClient) check(t testing.TB, r *ketoapi.RelationTuple) bool {
|
||||
q := r.ToURLQuery()
|
||||
bodyGet, codeGet := rc.makeRequest(t, http.MethodGet, fmt.Sprintf("%s?%s", check.RouteBase, q.Encode()), "", rc.readURL)
|
||||
|
||||
var respGet rts.CheckResponse
|
||||
require.NoError(t, json.Unmarshal([]byte(bodyGet), &respGet))
|
||||
|
||||
j, err := json.Marshal(r)
|
||||
require.NoError(t, err)
|
||||
bodyPost, codePost := rc.makeRequest(t, http.MethodPost, check.RouteBase, string(j), rc.readURL)
|
||||
|
||||
var respPost rts.CheckResponse
|
||||
require.NoError(t, json.Unmarshal([]byte(bodyPost), &respPost))
|
||||
|
||||
if codeGet == http.StatusOK && codePost == http.StatusOK {
|
||||
assert.Equal(t, true, respGet.Allowed, "%s", bodyGet)
|
||||
assert.Equal(t, true, respPost.Allowed, "%s", bodyPost)
|
||||
return true
|
||||
}
|
||||
|
||||
assert.Equal(t, http.StatusForbidden, codeGet, bodyGet)
|
||||
assert.Equal(t, http.StatusForbidden, codePost, bodyPost)
|
||||
assert.Equal(t, false, respGet.Allowed)
|
||||
assert.Equal(t, false, respPost.Allowed)
|
||||
return false
|
||||
}
|
||||
|
||||
func (rc *restClient) batchCheckErr(t testing.TB, requestTuples []*ketoapi.RelationTuple, expected *herodot.DefaultError) {
|
||||
req := httpclient.BatchCheckPermissionBody{
|
||||
Tuples: tuplesToRelationships(requestTuples),
|
||||
}
|
||||
j, err := json.Marshal(req)
|
||||
require.NoError(t, err)
|
||||
body, code := rc.makeRequest(t, http.MethodPost, check.BatchRoute, string(j), rc.readURL)
|
||||
assert.Equal(t, expected.CodeField, code)
|
||||
assert.Contains(t, body, expected.Reason())
|
||||
}
|
||||
|
||||
func (rc *restClient) batchCheck(t testing.TB, requestTuples []*ketoapi.RelationTuple) []checkResponse {
|
||||
req := httpclient.BatchCheckPermissionBody{
|
||||
Tuples: tuplesToRelationships(requestTuples),
|
||||
}
|
||||
j, err := json.Marshal(req)
|
||||
require.NoError(t, err)
|
||||
body, code := rc.makeRequest(t, http.MethodPost, check.BatchRoute, string(j), rc.readURL)
|
||||
require.Equal(t, http.StatusOK, code, "batch check failed unexpected with error code %d", code)
|
||||
|
||||
var respPost rts.BatchCheckResponse
|
||||
require.NoError(t, json.Unmarshal([]byte(body), &respPost))
|
||||
|
||||
responseChecks := make([]checkResponse, len(respPost.Results))
|
||||
for i, result := range respPost.Results {
|
||||
responseChecks[i] = checkResponse{
|
||||
allowed: result.Allowed,
|
||||
errorMessage: result.Error,
|
||||
}
|
||||
}
|
||||
return responseChecks
|
||||
}
|
||||
|
||||
func (rc *restClient) expand(t testing.TB, r *ketoapi.SubjectSet, depth int) *ketoapi.Tree[*ketoapi.RelationTuple] {
|
||||
query := r.ToURLQuery()
|
||||
query.Set("max-depth", fmt.Sprintf("%d", depth))
|
||||
|
||||
body, code := rc.makeRequest(t, http.MethodGet, fmt.Sprintf("%s?%s", expand.RouteBase, query.Encode()), "", rc.readURL)
|
||||
require.Equal(t, http.StatusOK, code, body)
|
||||
|
||||
tree := &ketoapi.Tree[*ketoapi.RelationTuple]{}
|
||||
require.NoError(t, json.Unmarshal([]byte(body), tree))
|
||||
|
||||
return tree
|
||||
}
|
||||
|
||||
func healthReady(t require.TestingT, readURL string) int {
|
||||
req, err := http.NewRequest("GET", readURL+healthx.ReadyCheckPath, nil)
|
||||
require.NoError(t, err)
|
||||
resp, err := http.DefaultClient.Do(req) //nolint:gosec // Test code with controlled URL.
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
return resp.StatusCode
|
||||
}
|
||||
|
||||
func waitUntilLive(t testing.TB, readURL string) {
|
||||
require.EventuallyWithT(t, func(t *assert.CollectT) {
|
||||
assert.Equal(t, http.StatusOK, healthReady(t, readURL))
|
||||
}, 5*time.Second, 10*time.Millisecond)
|
||||
}
|
||||
|
||||
func (rc *restClient) waitUntilLive(t testing.TB) {
|
||||
waitUntilLive(t, rc.readURL)
|
||||
}
|
||||
Reference in New Issue
Block a user