Explorar o código

feat(tests): get rid of legacy e2e tests, prepare #534

Peter Thomassen %!s(int64=4) %!d(string=hai) anos
pai
achega
af9f89aa79

+ 0 - 13
.github/workflows/main.yml

@@ -48,7 +48,6 @@ jobs:
       run: |
         cd $GITHUB_WORKSPACE
         docker-compose build
-        docker-compose -f docker-compose.yml -f docker-compose.test-e2e.yml build test-e2e
         docker-compose -f docker-compose.yml -f docker-compose.test-e2e2.yml build test-e2e2
 
     - name: Check for missing migrations
@@ -79,15 +78,3 @@ jobs:
         grep 'desec/' /var/log/syslog
         docker-compose -f docker-compose.yml -f docker-compose.test-api.yml down -v
         docker-compose down -v
-
-    - name: Run e2e Tests
-      run: |
-        docker-compose -f docker-compose.yml -f docker-compose.test-e2e.yml run -T test-e2e bash -c "./apiwait 45 && mocha ./spec"
-
-    - name: e2e Tests Logs and Cleanup
-      if: always()
-      run: |
-        docker-compose -f docker-compose.yml -f docker-compose.test-e2e.yml ps
-        grep 'desec/' /var/log/syslog
-        docker-compose -f docker-compose.yml -f docker-compose.test-e2e.yml down -v
-        docker-compose down -v

+ 0 - 47
docker-compose.test-e2e.yml

@@ -1,47 +0,0 @@
-version: '2.2'
-
-# mostly extending from main .yml
-services:
-  www:
-    environment:
-    - DESECSTACK_E2E_TEST=TRUE # increase abuse limits and such
-
-  api:
-    environment:
-    - DESECSTACK_E2E_TEST=TRUE # increase abuse limits and such
-
-  nslord:
-    networks:
-      front:
-        ipv4_address: ${DESECSTACK_IPV4_REAR_PREFIX16}.0.129 # make nslord available for test-e2e
-    environment:
-    - DESECSTACK_NSLORD_CACHE_TTL=0
-
-  test-e2e:
-    build: test/e2e
-    restart: "no"
-    environment:
-    - DESECSTACK_DOMAIN
-    - DESECSTACK_NS
-    - DESECSTACK_IPV4_REAR_PREFIX16
-    - DESECSTACK_IPV6_SUBNET
-    - DESECSTACK_IPV6_ADDRESS
-    - DESECSTACK_NSLORD_DEFAULT_TTL
-    mac_address: 06:42:ac:10:00:7f
-    depends_on:
-    - www
-    - nslord
-    - nsmaster
-    networks:
-      front:
-        ipv4_address: ${DESECSTACK_IPV4_REAR_PREFIX16}.0.127
-    extra_hosts:
-    - "checkipv4.dedyn.${DESECSTACK_DOMAIN}:${DESECSTACK_IPV4_REAR_PREFIX16}.0.128"
-    - "checkipv6.dedyn.${DESECSTACK_DOMAIN}:${DESECSTACK_IPV4_REAR_PREFIX16}.0.128"
-    - "checkip.dedyn.${DESECSTACK_DOMAIN}:${DESECSTACK_IPV4_REAR_PREFIX16}.0.128"
-    - "dedyn.${DESECSTACK_DOMAIN}:${DESECSTACK_IPV4_REAR_PREFIX16}.0.128"
-    - "desec.${DESECSTACK_DOMAIN}:${DESECSTACK_IPV4_REAR_PREFIX16}.0.128"
-    - "update6.dedyn.${DESECSTACK_DOMAIN}:${DESECSTACK_IPV4_REAR_PREFIX16}.0.128"
-    - "update.dedyn.${DESECSTACK_DOMAIN}:${DESECSTACK_IPV4_REAR_PREFIX16}.0.128"
-    - "www.dedyn.${DESECSTACK_DOMAIN}:${DESECSTACK_IPV4_REAR_PREFIX16}.0.128"
-    - "www.desec.${DESECSTACK_DOMAIN}:${DESECSTACK_IPV4_REAR_PREFIX16}.0.128"

+ 0 - 21
test/e2e/Dockerfile

