Repository: StackExchange/dnscontrol
Pull request #986 (2020-12-03 13:37:07)

js_test.go should generate & test zonefile output too
CC @tresni who gave me the idea for the zone testing


Patch:
diff --git a/go.sum b/go.sum
index e368f1a7..b2540f5a 100644
--- a/go.sum
+++ b/go.sum
@@ -472,8 +472,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03i
 golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
 golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201008141435-b3e1573b7520 h1:Bx6FllMpG4NWDOfhMBz1VR2QYNp/SAOHPIAsaVmxfPo=
-golang.org/x/sync v0.0.0-20201008141435-b3e1573b7520/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -571,8 +570,6 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc
 golang.org/x/tools v0.0.0-20200904185747-39188db58858 h1:xLt+iB5ksWcZVxqc+g9K41ZHy+6MKWfXCDsjSThnsPA=
 golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
 golang.org/x/tools v0.0.0-20201013201025-64a9e34f3752/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
-golang.org/x/tools v0.0.0-20201017001424-6003fad69a88 h1:ZB1XYzdDo7c/O48jzjMkvIjnC120Z9/CwgDWhePjQdQ=
-golang.org/x/tools v0.0.0-20201017001424-6003fad69a88/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
 golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b h1:Lq5JUTFhiybGVf28jB6QRpqd13/JPOaCnET17PVzYJE=
 golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/pkg/normalize/validate.go b/pkg/normalize/validate.go
index 468840bb..641cb138 100644
--- a/pkg/normalize/validate.go
+++ b/pkg/normalize/validate.go
@@ -395,7 +395,12 @@ func ValidateAndNormalizeConfig(config *models.DNSConfig) (errs []error) {
 					errs = append(errs, err)
 					continue
 				}
-				err = importTransform(config.FindDomain(rec.GetTargetField()), domain, table, rec.TTL)
+				c := config.FindDomain(rec.GetTargetField())
+				if c == nil {
+					err = fmt.Errorf("IMPORT_TRANSFORM mentions non-existant domain %q", rec.GetTargetField())
+					errs = append(errs, err)
+				}
+				err = importTransform(c, domain, table, rec.TTL)
 				if err != nil {
 					errs = append(errs, err)
 				}
diff --git a/providers/cloudflare/cloudflareProvider.go b/providers/cloudflare/cloudflareProvider.go
index 17907b77..29b574f2 100644
--- a/providers/cloudflare/cloudflareProvider.go
+++ b/providers/cloudflare/cloudflareProvider.go
@@ -439,11 +439,6 @@ func newCloudflare(m map[string]string, metadata json.RawMessage) (providers.DNS
 		return nil, fmt.Errorf("either both cloudflare accountid and accountname must be provided or neither")
 	}
 
