소스 검색

Switch to json crate, and test JSON output

Switching to the 'json' crate instead of 'serde_json' makes dog compile in debug mode 25% faster. It also has the ability to keep the source order of object keys, rather than alphabetising them, which I like.

Tests have been added that run dog with --json and pipe the result though jq to set the output schema in stone.
Benjamin Sago 4 년 전
부모
커밋
f94f48f5ba
59개의 변경된 파일1392개의 추가작업 그리고 64개의 파일을 삭제
  1. 1 2
      Cargo.lock
  2. 2 3
      Cargo.toml
  3. 55 55
      src/output.rs
  4. 2 1
      xtests/README.md
  5. 11 0
      xtests/madns/a-records.toml
  6. 11 0
      xtests/madns/aaaa-records.toml
  7. 27 0
      xtests/madns/caa-records.toml
  8. 11 0
      xtests/madns/cname-records.toml
  9. 11 0
      xtests/madns/eui48-records.toml
  10. 11 0
      xtests/madns/eui64-records.toml
  11. 11 0
      xtests/madns/hinfo-records.toml
  12. 49 3
      xtests/madns/loc-records.toml
  13. 11 0
      xtests/madns/mx-records.toml
  14. 11 0
      xtests/madns/naptr-records.toml
  15. 11 0
      xtests/madns/ns-records.toml
  16. 11 0
      xtests/madns/openpgpkey-records.toml
  17. 35 0
      xtests/madns/opt-records.toml
  18. 26 0
      xtests/madns/outputs/a.example.json
  19. 26 0
      xtests/madns/outputs/aaaa.example.json
  20. 26 0
      xtests/madns/outputs/ansi.str.example.json
  21. 28 0
      xtests/madns/outputs/caa.example.json
  22. 26 0
      xtests/madns/outputs/cname.example.json
  23. 28 0
      xtests/madns/outputs/critical.caa.example.json
  24. 35 0
      xtests/madns/outputs/do-flag.opt.example.json
  25. 26 0
      xtests/madns/outputs/eui48.example.json
  26. 26 0
      xtests/madns/outputs/eui64.example.json
  27. 35 0
      xtests/madns/outputs/far-negative-latitude.loc.invalid.json
  28. 35 0
      xtests/madns/outputs/far-negative-longitude.loc.invalid.json
  29. 35 0
      xtests/madns/outputs/far-positive-latitude.loc.invalid.json
  30. 35 0
      xtests/madns/outputs/far-positive-longitude.loc.invalid.json
  31. 27 0
      xtests/madns/outputs/hinfo.example.json
  32. 35 0
      xtests/madns/outputs/loc.example.json
  33. 27 0
      xtests/madns/outputs/mx.example.json
  34. 35 0
      xtests/madns/outputs/named.opt.invalid.json
  35. 30 0
      xtests/madns/outputs/naptr.example.json
  36. 26 0
      xtests/madns/outputs/newline.str.example.json
  37. 26 0
      xtests/madns/outputs/ns.example.json
  38. 26 0
      xtests/madns/outputs/null.str.example.json
  39. 26 0
      xtests/madns/outputs/openpgpkey.example.json
  40. 35 0
      xtests/madns/outputs/opt.example.json
  41. 35 0
      xtests/madns/outputs/other-flags.opt.example.json
  42. 28 0
      xtests/madns/outputs/others.caa.example.json
  43. 26 0
      xtests/madns/outputs/ptr.example.json
  44. 26 0
      xtests/madns/outputs/soa.example.json
  45. 29 0
      xtests/madns/outputs/srv.example.json
  46. 28 0
      xtests/madns/outputs/sshfp.example.json
  47. 26 0
      xtests/madns/outputs/tab.str.example.json
  48. 29 0
      xtests/madns/outputs/tlsa.example.json
  49. 28 0
      xtests/madns/outputs/txt.example.json
  50. 26 0
      xtests/madns/outputs/upperbit.str.example.json
  51. 28 0
      xtests/madns/outputs/uri.example.json
  52. 45 0
      xtests/madns/protocol-chars.toml
  53. 11 0
      xtests/madns/ptr-records.toml
  54. 11 0
      xtests/madns/soa-records.toml
  55. 11 0
      xtests/madns/srv-records.toml
  56. 11 0
      xtests/madns/sshfp-records.toml
  57. 11 0
      xtests/madns/tlsa-records.toml
  58. 11 0
      xtests/madns/txt-records.toml
  59. 11 0
      xtests/madns/uri-records.toml

+ 1 - 2
Cargo.lock

@@ -161,11 +161,10 @@ dependencies = [
  "dns-transport",
  "getopts",
  "ipconfig",
+ "json",
  "log",
  "pretty_assertions",
  "rand",
- "serde",
- "serde_json",
 ]
 
 [[package]]

+ 2 - 3
Cargo.toml

@@ -52,9 +52,8 @@ getopts = "0.2"
 # transaction ID generation
 rand = "0.8"
 
-# json
-serde = "1.0"
-serde_json = "1.0"
+# json output
+json = "0.12"
 
 # logging
 log = "0.4"

+ 55 - 55
src/output.rs

@@ -5,7 +5,7 @@ use std::time::Duration;
 use dns::{Response, Query, Answer, QClass, ErrorCode, WireError, MandatedLength};
 use dns::record::{Record, RecordType, UnknownQtype, OPT};
 use dns_transport::Error as TransportError;
