snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s

This commit is contained in:
upstream-mirror
2026-09-18 01:22:23 +05:00
commit 7e385fc126
8391 changed files with 1766236 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
package helpers
import (
"context"
"github.com/cortezaproject/corteza/server/app"
"github.com/cortezaproject/corteza/server/pkg/cli"
"github.com/cortezaproject/corteza/server/pkg/logger"
"github.com/cortezaproject/corteza/server/pkg/options"
"github.com/cortezaproject/corteza/server/pkg/rand"
"github.com/cortezaproject/corteza/server/system/types"
// Explicitly register SQLite (not done in the app as for testing only)
_ "github.com/cortezaproject/corteza/server/store/adapters/rdbms/drivers/sqlite"
)
func NewIntegrationTestApp(ctx context.Context, initTestServices func(*app.CortezaApp) error) *app.CortezaApp {
// Enforce debug logger for tests
logger.SetDefault(logger.MakeDebugLogger())
var (
a = app.New()
)
a.Opt = options.Init()
// When running integration tests, we want to upgrade the db. Always.
a.Opt.Upgrade.Always = true
// Create a new JWT secret (to prevent any security weirdness)
a.Opt.Auth.Secret = string(rand.Bytes(32))
a.Opt.Auth.DefaultClient = ""
a.Log = logger.Default()
a.DefaultAuthClient = &types.AuthClient{ID: 1, Handle: "test-auth-client", Secret: "integration-tests"}
cli.HandleError(a.InitStore(ctx))
cli.HandleError(initTestServices(a))
cli.HandleError(a.InitServices(ctx))
return a
}

View File

@@ -0,0 +1,172 @@
package helpers
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"strings"
"github.com/cortezaproject/corteza/server/compose/types"
)
type (
assertFn func(*http.Response, *http.Request) error
StdErrorResponse struct {
Error struct {
Message string
}
}
RecordValueErrorSetResponse struct {
Error struct {
Message string
Details []types.RecordValueError
}
}
)
// decodes response body to given struct
func DecodeBody(rsp *http.Response, s interface{}) error {
if err := json.NewDecoder(rsp.Body).Decode(&s); err != nil {
return fmt.Errorf("could not decode body: %w", err)
}
return nil
}
// Returns first input that could be an error
func firstErr(ee ...interface{}) error {
for _, e := range ee {
switch t := e.(type) {
case error:
if t != nil {
return t
}
case StdErrorResponse:
if t.Error.Message != "" {
return errors.New(t.Error.Message)
}
case string:
if t != "" {
return errors.New(t)
}
}
}
return nil
}
// AssertNoErrors ensures there are no errors in the response
func AssertNoErrors(rsp *http.Response, _ *http.Request) (err error) {
tmp := StdErrorResponse{}
return firstErr(DecodeBody(rsp, &tmp), tmp)
}
// Asserts that all expected errors are returned
//
// # Compares each error by Kind, Message and Meta.field
//
// Note: This assertion always expects errors!
func AssertRecordValueError(exp ...*types.RecordValueError) assertFn {
return func(rsp *http.Response, _ *http.Request) (err error) {
rcvd := RecordValueErrorSetResponse{}
if err = DecodeBody(rsp, &rcvd); err != nil {
return err
}
if len(rcvd.Error.Details) == 0 {
return fmt.Errorf("expecting value errors, none received")
}
expLoop:
for _, e := range exp {
for _, r := range rcvd.Error.Details {
if e.Kind != r.Kind {
continue
}
if e.Message != r.Message {
continue
}
if e.Meta["field"] != r.Meta["field"] {
continue
}
// found expected error
continue expLoop
}
// did not find expected error
return fmt.Errorf("did not find expected error %v", e)
}
return nil
}
}
// Dump can be put into Assert()
func Dump(rsp *http.Response, _ *http.Request) (err error) {
var payload interface{}
if err = DecodeBody(rsp, &payload); err != nil {
return err
}
return nil
}
// AssertError ensures there are no errors in the response
func AssertError(expectedError string) assertFn {
return func(rsp *http.Response, _ *http.Request) (err error) {
tmp := StdErrorResponse{}
if err = DecodeBody(rsp, &tmp); err != nil {
return err
}
if tmp.Error.Message == "" {
return fmt.Errorf("No error, expecting: %v", expectedError)
}
if expectedError != tmp.Error.Message {
return fmt.Errorf("Expecting error %v, got: %v", expectedError, tmp.Error.Message)
}
return nil
}
}
// AssertBody compares the raw body to the provided string
func AssertBody(expected string) assertFn {
return func(rsp *http.Response, _ *http.Request) (err error) {
bb, err := ioutil.ReadAll(rsp.Body)
if err != nil {
return err
}
got := strings.Trim(string(bb), " \n")
if expected != got {
return fmt.Errorf("Expecting: %v, got: %v", expected, got)
}
return nil
}
}
// AssertErrorP checks if the expected error is part of the error message
func AssertErrorP(expectedError string) assertFn {
return func(rsp *http.Response, _ *http.Request) (err error) {
tmp := StdErrorResponse{}
if err = DecodeBody(rsp, &tmp); err != nil {
return err
}
if tmp.Error.Message == "" {
return fmt.Errorf("No error, expecting error with: %v", expectedError)
}
if !strings.Contains(tmp.Error.Message, expectedError) {
return fmt.Errorf("Expecting error with %v, got: %v", expectedError, tmp.Error.Message)
}
return nil
}
}

