Skip to content
This repository was archived by the owner on Jun 6, 2025. It is now read-only.

Commit 7e49cb9

Browse files
authored
fix: testkit mock command handling on failures (#349)
1 parent 2efb8c3 commit 7e49cb9

20 files changed

Lines changed: 655 additions & 176 deletions

.circleci/config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ jobs:
220220
command: |
221221
npm ci
222222
npm install ../sdk/kalix-io-kalix-javascript-sdk-0.0.0.tgz
223+
npm test
223224
npm pack
224225
- run:
225226
name: "Test JS Value Entity Counter sample"

.prettierignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ jsdoc.json
1111
/tck/generated/**/*.*
1212
/tck/proto/**/*.*
1313
/testkit/dist/**/*.*
14-
/testkit/integration-test/proto.*
14+
/testkit/example/generated/**/*.*

samples/js/valueentity-counter/test/counter.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ describe("CounterService", () => {
4848
const entity = new MockValueEntity(counter, entityId);
4949
const result = entity.handleCommand("Increase", { entityId: entityId, value: -2 });
5050

51-
expect(result).to.deep.equal({});
51+
expect(result).to.be.undefined;
5252
expect(entity.error).to.be.equal(`Increase requires a positive value. It was [-2].`);
5353
});
5454
});

samples/ts/ts-valueentity-counter/test/counter.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ describe("CounterService", () => {
5656
value: -2
5757
});
5858

59-
expect(result).to.deep.equal({});
59+
expect(result).to.be.undefined;
6060
expect(entity.error).to.be.equal(`Increase requires a positive value. It was [-2].`);
6161
});
6262
});

testkit/.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
dist
2-
integration-test/proto.*
3-
integration-test/user-function.desc
2+
example/generated
43
*.tgz
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright 2021 Lightbend Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
syntax = "proto3";
16+
17+
package com.example;
18+
19+
import "google/protobuf/any.proto";
20+
import "kalix/annotations.proto";
21+
22+
message ExampleState {
23+
string field1 = 1;
24+
string field2 = 2;
25+
}
26+
27+
message ExampleEventOne {
28+
string field = 1;
29+
}
30+
31+
message ExampleEventTwo {
32+
string field = 1;
33+
}
34+
35+
message In {
36+
string field = 1 [(kalix.field).entity_key = true];
37+
}
38+
39+
message Out {
40+
string field = 1;
41+
}
42+
43+
service ExampleActionService {
44+
option (kalix.service) = {
45+
type : SERVICE_TYPE_ACTION
46+
};
47+
48+
rpc DoSomething(In) returns (Out);
49+
rpc StreamSomething(In) returns (stream Out);
50+
rpc Fail(In) returns (Out);
51+
}
52+
53+
option (kalix.file).value_entity = {
54+
name: "ExampleValueEntity"
55+
entity_type: "example-value-entity"
56+
state: "ExampleState"
57+
};
58+
59+
service ExampleValueEntityService {
60+
option (kalix.service) = {
61+
type : SERVICE_TYPE_ENTITY
62+
component : "ExampleValueEntity"
63+
};
64+
65+
rpc DoSomethingOne(In) returns (Out);
66+
rpc DoSomethingTwo(In) returns (Out);
67+
rpc Fail(In) returns (Out);
68+
}
69+
70+
option (kalix.file).event_sourced_entity = {
71+
name: "ExampleEventSourcedEntity"
72+
entity_type: "example-event-sourced-entity"
73+
state: "ExampleState"
74+
events: "ExampleEventOne"
75+
events: "ExampleEventTwo"
76+
};
77+
78+
service ExampleEventSourcedEntityService {
79+
option (kalix.service) = {
80+
type : SERVICE_TYPE_ENTITY
81+
component : "ExampleEventSourcedEntity"
82+
};
83+
84+
rpc DoSomethingOne(In) returns (Out);
85+
rpc DoSomethingTwo(In) returns (Out);
86+
rpc Fail(In) returns (Out);
87+
}