-	err := api.fetchDomainList()
-	if err != nil {
-		return nil, err
-	}
-
 	if len(metadata) > 0 {
 		parsedMeta := &struct {
 			IPConversions   string   `json:"ip_conversions"`


Test patch:
diff --git a/pkg/js/js_test.go b/pkg/js/js_test.go
index 52e4b009..d25a7261 100644
--- a/pkg/js/js_test.go
+++ b/pkg/js/js_test.go
@@ -1,7 +1,9 @@
 package js
 
 import (
+	"bytes"
 	"encoding/json"
+	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -9,6 +11,11 @@ import (
 	"unicode"
 
 	testifyrequire "github.com/stretchr/testify/require"
+
+	"github.com/StackExchange/dnscontrol/v3/pkg/normalize"
+	"github.com/StackExchange/dnscontrol/v3/pkg/prettyzone"
+	"github.com/StackExchange/dnscontrol/v3/providers"
+	_ "github.com/StackExchange/dnscontrol/v3/providers/_all"
 )
 
 const (
@@ -26,28 +33,90 @@ func TestParsedFiles(t *testing.T) {
 		t.Fatal(err)
 	}
 	for _, f := range files {
+		name := f.Name()
+
 		// run all js files that start with a number. Skip others.
-		if filepath.Ext(f.Name()) != ".js" || !unicode.IsNumber(rune(f.Name()[0])) {
+		if filepath.Ext(name) != ".js" || !unicode.IsNumber(rune(name[0])) {
 			continue
 		}
-		t.Run(f.Name(), func(t *testing.T) {
-			conf, err := ExecuteJavascript(string(filepath.Join(testDir, f.Name())), true, nil)
+		t.Run(name, func(t *testing.T) {
+			var err error
+
+			// Compile the .js file:
+			conf, err := ExecuteJavascript(string(filepath.Join(testDir, name)), true, nil)
 			if err != nil {
 				t.Fatal(err)
 			}
+
+			// Initialize any DNS providers mentioned.
+			for _, dProv := range conf.DNSProviders {
+				var pcfg = map[string]string{}
+				// Fake out any provider's validation tests.
+				switch dProv.Type {
+				case "CLOUDFLAREAPI":
+					pcfg["apitoken"] = "fake"
+				default:
+				}
+				_, err := providers.CreateDNSProvider(dProv.Type, pcfg, nil)
+				if err != nil {
+					t.Fatal(err)
+				}
+			}
+
+			// Test the JS compiled as expected (compare to the .json file)
 			actualJSON, err := json.MarshalIndent(conf, "", "  ")
 			if err != nil {
 				t.Fatal(err)
 			}
-			expectedFile := filepath.Join(testDir, f.Name()[:len(f.Name())-3]+".json")
+			testName := name[:len(name)-3]
+			expectedFile := filepath.Join(testDir, testName+".json")
 			expectedJSON, err := ioutil.ReadFile(expectedFile)
 			if err != nil {
 				t.Fatal(err)
 			}
 			es := string(expectedJSON)
 			as := string(actualJSON)
-			testifyrequire.JSONEqf(t, es, as, "EXPECTING %s", as)
+			_, _ = es, as
+			testifyrequire.JSONEqf(t, es, as, "EXPECTING %q = \n```\n%s\n```", expectedFile, as)
+
+			// For each domain, if there is a zone file, test against it:
+
+			errs := normalize.ValidateAndNormalizeConfig(conf)
+			if len(errs) != 0 {
+				t.Fatal(errs[0])
+			}
+
+			var dCount int
+			for _, dc := range conf.Domains {
+				zoneFile := filepath.Join(testDir, testName, dc.Name+".zone")
+				expectedZone, err := ioutil.ReadFile(zoneFile)
+				if err != nil {
+					continue
+				}
+				dCount++
+
+				// Generate the zonefile
+				var buf bytes.Buffer
+				err = prettyzone.WriteZoneFileRC(&buf, dc.Records, dc.Name, 300, nil)
+				if err != nil {
+					t.Fatal(err)
+				}
+				actualZone := buf.String()
+
+				es := string(expectedZone)
+				as := actualZone
+				if es != as {
+					// On failure, leave behind the .ACTUAL file.
+					ioutil.WriteFile(zoneFile+".ACTUAL", []byte(actualZone), 0644)
+				}
+				testifyrequire.Equal(t, es, as, "EXPECTING %q =\n```\n%s```", zoneFile, as)
+			}
+			if dCount > 0 && (len(conf.Domains) != dCount) {
+				t.Fatal(fmt.Errorf("only %d of %d domains in %q have zonefiles", dCount, len(conf.Domains), name))
+			}
+
 		})
+
 	}
 }
 
diff --git a/pkg/js/parse_tests/001-basic/foo.com.zone b/pkg/js/parse_tests/001-basic/foo.com.zone
new file mode 100644
index 00000000..7ba2819c
--- /dev/null
+++ b/pkg/js/parse_tests/001-basic/foo.com.zone
@@ -0,0 +1,2 @@
+$TTL 300
+@                IN A     1.2.3.4
diff --git a/pkg/js/parse_tests/002-ttl/foo.com.zone b/pkg/js/parse_tests/002-ttl/foo.com.zone
new file mode 100644
index 00000000..9acbaa7a
--- /dev/null
+++ b/pkg/js/parse_tests/002-ttl/foo.com.zone
@@ -0,0 +1,2 @@
+$TTL 300
+@          42    IN A     1.2.3.4
diff --git a/pkg/js/parse_tests/003-meta/foo.com.zone b/pkg/js/parse_tests/003-meta/foo.com.zone
new file mode 100644
index 00000000..7ba2819c
--- /dev/null
+++ b/pkg/js/parse_tests/003-meta/foo.com.zone
@@ -0,0 +1,2 @@
+$TTL 300
+@                IN A     1.2.3.4
diff --git a/pkg/js/parse_tests/004-ips/foo.com.zone b/pkg/js/parse_tests/004-ips/foo.com.zone
new file mode 100644
index 00000000..0c73c238
--- /dev/null
+++ b/pkg/js/parse_tests/004-ips/foo.com.zone
@@ -0,0 +1,4 @@
+$TTL 300
+@                IN A     1.2.3.4
+p1               IN A     1.2.3.5
+p255             IN A     1.2.4.3
diff --git a/pkg/js/parse_tests/006-transforms/foo.com.zone b/pkg/js/parse_tests/006-transforms/foo.com.zone
new file mode 100644
index 00000000..c684fee4
--- /dev/null
+++ b/pkg/js/parse_tests/006-transforms/foo.com.zone
@@ -0,0 +1,4 @@
+$TTL 300
+@                IN A     3.4.5.6
+                 IN A     4.5.6.7
+                 IN A     5.6.7.8
diff --git a/pkg/js/parse_tests/007-importTransformTTL.js b/pkg/js/parse_tests/007-importTransformTTL.js
index 8f44128f..33e8bec7 100644
--- a/pkg/js/parse_tests/007-importTransformTTL.js
+++ b/pkg/js/parse_tests/007-importTransformTTL.js
@@ -1,4 +1,5 @@
 var TRANSFORM_INT = [
     {low: "0.0.0.0", high: "1.1.1.1", newBase: "2.2.2.2" }
 ]
+D("foo2.com","reg");
 D("foo.com","reg",IMPORT_TRANSFORM(TRANSFORM_INT,"foo2.com",60))
diff --git a/pkg/js/parse_tests/007-importTransformTTL.json b/pkg/js/parse_tests/007-importTransformTTL.json
index 150781e9..6ae0a67f 100644
--- a/pkg/js/parse_tests/007-importTransformTTL.json
+++ b/pkg/js/parse_tests/007-importTransformTTL.json
@@ -1,22 +1,28 @@
 {
-  "registrars": [],
   "dns_providers": [],
   "domains": [
     {
-      "name": "foo.com",
-      "registrar": "reg",
       "dnsProviders": {},
+      "name": "foo2.com",
+      "records": [],
+      "registrar": "reg"
+    },
+    {
+      "dnsProviders": {},
+      "name": "foo.com",
       "records": [
         {
-          "type": "IMPORT_TRANSFORM",
+          "meta": {
+            "transform_table": "0.0.0.0 ~ 1.1.1.1 ~ 2.2.2.2 ~ "
+          },
           "name": "@",
           "target": "foo2.com",
           "ttl": 60,
-          "meta": {
-            "transform_table": "0.0.0.0 ~ 1.1.1.1 ~ 2.2.2.2 ~ "
-          }
+          "type": "IMPORT_TRANSFORM"
         }
-      ]
+      ],
+      "registrar": "reg"
     }
-  ]
+  ],
+  "registrars": []
 }
diff --git a/pkg/js/parse_tests/008-import/foo.com.zone b/pkg/js/parse_tests/008-import/foo.com.zone
new file mode 100644
index 00000000..7ba2819c
--- /dev/null
+++ b/pkg/js/parse_tests/008-import/foo.com.zone
@@ -0,0 +1,2 @@
+$TTL 300
+@                IN A     1.2.3.4
diff --git a/pkg/js/parse_tests/011-cfRedirect/foo.com.zone b/pkg/js/parse_tests/011-cfRedirect/foo.com.zone
new file mode 100644
index 00000000..4cf25444
--- /dev/null
+++ b/pkg/js/parse_tests/011-cfRedirect/foo.com.zone
@@ -0,0 +1,3 @@
+$TTL 300
+;@                IN CF_REDIRECT test.foo.com,https://goo.com/$1
+;@                IN CF_TEMP_REDIRECT test.foo.com,https://goo.com/$1
diff --git a/pkg/js/parse_tests/012-duration.js b/pkg/js/parse_tests/012-duration.js
index b5ac1c74..3d8d0378 100644
--- a/pkg/js/parse_tests/012-duration.js
+++ b/pkg/js/parse_tests/012-duration.js
@@ -1,7 +1,7 @@
 D("foo.com","none",
     A("@","1.2.3.4", TTL("300s")),
-    A("@","1.2.3.4", TTL("300")),
-    A("@","1.2.3.4", TTL("3m")),
-    A("@","1.2.3.4", TTL("3h")),
-    A("@","1.2.3.4", TTL("3d"))
-);
\ No newline at end of file
+    A("@","1.2.3.5", TTL("300")),
+    A("@","1.2.3.6", TTL("3m")),
+    A("@","1.2.3.7", TTL("3h")),
+    A("@","1.2.3.8", TTL("3d"))
+);
diff --git a/pkg/js/parse_tests/012-duration.json b/pkg/js/parse_tests/012-duration.json
index 5fe47fca..b86bf55b 100644
--- a/pkg/js/parse_tests/012-duration.json
+++ b/pkg/js/parse_tests/012-duration.json
@@ -16,25 +16,25 @@
         {
           "type": "A",
           "name": "@",
-          "target": "1.2.3.4",
+          "target": "1.2.3.5",
           "ttl": 300
         },
         {
           "type": "A",
           "name": "@",
-          "target": "1.2.3.4",
+          "target": "1.2.3.6",
           "ttl": 180
         },
         {
           "type": "A",
           "name": "@",
-          "target": "1.2.3.4",
+          "target": "1.2.3.7",
           "ttl": 10800
         },
         {
           "type": "A",
           "name": "@",
-          "target": "1.2.3.4",
+          "target": "1.2.3.8",
           "ttl": 259200
         }
       ]
diff --git a/pkg/js/parse_tests/012-duration/foo.com.zone b/pkg/js/parse_tests/012-duration/foo.com.zone
new file mode 100644
index 00000000..6a596591
--- /dev/null
+++ b/pkg/js/parse_tests/012-duration/foo.com.zone
@@ -0,0 +1,6 @@
+$TTL 300
+@                IN A     1.2.3.4
+                 IN A     1.2.3.5
+           180   IN A     1.2.3.6
+           10800 IN A     1.2.3.7
+           259200 IN A    1.2.3.8
diff --git a/pkg/js/parse_tests/013-mx/foo.com.zone b/pkg/js/parse_tests/013-mx/foo.com.zone
new file mode 100644
index 00000000..92f3bbe4
--- /dev/null
+++ b/pkg/js/parse_tests/013-mx/foo.com.zone
@@ -0,0 +1,2 @@
+$TTL 300
+@                IN MX    15 foo.com.
diff --git a/pkg/js/parse_tests/014-caa/foo.com.zone b/pkg/js/parse_tests/014-caa/foo.com.zone
new file mode 100644
index 00000000..88b19754
--- /dev/null
+++ b/pkg/js/parse_tests/014-caa/foo.com.zone
@@ -0,0 +1,6 @@
+$TTL 300
+@                IN CAA   128 iodef "https://example.com"
+                 IN CAA   128 iodef "mailto:test@example.com"
+                 IN CAA   0 iodef "http://example.com"
+                 IN CAA   0 issue "letsencrypt.org"
+                 IN CAA   0 issuewild ";"
diff --git a/pkg/js/parse_tests/015-tlsa/foo.com.zone b/pkg/js/parse_tests/015-tlsa/foo.com.zone
new file mode 100644
index 00000000..db5cbadc
--- /dev/null
+++ b/pkg/js/parse_tests/015-tlsa/foo.com.zone
@@ -0,0 +1,2 @@
+$TTL 300
+_443._tcp        IN TLSA  3 1 1 MDFiYTQ3MTljODBiNmZlOTExYjA5MWE3YzA1MTI0YjY0ZWVlY2U5NjRlMDljMDU4ZWY4Zjk4MDVkYWNhNTQ2YiAgLQo=
diff --git a/pkg/js/parse_tests/016-backslash/foo.com.zone b/pkg/js/parse_tests/016-backslash/foo.com.zone
new file mode 100644
index 00000000..3271869c
--- /dev/null
+++ b/pkg/js/parse_tests/016-backslash/foo.com.zone
@@ -0,0 +1,2 @@
+$TTL 300
+_dmarc           IN TXT   "v=DMARC1; p=reject; sp=reject; pct=100; rua=mailto:xx...@yyyy.com; ruf=mailto:xx...@yyyy.com; fo=1"
diff --git a/pkg/js/parse_tests/017-txt/foo.com.zone b/pkg/js/parse_tests/017-txt/foo.com.zone
new file mode 100644
index 00000000..9fe37d98
--- /dev/null
+++ b/pkg/js/parse_tests/017-txt/foo.com.zone
@@ -0,0 +1,6 @@
+$TTL 300
+a                IN TXT   "simple"
+b                IN TXT   "ws at end "
+c                IN TXT   "one"
+d                IN TXT   "bonie" "clyde"
+e                IN TXT   "straw" "wood" "brick"
diff --git a/pkg/js/parse_tests/018-dkim/foo.com.zone b/pkg/js/parse_tests/018-dkim/foo.com.zone
new file mode 100644
index 00000000..f8df5c84
--- /dev/null
+++ b/pkg/js/parse_tests/018-dkim/foo.com.zone
@@ -0,0 +1,2 @@
+$TTL 300
+dkimtest2        IN TXT   "this string is 255 bytes long.hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnKZogtjOlHoeY8iZ5o5brlPOsj/a2Q9Bopu1kHxlxrdw7tZVL9FzUMngiIYGrl8dbP7Rvk7TLMoxHxVkRZPBtIpsKIab/gOUoPLQVYbrAmzyguHYBwAApi3H/pvjUsK8+XF0dKY17AR96lokAPqvfBaUb+DSx8zNw2hrYWYVqvCtnxHUGEUhT1bTlEZBptH3j" "this is the remainder. it is 156 bytes long.mOhl2JmbsFKy+RoMTwbkk0/meRvcEFWLHkr4MSgbnie6OpQvM4Y51+kO6DUVr3rwjrdVO9wpFt+n/hdQ92TNif17RMJtE5AGaQ6BN3yJQIDAQAB;"
diff --git a/pkg/js/parse_tests/020-complexRequire/foo.com.zone b/pkg/js/parse_tests/020-complexRequire/foo.com.zone
new file mode 100644
index 00000000..b29f78cc
--- /dev/null
+++ b/pkg/js/parse_tests/020-complexRequire/foo.com.zone
@@ -0,0 +1,6 @@
+$TTL 300
+@                IN A     1.2.3.4
+a                IN CNAME foo.com.
+b                IN CNAME foo.com.
+c                IN CNAME foo.com.
+d                IN CNAME foo.com.
diff --git a/pkg/js/parse_tests/021-srv/foo.com.zone b/pkg/js/parse_tests/021-srv/foo.com.zone
new file mode 100644
index 00000000..2d39d9d2
--- /dev/null
+++ b/pkg/js/parse_tests/021-srv/foo.com.zone
@@ -0,0 +1,6 @@
+$TTL 300
+_ntp._udp        IN SRV   0 0 1 zeros.foo.com.
+                 IN SRV   1 100 123 one.foo.com.
+                 IN SRV   2 100 123 two.foo.com.
+                 IN SRV   3 100 123 localhost.foo.com.
+                 IN SRV   4 100 123 three.example.com.
diff --git a/pkg/js/parse_tests/022-sshfp/foo.com.zone b/pkg/js/parse_tests/022-sshfp/foo.com.zone
new file mode 100644
index 00000000..4286c376
--- /dev/null
+++ b/pkg/js/parse_tests/022-sshfp/foo.com.zone
@@ -0,0 +1,9 @@
+$TTL 300
+@                IN SSHFP 1 1 66C7D5540B7D75A1FB4C84FEBFA178AD99BDD67C
+                 IN SSHFP 1 2 745A635BC46A397A5C4F21D437483005BCC40D7511FF15FBFAFE913A081559BC
+                 IN SSHFP 2 1 66C7D5540B7D75A1FB4C84FEBFA178AD99BDD67C
+                 IN SSHFP 2 2 745A635BC46A397A5C4F21D437483005BCC40D7511FF15FBFAFE913A081559BC
+                 IN SSHFP 3 1 66C7D5540B7D75A1FB4C84FEBFA178AD99BDD67C
+                 IN SSHFP 3 2 745A635BC46A397A5C4F21D437483005BCC40D7511FF15FBFAFE913A081559BC
+                 IN SSHFP 4 1 66C7D5540B7D75A1FB4C84FEBFA178AD99BDD67C
+                 IN SSHFP 4 2 745A635BC46A397A5C4F21D437483005BCC40D7511FF15FBFAFE913A081559BC
diff --git a/pkg/js/parse_tests/023-ignored-glob-records/foo.com.zone b/pkg/js/parse_tests/023-ignored-glob-records/foo.com.zone
new file mode 100644
index 00000000..a3702f18
--- /dev/null
+++ b/pkg/js/parse_tests/023-ignored-glob-records/foo.com.zone
@@ -0,0 +1,1 @@
+$TTL 300
diff --git a/pkg/js/parse_tests/024-json-import/foo.com.zone b/pkg/js/parse_tests/024-json-import/foo.com.zone
new file mode 100644
index 00000000..dbdccd59
--- /dev/null
+++ b/pkg/js/parse_tests/024-json-import/foo.com.zone
@@ -0,0 +1,2 @@
+$TTL 300
+@                IN A     1.1.1.1
diff --git a/pkg/js/parse_tests/027-ds/foo.com.zone b/pkg/js/parse_tests/027-ds/foo.com.zone
new file mode 100644
index 00000000..ffbfbe53
--- /dev/null
+++ b/pkg/js/parse_tests/027-ds/foo.com.zone
@@ -0,0 +1,3 @@
+$TTL 300
+@                IN DS    1 1 1 FFFF
+                 IN DS    1000 13 2 AABBCCDDEEFF
diff --git a/pkg/js/parse_tests/028-dextend/bar.foo.com.zone b/pkg/js/parse_tests/028-dextend/bar.foo.com.zone
new file mode 100644
index 00000000..9b3c8beb
--- /dev/null
+++ b/pkg/js/parse_tests/028-dextend/bar.foo.com.zone
@@ -0,0 +1,3 @@
+$TTL 300
+@                IN A     10.3.3.3
+www              IN A     10.4.4.4
diff --git a/pkg/js/parse_tests/028-dextend/foo.com.zone b/pkg/js/parse_tests/028-dextend/foo.com.zone
new file mode 100644
index 00000000..09ace842
--- /dev/null
+++ b/pkg/js/parse_tests/028-dextend/foo.com.zone
@@ -0,0 +1,3 @@
+$TTL 300
+@                IN A     10.1.1.1
+www              IN A     10.2.2.2
diff --git a/pkg/js/parse_tests/028-dextend/foo.edu.zone b/pkg/js/parse_tests/028-dextend/foo.edu.zone
new file mode 100644
index 00000000..e095ab7e
--- /dev/null
+++ b/pkg/js/parse_tests/028-dextend/foo.edu.zone
@@ -0,0 +1,5 @@
+$TTL 300
+@                IN A     10.5.5.5
+more1            IN A     10.7.7.7
+more2            IN A     10.8.8.8
+www              IN A     10.6.6.6
diff --git a/pkg/js/parse_tests/029-dextendsub.js b/pkg/js/parse_tests/029-dextendsub.js
index f4d05de4..5804d0a9 100644
--- a/pkg/js/parse_tests/029-dextendsub.js
+++ b/pkg/js/parse_tests/029-dextendsub.js
@@ -76,9 +76,3 @@ D_EXTEND("sub.example.tld",
     // Also test for MX, NS, ANAME, SRV.
     // Not sure if PTR needs any special treatment. Haven't thought about it much.
 );
-
-D("foo.com", REG, DnsProvider(CF));
-D_EXTEND("sub.foo.com",
-    CF_REDIRECT("test.foo.com","https://goo.com/$1"),
-    CF_TEMP_REDIRECT("test.foo.com","https://goo.com/$1")
-);
diff --git a/pkg/js/parse_tests/029-dextendsub.json b/pkg/js/parse_tests/029-dextendsub.json
index ee803da3..2814df74 100644
--- a/pkg/js/parse_tests/029-dextendsub.json
+++ b/pkg/js/parse_tests/029-dextendsub.json
@@ -79,15 +79,6 @@
         { "name": "e.sub", "subdomain": "sub", "target": "otherdomain.tld.", "type": "CNAME" }
       ],
       "registrar": "Third-Party"
-    },
-    {
-      "name": "foo.com",
-      "dnsProviders": { "Cloudflare": -1 },
-      "records": [
-        { "name": "@", "target": "test.foo.com,https://goo.com/$1", "type": "CF_REDIRECT" },
-        { "name": "@", "target": "test.foo.com,https://goo.com/$1", "type": "CF_TEMP_REDIRECT" }
-      ],
-      "registrar": "Third-Party"
     }
   ],
   "registrars": [ { "name": "Third-Party", "type": "NONE" } ]
