浏览代码

Move huge unit tests out of wire module

I figured out why cargo-mutagen wasn't picking up any coverage for the integration tests: it was only being run when the 'test' flag was enabled, so it only picked up unit tests.

This was the only reason that these long tests were unit tests, cluttering up the wire.rs file, so now that's changed, they've been moved to integration test files. We also now log a warning if the feature somehow gets enabled when building the main application, which should offset the danger of this feature technically being available to non-test builds.
Benjamin Sago 4 年之前
父节点
当前提交
0d51c44c86

+ 1 - 1
dns/src/record/a.rs

@@ -23,7 +23,7 @@ impl Wire for A {
     const NAME: &'static str = "A";
     const RR_TYPE: u16 = 1;
 
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
         if stated_length != 4 {
             warn!("Length is incorrect (record length {:?}, but should be four)", stated_length);

+ 1 - 1
dns/src/record/aaaa.rs

@@ -23,7 +23,7 @@ impl Wire for AAAA {
     const NAME: &'static str = "AAAA";
     const RR_TYPE: u16 = 28;
 
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
         if stated_length != 16 {
             warn!("Length is incorrect (stated length {:?}, but should be sixteen)", stated_length);

+ 1 - 1
dns/src/record/caa.rs

@@ -28,7 +28,7 @@ impl Wire for CAA {
     const NAME: &'static str = "CAA";
     const RR_TYPE: u16 = 257;
 
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
         let flags = c.read_u8()?;
         trace!("Parsed flags -> {:#08b}", flags);

+ 1 - 1
dns/src/record/cname.rs

@@ -21,7 +21,7 @@ impl Wire for CNAME {
     const NAME: &'static str = "CNAME";
     const RR_TYPE: u16 = 5;
 
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
         let (domain, domain_length) = c.read_labels()?;
         trace!("Parsed domain -> {:?}", domain);

+ 1 - 1
dns/src/record/hinfo.rs

@@ -28,7 +28,7 @@ impl Wire for HINFO {
     const NAME: &'static str = "HINFO";
     const RR_TYPE: u16 = 13;
 
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
 
         let cpu_length = c.read_u8()?;

+ 1 - 1
dns/src/record/loc.rs

@@ -78,7 +78,7 @@ impl Wire for LOC {
     const NAME: &'static str = "LOC";
     const RR_TYPE: u16 = 29;
 
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
         let version = c.read_u8()?;
         trace!("Parsed version -> {:?}", version);

+ 1 - 1
dns/src/record/mx.rs

@@ -26,7 +26,7 @@ impl Wire for MX {
     const NAME: &'static str = "MX";
     const RR_TYPE: u16 = 15;
 
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
         let preference = c.read_u16::<BigEndian>()?;
         trace!("Parsed preference -> {:?}", preference);

+ 1 - 1
dns/src/record/naptr.rs

@@ -40,7 +40,7 @@ impl Wire for NAPTR {
     const NAME: &'static str = "MX";
     const RR_TYPE: u16 = 35;
 
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
         let order = c.read_u16::<BigEndian>()?;
         trace!("Parsed order -> {:?}", order);

+ 1 - 1
dns/src/record/ns.rs

@@ -22,7 +22,7 @@ impl Wire for NS {
     const NAME: &'static str = "NS";
     const RR_TYPE: u16 = 2;
 
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
         let (nameserver, nameserver_length) = c.read_labels()?;
         trace!("Parsed nameserver -> {:?}", nameserver);

+ 1 - 1
dns/src/record/opt.rs

@@ -62,7 +62,7 @@ impl OPT {
     /// See §6.1.3 of the RFC, “OPT Record TTL Field Use”.
     ///
     /// Unlike the `Wire::read` function, this does not require a length.
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     pub fn read(c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
         let udp_payload_size = c.read_u16::<BigEndian>()?;  // replaces the class field
         trace!("Parsed UDP payload size -> {:?}", udp_payload_size);

+ 1 - 1
dns/src/record/ptr.rs

@@ -27,7 +27,7 @@ impl Wire for PTR {
     const NAME: &'static str = "PTR";
     const RR_TYPE: u16 = 12;
 
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
         let (cname, cname_length) = c.read_labels()?;
         trace!("Parsed cname -> {:?}", cname);

+ 1 - 1
dns/src/record/soa.rs

@@ -47,7 +47,7 @@ impl Wire for SOA {
     const RR_TYPE: u16 = 6;
 
     #[allow(clippy::similar_names)]
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
         let (mname, mname_length) = c.read_labels()?;
         trace!("Parsed mname -> {:?}", mname);

+ 1 - 1
dns/src/record/srv.rs

@@ -33,7 +33,7 @@ impl Wire for SRV {
     const NAME: &'static str = "SRV";
     const RR_TYPE: u16 = 33;
 
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
         let priority = c.read_u16::<BigEndian>()?;
         trace!("Parsed priority -> {:?}", priority);

+ 1 - 1
dns/src/record/sshfp.rs

@@ -30,7 +30,7 @@ impl Wire for SSHFP {
     const NAME: &'static str = "SSHFP";
     const RR_TYPE: u16 = 44;
 
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
         let algorithm = c.read_u8()?;
         trace!("Parsed algorithm -> {:?}", algorithm);

+ 1 - 1
dns/src/record/tlsa.rs

@@ -34,7 +34,7 @@ impl Wire for TLSA {
     const NAME: &'static str = "TLSA";
     const RR_TYPE: u16 = 52;
 
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
 
         let certificate_usage = c.read_u8()?;

+ 1 - 1
dns/src/record/txt.rs

@@ -25,7 +25,7 @@ impl Wire for TXT {
     const NAME: &'static str = "TXT";
     const RR_TYPE: u16 = 16;
 
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
         let mut messages = Vec::new();
         let mut total_length = 0_u16;

+ 1 - 1
dns/src/record/uri.rs

@@ -32,7 +32,7 @@ impl Wire for URI {
     const NAME: &'static str = "URI";
     const RR_TYPE: u16 = 256;
 
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn read(stated_length: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
         let priority = c.read_u16::<BigEndian>()?;
         trace!("Parsed priority -> {:?}", priority);

+ 1 - 1
dns/src/strings.rs

@@ -116,7 +116,7 @@ const RECURSION_LIMIT: usize = 8;
 /// recursions to track backtracking positions. Returns the count of bytes
 /// that had to be read to produce the string, including the bytes to signify
 /// backtracking, but not including the bytes read _during_ backtracking.
-#[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+#[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
 fn read_string_recursive(labels: &mut Labels, c: &mut Cursor<&[u8]>, recursions: &mut Vec<u16>) -> Result<u16, WireError> {
     let mut bytes_read = 0;
 

+ 8 - 165
dns/src/wire.rs

@@ -54,7 +54,7 @@ impl Request {
 impl Response {
 
     /// Reads bytes off of the given slice, parsing them into a response.
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     pub fn from_bytes(bytes: &[u8]) -> Result<Self, WireError> {
         info!("Parsing response");
         trace!("Bytes -> {:?}", bytes);
@@ -108,7 +108,7 @@ impl Query {
 
     /// Reads bytes from the given cursor, and parses them into a query with
     /// the given domain name.
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn from_bytes(qname: Labels, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
         let qtype = c.read_u16::<BigEndian>()?;
         trace!("Read qtype -> {:?}", qtype);
@@ -125,7 +125,7 @@ impl Answer {
 
     /// Reads bytes from the given cursor, and parses them into an answer with
     /// the given domain name.
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn from_bytes(qname: Labels, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
         let qtype = c.read_u16::<BigEndian>()?;
         trace!("Read qtype -> {:?}", qtype);
@@ -156,10 +156,14 @@ impl Record {
 
     /// Reads at most `len` bytes from the given curser, and parses them into
     /// a record structure depending on the type number, which has already been read.
-    #[cfg_attr(all(test, feature = "with_mutagen"), ::mutagen::mutate)]
+    #[cfg_attr(feature = "with_mutagen", ::mutagen::mutate)]
     fn from_bytes(qtype: TypeInt, len: u16, c: &mut Cursor<&[u8]>) -> Result<Self, WireError> {
         use crate::record::*;
 
+        if cfg!(feature = "with_mutagen") {
+            warn!("Mutation is enabled!");
+        }
+
         macro_rules! try_record {
             ($record:tt) => {
                 if $record::RR_TYPE == qtype {
@@ -482,164 +486,3 @@ impl From<io::Error> for WireError {
         Self::IO
     }
 }
-
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use crate::record::{Record, A, SOA, OPT, UnknownQtype};
-    use std::net::Ipv4Addr;
-    use pretty_assertions::assert_eq;
-
-    #[test]
-    fn build_request() {
-        let request = Request {
-            transaction_id: 0xceac,
-            flags: Flags::query(),
-            query: Query {
-                qname: Labels::encode("rfcs.io").unwrap(),
-                qclass: QClass::Other(0x42),
-                qtype: 0x1234,
-            },
-            additional: Some(Request::additional_record()),
-        };
-
-        let result = vec![
-            0xce, 0xac,  // transaction ID
-            0x01, 0x00,  // flags (standard query)
-            0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,  // counts (1, 0, 0, 1)
-
-            // query:
-            0x04, 0x72, 0x66, 0x63, 0x73, 0x02, 0x69, 0x6f, 0x00,  // qname
-            0x12, 0x34,  // type
-            0x00, 0x42,  // class
-
-            // OPT record:
-            0x00,  // name
-            0x00, 0x29,  // type OPT
-            0x02, 0x00,  // UDP payload size
-            0x00,  // higher bits
-            0x00,  // EDNS(0) version
-            0x00, 0x00,  // more flags
-            0x00, 0x00,  // no data
-        ];
-
-        assert_eq!(request.to_bytes().unwrap(), result);
-    }
-
-    #[test]
-    fn complete_response() {
-
-        // This is an artifical amalgam of DNS, not a real-world response!
-        let buf = &[
-            0xce, 0xac,  // transaction ID
-            0x81, 0x80,  // flags (standard query, response, no error)
-            0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02,  // counts (1, 1, 1, 2)
-
-            // query:
-            0x05, 0x62, 0x73, 0x61, 0x67, 0x6f, 0x02, 0x6d, 0x65, 0x00,  // name
-            0x00, 0x01,  // type A
-            0x00, 0x01,  // class IN
-
-            // answer:
-            0xc0, 0x0c,  // name (backreference)
-            0x00, 0x01,  // type A
-            0x00, 0x01,  // class IN
-            0x00, 0x00, 0x03, 0x77,  // TTL
-            0x00, 0x04,  // data length 4
-            0x8a, 0x44, 0x75, 0x5e,  // IP address
-
-            // authoritative:
-            0x00,  // name
-            0x00, 0x06,  // type SOA
-            0x00, 0x01,  // class IN
-            0xFF, 0xFF, 0xFF, 0xFF,  // TTL (maximum possible!)
-            0x00, 0x1B,  // data length
-            0x01, 0x61, 0x00,  // primary name server ("a")
-            0x02, 0x6d, 0x78, 0x00,  // mailbox ("mx")
-            0x78, 0x68, 0x52, 0x2c,  // serial number
-            0x00, 0x00, 0x07, 0x08,  // refresh interval
-            0x00, 0x00, 0x03, 0x84,  // retry interval
-            0x00, 0x09, 0x3a, 0x80,  // expire limit
-            0x00, 0x01, 0x51, 0x80,  // minimum TTL
-
-            // additional 1:
-            0x00,  // name
-            0x00, 0x99,  // unknown type
-            0x00, 0x99,  // unknown class
-            0x12, 0x34, 0x56, 0x78,  // TTL
-            0x00, 0x04,  // data length 4
-            0x12, 0x34, 0x56, 0x78,  // data
-
-            // additional 2:
-            0x00,  // name
-            0x00, 0x29,  // type OPT
-            0x02, 0x00,  // UDP payload size
-            0x00,  // higher bits
-            0x00,  // EDNS(0) version
-            0x00, 0x00,  // more flags
-            0x00, 0x00,  // no data
-        ];
-
-        let response = Response {
-            transaction_id: 0xceac,
-            flags: Flags::standard_response(),
-            queries: vec![
-                Query {
-                    qname: Labels::encode("bsago.me").unwrap(),
-                    qclass: QClass::IN,
-                    qtype: qtype!(A),
-                },
-            ],
-            answers: vec![
-                Answer::Standard {
-                    qname: Labels::encode("bsago.me").unwrap(),
-                    qclass: QClass::IN,
-                    ttl: 887,
-                    record: Record::A(A {
-                        address: Ipv4Addr::new(138, 68, 117, 94),
-                    }),
-                }
-            ],
-            authorities: vec![
-                Answer::Standard {
-                    qname: Labels::root(),
-                    qclass: QClass::IN,
-                    ttl: 4294967295,
-                    record: Record::SOA(SOA {
-                        mname: Labels::encode("a").unwrap(),
-                        rname: Labels::encode("mx").unwrap(),
-                        serial: 2020102700,
-                        refresh_interval: 1800,
-                        retry_interval: 900,
-                        expire_limit: 604800,
-                        minimum_ttl: 86400,
-                    }),
-                }
-            ],
-            additionals: vec![
-                Answer::Standard {
-                    qname: Labels::root(),
-                    qclass: QClass::Other(153),
-                    ttl: 305419896,
-                    record: Record::Other {
-                        type_number: UnknownQtype::UnheardOf(153),
-                        bytes: vec![ 0x12, 0x34, 0x56, 0x78 ],
-                    },
-                },
-                Answer::Pseudo {
-                    qname: Labels::root(),
-                    opt: OPT {
-                        udp_payload_size: 512,
-                        higher_bits: 0,
-                        edns0_version: 0,
-                        flags: 0,
-                        data: vec![],
-                    },
-                },
-            ],
-        };
-
-        assert_eq!(Response::from_bytes(buf), Ok(response));
-    }
-}

+ 40 - 0
dns/tests/wire_building_tests.rs

@@ -0,0 +1,40 @@
+use dns::{Request, Flags, Query, Labels, QClass};
+
+use pretty_assertions::assert_eq;
+
+
+#[test]
+fn build_request() {
+    let request = Request {
+        transaction_id: 0xceac,
+        flags: Flags::query(),
+        query: Query {
+            qname: Labels::encode("rfcs.io").unwrap(),
+            qclass: QClass::Other(0x42),
+            qtype: 0x1234,
+        },
+        additional: Some(Request::additional_record()),
+    };
+
+    let result = vec![
+        0xce, 0xac,  // transaction ID
+        0x01, 0x00,  // flags (standard query)
+        0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,  // counts (1, 0, 0, 1)
+
+        // query:
+        0x04, 0x72, 0x66, 0x63, 0x73, 0x02, 0x69, 0x6f, 0x00,  // qname
+        0x12, 0x34,  // type
+        0x00, 0x42,  // class
+
+        // OPT record:
+        0x00,  // name
+        0x00, 0x29,  // type OPT
+        0x02, 0x00,  // UDP payload size
+        0x00,  // higher bits
+        0x00,  // EDNS(0) version
+        0x00, 0x00,  // more flags
+        0x00, 0x00,  // no data
+    ];
+
+    assert_eq!(request.to_bytes().unwrap(), result);
+}

+ 120 - 1
dns/tests/wire_parsing_tests.rs

@@ -1,7 +1,9 @@
 use std::net::Ipv4Addr;
 
 use dns::{Response, Query, Answer, Labels, Flags, Opcode, QClass, qtype};
-use dns::record::{Record, A, CNAME, OPT};
+use dns::record::{Record, A, CNAME, OPT, SOA, UnknownQtype};
+
+use pretty_assertions::assert_eq;
 
 
 #[test]
@@ -150,3 +152,120 @@ fn parse_response_with_mixed_string() {
 
     assert_eq!(Response::from_bytes(buf), Ok(response));
 }
+
+
+#[test]
+fn parse_response_with_multiple_additionals() {
+
+    // This is an artifical amalgam of DNS, not a real-world response!
+    let buf = &[
+        0xce, 0xac,  // transaction ID
+        0x81, 0x80,  // flags (standard query, response, no error)
+        0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02,  // counts (1, 1, 1, 2)
+
+        // query:
+        0x05, 0x62, 0x73, 0x61, 0x67, 0x6f, 0x02, 0x6d, 0x65, 0x00,  // name
+        0x00, 0x01,  // type A
+        0x00, 0x01,  // class IN
+
+        // answer:
+        0xc0, 0x0c,  // name (backreference)
+        0x00, 0x01,  // type A
+        0x00, 0x01,  // class IN
+        0x00, 0x00, 0x03, 0x77,  // TTL
+        0x00, 0x04,  // data length 4
+        0x8a, 0x44, 0x75, 0x5e,  // IP address
+
+        // authoritative:
+        0x00,  // name
+        0x00, 0x06,  // type SOA
+        0x00, 0x01,  // class IN
+        0xFF, 0xFF, 0xFF, 0xFF,  // TTL (maximum possible!)
+        0x00, 0x1B,  // data length
+        0x01, 0x61, 0x00,  // primary name server ("a")
+        0x02, 0x6d, 0x78, 0x00,  // mailbox ("mx")
+        0x78, 0x68, 0x52, 0x2c,  // serial number
+        0x00, 0x00, 0x07, 0x08,  // refresh interval
+        0x00, 0x00, 0x03, 0x84,  // retry interval
+        0x00, 0x09, 0x3a, 0x80,  // expire limit
+        0x00, 0x01, 0x51, 0x80,  // minimum TTL
+
+        // additional 1:
+        0x00,  // name
+        0x00, 0x99,  // unknown type
+        0x00, 0x99,  // unknown class
+        0x12, 0x34, 0x56, 0x78,  // TTL
+        0x00, 0x04,  // data length 4
+        0x12, 0x34, 0x56, 0x78,  // data
+
+        // additional 2:
+        0x00,  // name
+        0x00, 0x29,  // type OPT
+        0x02, 0x00,  // UDP payload size
+        0x00,  // higher bits
+        0x00,  // EDNS(0) version
+        0x00, 0x00,  // more flags
+        0x00, 0x00,  // no data
+    ];
+
+    let response = Response {
+        transaction_id: 0xceac,
+        flags: Flags::standard_response(),
+        queries: vec![
+            Query {
+                qname: Labels::encode("bsago.me").unwrap(),
+                qclass: QClass::IN,
+                qtype: qtype!(A),
+            },
+        ],
+        answers: vec![
+            Answer::Standard {
+                qname: Labels::encode("bsago.me").unwrap(),
+                qclass: QClass::IN,
+                ttl: 887,
+                record: Record::A(A {
+                    address: Ipv4Addr::new(138, 68, 117, 94),
+                }),
+            }
+        ],
+        authorities: vec![
+            Answer::Standard {
+                qname: Labels::root(),
+                qclass: QClass::IN,
+                ttl: 4294967295,
+                record: Record::SOA(SOA {
+                    mname: Labels::encode("a").unwrap(),
+                    rname: Labels::encode("mx").unwrap(),
+                    serial: 2020102700,
+                    refresh_interval: 1800,
+                    retry_interval: 900,
+                    expire_limit: 604800,
+                    minimum_ttl: 86400,
+                }),
+            }
+        ],
+        additionals: vec![
+            Answer::Standard {
+                qname: Labels::root(),
+                qclass: QClass::Other(153),
+                ttl: 305419896,
+                record: Record::Other {
+                    type_number: UnknownQtype::UnheardOf(153),
+                    bytes: vec![ 0x12, 0x34, 0x56, 0x78 ],
+                },
+            },
+            Answer::Pseudo {
+                qname: Labels::root(),
+                opt: OPT {
+                    udp_payload_size: 512,
+                    higher_bits: 0,
+                    edns0_version: 0,
+                    flags: 0,
+                    data: vec![],
+                },
+            },
+        ],
+    };
+
+    assert_eq!(Response::from_bytes(buf), Ok(response));
+}