akasha 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +10 -0
- data/.travis.yml +22 -30
- data/CHANGELOG.md +7 -0
- data/Dockerfile +24 -0
- data/Gemfile +2 -2
- data/Gemfile.lock +36 -10
- data/README.md +62 -55
- data/Rakefile +8 -3
- data/akasha.gemspec +13 -6
- data/bin/console +3 -3
- data/bin/integration-tests.sh +40 -0
- data/docker/docker-compose.yml +17 -0
- data/examples/sinatra/Gemfile +3 -3
- data/examples/sinatra/Gemfile.lock +28 -6
- data/examples/sinatra/app.rb +54 -23
- data/lib/akasha.rb +2 -0
- data/lib/akasha/aggregate/syntax_helpers.rb +1 -1
- data/lib/akasha/async_event_router.rb +44 -0
- data/lib/akasha/changeset.rb +3 -1
- data/lib/akasha/checkpoint/http_event_store_checkpoint.rb +45 -0
- data/lib/akasha/command_router.rb +11 -2
- data/lib/akasha/event.rb +8 -12
- data/lib/akasha/event_router.rb +10 -33
- data/lib/akasha/event_router_base.rb +39 -0
- data/lib/akasha/repository.rb +13 -0
- data/lib/akasha/storage/http_event_store.rb +39 -5
- data/lib/akasha/storage/http_event_store/client.rb +169 -0
- data/lib/akasha/storage/http_event_store/event_serializer.rb +34 -0
- data/lib/akasha/storage/http_event_store/projection_manager.rb +67 -0
- data/lib/akasha/storage/http_event_store/response_handler.rb +17 -0
- data/lib/akasha/storage/http_event_store/stream.rb +17 -17
- data/lib/akasha/storage/memory_event_store.rb +31 -1
- data/lib/akasha/storage/memory_event_store/stream.rb +12 -2
- data/lib/akasha/version.rb +1 -1
- metadata +121 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4078cc01d0c132a3b675ed21de42246880643c49
|
4
|
+
data.tar.gz: 0562b5f9607dea81f47720c680d3f5517c5190e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 114a107b63f0f25d9e21b5a00c8a4277885ca705ff7d324199a89e54004c41f410380608d7ba994e8d2f0e3dab220c8a09443c56ef36bc69c551f061d23b3366
|
7
|
+
data.tar.gz: 4ef66f8424b38a10ac6b8b4095e9a42f985babf00966d5e7d8928cc8fd588768cf1ee45baa516fe094a8b717ee560d44e5f5856028aa6af97eee806b88c3c1ca
|
data/.rubocop.yml
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
Style/FrozenStringLiteralComment:
|
2
2
|
Enabled: false
|
3
|
+
Metrics/AbcSize:
|
4
|
+
Max: 17
|
5
|
+
Metrics/BlockLength:
|
6
|
+
Exclude:
|
7
|
+
- spec/**/*_spec.rb
|
8
|
+
- akasha.gemspec
|
9
|
+
Metrics/ClassLength:
|
10
|
+
Max: 110
|
3
11
|
Metrics/LineLength:
|
4
12
|
Max: 120
|
5
13
|
Metrics/MethodLength:
|
6
14
|
Max: 15
|
15
|
+
Rails/Output:
|
16
|
+
Enabled: false
|
data/.travis.yml
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# Enables Travis to use their new container-based infrastructure
|
2
2
|
sudo: false
|
3
3
|
|
4
|
+
# Integration tests are using docker-compose
|
5
|
+
services:
|
6
|
+
- docker
|
7
|
+
|
4
8
|
# Build for Ruby
|
5
9
|
language: ruby
|
6
10
|
|
@@ -15,35 +19,23 @@ before_install:
|
|
15
19
|
- gem update bundler
|
16
20
|
|
17
21
|
# Specify which ruby versions you wish to run your tests on, each version will be used
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
# Define how to run your tests
|
25
|
-
script:
|
26
|
-
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
#
|
33
|
-
|
34
|
-
# - command_2
|
35
|
-
|
36
|
-
# Specify the recipients for email notification
|
37
|
-
#notifications:
|
38
|
-
# recipients:
|
39
|
-
# - email-address-1
|
40
|
-
# - email-address-2
|
22
|
+
matrix:
|
23
|
+
include:
|
24
|
+
- rvm: 2.3.6
|
25
|
+
- rvm: 2.5.1
|
26
|
+
- env: INTEGRATION_TESTS=true
|
27
|
+
|
28
|
+
# Define how to run your tests.
|
29
|
+
script:
|
30
|
+
- |
|
31
|
+
# Run either integration tests or specs.
|
32
|
+
case "$INTEGRATION_TESTS" in
|
33
|
+
true) bin/integration-tests.sh ;;
|
34
|
+
*) bundle exec rspec --tag ~integration ;;
|
35
|
+
esac
|
36
|
+
# Check if conventions are being followed.
|
37
|
+
- bundle exec rake rubocop
|
41
38
|
|
42
39
|
# Disable email notifications
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
# notifications:
|
47
|
-
# webhooks:
|
48
|
-
# urls:
|
49
|
-
# - https://webhooks.gitter.im/e/c6dbb9323007dfcf81df
|
40
|
+
notifications:
|
41
|
+
disabled: true
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## Version 0.3.0
|
4
|
+
|
5
|
+
* Asynchronous event listeners (`AsyncEventRouter`).
|
6
|
+
* Simplified initialization of event- and command routers.
|
7
|
+
* Remove dependency on the `http_event_store` gem.
|
8
|
+
* `Event#metadata` is no longer OpenStruct.
|
9
|
+
|
3
10
|
## Version 0.2.0
|
4
11
|
|
5
12
|
* Synchronous event listeners (see `examples/sinatra/app.rb`).
|
data/Dockerfile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
FROM ruby:2.5.1-alpine3.7
|
2
|
+
RUN set -eux; \
|
3
|
+
apk update; \
|
4
|
+
apk add git openssl; \
|
5
|
+
apk add build-base libffi-dev
|
6
|
+
|
7
|
+
ENV DOCKERIZE_VERSION v0.6.1
|
8
|
+
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
|
9
|
+
&& tar -C /usr/local/bin -xzvf dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
|
10
|
+
&& rm dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz
|
11
|
+
|
12
|
+
COPY Gemfile /var/akasha/
|
13
|
+
COPY Gemfile.lock /var/akasha/
|
14
|
+
COPY akasha.gemspec /var/akasha/
|
15
|
+
COPY lib/akasha/version.rb /var/akasha/lib/akasha/version.rb
|
16
|
+
|
17
|
+
RUN set -eux; \
|
18
|
+
cd /var/akasha; \
|
19
|
+
bundle
|
20
|
+
|
21
|
+
COPY . /var/akasha/
|
22
|
+
WORKDIR /var/akasha/
|
23
|
+
|
24
|
+
CMD dockerize -wait http://eventstore:2113 bundle exec rspec --tag integration
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,35 +1,46 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
akasha (0.
|
5
|
-
|
4
|
+
akasha (0.3.0)
|
5
|
+
corefines (~> 1.11)
|
6
|
+
faraday (~> 0.15)
|
7
|
+
faraday_middleware
|
8
|
+
rack (~> 2.0)
|
9
|
+
retries (~> 0.0)
|
10
|
+
typhoeus (~> 1.3)
|
6
11
|
|
7
12
|
GEM
|
8
13
|
remote: https://rubygems.org/
|
9
14
|
specs:
|
15
|
+
ast (2.3.0)
|
10
16
|
byebug (10.0.2)
|
11
17
|
coderay (1.1.2)
|
18
|
+
corefines (1.11.0)
|
12
19
|
diff-lcs (1.3)
|
20
|
+
ethon (0.11.0)
|
21
|
+
ffi (>= 1.3.0)
|
13
22
|
faraday (0.15.2)
|
14
23
|
multipart-post (>= 1.2, < 3)
|
15
24
|
faraday_middleware (0.12.2)
|
16
25
|
faraday (>= 0.7.4, < 1.0)
|
17
|
-
|
18
|
-
http_event_store (0.2.2)
|
19
|
-
faraday
|
20
|
-
faraday_middleware
|
21
|
-
hashie
|
22
|
-
json
|
23
|
-
json (2.1.0)
|
26
|
+
ffi (1.9.25)
|
24
27
|
method_source (0.9.0)
|
25
28
|
multipart-post (2.0.0)
|
29
|
+
parallel (1.12.0)
|
30
|
+
parser (2.4.0.0)
|
31
|
+
ast (~> 2.2)
|
32
|
+
powerpack (0.1.1)
|
26
33
|
pry (0.11.3)
|
27
34
|
coderay (~> 1.1.0)
|
28
35
|
method_source (~> 0.9.0)
|
29
36
|
pry-byebug (3.6.0)
|
30
37
|
byebug (~> 10.0)
|
31
38
|
pry (~> 0.10)
|
39
|
+
rack (2.0.5)
|
40
|
+
rainbow (2.2.2)
|
41
|
+
rake
|
32
42
|
rake (10.5.0)
|
43
|
+
retries (0.0.5)
|
33
44
|
rspec (3.7.0)
|
34
45
|
rspec-core (~> 3.7.0)
|
35
46
|
rspec-expectations (~> 3.7.0)
|
@@ -43,7 +54,20 @@ GEM
|
|
43
54
|
diff-lcs (>= 1.2.0, < 2.0)
|
44
55
|
rspec-support (~> 3.7.0)
|
45
56
|
rspec-support (3.7.1)
|
57
|
+
rspec-wait (0.0.9)
|
58
|
+
rspec (>= 3, < 4)
|
59
|
+
rubocop (0.50.0)
|
60
|
+
parallel (~> 1.10)
|
61
|
+
parser (>= 2.3.3.1, < 3.0)
|
62
|
+
powerpack (~> 0.1)
|
63
|
+
rainbow (>= 2.2.2, < 3.0)
|
64
|
+
ruby-progressbar (~> 1.7)
|
65
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
66
|
+
ruby-progressbar (1.9.0)
|
46
67
|
timecop (0.9.1)
|
68
|
+
typhoeus (1.3.0)
|
69
|
+
ethon (>= 0.9.0)
|
70
|
+
unicode-display_width (1.3.0)
|
47
71
|
|
48
72
|
PLATFORMS
|
49
73
|
ruby
|
@@ -54,7 +78,9 @@ DEPENDENCIES
|
|
54
78
|
pry-byebug
|
55
79
|
rake (~> 10.0)
|
56
80
|
rspec (~> 3.7)
|
57
|
-
|
81
|
+
rspec-wait (~> 0.0.9)
|
82
|
+
rubocop (~> 0.50)
|
83
|
+
timecop (~> 0.9)
|
58
84
|
|
59
85
|
BUNDLED WITH
|
60
86
|
1.16.2
|
data/README.md
CHANGED
@@ -20,74 +20,81 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
-
|
24
|
-
This library makes no assumptions about any web framework, you can use it in any way you see fit.
|
23
|
+
There is an example Sinatra app under `examples/sinatra` showing how to use the library in a web application.
|
24
|
+
This library itself makes no assumptions about any web framework, you can use it in any way you see fit.
|
25
25
|
|
26
|
-
|
27
|
-
require 'akasha'
|
28
|
-
require 'sinatra'
|
29
|
-
|
30
|
-
class User < Akasha::Aggregate
|
31
|
-
def sign_up(email:, password:, admin: false, **)
|
32
|
-
changeset.append(:user_signed_up, email: email, password: password, admin: admin)
|
33
|
-
end
|
34
|
-
|
35
|
-
def on_user_signed_up(email:, password:, admin:, **)
|
36
|
-
@email = email
|
37
|
-
@password = password
|
38
|
-
@admin = admin
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
|
43
|
-
before do
|
44
|
-
@router = Akasha::CommandRouter.new
|
45
|
-
|
46
|
-
# Aggregates will load from and save to in-memory storage.
|
47
|
-
repository = Akasha::Repository.new(Akasha::Storage::MemoryEventStore.new)
|
48
|
-
Akasha::Aggregate.connect!(repository)
|
49
|
-
|
50
|
-
# This is how you link commands to aggregates.
|
51
|
-
@router.register_default_route(:sign_up, User)
|
52
|
-
|
53
|
-
# Nearly identital to the default handling above but we're setting the admin
|
54
|
-
# flag to demo custom command handling.
|
55
|
-
@router.register_route(:sign_up_admin) do |aggregate_id, **data|
|
56
|
-
user = User.find_or_create(aggregate_id)
|
57
|
-
user.sign_up(email: data[:email], password: data[:password], admin: true)
|
58
|
-
user.save!
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
post '/users/:user_id' do # With CQRS client pass unique aggregate ids.
|
63
|
-
@router.route!(:sign_up,
|
64
|
-
params[:user_id],
|
65
|
-
email: params[:email],
|
66
|
-
password: params[:password])
|
67
|
-
'OK'
|
68
|
-
end
|
69
|
-
```
|
70
|
-
|
71
|
-
> Currently, only memory-based repository is supported.
|
72
|
-
|
73
|
-
## Next steps
|
26
|
+
## TODO
|
74
27
|
|
75
28
|
- [x] Command routing (default and user-defined)
|
76
29
|
- [x] Synchronous EventHandler
|
77
30
|
- [x] HTTP Eventstore storage backend
|
78
|
-
- [
|
79
|
-
- [
|
80
|
-
- [
|
31
|
+
- [x] Event#id for better idempotence (validate this claim)
|
32
|
+
- [x] Async EventHandlers (storing cursors in Eventstore, configurable durability guarantees)
|
33
|
+
- [x] Uniform intetrface for Client -- use Event.
|
34
|
+
- [x] Rewrite Client
|
35
|
+
- [x] Refactor Client code
|
36
|
+
- [x] Take care of created_at/updated_at (saved_at?)
|
37
|
+
- [x] Tests for HttpEventStore
|
38
|
+
- [x] Projections
|
39
|
+
- [x] Test for AsyncEventRouter using events not aggregate
|
40
|
+
- [x] BUG: Projection reorders events
|
41
|
+
- [x] Simplify AsyncEventRouter init
|
42
|
+
- [x] SyncEventRouter => EventRouter
|
43
|
+
- [x] Metadata not persisted
|
44
|
+
- [x] Refactoring & simplification.
|
45
|
+
- [x] Hash-based event and command router
|
46
|
+
- [x] Do we need EventListener class? Yes.
|
47
|
+
- [x] Assymetry between data and metadata.
|
48
|
+
- [x] Faster shutdown.
|
49
|
+
- [ ] Namespacing for events and aggregates and the projection
|
81
50
|
- [ ] Version-based concurrency
|
82
|
-
- [ ]
|
51
|
+
- [ ] Telemetry (Dogstatsd)
|
83
52
|
- [ ] Socket-based Eventstore storage backend
|
84
53
|
|
54
|
+
|
85
55
|
## Development
|
86
56
|
|
87
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
|
57
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. See Running tests.
|
58
|
+
|
59
|
+
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
88
60
|
|
89
61
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
90
62
|
|
63
|
+
|
64
|
+
## Running tests
|
65
|
+
|
66
|
+
Some tests require Eventstore to be running and available via `eventstore` host name. You can exclude these specs:
|
67
|
+
|
68
|
+
```
|
69
|
+
rspec --tag ~integration
|
70
|
+
```
|
71
|
+
|
72
|
+
The easiest way to run integration tests:
|
73
|
+
|
74
|
+
```
|
75
|
+
/bin/integration-tests.sh
|
76
|
+
```
|
77
|
+
|
78
|
+
This will use docker-compose to spin up containers containing the dependencies and tests themselves.
|
79
|
+
|
80
|
+
Because it's pretty slow, you may want to spin up a docker container containing event store:
|
81
|
+
|
82
|
+
```
|
83
|
+
docker run -e EVENTSTORE_START_STANDARD_PROJECTIONS=true --name akasha-eventstore -it -p 2113:2113 -p 1113:1113 -d eventstore/eventstore
|
84
|
+
```
|
85
|
+
|
86
|
+
and use RSpec run just integration specs like so:
|
87
|
+
|
88
|
+
```
|
89
|
+
rspec --tag integration
|
90
|
+
```
|
91
|
+
|
92
|
+
or run all tests:
|
93
|
+
|
94
|
+
```
|
95
|
+
rspec
|
96
|
+
```
|
97
|
+
|
91
98
|
## Contributing
|
92
99
|
|
93
100
|
Bug reports and pull requests are welcome on GitHub at https://github.com/bilus/akasha.
|
data/Rakefile
CHANGED
@@ -1,6 +1,11 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'rubocop/rake_task'
|
4
|
+
|
5
|
+
RuboCop::RakeTask.new(:rubocop) do |task|
|
6
|
+
task.options = ['--rails', '--display-cop-names']
|
7
|
+
end
|
3
8
|
|
4
9
|
RSpec::Core::RakeTask.new(:spec)
|
5
10
|
|
6
|
-
task :
|
11
|
+
task default: :spec
|
data/akasha.gemspec
CHANGED
@@ -9,9 +9,9 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ['Marcin Bilski']
|
10
10
|
spec.email = ['marcin@tooploox.com']
|
11
11
|
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
14
|
-
spec.homepage =
|
12
|
+
spec.summary = 'CQRS library for Ruby'
|
13
|
+
spec.description = 'A simple CQRS library for Ruby.'
|
14
|
+
spec.homepage = 'https://github.com/bilus/akasha'
|
15
15
|
spec.license = 'MIT'
|
16
16
|
|
17
17
|
# Specify which files should be added to the gem when it is released.
|
@@ -23,11 +23,18 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
24
|
spec.require_paths = ['lib']
|
25
25
|
|
26
|
-
spec.add_dependency '
|
26
|
+
spec.add_dependency 'faraday', '~> 0.15'
|
27
|
+
spec.add_dependency 'faraday_middleware'
|
28
|
+
spec.add_dependency 'typhoeus', '~> 1.3'
|
29
|
+
spec.add_dependency 'rack', '~> 2.0'
|
30
|
+
spec.add_dependency 'retries', '~> 0.0'
|
31
|
+
spec.add_dependency 'corefines', '~>1.11'
|
27
32
|
|
28
33
|
spec.add_development_dependency 'bundler', '~> 1.16'
|
34
|
+
spec.add_development_dependency 'pry-byebug'
|
29
35
|
spec.add_development_dependency 'rake', '~> 10.0'
|
30
36
|
spec.add_development_dependency 'rspec', '~> 3.7'
|
31
|
-
spec.add_development_dependency '
|
32
|
-
spec.add_development_dependency '
|
37
|
+
spec.add_development_dependency 'rspec-wait', '~> 0.0.9'
|
38
|
+
spec.add_development_dependency 'rubocop', '~> 0.50'
|
39
|
+
spec.add_development_dependency 'timecop', '~> 0.9'
|
33
40
|
end
|
data/bin/console
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'akasha'
|
5
5
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +10,5 @@ require "akasha"
|
|
10
10
|
# require "pry"
|
11
11
|
# Pry.start
|
12
12
|
|
13
|
-
require
|
13
|
+
require 'irb'
|
14
14
|
IRB.start(__FILE__)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# define some colors to use for output
|
4
|
+
RED='\033[0;31m'
|
5
|
+
GREEN='\033[0;32m'
|
6
|
+
NC='\033[0m'
|
7
|
+
|
8
|
+
PROJECT=ci
|
9
|
+
|
10
|
+
# kill and remove any running containers
|
11
|
+
cleanup () {
|
12
|
+
docker-compose -p $PROJECT down
|
13
|
+
}
|
14
|
+
# catch unexpected failures, do cleanup and output an error message
|
15
|
+
trap 'cleanup ; printf "${RED}Tests Failed For Unexpected Reasons${NC}\n"'\
|
16
|
+
HUP INT QUIT PIPE TERM
|
17
|
+
cd docker
|
18
|
+
echo "Starting tests"
|
19
|
+
# build and run the composed services
|
20
|
+
docker-compose -p $PROJECT up -d --build
|
21
|
+
if [ $? -ne 0 ] ; then
|
22
|
+
printf "${RED}Docker Compose Failed${NC}\n"
|
23
|
+
exit -1
|
24
|
+
fi
|
25
|
+
echo "Waiting for tests to finish"
|
26
|
+
# wait for the test service to complete and grab the exit code
|
27
|
+
TEST_EXIT_CODE=`docker wait ${PROJECT}_tests_1`
|
28
|
+
echo "Listing docker logs"
|
29
|
+
# output the logs for the test (for clarity)
|
30
|
+
docker logs ${PROJECT}_tests_1
|
31
|
+
# inspect the output of the test and display respective message
|
32
|
+
if [ -z ${TEST_EXIT_CODE+x} ] || [ "$TEST_EXIT_CODE" -ne 0 ] ; then
|
33
|
+
printf "${RED}Tests Failed${NC} - Exit Code: $TEST_EXIT_CODE\n"
|
34
|
+
else
|
35
|
+
printf "${GREEN}Tests Passed${NC}\n"
|
36
|
+
fi
|
37
|
+
# call the cleanup fuction
|
38
|
+
cleanup
|
39
|
+
# exit the script with the same code as the test service code
|
40
|
+
exit $TEST_EXIT_CODE
|