mirror of
https://github.com/zitadel/zitadel.git
synced 2025-07-15 08:18:40 +00:00

This PR summarizes multiple changes specifically only available with ZITADEL v3: - feat: Web Keys management (https://github.com/zitadel/zitadel/pull/9526) - fix(cmd): ensure proper working of mirror (https://github.com/zitadel/zitadel/pull/9509) - feat(Authz): system user support for permission check v2 (https://github.com/zitadel/zitadel/pull/9640) - chore(license): change from Apache to AGPL (https://github.com/zitadel/zitadel/pull/9597) - feat(console): list v2 sessions (https://github.com/zitadel/zitadel/pull/9539) - fix(console): add loginV2 feature flag (https://github.com/zitadel/zitadel/pull/9682) - fix(feature flags): allow reading "own" flags (https://github.com/zitadel/zitadel/pull/9649) - feat(console): add Actions V2 UI (https://github.com/zitadel/zitadel/pull/9591) BREAKING CHANGE - feat(webkey): migrate to v2beta API (https://github.com/zitadel/zitadel/pull/9445) - chore!: remove CockroachDB Support (https://github.com/zitadel/zitadel/pull/9444) - feat(actions): migrate to v2beta API (https://github.com/zitadel/zitadel/pull/9489) --------- Co-authored-by: Livio Spring <livio.a@gmail.com> Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com> Co-authored-by: Silvan <27845747+adlerhurst@users.noreply.github.com> Co-authored-by: Ramon <mail@conblem.me> Co-authored-by: Elio Bischof <elio@zitadel.com> Co-authored-by: Kenta Yamaguchi <56732734+KEY60228@users.noreply.github.com> Co-authored-by: Harsha Reddy <harsha.reddy@klaviyo.com> Co-authored-by: Livio Spring <livio@zitadel.com> Co-authored-by: Max Peintner <max@caos.ch> Co-authored-by: Iraq <66622793+kkrime@users.noreply.github.com> Co-authored-by: Florian Forster <florian@zitadel.com> Co-authored-by: Tim Möhlmann <tim+github@zitadel.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Max Peintner <peintnerm@gmail.com>
289 lines
7.0 KiB
Go
289 lines
7.0 KiB
Go
package execution
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/riverqueue/river"
|
|
"github.com/riverqueue/river/rivertype"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/zitadel/zitadel/internal/api/authz"
|
|
"github.com/zitadel/zitadel/internal/eventstore"
|
|
"github.com/zitadel/zitadel/internal/execution/mock"
|
|
"github.com/zitadel/zitadel/internal/query"
|
|
"github.com/zitadel/zitadel/internal/repository/action"
|
|
exec_repo "github.com/zitadel/zitadel/internal/repository/execution"
|
|
"github.com/zitadel/zitadel/internal/repository/user"
|
|
"github.com/zitadel/zitadel/internal/zerrors"
|
|
)
|
|
|
|
type fields struct {
|
|
queries *mock.MockQueries
|
|
queue *mock.MockQueue
|
|
}
|
|
type fieldsWorker struct {
|
|
now nowFunc
|
|
}
|
|
type args struct {
|
|
event eventstore.Event
|
|
mapper func(event eventstore.Event) (eventstore.Event, error)
|
|
}
|
|
type argsWorker struct {
|
|
job *river.Job[*exec_repo.Request]
|
|
}
|
|
type want struct {
|
|
noOperation bool
|
|
err assert.ErrorAssertionFunc
|
|
stmtErr assert.ErrorAssertionFunc
|
|
}
|
|
type wantWorker struct {
|
|
targets []*query.ExecutionTarget
|
|
sendStatusCode int
|
|
err assert.ErrorAssertionFunc
|
|
}
|
|
|
|
func newExecutionWorker(f fieldsWorker) *Worker {
|
|
return &Worker{
|
|
config: WorkerConfig{
|
|
Workers: 1,
|
|
TransactionDuration: 5 * time.Second,
|
|
MaxTtl: 5 * time.Minute,
|
|
},
|
|
now: f.now,
|
|
}
|
|
}
|
|
|
|
const (
|
|
userID = "user1"
|
|
orgID = "orgID"
|
|
instanceID = "instanceID"
|
|
eventID = "eventID"
|
|
eventData = `{"name":"name","script":"name(){}","timeout":3000000000,"allowedToFail":true}`
|
|
)
|
|
|
|
func Test_handleEventExecution(t *testing.T) {
|
|
testNow := time.Now
|
|
tests := []struct {
|
|
name string
|
|
test func() (fieldsWorker, argsWorker, wantWorker)
|
|
}{
|
|
{
|
|
"max TTL",
|
|
func() (fieldsWorker, argsWorker, wantWorker) {
|
|
return fieldsWorker{
|
|
now: testNow,
|
|
},
|
|
argsWorker{
|
|
job: &river.Job[*exec_repo.Request]{
|
|
JobRow: &rivertype.JobRow{
|
|
CreatedAt: time.Now().Add(-1 * time.Hour),
|
|
},
|
|
Args: &exec_repo.Request{
|
|
Aggregate: &eventstore.Aggregate{
|
|
InstanceID: instanceID,
|
|
ID: eventID,
|
|
ResourceOwner: instanceID,
|
|
},
|
|
Sequence: 1,
|
|
CreatedAt: time.Now().Add(-1 * time.Hour),
|
|
EventType: user.HumanInviteCodeAddedType,
|
|
UserID: userID,
|
|
EventData: []byte(eventData),
|
|
},
|
|
},
|
|
},
|
|
wantWorker{
|
|
targets: mockTargets(1),
|
|
sendStatusCode: http.StatusOK,
|
|
err: func(tt assert.TestingT, err error, i ...interface{}) bool {
|
|
return errors.Is(err, new(river.JobCancelError))
|
|
},
|
|
}
|
|
},
|
|
},
|
|
{
|
|
"none",
|
|
func() (fieldsWorker, argsWorker, wantWorker) {
|
|
return fieldsWorker{
|
|
now: testNow,
|
|
},
|
|
argsWorker{
|
|
job: &river.Job[*exec_repo.Request]{
|
|
JobRow: &rivertype.JobRow{
|
|
CreatedAt: time.Now(),
|
|
},
|
|
Args: &exec_repo.Request{
|
|
Aggregate: &eventstore.Aggregate{
|
|
InstanceID: instanceID,
|
|
ID: eventID,
|
|
ResourceOwner: instanceID,
|
|
},
|
|
Sequence: 1,
|
|
CreatedAt: time.Now(),
|
|
EventType: user.HumanInviteCodeAddedType,
|
|
UserID: userID,
|
|
EventData: []byte(eventData),
|
|
},
|
|
},
|
|
},
|
|
wantWorker{
|
|
targets: mockTargets(0),
|
|
sendStatusCode: http.StatusOK,
|
|
err: nil,
|
|
}
|
|
},
|
|
},
|
|
{
|
|
"single",
|
|
func() (fieldsWorker, argsWorker, wantWorker) {
|
|
return fieldsWorker{
|
|
now: testNow,
|
|
},
|
|
argsWorker{
|
|
job: &river.Job[*exec_repo.Request]{
|
|
JobRow: &rivertype.JobRow{
|
|
CreatedAt: time.Now(),
|
|
},
|
|
Args: &exec_repo.Request{
|
|
Aggregate: &eventstore.Aggregate{
|
|
InstanceID: instanceID,
|
|
Type: action.AggregateType,
|
|
Version: action.AggregateVersion,
|
|
ID: eventID,
|
|
ResourceOwner: orgID,
|
|
},
|
|
Sequence: 1,
|
|
CreatedAt: time.Now().UTC(),
|
|
EventType: action.AddedEventType,
|
|
UserID: userID,
|
|
EventData: []byte(eventData),
|
|
},
|
|
},
|
|
},
|
|
wantWorker{
|
|
targets: mockTargets(1),
|
|
sendStatusCode: http.StatusOK,
|
|
err: nil,
|
|
}
|
|
},
|
|
},
|
|
{
|
|
"single, failed 400",
|
|
func() (fieldsWorker, argsWorker, wantWorker) {
|
|
return fieldsWorker{
|
|
now: testNow,
|
|
},
|
|
argsWorker{
|
|
job: &river.Job[*exec_repo.Request]{
|
|
JobRow: &rivertype.JobRow{
|
|
CreatedAt: time.Now(),
|
|
},
|
|
Args: &exec_repo.Request{
|
|
Aggregate: &eventstore.Aggregate{
|
|
InstanceID: instanceID,
|
|
Type: action.AggregateType,
|
|
Version: action.AggregateVersion,
|
|
ID: eventID,
|
|
ResourceOwner: orgID,
|
|
},
|
|
Sequence: 1,
|
|
CreatedAt: time.Now().UTC(),
|
|
EventType: action.AddedEventType,
|
|
UserID: userID,
|
|
EventData: []byte(eventData),
|
|
},
|
|
},
|
|
},
|
|
wantWorker{
|
|
targets: mockTargets(1),
|
|
sendStatusCode: http.StatusBadRequest,
|
|
err: func(tt assert.TestingT, err error, i ...interface{}) bool {
|
|
return errors.Is(err, zerrors.ThrowPreconditionFailed(nil, "EXEC-dra6yamk98", "Errors.Execution.Failed"))
|
|
},
|
|
}
|
|
},
|
|
},
|
|
{
|
|
"multiple",
|
|
func() (fieldsWorker, argsWorker, wantWorker) {
|
|
return fieldsWorker{
|
|
now: testNow,
|
|
},
|
|
argsWorker{
|
|
job: &river.Job[*exec_repo.Request]{
|
|
JobRow: &rivertype.JobRow{
|
|
CreatedAt: time.Now(),
|
|
},
|
|
Args: &exec_repo.Request{
|
|
Aggregate: &eventstore.Aggregate{
|
|
InstanceID: instanceID,
|
|
Type: action.AggregateType,
|
|
Version: action.AggregateVersion,
|
|
ID: eventID,
|
|
ResourceOwner: orgID,
|
|
},
|
|
Sequence: 1,
|
|
CreatedAt: time.Now().UTC(),
|
|
EventType: action.AddedEventType,
|
|
UserID: userID,
|
|
EventData: []byte(eventData),
|
|
},
|
|
},
|
|
},
|
|
wantWorker{
|
|
targets: mockTargets(3),
|
|
sendStatusCode: http.StatusOK,
|
|
err: nil,
|
|
}
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
f, a, w := tt.test()
|
|
|
|
closeFuncs := make([]func(), len(w.targets))
|
|
calledFuncs := make([]func() bool, len(w.targets))
|
|
for i := range w.targets {
|
|
url, closeF, calledF := testServerCall(
|
|
exec_repo.ContextInfoFromRequest(a.job.Args),
|
|
time.Second,
|
|
w.sendStatusCode,
|
|
nil,
|
|
)
|
|
w.targets[i].Endpoint = url
|
|
closeFuncs[i] = closeF
|
|
calledFuncs[i] = calledF
|
|
}
|
|
|
|
data, err := json.Marshal(w.targets)
|
|
require.NoError(t, err)
|
|
a.job.Args.TargetsData = data
|
|
|
|
err = newExecutionWorker(f).Work(
|
|
authz.WithInstanceID(context.Background(), instanceID),
|
|
a.job,
|
|
)
|
|
|
|
if w.err != nil {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
assert.NoError(t, err)
|
|
|
|
for _, closeF := range closeFuncs {
|
|
closeF()
|
|
}
|
|
for _, calledF := range calledFuncs {
|
|
assert.True(t, calledF())
|
|
}
|
|
})
|
|
}
|
|
}
|