@@ -1,21 +0,0 @@
-FROM node:14
-
-RUN apt-get update && apt-get install -y \
-		dnsutils \
-		net-tools \
-		dirmngr gnupg \
-	--no-install-recommends && apt-get clean && rm -rf /var/lib/apt/lists/*
-
-RUN npm install -g chakram mocha
-
-RUN mkdir /usr/src/app
-WORKDIR /usr/src/app
-
-COPY ./package.json ./
-RUN npm install
-
-COPY *.js ./
-COPY ./spec ./spec
-COPY ./apiwait ./apiwait
-
-CMD ./apiwait 45 && mocha ./spec

+ 0 - 7
test/e2e/README.md

@@ -1,7 +0,0 @@
-# End to End Tests
-
-These image sources provide a Docker container that automatically runs all tests against the REST API upon startup.
-
-To run the tests, use
-
-    docker-compose -f docker-compose.yml -f docker-compose.test-e2e.yml run -T test-e2e bash -c "./apiwait 45 && mocha ./spec"

+ 0 - 23
test/e2e/apiwait

@@ -1,23 +0,0 @@
-#!/usr/bin/env bash
-
-if [ -f ./.env ] ; then
-    source ../../.env
-fi
-
-TIME=0
-LIMIT=${1:-3}  # getting limit or default to 3 [sic]
-URL=https://www/api/v1/
-
-until curl --insecure --fail -H "Host: desec.$DESECSTACK_DOMAIN" $URL > /dev/null 2> /dev/null
-do
-    sleep 1
-    ((TIME+=1))
-
-    if [ $TIME -gt $LIMIT ]; then
-        curl --insecure -H "Host: desec.$DESECSTACK_DOMAIN" $URL
-        echo "waited $LIMIT seconds for api (desec.$DESECSTACK_DOMAIN) at $URL, giving up" > /dev/stderr
-        exit 1
-    fi
-done
-
-echo "api (desec.$DESECSTACK_DOMAIN) came up at $URL after $TIME seconds:"

+ 0 - 1
test/e2e/mock-static/Dockerfile

@@ -1 +0,0 @@
-FROM nginx:stable

+ 0 - 21
test/e2e/package.json

@@ -1,21 +0,0 @@
-{
-  "name": "test-e2e",
-  "version": "1.0.0",
-  "description": "End-to-end tests for the desec-stack",
-  "main": "index.js",
-  "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
-  },
-  "author": "Nils Wisiol",
-  "license": "MIT",
-  "dependencies": {
-    "mocha": "~3.5.0",
-    "jquery": "~3.5.0",
-    "jsdom": "~11.1.0",
-    "atob": "^2.0.3",
-    "btoa": "^1.1.2",
-    "q": "~1.5.0",
-    "dns-packet": "~5.2.2",
-    "tv4-formats": "~3.0.3"
-  }
-}

+ 0 - 108
test/e2e/schemas.js

@@ -1,108 +0,0 @@
-// For format specs, see https://json-schema.org/latest/json-schema-validation.html#rfc.section.7.3
-
-exports.rootNoLogin = {
-    properties: {
-        login: { type: "string" },
-        register: { type: "string" },
-    }
-};
-
-exports.user = {
-    properties: {
-        created: {
-            type: "string",
-            format: "date-time"
-        },
-        email: {
-            type: "string",
-            format: "email"
-        },
-        id: {
-            type: "string",
-            format: "uuid"
-        },
-        limit_domains: { type: "integer" },
-    },
-    required: ["created", "email", "id", "limit_domains"]
-};
-
-exports.domain = {
-    properties: {
-        keys: {
-            type: "array",
-            items: {
-                properties: {
-                    dnskey:  { type: "string" },
-                    ds: {
-                        type: "array",
-                        items: { type: "string" },
-                        minItems: 1
-                    },
-                    flags:  { type: "integer" },
-                    keytype:  { type: "string" },
-                }
-            },
-            minItems: 1
-        },
-        name: {
-            type: "string",
-            format: "hostname"
-        },
-        created: {
-            type: "string",
-            format: "date-time"
-        },
-        published: {
-            type: "string",
-            format: "date-time"
-        },
-    },
-    required: ["name", "keys", "created", "published"]
-};
-
-exports.rrset = {
-    properties: {
-        created: {
-            type: "string",
-            format: "date-time"
-        },
-        domain: { type: "string" },
-        subname: { type: "string" },
-        name: { type: "string" },
-        records: {
-            type: "array",
-            items: { type: "string" },
-            minItems: 1
-        },
-        ttl: {
-            type: "integer",
-            minimum: 1
-        },
-        type: { type: "string" },
-    },
-    required: ["created", "domain", "subname", "name", "records", "ttl", "type"]
-};
-
-exports.rrsets = {
-    type: "array",
-    items: exports.rrset
-};
-
-exports.token = {
-    properties: {
-        name: { type: "string" },
-        created: { type: "string" },
-        id: {
-            type: "string",
-            format: "uuid"
-        },
-    },
-    required: ["name", "created", "id"]
-};
-
-exports.tokens = {
-    type: "array",
-    items: exports.token
-};
-
-exports.TOKEN_REGEX = /^[A-Za-z0-9_-]{28}$/

+ 0 - 247
test/e2e/setup.js

@@ -1,247 +0,0 @@
-var assert = require('assert');
-var schemas = require("./schemas.js");
-
-process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
-
-var chakram = require('/usr/local/lib/node_modules/chakram/lib/chakram.js');
-var Q = require('q');
-
-var packet = require('dns-packet');
-var dgram = require('dgram');
-var socket = dgram.createSocket('udp4');
-after("tear down udp4 socket", function () { socket.close(); });
-
-chakram.addMethod("body", function (respObj, expected) {
-    var body = respObj.body;
-
-    if (arguments.length === 1) {
-        this.assert(
-            body !== undefined && body !== null,
-            'expected body to exist',
-            'expected not to exist'
-        );
-    } else if (expected instanceof RegExp) {
-        this.assert(
-            expected.test(body),
-            'expected body with value ' + body + ' to match regex ' + expected,
-            'expected body with value ' + body + ' not to match regex ' + expected
-        );
-    } else if (typeof(expected) === 'function') {
-        expected(body);
-    } else {
-        this.assert(
-            body === expected,
-            'expected body with value ' + body + ' to match ' + expected,
-            'expected body with value ' + body + ' not to match ' + expected
-        );
-    }
-});
-
-var settings = {
-    headers: {},
-    followRedirect: false,
-    baseUrl: '',
-};
-
-chakram.getRequestSettings = function () {
-    return settings;
-};
-
-chakram.setRequestSettings = function (s) {
-    settings = s;
-    chakram.setRequestDefaults(settings);
-};
-chakram.setRequestSettings(settings);
-
-chakram.setBaseUrl = function (url) {
-    var s = chakram.getRequestSettings();
-    s.baseUrl = url;
-    chakram.setRequestSettings(s);
-};
-
-chakram.setRequestHeader = function (header, value) {
-    var s = chakram.getRequestSettings();
-    s.headers[header] = value;
-    chakram.setRequestSettings(s);
-};
-
-// Make JSON format validators available
-chakram.addSchemaFormat(require('tv4-formats'))
-
-/*
- * DNS Resolver Testing
- */
-
-// This structure will hold outstanding requests that
-// we're are currently awaiting a response for.
-// Note that there is no timeout mechanism; requests
-// that (for any reason) do not receive responses will
-// rot here indefinitely.
-// Format: request id -> promise
-var inflight = {};
-var nextId = 1;
-
-// Receive messages on our udp4 socket
-socket.on('message', function (message) {
-  try {
-      // decode reply, find promise, remove from inflight and resolve
-      var response = packet.decode(message);
-      var promise = inflight[response.id];
-      delete inflight[response.id];
-      if (response.rcode !== 'NOERROR' && response.rcode !== 'NXDOMAIN') {
-          promise.reject(response);
-      } else {
-          promise.resolve(response);
-      }
-  } catch (e) {
-      // ignore faulty packets
-  }
-});
-
-/**
- * Returns a promise that will eventually be resolved into an object representing nslord's answer for the RR set of
- * given name and type. For information about the object structure, check https://github.com/mafintosh/dns-packet.
- * In case of error, the promise is rejected and the dns-packet is given without further processing.
- * @param name full qualified domain name (no trailing dot)
- * @param type rrset type
- * @returns {promise|*|jQuery.promise|Promise}
- */
-chakram.resolve = function (name, type) {
-    var deferred = Q.defer();
-
-    var buf = packet.encode({
-        type: 'query',
-        id: nextId,
-        questions: [{
-            type: type,
-            class: 'IN',
-            name: name
-        }]
-    });
-
-    // FIXME contacting nslord for DNS responses. This can changed to nsmaster as soon as changes to the DNS are applied
-    // immediately, i.e. before pdns HTTP responses return to the API.
-    socket.send(buf, 0, buf.length, 53, process.env.DESECSTACK_IPV4_REAR_PREFIX16 + '.0.129');
-    inflight[nextId] = deferred;
-    nextId = (nextId + 1) % 65536;  // We don't care if id's are predictable in our test setting
-
-    return deferred.promise;
-};
-
-/**
- * Returns a promise that will eventually be resolved into an array of strings, representing nslord's answer for the
- * RR set of given name and type. Note that not all record type are supported. Unsupported record types will be given
- * in javascripts default representation, which is most likely an unusable mess.
- * In case of error, the promise is rejected and the dns-packet is given without further processing. For further
- * information on the structure of this object, check https://github.com/mafintosh/dns-packet.
- * @param name full qualified domain name (no trailing dot)
- * @param type rrset type
- * @returns {promise|*|jQuery.promise|Promise}
- */
-chakram.resolveStr = function (name, type) {
-    var deferred = Q.defer();
-
-    function convert (data, type) {
-        switch (type) {
-            case 'A': return data;
-            case 'AAAA': return data;
-            case 'MX': return data.preference + ' ' + data.exchange + '.';
-            case 'TXT': return '"' + data + '"';
-            // extend as needed
-            default: return data.toString();  // uh-oh, messy & ugly
-        }
-    }
-
-    chakram.resolve(name, type)
-        .then(function (respObj) {
-            var repr = [];
-
-            // convert to str
-            for (var a of respObj.answers) {
-                repr.push(convert(a.data, type));
-            }
-
-            deferred.resolve(repr);
-        })
-        .catch(function (error) {
-            deferred.reject(error);
-        });
-
-    return deferred.promise;
-};
-
-/**
- * A chainable property that does nothing. Can be used to increase readability of expectations.
- */
-chakram.addProperty("dns", function(){});
-
-/**
- * A shorthand for checking the TTL of an RR set.
- */
-chakram.addMethod("ttl", function (respObj, expected) {
-    this.assert(respObj.rcode === 'NOERROR', 'expected response to have rcode NOERROR');
-    this.assert(respObj.answers.length > 0, 'expected response to have answers');
-    this.assert(respObj.answers.every(function(elem) { return elem.ttl === parseInt(expected); }),
-        'TTL of at least one answer in the DNS packet didn\'t match expected value of ' + expected + ': ' +
-        respObj.answers.map(x => x.ttl));
-});
-
-exports.chakram = chakram;
-
-/**
- * A test that checks record contents by querying the API endpoint
- */
-function itPropagatesToTheApi(rrsets) {
-    return it("propagates to the API", function () {
-        promises = rrsets.map(function (rrset) {
-            return chakram
-                .get('/domains/' + rrset.domain + '/rrsets/' + rrset.subname + '.../' + rrset.type + '/')
-                .then(function (response) {
-                    if(rrset.records.length) {
-                        chakram.expect(response).to.have.status(200);
-                        chakram.expect(response).to.have.schema(schemas.rrset);
-                        chakram.expect(response.body.records).to.have.members(rrset.records);
-                        if (typeof rrset.ttl !== "undefined") {
-                            chakram.expect(response).to.have.json('ttl', rrset.ttl);
-                        }
-                    } else {
-                        assert(typeof rrset.ttl == "undefined");
-                        chakram.expect(response).to.have.status(404);
-                    }
-                });
-        });
-        return chakram.waitFor(promises);
-    });
-}
-
-/**
- * A test that checks DNS record contents
- */
-function itShowsUpInPdnsAs(subname, domain, type, records, ttl) {
-    var queryName = (typeof subname == 'undefined' ? '' : subname + '.') + domain;
-    return it('shows up correctly in pdns for ' + subname + '|' + domain + '|' + type, function () {
-        chakram.expect(chakram.resolveStr(queryName, type)).to.have.lengthOf(records.length);
-        chakram.expect(chakram.resolveStr(queryName, type)).to.have.members(records);
-        chakram.expect(chakram.resolveStr(queryName, type)).to.have.members(records.reverse());
-        if (typeof ttl !== "undefined") {
-            chakram.expect(chakram.resolve(queryName, type)).to.have.dns.ttl(ttl);
-        }
-        return chakram.wait();
-    });
-}
-
-/**
- * Injects a CAPTCHA with correct solution
- */
-function withCaptcha(f) {
-    return chakram.post('/captcha/', {}).then(function (response) {
-        return f({
-            "id": response.body.id,
-            "solution": response.body.content,
-        });
-    });
-}
-
-exports.itPropagatesToTheApi = itPropagatesToTheApi;
-exports.itShowsUpInPdnsAs = itShowsUpInPdnsAs;
-exports.withCaptcha = withCaptcha;

+ 0 - 1190
test/e2e/spec/api_spec.js

