sqewer 5.0.3 → 5.0.4

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.
@@ -1,69 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe Sqewer::MiddlewareStack do
4
- it 'returns a default instance' do
5
- stacks = (1..100).map { described_class.default }
6
- expect(stacks.uniq.length).to eq(1)
7
- end
8
-
9
- describe '#around_deserialization' do
10
- it 'works without any handlers added' do
11
- stack = described_class.new
12
- prepare_result = stack.around_deserialization(nil, 'msg-123', '{"body":"some text"}') { :prepared }
13
- expect(prepare_result).to eq(:prepared)
14
- end
15
-
16
- it 'works with an entire stack' do
17
- called = []
18
- handler = double('Some middleware')
19
- allow(handler).to receive(:around_deserialization){|*a, &blk|
20
- called << a
21
- blk.call
22
- }
23
-
24
- stack = described_class.new
25
- stack << handler
26
- stack << double('Object that does not handle around_deserialization')
27
- stack << handler
28
- stack << handler
29
- result = stack.around_deserialization(nil, 'msg-123', '{"body":"some text"}') { :foo }
30
- expect(result).to eq(:foo)
31
- expect(called).not_to be_empty
32
-
33
- expect(called).to eq([
34
- [nil, "msg-123", "{\"body\":\"some text\"}"],
35
- [nil, "msg-123", "{\"body\":\"some text\"}"],
36
- [nil, "msg-123", "{\"body\":\"some text\"}"]]
37
- )
38
- end
39
- end
40
-
41
- describe '#around_execution' do
42
- it 'works without any handlers added' do
43
- stack = described_class.new
44
- prepare_result = stack.around_execution(nil, nil) { :prepared }
45
- expect(prepare_result).to eq(:prepared)
46
- end
47
-
48
- it 'works with an entire stack' do
49
- called = []
50
- handler = double('Some middleware')
51
- allow(handler).to receive(:around_execution){|*a, &blk|
52
- called << a
53
- blk.call
54
- }
55
-
56
- stack = described_class.new
57
- stack << handler
58
- stack << handler
59
- stack << double('Object that does not handle around_execution')
60
- stack << handler
61
-
62
- result = stack.around_execution(:some_job, :some_context) { :executed }
63
- expect(result).to eq(:executed)
64
- expect(called).not_to be_empty
65
-
66
- expect(called).to eq([[:some_job, :some_context], [:some_job, :some_context], [:some_job, :some_context]])
67
- end
68
- end
69
- end
@@ -1,118 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe Sqewer::Serializer do
4
- describe '.default' do
5
- it 'returns the same Serializer instance' do
6
- instances = (1..1000).map{ described_class.default }
7
- instances.uniq!
8
- expect(instances).to be_one
9
-
10
- the_instance = instances[0]
11
- expect(the_instance).to respond_to(:serialize)
12
- expect(the_instance).to respond_to(:unserialize)
13
- end
14
- end
15
-
16
- describe '#serialize' do
17
-
18
- it 'serializes a Job that has no to_h support without its kwargs' do
19
- class JobWithoutToHash
20
- end
21
- job = JobWithoutToHash.new
22
- expect(described_class.new.serialize(job)).to eq("{\"_job_class\":\"JobWithoutToHash\",\"_job_params\":null}")
23
- end
24
-
25
- it 'serializes a Struct along with its members and the class name' do
26
- class SomeJob < Struct.new :one, :two
27
- end
28
-
29
- job = SomeJob.new(123, [456])
30
-
31
- expect(described_class.new.serialize(job)).to eq("{\"_job_class\":\"SomeJob\",\"_job_params\":{\"one\":123,\"two\":[456]}}")
32
- end
33
-
34
- it 'adds _execute_after when the value is given' do
35
- class ThirdJob < Struct.new :one, :two
36
- end
37
-
38
- job = ThirdJob.new(123, [456])
39
- res = described_class.new.serialize(job, Time.now.to_i + 1500)
40
- parsed = JSON.load(res)
41
-
42
- expect(parsed["_execute_after"]).to be_within(10).of(Time.now.to_i + 1500)
43
- end
44
-
45
- it 'raises an exception if the object is of an anonymous class' do
46
- s = Struct.new(:foo)
47
- o = s.new(1)
48
- expect {
49
- described_class.new.serialize(o)
50
- }.to raise_error(described_class::AnonymousJobClass)
51
- end
52
- end
53
-
54
- it 'is able to roundtrip a job with a parameter' do
55
- require 'ks'
56
-
57
- class LeJob < Ks.strict(:some_data)
58
- end
59
-
60
- job = LeJob.new(some_data: 123)
61
-
62
- subject = described_class.new
63
-
64
- serialized = subject.serialize(job)
65
- restored = subject.unserialize(serialized)
66
-
67
- expect(restored).to be_kind_of(LeJob)
68
- expect(restored.some_data).to eq(123)
69
- end
70
-
71
- describe '#unserialize' do
72
- it 'wraps the job with a Resubmit when the _execute_after key hints that it is too early' do
73
- class EvenSimplerJob; end
74
-
75
- timestamp_way_in_the_future = Time.now.to_i + (60 * 60 * 24 * 3)
76
- blob = '{"_job_class": "EvenSimplerJob", "_execute_after": %d}' % timestamp_way_in_the_future
77
- built_job = described_class.new.unserialize(blob)
78
-
79
- expect(built_job).to be_kind_of(Sqewer::Resubmit)
80
- expect(built_job.execute_after).to eq(timestamp_way_in_the_future)
81
-
82
- embedded_job = built_job.job
83
- expect(embedded_job).to be_kind_of(EvenSimplerJob)
84
- end
85
-
86
- it 'builds a job without keyword arguments if its constructor does not need any kwargs' do
87
- class EvenSimplerJob; end
88
-
89
- blob = '{"_job_class": "EvenSimplerJob"}'
90
- built_job = described_class.new.unserialize(blob)
91
-
92
- expect(built_job).to be_kind_of(EvenSimplerJob)
93
-
94
- blob = '{"_job_class": "EvenSimplerJob", "_job_params": null}'
95
- built_job = described_class.new.unserialize(blob)
96
-
97
- expect(built_job).to be_kind_of(EvenSimplerJob)
98
- end
99
-
100
- it 'raises an error if the job does not accept the keyword arguments given in the ticket' do
101
- class MicroJob; end
102
- blob = '{"_job_class": "MicroJob", "_job_params":{"foo": 1}}'
103
- expect {
104
- described_class.new.unserialize(blob)
105
- }.to raise_error(ArgumentError)
106
- end
107
-
108
- it 'instantiates the job with keyword arguments' do
109
- OtherValidJob = Ks.strict(:foo)
110
-
111
- blob = '{"_job_class": "OtherValidJob", "_job_params": {"foo": 1}}'
112
- built_job = described_class.new.unserialize(blob)
113
-
114
- expect(built_job).to be_kind_of(OtherValidJob)
115
- expect(built_job.foo).to eq(1)
116
- end
117
- end
118
- end
@@ -1,69 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe Sqewer::SimpleJob do
4
- it 'raises a clear error for an unknown attribute' do
5
- example_class = Class.new do
6
- attr_accessor :foo, :bar
7
- include Sqewer::SimpleJob
8
- end
9
-
10
- expect {
11
- example_class.new(zoo: 1, bar: 2)
12
- }.to raise_error(/Unknown attribute \:zoo for/)
13
- end
14
-
15
- it 'uses defined accessors to provide decent string representation' do
16
- example_class = Class.new do
17
- attr_accessor :foo, :bar
18
- include Sqewer::SimpleJob
19
- end
20
-
21
- job = example_class.new(foo: 1, bar: 2)
22
- expect(job.inspect).to include('Class')
23
- expect(job.inspect).to include('{:foo=>1, :bar=>2}')
24
- end
25
-
26
- it 'uses inspectable_attributes to limit the scope of .inspect' do
27
- example_class = Class.new do
28
- attr_accessor :foo, :bar
29
- def inspectable_attributes
30
- [:foo]
31
- end
32
- include Sqewer::SimpleJob
33
- end
34
-
35
- job = example_class.new(foo: 1, bar: 2)
36
- expect(job.inspect).to include('Class')
37
- expect(job.inspect).to include('{:foo=>1}')
38
- expect(job.inspect).not_to include('bar')
39
- end
40
-
41
- it 'provides for a keyword argument constructor and a to_h method' do
42
- example_class = Class.new do
43
- attr_accessor :foo, :bar
44
- include Sqewer::SimpleJob
45
- end
46
-
47
- string_repr = example_class.to_s
48
-
49
- new_instance = example_class.new(foo: 1, bar: 2)
50
-
51
- expect(new_instance.foo).to eq(1)
52
- expect(new_instance.bar).to eq(2)
53
-
54
- hash_repr = new_instance.to_h
55
- expect(hash_repr).to eq({foo: 1, bar: 2})
56
- end
57
-
58
-
59
- it 'raises if arguments are forgotten' do
60
- example_class = Class.new do
61
- attr_accessor :foo, :bar
62
- include Sqewer::SimpleJob
63
- end
64
-
65
- expect {
66
- example_class.new(foo: 1)
67
- }.to raise_error('Missing job attribute :bar')
68
- end
69
- end
@@ -1,61 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe Sqewer::Submitter do
4
- describe '.default' do
5
- it 'returns a set up Submitter with the configured Connection and Serializer' do
6
- expect(ENV).to receive(:fetch).with('SQS_QUEUE_URL').and_return('https://some-queue.aws.com')
7
-
8
- s = described_class.default
9
- expect(s.connection).to respond_to(:send_message)
10
- expect(s.serializer).to respond_to(:serialize)
11
- end
12
- end
13
-
14
- describe '#initialize' do
15
- it 'creates a Submitter that you can submit jobs through' do
16
- fake_serializer = double('Some serializer')
17
- allow(fake_serializer).to receive(:serialize) {|object_to_serialize|
18
- expect(object_to_serialize).not_to be_nil
19
- 'serialized-object-data'
20
- }
21
-
22
- fake_connection = double('Some SQS connection')
23
- expect(fake_connection).to receive(:send_message).at_least(5).times.with('serialized-object-data', {})
24
-
25
- subject = described_class.new(fake_connection, fake_serializer)
26
- 5.times { subject.submit!(:some_object) }
27
- end
28
-
29
- it 'passes the keyword arguments to send_message on the connection' do
30
- fake_serializer = double('Some serializer')
31
- allow(fake_serializer).to receive(:serialize) {|object_to_serialize|
32
- expect(object_to_serialize).not_to be_nil
33
- 'serialized-object-data'
34
- }
35
-
36
- fake_connection = double('Some SQS connection')
37
- expect(fake_connection).to receive(:send_message).with('serialized-object-data', {delay_seconds: 5})
38
-
39
- subject = described_class.new(fake_connection, fake_serializer)
40
- subject.submit!(:some_object, delay_seconds: 5)
41
- end
42
-
43
- it 'handles the massively delayed execution by clamping the delay_seconds to the SQS maximum, and saving the _execute_after' do
44
- fake_serializer = double('Some serializer')
45
- allow(fake_serializer).to receive(:serialize) {|object_to_serialize, timestamp_seconds|
46
-
47
- delay_by = Time.now.to_i + 4585659855
48
- expect(timestamp_seconds).to be_within(20).of(delay_by)
49
-
50
- expect(object_to_serialize).not_to be_nil
51
- 'serialized-object-data'
52
- }
53
-
54
- fake_connection = double('Some SQS connection')
55
- expect(fake_connection).to receive(:send_message).with('serialized-object-data', {delay_seconds: 899})
56
-
57
- subject = described_class.new(fake_connection, fake_serializer)
58
- subject.submit!(:some_object, delay_seconds: 4585659855)
59
- end
60
- end
61
- end
@@ -1,112 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe Sqewer::Worker, :sqs => true do
4
- let(:test_logger) {
5
- $stderr.sync = true
6
- ENV['SHOW_TEST_LOGS'] ? Logger.new($stderr) : Logger.new(StringIO.new(''))
7
- }
8
-
9
- it 'has all the necessary attributes' do
10
- attrs = [:logger, :connection, :serializer, :middleware_stack,
11
- :execution_context_class, :submitter_class, :num_threads]
12
- default_worker = described_class.default
13
- attrs.each do | attr_name |
14
- expect(default_worker).to respond_to(attr_name)
15
- expect(default_worker.public_send(attr_name)).not_to be_nil
16
- end
17
- end
18
-
19
- it 'supports .default' do
20
- default_worker = described_class.default
21
- expect(default_worker).to respond_to(:start)
22
- expect(default_worker).to respond_to(:stop)
23
- end
24
-
25
- it 'instantiates a new worker object on every call to .default' do
26
- workers = (1..10).map { described_class.default }
27
- expect(workers.uniq.length).to eq(10)
28
- end
29
-
30
- it 'instantiates a Logger to STDERR by default' do
31
- expect(Logger).to receive(:new).with(STDERR)
32
- worker = described_class.new
33
- end
34
-
35
- it 'can go through the full cycle of initialize, start, stop, start, stop' do
36
- worker = described_class.new(logger: test_logger)
37
- worker.start
38
- worker.stop
39
- worker.start
40
- worker.stop
41
- end
42
-
43
- it 'raises a state exception if being stopped without being started' do
44
- worker = described_class.new
45
- expect {
46
- worker.stop
47
- }.to raise_error(/Cannot change state/)
48
- end
49
-
50
- context 'when the job payload cannot be unserialized from JSON due to invalid syntax' do
51
- it 'is able to cope with an exception when the job class is unknown (one of generic exceptions)' do
52
- client = Aws::SQS::Client.new
53
- client.send_message(queue_url: ENV.fetch('SQS_QUEUE_URL'), message_body: '{"foo":')
54
-
55
- worker = described_class.new(logger: test_logger)
56
-
57
- worker.start
58
- sleep 2
59
- worker.stop
60
- end
61
- end
62
-
63
- context 'when the job cannot be instantiated due to an unknown class' do
64
- it 'is able to cope with an exception when the job class is unknown (one of generic exceptions)' do
65
- payload = JSON.dump({_job_class: 'UnknownJobClass', _job_params: {arg1: 'some value'}})
66
-
67
- client = Aws::SQS::Client.new
68
- client.send_message(queue_url: ENV.fetch('SQS_QUEUE_URL'), message_body: payload)
69
-
70
- worker = described_class.new(logger: test_logger)
71
-
72
- worker.start
73
- sleep 2
74
- worker.stop
75
-
76
- # expect(logger_output).to include('uninitialized constant UnknownJobClass')
77
- # expect(logger_output).to include('Stopping (clean shutdown)')
78
- end
79
- end
80
-
81
- context 'with a job that spawns another job' do
82
- it 'sets up the processing pipeline so that jobs can execute in sequence (with threads)' do
83
- class SecondaryJob
84
- def run
85
- File.open(File.join(Dir.tmpdir, 'secondary-job-run'),'w') {}
86
- end
87
- end
88
-
89
- class InitialJob
90
- def run(executor)
91
- File.open(File.join(Dir.tmpdir, 'initial-job-run'),'w') {}
92
- executor.submit!(SecondaryJob.new)
93
- end
94
- end
95
-
96
- payload = JSON.dump({_job_class: 'InitialJob'})
97
- client = Aws::SQS::Client.new
98
- client.send_message(queue_url: ENV.fetch('SQS_QUEUE_URL'), message_body: payload)
99
-
100
- worker = described_class.new(logger: test_logger, num_threads: 8)
101
-
102
- worker.start
103
-
104
- begin
105
- wait_for { File.exist?(File.join(Dir.tmpdir, 'initial-job-run')) }.to eq(true)
106
- wait_for { File.exist?(File.join(Dir.tmpdir, 'secondary-job-run')) }.to eq(true)
107
- ensure
108
- worker.stop
109
- end
110
- end
111
- end
112
- end
@@ -1,14 +0,0 @@
1
- require_relative 'spec_helper'
2
-
3
- describe Sqewer do
4
- it 'provides a #submit!() method that is a shortcut to the default submitter' do
5
- fake_submitter = double('Submitter')
6
- expect(Sqewer::Submitter).to receive(:default) { fake_submitter }
7
-
8
- first_job = double('Job1')
9
- second_job = double('Job2')
10
-
11
- expect(fake_submitter).to receive(:submit!).with(first_job, second_job, {})
12
- Sqewer.submit!(first_job, second_job)
13
- end
14
- end