diversion 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +193 -0
  6. data/Rakefile +7 -0
  7. data/diversion.gemspec +40 -0
  8. data/lib/diversion.rb +35 -0
  9. data/lib/diversion/client.rb +21 -0
  10. data/lib/diversion/configurable.rb +100 -0
  11. data/lib/diversion/decode.rb +21 -0
  12. data/lib/diversion/decode/json.rb +47 -0
  13. data/lib/diversion/decode/params.rb +55 -0
  14. data/lib/diversion/encode.rb +67 -0
  15. data/lib/diversion/encode/json.rb +31 -0
  16. data/lib/diversion/encode/params.rb +26 -0
  17. data/lib/diversion/error.rb +2 -0
  18. data/lib/diversion/error/bad_url_data_format.rb +6 -0
  19. data/lib/diversion/error/configuration_error.rb +6 -0
  20. data/lib/diversion/error/key_missing_error.rb +6 -0
  21. data/lib/diversion/error/uri_missing_error.rb +6 -0
  22. data/lib/diversion/mailer.rb +11 -0
  23. data/lib/diversion/signing.rb +19 -0
  24. data/lib/diversion/url.rb +43 -0
  25. data/lib/diversion/version.rb +17 -0
  26. data/spec/coverage/assets/0.7.1/application.css +1110 -0
  27. data/spec/coverage/assets/0.7.1/application.js +626 -0
  28. data/spec/coverage/assets/0.7.1/fancybox/blank.gif +0 -0
  29. data/spec/coverage/assets/0.7.1/fancybox/fancy_close.png +0 -0
  30. data/spec/coverage/assets/0.7.1/fancybox/fancy_loading.png +0 -0
  31. data/spec/coverage/assets/0.7.1/fancybox/fancy_nav_left.png +0 -0
  32. data/spec/coverage/assets/0.7.1/fancybox/fancy_nav_right.png +0 -0
  33. data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_e.png +0 -0
  34. data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_n.png +0 -0
  35. data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_ne.png +0 -0
  36. data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_nw.png +0 -0
  37. data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_s.png +0 -0
  38. data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_se.png +0 -0
  39. data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_sw.png +0 -0
  40. data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_w.png +0 -0
  41. data/spec/coverage/assets/0.7.1/fancybox/fancy_title_left.png +0 -0
  42. data/spec/coverage/assets/0.7.1/fancybox/fancy_title_main.png +0 -0
  43. data/spec/coverage/assets/0.7.1/fancybox/fancy_title_over.png +0 -0
  44. data/spec/coverage/assets/0.7.1/fancybox/fancy_title_right.png +0 -0
  45. data/spec/coverage/assets/0.7.1/fancybox/fancybox-x.png +0 -0
  46. data/spec/coverage/assets/0.7.1/fancybox/fancybox-y.png +0 -0
  47. data/spec/coverage/assets/0.7.1/fancybox/fancybox.png +0 -0
  48. data/spec/coverage/assets/0.7.1/favicon_green.png +0 -0
  49. data/spec/coverage/assets/0.7.1/favicon_red.png +0 -0
  50. data/spec/coverage/assets/0.7.1/favicon_yellow.png +0 -0
  51. data/spec/coverage/assets/0.7.1/loading.gif +0 -0
  52. data/spec/coverage/assets/0.7.1/magnify.png +0 -0
  53. data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  54. data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  55. data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  56. data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  57. data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  58. data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  59. data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  60. data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  61. data/spec/coverage/assets/0.7.1/smoothness/images/ui-icons_222222_256x240.png +0 -0
  62. data/spec/coverage/assets/0.7.1/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  63. data/spec/coverage/assets/0.7.1/smoothness/images/ui-icons_454545_256x240.png +0 -0
  64. data/spec/coverage/assets/0.7.1/smoothness/images/ui-icons_888888_256x240.png +0 -0
  65. data/spec/coverage/assets/0.7.1/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  66. data/spec/coverage/index.html +3146 -0
  67. data/spec/diversion/client_spec.rb +23 -0
  68. data/spec/diversion/configurable_spec.rb +128 -0
  69. data/spec/diversion/decode/json_decode_spec.rb +70 -0
  70. data/spec/diversion/decode/params_decode_spec.rb +74 -0
  71. data/spec/diversion/decode_spec.rb +12 -0
  72. data/spec/diversion/encode/json_encode_spec.rb +39 -0
  73. data/spec/diversion/encode/params_encode_spec.rb +39 -0
  74. data/spec/diversion/encode_spec.rb +30 -0
  75. data/spec/diversion/mailer_spec.rb +31 -0
  76. data/spec/diversion/support/global_shared_context.rb +29 -0
  77. data/spec/diversion/url_spec.rb +36 -0
  78. data/spec/fixtures/mail.html +13 -0
  79. data/spec/fixtures/sample_email.multipart +39 -0
  80. data/spec/fixtures/sample_email.text +12 -0
  81. data/spec/spec_helper.rb +84 -0
  82. metadata +323 -0
  83. metadata.gz.sig +0 -0
