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.
data/spec/spec_helper.rb DELETED
@@ -1,36 +0,0 @@
1
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
- $LOAD_PATH.unshift(File.dirname(__FILE__))
3
-
4
- require 'rspec'
5
- require 'rspec/wait'
6
- require 'dotenv'
7
- require 'aws-sdk'
8
- require 'simplecov'
9
- Dotenv.load
10
-
11
- SimpleCov.start
12
- require 'sqewer'
13
-
14
- RSpec.configure do |config|
15
- config.order = 'random'
16
- config.around :each do | example |
17
- if example.metadata[:sqs]
18
- queue_name = 'sqewer-test-queue-%s' % SecureRandom.hex(6)
19
- client = Aws::SQS::Client.new
20
- resp = client.create_queue(queue_name: queue_name)
21
- ENV['SQS_QUEUE_URL'] = resp.queue_url
22
-
23
- example.run
24
-
25
- # Sometimes the queue is already deleted before the example completes. If the test has passed,
26
- # we do not really care whether this invocation raises an exception about a non-existent queue since
27
- # all we care about is the queue _being gone_ at the end of the example.
28
- client.delete_queue(queue_url: ENV.fetch('SQS_QUEUE_URL')) rescue Aws::SQS::Errors::NonExistentQueue
29
-
30
- ENV.delete('SQS_QUEUE_URL')
31
- else
32
- example.run
33
- end
34
- end
35
- end
36
-
@@ -1,113 +0,0 @@
1
- require_relative '../spec_helper'
2
- require 'securerandom'
3
- require 'active_job'
4
- require 'active_record'
5
- require 'global_id'
6
- require_relative '../../lib/sqewer/extensions/active_job_adapter'
7
-
8
- class CreateFileJob < ActiveJob::Base
9
- def perform(file)
10
- File.open(file, 'w') {}
11
- end
12
- end
13
-
14
- class DeleteFileJob < ActiveJob::Base
15
- def perform(file)
16
- File.unlink(file)
17
- end
18
- end
19
-
20
- class ActivateUser < ActiveJob::Base
21
- def perform(user)
22
- user.active = true
23
- user.save!
24
- end
25
- end
26
-
27
- GlobalID.app = 'test-app'
28
- class User < ActiveRecord::Base
29
- include GlobalID::Identification
30
- end
31
-
32
- describe ActiveJob::QueueAdapters::SqewerAdapter, :sqs => true do
33
- let(:file) { File.join(Dir.tmpdir, "file_active_job_test_1") }
34
- let(:client) { ::Aws::SQS::Client.new }
35
-
36
- after :all do
37
- # Ensure database files get killed afterwards
38
- File.unlink(ActiveRecord::Base.connection_config[:database]) rescue nil
39
- end
40
-
41
- before :all do
42
- ActiveJob::Base.queue_adapter = ActiveJob::QueueAdapters::SqewerAdapter
43
-
44
- test_seed_name = SecureRandom.hex(4)
45
- ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ('master_db_%s.sqlite3' % test_seed_name))
46
-
47
- ActiveRecord::Migration.suppress_messages do
48
- ActiveRecord::Schema.define(:version => 1) do
49
- create_table :users do |t|
50
- t.string :email, :null => true
51
- t.boolean :active, default: false
52
- t.timestamps :null => false
53
- end
54
- end
55
- end
56
- end
57
-
58
- before do
59
- @queue_url_hash = { queue_url: ENV['SQS_QUEUE_URL'] }
60
- end
61
-
62
- it "sends job to the queue" do
63
- CreateFileJob.perform_later(file)
64
- resp = client.get_queue_attributes(@queue_url_hash.merge(attribute_names: ["ApproximateNumberOfMessages"]))
65
- expect(resp.attributes["ApproximateNumberOfMessages"].to_i).to eq(1)
66
- end
67
-
68
- it "correctly serializes the job into a Sqewer job" do
69
- job = CreateFileJob.perform_later(file)
70
- resp = client.receive_message(@queue_url_hash)
71
- serialized_job = JSON.parse(resp.messages.last.body)
72
-
73
- expect(serialized_job["_job_class"]).to eq("ActiveJob::QueueAdapters::SqewerAdapter::Performable")
74
- expect(serialized_job["_job_params"]["job"]["job_id"]).to eq(job.job_id)
75
- end
76
-
77
- it "executes job from the queue" do
78
- file_delayed = "#{file}_delayed"
79
- CreateFileJob.perform_later(file)
80
- CreateFileJob.perform_later(file_delayed)
81
- w = Sqewer::Worker.default
82
- w.start
83
- begin
84
- wait_for { File.exist?(file) }.to eq(true)
85
- File.unlink(file)
86
-
87
- wait_for { File.exist?(file_delayed) }.to eq(true)
88
- DeleteFileJob.set(wait: 5.seconds).perform_later(file_delayed)
89
-
90
- sleep 6
91
-
92
- expect(File.exist?(file_delayed)).to eq(false)
93
- ensure
94
- w.stop
95
- end
96
- end
97
-
98
- it "serializes and deserializes active record using GlobalID" do
99
- user = User.create(email: 'test@wetransfer.com')
100
- expect(user.active).to eq(false)
101
- ActivateUser.perform_later(user)
102
- w = Sqewer::Worker.default
103
- begin
104
- w.start
105
- sleep 4
106
- user.reload
107
- expect(user.active).to eq(true)
108
- ensure
109
- w.stop
110
- end
111
- end
112
-
113
- end
@@ -1,15 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe Sqewer::AtomicCounter do
4
- it 'is atomic' do
5
- c = described_class.new
6
- expect(c.to_i).to be_zero
7
-
8
- threads = (1..64).map do
9
- Thread.new { sleep(rand); c.increment! }
10
- end
11
- threads.map(&:join)
12
-
13
- expect(c.to_i).to eq(threads.length)
14
- end
15
- end
@@ -1,13 +0,0 @@
1
- require_relative '../../lib/sqewer'
2
-
3
- class MyJob
4
- include Sqewer::SimpleJob
5
- attr_accessor :first_name
6
- attr_accessor :last_name
7
-
8
- def run(executor)
9
- File.open("#{SecureRandom.hex(3)}-result", 'w') {|f| f << [first_name, last_name].join }
10
- end
11
- end
12
-
13
- Sqewer::CLI.start
@@ -1,83 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe Sqewer::CLI, :sqs => true, :wait => {timeout: 120} do
4
- after :each do
5
- Dir.glob('*-result').each{|path| File.unlink(path) }
6
- end
7
-
8
- describe 'with a mock Worker' do
9
- it 'uses just three methods' do
10
- mock_worker = Class.new do
11
- def self.start; end
12
- def self.stop; end
13
- def self.debug_thread_information!; end
14
- end
15
-
16
- worker_pid = fork do
17
- Sqewer::CLI.start(mock_worker)
18
- end
19
- sleep 1
20
-
21
- begin
22
- Process.kill('INFO', worker_pid) # Calls debug_thread_information!
23
- rescue ArgumentError, Errno::ENOTSUP # on Linux
24
- end
25
- Process.kill('TERM', worker_pid) # Terminates the worker
26
-
27
- wait_for {
28
- _, status = Process.wait2(worker_pid)
29
- expect(status.exitstatus).to be_zero # Must have quit cleanly
30
- }
31
- end
32
- end
33
-
34
- describe 'runs the commandline app, executes jobs and then quits cleanly' do
35
- it 'on a USR1 signal' do
36
- submitter = Sqewer::Connection.default
37
-
38
- pid = fork { exec("ruby #{__dir__}/cli_app.rb") }
39
-
40
- Thread.new do
41
- 20.times do
42
- j = {"_job_class" => 'MyJob', "_job_params" => {first_name: 'John', last_name: 'Doe'}}
43
- submitter.send_message(JSON.dump(j))
44
- end
45
- end
46
-
47
- sleep 8
48
- wait_for {
49
- Process.kill("USR1", pid)
50
- _, status = Process.wait2(pid)
51
- expect(status.exitstatus).to be_zero # Must have quit cleanly
52
- }
53
-
54
- generated_files = Dir.glob('*-result')
55
- expect(generated_files).not_to be_empty
56
- generated_files.each{|path| File.unlink(path) }
57
- end
58
-
59
- it 'on a TERM signal' do
60
- submitter = Sqewer::Connection.default
61
-
62
- pid = fork { exec("ruby #{__dir__}/cli_app.rb") }
63
-
64
- Thread.new do
65
- 20.times do
66
- j = {"_job_class" => 'MyJob', "_job_params" => {first_name: 'John', last_name: 'Doe'}}
67
- submitter.send_message(JSON.dump(j))
68
- end
69
- end
70
-
71
- sleep 8
72
- wait_for {
73
- Process.kill("TERM", pid)
74
- _, status = Process.wait2(pid)
75
- expect(status.exitstatus).to be_zero # Must have quit cleanly
76
- }
77
-
78
- generated_files = Dir.glob('*-result')
79
- expect(generated_files).not_to be_empty
80
- generated_files.each{|path| File.unlink(path) }
81
- end
82
- end
83
- end
@@ -1,145 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe Sqewer::Connection do
4
- describe '.default' do
5
- it 'returns a new Connection with the SQS queue location picked from SQS_QUEUE_URL envvar' do
6
- expect(ENV).to receive(:fetch).with('SQS_QUEUE_URL').and_return('https://aws-fake-queue.com')
7
- default = described_class.default
8
- expect(default).to be_kind_of(described_class)
9
- end
10
- end
11
-
12
- describe '#send_message' do
13
- it 'sends the message to the SQS client created with the URL given to the constructor' do
14
- fake_sqs_client = double('Client')
15
- expect(Aws::SQS::Client).to receive(:new) { fake_sqs_client }
16
- expect(fake_sqs_client).to receive(:send_message_batch).and_return(double(failed: []))
17
-
18
- conn = described_class.new('https://fake-queue.com')
19
- expect(conn).to receive(:send_multiple_messages).and_call_original
20
- conn.send_message('abcdef')
21
- end
22
-
23
- it 'passes keyword args to Aws::SQS::Client' do
24
- fake_sqs_client = double('Client')
25
- expect(Aws::SQS::Client).to receive(:new) { fake_sqs_client }
26
- expect(fake_sqs_client).to receive(:send_message_batch).and_return(double(failed: []))
27
-
28
- conn = described_class.new('https://fake-queue.com')
29
- expect(conn).to receive(:send_multiple_messages).and_call_original
30
- conn.send_message('abcdef', delay_seconds: 5)
31
- end
32
-
33
- it 'retries on networking errors'
34
- end
35
-
36
- describe '#send_multiple_messages' do
37
- it 'sends 100 messages' do
38
- fake_sqs_client = double('Client')
39
- expect(Aws::SQS::Client).to receive(:new) { fake_sqs_client }
40
- expect(fake_sqs_client).to receive(:send_message_batch).exactly(11).times {|kwargs|
41
- expect(kwargs[:queue_url]).to eq("https://fake-queue.com")
42
- expect(kwargs[:entries]).to be_kind_of(Array)
43
-
44
- entries = kwargs[:entries]
45
- expect(entries.length).to be <= 10 # At most 10 messages per batch
46
- entries.each do | entry |
47
- expect(entry[:id]).to be_kind_of(String)
48
- expect(entry[:message_body]).to be_kind_of(String)
49
- expect(entry[:message_body]).to match(/Hello/)
50
- end
51
- double(failed: [])
52
- }
53
-
54
- conn = described_class.new('https://fake-queue.com')
55
- conn.send_multiple_messages do | b|
56
- 102.times { b.send_message("Hello - #{SecureRandom.uuid}") }
57
- end
58
- end
59
-
60
- it 'raises an exception if any message fails sending' do
61
- fake_sqs_client = double('Client')
62
- expect(Aws::SQS::Client).to receive(:new) { fake_sqs_client }
63
- expect(fake_sqs_client).to receive(:send_message_batch) {|kwargs|
64
- double(failed: [double(message: 'Something went wrong at AWS')])
65
- }
66
-
67
- conn = described_class.new('https://fake-queue.com')
68
- expect {
69
- conn.send_multiple_messages do | b|
70
- 102.times { b.send_message("Hello - #{SecureRandom.uuid}") }
71
- end
72
- }.to raise_error(/messages failed to send/)
73
- end
74
-
75
- it 'retries on networking errors'
76
-
77
- end
78
-
79
- describe '#delete_message' do
80
- it 'deletes a single message'
81
- end
82
-
83
- describe '#delete_multiple_messages' do
84
- it 'deletes 100 messages' do
85
- fake_sqs_client = double('Client')
86
- expect(Aws::SQS::Client).to receive(:new) { fake_sqs_client }
87
- expect(fake_sqs_client).to receive(:delete_message_batch).exactly(11).times {|kwargs|
88
- expect(kwargs[:queue_url]).to eq("https://fake-queue.com")
89
- expect(kwargs[:entries]).to be_kind_of(Array)
90
-
91
- entries = kwargs[:entries]
92
- expect(entries.length).to be <= 10 # At most 10 messages per batch
93
- entries.each do | entry |
94
- expect(entry[:id]).to be_kind_of(String)
95
- expect(entry[:receipt_handle]).to be_kind_of(String)
96
- end
97
- double(failed: [])
98
- }
99
-
100
- conn = described_class.new('https://fake-queue.com')
101
- conn.delete_multiple_messages do | b|
102
- 102.times { b.delete_message(SecureRandom.uuid) }
103
- end
104
- end
105
-
106
- it 'raises an exception if any message fails sending' do
107
- fake_sqs_client = double('Client')
108
- expect(Aws::SQS::Client).to receive(:new) { fake_sqs_client }
109
- expect(fake_sqs_client).to receive(:delete_message_batch) {|kwargs|
110
- double(failed: [double(message: 'Something went wrong at AWS')])
111
- }
112
-
113
- conn = described_class.new('https://fake-queue.com')
114
- expect {
115
- conn.delete_multiple_messages do | b|
116
- 102.times { b.delete_message(SecureRandom.uuid) }
117
- end
118
- }.to raise_error(/messages failed to delete/)
119
- end
120
-
121
- it 'retries on networking errors'
122
- end
123
-
124
- describe '#receive_messages' do
125
- it 'uses the batched receive feature' do
126
- s = described_class.new('https://fake-queue')
127
-
128
- fake_sqs_client = double('Client')
129
- expect(Aws::SQS::Client).to receive(:new) { fake_sqs_client }
130
-
131
- fake_messages = (1..5).map {
132
- double(receipt_handle: SecureRandom.hex(4), body: SecureRandom.random_bytes(128))
133
- }
134
- fake_response = double(messages: fake_messages)
135
-
136
- expect(fake_sqs_client).to receive(:receive_message).with({:queue_url=>"https://fake-queue", :wait_time_seconds=>5,
137
- :max_number_of_messages=>10}).and_return(fake_response)
138
-
139
- messages = s.receive_messages
140
- expect(messages.length).to eq(5)
141
- end
142
-
143
- it 'retries on networking errors'
144
- end
145
- end
@@ -1,43 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe Sqewer::ExecutionContext do
4
- it 'offers a submit! that goes through the given Submitter argument' do
5
- fake_submitter = double('Submitter')
6
- expect(fake_submitter).to receive(:submit!).with(:fake_job, {})
7
-
8
- subject = described_class.new(fake_submitter)
9
- subject.submit!(:fake_job)
10
- end
11
-
12
- it 'offers arbitrary key/value storage' do
13
- fake_submitter = double('Submitter')
14
- subject = described_class.new(fake_submitter)
15
-
16
- subject['foo'] = 123
17
- expect(subject['foo']).to eq(123)
18
- expect(subject[:foo]).to eq(123)
19
- expect(subject.fetch(:foo)).to eq(123)
20
-
21
- expect {
22
- subject.fetch(:bar)
23
- }.to raise_error(KeyError)
24
-
25
- default_value = subject.fetch(:bar) { 123 }
26
- expect(default_value).to eq(123)
27
- end
28
-
29
- it 'returns the NullLogger from #logger if no logger was passed to the constructor' do
30
- fake_submitter = double('Submitter')
31
-
32
- subject = described_class.new(fake_submitter)
33
- expect(subject.logger).to eq(Sqewer::NullLogger)
34
- end
35
-
36
- it 'offers access to the given "logger" extra param if it was given to the constructor' do
37
- fake_submitter = double('Submitter')
38
- fake_logger = double('Logger')
39
-
40
- subject = described_class.new(fake_submitter, {'logger' => fake_logger})
41
- expect(subject.logger).to eq(fake_logger)
42
- end
43
- end