diff --git a/pkg/js/parse_tests/029-dextendsub/bar.foo.help.zone b/pkg/js/parse_tests/029-dextendsub/bar.foo.help.zone
new file mode 100644
index 00000000..5ab8bc94
--- /dev/null
+++ b/pkg/js/parse_tests/029-dextendsub/bar.foo.help.zone
@@ -0,0 +1,5 @@
+$TTL 300
+@                IN A     50.13.13.13
+www              IN A     50.14.14.14
+zip              IN A     50.15.15.15
+www.zip          IN A     50.16.16.16
diff --git a/pkg/js/parse_tests/029-dextendsub/bar.foo.tld.zone b/pkg/js/parse_tests/029-dextendsub/bar.foo.tld.zone
new file mode 100644
index 00000000..f9e0aacc
--- /dev/null
+++ b/pkg/js/parse_tests/029-dextendsub/bar.foo.tld.zone
@@ -0,0 +1,4 @@
+$TTL 300
+@                IN A     30.7.7.7
+a                IN A     30.9.9.9
+www              IN A     30.8.8.8
diff --git a/pkg/js/parse_tests/029-dextendsub/example.tld.zone b/pkg/js/parse_tests/029-dextendsub/example.tld.zone
new file mode 100644
index 00000000..da328701
--- /dev/null
+++ b/pkg/js/parse_tests/029-dextendsub/example.tld.zone
@@ -0,0 +1,5 @@
+$TTL 300
+a.sub            IN CNAME b.sub.example.tld.
+b.sub            IN CNAME sub.example.tld.
+c.sub            IN CNAME sub.example.tld.
+e.sub            IN CNAME otherdomain.tld.
diff --git a/pkg/js/parse_tests/029-dextendsub/foo.help.zone b/pkg/js/parse_tests/029-dextendsub/foo.help.zone
new file mode 100644
index 00000000..914ace11
--- /dev/null
+++ b/pkg/js/parse_tests/029-dextendsub/foo.help.zone
@@ -0,0 +1,5 @@
+$TTL 300
+@                IN A     40.12.12.12
+morty            IN A     40.17.17.17
+www.morty        IN A     40.18.18.18
+www              IN A     40.12.12.12
diff --git a/pkg/js/parse_tests/029-dextendsub/foo.here.zone b/pkg/js/parse_tests/029-dextendsub/foo.here.zone
new file mode 100644
index 00000000..c8f00fa3
--- /dev/null
+++ b/pkg/js/parse_tests/029-dextendsub/foo.here.zone
@@ -0,0 +1,7 @@
+$TTL 300
+@                IN A     60.19.19.19
+bar              IN A     60.21.21.21
+baz.bar          IN A     60.23.23.23
+www.baz.bar      IN A     60.24.24.24
+www.bar          IN A     60.22.22.22
+www              IN A     60.20.20.20
diff --git a/pkg/js/parse_tests/029-dextendsub/foo.net.zone b/pkg/js/parse_tests/029-dextendsub/foo.net.zone
new file mode 100644
index 00000000..c28fea6c
--- /dev/null
+++ b/pkg/js/parse_tests/029-dextendsub/foo.net.zone
@@ -0,0 +1,7 @@
+$TTL 300
+@                IN A     10.1.1.1
+bar              IN A     10.3.3.3
+www.bar          IN A     10.4.4.4
+a.long.path.of.sub.domains IN A 10.25.25.25
+www.a.long.path.of.sub.domains IN A 10.26.26.26
+www              IN A     10.2.2.2
diff --git a/pkg/js/parse_tests/029-dextendsub/foo.tld.zone b/pkg/js/parse_tests/029-dextendsub/foo.tld.zone
new file mode 100644
index 00000000..b50a2c14
--- /dev/null
+++ b/pkg/js/parse_tests/029-dextendsub/foo.tld.zone
@@ -0,0 +1,4 @@
+$TTL 300
+@                IN A     20.5.5.5
+a                IN A     20.10.10.10
+www              IN A     20.6.6.6
diff --git a/pkg/js/parse_tests/030-dextenddoc/domain.tld.zone b/pkg/js/parse_tests/030-dextenddoc/domain.tld.zone
new file mode 100644
index 00000000..f8e73858
--- /dev/null
+++ b/pkg/js/parse_tests/030-dextenddoc/domain.tld.zone
@@ -0,0 +1,13 @@
+$TTL 300
+@                IN A     127.0.0.1
+a                IN CNAME b.domain.tld.
+aaa              IN A     127.0.0.3
+c                IN CNAME d.domain.tld.
+sub              IN A     127.0.0.7
+bbb.sub          IN A     127.0.0.4
+ccc.sub          IN A     127.0.0.5
+e.sub            IN CNAME f.sub.domain.tld.
+i.sub            IN CNAME j.sub.domain.tld.
+ddd.sub.sub      IN A     127.0.0.6
+g.sub.sub        IN CNAME h.sub.sub.domain.tld.
+www              IN A     127.0.0.2
diff --git a/pkg/js/parse_tests/031-dextendnames/domain.tld.zone b/pkg/js/parse_tests/031-dextendnames/domain.tld.zone
new file mode 100644
index 00000000..a9f890ef
--- /dev/null
+++ b/pkg/js/parse_tests/031-dextendnames/domain.tld.zone
@@ -0,0 +1,13 @@
+$TTL 300
+@                IN A     127.0.0.1
+                 IN A     127.0.0.3
+a                IN A     127.0.0.2
+b                IN CNAME c.domain.tld.
+d                IN A     127.0.0.4
+e                IN CNAME f.domain.tld.
+ssub             IN A     127.0.0.7
+j.ssub           IN A     127.0.0.8
+k.ssub           IN CNAME l.ssub.domain.tld.
+ub               IN A     127.0.0.5
+g.ub             IN A     127.0.0.6
+h.ub             IN CNAME i.ub.domain.tld.
diff --git a/pkg/js/parse_tests/031-dextendnames/sub.domain.tld.zone b/pkg/js/parse_tests/031-dextendnames/sub.domain.tld.zone
new file mode 100644
index 00000000..eac9f2de
--- /dev/null
+++ b/pkg/js/parse_tests/031-dextendnames/sub.domain.tld.zone
@@ -0,0 +1,7 @@
+$TTL 300
+@                IN A     127.0.1.1
+                 IN A     127.0.1.3
+aa               IN A     127.0.1.2
+bb               IN CNAME cc.sub.domain.tld.
+dd               IN A     127.0.1.4
+ee               IN CNAME ff.sub.domain.tld.
diff --git a/pkg/js/parse_tests/032-reverseip.js b/pkg/js/parse_tests/032-reverseip.js
index a80b9240..f8f27445 100644
--- a/pkg/js/parse_tests/032-reverseip.js
+++ b/pkg/js/parse_tests/032-reverseip.js
@@ -13,17 +13,3 @@ D_EXTEND(REV("1.2.3.6"), PTR(REV("1.2.3.6"), "billy.example.com."))
 D_EXTEND(REV("1.2.3.0/24"), PTR("7", "my.example.com."))
 D_EXTEND(REV("1.2.3.0/24"), PTR("1.2.3.8", "fair.example.com."))
 D_EXTEND(REV("1.2.3.0/24"), PTR(REV("1.2.3.9/32"), "lady.example.com.", {skip_fqdn_check:"true"}))
