blob: ed1e72f3f6d173c0490f8fb9a4bea71d41aedf95 [file] [log] [blame]
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
couchTests.design_docs = function(debug) {
var db_name = get_random_db_name();
var db_name_a = get_random_db_name();
var db = new CouchDB(db_name, {"X-Couch-Full-Commit":"false"});
var db2 = new CouchDB(db_name_a, {"X-Couch-Full-Commit":"false"});
if (debug) debugger;
db.createDb();
db2.createDb();
/*
var server_config = [
{
section: "query_server_config",
key: "reduce_limit",
value: "false"
}
];
*/
// var testFun = function() {
var numDocs = 500;
function makebigstring(power) {
var str = "a";
while(power-- > 0) {
str = str + str;
}
return str;
}
var designDoc = {
_id: "_design/test",
language: "javascript",
whatever : {
stringzone : "exports.string = 'plankton';",
commonjs : {
whynot : "exports.test = require('../stringzone'); " +
"exports.foo = require('whatever/stringzone');",
upper : "exports.testing = require('./whynot').test.string.toUpperCase()+" +
"module.id+require('./whynot').foo.string",
circular_one: "require('./circular_two'); exports.name = 'One';",
circular_two: "require('./circular_one'); exports.name = 'Two';"
},
// paths relative to parent
idtest1: {
a: {
b: {d: "module.exports = require('../c/e').id;"},
c: {e: "exports.id = module.id;"}
}
},
// multiple paths relative to parent
idtest2: {
a: {
b: {d: "module.exports = require('../../a/c/e').id;"},
c: {e: "exports.id = module.id;"}
}
},
// paths relative to module
idtest3: {
a: {
b: "module.exports = require('./c/d').id;",
c: {
d: "module.exports = require('./e');",
e: "exports.id = module.id;"
}
}
},
// paths relative to module and parent
idtest4: {
a: {
b: "module.exports = require('../a/./c/d').id;",
c: {
d: "module.exports = require('./e');",
e: "exports.id = module.id;"
}
}
},
// paths relative to root
idtest5: {
a: "module.exports = require('whatever/idtest5/b').id;",
b: "exports.id = module.id;"
}
},
views: {
all_docs_twice: {
map:
(function(doc) {
emit(doc.integer, null);
emit(doc.integer, null);
}).toString()
},
no_docs: {
map:
(function(doc) {
}).toString()
},
single_doc: {
map:
(function(doc) {
if (doc._id === "1") {
emit(1, null);
}
}).toString()
},
summate: {
map:
(function(doc) {
emit(doc.integer, doc.integer);
}).toString(),
reduce:
(function(keys, values) {
return sum(values);
}).toString()
},
summate2: {
map:
(function(doc) {
emit(doc.integer, doc.integer);
}).toString(),
reduce:
(function(keys, values) {
return sum(values);
}).toString()
},
huge_src_and_results: {
map:
(function(doc) {
if (doc._id === "1") {
emit(makebigstring(16), null);
}
}).toString(),
reduce:
(function(keys, values) {
return makebigstring(16);
}).toString()
},
lib : {
baz : "exports.baz = 'bam';",
foo : {
foo : "exports.foo = 'bar';",
boom : "exports.boom = 'ok';",
zoom : "exports.zoom = 'yeah';"
}
},
commonjs : {
map :
(function(doc) {
emit(null, require('views/lib/foo/boom').boom);
}).toString()
}
},
shows: {
simple:
(function() {
return 'ok';
}).toString(),
requirey:
(function() {
var lib = require('whatever/commonjs/upper');
return lib.testing;
}).toString(),
circular:
(function() {
var lib = require('whatever/commonjs/upper');
return JSON.stringify(this);
}).toString(),
circular_require:
(function() {
return require('whatever/commonjs/circular_one').name;
}).toString(),
idtest1: (function() {
return require('whatever/idtest1/a/b/d');
}).toString(),
idtest2: (function() {
return require('whatever/idtest2/a/b/d');
}).toString(),
idtest3: (function() {
return require('whatever/idtest3/a/b');
}).toString(),
idtest4: (function() {
return require('whatever/idtest4/a/b');
}).toString(),
idtest5: (function() {
return require('whatever/idtest5/a');
}).toString()
}
}; // designDoc
var xhr = CouchDB.request(
"PUT", "/" + db_name_a + "/_design/test", {body: JSON.stringify(designDoc)}
);
var resp = JSON.parse(xhr.responseText);
TEquals(resp.rev, db.save(designDoc).rev);
// test commonjs require
xhr = CouchDB.request("GET", "/" + db_name + "/_design/test/_show/requirey");
T(xhr.status == 200);
TEquals("PLANKTONwhatever/commonjs/upperplankton", xhr.responseText);
xhr = CouchDB.request("GET", "/" + db_name + "/_design/test/_show/circular");
T(xhr.status == 200);
TEquals("javascript", JSON.parse(xhr.responseText).language);
// test circular commonjs dependencies
xhr = CouchDB.request(
"GET",
"/" + db_name + "/_design/test/_show/circular_require"
);
TEquals(200, xhr.status);
TEquals("One", xhr.responseText);
// test module id values are as expected:
xhr = CouchDB.request("GET", "/" + db_name + "/_design/test/_show/idtest1");
TEquals(200, xhr.status);
TEquals("whatever/idtest1/a/c/e", xhr.responseText);
xhr = CouchDB.request("GET", "/" + db_name + "/_design/test/_show/idtest2");
TEquals(200, xhr.status);
TEquals("whatever/idtest2/a/c/e", xhr.responseText);
xhr = CouchDB.request("GET", "/" + db_name + "/_design/test/_show/idtest3");
TEquals(200, xhr.status);
TEquals("whatever/idtest3/a/c/e", xhr.responseText);
xhr = CouchDB.request("GET", "/" + db_name + "/_design/test/_show/idtest4");
TEquals(200, xhr.status);
TEquals("whatever/idtest4/a/c/e", xhr.responseText);
xhr = CouchDB.request("GET", "/" + db_name + "/_design/test/_show/idtest5");
TEquals(200, xhr.status);
TEquals("whatever/idtest5/b", xhr.responseText);
var prev_view_sig = db.designInfo("_design/test").view_index.signature;
var prev_view_size = db.designInfo("_design/test").view_index.disk_size;
db.bulkSave(makeDocs(1, numDocs + 1));
T(db.ensureFullCommit().ok);
// test that we get correct design doc info back,
// and also that GET /db/_design/test/_info
// hasn't triggered an update of the views
db.view("test/summate", {stale: "ok"}); // make sure view group's open
for (var i = 0; i < 2; i++) {
var dinfo = db.designInfo("_design/test");
TEquals("test", dinfo.name);
var vinfo = dinfo.view_index;
TEquals(prev_view_size, vinfo.disk_size, "view group disk size didn't change");
TEquals(false, vinfo.compact_running);
TEquals(prev_view_sig, vinfo.signature, 'ddoc sig');
// wait some time (there were issues where an update
// of the views had been triggered in the background)
var start = new Date().getTime();
while (new Date().getTime() < start + 2000);
TEquals(0, db.view("test/all_docs_twice", {stale: "ok"}).total_rows, 'view info');
TEquals(0, db.view("test/single_doc", {stale: "ok"}).total_rows, 'view info');
TEquals(0, db.view("test/summate", {stale: "ok"}).rows.length, 'view info');
T(db.ensureFullCommit().ok);
// restartServer();
};
db.bulkSave(makeDocs(numDocs + 1, numDocs * 2 + 1));
T(db.ensureFullCommit().ok);
// open view group
db.view("test/summate", {stale: "ok"});
// wait so the views can get initialized
var start = new Date().getTime();
while (new Date().getTime() < start + 2000);
// test that POST /db/_view_cleanup
// doesn't trigger an update of the views
var len1 = db.view("test/all_docs_twice", {stale: "ok"}).total_rows;
var len2 = db.view("test/single_doc", {stale: "ok"}).total_rows;
var len3 = db.view("test/summate", {stale: "ok"}).rows.length;
for (i = 0; i < 2; i++) {
T(db.viewCleanup().ok);
// wait some time (there were issues where an update
// of the views had been triggered in the background)
start = new Date().getTime();
while (new Date().getTime() < start + 2000);
TEquals(len1, db.view("test/all_docs_twice", {stale: "ok"}).total_rows, 'view cleanup');
TEquals(len2, db.view("test/single_doc", {stale: "ok"}).total_rows, 'view cleanup');
TEquals(len3, db.view("test/summate", {stale: "ok"}).rows.length, 'view cleanup');
T(db.ensureFullCommit().ok);
// restartServer();
// we'll test whether the view group stays closed
// and the views stay uninitialized (they should!)
len1 = len2 = len3 = 0;
};
// test commonjs in map functions
resp = db.view("test/commonjs", {limit:1});
T(resp.rows[0].value == 'ok');
// test that the _all_docs view returns correctly with keys
var results = db.allDocs({startkey:"_design", endkey:"_design0"});
T(results.rows.length == 1);
for (i = 0; i < 2; i++) {
var rows = db.view("test/all_docs_twice").rows;
for (var j = 0; j < numDocs; j++) {
T(rows[2 * j].key == (j + 1));
T(rows[(2 * j) + 1].key == (j + 1));
};
T(db.view("test/no_docs").total_rows == 0);
T(db.view("test/single_doc").total_rows == 1);
T(db.ensureFullCommit().ok);
// restartServer();
};
// test when language not specified, Javascript is implied
var designDoc2 = {
_id: "_design/test2",
// language: "javascript",
views: {
single_doc: {
map:
(function(doc) {
if (doc._id === "1") {
emit(1, null);
}
}).toString()
}
}
};
T(db.save(designDoc2).ok);
T(db.view("test2/single_doc").total_rows == 1);
var summate = function(N) {
return (N + 1) * (N / 2);
};
var result = db.view("test/summate");
T(result.rows[0].value == summate(numDocs * 2));
result = db.view("test/summate", {startkey: 4, endkey: 4});
T(result.rows[0].value == 4);
result = db.view("test/summate", {startkey: 4, endkey: 5});
T(result.rows[0].value == 9);
result = db.view("test/summate", {startkey: 4, endkey: 6});
T(result.rows[0].value == 15);
// test start_key and end_key aliases
result = db.view("test/summate", {start_key: 4, end_key: 6});
T(result.rows[0].value == 15);
// Verify that a shared index (view def is an exact copy of "summate")
// does not confuse the reduce stage
result = db.view("test/summate2", {startkey: 4, endkey: 6});
T(result.rows[0].value == 15);
for(i = 1; i < (numDocs / 2); i += 30) {
result = db.view("test/summate", {startkey: i, endkey: (numDocs - i)});
T(result.rows[0].value == summate(numDocs - i) - summate(i - 1));
}
T(db.deleteDoc(designDoc).ok);
waitForSuccess(function() {
var ddoc = db.open(designDoc._id)
if (ddoc != null) {
throw({});
}
return true;
}, 'db.open(designDoc._id)');
T(db.view("test/no_docs") == null);
T(db.ensureFullCommit().ok);
// restartServer();
T(db.open(designDoc._id) == null);
T(db.view("test/no_docs") == null);
// trigger ddoc cleanup
T(db.viewCleanup().ok);
//}; // enf of testFun
// not used now as we don't have modifications so far (would have to put them in)
//run_on_modified_server(server_config, testFun);
// COUCHDB-1227 - if a design document is deleted, by adding a "_deleted"
// field with the boolean value true, its validate_doc_update functions
// should no longer have effect.
db.deleteDb();
// avoid Heisenbugs w/ files remaining - create a new name
db_name = get_random_db_name();
db = new CouchDB(db_name, {"X-Couch-Full-Commit":"false"});
db.createDb();
var ddoc = {
_id: "_design/test",
language: "javascript",
validate_doc_update: (function(newDoc, oldDoc, userCtx, secObj) {
if (newDoc.value % 2 == 0) {
throw({forbidden: "dont like even numbers"});
}
return true;
}).toString()
};
TEquals(true, db.save(ddoc).ok);
try {
db.save({_id: "doc1", value: 4});
T(false, "doc insertion should have failed");
} catch (x) {
TEquals("forbidden", x.error);
}
var doc = db.open("doc1");
TEquals(null, doc);
ddoc._deleted = true;
TEquals(true, db.save(ddoc).ok);
try {
TEquals(true, db.save({_id: "doc1", value: 4}).ok);
} catch (x) {
T(false, "doc insertion should have succeeded");
}
doc = db.open("doc1");
TEquals(true, doc !== null, "doc was not persisted");
TEquals(4, doc.value);
// cleanup
db.deleteDb();
db2.deleteDb();
};