database_unittest.cc 145 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

15 16
#include <stdlib.h>

17
#include <boost/shared_ptr.hpp>
18
#include <boost/lexical_cast.hpp>
19

20 21
#include <gtest/gtest.h>

22 23
#include <exceptions/exceptions.h>

24
#include <dns/name.h>
25
#include <dns/rrttl.h>
26
#include <dns/rrset.h>
27
#include <exceptions/exceptions.h>
28 29

#include <datasrc/database.h>
30 31
#include <datasrc/zone.h>
#include <datasrc/data_source.h>
32
#include <datasrc/iterator.h>
33
#include <datasrc/sqlite3_accessor.h>
34

35 36
#include <testutils/dnsmessage_test.h>

37
#include <map>
38
#include <vector>
39 40 41

using namespace isc::datasrc;
using namespace std;
42 43 44
// don't import the entire boost namespace.  It will unexpectedly hide uint32_t
// for some systems.
using boost::dynamic_pointer_cast;
45
using boost::lexical_cast;
46
using namespace isc::dns;
47 48 49

namespace {

50 51 52 53
// Imaginary zone IDs used in the mock accessor below.
const int READONLY_ZONE_ID = 42;
const int WRITABLE_ZONE_ID = 4200;

54 55 56 57 58 59
// Commonly used test data
const char* const TEST_RECORDS[][5] = {
    // some plain data
    {"www.example.org.", "A", "3600", "", "192.0.2.1"},
    {"www.example.org.", "AAAA", "3600", "", "2001:db8::1"},
    {"www.example.org.", "AAAA", "3600", "", "2001:db8::2"},
60 61
    {"www.example.org.", "NSEC", "3600", "", "www2.example.org. A AAAA NSEC RRSIG"},
    {"www.example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
62 63

    {"www2.example.org.", "A", "3600", "", "192.0.2.1"},
64
    {"www2.example.org.", "AAAA", "3600", "", "2001:db8::1"},
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
    {"www2.example.org.", "A", "3600", "", "192.0.2.2"},

    {"cname.example.org.", "CNAME", "3600", "", "www.example.org."},

    // some DNSSEC-'signed' data
    {"signed1.example.org.", "A", "3600", "", "192.0.2.1"},
    {"signed1.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},

    {"signed1.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"},
    {"signed1.example.org.", "AAAA", "3600", "", "2001:db8::1"},
    {"signed1.example.org.", "AAAA", "3600", "", "2001:db8::2"},
    {"signed1.example.org.", "RRSIG", "3600", "", "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},

    {"signedcname1.example.org.", "CNAME", "3600", "", "www.example.org."},
    {"signedcname1.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},

    // special case might fail; sig is for cname, which isn't there (should be ignored)
    // (ignoring of 'normal' other type is done above by www.)
    {"acnamesig1.example.org.", "A", "3600", "", "192.0.2.1"},
    {"acnamesig1.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
    {"acnamesig1.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},

    // let's pretend we have a database that is not careful
    // about the order in which it returns data
    {"signed2.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
    {"signed2.example.org.", "AAAA", "3600", "", "2001:db8::2"},
    {"signed2.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"},
    {"signed2.example.org.", "A", "3600", "", "192.0.2.1"},
    {"signed2.example.org.", "RRSIG", "3600", "", "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
    {"signed2.example.org.", "AAAA", "3600", "", "2001:db8::1"},

    {"signedcname2.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
    {"signedcname2.example.org.", "CNAME", "3600", "", "www.example.org."},

    {"acnamesig2.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
    {"acnamesig2.example.org.", "A", "3600", "", "192.0.2.1"},
    {"acnamesig2.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},

    {"acnamesig3.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
    {"acnamesig3.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
    {"acnamesig3.example.org.", "A", "3600", "", "192.0.2.1"},

    {"ttldiff1.example.org.", "A", "3600", "", "192.0.2.1"},
    {"ttldiff1.example.org.", "A", "360", "", "192.0.2.2"},

    {"ttldiff2.example.org.", "A", "360", "", "192.0.2.1"},
    {"ttldiff2.example.org.", "A", "3600", "", "192.0.2.2"},

    // also add some intentionally bad data
    {"badcname1.example.org.", "A", "3600", "", "192.0.2.1"},
    {"badcname1.example.org.", "CNAME", "3600", "", "www.example.org."},

    {"badcname2.example.org.", "CNAME", "3600", "", "www.example.org."},
    {"badcname2.example.org.", "A", "3600", "", "192.0.2.1"},

    {"badcname3.example.org.", "CNAME", "3600", "", "www.example.org."},
    {"badcname3.example.org.", "CNAME", "3600", "", "www.example2.org."},

    {"badrdata.example.org.", "A", "3600", "", "bad"},

    {"badtype.example.org.", "BAD_TYPE", "3600", "", "192.0.2.1"},

    {"badttl.example.org.", "A", "badttl", "", "192.0.2.1"},

    {"badsig.example.org.", "A", "badttl", "", "192.0.2.1"},
    {"badsig.example.org.", "RRSIG", "3600", "", "A 5 3 3600 somebaddata 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},

    {"badsigtype.example.org.", "A", "3600", "", "192.0.2.1"},
    {"badsigtype.example.org.", "RRSIG", "3600", "TXT", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},

    // Data for testing delegation (with NS and DNAME)
    {"delegation.example.org.", "NS", "3600", "", "ns.example.com."},
    {"delegation.example.org.", "NS", "3600", "",
     "ns.delegation.example.org."},
139
    {"delegation.example.org.", "DS", "3600", "", "1 RSAMD5 2 abcd"},
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
    {"delegation.example.org.", "RRSIG", "3600", "", "NS 5 3 3600 "
     "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
    {"ns.delegation.example.org.", "A", "3600", "", "192.0.2.1"},
    {"deep.below.delegation.example.org.", "A", "3600", "", "192.0.2.1"},

    {"dname.example.org.", "A", "3600", "", "192.0.2.1"},
    {"dname.example.org.", "DNAME", "3600", "", "dname.example.com."},
    {"dname.example.org.", "RRSIG", "3600", "",
     "DNAME 5 3 3600 20000101000000 20000201000000 12345 "
     "example.org. FAKEFAKEFAKE"},

    {"below.dname.example.org.", "A", "3600", "", "192.0.2.1"},

    // Broken NS
    {"brokenns1.example.org.", "A", "3600", "", "192.0.2.1"},
    {"brokenns1.example.org.", "NS", "3600", "", "ns.example.com."},

    {"brokenns2.example.org.", "NS", "3600", "", "ns.example.com."},
    {"brokenns2.example.org.", "A", "3600", "", "192.0.2.1"},

    // Now double DNAME, to test failure mode
    {"baddname.example.org.", "DNAME", "3600", "", "dname1.example.com."},
    {"baddname.example.org.", "DNAME", "3600", "", "dname2.example.com."},

    // Put some data into apex (including NS) so we can check our NS
    // doesn't break anything
166 167
    {"example.org.", "SOA", "3600", "", "ns1.example.org. admin.example.org. "
     "1234 3600 1800 2419200 7200" },
168 169
    {"example.org.", "NS", "3600", "", "ns.example.com."},
    {"example.org.", "A", "3600", "", "192.0.2.1"},
170
    {"example.org.", "NSEC", "3600", "", "acnamesig1.example.org. NS A NSEC RRSIG"},
171 172
    {"example.org.", "RRSIG", "3600", "", "SOA 5 3 3600 20000101000000 "
              "20000201000000 12345 example.org. FAKEFAKEFAKE"},
173 174
    {"example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 "
              "20000201000000 12345 example.org. FAKEFAKEFAKE"},
175 176 177 178 179 180 181 182
    {"example.org.", "RRSIG", "3600", "", "NS 5 3 3600 20000101000000 "
              "20000201000000 12345 example.org. FAKEFAKEFAKE"},

    // This is because of empty domain test
    {"a.b.example.org.", "A", "3600", "", "192.0.2.1"},

    // Something for wildcards
    {"*.wild.example.org.", "A", "3600", "", "192.0.2.5"},
183
    {"*.wild.example.org.", "RRSIG", "3600", "A", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
184 185
    {"*.wild.example.org.", "NSEC", "3600", "", "cancel.here.wild.example.org. A NSEC RRSIG"},
    {"*.wild.example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
186 187 188 189 190
    {"cancel.here.wild.example.org.", "AAAA", "3600", "", "2001:db8::5"},
    {"delegatedwild.example.org.", "NS", "3600", "", "ns.example.com."},
    {"*.delegatedwild.example.org.", "A", "3600", "", "192.0.2.5"},
    {"wild.*.foo.example.org.", "A", "3600", "", "192.0.2.5"},
    {"wild.*.foo.*.bar.example.org.", "A", "3600", "", "192.0.2.5"},
191 192
    {"wild.*.foo.*.bar.example.org.", "NSEC", "3600", "",
     "brokenns1.example.org. A NSEC"},
193
    {"bao.example.org.", "NSEC", "3600", "", "wild.*.foo.*.bar.example.org. NSEC"},
194
    {"*.cnamewild.example.org.", "CNAME", "3600", "", "www.example.org."},
195
    {"*.dnamewild.example.org.", "DNAME", "3600", "", "dname.example.com."},
196
    {"*.nswild.example.org.", "NS", "3600", "", "ns.example.com."},
197 198 199
    // For NSEC empty non-terminal
    {"l.example.org.", "NSEC", "3600", "", "empty.nonterminal.example.org. NSEC"},
    {"empty.nonterminal.example.org.", "A", "3600", "", "192.0.2.1"},
200 201 202 203
    // Invalid rdata
    {"invalidrdata.example.org.", "A", "3600", "", "Bunch of nonsense"},
    {"invalidrdata2.example.org.", "A", "3600", "", "192.0.2.1"},
    {"invalidrdata2.example.org.", "RRSIG", "3600", "", "Nonsense"},
204 205 206 207

    {NULL, NULL, NULL, NULL, NULL},
};

208
/*
209
 * An accessor with minimum implementation, keeping the original
210
 * "NotImplemented" methods.
211
 */
212
class NopAccessor : public DatabaseAccessor {
213
public:
214 215
    NopAccessor() : database_name_("mock_database")
    { }
216

217 218
    virtual std::pair<bool, int> getZone(const std::string& name) const {
        if (name == "example.org.") {
219
            return (std::pair<bool, int>(true, READONLY_ZONE_ID));
220
        } else if (name == "null.example.org.") {
221
            return (std::pair<bool, int>(true, 13));
222
        } else if (name == "empty.example.org.") {
223
            return (std::pair<bool, int>(true, 0));
224
        } else if (name == "bad.example.org.") {
225
            return (std::pair<bool, int>(true, -1));
226 227 228 229
        } else {
            return (std::pair<bool, int>(false, 0));
        }
    }
230

231
    virtual boost::shared_ptr<DatabaseAccessor> clone() {
232
        // This accessor is stateless, so we can simply return a new instance.
233
        return (boost::shared_ptr<DatabaseAccessor>(new NopAccessor));
234
    }
235

JINMEI Tatuya's avatar
JINMEI Tatuya committed
236 237 238 239
    virtual std::pair<bool, int> startUpdateZone(const std::string&, bool) {
        // return dummy value.  unused anyway.
        return (pair<bool, int>(true, 0));
    }
240
    virtual void startTransaction() {}
241 242
    virtual void commit() {}
    virtual void rollback() {}
243 244
    virtual void addRecordToZone(const string (&)[ADD_COLUMN_COUNT]) {}
    virtual void deleteRecordInZone(const string (&)[DEL_PARAM_COUNT]) {}
245 246
    virtual void addRecordDiff(int, uint32_t, DiffOperation,
                               const std::string (&)[DIFF_PARAM_COUNT]) {}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
247

248 249 250 251
    virtual const std::string& getDBName() const {
        return (database_name_);
    }

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
252 253 254
    virtual IteratorContextPtr getRecords(const std::string&, int, bool)
        const
        {
255 256
        isc_throw(isc::NotImplemented,
                  "This database datasource can't be iterated");
257
    }
258 259 260 261

    virtual IteratorContextPtr getAllRecords(int) const {
        isc_throw(isc::NotImplemented,
                  "This database datasource can't be iterated");
262 263
    }

264
    virtual IteratorContextPtr getDiffs(int, uint32_t, uint32_t) const {
265
        isc_throw(isc::NotImplemented,
266
                  "This database datasource doesn't support diffs");
267 268
    }

269 270 271 272
    virtual std::string findPreviousName(int, const std::string&) const {
        isc_throw(isc::NotImplemented,
                  "This data source doesn't support DNSSEC");
    }
273 274 275
private:
    const std::string database_name_;

276 277
};

278 279 280 281 282 283 284 285 286 287 288
/**
 * Single journal entry in the mock database.
 *
 * All the members there are public for simplicity, as it only stores data.
 * We use the implicit constructor and operator. The members can't be const
 * because of the assignment operator (used in the vectors).
 */
struct JournalEntry {
    JournalEntry(int id, uint32_t serial,
                 DatabaseAccessor::DiffOperation operation,
                 const std::string (&data)[DatabaseAccessor::DIFF_PARAM_COUNT])
289 290 291 292 293 294 295 296
        : id_(id), serial_(serial), operation_(operation)
    {
        data_[DatabaseAccessor::DIFF_NAME] = data[DatabaseAccessor::DIFF_NAME];
        data_[DatabaseAccessor::DIFF_TYPE] = data[DatabaseAccessor::DIFF_TYPE];
        data_[DatabaseAccessor::DIFF_TTL] = data[DatabaseAccessor::DIFF_TTL];
        data_[DatabaseAccessor::DIFF_RDATA] =
            data[DatabaseAccessor::DIFF_RDATA];
    }
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
    JournalEntry(int id, uint32_t serial,
                 DatabaseAccessor::DiffOperation operation,
                 const std::string& name, const std::string& type,
                 const std::string& ttl, const std::string& rdata):
        id_(id), serial_(serial), operation_(operation)
    {
        data_[DatabaseAccessor::DIFF_NAME] = name;
        data_[DatabaseAccessor::DIFF_TYPE] = type;
        data_[DatabaseAccessor::DIFF_TTL] = ttl;
        data_[DatabaseAccessor::DIFF_RDATA] = rdata;
    }
    int id_;
    uint32_t serial_;
    DatabaseAccessor::DiffOperation operation_;
    std::string data_[DatabaseAccessor::DIFF_PARAM_COUNT];
    bool operator==(const JournalEntry& other) const {
313
        for (size_t i = 0; i < DatabaseAccessor::DIFF_PARAM_COUNT; ++ i) {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
314 315 316
            if (data_[i] != other.data_[i]) {
                return false;
            }
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
317
        }
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
318
        // No need to check data here, checked above
319
        return (id_ == other.id_ && serial_ == other.serial_ &&
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
320
                operation_ == other.operation_);
321 322 323
    }
};

324
/*
325
 * A virtual database accessor that pretends it contains single zone --
326 327 328 329 330
 * example.org.
 *
 * It has the same getZone method as NopConnection, but it provides
 * implementation of the optional functionality.
 */
331
class MockAccessor : public NopAccessor {
332 333 334
    // Type of mock database "row"s.  This is a map whose keys are the
    // own names.  We internally sort them by the name comparison order.
    struct NameCompare : public binary_function<string, string, bool> {
335
        bool operator()(const string& n1, const string& n2) const {
336 337 338 339 340 341
            return (Name(n1).compare(Name(n2)).getOrder() < 0);
        }
    };
    typedef std::map<std::string,
                     std::vector< std::vector<std::string> >,
                     NameCompare > Domains;
342

343
public:
344
    MockAccessor() : rollbacked_(false), did_transaction_(false) {
345 346 347
        readonly_records_ = &readonly_records_master_;
        update_records_ = &update_records_master_;
        empty_records_ = &empty_records_master_;
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
348
        journal_entries_ = &journal_entries_master_;
349 350
        fillData();
    }
351

352 353
    virtual boost::shared_ptr<DatabaseAccessor> clone() {
        boost::shared_ptr<MockAccessor> cloned_accessor(new MockAccessor());
354 355 356
        cloned_accessor->readonly_records_ = &readonly_records_master_;
        cloned_accessor->update_records_ = &update_records_master_;
        cloned_accessor->empty_records_ = &empty_records_master_;
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
357
        cloned_accessor->journal_entries_ = &journal_entries_master_;
358 359 360 361
        latest_clone_ = cloned_accessor;
        return (cloned_accessor);
    }

362 363 364 365
    virtual void startTransaction() {
        // Currently we only use this transaction for simple read-only
        // operations.  So we just make a local copy of the data (we don't
        // care about what happens after commit() or rollback()).
366 367 368 369 370 371 372 373 374
        // Obviously as a consequence, if a test case tries to make multiple
        // transactions on a single mock accessor it will fail.

        // Check any attempt of multiple transactions
        if (did_transaction_) {
            isc_throw(isc::Unexpected, "MockAccessor::startTransaction() "
                      "called multiple times - likely a bug in the test");
        }

375
        readonly_records_copy_ = *readonly_records_;
376
        readonly_records_ = &readonly_records_copy_;
377
        did_transaction_ = true;
378 379
    }

380
private:
381 382 383
    class MockNameIteratorContext : public IteratorContext {
    public:
        MockNameIteratorContext(const MockAccessor& mock_accessor, int zone_id,
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
384
                                const std::string& name, bool subdomains) :
385
            searched_name_(name), cur_record_(0)
386
        {
387
            // 'hardcoded' names to trigger exceptions
388
            // On these names some exceptions are thrown, to test the robustness
389
            // of the find() method.
390 391 392 393 394 395 396 397
            if (searched_name_ == "dsexception.in.search.") {
                isc_throw(DataSourceError, "datasource exception on search");
            } else if (searched_name_ == "iscexception.in.search.") {
                isc_throw(isc::Exception, "isc exception on search");
            } else if (searched_name_ == "basicexception.in.search.") {
                throw std::exception();
            }

398 399 400
            cur_record_ = 0;
            const Domains& cur_records = mock_accessor.getMockRecords(zone_id);
            if (cur_records.count(name) > 0) {
401 402
                    // we're not aiming for efficiency in this test, simply
                    // copy the relevant vector from records
403 404 405 406 407
                    cur_name = cur_records.find(name)->second;
            } else if (subdomains) {
                cur_name.clear();
                // Just walk everything and check if it is a subdomain.
                // If it is, just copy all data from there.
408
                for (Domains::const_iterator i = cur_records.begin();
409 410 411 412 413 414
                     i != cur_records.end(); ++i) {
                    const Name local(i->first);
                    if (local.compare(Name(name)).getRelation() ==
                        isc::dns::NameComparisonResult::SUBDOMAIN) {
                        cur_name.insert(cur_name.end(), i->second.begin(),
                                        i->second.end());
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
415
                    }
416
                }
417 418
            } else {
                cur_name.clear();
419 420 421
            }
        }

422
        virtual bool getNext(std::string (&columns)[COLUMN_COUNT]) {
423 424 425 426 427 428 429 430 431
            if (searched_name_ == "dsexception.in.getnext.") {
                isc_throw(DataSourceError, "datasource exception on getnextrecord");
            } else if (searched_name_ == "iscexception.in.getnext.") {
                isc_throw(isc::Exception, "isc exception on getnextrecord");
            } else if (searched_name_ == "basicexception.in.getnext.") {
                throw std::exception();
            }

            if (cur_record_ < cur_name.size()) {
432
                for (size_t i = 0; i < COLUMN_COUNT; ++i) {
433 434 435 436 437 438 439 440 441 442 443
                    columns[i] = cur_name[cur_record_][i];
                }
                cur_record_++;
                return (true);
            } else {
                return (false);
            }
        }

    private:
        const std::string searched_name_;
444
        size_t cur_record_;
445 446 447
        std::vector< std::vector<std::string> > cur_name;
    };

448 449 450
    class MockIteratorContext : public IteratorContext {
    private:
        int step;
451
        const Domains& domains_;
452
    public:
453 454
        MockIteratorContext(const Domains& domains) :
            step(0), domains_(domains)
455
        { }
456
        virtual bool getNext(string (&data)[COLUMN_COUNT]) {
457 458 459 460 461 462 463
            // A special case: if the given set of domains is already empty,
            // we always return false.
            if (domains_.empty()) {
                return (false);
            }

            // Return faked data for tests
464 465
            switch (step ++) {
                case 0:
466 467 468 469 470 471
                    data[DatabaseAccessor::NAME_COLUMN] = "example.org";
                    data[DatabaseAccessor::TYPE_COLUMN] = "A";
                    data[DatabaseAccessor::TTL_COLUMN] = "3600";
                    data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.1";
                    return (true);
                case 1:
472 473
                    data[DatabaseAccessor::NAME_COLUMN] = "example.org";
                    data[DatabaseAccessor::TYPE_COLUMN] = "SOA";
474
                    data[DatabaseAccessor::TTL_COLUMN] = "3600";
475
                    data[DatabaseAccessor::RDATA_COLUMN] = "ns1.example.org. admin.example.org. "
476 477
                        "1234 3600 1800 2419200 7200";
                    return (true);
478
                case 2:
479 480 481 482
                    data[DatabaseAccessor::NAME_COLUMN] = "x.example.org";
                    data[DatabaseAccessor::TYPE_COLUMN] = "A";
                    data[DatabaseAccessor::TTL_COLUMN] = "300";
                    data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.1";
483
                    return (true);
484
                case 3:
485 486 487 488
                    data[DatabaseAccessor::NAME_COLUMN] = "x.example.org";
                    data[DatabaseAccessor::TYPE_COLUMN] = "A";
                    data[DatabaseAccessor::TTL_COLUMN] = "300";
                    data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.2";
489
                    return (true);
490
                case 4:
491 492 493 494
                    data[DatabaseAccessor::NAME_COLUMN] = "x.example.org";
                    data[DatabaseAccessor::TYPE_COLUMN] = "AAAA";
                    data[DatabaseAccessor::TTL_COLUMN] = "300";
                    data[DatabaseAccessor::RDATA_COLUMN] = "2001:db8::1";
495
                    return (true);
496
                case 5:
497 498 499 500
                    data[DatabaseAccessor::NAME_COLUMN] = "x.example.org";
                    data[DatabaseAccessor::TYPE_COLUMN] = "AAAA";
                    data[DatabaseAccessor::TTL_COLUMN] = "300";
                    data[DatabaseAccessor::RDATA_COLUMN] = "2001:db8::2";
501
                    return (true);
502
                case 6:
503 504 505 506 507
                    data[DatabaseAccessor::NAME_COLUMN] = "ttldiff.example.org";
                    data[DatabaseAccessor::TYPE_COLUMN] = "A";
                    data[DatabaseAccessor::TTL_COLUMN] = "300";
                    data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.1";
                    return (true);
508
                case 7:
509 510 511 512 513
                    data[DatabaseAccessor::NAME_COLUMN] = "ttldiff.example.org";
                    data[DatabaseAccessor::TYPE_COLUMN] = "A";
                    data[DatabaseAccessor::TTL_COLUMN] = "600";
                    data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.2";
                    return (true);
514 515 516
                default:
                    ADD_FAILURE() <<
                        "Request past the end of iterator context";
517
                case 8:
518 519 520 521 522 523
                    return (false);
            }
        }
    };
    class EmptyIteratorContext : public IteratorContext {
    public:
524
        virtual bool getNext(string(&)[COLUMN_COUNT]) {
525 526 527 528 529 530 531 532 533 534
            return (false);
        }
    };
    class BadIteratorContext : public IteratorContext {
    private:
        int step;
    public:
        BadIteratorContext() :
            step(0)
        { }
535
        virtual bool getNext(string (&data)[COLUMN_COUNT]) {
536 537
            switch (step ++) {
                case 0:
538 539 540 541
                    data[DatabaseAccessor::NAME_COLUMN] = "x.example.org";
                    data[DatabaseAccessor::TYPE_COLUMN] = "A";
                    data[DatabaseAccessor::TTL_COLUMN] = "300";
                    data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.1";
542 543
                    return (true);
                case 1:
544 545 546 547
                    data[DatabaseAccessor::NAME_COLUMN] = "x.example.org";
                    data[DatabaseAccessor::TYPE_COLUMN] = "A";
                    data[DatabaseAccessor::TTL_COLUMN] = "301";
                    data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.2";
548 549 550 551 552 553 554 555 556
                    return (true);
                default:
                    ADD_FAILURE() <<
                        "Request past the end of iterator context";
                case 2:
                    return (false);
            }
        }
    };
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
    class MockDiffIteratorContext : public IteratorContext {
        const vector<JournalEntry> diffs_;
        vector<JournalEntry>::const_iterator it_;
    public:
        MockDiffIteratorContext(const vector<JournalEntry>& diffs) :
            diffs_(diffs), it_(diffs_.begin())
        {}
        virtual bool getNext(string (&data)[COLUMN_COUNT]) {
            if (it_ == diffs_.end()) {
                return (false);
            }
            data[DatabaseAccessor::NAME_COLUMN] =
                (*it_).data_[DatabaseAccessor::DIFF_NAME];
            data[DatabaseAccessor::TYPE_COLUMN] =
                (*it_).data_[DatabaseAccessor::DIFF_TYPE];
            data[DatabaseAccessor::TTL_COLUMN] =
                (*it_).data_[DatabaseAccessor::DIFF_TTL];
            data[DatabaseAccessor::RDATA_COLUMN] =
                (*it_).data_[DatabaseAccessor::DIFF_RDATA];
            ++it_;
            return (true);
        }
    };
580
public:
581
    virtual IteratorContextPtr getAllRecords(int id) const {
582
        if (id == READONLY_ZONE_ID) {
583 584
            return (IteratorContextPtr(new MockIteratorContext(
                                           *readonly_records_)));
585 586 587 588 589 590 591 592 593 594
        } else if (id == 13) {
            return (IteratorContextPtr());
        } else if (id == 0) {
            return (IteratorContextPtr(new EmptyIteratorContext()));
        } else if (id == -1) {
            return (IteratorContextPtr(new BadIteratorContext()));
        } else {
            isc_throw(isc::Unexpected, "Unknown zone ID");
        }
    }
595

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
596 597 598
    virtual IteratorContextPtr getRecords(const std::string& name, int id,
                                          bool subdomains) const
    {
599 600 601 602
        if (id == READONLY_ZONE_ID || id == WRITABLE_ZONE_ID) {
            return (IteratorContextPtr(
                        new MockNameIteratorContext(*this, id, name,
                                                    subdomains)));
603
        } else {
604 605 606 607 608
            // This iterator is bogus, but for the cases tested below that's
            // sufficient.
            return (IteratorContextPtr(
                        new MockNameIteratorContext(*this, READONLY_ZONE_ID,
                                                    name, subdomains)));
609 610 611
        }
    }

612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
    virtual pair<bool, int> startUpdateZone(const std::string& zone_name,
                                            bool replace)
    {
        const pair<bool, int> zone_info = getZone(zone_name);
        if (!zone_info.first) {
            return (pair<bool, int>(false, 0));
        }

        // Prepare the record set for update.  If replacing the existing one,
        // we use an empty set; otherwise we use a writable copy of the
        // original.
        if (replace) {
            update_records_->clear();
        } else {
            *update_records_ = *readonly_records_;
        }

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
629 630 631 632 633 634 635
        if (zone_name == "bad.example.org.") {
            return (pair<bool, int>(true, -1));
        } else if (zone_name == "null.example.org.") {
            return (pair<bool, int>(true, 13));
        } else {
            return (pair<bool, int>(true, WRITABLE_ZONE_ID));
        }
636
    }
637
    virtual void commit() {
638 639
        *readonly_records_ = *update_records_;
    }
640
    virtual void rollback() {
641 642 643 644 645 646 647
        // Special hook: if something with a name of "throw.example.org"
        // has been added, trigger an imaginary unexpected event with an
        // exception.
        if (update_records_->count("throw.example.org.") > 0) {
            isc_throw(DataSourceError, "unexpected failure in rollback");
        }

648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
        rollbacked_ = true;
    }
    virtual void addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) {
        // Copy the current value to cur_name.  If it doesn't exist,
        // operator[] will create a new one.
        cur_name_ = (*update_records_)[columns[DatabaseAccessor::ADD_NAME]];

        vector<string> record_columns;
        record_columns.push_back(columns[DatabaseAccessor::ADD_TYPE]);
        record_columns.push_back(columns[DatabaseAccessor::ADD_TTL]);
        record_columns.push_back(columns[DatabaseAccessor::ADD_SIGTYPE]);
        record_columns.push_back(columns[DatabaseAccessor::ADD_RDATA]);
        record_columns.push_back(columns[DatabaseAccessor::ADD_NAME]);

        // copy back the added entry
        cur_name_.push_back(record_columns);
        (*update_records_)[columns[DatabaseAccessor::ADD_NAME]] = cur_name_;

        // remember this one so that test cases can check it.
        copy(columns, columns + DatabaseAccessor::ADD_COLUMN_COUNT,
             columns_lastadded_);
    }

    // Helper predicate class used in deleteRecordInZone().
    struct deleteMatch {
        deleteMatch(const string& type, const string& rdata) :
            type_(type), rdata_(rdata)
        {}
        bool operator()(const vector<string>& row) const {
            return (row[0] == type_ && row[3] == rdata_);
        }
        const string& type_;
        const string& rdata_;
    };

    virtual void deleteRecordInZone(const string (&params)[DEL_PARAM_COUNT]) {
        vector<vector<string> >& records =
            (*update_records_)[params[DatabaseAccessor::DEL_NAME]];
        records.erase(remove_if(records.begin(), records.end(),
                                deleteMatch(
                                    params[DatabaseAccessor::DEL_TYPE],
                                    params[DatabaseAccessor::DEL_RDATA])),
                      records.end());
        if (records.empty()) {
            (*update_records_).erase(params[DatabaseAccessor::DEL_NAME]);
        }
    }

696 697 698 699 700 701 702
    //
    // Helper methods to keep track of some update related activities
    //
    bool isRollbacked() const {
        return (rollbacked_);
    }

703
    const string* getLastAdded() const {
704 705 706 707
        return (columns_lastadded_);
    }

    // This allows the test code to get the accessor used in an update context
708
    boost::shared_ptr<const MockAccessor> getLatestClone() const {
709 710 711
        return (latest_clone_);
    }

712
    virtual std::string findPreviousName(int id, const std::string& rname)
713 714 715 716
        const
    {
        if (id == -1) {
            isc_throw(isc::NotImplemented, "Test not implemented behaviour");
717 718 719 720
        } else if (id == READONLY_ZONE_ID) {
            // For some specific names we intentionally return broken or
            // unexpected result.
            if (rname == "org.example.badnsec2.") {
721
                return ("badnsec1.example.org.");
722 723
            } else if (rname == "org.example.brokenname.") {
                return ("brokenname...example.org.");
724 725
            } else if (rname == "org.example.notimplnsec." ||
                       rname == "org.example.wild.here.") {
726
                isc_throw(isc::NotImplemented, "Not implemented in this test");
727 728 729 730 731 732 733 734 735 736 737 738
            }

            // For the general case, we search for the first name N in the
            // domains that meets N >= reverse(rname) using lower_bound.
            // The "previous name" is the name of the previous entry of N.
            // Note that Domains are internally sorted by the Name comparison
            // order.  Due to the API requirement we are given a reversed
            // name (rname), so we need to reverse it again to convert it
            // to the original name.
            Domains::const_iterator it(readonly_records_->lower_bound(
                                           Name(rname).reverse().toText()));
            if (it == readonly_records_->begin()) {
739 740
                isc_throw(isc::Unexpected, "Unexpected name");
            }
741 742 743 744
            if (it == readonly_records_->end()) {
                return ((*readonly_records_->rbegin()).first);
            }
            return ((*(--it)).first);
745 746 747 748
        } else {
            isc_throw(isc::Unexpected, "Unknown zone ID");
        }
    }
749 750 751 752 753 754 755 756 757
    virtual void addRecordDiff(int id, uint32_t serial,
                               DiffOperation operation,
                               const std::string (&data)[DIFF_PARAM_COUNT])
    {
        if (id == 13) { // The null zone doesn't support journaling
            isc_throw(isc::NotImplemented, "Test not implemented behaviour");
        } else if (id == -1) { // Bad zone throws
            isc_throw(DataSourceError, "Test error");
        } else {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
758
            journal_entries_->push_back(JournalEntry(id, serial, operation,
759
                                                     data));
760 761 762
        }
    }

763 764 765 766 767 768 769 770 771
    virtual IteratorContextPtr getDiffs(int id, uint32_t start,
                                        uint32_t end) const
    {
        vector<JournalEntry> selected_jnl;

        for (vector<JournalEntry>::const_iterator it =
                 journal_entries_->begin();
             it != journal_entries_->end(); ++it)
        {
772 773 774 775 776 777 778 779 780 781 782
            // For simplicity we assume this method is called for the
            // "readonly" zone possibly after making updates on the "writable"
            // copy and committing them.
            if (id != READONLY_ZONE_ID) {
                continue;
            }

            // Note: the following logic is not 100% accurate in terms of
            // serial number arithmetic; we prefer brevity for testing.
            // Skip until we see the starting serial.  Once we started
            // recording this condition is ignored (to support wrap-around
JINMEI Tatuya's avatar
JINMEI Tatuya committed
783 784
            // case).  Also, it ignores the RR type; it only checks the
            // versions.
785
            if ((*it).serial_ < start && selected_jnl.empty()) {
786 787
                continue;
            }
788 789 790
            if ((*it).serial_ > end) { // gone over the end serial. we're done.
                break;
            }
791
            selected_jnl.push_back(*it);
792
        }
793 794 795 796 797 798 799

        // Check if we've found the requested range.  If not, throw.
        if (selected_jnl.empty() || selected_jnl.front().serial_ != start ||
            selected_jnl.back().serial_ != end) {
            isc_throw(NoSuchSerial, "requested diff range is not found");
        }

800
        return (IteratorContextPtr(new MockDiffIteratorContext(selected_jnl)));
801 802 803
    }

    // Check the journal is as expected and clear the journal
804
    void checkJournal(const std::vector<JournalEntry> &expected) const {
805 806
        std::vector<JournalEntry> journal;
        // Clean the journal, but keep local copy to check
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
807
        journal.swap(*journal_entries_);
808
        ASSERT_EQ(expected.size(), journal.size());
809
        for (size_t i = 0; i < expected.size(); ++ i) {
810 811 812
            EXPECT_TRUE(expected[i] == journal[i]);
        }
    }
813

814
private:
815 816
    // The following member variables are storage and/or update work space
    // of the test zone.  The "master"s are the real objects that contain
817
    // the data, and they are shared among all accessors cloned from
818 819
    // an initially created one.  The "copy" data will be used for read-only
    // transaction.  The pointer members allow the sharing.
820 821 822 823 824
    // "readonly" is for normal lookups.  "update" is the workspace for
    // updates.  When update starts it will be initialized either as an
    // empty set (when replacing the entire zone) or as a copy of the
    // "readonly" one.  "empty" is a sentinel to produce negative results.
    Domains readonly_records_master_;
825
    Domains readonly_records_copy_;
826 827 828 829 830 831
    Domains* readonly_records_;
    Domains update_records_master_;
    Domains* update_records_;
    const Domains empty_records_master_;
    const Domains* empty_records_;

832
    // The journal data
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
833 834
    std::vector<JournalEntry> journal_entries_master_;
    std::vector<JournalEntry>* journal_entries_;
835

836 837 838
    // used as temporary storage after searchForRecord() and during
    // getNextRecord() calls, as well as during the building of the
    // fake data
839 840 841
    std::vector< std::vector<std::string> > cur_name_;

    // The columns that were most recently added via addRecordToZone()
842
    string columns_lastadded_[ADD_COLUMN_COUNT];
843 844 845 846 847 848 849 850

    // Whether rollback operation has been performed for the database.
    // Not useful except for purely testing purpose.
    bool rollbacked_;

    // Remember the mock accessor that was last cloned
    boost::shared_ptr<MockAccessor> latest_clone_;

851 852 853
    // Internal flag for duplicate check
    bool did_transaction_;

854 855 856 857 858 859 860 861
    const Domains& getMockRecords(int zone_id) const {
        if (zone_id == READONLY_ZONE_ID) {
            return (*readonly_records_);
        } else if (zone_id == WRITABLE_ZONE_ID) {
            return (*update_records_);
        }
        return (*empty_records_);
    }
862

863 864 865
    // Adds one record to the current name in the database
    // The actual data will not be added to 'records' until
    // addCurName() is called
866 867
    void addRecord(const std::string& type,
                   const std::string& ttl,
868 869 870 871
                   const std::string& sigtype,
                   const std::string& rdata) {
        std::vector<std::string> columns;
        columns.push_back(type);
872
        columns.push_back(ttl);
873 874
        columns.push_back(sigtype);
        columns.push_back(rdata);
875
        cur_name_.push_back(columns);
876 877
    }

878
    // Adds all records we just built with calls to addRecords
879
    // to the actual fake database. This will clear cur_name_,
880
    // so we can immediately start adding new records.
881
    void addCurName(const std::string& name) {
882
        ASSERT_EQ(0, readonly_records_->count(name));
883 884
        // Append the name to all of them
        for (std::vector<std::vector<std::string> >::iterator
885
             i = cur_name_.begin(); i != cur_name_.end(); ++ i) {
886 887
            i->push_back(name);
        }
888 889
        (*readonly_records_)[name] = cur_name_;
        cur_name_.clear();
890 891
    }

892 893 894 895 896 897 898 899 900 901
    // Fills the database with zone data.
    // This method constructs a number of resource records (with addRecord),
    // which will all be added for one domain name to the fake database
    // (with addCurName). So for instance the first set of calls create
    // data for the name 'www.example.org', which will consist of one A RRset
    // of one record, and one AAAA RRset of two records.
    // The order in which they are added is the order in which getNextRecord()
    // will return them (so we can test whether find() etc. support data that
    // might not come in 'normal' order)
    // It shall immediately fail if you try to add the same name twice.
902
    void fillData() {
903 904 905 906 907 908 909 910 911 912 913
        const char* prev_name = NULL;
        for (int i = 0; TEST_RECORDS[i][0] != NULL; ++i) {
            if (prev_name != NULL &&
                strcmp(prev_name, TEST_RECORDS[i][0]) != 0) {
                addCurName(prev_name);
            }
            prev_name = TEST_RECORDS[i][0];
            addRecord(TEST_RECORDS[i][1], TEST_RECORDS[i][2],
                      TEST_RECORDS[i][3], TEST_RECORDS[i][4]);
        }
        addCurName(prev_name);
914
    }
915 916
};

917 918
// This tests the default getRecords behaviour, throwing NotImplemented
TEST(DatabaseConnectionTest, getRecords) {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
919
    EXPECT_THROW(NopAccessor().getRecords(".", 1, false),
920 921 922
                 isc::NotImplemented);
}

923 924
// This tests the default getAllRecords behaviour, throwing NotImplemented
TEST(DatabaseConnectionTest, getAllRecords) {
925
    // The parameters don't matter
926
    EXPECT_THROW(NopAccessor().getAllRecords(1),
927 928 929
                 isc::NotImplemented);
}

930 931 932 933
// This test fixture is templated so that we can share (most of) the test
// cases with different types of data sources.  Note that in test cases
// we need to use 'this' to refer to member variables of the test class.
template <typename ACCESSOR_TYPE>
934 935
class DatabaseClientTest : public ::testing::Test {
public:
936 937 938 939
    DatabaseClientTest() : zname_("example.org"), qname_("www.example.org"),
                           qclass_(RRClass::IN()), qtype_(RRType::A()),
                           rrttl_(3600)
    {
940
        createClient();
941 942 943 944 945 946 947 948 949 950 951 952

        // set up the commonly used finder.
        DataSourceClient::FindResult zone(client_->findZone(zname_));
        assert(zone.code == result::SUCCESS);
        finder_ = dynamic_pointer_cast<DatabaseClient::Finder>(
            zone.zone_finder);

        // Test IN/A RDATA to be added in update tests.  Intentionally using
        // different data than the initial data configured in the MockAccessor.
        rrset_.reset(new RRset(qname_, qclass_, qtype_, rrttl_));
        rrset_->addRdata(rdata::createRdata(rrset_->getType(),
                                            rrset_->getClass(), "192.0.2.2"));
953 954 955 956
        soa_.reset(new RRset(zname_, qclass_, RRType::SOA(), rrttl_));
        soa_->addRdata(rdata::createRdata(soa_->getType(), soa_->getClass(),
                                         "ns1.example.org. admin.example.org. "
                                         "1234 3600 1800 2419200 7200"));
957 958 959 960 961 962 963 964 965

        // And its RRSIG.  Also different from the configured one.
        rrsigset_.reset(new RRset(qname_, qclass_, RRType::RRSIG(),
                                  rrttl_));
        rrsigset_->addRdata(rdata::createRdata(rrsigset_->getType(),
                                               rrsigset_->getClass(),
                                               "A 5 3 0 20000101000000 "
                                               "20000201000000 0 example.org. "
                                               "FAKEFAKEFAKE"));
966
    }
967

968 969 970 971 972
    /*
     * We initialize the client from a function, so we can call it multiple
     * times per test.
     */
    void createClient() {
973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990
        // To make sure we always have empty diffs table at the beginning of
        // each test, we re-install the writable data source here.
        // Note: this is SQLite3 specific and a waste (though otherwise
        // harmless) for other types of data sources.  If and when we support
        // more types of data sources in this test framework, we should
        // probably move this to some specialized templated method specific
        // to SQLite3 (or for even a longer term we should add an API to
        // purge the diffs table).
        const char* const install_cmd = INSTALL_PROG " " TEST_DATA_DIR
            "/rwtest.sqlite3 " TEST_DATA_BUILDDIR
            "/rwtest.sqlite3.copied";
        if (system(install_cmd) != 0) {
            // any exception will do, this is failure in test setup, but nice
            // to show the command that fails, and shouldn't be caught
            isc_throw(isc::Exception,
                      "Error setting up; command failed: " << install_cmd);
        }

991 992
        current_accessor_ = new ACCESSOR_TYPE();
        is_mock_ = (dynamic_cast<MockAccessor*>(current_accessor_) != NULL);
993
        client_.reset(new DatabaseClient(qclass_,
994
                                         boost::shared_ptr<ACCESSOR_TYPE>(
995
                                             current_accessor_)));
996
    }
997

998 999
    /**
     * Check the zone finder is a valid one and references the zone ID and
1000
     * database available here.
1001 1002 1003
     */
    void checkZoneFinder(const DataSourceClient::FindResult& zone) {
        ASSERT_NE(ZoneFinderPtr(), zone.zone_finder) << "No zone finder";
1004
        boost::shared_ptr<DatabaseClient::Finder> finder(
1005
            dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
1006
        ASSERT_NE(boost::shared_ptr<DatabaseClient::Finder>(), finder) <<
1007
            "Wrong type of finder";
1008 1009 1010
        if (is_mock_) {
            EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id());
        }
1011
        EXPECT_EQ(current_accessor_, &finder->getAccessor());
1012
    }
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
1013

1014
    boost::shared_ptr<DatabaseClient::Finder> getFinder() {
1015
        DataSourceClient::FindResult zone(client_->findZone(zname_));
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
1016
        EXPECT_EQ(result::SUCCESS, zone.code);
1017
        boost::shared_ptr<DatabaseClient::Finder> finder(
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
1018
            dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
1019 1020 1021
        if (is_mock_) {
            EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id());
        }
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
1022 1023 1024 1025

        return (finder);
    }

1026
    // Helper methods for update tests
1027 1028 1029 1030 1031 1032 1033 1034
    bool isRollbacked(bool expected = false) const {
        if (is_mock_) {
            const MockAccessor& mock_accessor =
                dynamic_cast<const MockAccessor&>(*update_accessor_);
            return (mock_accessor.isRollbacked());
        } else {
            return (expected);
        }
1035 1036 1037
    }

    void checkLastAdded(const char* const expected[]) const {
1038 1039 1040 1041 1042 1043 1044
        if (is_mock_) {
            const MockAccessor* mock_accessor =
                dynamic_cast<const MockAccessor*>(current_accessor_);
            for (int i = 0; i < DatabaseAccessor::ADD_COLUMN_COUNT; ++i) {
                EXPECT_EQ(expected[i],
                          mock_accessor->getLatestClone()->getLastAdded()[i]);
            }
1045 1046 1047 1048
        }
    }

    void setUpdateAccessor() {
1049 1050 1051 1052 1053
        if (is_mock_) {
            const MockAccessor* mock_accessor =
                dynamic_cast<const MockAccessor*>(current_accessor_);
            update_accessor_ = mock_accessor->getLatestClone();
        }
1054 1055
    }

1056 1057 1058 1059 1060
    void checkJournal(const vector<JournalEntry>& expected) {
        if (is_mock_) {
            const MockAccessor* mock_accessor =
                dynamic_cast<const MockAccessor*>(current_accessor_);
            mock_accessor->checkJournal(expected);
1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
        } else {
            // For other generic databases, retrieve the diff using the
            // reader class and compare the resulting sequence of RRset.
            // For simplicity we only consider the case where the expected
            // sequence is not empty.
            ASSERT_FALSE(expected.empty());
            const Name zone_name(expected.front().
                                 data_[DatabaseAccessor::DIFF_NAME]);
            ZoneJournalReaderPtr jnl_reader =
                client_->getJournalReader(zone_name,
                                          expected.front().serial_,
                                          expected.back().serial_).second;
            ASSERT_TRUE(jnl_reader);
            ConstRRsetPtr rrset;
            vector<JournalEntry>::const_iterator it = expected.begin();
1076 1077 1078
            for (rrset = jnl_reader->getNextDiff();
                 rrset && it != expected.end();
                 rrset = jnl_reader->getNextDiff(), ++it) {
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
                typedef DatabaseAccessor Accessor;
                RRsetPtr expected_rrset(
                    new RRset(Name((*it).data_[Accessor::DIFF_NAME]),
                              qclass_,
                              RRType((*it).data_[Accessor::DIFF_TYPE]),
                              RRTTL((*it).data_[Accessor::DIFF_TTL])));
                expected_rrset->addRdata(
                    rdata::createRdata(expected_rrset->getType