@@ -0,0 +1,23 @@
1
+ require "spec_helper"
2
+
3
+ describe Diversion::Client do
4
+
5
+ it "delegates to a client" do
6
+ expect(Diversion.encode(HTML)).to eq(html_params_encoded)
7
+ end
8
+
9
+ it "configures" do
10
+ client = Diversion::Client.new
11
+ client.configure do |c|
12
+ c.host = "dummy.host"
13
+ end
14
+ expect(client.host).to eq("dummy.host")
15
+ end
16
+
17
+ it "allows for separate configs" do
18
+ m1 = Diversion::Client.new({:host => "test1"})
19
+ m2 = Diversion::Client.new({:host => "test2"})
20
+ expect(m1.host).to_not eq(m2.host)
21
+ end
22
+
23
+ end
@@ -0,0 +1,128 @@
1
+ require 'spec_helper'
2
+
3
+ class ConfigTestStub
4
+ include Diversion::Configurable
5
+ end
6
+
7
+ describe Diversion::Configurable do
8
+
9
+ let(:config) { config = ConfigTestStub.new; config.reset!; config }
10
+
11
+ def set_config(&block) config.configure do |c| c.instance_eval(&block) end; end
12
+ def host(value); set_config{|c|c.host = value}; end
13
+ def path(value); set_config{|c|c.path = value}; end
14
+ def port(value); set_config{|c|c.port = value}; end
15
+ def encode_uris(value); set_config{|c|c.encode_uris = value}; end
16
+ def sign_length(value); set_config{|c|c.sign_length = value}; end
17
+ def url_encoding(value); set_config{|c|c.url_encoding = value}; end
18
+ def url_decoding(value); set_config{|c|c.url_decoding = value}; end
19
+
20
+ describe "host" do
21
+ it "must be a string" do
22
+ expect{host(1)}.to raise_error
23
+ end
24
+ it "accepts string type" do
25
+ expect{host('test')}.to_not raise_error
26
+ end
27
+ it "cannot end with trailing slash" do
28
+ expect{host('test/')}.to raise_error
29
+ end
30
+ it "doesnt accept nil" do
31
+ expect{host(nil)}.to raise_error
32
+ end
33
+ end
34
+
35
+ describe "path" do
36
+ it "must be a string" do
37
+ expect{path(1)}.to raise_error
38
+ end
39
+ it "accepts string type" do
40
+ expect{path('test/')}.to_not raise_error
41
+ end
42
+ it "must end with trailing slash" do
43
+ expect{path('test')}.to raise_error
44
+ end
45
+ it "doesnt accept nil" do
46
+ expect{path(nil)}.to raise_error
47
+ end
48
+ end
49
+
50
+ describe "port" do
51
+ it "must be an integer" do
52
+ expect{port('test')}.to raise_error
53
+ end
54
+ it "accepts an integer" do
55
+ expect{port(1)}.to_not raise_error
56
+ end
57
+ it "does not accept 0" do
58
+ expect{port(0)}.to raise_error
59
+ end
60
+ it "does not accept <0" do
61
+ expect{port(-1)}.to raise_error
62
+ end
63
+ it "doesnt accept nil" do
64
+ expect{port(nil)}.to raise_error
65
+ end
66
+ end
67
+
68
+ describe "sign_length" do
69
+ it "must be an integer" do
70
+ expect{sign_length('test')}.to raise_error
71
+ end
72
+ it "accepts 0 (disaled)" do
73
+ expect{sign_length(0)}.to_not raise_error
74
+ end
75
+ it "accepts integer at lower range" do
76
+ expect{sign_length(1)}.to_not raise_error
77
+ end
78
+ it "accepts integer at higher range" do
79
+ expect{sign_length(Diversion::Signing::MAX_SIGN_LENGTH)}.to_not raise_error
80
+ end
81
+ it "does not accept <0" do
82
+ expect{sign_length(-1)}.to raise_error
83
+ end
84
+ it "doesnt accept nil" do
85
+ expect{sign_length(nil)}.to raise_error
86
+ end
87
+ end
88
+
89
+ describe "encode_uris" do
90
+ it "must be an array" do
91
+ expect{encode_uris('test')}.to raise_error
92
+ end
93
+ it "accepts uri" do
94
+ expect{encode_uris(['http'])}.to_not raise_error
95
+ end
96
+ it "doesnt accept nil" do
97
+ expect{encode_uris(nil)}.to raise_error
98
+ end
99
+ it "doesnt accept empty array" do
100
+ expect{encode_uris([])}.to raise_error
101
+ end
102
+ end
103
+
104
+ describe "url_encoding" do
105
+ it "must be a module" do
106
+ expect{url_encoding('test')}.to raise_error
107
+ end
108
+ it "accepts a valid module" do
109
+ expect{url_encoding(Diversion::Encode::Json)}.to_not raise_error
110
+ end
111
+ it "doesn't accept an invalid module" do
112
+ expect{url_encoding(Diversion::Decode::Json)}.to raise_error
113
+ end
114
+ end
115
+
116
+ describe "url_decoding" do
117
+ it "must be a module" do
118
+ expect{url_decoding('test')}.to raise_error
119
+ end
120
+ it "accepts a valid module" do
121
+ expect{url_decoding(Diversion::Decode::Json)}.to_not raise_error
122
+ end
123
+ it "doesn't accept an invalid module" do
124
+ expect{url_decoding(Diversion::Encode::Json)}.to raise_error
125
+ end
126
+ end
127
+
128
+ end
@@ -0,0 +1,70 @@
1
+ require "spec_helper"
2
+
3
+ describe Diversion::Decode::Json do
4
+
5
+ include_context "json"
6
+
7
+ it "returns correct type for non-signed" do
8
+ expect(decode_json).to be_a Hash
9
+ end
10
+
11
+ it "returns correct type for signed" do
12
+ expect(decode_json_signed).to be_a Hash
13
+ end
14
+
15
+ it "raises when data format invalid" do
16
+ expect{client.decode("test-test-test")}.to raise_error(Diversion::Error::BadUrlDataFormat)
17
+ end
18
+
19
+ it "doesn't parse when passed bad data" do
20
+ expect(client.decode("test-test")[:parsed]).to be_false
21
+ end
22
+
23
+ it "parses when passed good data" do
24
+ expect(decode_json[:parsed]).to be_true
25
+ end
26
+
27
+ it "returns expected values when passed good unsigned data" do
28
+ hash = decode_json
29
+ expect(hash[:url]).to eq("http://test.com/test")
30
+ expect(hash[:signed]).to be_false
31
+ expect(hash[:key_presented]).to be_empty
32
+ expect(hash[:key_expected]).to be_empty
33
+ expect(hash[:key_verified]).to be_false
34
+ end
35
+
36
+ it "returns expected values when passed good signed data" do
37
+ hash = decode_json_signed
38
+ expect(hash[:url]).to eq("http://test.com/test")
39
+ expect(hash[:signed]).to be_true
40
+ expect(hash[:key_presented]).to eq(JSON_KEY)
41
+ expect(hash[:key_expected]).to eq(JSON_KEY)
42
+ expect(hash[:key_verified]).to be_true
43
+ end
44
+
45
+ it "returns expected values when passed badly signed data" do
46
+ hash = decode_json_bad_key
47
+ expect(hash[:url]).to eq("http://test.com/test")
48
+ expect(hash[:signed]).to be_true
49
+ expect(hash[:key_presented]).to eq(JSON_KEY_BAD)
50
+ expect(hash[:key_expected]).to eq(JSON_KEY)
51
+ expect(hash[:key_verified]).to be_false
52
+ end
53
+
54
+ it "decodes parameters as expected" do
55
+ enc = client.encode(HTML, {:b => 999})
56
+ data = enc.scan(/".*?\/.*?\/.*?\/.*?\/.*?\/(.*?)"/).first.first
57
+ result = client.decode(data)
58
+ expect(result[:test]).to eq("1234")
59
+ expect(result[:b]).to eq(999)
60
+ end
61
+
62
+ it "decodes parameters as expected when signed" do
63
+ enc = client_sign.encode(HTML, {:b => 999})
64
+ data = enc.scan(/".*?\/.*?\/.*?\/.*?\/.*?\/(.*?)"/).first.first
65
+ hash = client.decode(data)
66
+ expect(hash[:test]).to eq("1234")
67
+ expect(hash[:b]).to eq(999)
68
+ end
69
+
70
+ end
@@ -0,0 +1,74 @@
1
+ require "spec_helper"
2
+
3
+ describe Diversion::Decode::Params do
4
+
5
+ include_context "params"
6
+
7
+ it "returns correct type for non-signed" do
8
+ expect(decode_params).to be_a Hash
9
+ end
10
+
11
+ it "returns correct type for signed" do
12
+ expect(decode_params_signed).to be_a Hash
13
+ end
14
+
15
+ it "raises when data format invalid" do
16
+ expect{client.decode("badparam=badvalue")}.to raise_error(Diversion::Error::BadUrlDataFormat)
17
+ end
18
+
19
+ it "raises when data format invalid" do
20
+ expect{client.decode("rubbish")}.to raise_error(Diversion::Error::BadUrlDataFormat)
21
+ end
22
+
23
+ it "raises when missing url data parameter" do
24
+ expect{client.decode("d=#{CGI::escape('badparam=badvalue')}")}.to raise_error(Diversion::Error::BadUrlDataFormat)
25
+ end
26
+
27
+ it "raises when missing url data parameter empty" do
28
+ expect{client.decode("d=#{CGI::escape('url=')}")}.to raise_error(Diversion::Error::BadUrlDataFormat)
29
+ end
30
+
31
+ it "returns expected values when passed good unsigned data" do
32
+ hash = decode_params
33
+ expect(hash[:url]).to eq("http://test.com/test")
34
+ expect(hash[:signed]).to be_false
35
+ expect(hash[:key_presented]).to be_empty
36
+ expect(hash[:key_expected]).to be_empty
37
+ expect(hash[:key_verified]).to be_false
38
+ end
39
+
40
+ it "returns expected values when passed good signed data" do
41
+ hash = decode_params_signed
42
+ expect(hash[:url]).to eq("http://test.com/test")
43
+ expect(hash[:signed]).to be_true
44
+ expect(hash[:key_presented]).to eq(PARAMS_KEY)
45
+ expect(hash[:key_expected]).to eq(PARAMS_KEY)
46
+ expect(hash[:key_verified]).to be_true
47
+ end
48
+
49
+ it "returns expected values when passed badly signed data" do
50
+ hash = decode_params_bad_key
51
+ expect(hash[:url]).to eq("http://test.com/test")
52
+ expect(hash[:signed]).to be_true
53
+ expect(hash[:key_presented]).to eq(PARAMS_KEY_BAD)
54
+ expect(hash[:key_expected]).to eq(PARAMS_KEY)
55
+ expect(hash[:key_verified]).to be_false
56
+ end
57
+
58
+ it "decodes parameters as expected" do
59
+ enc = client.encode(HTML, {:b => 999})
60
+ data = enc.scan(/".*?\/.*?\/.*?\/.*?\/.*?\/(.*?)"/).first.first[1..-1]
61
+ result = client.decode(data)
62
+ expect(result[:test]).to eq("1234")
63
+ expect(result[:b]).to eq("999")
64
+ end
65
+
66
+ it "decodes parameters as expected when signed" do
67
+ enc = client_sign.encode(HTML, {:b => 999})
68
+ data = enc.scan(/".*?\/.*?\/.*?\/.*?\/.*?\/(.*?)"/).first.first[1..-1]
69
+ hash = client.decode(data)
70
+ expect(hash[:test]).to eq("1234")
71
+ expect(hash[:b]).to eq("999")
72
+ end
73
+
74
+ end
@@ -0,0 +1,12 @@
1
+ require "spec_helper"
2
+
3
+ describe Diversion::Decode do
4
+
5
+ # we use json encoding here but it doesn't matter as we're testing common functionality
6
+ include_context "json"
7
+
8
+ it "raise ArgumentError if data.length == 0" do
9
+ expect{client.decode("")}.to raise_error(ArgumentError)
10
+ end
11
+
12
+ end
@@ -0,0 +1,39 @@
1
+ require "spec_helper"
2
+
3
+ describe Diversion::Encode::Json do
4
+
5
+ include_context "json"
6
+
7
+ it "returns correct type" do
8
+ expect(encode_email).to be_a String
9
+ end
10
+
11
+ it "uses correct path" do
12
+ expect(encode_json_html).to match(/http:\/\/localhost.domain\/redirect\/1\/[A-Za-z0-9].*\">test<\/a>/)
13
+ end
14
+
15
+ it "doesn't add port for 80" do
16
+ client.port = 80
17
+ expect(encode_json_html).to_not match(/http:\/\/localhost.domain:80\/redirect/)
18
+ end
19
+
20
+ it "adds port number for non-80 port" do
21
+ client.port = 81
22
+ expect(encode_json_html).to match(/http:\/\/localhost.domain:81\/redirect/)
23
+ end
24
+
25
+ it "doesn't sign by default" do
26
+ expect(encode_json_html).to_not match(/-/)
27
+ end
28
+
29
+ it "signs correctly" do
30
+ client_sign.sign_length = 32
31
+ expect(encode_json_html_signed).to match(/-[A-Za-z0-9]{32}\"/)
32
+ end
33
+
34
+ it "observes sign_length" do
35
+ client_sign.sign_length = 2
36
+ expect(encode_json_html_signed).to match(/-[A-Za-z0-9]{2}\"/)
37
+ end
38
+
39
+ end
@@ -0,0 +1,39 @@
1
+ require "spec_helper"
2
+
3
+ describe Diversion::Encode::Params do
4
+
5
+ include_context "params"
6
+
7
+ it "returns correct type" do
8
+ expect(encode_email).to be_a String
9
+ end
10
+
11
+ it "uses correct path" do
12
+ expect(encode_params_html).to eq(html_params_encoded)
13
+ end
14
+
15
+ it "doesn't add port for 80" do
16
+ client.port = 80
17
+ expect(encode_params_html).to eq(html_params_encoded)
18
+ end
19
+
20
+ it "adds port number for non-80 port" do
21
+ client.port = 81
22
+ expect(encode_params_html).to eq(html_params_encoded({:port => 81}))
23
+ end
24
+
25
+ it "doesn't sign by default" do
26
+ expect(encode_params_html).to eq(html_params_encoded)
27
+ end
28
+
29
+ it "signs correctly" do
30
+ client_sign.sign_length = 32
31
+ expect(encode_params_html_signed).to eq(html_params_encoded({:sign_length => DEFAULT_SIGN_LEN}))
32
+ end
33
+
34
+ it "observes sign_length" do
35
+ client_sign.sign_length = 2
36
+ expect(encode_params_html_signed).to eq(html_params_encoded({:sign_length => 2}))
37
+ end
38
+
39
+ end
@@ -0,0 +1,30 @@
1
+ require "spec_helper"
2
+
3
+ describe Diversion::Encode do
4
+
5
+ # we use json encoding here but it doesn't matter as we're testing common functionality
6
+ include_context "json"
7
+
8
+ it "ignores non-http uris" do
9
+ expect(encode_email).to include('mailto:jess@doesnotexist.domain')
10
+ end
11
+
12
+ it "converts http uris" do
13
+ expect(encode_email).to_not include('https://twitter.com/intent/tweet?in_reply_to=51113028241989632')
14
+ end
15
+
16
+ it "removes data attributes" do
17
+ expect(encode_email).to_not include('data-')
18
+ end
19
+
20
+ it "raises when signing key not set" do
21
+ client_sign.sign_key = nil
22
+ expect { encode_json_html_signed }.to raise_error(Diversion::Error::KeyMissingError)
23
+ end
24
+
25
+ it "raises when no uris defined" do
26
+ client.encode_uris = []
27
+ expect { encode_json_html_signed }.to raise_error(Diversion::Error::ConfigurationError)
28
+ end
29
+
30
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mail::Message do
4
+
5
+ let(:multipart) { Mail::Message.new(fixture('sample_email.multipart').read).diversion }
6
+ let(:multipart_args) { Mail::Message.new(fixture('sample_email.multipart').read).diversion({:a => 1}, {:encode_uris => ['http']}) }
7
+ let(:text_only) { Mail::Message.new(fixture('sample_email.text').read).diversion }
8
+
9
+ it "replaces html part" do
10
+ expect(multipart.html_part.body).to include('<a href="http://localhost.domain/redirect/1/?d=url%3Dhttp%253A%252F%252Fwww.youtube.com%252Fwatch%253Fv%253DFE_9CzLCbkY">Dream of the 90s</a>')
11
+ expect(multipart.html_part.body).to include('<a href="http://localhost.domain/redirect/1/?d=url%3Dhttps%253A%252F%252Fwww.youtube.com%252Fwatch%253Fv%253DOyQ6pqPFwTI">Customers only</a>')
12
+ end
13
+
14
+ it "doesn't replace text part" do
15
+ expect(text_only.body).to include('http://www.youtube.com/watch?v=FE_9CzLCbkY')
16
+ expect(text_only.body).to include('http://www.youtube.com/watch?v=OyQ6pqPFwTI')
17
+ end
18
+
19
+ it "doesn't fail when only text part present" do
20
+ expect{text_only}.to_not raise_error
21
+ end
22
+
23
+ it "honors additional global data parameters" do
24
+ expect(multipart_args.html_part.body).to include('<a href="http://localhost.domain/redirect/1/?d=a%3D1%26url%3Dhttp%253A%252F%252Fwww.youtube.com%252Fwatch%253Fv%253DFE_9CzLCbkY">Dream of the 90s</a>')
25
+ end
26
+
27
+ it "honors additional config arguments" do
28
+ expect(multipart_args.html_part.body).to include('<a href="https://www.youtube.com/watch?v=OyQ6pqPFwTI">Customers only</a>')
29
+ end
30
+
31
+ end