@@ -1,1190 +0,0 @@
-var chakram = require("./../setup.js").chakram;
-var expect = chakram.expect;
-var itPropagatesToTheApi = require("./../setup.js").itPropagatesToTheApi;
-var itShowsUpInPdnsAs = require("./../setup.js").itShowsUpInPdnsAs;
-var schemas = require("./../schemas.js");
-var withCaptcha = require("./../setup.js").withCaptcha;
-
-describe("API Versioning", function () {
-
-    before(function () {
-        chakram.setRequestDefaults({
-            headers: {
-                'Host': 'desec.' + process.env.DESECSTACK_DOMAIN,
-            },
-            followRedirect: false,
-            baseUrl: 'https://www/api',
-        })
-    });
-
-    [
-        'v1',
-        'v2',
-    ].forEach(function (version) {
-        it("maintains the requested version " + version, function() {
-            chakram.get('/' + version + '/').then(function (response) {
-                expect(response).to.have.schema(schemas.rootNoLogin);
-                let regex = new RegExp('https://[^/]+/api/' + version + '/auth/login/', 'g')
-                expect(response.body.login).to.match(regex);
-                return chakram.wait();
-            });
-        });
-    })
-
-});
-
-describe("API v1", function () {
-    this.timeout(3000);
-
-    let publicSuffix = 'dedyn.' + process.env.DESECSTACK_DOMAIN;  // see settings.py
-
-    before(function () {
-        chakram.setRequestDefaults({
-            headers: {
-                'Host': 'desec.' + process.env.DESECSTACK_DOMAIN,
-            },
-            followRedirect: false,
-            baseUrl: 'https://www/api/v1',
-        })
-
-        // ensure that the public suffix domain is set up and ready to use
-        let email = 'admin@example.com';
-        let password = 'admin123!';
-        return withCaptcha(function (captcha) {
-            return chakram.post('/auth/', {
-                    "email": email,
-                    "password": password,
-                    "captcha": captcha,
-                }).then(function (registerResponse) {
-                    return chakram.post('/auth/login/', {
-                        "email": email,
-                        "password": password,
-                    }).then(function (loginResponse) {
-                        return chakram.post('/domains/', {
-                            name: publicSuffix,
-                        }, {
-                            headers: {'Authorization': 'Token ' + loginResponse.body.token }
-                        }); // note that we ignore errors here
-                    });
-            });
-        });
-    });
-
-    it("has HSTS header", function () {
-        var response = chakram.get('/');
-        expect(response).to.have.header('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload');
-        return chakram.wait();
-    });
-
-    it("has CORS headers", function () {
-        return chakram.options('/', {headers: {'Origin': 'http://foo.example' }}).then(function (response) {
-            expect(response).to.have.header('access-control-allow-origin', '*');
-            expect(response).to.have.header('access-control-allow-headers', /.*authorization.*/);
-            return chakram.wait();
-        });
-    });
-
-    describe("user registration", function () {
-
-        var captcha;
-
-        before(function () {
-            return withCaptcha(function (_captcha) {
-                captcha = _captcha;
-            });
-        });
-
-        it("returns a user object", function () {
-            var email, password, token;
-
-            email = require("uuid").v4() + '@e2etest.local';
-            password = require("uuid").v4();
-
-            var response = chakram.post('/auth/', {
-                "email": email,
-                "password": password,
-                "captcha": captcha,
-            });
-
-            return expect(response).to.have.status(202);
-        });
-    });
-
-    describe("user account", function () {
-
-        var email, password;
-
-        before(function () {
-
-            // register a user that we can work with
-            email = require("uuid").v4() + '@e2etest.local';
-            password = require("uuid").v4();
-
-            let response = withCaptcha(function (captcha) {
-                return chakram.post('/auth/', {
-                    "email": email,
-                    "password": password,
-                    "captcha": captcha,
-                });
-            });
-
-            return expect(response).to.have.status(202);
-        });
-
-        it("returns a token when logging in", function () {
-            return chakram.post('/auth/login/', {
-                "email": email,
-                "password": password,
-            }).then(function (loginResponse) {
-                expect(loginResponse.body.token).to.match(schemas.TOKEN_REGEX);
-            });
-        });
-
-        describe("auth/account/ endpoint", function () {
-            var email2, password2, token2;
-
-            before(function () {
-                // register an independent user to screw around with
-                email2 = require("uuid").v4() + '@e2etest.local';
-                password2 = require("uuid").v4();
-
-                return withCaptcha(function (captcha) {
-                    return chakram.post('/auth/', {
-                        "email": email2,
-                        "password": password2,
-                        "captcha": captcha,
-                    }).then(function () {
-                        return chakram.post('/auth/login/', {
-                            "email": email2,
-                            "password": password2,
-                        }).then(function (response) {
-                            token2 = response.body.token
-                        });
-                    });
-                });
-            });
-
-            it("returns JSON of correct schema", function () {
-                var response = chakram.get('/auth/account/', {
-                    headers: {'Authorization': 'Token ' + token2 }
-                });
-                expect(response).to.have.status(200);
-                expect(response).to.have.schema(schemas.user);
-                return chakram.wait();
-            });
-
-            it("allows triggering change email process", function () {
-                return chakram.post('/auth/account/change-email/', {
-                    "email": email2,
-                    "password": password2,
-                    "new_email": require("uuid").v4() + '@e2etest.local',
-                }).then(function (response) {
-                    expect(response).to.have.status(202);
-                });
-            });
-        });
-    });
-
-    var email = require("uuid").v4() + '@e2etest.local';
-    describe("with user account [" + email + "]", function () {
-
-        var apiHomeSchema = {
-            properties: {
-                domains: {type: "string"},
-                tokens: {type: "string"},
-                account: {type: "object"},
-            },
-            required: ["domains", "tokens", "account"]
-        };
-
-        var password, token;
-
-        before(function () {
-            chakram.setRequestSettings({
-                headers: {
-                    'Host': 'desec.' + process.env.DESECSTACK_DOMAIN,
-                },
-                followRedirect: false,
-                baseUrl: 'https://www/api/v1',
-            });
-
-            // register a user that we can login and work with
-            password = require("uuid").v4();
-
-            return withCaptcha(function (captcha) {
-                return chakram.post('/auth/', {
-                    "email": email,
-                    "password": password,
-                    "captcha": captcha,
-                }).then(function () {
-                    return chakram.post('/auth/login/', {
-                        "email": email,
-                        "password": password,
-                    }).then(function (loginResponse) {
-                        expect(loginResponse.body.token).to.match(schemas.TOKEN_REGEX);
-                        token = loginResponse.body.token;
-                        chakram.setRequestHeader('Authorization', 'Token ' + token);
-                    });
-                });
-            });
-        });
-
-        describe("(logged in)", function () {
-
-            describe("api 'homepage'", function () {
-
-                var response;
-
-                before(function () {
-                    return chakram.get('/').then(function (_response) {
-                        response = _response;
-                    });
-                });
-
-                it('has status 200', function () {
-                    return expect(response).to.have.status(200);
-                });
-
-                it('looks according to the schema', function () {
-                    return expect(response).to.have.schema(apiHomeSchema);
-                });
-
-            });
-
-            describe("on domains/ endpoint", function () {
-
-                var domain = 'e2etest-' + require("uuid").v4() + '.' + publicSuffix;
-                before(function () {
-                    return expect(chakram.post('/domains/', {'name': domain})).to.have.status(201);
-                });
-
-                it("can register a domain name", function () {
-                    var response = chakram.get('/domains/' + domain + '/');
-                    expect(response).to.have.status(200);
-                    expect(response).to.have.schema(schemas.domain);
-                    return chakram.wait();
-                });
-
-                itShowsUpInPdnsAs('', domain, 'NS', process.env.DESECSTACK_NS.split(/\s+/),  process.env.DESECSTACK_NSLORD_DEFAULT_TTL);
-
-                describe("on rrsets/ endpoint", function () {
-                    it("can retrieve RRsets", function () {
-                        var response = chakram.get('/domains/' + domain + '/rrsets/');
-                        expect(response).to.have.status(200);
-                        expect(response).to.have.schema(schemas.rrsets);
-
-                        response = chakram.get('/domains/' + domain + '/rrsets/.../NS/');
-                        expect(response).to.have.status(200);
-                        expect(response).to.have.schema(schemas.rrset);
-
-                        response = chakram.get('/domains/' + domain + '/rrsets/@/NS/');
-                        expect(response).to.have.status(200);
-                        expect(response).to.have.schema(schemas.rrset);
-
-                        return chakram.wait();
-                    });
-                });
-            });
-
-            describe('POST rrsets/ with fresh domain', function () {
-
-                var domain = 'e2etest-' + require("uuid").v4() + '.' + publicSuffix;
-                before(function () {
-                    return expect(chakram.post('/domains/', {'name': domain})).to.have.status(201);
-                });
-
-                describe("can set an A RRset", function () {
-                    before(function () {
-                        var response = chakram.post(
-                            '/domains/' + domain + '/rrsets/',
-                            {'subname': '', 'type': 'A', 'records': ['127.0.0.1'], 'ttl': 60}
-                        );
-                        expect(response).to.have.status(201);
-                        expect(response).to.have.schema(schemas.rrset);
-                        expect(response).to.have.json('ttl', 60);
-                        expect(response).to.have.json('records', ['127.0.0.1']);
-                        return chakram.wait();
-                    });
-
-                    itPropagatesToTheApi([
-                        {subname: '', domain: domain, type: 'A', ttl: 60, records: ['127.0.0.1']},
-                    ]);
-
-                    itShowsUpInPdnsAs('', domain, 'A', ['127.0.0.1'], 60);
-                });
-
-                describe("cannot create RRsets of restricted or dead type", function () {
-
-                    var rrTypes = ['ALIAS', 'SOA', 'RRSIG', 'DNSKEY', 'NSEC3PARAM', 'OPT'];
-                    for (var i = 0; i < rrTypes.length; i++) {
-                        var rrType = rrTypes[i];
-                        it(rrType, function () {
-                            return expect(chakram.post(
-                                    '/domains/' + domain + '/rrsets/',
-                                    {'subname': 'not-welcome', 'type': rrType, 'records': ['127.0.0.1'], 'ttl': 60}
-                                )).to.have.status(400);
-                        });
-                    }
-
-                });
-
-                it("cannot update RRSets for nonexistent domain name", function () {
-                    return expect(chakram.patch(
-                            '/domains/nonexistent.e2e.domain/rrsets/',
-                            {'subname': '', 'type': 'A', 'records': ['127.0.0.1'], 'ttl': 60}
-                        )).to.have.status(404);
-                });
-
-                it("cannot create RRSets for nonexistent domain name", function () {
-                    return expect(chakram.post(
-                            '/domains/nonexistent.e2e.domain/rrsets/',
-                            {'subname': '', 'type': 'A', 'records': ['127.0.0.1'], 'ttl': 60}
-                        )).to.have.status(404);
-                });
-
-                it("cannot set unicode RRsets", function () {
-                    return expect(chakram.post(
-                            '/domains/' + domain + '/rrsets/',
-                            {'subname': '想不出来', 'type': 'A', 'records': ['127.0.0.1'], 'ttl': 60}
-                        )).to.have.status(400);
-                });
-
-                describe("can set a wildcard AAAA RRset with multiple records", function () {
-                    before(function () {
-                        return chakram.post(
-                            '/domains/' + domain + '/rrsets/',
-                            {'subname': '*.foobar', 'type': 'AAAA', 'records': ['::1', 'bade::affe'], 'ttl': 60}
-                        );
-                    });
-
-                    itPropagatesToTheApi([
-                        {subname: '*.foobar', domain: domain, type: 'AAAA', ttl: 60, records: ['::1', 'bade::affe']},
-                        {subname: '*.foobar', domain: domain, type: 'AAAA', records: ['bade::affe', '::1']},
-                    ]);
-
-                    itShowsUpInPdnsAs('test.foobar', domain, 'AAAA', ['::1', 'bade::affe'], 60);
-                });
-
-                describe("cannot create RRsets with duplicate record content", function () {
-                    it("rejects exact duplicates", function () {
-                        return expect(chakram.post(
-                            '/domains/' + domain + '/rrsets/',
-                            {
-                                'subname': 'duplicate-contents', 'type': 'AAAA',
-                                'records': ['::1', '::1'], 'ttl': 60
-                            }
-                        )).to.have.status(400);
-                    });
-
-                    it("rejects semantic duplicates", function () {
-                        return expect(chakram.post(
-                            '/domains/' + domain + '/rrsets/',
-                            {
-                                'subname': 'duplicate-contents', 'type': 'AAAA',
-                                'records': ['::1', '::0001'], 'ttl': 60
-                            }
-                        )).to.have.status(400);
-                    });
-
-                    describe("even in subsequent requests", function () {
-                        before(function() {
-                            return expect(chakram.post(
-                                '/domains/' + domain + '/rrsets/',
-                                {
-                                    'subname': 'duplicate-contents', 'type': 'AAAA',
-                                    'records': ['::1'], 'ttl': 60
-                                }
-                            )).to.have.status(201);
-                        });
-
-                        it("still does not accept a semantic duplicate", function () {
-                            return expect(chakram.post(
-                                '/domains/' + domain + '/rrsets/',
-                                {
-                                    'subname': 'duplicate-contents', 'type': 'AAAA',
-                                    'records': ['::0001'], 'ttl': 60
-                                }
-                            )).to.have.status(400);
-                        });
-
-                        it("still does not accept a semantic duplicates", function () {
-                            return expect(chakram.post(
-                                '/domains/' + domain + '/rrsets/',
-                                {
-                                    'subname': 'duplicate-contents', 'type': 'AAAA',
-                                    'records': ['::1', '::0001'], 'ttl': 60
-                                }
-                            )).to.have.status(400);
-                        });
-                    })
-                });
-
-                describe("can bulk-post an AAAA and an MX record", function () {
-                    before(function () {
-                        var response = chakram.post(
-                            '/domains/' + domain + '/rrsets/',
-                            [
-                                { 'subname': 'ipv6', 'type': 'AAAA', 'records': ['dead::beef'], 'ttl': 3622 },
-                                { 'subname': '', 'type': 'MX', 'records': ['10 mail.example.com.', '20 mail.example.net.'], 'ttl': 3633 }
-                            ]
-                        );
-                        expect(response).to.have.status(201);
-                        expect(response).to.have.schema(schemas.rrsets);
-                        return chakram.wait();
-                    });
-
-                    itPropagatesToTheApi([
-                        {subname: 'ipv6', domain: domain, type: 'AAAA', ttl: 3622, records: ['dead::beef']},
-                        {subname: '', domain: domain, type: 'MX', ttl: 3633, records: ['10 mail.example.com.', '20 mail.example.net.']},
-                    ]);
-
-                    itShowsUpInPdnsAs('ipv6', domain, 'AAAA', ['dead::beef'], 3622);
-
-                    itShowsUpInPdnsAs('', domain, 'MX', ['10 mail.example.com.', '20 mail.example.net.'], 3633);
-                });
-
-                describe("cannot bulk-post with missing or invalid fields", function () {
-                    before(function () {
-                        // Set an RRset that we'll try to overwrite
-                        var response = chakram.post(
-                            '/domains/' + domain + '/rrsets/',
-                            {'ttl': 3650, 'type': 'TXT', 'records': ['"foo"']}
-                        );
-                        expect(response).to.have.status(201);
-
-                        var response = chakram.post(
-                            '/domains/' + domain + '/rrsets/',
-                            [
-                                {'subname': 'a.1', 'records': ['dead::beef'], 'ttl': 3622},
-                                {'subname': 'b.1', 'ttl': -50, 'type': 'AAAA', 'records': ['dead::beef']},
-                                {'ttl': 3640, 'type': 'TXT', 'records': ['"bar"']},
-                                {'subname': 'c.1', 'records': ['dead::beef'], 'type': 'AAAA'},
-                                {'subname': 'd.1', 'ttl': 3650, 'type': 'AAAA'},
-                                {'subname': 'd.1', 'ttl': 3650, 'type': 'SOA', 'records': ['get.desec.io. get.desec.io. 2018034419 10800 3600 604800 60']},
-                                {'subname': 'd.1', 'ttl': 3650, 'type': 'OPT', 'records': ['9999']},
-                                {'subname': 'd.1', 'ttl': 3650, 'type': 'TYPE099', 'records': ['v=spf1 mx -all']},
-                            ]
-                        );
-                        expect(response).to.have.status(400);
-                        expect(response).to.have.json([
-                            { type: [ 'This field is required.' ] },
-                            { ttl: [ 'Ensure this value is greater than or equal to 60.' ] },
-                            { subname: [ 'This field is required.' ] },
-                            { ttl: [ 'This field is required.' ] },
-                            { records: [ 'This field is required.' ] },
-                            { type: [ 'You cannot tinker with the SOA RR set. It is managed automatically.' ] },
-                            { type: [ 'You cannot tinker with the OPT RR set. It is managed automatically.' ] },
-                            { type: [ 'Generic type format is not supported.' ] },
-                        ]);
-
-                        return chakram.wait();
-                    });
-
-                    it("does not propagate partially to the API", function () {
-                        return chakram.waitFor([
-                            chakram
-                                .get('/domains/' + domain + '/rrsets/b.1.../AAAA/')
-                                .then(function (response) {
-                                    expect(response).to.have.status(404);
-                                }),
-                            chakram
-                                .get('/domains/' + domain + '/rrsets/.../TXT/')
-                                .then(function (response) {
-                                    expect(response).to.have.status(200);
-                                    expect(response).to.have.json('ttl', 3650);
-                                    expect(response.body.records).to.have.members(['"foo"']);
-                                }),
-                            ]);
-                    });
-
-                    itShowsUpInPdnsAs('b.1', domain, 'AAAA', []);
-                });
-
-                context("with a pre-existing RRset", function () {
-                    before(function () {
-                        var response = chakram.post(
-                            '/domains/' + domain + '/rrsets/',
-                            [
-                                {'subname': 'a.2', 'ttl': 3650, 'type': 'TXT', 'records': ['"foo"']},
-                                {'subname': 'c.2', 'ttl': 3650, 'type': 'TXT', 'records': ['"foo"']},
-                                {'subname': 'delete-test', 'ttl': 3650, 'type': 'A', 'records': ['127.1.2.3']},
-                                {'subname': 'replace-test-1', 'ttl': 3650, 'type': 'AAAA', 'records': ['::1', '::2']},
-                                {'subname': 'replace-test-2', 'ttl': 3650, 'type': 'AAAA', 'records': ['::1', '::2']},
-                            ]
-                        );
-                        return expect(response).to.have.status(201);
-                    });
-
-                    describe("can delete an RRset", function () {
-                        before(function () {
-                            var response = chakram.delete('/domains/' + domain + '/rrsets/delete-test.../A/');
-                            return expect(response).to.have.status(204);
-                        });
-
-                        itPropagatesToTheApi([
-                            {subname: 'delete-test', domain: domain, type: 'A', records: []},
-                        ]);
-
-                        itShowsUpInPdnsAs('delete-test', domain, 'A', []);
-                    });
-
-                    describe("can be replaced with a CNAME record", function () {
-                        before(function () {
-                            var response = chakram.put(
-                                '/domains/' + domain + '/rrsets/',
-                                [
-                                    {'subname': 'replace-test-1', 'ttl': 3650, 'type': 'AAAA', 'records': []},
-                                    {'subname': 'replace-test-1', 'ttl': 3601, 'type': 'CNAME', 'records': ['example.com.']},
-                                ]
-                            );
-                            return expect(response).to.have.status(200);
-                        });
-
-                        itPropagatesToTheApi([
-                            {subname: 'replace-test-1', domain: domain, type: 'AAAA', records: []},
-                            {subname: 'replace-test-1', domain: domain, type: 'CNAME', records: ["example.com."]},
-                        ]);
-
-                        itShowsUpInPdnsAs('replace-test-1', domain, 'AAAA', ["example.com"]);
-                        itShowsUpInPdnsAs('replace-test-1', domain, 'CNAME', ["example.com"]);
-                    });
-
-                    describe("cannot be replaced with a malformed CNAME record", function () {
-                        before(function () {
-                            var response = chakram.put(
-                                '/domains/' + domain + '/rrsets/',
-                                [
-                                    {'subname': 'replace-test-2', 'ttl': 3650, 'type': 'AAAA', 'records': []},
-                                    {'subname': 'replace-test-2', 'ttl': 3601, 'type': 'CNAME', 'records': ['no.trailing.dot']},
-                                ]
-                            );
-                            return expect(response).to.have.status(400);
-                        });
-
-                        itPropagatesToTheApi([
-                            {subname: 'replace-test-2', domain: domain, type: 'AAAA', records: ["::1", "::2"]},
-                            {subname: 'replace-test-2', domain: domain, type: 'CNAME', records: []},
-                        ]);
-
-                        itShowsUpInPdnsAs('replace-test-2', domain, 'AAAA', ["::1", "::2"]);
-                        itShowsUpInPdnsAs('replace-test-2', domain, 'CNAME', []);
-                    });
-
-                    describe("cannot bulk-post existing or duplicate RRsets", function () {
-                        var response;
-
-                        before(function () {
-                            response = chakram.post(
-                                '/domains/' + domain + '/rrsets/',
-                                [
-                                    {'subname': 'a.2', 'ttl': 3640, 'type': 'TXT', 'records': ['"bar"']},
-                                    {'subname': 'a.2', 'ttl': 3640, 'type': 'TXT', 'records': ['"bar"']},
-                                ]
-                            );
-                            expect(response).to.have.status(400);
-                            return chakram.wait();
-                        });
-
-                        it("gives the right response", function () {
-                            expect(response).to.have.json([
-                                {"non_field_errors": ["Same subname and type as in position(s) 1, but must be unique."]},
-                                {"non_field_errors": ["Same subname and type as in position(s) 0, but must be unique."]}
-                            ]);
-                            return chakram.wait();
-                        });
-
-                        it("does not touch records in the API", function () {
-                            return chakram
-                                .get('/domains/' + domain + '/rrsets/a.2.../TXT/')
-                                .then(function (response) {
-                                    expect(response).to.have.status(200);
-                                    expect(response).to.have.json('ttl', 3650);
-                                    expect(response.body.records).to.have.members(['"foo"']);
-                                });
-                        });
-
-                        itShowsUpInPdnsAs('a.2', domain, 'TXT', ['"foo"'], 3650);
-                    });
-
-                    describe("cannot delete RRsets via bulk-post", function () {
-                        var response;
-
-                        before(function () {
-                            response = chakram.post(
-                                '/domains/' + domain + '/rrsets/',
-                                [
-                                    {'subname': 'c.2', 'ttl': 3640, 'type': 'TXT', 'records': []},
-                                ]
-                            );
-                            return expect(response).to.have.status(400);
-                        });
-
-                        it("gives the right response", function () {
-                            return expect(response).to.have.json([
-                                {'records': ['This field must not be empty when using POST.']},
-                            ]);
-                        });
-                    });
-                });
-
-                describe("cannot bulk-post with invalid input", function () {
-                    it("gives the right response for invalid type", function () {
-                        var response = chakram.post(
-                            '/domains/' + domain + '/rrsets/',
-                            [{'subname': 'a.2', 'ttl': 3650, 'type': 'INVALID', 'records': ['"foo"']}]
-                        );
-                        return expect(response).to.have.status(400);
-                    });
-
-                    it("gives the right response for invalid records", function () {
-                        var response = chakram.post(
-                            '/domains/' + domain + '/rrsets/',
-                            [{'subname': 'a.2', 'ttl': 3650, 'type': 'MX', 'records': ['1.2.3.4']}]
-                        );
-                        return expect(response).to.have.status(400);
-                    });
-
-                    it("gives the right response for records contents being null", function () {
-                        var response = chakram.post(
-                            '/domains/' + domain + '/rrsets/',
-                            [{'subname': 'a.2', 'ttl': 3650, 'type': 'MX', 'records': ['1.2.3.4', null]}]
-                        );
-                        return expect(response).to.have.status(400);
-                    });
-                });
-
-            });
-
-            describe('PUT rrsets/ with fresh domain', function () {
-
-                var domain = 'e2etest-' + require("uuid").v4() + '.' + publicSuffix;
-                before(function () {
-                    return expect(chakram.post('/domains/', {'name': domain})).to.have.status(201);
-                });
-
-                describe("can overwrite a single existing RRset using PUT", function () {
-                    before(function () {
-                        var response = chakram.post(
-                            '/domains/' + domain + '/rrsets/',
-                            { 'subname': 'single', 'type': 'AAAA', 'records': ['bade::fefe'], 'ttl': 3662 }
-                        ).then(function () {
-                            return chakram.put(
-                                '/domains/' + domain + '/rrsets/single.../AAAA/',
-                                { 'subname': 'single', 'type': 'AAAA', 'records': ['fefe::bade'], 'ttl': 3631 }
-                            );
-                        });
-                        expect(response).to.have.status(200);
-                        expect(response).to.have.schema(schemas.rrset);
-                        return chakram.wait();
-                    });
-
-                    itPropagatesToTheApi([
-                        {subname: 'single', domain: domain, type: 'AAAA', ttl: 3631, records: ['fefe::bade']},
-                    ]);
-
-                    itShowsUpInPdnsAs('single', domain, 'AAAA', ['fefe::bade'], 3631);
-                });
-
-                describe("can bulk-put an AAAA and an MX record", function () {
-                    before(function () {
-                        var response = chakram.put(
-                            '/domains/' + domain + '/rrsets/',
-                            [
-                                { 'subname': 'ipv6', 'type': 'AAAA', 'records': ['dead::beef'], 'ttl': 3622 },
-                                { 'subname': '', 'type': 'MX', 'records': ['10 mail.example.com.', '20 mail.example.net.'], 'ttl': 3633 }
-                            ]
-                        );
-                        expect(response).to.have.status(200);
-                        expect(response).to.have.schema(schemas.rrsets);
-                        return chakram.wait();
-                    });
-
-                    itPropagatesToTheApi([
-                        {subname: 'ipv6', domain: domain, type: 'AAAA', ttl: 3622, records: ['dead::beef']},
-                        {subname: '', domain: domain, type: 'MX', ttl: 3633, records: ['10 mail.example.com.', '20 mail.example.net.']},
-                    ]);
-
-                    itShowsUpInPdnsAs('ipv6', domain, 'AAAA', ['dead::beef'], 3622);
-
-                    itShowsUpInPdnsAs('', domain, 'MX', ['10 mail.example.com.', '20 mail.example.net.'], 3633);
-                });
-
-                describe("cannot bulk-put with missing or invalid fields", function () {
-                    before(function () {
-                        // Set an RRset that we'll try to overwrite
-                        var response = chakram.post(
-                            '/domains/' + domain + '/rrsets/',
-                            {'ttl': 3650, 'type': 'TXT', 'records': ['"foo"']}
-                        );
-                        expect(response).to.have.status(201);
-
-                        var response = chakram.put(
-                            '/domains/' + domain + '/rrsets/',
-                            [
-                                {'subname': 'a.1', 'records': ['dead::beef'], 'ttl': 3622},
-                                {'subname': 'b.1', 'ttl': -50, 'type': 'AAAA', 'records': ['dead::beef']},
-                                {'ttl': 3640, 'type': 'TXT', 'records': ['"bar"']},
-                                {'subname': 'c.1', 'records': ['dead::beef'], 'type': 'AAAA'},
-                                {'subname': 'd.1', 'ttl': 3650, 'type': 'AAAA'},
-                            ]
-                        );
-                        expect(response).to.have.status(400);
-                        expect(response).to.have.json([
-                            { type: [ 'This field is required.' ] },
-                            { ttl: [ 'Ensure this value is greater than or equal to 60.' ] },
-                            { subname: [ 'This field is required.' ] },
-                            { ttl: [ 'This field is required.' ] },
-                            { records: [ 'This field is required.' ] },
-                        ]);
-
-                        return chakram.wait();
-                    });
-
-                    it("does not propagate partially to the API", function () {
-                        return chakram.waitFor([
-                            chakram
-                                .get('/domains/' + domain + '/rrsets/b.1.../AAAA/')
-                                .then(function (response) {
-                                    expect(response).to.have.status(404);
-                                }),
-                            chakram
-                                .get('/domains/' + domain + '/rrsets/.../TXT/')
-                                .then(function (response) {
-                                    expect(response).to.have.status(200);
-                                    expect(response).to.have.json('ttl', 3650);
-                                    expect(response.body.records).to.have.members(['"foo"']);
-                                }),
-                            ]);
-                    });
-
-                    itShowsUpInPdnsAs('b.1', domain, 'AAAA', []);
-                });
-
-                context("with a pre-existing RRset", function () {
-                    before(function () {
-                        var response = chakram.post(
-                            '/domains/' + domain + '/rrsets/',
-                            [
-                                {'subname': 'a.2', 'ttl': 3650, 'type': 'TXT', 'records': ['"foo"']},
-                                {'subname': 'b.2', 'ttl': 3650, 'type': 'TXT', 'records': ['"foo"']},
-                                {'subname': 'c.2', 'ttl': 3650, 'type': 'A', 'records': ['1.2.3.4']},
-                            ]
-                        );
-                        expect(response).to.have.status(201);
-                        return chakram.wait();
-                    });
-
-                    describe("can bulk-put existing RRsets", function () {
-                        var response;
-
-                        before(function () {
-                            response = chakram.put(
-                                '/domains/' + domain + '/rrsets/',
-                                [
-                                    {'subname': 'a.2', 'ttl': 3640, 'type': 'TXT', 'records': ['"bar"']},
-                                ]
-                            );
-                            expect(response).to.have.status(200);
-                            expect(response).to.have.schema(schemas.rrsets);
-                            return chakram.wait();
-                        });
-
-                        it("does modify records in the API", function () {
-                            return chakram
-                                .get('/domains/' + domain + '/rrsets/a.2.../TXT/')
-                                .then(function (response) {
-                                    expect(response).to.have.status(200);
-                                    expect(response).to.have.json('ttl', 3640);
-                                    expect(response.body.records).to.have.members(['"bar"']);
-                                });
-                        });
-
-                        itShowsUpInPdnsAs('a.2', domain, 'TXT', ['"bar"'], 3640);
-                    });
-
-                    describe("cannot bulk-put duplicate RRsets", function () {
-                        var response;
-
-                        before(function () {
-                            response = chakram.put(
-                                '/domains/' + domain + '/rrsets/',
-                                [
-                                    {'subname': 'b.2', 'ttl': 3660, 'type': 'TXT', 'records': ['"bar"']},
-                                    {'subname': 'b.2', 'ttl': 3660, 'type': 'TXT', 'records': ['"bar"']},
-                                ]
-                            );
-                            return expect(response).to.have.status(400);
-                        });
-
-                        it("gives the right response", function () {
-                            return expect(response).to.have.json([
-                                { 'non_field_errors': [ 'Same subname and type as in position(s) 1, but must be unique.' ] },
-                                { 'non_field_errors': [ 'Same subname and type as in position(s) 0, but must be unique.' ] },
-                            ]);
-                        });
-
-                        it("does not touch records in the API", function () {
-                            return chakram
-                                .get('/domains/' + domain + '/rrsets/b.2.../TXT/')
-                                .then(function (response) {
-                                    expect(response).to.have.status(200);
-                                    expect(response).to.have.json('ttl', 3650);
-                                    expect(response.body.records).to.have.members(['"foo"']);
-                                });
-                        });
-
-                        itShowsUpInPdnsAs('b.2', domain, 'TXT', ['"foo"'], 3650);
-                    });
-
-                    describe("can delete RRsets via bulk-put", function () {
-                        var response;
-
-                        before(function () {
-                            response = chakram.put(
-                                '/domains/' + domain + '/rrsets/',
-                                [
-                                    {'subname': 'c.2', 'ttl': 3640, 'type': 'A', 'records': []},
-                                ]
-                            );
-                            return expect(response).to.have.status(200);
-                        });
-
-                        it("gives the right response", function () {
-                            var response = chakram.get('/domains/' + domain + '/rrsets/c.2.../A/');
-                            return expect(response).to.have.status(404);
-                        });
-                    });
-                });
-
-                describe("cannot bulk-put with invalid input", function () {
-                    it("gives the right response for invalid type", function () {
-                        var response = chakram.put(
-                            '/domains/' + domain + '/rrsets/',
-                            [{'subname': 'a.2', 'ttl': 3650, 'type': 'INVALID', 'records': ['"foo"']}]
-                        );
-                        return expect(response).to.have.status(400);
-                    });
-
-                    it("gives the right response for invalid records", function () {
-                        var response = chakram.put(
-                            '/domains/' + domain + '/rrsets/',
-                            [{'subname': 'a.2', 'ttl': 3650, 'type': 'MX', 'records': ['1.2.3.4']}]
-                        );
-                        return expect(response).to.have.status(400);
-                    });
-
-                    it("gives the right response for records contents being null", function () {
-                        var response = chakram.put(
-                            '/domains/' + domain + '/rrsets/',
-                            [{'subname': 'a.2', 'ttl': 3650, 'type': 'MX', 'records': ['1.2.3.4', null]}]
-                        );
-                        return expect(response).to.have.status(400);
-                    });
-                });
-
-            });
-
-            describe('PATCH rrsets/ with fresh domain', function () {
-
-                var domain = 'e2etest-' + require("uuid").v4() + '.' + publicSuffix;
-                before(function () {
-                    return expect(chakram.post('/domains/', {'name': domain})).to.have.status(201);
-                });
-
-                describe("can modify a single existing RRset using PATCH", function () {
-                    before(function () {
-                        var response = chakram.post(
-                            '/domains/' + domain + '/rrsets/',
-                            { 'subname': 'single', 'type': 'AAAA', 'records': ['bade::fefe'], 'ttl': 3662 }
-                        ).then(function () {
-                            return chakram.patch(
-                                '/domains/' + domain + '/rrsets/single.../AAAA/',
-                                { 'records': ['fefe::bade'], 'ttl': 3631 }
-                            );
-                        });
-                        expect(response).to.have.status(200);
-                        expect(response).to.have.schema(schemas.rrset);
-                        return chakram.wait();
-                    });
-
-                    itPropagatesToTheApi([
-                        {subname: 'single', domain: domain, type: 'AAAA', ttl: 3631, records: ['fefe::bade']},
-                    ]);
-
-                    itShowsUpInPdnsAs('single', domain, 'AAAA', ['fefe::bade'], 3631);
-                });
-
-                describe("can bulk-patch an AAAA and an MX record", function () {
-                    before(function () {
-                        var response = chakram.patch(
-                            '/domains/' + domain + '/rrsets/',
-                            [
-                                { 'subname': 'ipv6', 'type': 'AAAA', 'records': ['dead::beef'], 'ttl': 3622 },
-                                { 'subname': '', 'type': 'MX', 'records': ['10 mail.example.com.', '20 mail.example.net.'], 'ttl': 3633 }
-                            ]
-                        );
-                        expect(response).to.have.status(200);
-                        expect(response).to.have.schema(schemas.rrsets);
-                        return chakram.wait();
-                    });
-
-                    itPropagatesToTheApi([
-                        {subname: 'ipv6', domain: domain, type: 'AAAA', ttl: 3622, records: ['dead::beef']},
-                        {subname: '', domain: domain, type: 'MX', ttl: 3633, records: ['10 mail.example.com.', '20 mail.example.net.']},
-                    ]);
-
-                    itShowsUpInPdnsAs('ipv6', domain, 'AAAA', ['dead::beef'], 3622);
-
-                    itShowsUpInPdnsAs('', domain, 'MX', ['10 mail.example.com.', '20 mail.example.net.'], 3633);
-                });
-
-                describe("cannot bulk-patch with missing or invalid fields", function () {
-                    before(function () {
-                        // Set an RRset that we'll try to overwrite
-                        var response = chakram.post(
-                            '/domains/' + domain + '/rrsets/',
-                            {'ttl': 3650, 'type': 'TXT', 'records': ['"foo"']}
-                        );
-                        expect(response).to.have.status(201);
-
-                        var response = chakram.patch(
-                            '/domains/' + domain + '/rrsets/',
-                            [
-                                {'subname': 'a.1', 'records': ['dead::beef'], 'ttl': 3622},
-                                {'subname': 'b.1', 'ttl': -50, 'type': 'AAAA', 'records': ['dead::beef']},
-                                {'ttl': 3640, 'type': 'TXT', 'records': ['"bar"']},
-                                {'subname': 'c.1', 'records': ['dead::beef'], 'type': 'AAAA'},
-                                {'subname': 'd.1', 'ttl': 3650, 'type': 'AAAA'},
-                            ]
-                        );
-                        expect(response).to.have.status(400);
-                        expect(response).to.have.json([
-                            { type: [ 'This field is required.' ] },
-                            { ttl: [ 'Ensure this value is greater than or equal to 60.' ] },
-                            { subname: [ 'This field is required.' ] },
-                            { ttl: ['This field is required.']} ,
-                            { records: ['This field is required.']} ,
-                        ]);
-
-                        return chakram.wait();
-                    });
-
-                    it("does not propagate partially to the API", function () {
-                        return chakram.waitFor([
-                            chakram
-                                .get('/domains/' + domain + '/rrsets/b.1.../AAAA/')
-                                .then(function (response) {
-                                    expect(response).to.have.status(404);
-                                }),
-                            chakram
-                                .get('/domains/' + domain + '/rrsets/.../TXT/')
-                                .then(function (response) {
-                                    expect(response).to.have.status(200);
-                                    expect(response).to.have.json('ttl', 3650);
-                                    expect(response.body.records).to.have.members(['"foo"']);
-                                }),
-                            ]);
-                    });
-
-                    itShowsUpInPdnsAs('b.1', domain, 'AAAA', []);
-                });
-
-                context("with a pre-existing RRset", function () {
-                    before(function () {
-                        var response = chakram.post(
-                            '/domains/' + domain + '/rrsets/',
-                            [
-                                {'subname': 'a.1', 'ttl': 3650, 'type': 'TXT', 'records': ['"foo"']},
-                                {'subname': 'a.2', 'ttl': 3650, 'type': 'A', 'records': ['4.3.2.1']},
-                                {'subname': 'a.2', 'ttl': 3650, 'type': 'TXT', 'records': ['"foo"']},
-                                {'subname': 'b.2', 'ttl': 3650, 'type': 'A', 'records': ['5.4.3.2']},
-                                {'subname': 'b.2', 'ttl': 3650, 'type': 'TXT', 'records': ['"foo"']},
-                                {'subname': 'c.2', 'ttl': 3650, 'type': 'A', 'records': ['1.2.3.4']},
-                            ]
-                        );
-                        return expect(response).to.have.status(201);
-                    });
-
-                    describe("can bulk-patch existing RRsets", function () {
-                        var response;
-
-                        before(function () {
-                            response = chakram.patch(
-                                '/domains/' + domain + '/rrsets/',
-                                [
-                                    {'subname': 'a.1', 'type': 'TXT', 'records': ['"bar"']},
-                                    {'subname': 'a.2', 'ttl': 3640, 'type': 'TXT', 'records': ['"bar"']},
-                                ]
-                            );
-                            expect(response).to.have.status(200);
-                            expect(response).to.have.schema(schemas.rrsets);
-                            return chakram.wait();
-                        });
-
-                        it("does modify records in the API", function () {
-                            return chakram.waitFor([
-                                chakram
-                                    .get('/domains/' + domain + '/rrsets/a.1.../TXT/')
-                                    .then(function (response) {
-                                        expect(response).to.have.status(200);
-                                        expect(response).to.have.json('ttl', 3650);
-                                        expect(response.body.records).to.have.members(['"bar"']);
-                                    }),
-                                chakram
-                                    .get('/domains/' + domain + '/rrsets/a.2.../TXT/')
-                                    .then(function (response) {
-                                        expect(response).to.have.status(200);
-                                        expect(response).to.have.json('ttl', 3640);
-                                        expect(response.body.records).to.have.members(['"bar"']);
-                                    }),
-                            ]);
-                        });
-
-                        itShowsUpInPdnsAs('a.2', domain, 'TXT', ['"bar"'], 3640);
-                    });
-
-                    describe("cannot bulk-patch duplicate RRsets", function () {
-                        var response;
-
-                        before(function () {
-                            response = chakram.patch(
-                                '/domains/' + domain + '/rrsets/',
-                                [
-                                    {'subname': 'b.2', 'ttl': 3640, 'type': 'TXT', 'records': ['"bar"']},
-                                    {'subname': 'b.2', 'ttl': 3640, 'type': 'TXT', 'records': ['"bar"']},
-                                ]
-                            );
-                            return expect(response).to.have.status(400);
-                        });
-
-                        it("gives the right response", function () {
-                            return expect(response).to.have.json([
-                                { 'non_field_errors': [ 'Same subname and type as in position(s) 1, but must be unique.' ] },
-                                { 'non_field_errors': [ 'Same subname and type as in position(s) 0, but must be unique.' ] },
-                            ]);
-                        });
-
-                        it("does not touch records in the API", function () {
-                            return chakram
-                                .get('/domains/' + domain + '/rrsets/b.2.../TXT/')
-                                .then(function (response) {
-                                    expect(response).to.have.status(200);
-                                    expect(response).to.have.json('ttl', 3650);
-                                    expect(response.body.records).to.have.members(['"foo"']);
-                                });
-                        });
-
-                        itShowsUpInPdnsAs('b.2', domain, 'TXT', ['"foo"'], 3650);
-                    });
-
-                    describe("can delete RRsets via bulk-patch", function () {
-                        var response;
-
-                        before(function () {
-                            response = chakram.patch(
-                                '/domains/' + domain + '/rrsets/',
-                                [
-                                    {'subname': 'c.2', 'type': 'A', 'records': []},
-                                ]
-                            );
-                            return expect(response).to.have.status(200);
-                        });
-
-                        it("gives the right response", function () {
-                            var response = chakram.get('/domains/' + domain + '/rrsets/c.2.../A/');
-                            return expect(response).to.have.status(404);
-                        });
-                    });
-                });
-
-                describe("cannot bulk-patch with invalid input", function () {
-                    it("gives the right response for invalid type", function () {
-                        var response = chakram.patch(
-                            '/domains/' + domain + '/rrsets/',
-                            [{'subname': 'a.2', 'ttl': 3650, 'type': 'INVALID', 'records': ['"foo"']}]
-                        );
-                        return expect(response).to.have.status(400);
-                    });
-
-                    it("gives the right response for invalid records", function () {
-                        var response = chakram.patch(
-                            '/domains/' + domain + '/rrsets/',
-                            [{'subname': 'a.2', 'ttl': 3650, 'type': 'MX', 'records': ['1.2.3.4']}]
-                        );
-                        return expect(response).to.have.status(400);
-                    });
-
-                    it("gives the right response for records contents being null", function () {
-                        var response = chakram.patch(
-                            '/domains/' + domain + '/rrsets/',
-                            [{'subname': 'a.2', 'ttl': 3650, 'type': 'MX', 'records': ['1.2.3.4', null]}]
-                        );
-                        return expect(response).to.have.status(400);
-                    });
-                });
-
-            });
-
-            describe("auth/tokens/ endpoint", function () {
-
-                var tokenId;
-                var tokenValue;
-
-                function createTokenWithName () {
-                    var tokenname = "e2e-token-" + require("uuid").v4();
-                    return chakram.post('/auth/tokens/', { name: tokenname }).then(function (response) {
-                        expect(response).to.have.status(201);
-                        expect(response).to.have.json('name', tokenname);
-                        tokenId = response.body['id'];
-                    });
-                }
-
-                function createToken () {
-                    return chakram.post('/auth/tokens/').then(function (response) {
-                        expect(response).to.have.status(201);
-                        tokenId = response.body['id'];
-                        tokenValue = response.body['value'];
-                    });
-                }
-
-                it("can create tokens", createToken);
-
-                it("can create tokens with name", createTokenWithName)
-
-                describe("with tokens", function () {
-                    before(createToken)
-
-                    it("a list of tokens can be retrieved", function () {
-                        var response = chakram.get('/auth/tokens/');
-                        return expect(response).to.have.schema(schemas.tokens);
-                    });
-
-                    describe("can delete token", function () {
-
-                        before( function () {
-                            var response = chakram.delete('/auth/tokens/' + tokenId + '/');
-                            return expect(response).to.have.status(204);
-                        });
-
-                        it("deactivates the token", function () {
-                            return expect(chakram.get('/auth/tokens/', {
-                                headers: {'Authorization': 'Token ' + tokenValue }
-                            })).to.have.status(401);
-                        });
-
-                    });
-
-                    it("deleting nonexistent tokens yields 204", function () {
-                        var response = chakram.delete('/auth/tokens/wedonthavethisid/');
-                        return expect(response).to.have.status(204);
-                    });
-
-                });
-
-            })
-
-        });
-
-    });
-
-});

