@@ -3,6 +3,8 @@ package joker
33import (
44 "reflect"
55 "testing"
6+
7+ "github.com/StackExchange/dnscontrol/v4/models"
68)
79
810func TestParseZoneLine (t * testing.T ) {
@@ -63,13 +65,13 @@ func TestParseZoneLine(t *testing.T) {
6365 },
6466 {
6567 name : "CAA record" ,
66- input : "@ CAA 0 0 issue \" letsencrypt.org\" 300 " ,
67- expected : []string {"@" , "CAA" , "0" , "0" , "issue" , "\" letsencrypt.org\" " , "300 " },
68+ input : "@ CAA 0 0 issue \" letsencrypt.org\" 303 " ,
69+ expected : []string {"@" , "CAA" , "0" , "0" , "issue" , "\" letsencrypt.org\" " , "303 " },
6870 },
6971 {
7072 name : "NAPTR record" ,
71- input : "@ NAPTR 100/50 target.example.com. \" u\" \" sip+E2U\" \" !^.*$!sip:info@example.com!\" 300 " ,
72- expected : []string {"@" , "NAPTR" , "100/50" , "target.example.com." , "\" u\" " , "\" sip+E2U\" " , "\" !^.*$!sip:info@example.com!\" " , "300 " },
73+ input : "@ NAPTR 100/50 target.example.com. \" u\" \" sip+E2U\" \" !^.*$!sip:info@example.com!\" 313 " ,
74+ expected : []string {"@" , "NAPTR" , "100/50" , "target.example.com." , "\" u\" " , "\" sip+E2U\" " , "\" !^.*$!sip:info@example.com!\" " , "313 " },
7375 },
7476 {
7577 name : "multiple consecutive spaces" ,
@@ -193,3 +195,323 @@ func TestParseZoneLineEdgeCases(t *testing.T) {
193195 })
194196 }
195197}
198+ func TestParseZoneRecords (t * testing.T ) {
199+ api := & jokerProvider {}
200+ domain := "example.com"
201+
202+ tests := []struct {
203+ name string
204+ zoneData string
205+ wantCount int
206+ validate func (t * testing.T , records models.Records )
207+ }{
208+ {
209+ name : "single A record" ,
210+ zoneData : `www A 0 1.2.3.4 300` ,
211+ wantCount : 1 ,
212+ validate : func (t * testing.T , records models.Records ) {
213+ if records [0 ].Type != "A" {
214+ t .Errorf ("Type = %v, want A" , records [0 ].Type )
215+ }
216+ if records [0 ].GetLabelFQDN () != "www.example.com" {
217+ t .Errorf ("Label = %v, want www.example.com" , records [0 ].GetLabelFQDN ())
218+ }
219+ if records [0 ].GetTargetField () != "1.2.3.4" {
220+ t .Errorf ("Target = %v, want 1.2.3.4" , records [0 ].GetTargetField ())
221+ }
222+ if records [0 ].TTL != 300 {
223+ t .Errorf ("TTL = %v, want 300" , records [0 ].TTL )
224+ }
225+ },
226+ },
227+ {
228+ name : "root domain A record with @" ,
229+ zoneData : `@ A 0 127.0.0.1 300` ,
230+ wantCount : 1 ,
231+ validate : func (t * testing.T , records models.Records ) {
232+ if records [0 ].GetLabelFQDN () != "example.com" {
233+ t .Errorf ("Label = %v, want example.com" , records [0 ].GetLabelFQDN ())
234+ }
235+ if records [0 ].GetTargetField () != "127.0.0.1" {
236+ t .Errorf ("Target = %v, want 127.0.0.1" , records [0 ].GetTargetField ())
237+ }
238+ },
239+ },
240+ {
241+ name : "AAAA record" ,
242+ zoneData : `www AAAA 0 2001:db8::1 300` ,
243+ wantCount : 1 ,
244+ validate : func (t * testing.T , records models.Records ) {
245+ if records [0 ].Type != "AAAA" {
246+ t .Errorf ("Type = %v, want AAAA" , records [0 ].Type )
247+ }
248+ if records [0 ].GetTargetField () != "2001:db8::1" {
249+ t .Errorf ("Target = %v, want 2001:db8::1" , records [0 ].GetTargetField ())
250+ }
251+ },
252+ },
253+ {
254+ name : "CNAME record with FQDN" ,
255+ zoneData : `mail CNAME 0 example.com. 300` ,
256+ wantCount : 1 ,
257+ validate : func (t * testing.T , records models.Records ) {
258+ if records [0 ].Type != "CNAME" {
259+ t .Errorf ("Type = %v, want CNAME" , records [0 ].Type )
260+ }
261+ if records [0 ].GetTargetField () != "example.com." {
262+ t .Errorf ("Target = %v, want example.com." , records [0 ].GetTargetField ())
263+ }
264+ },
265+ },
266+ {
267+ name : "CNAME record without trailing dot" ,
268+ zoneData : `mail CNAME 0 target.example.com 300` ,
269+ wantCount : 1 ,
270+ validate : func (t * testing.T , records models.Records ) {
271+ if records [0 ].GetTargetField () != "target.example.com." {
272+ t .Errorf ("Target = %v, want target.example.com." , records [0 ].GetTargetField ())
273+ }
274+ },
275+ },
276+ {
277+ name : "NS record" ,
278+ zoneData : `sub NS 0 ns1.example.com. 300` ,
279+ wantCount : 1 ,
280+ validate : func (t * testing.T , records models.Records ) {
281+ if records [0 ].Type != "NS" {
282+ t .Errorf ("Type = %v, want NS" , records [0 ].Type )
283+ }
284+ if records [0 ].GetTargetField () != "ns1.example.com." {
285+ t .Errorf ("Target = %v, want ns1.example.com." , records [0 ].GetTargetField ())
286+ }
287+ },
288+ },
289+ {
290+ name : "MX record" ,
291+ zoneData : `@ MX 10 mail.example.com. 300` ,
292+ wantCount : 1 ,
293+ validate : func (t * testing.T , records models.Records ) {
294+ if records [0 ].Type != "MX" {
295+ t .Errorf ("Type = %v, want MX" , records [0 ].Type )
296+ }
297+ if records [0 ].MxPreference != 10 {
298+ t .Errorf ("MxPreference = %v, want 10" , records [0 ].MxPreference )
299+ }
300+ if records [0 ].GetTargetField () != "mail.example.com." {
301+ t .Errorf ("Target = %v, want mail.example.com." , records [0 ].GetTargetField ())
302+ }
303+ },
304+ },
305+ {
306+ name : "TXT record with quoted content" ,
307+ zoneData : `@ TXT 0 "v=spf1 include:_spf.google.com ~all" 301` ,
308+ wantCount : 1 ,
309+ validate : func (t * testing.T , records models.Records ) {
310+ if records [0 ].Type != "TXT" {
311+ t .Errorf ("Type = %v, want TXT" , records [0 ].Type )
312+ }
313+ want := "v=spf1 include:_spf.google.com ~all"
314+ if records [0 ].GetTargetField () != want {
315+ t .Errorf ("Target = %v, want %v" , records [0 ].GetTargetField (), want )
316+ }
317+ wantTTL := uint32 (301 )
318+ if records [0 ].TTL != wantTTL {
319+ t .Errorf ("TTL = %v, want %d" , records [0 ].TTL , wantTTL )
320+ }
321+ },
322+ },
323+ {
324+ name : "TXT record with escaped quotes" ,
325+ zoneData : `test TXT 0 "value with \\\"escaped\\\" quotes" 399` ,
326+ wantCount : 1 ,
327+ validate : func (t * testing.T , records models.Records ) {
328+ want := "value with \\ \" escaped\\ \" quotes"
329+ if records [0 ].GetTargetField () != want {
330+ t .Errorf ("Target = %v, want %v" , records [0 ].GetTargetField (), want )
331+ }
332+ },
333+ },
334+ {
335+ name : "TXT record with escaped backslashes" ,
336+ zoneData : `test TXT 0 "path\\\\to\\\\file" 300` ,
337+ wantCount : 1 ,
338+ validate : func (t * testing.T , records models.Records ) {
339+ want := "path\\ \\ to\\ \\ file"
340+ if records [0 ].GetTargetField () != want {
341+ t .Errorf ("Target = %v, want %v" , records [0 ].GetTargetField (), want )
342+ }
343+ },
344+ },
345+ {
346+ name : "SRV record" ,
347+ zoneData : `_sip._tcp SRV 10/20 sip.example.com:5060 300` ,
348+ wantCount : 1 ,
349+ validate : func (t * testing.T , records models.Records ) {
350+ if records [0 ].Type != "SRV" {
351+ t .Errorf ("Type = %v, want SRV" , records [0 ].Type )
352+ }
353+ if records [0 ].SrvPriority != 10 {
354+ t .Errorf ("SrvPriority = %v, want 10" , records [0 ].SrvPriority )
355+ }
356+ if records [0 ].SrvWeight != 20 {
357+ t .Errorf ("SrvWeight = %v, want 20" , records [0 ].SrvWeight )
358+ }
359+ if records [0 ].SrvPort != 5060 {
360+ t .Errorf ("SrvPort = %v, want 5060" , records [0 ].SrvPort )
361+ }
362+ if records [0 ].GetTargetField () != "sip.example.com." {
363+ t .Errorf ("Target = %v, want sip.example.com." , records [0 ].GetTargetField ())
364+ }
365+ },
366+ },
367+ {
368+ name : "CAA record" ,
369+ zoneData : `@ CAA 0 issue "letsencrypt.org" 303` ,
370+ wantCount : 1 ,
371+ validate : func (t * testing.T , records models.Records ) {
372+ if records [0 ].Type != "CAA" {
373+ t .Errorf ("Type = %v, want CAA" , records [0 ].Type )
374+ }
375+ if records [0 ].CaaFlag != 0 {
376+ t .Errorf ("CaaFlag = %v, want 0" , records [0 ].CaaFlag )
377+ }
378+ if records [0 ].CaaTag != "issue" {
379+ t .Errorf ("CaaTag = %v, want issue" , records [0 ].CaaTag )
380+ }
381+ if records [0 ].GetTargetField () != "letsencrypt.org" {
382+ t .Errorf ("Target = %v, want letsencrypt.org" , records [0 ].GetTargetField ())
383+ }
384+ if records [0 ].TTL != 303 {
385+ t .Errorf ("TTL = %v, want 303" , records [0 ].TTL )
386+ }
387+ },
388+ },
389+ {
390+ name : "NAPTR record" ,
391+ zoneData : `@ NAPTR 100/50 target.example.com. 315 0 0 "u" "sip+E2U" "!^.*$!sip:info@example.com!"` ,
392+ wantCount : 1 ,
393+ validate : func (t * testing.T , records models.Records ) {
394+ if records [0 ].Type != "NAPTR" {
395+ t .Errorf ("Type = %v, want NAPTR" , records [0 ].Type )
396+ }
397+ if records [0 ].NaptrOrder != 100 {
398+ t .Errorf ("NaptrOrder = %v, want 100" , records [0 ].NaptrOrder )
399+ }
400+ if records [0 ].NaptrPreference != 50 {
401+ t .Errorf ("NaptrPreference = %v, want 50" , records [0 ].NaptrPreference )
402+ }
403+ if records [0 ].NaptrFlags != "u" {
404+ t .Errorf ("NaptrFlags = %v, want u" , records [0 ].NaptrFlags )
405+ }
406+ if records [0 ].NaptrService != "sip+E2U" {
407+ t .Errorf ("NaptrService = %v, want sip+E2U" , records [0 ].NaptrService )
408+ }
409+ if records [0 ].NaptrRegexp != "!^.*$!sip:info@example.com!" {
410+ t .Errorf ("NaptrRegexp = %v, want !^.*$!sip:info@example.com!" , records [0 ].NaptrRegexp )
411+ }
412+ if records [0 ].GetTargetField () != "target.example.com." {
413+ t .Errorf ("Target = %v, want target.example.com." , records [0 ].GetTargetField ())
414+ }
415+ if records [0 ].TTL != 315 {
416+ t .Errorf ("TTL = %v, want 315" , records [0 ].TTL )
417+ }
418+ },
419+ },
420+ {
421+ name : "multiple records" ,
422+ zoneData : `www A 0 1.2.3.4 300
423+ mail CNAME 0 example.com. 300
424+ @ MX 10 mail.example.com. 300` ,
425+ wantCount : 3 ,
426+ validate : func (t * testing.T , records models.Records ) {
427+ if records [0 ].Type != "A" {
428+ t .Errorf ("First record type = %v, want A" , records [0 ].Type )
429+ }
430+ if records [1 ].Type != "CNAME" {
431+ t .Errorf ("Second record type = %v, want CNAME" , records [1 ].Type )
432+ }
433+ if records [2 ].Type != "MX" {
434+ t .Errorf ("Third record type = %v, want MX" , records [2 ].Type )
435+ }
436+ },
437+ },
438+ {
439+ name : "skip comments and directives" ,
440+ zoneData : `# This is a comment
441+ $ORIGIN example.com.
442+ www A 0 1.2.3.4 300` ,
443+ wantCount : 1 ,
444+ validate : func (t * testing.T , records models.Records ) {
445+ if records [0 ].Type != "A" {
446+ t .Errorf ("Type = %v, want A" , records [0 ].Type )
447+ }
448+ },
449+ },
450+ {
451+ name : "skip empty lines" ,
452+ zoneData : `www A 0 1.2.3.4 300
453+
454+ mail CNAME 0 example.com. 300` ,
455+ wantCount : 2 ,
456+ validate : func (t * testing.T , records models.Records ) {
457+ if len (records ) != 2 {
458+ t .Errorf ("Record count = %v, want 2" , len (records ))
459+ }
460+ },
461+ },
462+ {
463+ name : "skip malformed lines" ,
464+ zoneData : `www A 0
465+ valid A 0 1.2.3.4 300` ,
466+ wantCount : 1 ,
467+ validate : func (t * testing.T , records models.Records ) {
468+ if records [0 ].GetLabelFQDN () != "valid.example.com" {
469+ t .Errorf ("Label = %v, want valid.example.com" , records [0 ].GetLabelFQDN ())
470+ }
471+ },
472+ },
473+ {
474+ name : "default TTL when missing" ,
475+ zoneData : `www A 0 1.2.3.4` ,
476+ wantCount : 1 ,
477+ validate : func (t * testing.T , records models.Records ) {
478+ if records [0 ].TTL != 300 {
479+ t .Errorf ("TTL = %v, want 300" , records [0 ].TTL )
480+ }
481+ },
482+ },
483+ {
484+ name : "skip unsupported record types" ,
485+ zoneData : `www A 0 1.2.3.4 300
486+ test UNKNOWN 0 value 300
487+ mail CNAME 0 example.com. 300` ,
488+ wantCount : 2 ,
489+ validate : func (t * testing.T , records models.Records ) {
490+ if len (records ) != 2 {
491+ t .Errorf ("Record count = %v, want 2" , len (records ))
492+ }
493+ },
494+ },
495+ {
496+ name : "empty zone data" ,
497+ zoneData : "" ,
498+ wantCount : 0 ,
499+ validate : func (t * testing.T , records models.Records ) {},
500+ },
501+ }
502+
503+ for _ , tt := range tests {
504+ t .Run (tt .name , func (t * testing.T ) {
505+ records , err := api .parseZoneRecords (domain , tt .zoneData )
506+ if err != nil {
507+ t .Fatalf ("parseZoneRecords() error = %v" , err )
508+ }
509+ if len (records ) != tt .wantCount {
510+ t .Errorf ("parseZoneRecords() returned %d records, want %d" , len (records ), tt .wantCount )
511+ }
512+ if tt .validate != nil {
513+ tt .validate (t , records )
514+ }
515+ })
516+ }
517+ }
0 commit comments