-
-// Expected zone: 3.2.1.in-addr.arpa.zone
-// $TTL 300
-//; generated with dnscontrol 2020-11-30T12:56:28-05:00
-//@                IN SOA   DEFAULT_NOT_SET. DEFAULT_NOT_SET. 2020113000 3600 600 604800 1440
-//1                IN PTR   foo.example.com.
-//2                IN PTR   bar.example.com.
-//3                IN PTR   baz.example.com.
-//4                IN PTR   silly.example.com.
-//5                IN PTR   willy.example.com.
-//6                IN PTR   billy.example.com.
-//7                IN PTR   my.example.com.
-//8                IN PTR   fair.example.com.
-//9                IN PTR   lady.example.com.
diff --git a/pkg/js/parse_tests/032-reverseip/3.2.1.in-addr.arpa.zone b/pkg/js/parse_tests/032-reverseip/3.2.1.in-addr.arpa.zone
new file mode 100644
index 00000000..3610bde0
--- /dev/null
+++ b/pkg/js/parse_tests/032-reverseip/3.2.1.in-addr.arpa.zone
@@ -0,0 +1,10 @@
+$TTL 300
+1                IN PTR   foo.example.com.
+2                IN PTR   bar.example.com.
+3                IN PTR   baz.example.com.
+4                IN PTR   silly.example.com.
+5                IN PTR   willy.example.com.
+6                IN PTR   billy.example.com.
+7                IN PTR   my.example.com.
+8                IN PTR   fair.example.com.
+9                IN PTR   lady.example.com.
diff --git a/pkg/js/parse_tests/033-revextend.js b/pkg/js/parse_tests/033-revextend.js
index a1b85892..2bcab1a9 100644
--- a/pkg/js/parse_tests/033-revextend.js
+++ b/pkg/js/parse_tests/033-revextend.js
@@ -6,10 +6,3 @@ D(REV('1.3.0.0/16'), REGISTRAR, DnsProvider(BIND),
 	NS(REV('1.3.1.0/24'), "ns1.example.com.")
 );
 D_EXTEND(REV("1.3.2.0/24"), NS(REV("1.3.2.0/24"), "ns2.example.org."))