+ 0 - 69
test/e2e/spec/donation_spec.js

@@ -1,69 +0,0 @@
-var chakram = require("./../setup.js").chakram;
-var expect = chakram.expect;
-
-// ('name', 'iban', 'bic', 'amount', 'message', 'email')
-var apiDonationSchema = {
-    properties: {
-        name: {type: "string"},
-        iban: {type: "string"},
-        bic: {type: "string"},
-        amount: {type: "string"},
-        message: {type: "string"},
-        email: {type: "string"},
-    },
-    required: ["name", "iban", "bic", "amount"]
-};
-
-before(function () {
-    chakram.setRequestSettings({
-        headers: {
-            'Host': 'desec.' + process.env.DESECSTACK_DOMAIN,
-        },
-        followRedirect: false,
-        baseUrl: 'https://www/api/v1',
-    });
-});
-
-describe("donating", function () {
-
-    describe("without message and IBAN containing spaces", function () {
-
-        var response;
-        var iban = "DE89 3704 0044 0532 0130 00";
-
-        before(function() {
-            response = chakram.post('/donation/', {
-                "name": "Drama Queen",
-                "iban": iban,
-                "bic": "MARKDEF1100",
-                "amount": "3.14",
-                "email": "drama@queen.world",
-            });
-        });
-
-        it("goes through", function () {
-           return expect(response).to.have.status(201);
-        });
-
-        it("follows donation schema", function () {
-            return expect(response).to.have.schema(apiDonationSchema);
-        });
-
-    });
-
-    it("does not require an email address", function () {
-        var email, password, token;
-
-        var response = chakram.post('/donation/', {
-            "name": "Drama Queen",
-            "iban": "DE89370400440532013000",
-            "bic": "MARKDEF1100",
-            "amount": "3.14",
-        });
-
-        return expect(response).to.have.status(201);
-    });
-
-    it("sends emails")
-
-});

