1// Copyright 2011 Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9//   notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above copyright
11//   notice, this list of conditions and the following disclaimer in the
12//   documentation and/or other materials provided with the distribution.
13// * Neither the name of Google Inc. nor the names of its contributors
14//   may be used to endorse or promote products derived from this software
15//   without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29#include "store/backend.hpp"
30
31extern "C" {
32#include <sys/stat.h>
33}
34
35#include <atf-c++.hpp>
36
37#include "store/exceptions.hpp"
38#include "store/metadata.hpp"
39#include "utils/datetime.hpp"
40#include "utils/env.hpp"
41#include "utils/fs/operations.hpp"
42#include "utils/fs/path.hpp"
43#include "utils/logging/operations.hpp"
44#include "utils/sqlite/database.hpp"
45#include "utils/sqlite/statement.ipp"
46
47namespace datetime = utils::datetime;
48namespace fs = utils::fs;
49namespace logging = utils::logging;
50namespace sqlite = utils::sqlite;
51
52
53ATF_TEST_CASE_WITHOUT_HEAD(detail__backup_database__ok);
54ATF_TEST_CASE_BODY(detail__backup_database__ok)
55{
56    atf::utils::create_file("test.db", "The DB\n");
57    store::detail::backup_database(fs::path("test.db"), 13);
58    ATF_REQUIRE(fs::exists(fs::path("test.db")));
59    ATF_REQUIRE(fs::exists(fs::path("test.db.v13.backup")));
60    ATF_REQUIRE(atf::utils::compare_file("test.db.v13.backup", "The DB\n"));
61}
62
63
64ATF_TEST_CASE_WITHOUT_HEAD(detail__backup_database__ok_overwrite);
65ATF_TEST_CASE_BODY(detail__backup_database__ok_overwrite)
66{
67    atf::utils::create_file("test.db", "Original contents");
68    atf::utils::create_file("test.db.v1.backup", "Overwrite me");
69    store::detail::backup_database(fs::path("test.db"), 1);
70    ATF_REQUIRE(fs::exists(fs::path("test.db")));
71    ATF_REQUIRE(fs::exists(fs::path("test.db.v1.backup")));
72    ATF_REQUIRE(atf::utils::compare_file("test.db.v1.backup",
73                                         "Original contents"));
74}
75
76
77ATF_TEST_CASE_WITHOUT_HEAD(detail__backup_database__fail_open);
78ATF_TEST_CASE_BODY(detail__backup_database__fail_open)
79{
80    ATF_REQUIRE_THROW_RE(store::error, "Cannot open.*foo.db",
81                         store::detail::backup_database(fs::path("foo.db"), 5));
82}
83
84
85ATF_TEST_CASE(detail__backup_database__fail_create);
86ATF_TEST_CASE_HEAD(detail__backup_database__fail_create)
87{
88    set_md_var("require.user", "unprivileged");
89}
90ATF_TEST_CASE_BODY(detail__backup_database__fail_create)
91{
92    ATF_REQUIRE(::mkdir("dir", 0755) != -1);
93    atf::utils::create_file("dir/test.db", "Does not need to be valid");
94    ATF_REQUIRE(::chmod("dir", 0111) != -1);
95    ATF_REQUIRE_THROW_RE(
96        store::error, "Cannot create.*dir/test.db.v13.backup",
97        store::detail::backup_database(fs::path("dir/test.db"), 13));
98}
99
100
101ATF_TEST_CASE(detail__initialize__ok);
102ATF_TEST_CASE_HEAD(detail__initialize__ok)
103{
104    logging::set_inmemory();
105    set_md_var("require.files", store::detail::schema_file().c_str());
106}
107ATF_TEST_CASE_BODY(detail__initialize__ok)
108{
109    sqlite::database db = sqlite::database::in_memory();
110    const datetime::timestamp before = datetime::timestamp::now();
111    const store::metadata md = store::detail::initialize(db);
112    const datetime::timestamp after = datetime::timestamp::now();
113
114    ATF_REQUIRE(md.timestamp() >= before.to_seconds());
115    ATF_REQUIRE(md.timestamp() <= after.to_microseconds());
116    ATF_REQUIRE_EQ(store::detail::current_schema_version, md.schema_version());
117
118    // Query some known tables to ensure they were created.
119    db.exec("SELECT * FROM metadata");
120
121    // And now query some know values.
122    sqlite::statement stmt = db.create_statement(
123        "SELECT COUNT(*) FROM metadata");
124    ATF_REQUIRE(stmt.step());
125    ATF_REQUIRE_EQ(1, stmt.column_int(0));
126    ATF_REQUIRE(!stmt.step());
127}
128
129
130ATF_TEST_CASE_WITHOUT_HEAD(detail__initialize__missing_schema);
131ATF_TEST_CASE_BODY(detail__initialize__missing_schema)
132{
133    utils::setenv("KYUA_STOREDIR", "/non-existent");
134    store::detail::current_schema_version = 712;
135
136    sqlite::database db = sqlite::database::in_memory();
137    ATF_REQUIRE_THROW_RE(store::error,
138                         "Cannot open.*'/non-existent/schema_v712.sql'",
139                         store::detail::initialize(db));
140}
141
142
143ATF_TEST_CASE_WITHOUT_HEAD(detail__initialize__sqlite_error);
144ATF_TEST_CASE_BODY(detail__initialize__sqlite_error)
145{
146    utils::setenv("KYUA_STOREDIR", ".");
147    store::detail::current_schema_version = 712;
148
149    atf::utils::create_file("schema_v712.sql", "foo_bar_baz;\n");
150
151    sqlite::database db = sqlite::database::in_memory();
152    ATF_REQUIRE_THROW_RE(store::error, "Failed to initialize.*:.*foo_bar_baz",
153                         store::detail::initialize(db));
154}
155
156
157ATF_TEST_CASE_WITHOUT_HEAD(detail__migration_file__builtin);
158ATF_TEST_CASE_BODY(detail__migration_file__builtin)
159{
160    utils::unsetenv("KYUA_STOREDIR");
161    ATF_REQUIRE_EQ(fs::path(KYUA_STOREDIR) / "migrate_v5_v9.sql",
162                   store::detail::migration_file(5, 9));
163}
164
165
166ATF_TEST_CASE_WITHOUT_HEAD(detail__migration_file__overriden);
167ATF_TEST_CASE_BODY(detail__migration_file__overriden)
168{
169    utils::setenv("KYUA_STOREDIR", "/tmp/test");
170    ATF_REQUIRE_EQ(fs::path("/tmp/test/migrate_v5_v9.sql"),
171                   store::detail::migration_file(5, 9));
172}
173
174
175ATF_TEST_CASE_WITHOUT_HEAD(detail__schema_file__builtin);
176ATF_TEST_CASE_BODY(detail__schema_file__builtin)
177{
178    utils::unsetenv("KYUA_STOREDIR");
179    ATF_REQUIRE_EQ(fs::path(KYUA_STOREDIR) / "schema_v2.sql",
180                   store::detail::schema_file());
181}
182
183
184ATF_TEST_CASE_WITHOUT_HEAD(detail__schema_file__overriden);
185ATF_TEST_CASE_BODY(detail__schema_file__overriden)
186{
187    utils::setenv("KYUA_STOREDIR", "/tmp/test");
188    store::detail::current_schema_version = 123;
189    ATF_REQUIRE_EQ(fs::path("/tmp/test/schema_v123.sql"),
190                   store::detail::schema_file());
191}
192
193
194ATF_TEST_CASE(backend__open_ro__ok);
195ATF_TEST_CASE_HEAD(backend__open_ro__ok)
196{
197    logging::set_inmemory();
198    set_md_var("require.files", store::detail::schema_file().c_str());
199}
200ATF_TEST_CASE_BODY(backend__open_ro__ok)
201{
202    {
203        sqlite::database db = sqlite::database::open(
204            fs::path("test.db"), sqlite::open_readwrite | sqlite::open_create);
205        store::detail::initialize(db);
206    }
207    store::backend backend = store::backend::open_ro(fs::path("test.db"));
208    backend.database().exec("SELECT * FROM metadata");
209}
210
211
212ATF_TEST_CASE_WITHOUT_HEAD(backend__open_ro__missing_file);
213ATF_TEST_CASE_BODY(backend__open_ro__missing_file)
214{
215    ATF_REQUIRE_THROW_RE(store::error, "Cannot open 'missing.db': ",
216                         store::backend::open_ro(fs::path("missing.db")));
217    ATF_REQUIRE(!fs::exists(fs::path("missing.db")));
218}
219
220
221ATF_TEST_CASE(backend__open_ro__integrity_error);
222ATF_TEST_CASE_HEAD(backend__open_ro__integrity_error)
223{
224    logging::set_inmemory();
225    set_md_var("require.files", store::detail::schema_file().c_str());
226}
227ATF_TEST_CASE_BODY(backend__open_ro__integrity_error)
228{
229    {
230        sqlite::database db = sqlite::database::open(
231            fs::path("test.db"), sqlite::open_readwrite | sqlite::open_create);
232        store::detail::initialize(db);
233        db.exec("DELETE FROM metadata");
234    }
235    ATF_REQUIRE_THROW_RE(store::integrity_error, "metadata.*empty",
236                         store::backend::open_ro(fs::path("test.db")));
237}
238
239
240ATF_TEST_CASE(backend__open_rw__ok);
241ATF_TEST_CASE_HEAD(backend__open_rw__ok)
242{
243    logging::set_inmemory();
244    set_md_var("require.files", store::detail::schema_file().c_str());
245}
246ATF_TEST_CASE_BODY(backend__open_rw__ok)
247{
248    {
249        sqlite::database db = sqlite::database::open(
250            fs::path("test.db"), sqlite::open_readwrite | sqlite::open_create);
251        store::detail::initialize(db);
252    }
253    store::backend backend = store::backend::open_rw(fs::path("test.db"));
254    backend.database().exec("SELECT * FROM metadata");
255}
256
257
258ATF_TEST_CASE(backend__open_rw__create_missing);
259ATF_TEST_CASE_HEAD(backend__open_rw__create_missing)
260{
261    logging::set_inmemory();
262    set_md_var("require.files", store::detail::schema_file().c_str());
263}
264ATF_TEST_CASE_BODY(backend__open_rw__create_missing)
265{
266    store::backend backend = store::backend::open_rw(fs::path("test.db"));
267    backend.database().exec("SELECT * FROM metadata");
268}
269
270
271ATF_TEST_CASE(backend__open_rw__integrity_error);
272ATF_TEST_CASE_HEAD(backend__open_rw__integrity_error)
273{
274    logging::set_inmemory();
275    set_md_var("require.files", store::detail::schema_file().c_str());
276}
277ATF_TEST_CASE_BODY(backend__open_rw__integrity_error)
278{
279    {
280        sqlite::database db = sqlite::database::open(
281            fs::path("test.db"), sqlite::open_readwrite | sqlite::open_create);
282        store::detail::initialize(db);
283        db.exec("DELETE FROM metadata");
284    }
285    ATF_REQUIRE_THROW_RE(store::integrity_error, "metadata.*empty",
286                         store::backend::open_rw(fs::path("test.db")));
287}
288
289
290ATF_INIT_TEST_CASES(tcs)
291{
292    ATF_ADD_TEST_CASE(tcs, detail__backup_database__ok);
293    ATF_ADD_TEST_CASE(tcs, detail__backup_database__ok_overwrite);
294    ATF_ADD_TEST_CASE(tcs, detail__backup_database__fail_open);
295    ATF_ADD_TEST_CASE(tcs, detail__backup_database__fail_create);
296
297    ATF_ADD_TEST_CASE(tcs, detail__initialize__ok);
298    ATF_ADD_TEST_CASE(tcs, detail__initialize__missing_schema);
299    ATF_ADD_TEST_CASE(tcs, detail__initialize__sqlite_error);
300
301    ATF_ADD_TEST_CASE(tcs, detail__migration_file__builtin);
302    ATF_ADD_TEST_CASE(tcs, detail__migration_file__overriden);
303
304    ATF_ADD_TEST_CASE(tcs, detail__schema_file__builtin);
305    ATF_ADD_TEST_CASE(tcs, detail__schema_file__overriden);
306
307    ATF_ADD_TEST_CASE(tcs, backend__open_ro__ok);
308    ATF_ADD_TEST_CASE(tcs, backend__open_ro__missing_file);
309    ATF_ADD_TEST_CASE(tcs, backend__open_ro__integrity_error);
310    ATF_ADD_TEST_CASE(tcs, backend__open_rw__ok);
311    ATF_ADD_TEST_CASE(tcs, backend__open_rw__create_missing);
312    ATF_ADD_TEST_CASE(tcs, backend__open_rw__integrity_error);
313
314    // Tests for migrate_schema are in schema_inttest.  This is because, for
315    // such tests to be meaningful, they need to be integration tests and don't
316    // really fit the goal of this unit-test module.
317}
318