View File

@@ -0,0 +1,23 @@
package helpers
import (
"net/http"
"github.com/go-chi/chi/v5"
"github.com/steinfletcher/apitest"
"github.com/cortezaproject/corteza/server/pkg/auth"
)
func BindAuthMiddleware(r chi.Router) {
r.Use(
auth.HttpTokenVerifier,
auth.HttpTokenValidator(),
)
}
func ReqHeaderRawAuthBearer(token []byte) apitest.Intercept {
return func(req *http.Request) {
req.Header.Set("Authorization", "Bearer "+string(token))
}
}

View File

@@ -0,0 +1,16 @@
package helpers
import (
"github.com/joho/godotenv"
"os"
)
// RecursiveDotEnvLoad loads ENV variables from .evn files 3 levels down
func RecursiveDotEnvLoad() {
for _, loc := range []string{".env", "../.env", "../../.env"} {
if _, err := os.Stat(loc); err == nil {
godotenv.Load(loc)
break
}
}
}

View File

@@ -0,0 +1,11 @@
package helpers
import "encoding/json"
func JSON(i interface{}) string {
if b, err := json.Marshal(i); err != nil {
panic(err)
} else {
return string(b)
}
}

View File

@@ -0,0 +1,77 @@
package helpers
import (
"context"
"net/http"
"net/url"
"strings"
"testing"
"github.com/cortezaproject/corteza/server/pkg/label/types"
"github.com/cortezaproject/corteza/server/store"
"github.com/steinfletcher/apitest"
"github.com/stretchr/testify/require"
)
func SetLabelsViaAPI(api *apitest.APITest, t *testing.T, endpoint string, in, out interface{}) {
var (
payload = struct {
Response interface{}
}{}
req *apitest.Request
)
payload.Response = out
if strings.HasPrefix(endpoint, "PUT ") {
// a little workaround for our inconsistencies...
req = api.Put(endpoint[4:])
} else {
req = api.Post(endpoint)
}
req.JSON(JSON(in)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(AssertNoErrors).
End().
JSON(&payload)
}
func SearchWithLabelsViaAPI(api *apitest.APITest, t *testing.T, endpoint string, res interface{}, labels url.Values) {
var payload = struct{ Response struct{ Set interface{} } }{}
payload.Response.Set = res
api.
Get(endpoint).
QueryCollection(labels).
Expect(t).
Status(http.StatusOK).
Assert(AssertNoErrors).
End().
JSON(&payload)
}
func LoadLabelsFromStore(t *testing.T, s store.Storer, resKind string, id uint64) map[string]types.LabelValue {
ll, _, err := s.SearchLabels(context.Background(), types.LabelFilter{Kind: resKind})
require.NoError(t, err)
if len(ll) == 0 {
// small adjustment
return nil
}
return ll.FilterByResource(resKind, id)
}
func LabelsToStrings(labels map[string]types.LabelValue) map[string]string {
if labels == nil {
return nil
}
result := make(map[string]string, len(labels))
for k, v := range labels {
result[k] = v.Val
}
return result
}

View File

@@ -0,0 +1,120 @@
package helpers
import (
"context"
automationTypes "github.com/cortezaproject/corteza/server/automation/types"
composeTypes "github.com/cortezaproject/corteza/server/compose/types"
"github.com/cortezaproject/corteza/server/pkg/auth"
"github.com/cortezaproject/corteza/server/pkg/cli"
"github.com/cortezaproject/corteza/server/pkg/eventbus"
"github.com/cortezaproject/corteza/server/pkg/rbac"
"github.com/cortezaproject/corteza/server/system/types"
)
type (
myRoleGetter interface {
MyRole() uint64
}
)
func UpdateRBAC(rr ...uint64) {
// make sure event listener for role changes is removed
eventbus.Service().UnregisterByResource("system:role")
// convert given list of role IDs to RBAC roles
// and update service
ccr := []*rbac.Role{}
// Make sure all bypass roles are set on RBAC service
for _, r := range auth.ServiceUser().Roles() {
ccr = append(ccr, rbac.BypassRole.Make(r, auth.BypassRoleHandle))
}
for _, r := range rr {
ccr = append(ccr, rbac.CommonRole.Make(r, "integration-tests"))
}
rbac.Global().Clear()
rbac.Global().UpdateRoles(ccr...)
}
func AllowMe(mrg myRoleGetter, r string, oo ...string) {
for _, o := range oo {
Grant(rbac.AllowRule(mrg.MyRole(), r, o))
}
}
func Allow(role *types.Role, r string, oo ...string) {
for _, o := range oo {
Grant(rbac.AllowRule(role.ID, r, o))
}
}
func DenyMe(mrg myRoleGetter, r string, oo ...string) {
for _, o := range oo {
Grant(rbac.DenyRule(mrg.MyRole(), r, o))
}
}
func Deny(role *types.Role, r string, oo ...string) {
for _, o := range oo {
Grant(rbac.DenyRule(role.ID, r, o))
}
}
func Grant(rr ...*rbac.Rule) {
cli.HandleError(rbac.Global().Grant(context.Background(), rr...))
}
// Common RBAC presets
func AllowMeModuleCreate(mrg myRoleGetter) {
AllowMe(mrg, composeTypes.NamespaceRbacResource(0), "module.create")
}
func AllowMeWorkflowSearch(mrg myRoleGetter) {
AllowMe(mrg, automationTypes.WorkflowRbacResource(0), "workflows.search")
AllowMe(mrg, automationTypes.WorkflowRbacResource(0), "triggers.search")
}
func AllowMeModuleSearch(mrg myRoleGetter) {
AllowMe(mrg, composeTypes.NamespaceRbacResource(0), "modules.search")
AllowMe(mrg, composeTypes.ModuleRbacResource(0, 0), "read")
}
func AllowMeModuleCRUD(mrg myRoleGetter) {
AllowMe(mrg, composeTypes.NamespaceRbacResource(0), "module.create")
AllowMe(mrg, composeTypes.NamespaceRbacResource(0), "modules.search")
AllowMe(mrg, composeTypes.ModuleRbacResource(0, 0), "read")
AllowMe(mrg, composeTypes.ModuleRbacResource(0, 0), "update")
AllowMe(mrg, composeTypes.ModuleRbacResource(0, 0), "delete")
}
func AllowMeRecordCRUD(mrg myRoleGetter) {
AllowMe(mrg, composeTypes.ModuleRbacResource(0, 0), "record.create")
AllowMe(mrg, composeTypes.ModuleRbacResource(0, 0), "records.search")
AllowMe(mrg, composeTypes.RecordRbacResource(0, 0, 0), "read")
AllowMe(mrg, composeTypes.RecordRbacResource(0, 0, 0), "update")
AllowMe(mrg, composeTypes.RecordRbacResource(0, 0, 0), "delete")
AllowMe(mrg, composeTypes.ModuleFieldRbacResource(0, 0, 0), "record.value.update")
AllowMe(mrg, composeTypes.ModuleFieldRbacResource(0, 0, 0), "record.value.read")
}
func AllowMeDalConnectionSearch(mrg myRoleGetter) {
AllowMe(mrg, types.ComponentRbacResource(), "dal-connections.search")
AllowMe(mrg, types.DalConnectionRbacResource(0), "read")
}
func AllowMeDalConnectionCRUD(mrg myRoleGetter) {
AllowMe(mrg, types.ComponentRbacResource(),
"dal-connection.create",
"dal-connections.search",
)
AllowMe(mrg, types.DalConnectionRbacResource(0),
"read",
"update",
"delete",
"dal-config.manage",
)
}

View File

@@ -0,0 +1,40 @@
package helpers
import (
"bytes"
"fmt"
"mime/multipart"
"net/textproto"
"testing"
"github.com/steinfletcher/apitest"
"github.com/stretchr/testify/require"
)
func InitFileUpload(t *testing.T, apiTest *apitest.APITest, endpoint string, form map[string]string, file []byte, name, mimetype string) *apitest.Response {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
for k, v := range form {
require.NoError(t, writer.WriteField(k, v))
}
header := make(textproto.MIMEHeader)
header.Set("Content-Disposition",
fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
"upload", name))
header.Set("Content-Type", mimetype)
part, err := writer.CreatePart(header)
require.NoError(t, err)
_, err = part.Write(file)
require.NoError(t, err)
require.NoError(t, writer.Close())
return apiTest.
Post(endpoint).
Body(body.String()).
Header("Content-Type", writer.FormDataContentType()).
Header("Accept", "application/json").
Expect(t)
}