-
-// Expeccted zone: 3.1.in-addr.arpa.zone
-// $TTL 300
-// ; generated with dnscontrol 2020-11-30T12:58:47-05:00
-// @                IN SOA   DEFAULT_NOT_SET. DEFAULT_NOT_SET. 2020113000 3600 600 604800 1440
-// 1                IN NS    ns1.example.com.
-// 2                IN NS    ns2.example.org.
diff --git a/pkg/js/parse_tests/033-revextend/3.1.in-addr.arpa.zone b/pkg/js/parse_tests/033-revextend/3.1.in-addr.arpa.zone
new file mode 100644
index 00000000..765252b5
--- /dev/null
+++ b/pkg/js/parse_tests/033-revextend/3.1.in-addr.arpa.zone
@@ -0,0 +1,3 @@
+$TTL 300
+1                IN NS    ns1.example.com.
+2                IN NS    ns2.example.org.
diff --git a/pkg/js/parse_tests/035-naptr/foo.com.zone b/pkg/js/parse_tests/035-naptr/foo.com.zone
new file mode 100644
index 00000000..4b3db8ef
--- /dev/null
+++ b/pkg/js/parse_tests/035-naptr/foo.com.zone
@@ -0,0 +1,3 @@
+$TTL 300
+@                IN NAPTR 100 10 "U" "E2U+sip" "!^.*$!sip:customer-service@example.com!" example.foo.com.
+                 IN NAPTR 102 10 "U" "E2U+email" "!^.*$!mailto:information@example.com!" example.foo.com.
diff --git a/pkg/js/parse_tests/036-dextendcf.js b/pkg/js/parse_tests/036-dextendcf.js
new file mode 100644
index 00000000..f3785f10
--- /dev/null
+++ b/pkg/js/parse_tests/036-dextendcf.js
@@ -0,0 +1,10 @@
+var REG = NewRegistrar("Third-Party", "NONE");
+var CF = NewDnsProvider("Cloudflare", "CLOUDFLAREAPI");
+
+D("foo.com", REG, DnsProvider(CF));
+D_EXTEND("sub.foo.com",
+    A("test1.foo.com","10.2.3.1"),
+    A("test2.foo.com","10.2.3.2"),
+    CF_REDIRECT("test1.foo.com","https://goo.com/$1"),
+    CF_TEMP_REDIRECT("test2.foo.com","https://goo.com/$1")
+);
diff --git a/pkg/js/parse_tests/036-dextendcf.json b/pkg/js/parse_tests/036-dextendcf.json
new file mode 100644
index 00000000..4abf1af5
--- /dev/null
+++ b/pkg/js/parse_tests/036-dextendcf.json
@@ -0,0 +1,47 @@
+{
+  "dns_providers": [
+    {
+      "name": "Cloudflare",
+      "type": "CLOUDFLAREAPI"
+    }
+  ],
+  "domains": [
+    {
+      "dnsProviders": {
+        "Cloudflare": -1
+      },
+      "name": "foo.com",
+      "records": [
+        {
+          "name": "test1.foo.com.sub",
+          "subdomain": "sub",
+          "target": "10.2.3.1",
+          "type": "A"
+        },
+        {
+          "name": "test2.foo.com.sub",
+          "subdomain": "sub",
+          "target": "10.2.3.2",
+          "type": "A"
+        },
+        {
+          "name": "@",
+          "target": "test1.foo.com,https://goo.com/$1",
+          "type": "CF_REDIRECT"
+        },
+        {
+          "name": "@",
+          "target": "test2.foo.com,https://goo.com/$1",
+          "type": "CF_TEMP_REDIRECT"
+        }
+      ],
+      "registrar": "Third-Party"
+    }
+  ],
+  "registrars": [
+    {
+      "name": "Third-Party",
+      "type": "NONE"
+    }
+  ]
+}
diff --git a/pkg/js/parse_tests/036-dextendcf/foo.com.zone b/pkg/js/parse_tests/036-dextendcf/foo.com.zone
new file mode 100644
index 00000000..dbea109b
--- /dev/null
+++ b/pkg/js/parse_tests/036-dextendcf/foo.com.zone
@@ -0,0 +1,5 @@
+$TTL 300
+;@                IN CF_REDIRECT test1.foo.com,https://goo.com/$1
+;@                IN CF_TEMP_REDIRECT test2.foo.com,https://goo.com/$1
+test1.foo.com.sub IN A    10.2.3.1
+test2.foo.com.sub IN A    10.2.3.2