+ 0 - 208
test/e2e/spec/dyndns_spec.js

@@ -1,208 +0,0 @@
-var chakram = require("./../setup.js").chakram;
-var expect = chakram.expect;
-var itShowsUpInPdnsAs = require("./../setup.js").itShowsUpInPdnsAs;
-var schemas = require("./../schemas.js");
-var withCaptcha = require("./../setup.js").withCaptcha;
-
-describe("dyndns service", function () {
-
-    let publicSuffix = 'dedyn.' + process.env.DESECSTACK_DOMAIN;  // see settings.py
-
-    before(function () {
-        chakram.setRequestSettings({
-            headers: {
-                'Host': 'desec.' + process.env.DESECSTACK_DOMAIN,
-            },
-            followRedirect: false,
-            baseUrl: 'https://www/api/v1',
-        });
-    });
-
-    var email = require("uuid").v4() + '@e2etest.local';
-    describe("with user account [" + email + "]", function () {
-
-        var password, token;
-
-        before(function () {
-            // register a user that we can login and work with
-            password = require("uuid").v4();
-
-            return withCaptcha(function (captcha) {
-                return chakram.post('/auth/', {
-                    "email": email,
-                    "password": password,
-                    "captcha": captcha,
-                }).then(function () {
-                    return chakram.post('/auth/login/', {
-                        "email": email,
-                        "password": password,
-                    }).then(function (loginResponse) {
-                        expect(loginResponse.body.token).to.match(schemas.TOKEN_REGEX);
-                        token = loginResponse.body.token;
-                        chakram.setRequestHeader('Authorization', 'Token ' + token);
-                    });
-                });
-            });
-        });
-
-        var domain = 'e2etest-' + require("uuid").v4() + '.' + publicSuffix;
-        describe("and domain [" + domain + "]", function () {
-
-            before(function () {
-                chakram.setRequestHeader('Authorization', 'Token ' + token);
-                return expect(chakram.post('/domains/', {'name': domain})).to.have.status(201);
-            });
-
-            describe("dyndns12 endpoint with basic auth", function () {
-
-                var apiAccessConfig;
-
-                before(function () {
-                    apiAccessConfig = {
-                        headers: {
-                            Host: 'desec.' + process.env.DESECSTACK_DOMAIN,
-                            Authorization: 'Token ' + token,
-                        }
-                    };
-                    chakram.setRequestHeader('Host', 'update.dedyn.' + process.env.DESECSTACK_DOMAIN);
-                    chakram.setRequestHeader('Authorization', 'Basic ' + require('btoa')(domain + ':' + token));
-                    chakram.setRequestHeader('Accept', '*/*');
-                    chakram.setBaseUrl('https://www');
-                });
-
-                describe("updates without any arguments", function () {
-
-                    before(function () {
-                        var response = chakram.get('/'); // TODO also try other URLs
-                        expect(response).to.have.body('good');
-                        expect(response).to.have.status(200);
-                        return chakram.wait();
-                    });
-
-                    it('propagate to the API', function () {
-                        var response = chakram.get('/api/v1/domains/' + domain + '/rrsets/.../A/', apiAccessConfig);
-                        return expect(response).to.have.json('records', [process.env.DESECSTACK_IPV4_REAR_PREFIX16 + '.0.127']);
-                    });
-
-                    itShowsUpInPdnsAs('', domain, 'A', [process.env.DESECSTACK_IPV4_REAR_PREFIX16 + '.0.127'], 60);
-
-                    itShowsUpInPdnsAs('', domain, 'AAAA', []);
-                });
-
-                describe("v4 updates by query parameter", function () {
-
-                    before(function () {
-                        var response = chakram.get('/update/?ip=1.2.3.4');
-                        expect(response).to.have.body('good');
-                        expect(response).to.have.status(200);
-                        return chakram.wait();
-                    });
-
-                    it('propagate to the API', function () {
-                        var response = chakram.get('/api/v1/domains/' + domain + '/rrsets/.../A/', apiAccessConfig);
-                        return expect(response).to.have.json('records', ['1.2.3.4']);
-                    });
-
-                    itShowsUpInPdnsAs('', domain, 'A', ['1.2.3.4'], 60);
-
-                    itShowsUpInPdnsAs('', domain, 'AAAA', []);
-
-                    describe("removes v4 address with empty query param", function () {
-
-                        before(function () {
-                            var response = chakram.get('/update/?ip=&ipv6=bade::affe');
-                            expect(response).to.have.body('good');
-                            expect(response).to.have.status(200);
-                            return chakram.wait();
-                        });
-
-                        it('propagate to the API (v4)', function () {
-                            var response = chakram.get('/api/v1/domains/' + domain + '/rrsets/.../A/', apiAccessConfig);
-                            return expect(response).to.have.status(404);
-                        });
-
-                        it('propagate to the API (v6)', function () {
-                            var response = chakram.get('/api/v1/domains/' + domain + '/rrsets/.../AAAA/', apiAccessConfig);
-                            return expect(response).to.have.json('records', ['bade::affe']);
-                        });
-
-                        itShowsUpInPdnsAs('', domain, 'A', []);
-
-                        itShowsUpInPdnsAs('', domain, 'AAAA', ['bade::affe'], 60);
-                    });
-
-                });
-
-                describe("v6 updates by query parameter", function () {
-
-                    before(function () {
-                        var response = chakram.get('/update/?ipv6=dead::beef');
-                        expect(response).to.have.body('good');
-                        expect(response).to.have.status(200);
-                        return chakram.wait();
-                    });
-
-                    it('propagate to the API', function () {
-                        var response = chakram.get('/api/v1/domains/' + domain + '/rrsets/.../AAAA/', apiAccessConfig);
-                        return expect(response).to.have.json('records', ['dead::beef']);
-                    });
-
-                    itShowsUpInPdnsAs('', domain, 'AAAA', ['dead::beef'], 60);
-
-                    itShowsUpInPdnsAs('', domain, 'A', [process.env.DESECSTACK_IPV4_REAR_PREFIX16 + '.0.127'], 60); // taken from the v4 connection
-
-                    describe("removes v6 address with empty query param", function () {
-
-                        before(function () {
-                            var response = chakram.get('/update/?ip=1.3.3.7&ipv6=');
-                            expect(response).to.have.body('good');
-                            expect(response).to.have.status(200);
-                            return chakram.wait();
-                        });
-
-                        it('propagate to the API (v4)', function () {
-                            var response = chakram.get('/api/v1/domains/' + domain + '/rrsets/.../A/', apiAccessConfig);
-                            return expect(response).to.have.json('records', ['1.3.3.7']);
-                        });
-
-                        it('propagate to the API (v6)', function () {
-                            var response = chakram.get('/api/v1/domains/' + domain + '/rrsets/.../AAAA/', apiAccessConfig);
-                            return expect(response).to.have.status(404);
-                        });
-
-                        itShowsUpInPdnsAs('', domain, 'A', ['1.3.3.7'], 60);
-
-                        itShowsUpInPdnsAs('', domain, 'AAAA', []);
-                    });
-
-                });
-
-                describe("v4 and v6 updates by query parameter", function () {
-
-                    before(function () {
-                        var response = chakram.get('/update/?ip=192.168.1.1&ipv6=::1');
-                        expect(response).to.have.body('good');
-                        expect(response).to.have.status(200);
-                        return chakram.wait();
-                    });
-
-                    it('propagate to the API', function () {
-                        var response = chakram.get('/api/v1/domains/' + domain + '/rrsets/.../A/', apiAccessConfig);
-                        expect(response).to.have.json('records', ['192.168.1.1']);
-                        response = chakram.get('/api/v1/domains/' + domain + '/rrsets/.../AAAA/', apiAccessConfig);
-                        expect(response).to.have.json('records', ['::1']);
-                        return chakram.wait();
-                    });
-
-                    itShowsUpInPdnsAs('', domain, 'A', ['192.168.1.1'], 60);
-
-                    itShowsUpInPdnsAs('', domain, 'AAAA', ['::1'], 60);
-                });
-
-            });
-
-        });
-
-    });
-
-});

