dmarc 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/.travis.yml +15 -5
- data/ChangeLog.md +13 -1
- data/Gemfile +5 -0
- data/README.md +43 -2
- data/dmarc.gemspec +1 -0
- data/lib/dmarc.rb +1 -0
- data/lib/dmarc/dmarc.rb +30 -0
- data/lib/dmarc/exceptions.rb +3 -16
- data/lib/dmarc/parser.rb +29 -17
- data/lib/dmarc/record.rb +190 -17
- data/lib/dmarc/version.rb +1 -1
- data/spec/dmarc_spec.rb +24 -0
- data/spec/parser_spec.rb +285 -239
- data/spec/record_spec.rb +91 -35
- data/spec/spec_helper.rb +3 -0
- metadata +18 -15
data/lib/dmarc/version.rb
CHANGED
data/spec/dmarc_spec.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dmarc'
|
3
|
+
|
4
|
+
describe DMARC do
|
5
|
+
subject { described_class }
|
6
|
+
|
7
|
+
describe ".query" do
|
8
|
+
context "when given a domain" do
|
9
|
+
let(:domain) { 'google.com' }
|
10
|
+
|
11
|
+
it "should query and parse the DMARC record" do
|
12
|
+
record = subject.query(domain)
|
13
|
+
|
14
|
+
expect(record).to be == 'v=DMARC1; p=quarantine; rua=mailto:mailauth-reports@google.com'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "when given a bad domain" do
|
19
|
+
it "should raise a DNS error" do
|
20
|
+
expect(subject.query('foobar.com')).to be_nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/spec/parser_spec.rb
CHANGED
@@ -2,301 +2,322 @@ require 'spec_helper'
|
|
2
2
|
require 'dmarc/parser'
|
3
3
|
|
4
4
|
describe Parser do
|
5
|
-
describe
|
6
|
-
|
5
|
+
describe "rules" do
|
6
|
+
describe 'dmarc_uri' do
|
7
|
+
subject { described_class.new.dmarc_uri }
|
7
8
|
|
8
|
-
|
9
|
+
let(:uri) { 'mailto:user@example.org' }
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
it 'parses mailto URIs' do
|
12
|
+
expect(subject.parse(uri)).to eq(uri: uri)
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
it 'parses mailto URIs with size' do
|
16
|
+
expect(subject.parse(uri + '!20')).to eq(uri: uri, size: '20')
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
-
|
19
|
+
it 'parses mailto URIs with size and unit' do
|
20
|
+
expect(subject.parse(uri + '!20k')).to eq(uri: uri, size: '20', unit: 'k')
|
21
|
+
end
|
20
22
|
end
|
21
|
-
end
|
22
23
|
|
23
|
-
|
24
|
-
|
24
|
+
describe 'dmarc_record' do
|
25
|
+
subject { described_class.new.dmarc_record }
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
it 'parses version and policy' do
|
28
|
+
record = 'v=DMARC1;p=none'
|
29
|
+
expect(subject.parse record).to eq([
|
30
|
+
{v: 'DMARC1'},
|
31
|
+
{p: 'none'}
|
32
|
+
])
|
33
|
+
end
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
35
|
+
it 'parses version, policy, and other tags' do
|
36
|
+
record = 'v=DMARC1;p=none;sp=reject;adkim=r;aspf=r'
|
37
|
+
expect(subject.parse(record)).to eq([
|
38
|
+
{v: 'DMARC1'},
|
39
|
+
{p: 'none'},
|
40
|
+
{sp: 'reject'},
|
41
|
+
{adkim: 'r'},
|
42
|
+
{aspf: 'r'}
|
43
|
+
])
|
44
|
+
end
|
44
45
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
46
|
+
it 'parses version, policy, and rua' do
|
47
|
+
record = 'v=DMARC1;p=quarantine;rua=mailto:foo@example.com,mailto:bar@example.com'
|
48
|
+
expect(subject.parse record).to eq([
|
49
|
+
{v: 'DMARC1'},
|
50
|
+
{p: 'quarantine'},
|
51
|
+
{rua: [
|
52
|
+
{uri: 'mailto:foo@example.com'},
|
53
|
+
{uri: 'mailto:bar@example.com'}
|
54
|
+
]}
|
55
|
+
])
|
56
|
+
end
|
56
57
|
|
57
|
-
|
58
|
-
|
59
|
-
record2 = 'v = DMARC1 ; p = none ; sp = reject'
|
60
|
-
expect(subject.parse record1).to eq(subject.parse record2)
|
61
|
-
end
|
58
|
+
it 'ignores order of tags' do
|
59
|
+
record = 'v=DMARC1; rua=mailto:foo@example.com; p=none'
|
62
60
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
{adkim: 'r'},
|
70
|
-
{aspf: 'r'}
|
71
|
-
])
|
72
|
-
end
|
61
|
+
expect(subject.parse(record)).to eq([
|
62
|
+
{v: 'DMARC1'},
|
63
|
+
{rua: {uri: 'mailto:foo@example.com'}},
|
64
|
+
{p: 'none'}
|
65
|
+
])
|
66
|
+
end
|
73
67
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
])
|
80
|
-
end
|
81
|
-
end
|
68
|
+
it 'ignores spacing' do
|
69
|
+
record1 = 'v=DMARC1;p=none;sp=reject'
|
70
|
+
record2 = 'v = DMARC1 ; p = none ; sp = reject'
|
71
|
+
expect(subject.parse record1).to eq(subject.parse record2)
|
72
|
+
end
|
82
73
|
|
83
|
-
|
84
|
-
|
74
|
+
it 'ignores trailing separators' do
|
75
|
+
record = 'v=DMARC1; p=none; '
|
85
76
|
|
86
|
-
|
87
|
-
|
88
|
-
|
77
|
+
expect(subject.parse(record)).to eq([
|
78
|
+
{v: 'DMARC1'},
|
79
|
+
{p: 'none'}
|
80
|
+
])
|
81
|
+
end
|
89
82
|
|
90
|
-
|
91
|
-
|
83
|
+
it "ignores unknown tags" do
|
84
|
+
record = 'v=DMARC1;p=none;foo=xxx;sp=reject;bar=xxx;adkim=r;aspf=r'
|
85
|
+
expect(subject.parse(record)).to eq([
|
86
|
+
{v: 'DMARC1'},
|
87
|
+
{p: 'none'},
|
88
|
+
{sp: 'reject'},
|
89
|
+
{adkim: 'r'},
|
90
|
+
{aspf: 'r'}
|
91
|
+
])
|
92
|
+
end
|
93
|
+
|
94
|
+
it "ignores syntax errors" do
|
95
|
+
record = 'v=DMARC1;p=none;sp;adkim=X;aspf='
|
96
|
+
expect(subject.parse(record)).to eq([
|
97
|
+
{v: 'DMARC1'},
|
98
|
+
{p: 'none'}
|
99
|
+
])
|
100
|
+
end
|
92
101
|
end
|
93
|
-
end
|
94
102
|
|
95
|
-
|
96
|
-
|
103
|
+
describe 'dmarc_version' do
|
104
|
+
subject { described_class.new.dmarc_version }
|
97
105
|
|
98
|
-
|
99
|
-
|
100
|
-
|
106
|
+
it 'parses DMARC versions' do
|
107
|
+
expect(subject.parse('v=DMARC1')).to eq(v: 'DMARC1')
|
108
|
+
end
|
101
109
|
|
102
|
-
|
103
|
-
|
110
|
+
it 'ignores spacing' do
|
111
|
+
expect(subject.parse 'v = DMARC1').to eq(subject.parse 'v=DMARC1')
|
112
|
+
end
|
104
113
|
end
|
105
114
|
|
106
|
-
|
107
|
-
|
108
|
-
end
|
115
|
+
describe 'dmarc_request' do
|
116
|
+
subject { described_class.new.dmarc_request }
|
109
117
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
end
|
118
|
+
it 'parses "none" requests' do
|
119
|
+
expect(subject.parse('p=none')).to eq(p: 'none')
|
120
|
+
end
|
114
121
|
|
115
|
-
|
116
|
-
|
122
|
+
it 'parses quarantine requests' do
|
123
|
+
expect(subject.parse('p=quarantine')).to eq(p: 'quarantine')
|
124
|
+
end
|
117
125
|
|
118
|
-
|
119
|
-
|
120
|
-
|
126
|
+
it 'parses reject requests' do
|
127
|
+
expect(subject.parse('p=reject')).to eq(p: 'reject')
|
128
|
+
end
|
121
129
|
|
122
|
-
|
123
|
-
|
130
|
+
it 'ingores spacing' do
|
131
|
+
expect(subject.parse 'p=none').to eq(subject.parse 'p = none')
|
132
|
+
end
|
124
133
|
end
|
125
134
|
|
126
|
-
|
127
|
-
|
128
|
-
end
|
135
|
+
describe 'dmarc_srequest' do
|
136
|
+
subject { described_class.new.dmarc_srequest }
|
129
137
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
end
|
138
|
+
it 'parses "none" requests' do
|
139
|
+
expect(subject.parse('sp=none')).to eq(sp: 'none')
|
140
|
+
end
|
134
141
|
|
135
|
-
|
136
|
-
|
142
|
+
it 'parses quarantine requests' do
|
143
|
+
expect(subject.parse('sp=quarantine')).to eq(sp: 'quarantine')
|
144
|
+
end
|
137
145
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
uri: 'mailto:user@example.org'
|
142
|
-
}
|
143
|
-
)
|
144
|
-
end
|
146
|
+
it 'parses reject requests' do
|
147
|
+
expect(subject.parse('sp=reject')).to eq(sp: 'reject')
|
148
|
+
end
|
145
149
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
).to eq(
|
150
|
-
rua: [
|
151
|
-
{uri: 'mailto:user1@example.org'},
|
152
|
-
{uri: 'mailto:user2@example.org'}
|
153
|
-
]
|
154
|
-
)
|
150
|
+
it 'ignores spacing' do
|
151
|
+
expect(subject.parse 'sp=none').to eq(subject.parse 'sp = none')
|
152
|
+
end
|
155
153
|
end
|
156
154
|
|
157
|
-
|
158
|
-
|
159
|
-
rua: {
|
160
|
-
uri: 'mailto:user1@example.com',
|
161
|
-
size: '20',
|
162
|
-
unit: 'm'
|
163
|
-
}
|
164
|
-
)
|
165
|
-
end
|
166
|
-
end
|
155
|
+
describe 'dmarc_auri' do
|
156
|
+
subject { described_class.new.dmarc_auri }
|
167
157
|
|
168
|
-
|
169
|
-
|
158
|
+
it 'parses one URI' do
|
159
|
+
expect(subject.parse('rua=mailto:user@example.org')).to eq(
|
160
|
+
rua: {
|
161
|
+
uri: 'mailto:user@example.org'
|
162
|
+
}
|
163
|
+
)
|
164
|
+
end
|
170
165
|
|
171
|
-
|
172
|
-
|
173
|
-
|
166
|
+
it 'parses many URIs' do
|
167
|
+
expect(
|
168
|
+
subject.parse('rua = mailto:user1@example.org, mailto:user2@example.org')
|
169
|
+
).to eq(
|
170
|
+
rua: [
|
171
|
+
{uri: 'mailto:user1@example.org'},
|
172
|
+
{uri: 'mailto:user2@example.org'}
|
173
|
+
]
|
174
|
+
)
|
175
|
+
end
|
174
176
|
|
175
|
-
|
176
|
-
|
177
|
+
it 'parses maximum report size' do
|
178
|
+
expect(subject.parse('rua = mailto:user1@example.com!20m')).to eq(
|
179
|
+
rua: {
|
180
|
+
uri: 'mailto:user1@example.com',
|
181
|
+
size: '20',
|
182
|
+
unit: 'm'
|
183
|
+
}
|
184
|
+
)
|
185
|
+
end
|
177
186
|
end
|
178
187
|
|
179
|
-
|
180
|
-
|
181
|
-
end
|
182
|
-
end
|
188
|
+
describe 'dmarc_ainterval' do
|
189
|
+
subject { described_class.new.dmarc_ainterval }
|
183
190
|
|
184
|
-
|
185
|
-
|
191
|
+
it 'parses a one digit interval' do
|
192
|
+
expect(subject.parse('ri=1')).to eq(ri: '1')
|
193
|
+
end
|
186
194
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
uri: 'mailto:user@example.org'
|
191
|
-
}
|
192
|
-
)
|
193
|
-
end
|
195
|
+
it 'parses a many digit interval' do
|
196
|
+
expect(subject.parse('ri=86400')).to eq(ri: '86400')
|
197
|
+
end
|
194
198
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
).to eq(
|
199
|
-
ruf: [
|
200
|
-
{uri: 'mailto:user1@example.org'},
|
201
|
-
{uri: 'mailto:user2@example.org'}
|
202
|
-
]
|
203
|
-
)
|
199
|
+
it 'ignores spacing' do
|
200
|
+
expect(subject.parse 'ri = 86400').to eq(subject.parse 'ri=86400')
|
201
|
+
end
|
204
202
|
end
|
205
203
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
204
|
+
describe 'dmarc_furi' do
|
205
|
+
subject { described_class.new.dmarc_furi }
|
206
|
+
|
207
|
+
it 'parses one URI' do
|
208
|
+
expect(subject.parse('ruf=mailto:user@example.org')).to eq(
|
209
|
+
ruf: {
|
210
|
+
uri: 'mailto:user@example.org'
|
211
|
+
}
|
212
|
+
)
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'parses many URIs' do
|
216
|
+
expect(
|
217
|
+
subject.parse('ruf = mailto:user1@example.org, mailto:user2@example.org')
|
218
|
+
).to eq(
|
219
|
+
ruf: [
|
220
|
+
{uri: 'mailto:user1@example.org'},
|
221
|
+
{uri: 'mailto:user2@example.org'}
|
222
|
+
]
|
223
|
+
)
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'parses maximum report size' do
|
227
|
+
expect(subject.parse('ruf = mailto:user1@example.com!20m')).to eq(
|
228
|
+
ruf: {
|
229
|
+
uri: 'mailto:user1@example.com',
|
230
|
+
size: '20',
|
231
|
+
unit: 'm'
|
232
|
+
}
|
233
|
+
)
|
234
|
+
end
|
214
235
|
end
|
215
|
-
end
|
216
236
|
|
217
|
-
|
218
|
-
|
237
|
+
describe 'dmarc_fo' do
|
238
|
+
let(:fo) { described_class.new.dmarc_fo }
|
219
239
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
240
|
+
context 'one value' do
|
241
|
+
%w[0 1 d s].each do |value|
|
242
|
+
it "parses #{value}" do
|
243
|
+
expect(fo.parse("fo=#{value}")).to eq(fo: {fo_opt: value})
|
244
|
+
end
|
224
245
|
end
|
225
246
|
end
|
226
|
-
end
|
227
247
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
248
|
+
it 'parses many values' do
|
249
|
+
expect(fo.parse('fo=0:1:d:s')).to eq(
|
250
|
+
fo: [{fo_opt: '0'}, {fo_opt: '1'}, {fo_opt: 'd'}, {fo_opt: 's'}]
|
251
|
+
)
|
252
|
+
end
|
232
253
|
end
|
233
|
-
end
|
234
254
|
|
235
|
-
|
236
|
-
|
255
|
+
describe 'dmarc_rfmt' do
|
256
|
+
subject { described_class.new.dmarc_rfmt }
|
237
257
|
|
238
|
-
|
239
|
-
|
240
|
-
|
258
|
+
it 'parses afrf format' do
|
259
|
+
expect(subject.parse('rf=afrf')).to eq(rf: 'afrf')
|
260
|
+
end
|
241
261
|
|
242
|
-
|
243
|
-
|
244
|
-
|
262
|
+
it 'parses iodef format' do
|
263
|
+
expect(subject.parse('rf=iodef')).to eq(rf: 'iodef')
|
264
|
+
end
|
245
265
|
|
246
|
-
|
247
|
-
|
266
|
+
it 'ignores spacing' do
|
267
|
+
expect(subject.parse 'rf=iodef').to eq(subject.parse 'rf = iodef')
|
268
|
+
end
|
248
269
|
end
|
249
|
-
end
|
250
270
|
|
251
|
-
|
252
|
-
|
271
|
+
describe 'dmarc_percent' do
|
272
|
+
subject { described_class.new.dmarc_percent }
|
253
273
|
|
254
|
-
|
255
|
-
|
256
|
-
|
274
|
+
it 'parses a one-digit percent' do
|
275
|
+
expect(subject.parse('pct=1')).to eq(pct: '1')
|
276
|
+
end
|
257
277
|
|
258
|
-
|
259
|
-
|
260
|
-
|
278
|
+
it 'parses a two-digit percent' do
|
279
|
+
expect(subject.parse('pct=10')).to eq(pct: '10')
|
280
|
+
end
|
261
281
|
|
262
|
-
|
263
|
-
|
264
|
-
|
282
|
+
it 'parses a three-digit percent' do
|
283
|
+
expect(subject.parse('pct=100')).to eq(pct: '100')
|
284
|
+
end
|
265
285
|
|
266
|
-
|
267
|
-
|
286
|
+
it 'ignores spacing' do
|
287
|
+
expect(subject.parse 'pct = 100').to eq(subject.parse 'pct=100')
|
288
|
+
end
|
268
289
|
end
|
269
|
-
end
|
270
290
|
|
271
|
-
|
272
|
-
|
291
|
+
describe 'dmarc_adkim' do
|
292
|
+
subject { described_class.new.dmarc_adkim }
|
273
293
|
|
274
|
-
|
275
|
-
|
276
|
-
|
294
|
+
it 'parses a relaxed DKIM policy' do
|
295
|
+
expect(subject.parse('adkim=r')).to eq(adkim: 'r')
|
296
|
+
end
|
277
297
|
|
278
|
-
|
279
|
-
|
280
|
-
|
298
|
+
it 'parses a strict DKIM policy' do
|
299
|
+
expect(subject.parse('adkim=s')).to eq(adkim: 's')
|
300
|
+
end
|
281
301
|
|
282
|
-
|
283
|
-
|
302
|
+
it 'ignores spacing' do
|
303
|
+
expect(subject.parse 'adkim=r').to eq(subject.parse 'adkim = r')
|
304
|
+
end
|
284
305
|
end
|
285
|
-
end
|
286
306
|
|
287
|
-
|
288
|
-
|
307
|
+
describe 'dmarc_aspf' do
|
308
|
+
subject { described_class.new.dmarc_aspf }
|
289
309
|
|
290
|
-
|
291
|
-
|
292
|
-
|
310
|
+
it 'parses a relaxed SPF policy' do
|
311
|
+
expect(subject.parse('aspf=r')).to eq(aspf: 'r')
|
312
|
+
end
|
293
313
|
|
294
|
-
|
295
|
-
|
296
|
-
|
314
|
+
it 'parses a strict SPF policy' do
|
315
|
+
expect(subject.parse('aspf=s')).to eq(aspf: 's')
|
316
|
+
end
|
297
317
|
|
298
|
-
|
299
|
-
|
318
|
+
it 'ignores spacing' do
|
319
|
+
expect(subject.parse 'aspf=s').to eq(subject.parse 'aspf = s')
|
320
|
+
end
|
300
321
|
end
|
301
322
|
end
|
302
323
|
|
@@ -305,17 +326,29 @@ describe Parser do
|
|
305
326
|
[
|
306
327
|
{v: 'DMARC1'},
|
307
328
|
{p: 'none'},
|
329
|
+
{sp: 'reject'},
|
308
330
|
{pct: '100'},
|
309
|
-
{
|
331
|
+
{adkim: 'r'},
|
332
|
+
{aspf: 'r'},
|
333
|
+
{fo: [{fo_opt: '0'}, {fo_opt: '1'}, {fo_opt: 'd'}, {fo_opt: 's'}]},
|
334
|
+
{rua: {uri: "mailto:d@rua.agari.com"}}
|
310
335
|
]
|
311
336
|
end
|
312
337
|
|
313
338
|
subject { described_class.new.apply(tree) }
|
314
339
|
|
340
|
+
it "should coerce :v into a Symbol" do
|
341
|
+
expect(subject).to include(v: :DMARC1)
|
342
|
+
end
|
343
|
+
|
315
344
|
it "should coerce :p into a Symbol" do
|
316
345
|
expect(subject).to include(p: :none)
|
317
346
|
end
|
318
347
|
|
348
|
+
it "should coerce :sp into a Symbol" do
|
349
|
+
expect(subject).to include(sp: :reject)
|
350
|
+
end
|
351
|
+
|
319
352
|
it "should flatten :fo options" do
|
320
353
|
expect(subject).to include(fo: %w[0 1 d s])
|
321
354
|
end
|
@@ -323,34 +356,47 @@ describe Parser do
|
|
323
356
|
it "should coerce :pct into an Integer" do
|
324
357
|
expect(subject).to include(pct: 100)
|
325
358
|
end
|
359
|
+
|
360
|
+
it "should coerce :adkim into a Symbol" do
|
361
|
+
expect(subject).to include(adkim: :r)
|
362
|
+
end
|
363
|
+
|
364
|
+
it "should coerce :aspf into a Symbol" do
|
365
|
+
expect(subject).to include(aspf: :r)
|
366
|
+
end
|
367
|
+
|
368
|
+
it "should convert {uri: ...} to URI objects" do
|
369
|
+
expect(subject).to include(rua: [URI("mailto:d@rua.agari.com")])
|
370
|
+
end
|
326
371
|
end
|
327
372
|
|
328
|
-
describe "
|
373
|
+
describe ".parse" do
|
374
|
+
subject { described_class }
|
375
|
+
|
329
376
|
let(:record) { "v=DMARC1;p=none;pct=100;fo=0:1:d:s" }
|
330
377
|
|
331
378
|
it "should combine all tags into one Hash" do
|
332
379
|
expect(subject.parse(record)).to eq(
|
333
|
-
v:
|
380
|
+
v: :DMARC1,
|
334
381
|
p: :none,
|
335
382
|
pct: 100,
|
336
383
|
fo: ['0', '1', 'd', 's']
|
337
384
|
)
|
338
385
|
end
|
339
386
|
|
340
|
-
|
341
|
-
|
342
|
-
context "Alexa Top 500", :gauntlet do
|
343
|
-
require 'csv'
|
387
|
+
context "Alexa Top 500", :gauntlet do
|
388
|
+
require 'csv'
|
344
389
|
|
345
|
-
|
346
|
-
|
390
|
+
path = File.expand_path('../data/alexa.csv', __FILE__)
|
391
|
+
csv = CSV.new(open(path), headers: false)
|
347
392
|
|
348
|
-
|
349
|
-
|
393
|
+
csv.each do |row|
|
394
|
+
rank, domain, dmarc = row
|
350
395
|
|
351
|
-
|
352
|
-
|
353
|
-
|
396
|
+
if dmarc
|
397
|
+
it "should parse #{dmarc.inspect}" do
|
398
|
+
expect { subject.parse(dmarc) }.to_not raise_error
|
399
|
+
end
|
354
400
|
end
|
355
401
|
end
|
356
402
|
end
|