-use serde_json::{json, Value as JsonValue};
+use json::{object, JsonValue};
 
 use crate::colours::Colours;
 use crate::table::{Table, Section};
@@ -102,31 +102,31 @@ impl OutputFormat {
                 let mut rs = Vec::new();
 
                 for response in responses {
-                    let json = json!({
+                    let json = object! {
                         "queries": json_queries(response.queries),
                         "answers": json_answers(response.answers),
                         "authorities": json_answers(response.authorities),
                         "additionals": json_answers(response.additionals),
-                    });
+                    };
 
                     rs.push(json);
                 }
 
                 if let Some(duration) = duration {
-                    let object = json!({
+                    let object = object! {
                         "responses": rs,
                         "duration": {
                             "secs": duration.as_secs(),
                             "millis": duration.subsec_millis(),
                         },
-                    });
+                    };
 
                     println!("{}", object);
                 }
                 else {
-                    let object = json!({
+                    let object = object! {
                         "responses": rs,
-                    });
+                    };
 
                     println!("{}", object);
                 }
@@ -168,11 +168,11 @@ impl OutputFormat {
             }
 
             Self::JSON => {
-                let object = json!({
+                let object = object! {
                     "error": true,
                     "error_phase": erroneous_phase(&error),
                     "error_message": error_message(error),
-                });
+                };
 
                 eprintln!("{}", object);
             }
@@ -339,11 +339,11 @@ fn format_duration_hms(seconds: u32) -> String {
 /// Serialises multiple DNS queries as a JSON value.
 fn json_queries(queries: Vec<Query>) -> JsonValue {
     let queries = queries.iter().map(|q| {
-        json!({
+        object! {
             "name": q.qname.to_string(),
             "class": json_class(q.qclass),
             "type": json_record_type_name(q.qtype),
-        })
+        }
     }).collect::<Vec<_>>();
 
     queries.into()
@@ -354,23 +354,23 @@ fn json_answers(answers: Vec<Answer>) -> JsonValue {
     let answers = answers.into_iter().map(|a| {
         match a {
             Answer::Standard { qname, qclass, ttl, record } => {
-                json!({
+                object! {
                     "name": qname.to_string(),
                     "class": json_class(qclass),
                     "ttl": ttl,
                     "type": json_record_name(&record),
                     "data": json_record_data(record),
-                })
+                }
             }
             Answer::Pseudo { qname, opt } => {
-                json!({
+                object! {
                     "name": qname.to_string(),
                     "type": "OPT",
                     "data": {
                         "version": opt.edns0_version,
                         "data": opt.data,
                     },
-                })
+                }
             }
         }
     }).collect::<Vec<_>>();
@@ -456,45 +456,45 @@ fn json_record_name(record: &Record) -> JsonValue {
 fn json_record_data(record: Record) -> JsonValue {
     match record {
         Record::A(a) => {
-            json!({
+            object! {
                 "address": a.address.to_string(),
-            })
+            }
         }
         Record::AAAA(aaaa) => {
-            json!({
+            object! {
                 "address": aaaa.address.to_string(),
-            })
+            }
         }
         Record::CAA(caa) => {
-            json!({
+            object! {
                 "critical": caa.critical,
                 "tag": caa.tag,
                 "value": caa.value,
-            })
+            }
         }
         Record::CNAME(cname) => {
-            json!({
+            object! {
                 "domain": cname.domain.to_string(),
-            })
+            }
         }
         Record::EUI48(eui48) => {
-            json!({
+            object! {
                 "identifier": eui48.formatted_address(),
-            })
+            }
         }
         Record::EUI64(eui64) => {
-            json!({
+            object! {
                 "identifier": eui64.formatted_address(),
-            })
+            }
         }
         Record::HINFO(hinfo) => {
-            json!({
+            object! {
                 "cpu": hinfo.cpu,
                 "os": hinfo.os,
-            })
+            }
         }
         Record::LOC(loc) => {
-            json!({
+            object! {
                 "size": loc.size.to_string(),
                 "precision": {
                     "horizontal": loc.horizontal_precision,
@@ -505,82 +505,82 @@ fn json_record_data(record: Record) -> JsonValue {
                     "longitude": loc.longitude.map(|e| e.to_string()),
                     "altitude": loc.altitude.to_string(),
                 },
-            })
+            }
         }
         Record::MX(mx) => {
-            json!({
+            object! {
                 "preference": mx.preference,
                 "exchange": mx.exchange.to_string(),
-            })
+            }
         }
         Record::NAPTR(naptr) => {
-            json!({
+            object! {
                 "order": naptr.order,
                 "flags": naptr.flags,
                 "service": naptr.service,
                 "regex": naptr.regex,
                 "replacement": naptr.replacement.to_string(),
-            })
+            }
         }
         Record::NS(ns) => {
-            json!({
+            object! {
                 "nameserver": ns.nameserver.to_string(),
-            })
+            }
         }
         Record::OPENPGPKEY(opgp) => {
-            json!({
+            object! {
                 "key": opgp.base64_key(),
-            })
+            }
         }
         Record::PTR(ptr) => {
-            json!({
+            object! {
                 "cname": ptr.cname.to_string(),
-            })
+            }
         }
         Record::SSHFP(sshfp) => {
-            json!({
+            object! {
                 "algorithm": sshfp.algorithm,
                 "fingerprint_type": sshfp.fingerprint_type,
                 "fingerprint": sshfp.hex_fingerprint(),
-            })
+            }
         }
         Record::SOA(soa) => {
-            json!({
+            object! {
                 "mname": soa.mname.to_string(),
-            })
+            }
         }
         Record::SRV(srv) => {
-            json!({
+            object! {
                 "priority": srv.priority,
                 "weight": srv.weight,
                 "port": srv.port,
                 "target": srv.target.to_string(),
-            })
+            }
         }
         Record::TLSA(tlsa) => {
-            json!({
+            object! {
                 "certificate_usage": tlsa.certificate_usage,
                 "selector": tlsa.selector,
                 "matching_type": tlsa.matching_type,
                 "certificate_data": tlsa.hex_certificate_data(),
-            })
+            }
         }
         Record::TXT(txt) => {
-            json!({
+            object! {
                 "messages": txt.messages,
-            })
+            }
         }
         Record::URI(uri) => {
-            json!({
+            object! {
                 "priority": uri.priority,
                 "weight": uri.weight,
                 "target": uri.target,
-            })
+            }
         }
         Record::Other { bytes, .. } => {
-            json!({
+            object! {
                 "bytes": bytes,
-            })
+            }
         }
     }
 }

+ 2 - 1
xtests/README.md

@@ -2,7 +2,7 @@
 
 This is dog’s extended test suite. The checks herein form a complete end-to-end set of tests, covering things like network connections, DNS protocol parsing, command-line options, error handling, and edge case behaviour.
 
-The checks are written as [Specsheet] documents, which you’ll need to have installed.
+The checks are written as [Specsheet] documents, which you’ll need to have installed. For the JSON tests, you’ll also need [jq].
 
 Because these tests make connections over the network, the outcome of the test suite will depend on your own machine‘s Internet connection! It also means that your own IP address will be recorded as making the requests.
 
@@ -30,5 +30,6 @@ To run a subset of the checks, you can filter with the following tags:
 You can also use a DNS record type as a tag to only run the checks for that particular type.
 
 [Specsheet]: https://specsheet.software/
+[jq]: https://stedolan.github.io/jq/
 [public Cloudflare DNS resolver]: https://developers.cloudflare.com/1.1.1.1/
 [madns resolver]: https://madns.binarystar.systems/

+ 11 - 0
xtests/madns/a-records.toml

@@ -9,6 +9,17 @@ status = 0
 tags = [ "a", "madns" ]
 
 
+# A record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘a.example’ prints the correct A record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 A a.example --json | jq"
+stdout = { file = "outputs/a.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "a", "madns", "json" ]
+
+
 # A record invalid packets
 
 [[cmd]]

+ 11 - 0
xtests/madns/aaaa-records.toml

@@ -9,6 +9,17 @@ status = 0
 tags = [ "aaaa", "madns" ]
 
 
+# AAAA record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘aaaa.example’ prints the correct AAAA record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 AAAA aaaa.example --json | jq"
+stdout = { file = "outputs/aaaa.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "aaaa", "madns", "json" ]
+
+
 # AAAA record invalid packets
 
 [[cmd]]

+ 27 - 0
xtests/madns/caa-records.toml

@@ -25,6 +25,33 @@ status = 0
 tags = [ "caa", "madns" ]
 
 
+# CAA record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘caa.example’ prints the correct CAA record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 CAA caa.example --json | jq"
+stdout = { file = "outputs/caa.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "caa", "madns", "json" ]
+
+[[cmd]]
+name = "Running with ‘critical.caa.example’ prints the correct CAA record structurewith the flag"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 CAA critical.caa.example --json | jq"
+stdout = { file = "outputs/critical.caa.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "caa", "madns", "json" ]
+
+[[cmd]]
+name = "Running with ‘others.caa.example’ prints the correct CAA record structure and ignores the flags"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 CAA others.caa.example --json | jq"
+stdout = { file = "outputs/others.caa.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "caa", "madns", "json" ]
+
+
 # CAA record invalid packets
 
 [[cmd]]

+ 11 - 0
xtests/madns/cname-records.toml

@@ -9,6 +9,17 @@ status = 0
 tags = [ "cname", "madns" ]
 
 
+# CNAME record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘cname.example’ prints the correct CNAME record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 CNAME cname.example --json | jq"
+stdout = { file = "outputs/cname.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "cname", "madns", "json" ]
+
+
 # CNAME record invalid packets
 
 [[cmd]]

+ 11 - 0
xtests/madns/eui48-records.toml

@@ -9,6 +9,17 @@ status = 0
 tags = [ "eui48", "madns" ]
 
 
+# EUI48 record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘eui48.example’ prints the correct EUI48 record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 EUI48 eui48.example --json | jq"
+stdout = { file = "outputs/eui48.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "eui48", "madns", "json" ]
+
+
 # EUI48 record invalid packets
 
 [[cmd]]

+ 11 - 0
xtests/madns/eui64-records.toml

@@ -9,6 +9,17 @@ status = 0
 tags = [ "eui64", "madns" ]
 
 
+# EUI64 record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘eui64.example’ prints the correct EUI64 record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 EUI64 eui64.example --json | jq"
+stdout = { file = "outputs/eui64.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "eui64", "madns", "json" ]
+
+
 # EUI64 record invalid packets
 
 [[cmd]]

+ 11 - 0
xtests/madns/hinfo-records.toml

@@ -9,6 +9,17 @@ status = 0
 tags = [ "hinfo", "madns" ]
 
 
+# HINFO record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘hinfo.example’ prints the correct HINFO record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 HINFO hinfo.example --json | jq"
+stdout = { file = "outputs/hinfo.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "hinfo", "madns", "json" ]
+
+
 # HINFO record invalid packets
 
 [[cmd]]

+ 49 - 3
xtests/madns/loc-records.toml

@@ -9,6 +9,17 @@ status = 0
 tags = [ "loc", "madns" ]
 
 
+# LOC record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘loc.example’ prints the correct LOC record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 LOC loc.example --json | jq"
+stdout = { file = "outputs/loc.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "loc", "madns", "json" ]
+
+
 # LOC record out-of-range positions
 
 [[cmd]]
@@ -20,7 +31,7 @@ status = 0
 tags = [ "loc", "madns" ]
 
 [[cmd]]
-name = "Running with ‘far-positive-longitude.loc.invalid’ displays a record length error"
+name = "Running with ‘far-positive-longitude.loc.invalid’ displays a record with an out-of-range field"
 shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 LOC far-positive-longitude.loc.invalid"
 stdout = { file = "outputs/far-positive-longitude.loc.invalid.ansitxt" }
 stderr = { empty = true }
@@ -28,7 +39,7 @@ status = 0
 tags = [ "loc", "madns" ]
 
 [[cmd]]
-name = "Running with ‘far-negative-latitude.loc.invalid’ displays a record length error"
+name = "Running with ‘far-negative-latitude.loc.invalid’ displays a record with an out-of-range field"
 shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 LOC far-negative-latitude.loc.invalid"
 stdout = { file = "outputs/far-negative-latitude.loc.invalid.ansitxt" }
 stderr = { empty = true }
@@ -36,7 +47,7 @@ status = 0
 tags = [ "loc", "madns" ]
 
 [[cmd]]
-name = "Running with ‘far-positive-latitude.loc.invalid’ displays a record length error"
+name = "Running with ‘far-positive-latitude.loc.invalid’ displays a record with an out-of-range field"
 shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 LOC far-positive-latitude.loc.invalid"
 stdout = { file = "outputs/far-positive-latitude.loc.invalid.ansitxt" }
 stderr = { empty = true }
@@ -44,6 +55,41 @@ status = 0
 tags = [ "loc", "madns" ]
 
 
+# LOC record out-of-range positions (JSON)
+
+[[cmd]]
+name = "Running with ‘far-negative-longitude.loc.invalid’ displays a record structure with an out-of-range field"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 LOC far-negative-longitude.loc.invalid --json | jq"
+stdout = { file = "outputs/far-negative-longitude.loc.invalid.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "loc", "madns", "json" ]
+
+[[cmd]]
+name = "Running with ‘far-positive-longitude.loc.invalid’ displays a record structure with an out-of-range field"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 LOC far-positive-longitude.loc.invalid --json | jq"
+stdout = { file = "outputs/far-positive-longitude.loc.invalid.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "loc", "madns", "json" ]
+
+[[cmd]]
+name = "Running with ‘far-negative-latitude.loc.invalid’ displays a record structure with an out-of-range field"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 LOC far-negative-latitude.loc.invalid --json | jq"
+stdout = { file = "outputs/far-negative-latitude.loc.invalid.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "loc", "madns", "json" ]
+
+[[cmd]]
+name = "Running with ‘far-positive-latitude.loc.invalid’ displays a record structure with an out-of-range field"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 LOC far-positive-latitude.loc.invalid --json | jq"
+stdout = { file = "outputs/far-positive-latitude.loc.invalid.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "loc", "madns", "json" ]
+
+
 # LOC record version 1
 
 [[cmd]]

+ 11 - 0
xtests/madns/mx-records.toml

@@ -9,6 +9,17 @@ status = 0
 tags = [ "mx", "madns" ]
 
 
+# MX record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘mx.example’ prints the correct MX record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 MX mx.example --json | jq"
+stdout = { file = "outputs/mx.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "mx", "madns", "json" ]
+
+
 # MX record invalid packets
 
 [[cmd]]

+ 11 - 0
xtests/madns/naptr-records.toml

@@ -17,6 +17,17 @@ status = 0
 tags = [ "naptr", "madns" ]
 
 
+# NAPTR record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘naptr.example’ prints the correct NAPTR record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 NAPTR naptr.example --json | jq"
+stdout = { file = "outputs/naptr.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "naptr", "madns", "json" ]
+
+
 # NAPTR record invalid packets
 
 [[cmd]]

+ 11 - 0
xtests/madns/ns-records.toml

@@ -9,6 +9,17 @@ status = 0
 tags = [ "ns", "madns" ]
 
 
+# NS record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘ns.example’ prints the correct NS record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 NS ns.example --json | jq"
+stdout = { file = "outputs/ns.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "ns", "madns", "json" ]
+
+
 # NS record invalid packets
 
 [[cmd]]

+ 11 - 0
xtests/madns/openpgpkey-records.toml

@@ -9,6 +9,17 @@ status = 0
 tags = [ "openpgpkey", "madns" ]
 
 
+# OPENPGPKEY record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘openpgpkey.example’ prints the correct OPENPGPKEY record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 OPENPGPKEY openpgpkey.example --json | jq"
+stdout = { file = "outputs/openpgpkey.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "openpgpkey", "madns", "json" ]
+
+
 # OPENPGPKEY record invalid packets
 
 [[cmd]]

+ 35 - 0
xtests/madns/opt-records.toml

@@ -33,6 +33,41 @@ status = 0
 tags = [ "opt", "madns" ]
 
 
+# OPT record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘opt.example’ prints the correct OPT record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 A opt.example --edns=show --json | jq"
+stdout = { file = "outputs/opt.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "opt", "madns", "json" ]
+
+[[cmd]]
+name = "Running with ‘do-flag.opt.example’ prints the correct OPT record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 A do-flag.opt.example --edns=show --json | jq"
+stdout = { file = "outputs/do-flag.opt.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "opt", "madns", "json" ]
+
+[[cmd]]
+name = "Running with ‘other-flags.opt.example’ prints the correct OPT record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 A other-flags.opt.example --edns=show --json | jq"
+stdout = { file = "outputs/other-flags.opt.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "opt", "madns", "json" ]
+
+[[cmd]]
+name = "Running with ‘named.opt.invalid’ prints the correct OPT record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 A named.opt.invalid --edns=show --json | jq"
+stdout = { file = "outputs/named.opt.invalid.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "opt", "madns", "json" ]
+
+
 # OPT record invalid packets
 
 [[cmd]]

+ 26 - 0
xtests/madns/outputs/a.example.json

@@ -0,0 +1,26 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "a.example.",
+          "class": "IN",
+          "type": "A"
+        }
+      ],
+      "answers": [
+        {
+          "name": "a.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "A",
+          "data": {
+            "address": "127.0.0.1"
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 26 - 0
xtests/madns/outputs/aaaa.example.json

@@ -0,0 +1,26 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "aaaa.example.",
+          "class": "IN",
+          "type": "AAAA"
+        }
+      ],
+      "answers": [
+        {
+          "name": "aaaa.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "AAAA",
+          "data": {
+            "address": "::1"
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 26 - 0
xtests/madns/outputs/ansi.str.example.json

@@ -0,0 +1,26 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "ansi.str.example.",
+          "class": "IN",
+          "type": "CNAME"
+        }
+      ],
+      "answers": [
+        {
+          "name": "ansi.str.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "CNAME",
+          "data": {
+            "domain": "\u001b[32mgreen.\u001b[34mblue.\u001b[31mred.\u001b[0m."
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 28 - 0
xtests/madns/outputs/caa.example.json

@@ -0,0 +1,28 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "caa.example.",
+          "class": "IN",
+          "type": "CAA"
+        }
+      ],
+      "answers": [
+        {
+          "name": "caa.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "CAA",
+          "data": {
+            "critical": false,
+            "tag": "issuewild",
+            "value": "trustworthy.example"
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 26 - 0
xtests/madns/outputs/cname.example.json

@@ -0,0 +1,26 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "cname.example.",
+          "class": "IN",
+          "type": "CNAME"
+        }
+      ],
+      "answers": [
+        {
+          "name": "cname.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "CNAME",
+          "data": {
+            "domain": "dns.lookup.dog."
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 28 - 0
xtests/madns/outputs/critical.caa.example.json

@@ -0,0 +1,28 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "critical.caa.example.",
+          "class": "IN",
+          "type": "CAA"
+        }
+      ],
+      "answers": [
+        {
+          "name": "critical.caa.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "CAA",
+          "data": {
+            "critical": true,
+            "tag": "issuewild",
+            "value": "trustworthy.example"
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 35 - 0
xtests/madns/outputs/do-flag.opt.example.json

@@ -0,0 +1,35 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "do-flag.opt.example.",
+          "class": "IN",
+          "type": "A"
+        }
+      ],
+      "answers": [
+        {
+          "name": "do-flag.opt.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "A",
+          "data": {
+            "address": "127.0.0.1"
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": [
+        {
+          "name": "",
+          "type": "OPT",
+          "data": {
+            "version": 0,
+            "data": []
+          }
+        }
+      ]
+    }
+  ]
+}

+ 26 - 0
xtests/madns/outputs/eui48.example.json

@@ -0,0 +1,26 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "eui48.example.",
+          "class": "IN",
+          "type": "EUI48"
+        }
+      ],
+      "answers": [
+        {
+          "name": "eui48.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "EUI48",
+          "data": {
+            "identifier": "12-34-56-78-90-ab"
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 26 - 0
xtests/madns/outputs/eui64.example.json

@@ -0,0 +1,26 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "eui64.example.",
+          "class": "IN",
+          "type": "EUI64"
+        }
+      ],
+      "answers": [
+        {
+          "name": "eui64.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "EUI64",
+          "data": {
+            "identifier": "12-34-56-ff-fe-78-90-ab"
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 35 - 0
xtests/madns/outputs/far-negative-latitude.loc.invalid.json

@@ -0,0 +1,35 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "far-negative-latitude.loc.invalid.",
+          "class": "IN",
+          "type": "LOC"
+        }
+      ],
+      "answers": [
+        {
+          "name": "far-negative-latitude.loc.invalid.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "LOC",
+          "data": {
+            "size": "3e2",
+            "precision": {
+              "horizontal": 0,
+              "vertical": 0
+            },
+            "point": {
+              "latitude": null,
+              "longitude": "0°0′0″ E",
+              "altitude": "0m"
+            }
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 35 - 0
xtests/madns/outputs/far-negative-longitude.loc.invalid.json

@@ -0,0 +1,35 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "far-negative-longitude.loc.invalid.",
+          "class": "IN",
+          "type": "LOC"
+        }
+      ],
+      "answers": [
+        {
+          "name": "far-negative-longitude.loc.invalid.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "LOC",
+          "data": {
+            "size": "3e2",
+            "precision": {
+              "horizontal": 0,
+              "vertical": 0
+            },
+            "point": {
+              "latitude": "0°0′0″ N",
+              "longitude": null,
+              "altitude": "0m"
+            }
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 35 - 0
xtests/madns/outputs/far-positive-latitude.loc.invalid.json

@@ -0,0 +1,35 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "far-positive-latitude.loc.invalid.",
+          "class": "IN",
+          "type": "LOC"
+        }
+      ],
+      "answers": [
+        {
+          "name": "far-positive-latitude.loc.invalid.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "LOC",
+          "data": {
+            "size": "3e2",
+            "precision": {
+              "horizontal": 0,
+              "vertical": 0
+            },
+            "point": {
+              "latitude": null,
+              "longitude": "0°0′0″ E",
+              "altitude": "0m"
+            }
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 35 - 0
xtests/madns/outputs/far-positive-longitude.loc.invalid.json

@@ -0,0 +1,35 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "far-positive-longitude.loc.invalid.",
+          "class": "IN",
+          "type": "LOC"
+        }
+      ],
+      "answers": [
+        {
+          "name": "far-positive-longitude.loc.invalid.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "LOC",
+          "data": {
+            "size": "3e2",
+            "precision": {
+              "horizontal": 0,
+              "vertical": 0
+            },
+            "point": {
+              "latitude": "0°0′0″ N",
+              "longitude": null,
+              "altitude": "0m"
+            }
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 27 - 0
xtests/madns/outputs/hinfo.example.json

@@ -0,0 +1,27 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "hinfo.example.",
+          "class": "IN",
+          "type": "HINFO"
+        }
+      ],
+      "answers": [
+        {
+          "name": "hinfo.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "HINFO",
+          "data": {
+            "cpu": "some-kinda-cpu",
+            "os": "some-kinda-os"
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 35 - 0
xtests/madns/outputs/loc.example.json

@@ -0,0 +1,35 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "loc.example.",
+          "class": "IN",
+          "type": "LOC"
+        }
+      ],
+      "answers": [
+        {
+          "name": "loc.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "LOC",
+          "data": {
+            "size": "3e2",
+            "precision": {
+              "horizontal": 0,
+              "vertical": 0
+            },
+            "point": {
+              "latitude": "51°30′12.748″ N",
+              "longitude": "0°7′39.611″ W",
+              "altitude": "0m"
+            }
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 27 - 0
xtests/madns/outputs/mx.example.json

@@ -0,0 +1,27 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "mx.example.",
+          "class": "IN",
+          "type": "MX"
+        }
+      ],
+      "answers": [
+        {
+          "name": "mx.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "MX",
+          "data": {
+            "preference": 10,
+            "exchange": "exchange.example."
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 35 - 0
xtests/madns/outputs/named.opt.invalid.json

@@ -0,0 +1,35 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "named.opt.invalid.",
+          "class": "IN",
+          "type": "A"
+        }
+      ],
+      "answers": [
+        {
+          "name": "named.opt.invalid.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "A",
+          "data": {
+            "address": "127.0.0.1"
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": [
+        {
+          "name": "bingle.bongle.dingle.dangle.",
+          "type": "OPT",
+          "data": {
+            "version": 0,
+            "data": []
+          }
+        }
+      ]
+    }
+  ]
+}

+ 30 - 0
xtests/madns/outputs/naptr.example.json

@@ -0,0 +1,30 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "naptr.example.",
+          "class": "IN",
+          "type": "NAPTR"
+        }
+      ],
+      "answers": [
+        {
+          "name": "naptr.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "NAPTR",
+          "data": {
+            "order": 5,
+            "flags": "s",
+            "service": "SRV",
+            "regex": "\\d\\d:\\d\\d:\\d\\d",
+            "replacement": "srv.example."
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 26 - 0
xtests/madns/outputs/newline.str.example.json

@@ -0,0 +1,26 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "newline.str.example.",
+          "class": "IN",
+          "type": "CNAME"
+        }
+      ],
+      "answers": [
+        {
+          "name": "newline.str.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "CNAME",
+          "data": {
+            "domain": "some\nnew\r\nlines\n.example."
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 26 - 0
xtests/madns/outputs/ns.example.json

@@ -0,0 +1,26 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "ns.example.",
+          "class": "IN",
+          "type": "NS"
+        }
+      ],
+      "answers": [
+        {
+          "name": "ns.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "NS",
+          "data": {
+            "nameserver": "a.gtld-servers.net."
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 26 - 0
xtests/madns/outputs/null.str.example.json

@@ -0,0 +1,26 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "null.str.example.",
+          "class": "IN",
+          "type": "CNAME"
+        }
+      ],
+      "answers": [
+        {
+          "name": "null.str.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "CNAME",
+          "data": {
+            "domain": "some\u0000null\u0000\u0000chars\u0000.example."
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 26 - 0
xtests/madns/outputs/openpgpkey.example.json

@@ -0,0 +1,26 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "openpgpkey.example.",
+          "class": "IN",
+          "type": "OPENPGPKEY"
+        }
+      ],
+      "answers": [
+        {
+          "name": "openpgpkey.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "OPENPGPKEY",
+          "data": {
+            "key": "EjRWeA=="
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 35 - 0
xtests/madns/outputs/opt.example.json

@@ -0,0 +1,35 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "opt.example.",
+          "class": "IN",
+          "type": "A"
+        }
+      ],
+      "answers": [
+        {
+          "name": "opt.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "A",
+          "data": {
+            "address": "127.0.0.1"
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": [
+        {
+          "name": "",
+          "type": "OPT",
+          "data": {
+            "version": 0,
+            "data": []
+          }
+        }
+      ]
+    }
+  ]
+}

+ 35 - 0
xtests/madns/outputs/other-flags.opt.example.json

@@ -0,0 +1,35 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "other-flags.opt.example.",
+          "class": "IN",
+          "type": "A"
+        }
+      ],
+      "answers": [
+        {
+          "name": "other-flags.opt.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "A",
+          "data": {
+            "address": "127.0.0.1"
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": [
+        {
+          "name": "",
+          "type": "OPT",
+          "data": {
+            "version": 0,
+            "data": []
+          }
+        }
+      ]
+    }
+  ]
+}

+ 28 - 0
xtests/madns/outputs/others.caa.example.json

@@ -0,0 +1,28 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "caa.example.",
+          "class": "IN",
+          "type": "CAA"
+        }
+      ],
+      "answers": [
+        {
+          "name": "caa.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "CAA",
+          "data": {
+            "critical": false,
+            "tag": "issuewild",
+            "value": "trustworthy.example"
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 26 - 0
xtests/madns/outputs/ptr.example.json

@@ -0,0 +1,26 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "ptr.example.",
+          "class": "IN",
+          "type": "PTR"
+        }
+      ],
+      "answers": [
+        {
+          "name": "ptr.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "PTR",
+          "data": {
+            "cname": "dns.example."
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 26 - 0
xtests/madns/outputs/soa.example.json

@@ -0,0 +1,26 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "soa.example.",
+          "class": "IN",
+          "type": "SOA"
+        }
+      ],
+      "answers": [
+        {
+          "name": "soa.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "SOA",
+          "data": {
+            "mname": "mname.example."
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 29 - 0
xtests/madns/outputs/srv.example.json

@@ -0,0 +1,29 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "srv.example.",
+          "class": "IN",
+          "type": "SRV"
+        }
+      ],
+      "answers": [
+        {
+          "name": "srv.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "SRV",
+          "data": {
+            "priority": 1,
+            "weight": 1,
+            "port": 37500,
+            "target": "service.example."
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 28 - 0
xtests/madns/outputs/sshfp.example.json

@@ -0,0 +1,28 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "sshfp.example.",
+          "class": "IN",
+          "type": "SSHFP"
+        }
+      ],
+      "answers": [
+        {
+          "name": "sshfp.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "SSHFP",
+          "data": {
+            "algorithm": 1,
+            "fingerprint_type": 1,
+            "fingerprint": "212223242526"
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 26 - 0
xtests/madns/outputs/tab.str.example.json

@@ -0,0 +1,26 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "tab.str.example.",
+          "class": "IN",
+          "type": "CNAME"
+        }
+      ],
+      "answers": [
+        {
+          "name": "tab.str.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "CNAME",
+          "data": {
+            "domain": "some\ttab\t\tchars\t.example."
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 29 - 0
xtests/madns/outputs/tlsa.example.json

@@ -0,0 +1,29 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "tlsa.example.",
+          "class": "IN",
+          "type": "TLSA"
+        }
+      ],
+      "answers": [
+        {
+          "name": "tlsa.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "TLSA",
+          "data": {
+            "certificate_usage": 3,
+            "selector": 1,
+            "matching_type": 1,
+            "certificate_data": "112233445566"
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 28 - 0
xtests/madns/outputs/txt.example.json

@@ -0,0 +1,28 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "txt.example.",
+          "class": "IN",
+          "type": "TXT"
+        }
+      ],
+      "answers": [
+        {
+          "name": "txt.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "TXT",
+          "data": {
+            "messages": [
+              "Cache Invalidation and Naming Things"
+            ]
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 26 - 0
xtests/madns/outputs/upperbit.str.example.json

@@ -0,0 +1,26 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "upperbit.str.example.",
+          "class": "IN",
+          "type": "CNAME"
+        }
+      ],
+      "answers": [
+        {
+          "name": "upperbit.str.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "CNAME",
+          "data": {
+            "domain": "\u007f�����.example."
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 28 - 0
xtests/madns/outputs/uri.example.json

@@ -0,0 +1,28 @@
+{
+  "responses": [
+    {
+      "queries": [
+        {
+          "name": "uri.example.",
+          "class": "IN",
+          "type": "URI"
+        }
+      ],
+      "answers": [
+        {
+          "name": "uri.example.",
+          "class": "IN",
+          "ttl": 600,
+          "type": "URI",
+          "data": {
+            "priority": 10,
+            "weight": 16,
+            "target": "https://rfcs.io/"
+          }
+        }
+      ],
+      "authorities": [],
+      "additionals": []
+    }
+  ]
+}

+ 45 - 0
xtests/madns/protocol-chars.toml

@@ -1,3 +1,5 @@
+# Character escaping
+
 [[cmd]]
 name = "Running with ‘ansi.str.example’ properly escapes the codes"
 shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 CNAME ansi.str.example"
@@ -37,3 +39,46 @@ stdout = { file = "outputs/upperbit.str.example.ansitxt" }
 stderr = { empty = true }
 status = 0
 tags = [ "protocol", "madns" ]
+
+
+# Character escaping (JSON)
+
+[[cmd]]
+name = "Running with ‘ansi.str.example’ properly escapes the codes in the JSON string"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 CNAME ansi.str.example --json | jq"
+stdout = { file = "outputs/ansi.str.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "protocol", "madns", "json" ]
+
+[[cmd]]
+name = "Running with ‘newline.str.example’ properly escapes the newlines in the JSON string"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 CNAME newline.str.example --json | jq"
+stdout = { file = "outputs/newline.str.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "protocol", "madns", "json" ]
+
+[[cmd]]
+name = "Running with ‘null.str.example’ properly handles the null bytes in the JSON string"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 CNAME null.str.example --json | jq"
+stdout = { file = "outputs/null.str.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "protocol", "madns", "json" ]
+
+[[cmd]]
+name = "Running with ‘tab.str.example’ properly escapes the tabs in the JSON string"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 CNAME tab.str.example --json | jq"
+stdout = { file = "outputs/tab.str.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "protocol", "madns", "json" ]
+
+[[cmd]]
+name = "Running with ‘upperbit.str.example’ properly escapes the upper-bit characters in the JSON string"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 CNAME upperbit.str.example --json | jq"
+stdout = { file = "outputs/upperbit.str.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "protocol", "madns", "json" ]

+ 11 - 0
xtests/madns/ptr-records.toml

@@ -9,6 +9,17 @@ status = 0
 tags = [ "ptr", "madns" ]
 
 
+# PTR record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘ptr.example’ prints the correct PTR record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 PTR ptr.example --json | jq"
+stdout = { file = "outputs/ptr.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "ptr", "madns", "json" ]
+
+
 # PTR record invalid packets
 
 [[cmd]]

+ 11 - 0
xtests/madns/soa-records.toml

@@ -9,6 +9,17 @@ status = 0
 tags = [ "soa", "madns" ]
 
 
+# SOA record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘soa.example’ prints the correct SOA record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 SOA soa.example --json | jq"
+stdout = { file = "outputs/soa.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "soa", "madns", "json" ]
+
+
 # SOA record invalid packets
 
 [[cmd]]

+ 11 - 0
xtests/madns/srv-records.toml

@@ -9,6 +9,17 @@ status = 0
 tags = [ "soa", "madns" ]
 
 
+# SRV record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘srv.example’ prints the correct SRV record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 SRV srv.example --json | jq"
+stdout = { file = "outputs/srv.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "soa", "madns", "json" ]
+
+
 # SRV record invalid packets
 
 [[cmd]]

+ 11 - 0
xtests/madns/sshfp-records.toml

@@ -9,6 +9,17 @@ status = 0
 tags = [ "sshfp", "madns" ]
 
 
+# SSHFP record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘sshfp.example’ prints the correct SSHFP record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 SSHFP sshfp.example --json | jq"
+stdout = { file = "outputs/sshfp.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "sshfp", "madns", "json" ]
+
+
 # SSHFP record invalid packets
 
 [[cmd]]

+ 11 - 0
xtests/madns/tlsa-records.toml

@@ -9,6 +9,17 @@ status = 0
 tags = [ "tlsa", "madns" ]
 
 
+# TLSA record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘tlsa.example’ prints the correct TLSA record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 TLSA tlsa.example --json | jq"
+stdout = { file = "outputs/tlsa.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "tlsa", "madns", "json" ]
+
+
 # TLSA record invalid packets
 
 [[cmd]]

+ 11 - 0
xtests/madns/txt-records.toml

@@ -9,6 +9,17 @@ status = 0
 tags = [ "txt", "madns" ]
 
 
+# TXT record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘txt.example’ prints the correct TXT record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 TXT txt.example --json | jq"
+stdout = { file = "outputs/txt.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "txt", "madns", "json" ]
+
+
 # TXT record invalid packets
 
 [[cmd]]

+ 11 - 0
xtests/madns/uri-records.toml

@@ -17,6 +17,17 @@ status = 0
 tags = [ "uri", "madns" ]
 
 
+# URI record successes (JSON)
+
+[[cmd]]
+name = "Running with ‘uri.example’ prints the correct URI record structure"
+shell = "dog --colour=always --tcp @madns.binarystar.systems:5301 URI uri.example --json | jq"
+stdout = { file = "outputs/uri.example.json" }
+stderr = { empty = true }
+status = 0
+tags = [ "uri", "madns", "json" ]
+
+
 # URI record invalid packets
 
 [[cmd]]