testkit/example/src/action.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2021 Lightbend Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { Action, GrpcStatus, Reply } from '@kalix-io/kalix-javascript-sdk';
18+
import { ExampleActionService } from '../types/action';
19+
20+
const action: ExampleActionService = new Action(
21+
'example.proto',
22+
'com.example.ExampleActionService',
23+
{
24+
includeDirs: ['example/proto'],
25+
},
26+
);
27+
28+
action.commandHandlers = {
29+
DoSomething: (input) => {
30+
return { field: 'Received: ' + input.field };
31+
},
32+
StreamSomething: (input, ctx) => {
33+
ctx.write({ field: 'Received: ' + input.field });
34+
ctx.end();
35+
},
36+
Fail: () => {
37+
return Reply.failure('some-error', GrpcStatus.AlreadyExists);
38+
},
39+
};
40+
41+
export default action;
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright 2021 Lightbend Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import {
18+
EventSourcedEntity,
19+
GrpcStatus,
20+
Reply,
21+
} from '@kalix-io/kalix-javascript-sdk';
22+
import { ExampleEventSourcedEntityService } from '../types/event-sourced-entity';
23+
24+
const entity: ExampleEventSourcedEntityService = new EventSourcedEntity(
25+
'example.proto',
26+
'com.example.ExampleEventSourcedEntityService',
27+
'example-event-sourced-entity',
28+
{
29+
includeDirs: ['example/proto'],
30+
},
31+
);
32+
33+
const ExampleState = entity.lookupType('com.example.ExampleState');
34+
const ExampleEventOne = entity.lookupType('com.example.ExampleEventOne');
35+
const ExampleEventTwo = entity.lookupType('com.example.ExampleEventTwo');
36+
37+
entity.setInitial(() => ExampleState.create({}));
38+
39+
entity.setBehavior(() => {
40+
return {
41+
commandHandlers: {
42+
DoSomethingOne: (input, _state, ctx) => {
43+
ctx.emit(ExampleEventOne.create({ field: input.field }));
44+
return Reply.message({
45+
field: 'EventSourcedEntity received: ' + input.field,
46+
});
47+
},
48+
DoSomethingTwo: (input, _state, ctx) => {
49+
ctx.emit(ExampleEventTwo.create({ field: input.field }));
50+
return new Promise((resolve) => {
51+
setTimeout(
52+
() =>
53+
resolve({
54+
field: 'EventSourcedEntity async received: ' + input.field,
55+
}),
56+
1000,
57+
);
58+
});
59+
},
60+
Fail: () => Reply.failure('some-error', GrpcStatus.AlreadyExists),
61+
},
62+
eventHandlers: {
63+
ExampleEventOne(event, state) {
64+
state.field1 = event.field;
65+
return state;
66+
},
67+
ExampleEventTwo(event, state) {
68+
state.field2 = event.field;
69+
return state;
70+
},
71+
},
72+
};
73+
});
74+
75+
export default entity;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2021 Lightbend Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { GrpcStatus, Reply, ValueEntity } from '@kalix-io/kalix-javascript-sdk';
18+
import { ExampleValueEntityService } from '../types/value-entity';
19+
20+
const entity: ExampleValueEntityService = new ValueEntity(
21+
'example.proto',
22+
'com.example.ExampleValueEntityService',
23+
'example-value-entity',
24+
{
25+
includeDirs: ['example/proto'],
26+
},
27+
);
28+
29+
const ExampleState = entity.lookupType('com.example.ExampleState');
30+
31+
entity.setInitial(() => ExampleState.create({}));
32+
33+
entity.commandHandlers = {
34+
DoSomethingOne: (input, state, ctx) => {
35+
state.field1 = input.field;
36+
ctx.updateState(state);
37+
return Reply.message({ field: `ValueEntity received: ${input.field}` });
38+
},
39+
DoSomethingTwo: (input, state, ctx) => {
40+
state.field2 = input.field;
41+
ctx.updateState(state);
42+
return new Promise((resolve) => {
43+
setTimeout(
44+
() => resolve({ field: `ValueEntity async received: ${input.field}` }),
45+
1000,
46+
);
47+
});
48+
},
49+
Fail: () => Reply.failure('some-error', GrpcStatus.AlreadyExists),
50+
};
51+
52+
export default entity;

testkit/example/types/action.d.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2021 Lightbend Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
// Types that codegen will generate.
18+
19+
import { Action, CommandReply } from '@kalix-io/kalix-javascript-sdk';
20+
import * as proto from '../generated/proto';
21+
22+
export declare namespace api {
23+
type In = proto.com.example.In;
24+
type IOut = proto.com.example.IOut;
25+
type IAny = proto.google.protobuf.IAny;
26+
}
27+
28+
export declare namespace ExampleActionService {
29+
type CommandHandlers = {
30+
DoSomething: (
31+
command: api.In,
32+
ctx: Action.UnaryCommandContext<api.IOut>,
33+
) => CommandReply<api.IOut> | Promise<CommandReply<api.IOut>>;
34+
StreamSomething: (
35+
command: api.In,
36+
ctx: Action.StreamedOutCommandContext<api.IOut>,
37+
) => void;
38+
Fail: (
39+
command: api.In,
40+
ctx: Action.UnaryCommandContext<api.IOut>,
41+
) => CommandReply<api.IOut> | Promise<CommandReply<api.IOut>>;
42+
};
43+
}
44+
45+
export declare type ExampleActionService =
46+
Action<ExampleActionService.CommandHandlers>;

0 commit comments

Comments
 (0)