Skip to content

Commit

Permalink
Update task test to cover data object relation
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-dobermann committed Dec 16, 2024
1 parent b7fd8a4 commit a27f013
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 68 deletions.
80 changes: 31 additions & 49 deletions pkg/model/activities/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func (t *Task) LoadData(ctx context.Context) error {
errs.E(err))
}

if err := dii[index].Subject().Structure().Update(v); err != nil {
if err := dii[index].Subject().Structure().Update(v.Structure().Get()); err != nil {
return errs.New(
errs.M("couldn't update input %q", dii[index].Name()),
errs.C(errorClass, errs.OperationFailed),
Expand Down Expand Up @@ -237,40 +237,52 @@ func (t *Task) updateOutputs(s scope.Scope) ([]*data.Parameter, error) {

// Outputs returns a list of output parameters of the Task
func (t *Task) Outputs() []*data.ItemAwareElement {
outputs := []*data.ItemAwareElement{}
return t.getParams(data.Output)
}

opp, _ := t.IoSpec.Parameters(data.Output)
for _, op := range opp {
outputs = append(outputs, &op.ItemAwareElement)
// BindOutgoing adds new outgoing data association.
func (t *Task) BindOutgoing(oa *data.Association) error {
return t.bindAssociation(oa, data.Output)
}

// getParams returns a list of the Task parameters input or output according to
// direction dir.
func (t *Task) getParams(dir data.Direction) []*data.ItemAwareElement {
pp := []*data.ItemAwareElement{}

params, _ := t.IoSpec.Parameters(dir)
for _, p := range params {
pp = append(pp, &p.ItemAwareElement)
}

return outputs
return pp
}

// BindOutgoing adds new outgoing data association.
func (t *Task) BindOutgoing(oa *data.Association) error {
if oa == nil {
// bindAssociation binds data association to the Task according to dir either
// input or output.
func (t *Task) bindAssociation(a *data.Association, dir data.Direction) error {
if a == nil {
return errs.New(
errs.M("couldn't bind empty association"),
errs.C(errorClass, errs.EmptyNotAllowed))
}

if slices.ContainsFunc(
t.dataAssociations[data.Output],
func(a *data.Association) bool {
return a.Id() == oa.Id()
t.dataAssociations[dir],
func(da *data.Association) bool {
return da.Id() == a.Id()
}) {
return errs.New(
errs.M("association already binded"),
errs.C(errorClass, errs.DuplicateObject),
errs.D("association_id", oa.Id()))
errs.D("association_id", a.Id()))
}

// TODO: Consider checking existence of output parameter equal to
// oa source.
// TODO: Consider checking existence of parameter equal to
// a source or target.

t.dataAssociations[data.Output] = append(
t.dataAssociations[data.Output], oa)
t.dataAssociations[dir] = append(
t.dataAssociations[dir], a)

return nil
}
Expand All @@ -279,42 +291,12 @@ func (t *Task) BindOutgoing(oa *data.Association) error {

// Inputs returns list of input parameters's ItemAwareElements.
func (t *Task) Inputs() []*data.ItemAwareElement {
inputs := []*data.ItemAwareElement{}

ipp, _ := t.IoSpec.Parameters(data.Input)
for _, ip := range ipp {
inputs = append(inputs, &ip.ItemAwareElement)
}

return inputs
return t.getParams(data.Input)
}

// BindIncoming adds new incoming data association to the Task.
func (t *Task) BindIncoming(ia *data.Association) error {
if ia == nil {
return errs.New(
errs.M("couldn't bind empty association"),
errs.C(errorClass, errs.EmptyNotAllowed))
}

if slices.ContainsFunc(
t.dataAssociations[data.Input],
func(a *data.Association) bool {
return a.Id() == ia.Id()
}) {
return errs.New(
errs.M("association already binded"),
errs.C(errorClass, errs.DuplicateObject),
errs.D("association_id", ia.Id()))
}

// TODO: Consider checking existence of input parameter equal to
// oa source.

t.dataAssociations[data.Input] = append(
t.dataAssociations[data.Input], ia)

return nil
return t.bindAssociation(ia, data.Input)
}

// -----------------------------------------------------------------------------
Expand Down
98 changes: 82 additions & 16 deletions pkg/model/activities/task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package activities_test
import (
"context"
"fmt"
"reflect"
"slices"
"testing"

Expand All @@ -11,6 +12,7 @@ import (
"github.com/dr-dobermann/gobpm/pkg/model/activities"
"github.com/dr-dobermann/gobpm/pkg/model/data"
"github.com/dr-dobermann/gobpm/pkg/model/data/values"
dataobjects "github.com/dr-dobermann/gobpm/pkg/model/data_objects"
"github.com/dr-dobermann/gobpm/pkg/model/flow"
"github.com/dr-dobermann/gobpm/pkg/model/foundation"
"github.com/dr-dobermann/gobpm/pkg/model/options"
Expand Down Expand Up @@ -80,41 +82,105 @@ func TestTaskData(t *testing.T) {
task, err := activities.NewTask(
"test",
data.WithProperties(props...),
activities.WithoutParams())
activities.WithSet("input set", "",
data.Input, data.DefaultSet,
[]*data.Parameter{
data.MustParameter("y param",
data.MustItemAwareElement(
data.MustItemDefinition(
values.NewVariable(100.500),
foundation.WithId("y")),
data.ReadyDataState)),
}),
activities.WithSet("output set", "",
data.Output, data.DefaultSet,
[]*data.Parameter{
data.MustParameter(
"y param",
data.MustItemAwareElement(
data.MustItemDefinition(
values.NewVariable(0.0),
foundation.WithId("y")),
nil)),
}))
require.NoError(t, err)

// set task data path
dp, err := scope.NewDataPath("/task")
require.NoError(t, err)

s := mockscope.NewMockScope(t)
s.On("LoadData", task, mock.AnythingOfType("*data.Property"), mock.AnythingOfType("*data.Property")).
s.On("LoadData", task,
mock.AnythingOfType("*data.Property"),
mock.AnythingOfType("*data.Property"),
mock.AnythingOfType("*data.Parameter")).
Return(
func(ndl scope.NodeDataLoader, dd ...data.Data) error {
for _, d := range dd {
t.Log(" >> got data: ", d.Name())

p, ok := d.(*data.Property)
if !ok {
return fmt.Errorf("couldn't convert data %q to Property", d.Name())
t.Log(" >> got data: ", d.Name(), " = ", d.Value().Get())

switch dv := d.(type) {
case *data.Property:
if idx := slices.IndexFunc(
props,
func(prop *data.Property) bool {
return dv.Id() == prop.Id()
}); idx == -1 {
return fmt.Errorf("couldn't find property %q", d.Name())
}
case *data.Parameter:
if dv.Name() != "y param" {
return fmt.Errorf("invalid parameter name")
}
default:
return fmt.Errorf("TEST: invalid type of data: %s",
reflect.TypeOf(d).String())
}

if idx := slices.IndexFunc(
props,
func(prop *data.Property) bool {
return p.Id() == prop.Id()
}); idx == -1 {
return fmt.Errorf("couldn't find property %q", d.Name())
}
}

return nil
})
s.EXPECT().
GetDataById(dp, "y").
Return(data.MustItemAwareElement(
data.MustItemDefinition(
values.NewVariable(23.02),
foundation.WithId("y")),
data.ReadyDataState), nil)

dp, err := scope.NewDataPath("/task")
err = task.RegisterData(dp, s)
require.NoError(t, err)

err = task.RegisterData(dp, s)
// add association to DataObject
inpDO, err := dataobjects.New(
"input data object",
data.MustItemDefinition(
values.NewVariable(23.02),
foundation.WithId("y")),
data.ReadyDataState)
require.NoError(t, err)
require.NoError(t, inpDO.AssociateTarget(task, nil))

outDO, err := dataobjects.New(
"output data object",
data.MustItemDefinition(
values.NewVariable(11.09),
foundation.WithId("y")),
nil)
require.NoError(t, err)
require.NoError(t, outDO.AssociateSource(task, []string{"y"}, nil))

require.NoError(t, task.LoadData(context.Background()))

// check input parameters
inParams, err := task.IoSpec.Parameters(data.Input)
require.NoError(t, err)
require.Len(t, inParams, 1)
require.Equal(t, 23.02, inParams[0].Subject().Structure().Get())

require.NoError(t, task.UploadData(context.Background(), s))
require.Equal(t, 23.02, outDO.Subject().Structure().Get())
})

t.Run("data associations",
Expand Down
17 changes: 15 additions & 2 deletions pkg/model/data/association.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func NewAssociation(
return aCfg.newAssociation()
}

// UpdateSource updates source with a new value.
// UpdateSource updates association source and target with a new value.
func (a *Association) UpdateSource(
ctx context.Context,
iDef *ItemDefinition,
Expand All @@ -143,6 +143,7 @@ func (a *Association) UpdateSource(
errs.C(errorClass, errs.EmptyNotAllowed))
}

// find correlated source ItemAwareElement
iae, ok := a.sources[iDef.Id()]
if !ok {
return errs.New(
Expand All @@ -152,11 +153,13 @@ func (a *Association) UpdateSource(
errs.D("source_id", iDef.Id()))
}

// update source and its status
if err := iae.Value().Update(iDef.structure.Get()); err != nil {
return errs.New(
errs.M("source updating failed"),
errs.C(errorClass, errs.OperationFailed),
errs.E(err),
errs.D("source_id", iDef.Id()),
errs.D("association_id", a.Id()))
}

Expand All @@ -168,7 +171,17 @@ func (a *Association) UpdateSource(
errs.D("source_id", iae.ItemDefinition().Id()))
}

if err := a.target.UpdateState(UnavailableDataState); err != nil {
// update target and its status
if err := a.target.Value().Update(iDef.structure.Get()); err != nil {
return errs.New(
errs.M("target updating failed"),
errs.C(errorClass, errs.OperationFailed),
errs.E(err),
errs.D("target_id", a.target.subject.Id()),
errs.D("association_id", a.Id()))
}

if err := a.target.UpdateState(ReadyDataState); err != nil {
return errs.New(
errs.M("association target state update failed"),
errs.C(errorClass, errs.OperationFailed),
Expand Down
2 changes: 1 addition & 1 deletion pkg/model/data/association_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func TestAssociations(t *testing.T) {
foundation.WithId("source")))
require.NoError(t, err)

require.False(t, a.IsReady())
require.True(t, a.IsReady())

v, err = a.Value(context.Background())

Expand Down

0 comments on commit a27f013

Please sign in to comment.