wicoris-postman 0.10.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.
@@ -0,0 +1,145 @@
1
+ require 'spec_helper'
2
+
3
+ module Wicoris::Postman
4
+ describe Copier do
5
+ let(:job) { double('job') }
6
+ let(:copier) { Copier.new(job, {}) }
7
+
8
+ describe '#run' do
9
+
10
+ before do
11
+ job.stub(:letter).and_return('/tmp/foo.pdf')
12
+ copier.stub(:destination).and_return('/export')
13
+ end
14
+
15
+ context 'without noop' do
16
+ let(:copier) { Copier.new(job, {}) }
17
+
18
+ it 'copies the letter to destination' do
19
+ copier.should_receive(:source).and_return('/chunky.pdf')
20
+ copier.should_receive(:destination).and_return('/bacon.pdf')
21
+ FileUtils.should_receive(:cp).
22
+ with('/chunky.pdf', '/bacon.pdf', :noop => false)
23
+ copier.run
24
+ end
25
+ end
26
+
27
+ context 'with noop' do
28
+ let(:copier) { Copier.new(job, :noop => true) }
29
+
30
+ it 'does not copy the letter to destination' do
31
+ FileUtils.should_receive(:cp).
32
+ with('/tmp/foo.pdf', '/export', :noop => true)
33
+ copier.run
34
+ end
35
+ end
36
+ end
37
+
38
+ describe '#source' do
39
+ it 'returns the letter' do
40
+ job.should_receive(:letter).and_return('/chunky/bacon.pdf')
41
+ expect(copier.send(:source)).to eq '/chunky/bacon.pdf'
42
+ end
43
+ end
44
+
45
+ describe '#fingerprint' do
46
+ it 'returns the first 4 bytes of the source hexdigest' do
47
+ copier.should_receive(:source).and_return('/chunky/bacon.pdf')
48
+ File.should_receive(:read).with('/chunky/bacon.pdf').
49
+ and_return('content')
50
+ Digest::MD5.should_receive(:hexdigest).with('content').
51
+ and_return('cafebabedeadbeef')
52
+ expect(copier.send(:fingerprint)).to eq 'cafe'
53
+ end
54
+ end
55
+
56
+ describe '#destination' do
57
+ it 'returns full path from output filename' do
58
+ copier.should_receive(:outdir).and_return('/chunky')
59
+ copier.should_receive(:filename).and_return('bacon.pdf')
60
+ expect(copier.send(:destination)).to eq '/chunky/bacon.pdf'
61
+ end
62
+ end
63
+
64
+ describe '#outdir' do
65
+ let(:copier) { Copier.new(job, {:outdir => '/chunky'}) }
66
+
67
+ before do
68
+ [:exists?, :directory?, :writable?].each do |method|
69
+ File.stub(method).and_return(true)
70
+ end
71
+ end
72
+
73
+ it 'returns the output dir' do
74
+ expect(copier.send(:outdir)).to eq '/chunky'
75
+ end
76
+
77
+ it 'raises when directory does not exist' do
78
+ File.should_receive(:exists?).and_return(false)
79
+ expect { copier.send(:outdir) }.to raise_error(
80
+ %r{output directory does not exist: /chunky}i)
81
+ end
82
+
83
+ it 'raises when not a directory' do
84
+ File.should_receive(:directory?).and_return(false)
85
+ expect { copier.send(:outdir) }.to raise_error(
86
+ %r{output directory is no directory: '/chunky'}i)
87
+ end
88
+
89
+ it 'raises when not writable' do
90
+ File.should_receive(:writable?).and_return(false)
91
+ expect { copier.send(:outdir) }.to raise_error(
92
+ %r{output directory not writable: '/chunky'}i)
93
+ end
94
+
95
+ context 'without outdir' do
96
+ let(:copier) { Copier.new(job, {:outdir => nil}) }
97
+
98
+ it 'raises an error' do
99
+ expect { copier.send(:outdir) }.to raise_error(
100
+ %r{No output directory given}i)
101
+ end
102
+ end
103
+ end
104
+
105
+ describe '#filename' do
106
+ it 'returns a filename based on its components and suffix' do
107
+ copier.stub(:filename_components).
108
+ and_return %w(chunky bacon 42)
109
+ copier.stub(:suffix).and_return('.foo')
110
+ expect(copier.send(:filename)).to eq 'chunky_bacon_42.foo'
111
+ end
112
+
113
+ it 'raises on missing filename component' do
114
+ copier.stub(:filename_components).
115
+ and_return ['chunky', nil, 'bacon']
116
+ expect{ copier.send(:filename) }.to raise_error(
117
+ /missing patient demographics/i)
118
+ end
119
+
120
+ it 'raises on empty filename component' do
121
+ copier.stub(:filename_components).
122
+ and_return ['chunky', '', 'bacon']
123
+ expect{ copier.send(:filename) }.to raise_error(
124
+ /missing patient demographics/i)
125
+ end
126
+ end
127
+
128
+ describe '#filename_components' do
129
+ it 'orders the components by most relevant patient demographics' do
130
+ job.should_receive(:patient_first_name).and_return('Chuck')
131
+ job.should_receive(:patient_last_name).and_return('Norris')
132
+ job.should_receive(:patient_date_of_birth).and_return('1940-03-10')
133
+ copier.should_receive(:fingerprint).and_return('abcd')
134
+ expect(copier.send(:filename_components)).to eq(
135
+ %w(Norris Chuck 1940-03-10 abcd))
136
+ end
137
+ end
138
+
139
+ describe '#suffix' do
140
+ it 'returns the filename suffix' do
141
+ expect(copier.send(:suffix)).to eq '.pdf'
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+
3
+ module Wicoris::Postman
4
+ describe FaxMachine do
5
+ let(:job) { double('job') }
6
+ let(:fax) { FaxMachine.new(job) }
7
+
8
+ before do
9
+ fax.stub(:system)
10
+ end
11
+
12
+ describe '#run' do
13
+ let(:logger) { double('logger') }
14
+
15
+ it 'faxes the document' do
16
+ fax.stub(:command).and_return('chunky bacon')
17
+ fax.should_receive(:system).with('chunky bacon')
18
+ fax.run
19
+ end
20
+
21
+ context 'with noop' do
22
+ let(:fax) { FaxMachine.new(job, :noop => true) }
23
+
24
+ it 'does not fax the job' do
25
+ fax.stub(:command).and_return('chunky bacon')
26
+ fax.should_not_receive(:system)
27
+ fax.run
28
+ end
29
+ end
30
+ end
31
+
32
+ describe '#command' do
33
+ it 'should return the command-line for sending a fax' do
34
+ fax.stub(:validated_phone).and_return('042')
35
+ job.stub(:letter).and_return('/tmp/foo.pdf')
36
+ expect(fax.send(:command)).to eq(
37
+ "lp -d Fax -o phone=042 '/tmp/foo.pdf'")
38
+ end
39
+ end
40
+
41
+ describe '#phone' do
42
+ it 'adds the dial-out prefix' do
43
+ job.stub(:phone).and_return('0123456789')
44
+ expect(fax.validated_phone).to eq '00123456789'
45
+ end
46
+
47
+ it 'removes all whitespaces and dashes from the number' do
48
+ job.stub(:phone).and_return("\t012-345 6789 \n")
49
+ expect(fax.validated_phone).to eq '00123456789'
50
+ end
51
+
52
+ it 'raises when phone is missing' do
53
+ job.stub(:phone).and_return(nil)
54
+ expect{ fax.validated_phone }.to raise_error ArgumentError
55
+ end
56
+
57
+ it 'raises when one leading zero is missing' do
58
+ job.stub(:phone).and_return('1234567890')
59
+ expect{ fax.validated_phone }.to raise_error ArgumentError
60
+ end
61
+
62
+ it 'raises when begins with more then one zero' do
63
+ job.stub(:phone).and_return('00123456789')
64
+ expect{ fax.validated_phone }.to raise_error ArgumentError
65
+ end
66
+
67
+ it 'raises when shorter then 9 digits' do
68
+ job.stub(:phone).and_return('01234567')
69
+ expect{ fax.validated_phone }.to raise_error ArgumentError
70
+ end
71
+
72
+ it 'raises when number contains invalid chars' do
73
+ job.stub(:phone).and_return('01234hello')
74
+ expect{ fax.validated_phone }.to raise_error ArgumentError
75
+ end
76
+
77
+ it 'includes the phone number in the exception' do
78
+ job.stub(:phone).and_return('42')
79
+ expect{ fax.validated_phone }.to raise_error /42/
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,182 @@
1
+ require 'spec_helper'
2
+
3
+ module Wicoris::Postman
4
+ describe Job do
5
+ let(:job) { Job.new('example.json') }
6
+
7
+ before do
8
+ FileUtils.stub(:rm)
9
+ end
10
+
11
+ describe '#json' do
12
+ it 'parses and returns the JSON file' do
13
+ job.should_receive(:json_file_content).and_return('content')
14
+ JSON.should_receive(:parse).with('content').once.and_return('json')
15
+ 2.times { expect(job.send(:json)).to eq 'json' }
16
+ end
17
+ end
18
+
19
+ describe '#json_file_content' do
20
+ it 'returns the file content' do
21
+ File.should_receive(:read).with('example.json').and_return('content')
22
+ expect(job.send(:json_file_content)).to eq 'content'
23
+ end
24
+
25
+ it 'converts Mac Roman encoding to UTF-8' do
26
+ File.stub(:read).and_return("Schl\237ter")
27
+ expect(job.send(:json_file_content)).to eq 'Schlüter'
28
+ end
29
+ end
30
+
31
+ describe '#letter' do
32
+ before do
33
+ File.stub(:exists?).and_return(true)
34
+ end
35
+
36
+ it 'returns the letter path' do
37
+ job.stub(:json).and_return('file' => '/tmp/foo.pdf')
38
+ expect(job.letter).to eq '/tmp/foo.pdf'
39
+ end
40
+
41
+ it 'raises when letter is missing' do
42
+ File.should_receive(:exists?).and_return(false)
43
+ job.stub(:file).and_return('chunky')
44
+ expect {
45
+ job.send(:letter)
46
+ }.to raise_error /letter does not exist.*chunky/i
47
+ end
48
+ end
49
+
50
+ describe '#method_missing' do
51
+ context 'with existing JSON attribute' do
52
+ it 'returns it' do
53
+ job.stub(:json).and_return('chunky' => 'bacon')
54
+ expect(job.chunky).to eq 'bacon'
55
+ end
56
+ end
57
+
58
+ context 'without matching JSON attribute' do
59
+ it 'returns it' do
60
+ job.stub(:json).and_return({})
61
+ expect {
62
+ job.chunky
63
+ }.to raise_error /undefined method/i
64
+ end
65
+ end
66
+ end
67
+
68
+ describe '#process' do
69
+ context 'when fax' do
70
+ it 'faxes the letter' do
71
+ job.should_receive(:json).and_return('type' => 'fax')
72
+ fax_machine = double('fax_machine')
73
+ FaxMachine.should_receive(:new).with(job, {}).and_return(fax_machine)
74
+ fax_machine.should_receive(:run).ordered
75
+ job.process
76
+ end
77
+ end
78
+
79
+ context 'when copy' do
80
+ it 'copies the letter' do
81
+ job.should_receive(:json).and_return('type' => 'copy')
82
+ copier = double('copier')
83
+ Copier.should_receive(:new).with(job, {}).and_return(copier)
84
+ copier.should_receive(:run).ordered
85
+ job.process
86
+ end
87
+ end
88
+ end
89
+
90
+ describe '#patient_first_name' do
91
+ it 'returns the patients first name' do
92
+ job.stub(:json).and_return('patient_first_name' => 'Chuck')
93
+ expect(job.patient_first_name).to eq 'Chuck'
94
+ end
95
+ end
96
+
97
+ describe '#patient_last_name' do
98
+ it 'returns the patients last name' do
99
+ job.stub(:json).and_return('patient_last_name' => 'Norris')
100
+ expect(job.patient_last_name).to eq 'Norris'
101
+ end
102
+ end
103
+
104
+ describe '#patient_date_of_birth' do
105
+ it 'returns the patients date of birth' do
106
+ job.stub(:json).and_return('patient_date_of_birth' => '1940-03-10')
107
+ expect(job.patient_date_of_birth).to eq '1940-03-10'
108
+ end
109
+ end
110
+
111
+ describe '#clear!' do
112
+ before do
113
+ job.stub(:json).and_return('')
114
+ end
115
+
116
+ it 'deletes the json file' do
117
+ FileUtils.should_receive(:rm).with('example.json', :noop => false)
118
+ job.clear!
119
+ end
120
+
121
+ context 'with noop' do
122
+ let(:job) { Job.new('example.json', :noop => true) }
123
+
124
+ it 'does not delete the json file' do
125
+ FileUtils.should_receive(:rm).with('example.json', :noop => true)
126
+ job.clear!
127
+ end
128
+ end
129
+
130
+ context 'without parsable JSON' do
131
+ let(:logger) { double('logger', :warn => true) }
132
+
133
+ before do
134
+ job.stub(:json).
135
+ and_return { raise JSON::ParserError, 'Shitty JSON' }
136
+ job.stub(:logger).and_return(logger)
137
+ end
138
+
139
+ it 'does not delete the JSON file' do
140
+ FileUtils.should_not_receive(:rm)
141
+ job.clear!
142
+ end
143
+
144
+ it 'logs a warning' do
145
+ logger.should_receive(:warn).with({
146
+ :message => 'Refused to delete non-JSON file.',
147
+ :json_file => 'example.json'
148
+ })
149
+ job.clear!
150
+ end
151
+ end
152
+ end
153
+
154
+ describe '#to_hash' do
155
+ let(:job) { Job.new('example.json') }
156
+
157
+ context 'with parsable JSON' do
158
+ before do
159
+ job.stub(:json).and_return({'chunky' => 'bacon'})
160
+ end
161
+
162
+ it 'includes all JSON attributes' do
163
+ expect(job.to_hash['chunky']).to eq 'bacon'
164
+ end
165
+
166
+ it 'includes the JSON filename' do
167
+ expect(job.to_hash['json_file']).to eq 'example.json'
168
+ end
169
+ end
170
+
171
+ context 'with unparsable JSON' do
172
+ before do
173
+ job.stub(:json).and_return { raise JSON::ParserError, 'Shitty JSON' }
174
+ end
175
+
176
+ it 'includes only the JSON filename' do
177
+ expect(job.to_hash).to eq({'json_file' => 'example.json'})
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ module Wicoris::Postman
4
+ describe Logger do
5
+ let(:logger) { Logger.new }
6
+
7
+ describe '#info' do
8
+ it 'only accepts one argument' do
9
+ expect {
10
+ logger.info('hey', :explosion => true)
11
+ }.to raise_error
12
+ #}.to raise_error ArgumentError
13
+ end
14
+ end
15
+
16
+ describe '#error' do
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ module Wicoris::Postman
4
+ describe Postman do
5
+ let(:postman) { Postman.new }
6
+
7
+ describe '#run' do
8
+ let(:job) { double('job') }
9
+
10
+ it 'processes each job' do
11
+ postman.should_receive(:jobs).
12
+ and_return [job, job]
13
+ job.should_receive(:process).twice
14
+ job.should_receive(:clear!).twice
15
+ postman.run
16
+ end
17
+
18
+ it 'clears failed jobs too' do
19
+ postman.stub(:jobs).and_return [job]
20
+ job.stub(:process)
21
+ job.should_receive(:clear!)
22
+ postman.run
23
+ end
24
+
25
+ context 'when job raises an error' do
26
+ it 'rescues failed jobs' do
27
+ postman.stub(:jobs).and_return [job, job]
28
+ job.should_receive(:process).twice.and_return { raise 'BOOOM!' }
29
+ job.stub(:clear!)
30
+ postman.run
31
+ end
32
+
33
+ it 'clears failed jobs too' do
34
+ postman.stub(:jobs).and_return [job]
35
+ job.stub(:process).and_return { raise 'BOOOM!' }
36
+ job.should_receive(:clear!)
37
+ postman.run
38
+ end
39
+ end
40
+ end
41
+
42
+ describe '#jobs' do
43
+ let(:postman) { Postman.new(:chunky => 'bacon') }
44
+
45
+ it 'initializes jobs by json files' do
46
+ job = double('job')
47
+ postman.should_receive(:json_files).
48
+ and_return ['example.json', 'example.json']
49
+ Job.should_receive(:new).
50
+ with('example.json', {:chunky => 'bacon'}).twice.and_return(job)
51
+ expect(postman.send(:jobs)).to eq [job, job]
52
+ end
53
+ end
54
+
55
+ describe '#json_files' do
56
+ it 'returns a list of all json files within the jobdir' do
57
+ postman.should_receive(:jobdir).and_return('jobdir')
58
+ Dir.should_receive(:glob).with('jobdir/*.JSON', File::FNM_CASEFOLD).
59
+ and_return('some files')
60
+ expect(postman.send(:json_files)).to eq 'some files'
61
+ end
62
+ end
63
+
64
+ describe '#jobdir' do
65
+ let(:postman) { Postman.new(:jobdir => '/chunky') }
66
+
67
+ it 'returns the jobdir from config' do
68
+ expect(postman.send(:jobdir)).to eq '/chunky'
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'wicoris/postman/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "wicoris-postman"
8
+ spec.version = Wicoris::Postman::VERSION
9
+ spec.authors = ["Björn Albers"]
10
+ spec.email = ["bjoernalbers@googlemail.com"]
11
+ spec.description = %q{Deliver letters from WiCoRIS}
12
+ spec.summary = "#{spec.name}-#{spec.version}"
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "json_pure", "~> 1.8"
22
+ spec.add_dependency "cabin", "~> 0.6"
23
+ spec.add_dependency "mixlib-cli", "~> 1.4"
24
+ spec.add_dependency "mixlib-config", "~> 2.1"
25
+ spec.add_dependency "jlo", "~> 0.3"
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.3"
28
+ spec.add_development_dependency "rake"
29
+ spec.add_development_dependency "aruba", "~> 0.5"
30
+ spec.add_development_dependency "aruba-doubles", "~> 1.2"
31
+ end