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
161 lines
4.9 KiB
Go
161 lines
4.9 KiB
Go
// Copyright © 2023 Ory Corp
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package e2e
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/ory/herodot"
|
|
"github.com/ory/x/cmdx"
|
|
prometheus "github.com/ory/x/prometheusx"
|
|
|
|
"github.com/ory/keto/cmd"
|
|
cliclient "github.com/ory/keto/cmd/client"
|
|
"github.com/ory/keto/internal/relationtuple"
|
|
"github.com/ory/keto/internal/x/dbx"
|
|
"github.com/ory/keto/ketoapi"
|
|
)
|
|
|
|
type (
|
|
transactClient interface {
|
|
client
|
|
transactTuples(t testing.TB, ins []*ketoapi.RelationTuple, del []*ketoapi.RelationTuple)
|
|
}
|
|
client interface {
|
|
createTuple(t testing.TB, r *ketoapi.RelationTuple)
|
|
deleteTuple(t testing.TB, r *ketoapi.RelationTuple)
|
|
deleteAllTuples(t testing.TB, q *ketoapi.RelationQuery)
|
|
queryTuple(t testing.TB, q *ketoapi.RelationQuery, opts ...paginationOptionSetter) *ketoapi.GetResponse
|
|
queryTupleErr(t testing.TB, expected *herodot.DefaultError, q *ketoapi.RelationQuery, opts ...paginationOptionSetter)
|
|
check(t testing.TB, r *ketoapi.RelationTuple) bool
|
|
batchCheck(t testing.TB, r []*ketoapi.RelationTuple) []checkResponse
|
|
batchCheckErr(t testing.TB, requestTuples []*ketoapi.RelationTuple, expected *herodot.DefaultError)
|
|
expand(t testing.TB, r *ketoapi.SubjectSet, depth int) *ketoapi.Tree[*ketoapi.RelationTuple]
|
|
oplCheckSyntax(t testing.TB, content []byte) []*ketoapi.ParseError
|
|
waitUntilLive(t testing.TB)
|
|
queryNamespaces(t testing.TB) ketoapi.GetNamespacesResponse
|
|
}
|
|
)
|
|
|
|
const (
|
|
promLogLine = "promhttp_metric_handler_requests_total"
|
|
)
|
|
|
|
func Test(t *testing.T) {
|
|
t.Parallel()
|
|
for _, dsn := range dbx.GetDSNs(t) {
|
|
t.Run(fmt.Sprintf("dsn=%s", dsn.Name), func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, reg, namespaceTestMgr, getAddr := newInitializedReg(t, dsn, nil)
|
|
|
|
closeServer := startServer(ctx, t, reg)
|
|
t.Cleanup(closeServer)
|
|
|
|
_, _, readAddr := getAddr(t, "read")
|
|
_, _, writeAddr := getAddr(t, "write")
|
|
_, _, oplAddr := getAddr(t, "opl")
|
|
_, _, metricsAddr := getAddr(t, "metrics")
|
|
|
|
// The test cases start here
|
|
// We execute every test with all clients available
|
|
for _, cl := range []client{
|
|
newGrpcClient(t, ctx,
|
|
readAddr,
|
|
writeAddr,
|
|
oplAddr,
|
|
),
|
|
&restClient{
|
|
readURL: "http://" + readAddr,
|
|
writeURL: "http://" + writeAddr,
|
|
oplSyntaxURL: "http://" + oplAddr,
|
|
},
|
|
&cliClient{c: &cmdx.CommandExecuter{
|
|
New: func() *cobra.Command {
|
|
return cmd.NewRootCmd(nil)
|
|
},
|
|
Ctx: ctx,
|
|
PersistentArgs: []string{
|
|
"--" + cliclient.FlagReadRemote, readAddr,
|
|
"--" + cliclient.FlagWriteRemote, writeAddr,
|
|
"--insecure-disable-transport-security=true",
|
|
"--" + cmdx.FlagFormat, string(cmdx.FormatJSON),
|
|
},
|
|
}},
|
|
&sdkClient{
|
|
readRemote: readAddr,
|
|
writeRemote: writeAddr,
|
|
syntaxRemote: oplAddr,
|
|
},
|
|
} {
|
|
t.Run(fmt.Sprintf("client=%T", cl), runCases(cl, namespaceTestMgr))
|
|
|
|
if tc, ok := cl.(transactClient); ok {
|
|
t.Run(fmt.Sprintf("transactClient=%T", cl), runTransactionCases(tc, namespaceTestMgr))
|
|
}
|
|
}
|
|
|
|
t.Run("case=metrics are served", func(t *testing.T) {
|
|
t.Parallel()
|
|
newGrpcClient(t, ctx,
|
|
readAddr,
|
|
writeAddr,
|
|
oplAddr,
|
|
).waitUntilLive(t)
|
|
|
|
t.Run("case=on "+prometheus.MetricsPrometheusPath, func(t *testing.T) {
|
|
t.Parallel()
|
|
resp, err := http.Get(fmt.Sprintf("http://%s%s", metricsAddr, prometheus.MetricsPrometheusPath))
|
|
require.NoError(t, err)
|
|
require.Equal(t, resp.StatusCode, http.StatusOK)
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
require.Contains(t, string(body), promLogLine)
|
|
})
|
|
|
|
t.Run("case=not on /", func(t *testing.T) {
|
|
t.Parallel()
|
|
resp, err := http.Get(fmt.Sprintf("http://%s", metricsAddr))
|
|
require.NoError(t, err)
|
|
require.Equal(t, resp.StatusCode, http.StatusNotFound)
|
|
})
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestServeCORS(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, reg, _, getAddr := newInitializedReg(t, dbx.GetSqlite(t), map[string]any{
|
|
"serve.read.cors.enabled": true,
|
|
"serve.read.cors.debug": true,
|
|
"serve.read.cors.allowed_methods": []string{http.MethodGet},
|
|
"serve.read.cors.allowed_origins": []string{"https://ory.sh"},
|
|
})
|
|
|
|
closeServer := startServer(ctx, t, reg)
|
|
t.Cleanup(closeServer)
|
|
|
|
_, _, readAddr := getAddr(t, "read")
|
|
waitUntilLive(t, "http://"+readAddr)
|
|
t.Log("Health check is ready")
|
|
|
|
req, err := http.NewRequest(http.MethodOptions, "http://"+readAddr+relationtuple.ReadRouteBase, nil)
|
|
require.NoError(t, err)
|
|
req.Header.Set("Origin", "https://ory.sh")
|
|
req.Header.Set("Access-Control-Request-Method", "GET")
|
|
resp, err := http.DefaultClient.Do(req) //nolint:gosec // Test code with controlled URL.
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusNoContent, resp.StatusCode)
|
|
assert.Equalf(t, "https://ory.sh", resp.Header.Get("Access-Control-Allow-Origin"), "%+v", resp.Header)
|
|
}
|