+ 0 - 18
test/e2e/spec/test_configuration_spec.js

@@ -1,18 +0,0 @@
-var chakram = require('./../setup.js').chakram;
-var expect = chakram.expect;
-
-describe("test configuration", function () {
-
-    it("has a hostname", function () {
-        return expect(process.env.DESECSTACK_DOMAIN).to.exist;
-    });
-
-    it("knows the ipv4 prefix", function () {
-        return expect(process.env.DESECSTACK_IPV4_REAR_PREFIX16).to.exist;
-    });
-
-    it("knows the ipv6 address of www", function () {
-        return expect(process.env.DESECSTACK_IPV6_ADDRESS).to.exist;
-    });
-
-});

+ 0 - 169
test/e2e/spec/www_spec.js

@@ -1,169 +0,0 @@
-var chakram = require("./../setup.js").chakram;
-var expect = chakram.expect;
-
-// obviously, I took this shamelessly and without further verification from stack overflow
-// https://stackoverflow.com/a/17871737
-var REGEX_IPV6_ADDRESS = /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/;
-
-describe("www/nginx", function () {
-
-    before(function () {
-        var s = chakram.getRequestSettings();
-        s.followRedirect = false;
-        s.baseUrl = '';
-        chakram.setRequestSettings(s);
-    });
-
-    describe("dedyn host", function () {
-
-        before(function () {
-            chakram.setRequestHeader('Host', 'dedyn.' + process.env.DESECSTACK_DOMAIN);
-        });
-
-        it("redirects to the desec host", function () {
-
-            [
-                'https://' + process.env.DESECSTACK_IPV4_REAR_PREFIX16 + '.0.128/',
-                'http://' + process.env.DESECSTACK_IPV4_REAR_PREFIX16 + '.0.128/',
-                'https://[' + process.env.DESECSTACK_IPV6_ADDRESS + ']/',
-                'http://[' + process.env.DESECSTACK_IPV6_ADDRESS + ']/',
-            ].forEach(function (url) {
-                var response = chakram.get(url);
-                expect(response).to.have.status(301);
-                expect(response).to.have.header('Location', 'https://desec.' + process.env.DESECSTACK_DOMAIN + '/');
-            });
-
-            return chakram.wait();
-        });
-
-    });
-
-    describe("checkip.dedyn host", function () {
-
-        before(function () {
-            chakram.setRequestHeader('Host', 'checkip.dedyn.' + process.env.DESECSTACK_DOMAIN);
-        });
-
-        describe("contacted through SSL/TLS", function () {
-
-            it('returns the ipv4 address when contacted through ipv4', function () {
-                var response = chakram.get('https://' + process.env.DESECSTACK_IPV4_REAR_PREFIX16 + '.0.128/');
-                return expect(response).to.have.body(process.env.DESECSTACK_IPV4_REAR_PREFIX16 + '.0.127');
-            });
-
-            it('returns an ipv6 address when contacted through ipv6', function () {
-                var response = chakram.get('https://[' + process.env.DESECSTACK_IPV6_ADDRESS + ']/');
-
-                // it's hard to find out which IPv6 address we actually expect here
-                // and as we are inside the docker network anyway (that is, we are
-                // topologically not in the same place as the end user), it's hard
-                // if the correct address is returned. Hence, we will stick to some
-                // simple tests.
-                expect(response).to.have.body(REGEX_IPV6_ADDRESS);
-                return chakram.wait();
-            });
-
-        });
-
-        describe("contacted without encryption", function () {
-
-            it('redirects to SSL/TLS when contacted through ipv4', function () {
-                var response = chakram.get('http://' + process.env.DESECSTACK_IPV4_REAR_PREFIX16 + '.0.128/');
-                expect(response).to.have.status(301);
-                expect(response).to.have.header('Location', 'https://checkip.dedyn.' + process.env.DESECSTACK_DOMAIN + '/');
-                return chakram.wait();
-            });
-
-            it('redirects to SSL/TLS when contacted through ipv6', function () {
-                var response = chakram.get('http://[' + process.env.DESECSTACK_IPV6_ADDRESS + ']/');
-                expect(response).to.have.status(301);
-                expect(response).to.have.header('Location', 'https://checkip.dedyn.' + process.env.DESECSTACK_DOMAIN + '/');
-                return chakram.wait();
-            });
-
-        });
-
-    });
-
-    describe("checkipv4.dedyn host", function () {
-
-        before(function () {
-            chakram.setRequestHeader('Host', 'checkipv4.dedyn.' + process.env.DESECSTACK_DOMAIN);
-        });
-
-        it('returns the ipv4 address when contacted through ipv4', function () {
-            var response = chakram.get('https://' + process.env.DESECSTACK_IPV4_REAR_PREFIX16 + '.0.128/');
-            return expect(response).to.have.body(process.env.DESECSTACK_IPV4_REAR_PREFIX16 + '.0.127');
-        });
-
-        it('redirects to SSL/TLS when concated without encryption', function () {
-            var response = chakram.get('http://' + process.env.DESECSTACK_IPV4_REAR_PREFIX16 + '.0.128/');
-            expect(response).to.have.status(301);
-            expect(response).to.have.header('Location', 'https://checkipv4.dedyn.' + process.env.DESECSTACK_DOMAIN + '/');
-            return chakram.wait();
-        });
-
-        it('closes the connection when contacted through ipv6', function () {
-            var response = chakram.get('https://[' + process.env.DESECSTACK_IPV6_ADDRESS + ']/');
-            return expect(response).to.not.have.a.body();
-        });
-
-    });
-
-    describe("checkipv6.dedyn host", function () {
-
-        before(function () {
-            chakram.setRequestHeader('Host', 'checkipv6.dedyn.' + process.env.DESECSTACK_DOMAIN);
-        });
-
-        it('closes the connection when contacted through ipv4', function () {
-            var response = chakram.get('https://' + process.env.DESECSTACK_IPV4_REAR_PREFIX16 + '.0.128/');
-            return expect(response).to.not.have.a.body();
-        });
-
-        it('redirects to SSL/TLS when concated without encryption', function () {
-            var response = chakram.get('http://[' + process.env.DESECSTACK_IPV6_ADDRESS + ']/');
-            expect(response).to.have.status(301);
-            expect(response).to.have.header('Location', 'https://checkipv6.dedyn.' + process.env.DESECSTACK_DOMAIN + '/');
-            return chakram.wait();
-        });
-
-        it('returns an ipv6 address when contacted through ipv6', function () {
-            var response = chakram.get('https://[' + process.env.DESECSTACK_IPV6_ADDRESS + ']/');
-
-            // it's hard to find out which IPv6 address we actually expect here
-            // and as we are inside the docker network anyway (that is, we are
-            // topologically not in the same place as the end user), it's hard
-            // if the correct address is returned. Hence, we will stick to some
-            // simple tests.
-            expect(response).to.have.body(REGEX_IPV6_ADDRESS);
-            return chakram.wait();
-        });
-
-    });
-
-    describe("desec host", function () {
-
-        before(function () {
-            chakram.setRequestHeader('Host', 'desec.' + process.env.DESECSTACK_DOMAIN);
-        });
-
-        it.skip("is alive", function () {  // disabled as we receive 503 while webapp is being built
-            var response = chakram.get('https://www/');
-            return expect(response).to.have.status(200);
-        });
-
-        it("has security headers", function () {
-            var response = chakram.get('https://www/');
-            expect(response).to.have.header('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload');
-            expect(response).to.have.header('Content-Security-Policy', "default-src 'self'; frame-src 'none'; connect-src 'self'; font-src 'self'; img-src 'self' data:; media-src data:; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; base-uri 'self'; frame-ancestors 'none'; block-all-mixed-content; form-action 'none';");
-            expect(response).to.have.header('X-Frame-Options', 'deny');
-            expect(response).to.have.header('X-Content-Type-Options', 'nosniff');
-            expect(response).to.have.header('Referrer-Policy', 'strict-origin-when-cross-origin');
-            expect(response).to.have.header('X-XSS-Protection', '1; mode=block');
-            return chakram.wait();
-        });
-
-    });
-
-});