PageRenderTime 57ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/rel/overlay/share/www/script/test/list_views.js

http://github.com/cloudant/bigcouch
JavaScript | 475 lines | 383 code | 52 blank | 40 comment | 42 complexity | 698fe197661f942714d23b8ecd760d06 MD5 | raw file
Possible License(s): Apache-2.0
  1. // Licensed under the Apache License, Version 2.0 (the "License"); you may not
  2. // use this file except in compliance with the License. You may obtain a copy of
  3. // the License at
  4. //
  5. // http://www.apache.org/licenses/LICENSE-2.0
  6. //
  7. // Unless required by applicable law or agreed to in writing, software
  8. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  9. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  10. // License for the specific language governing permissions and limitations under
  11. // the License.
  12. couchTests.list_views = function(debug) {
  13. var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"});
  14. db.deleteDb();
  15. db.createDb();
  16. if (debug) debugger;
  17. var designDoc = {
  18. _id:"_design/lists",
  19. language: "javascript",
  20. views : {
  21. basicView : {
  22. map : stringFun(function(doc) {
  23. emit(doc.integer, doc.string);
  24. })
  25. },
  26. withReduce : {
  27. map : stringFun(function(doc) {
  28. emit(doc.integer, doc.string);
  29. }),
  30. reduce : stringFun(function(keys, values, rereduce) {
  31. if (rereduce) {
  32. return sum(values);
  33. } else {
  34. return values.length;
  35. }
  36. })
  37. }
  38. },
  39. lists: {
  40. basicBasic : stringFun(function(head, req) {
  41. send("head");
  42. var row;
  43. while(row = getRow()) {
  44. log("row: "+toJSON(row));
  45. send(row.key);
  46. };
  47. return "tail";
  48. }),
  49. basicJSON : stringFun(function(head, req) {
  50. start({"headers":{"Content-Type" : "application/json"}});
  51. send('{"head":'+toJSON(head)+', ');
  52. send('"req":'+toJSON(req)+', ');
  53. send('"rows":[');
  54. var row, sep = '';
  55. while (row = getRow()) {
  56. send(sep + toJSON(row));
  57. sep = ', ';
  58. }
  59. return "]}";
  60. }),
  61. simpleForm: stringFun(function(head, req) {
  62. log("simpleForm");
  63. send('<ul>');
  64. var row, row_number = 0, prevKey, firstKey = null;
  65. while (row = getRow()) {
  66. row_number += 1;
  67. if (!firstKey) firstKey = row.key;
  68. prevKey = row.key;
  69. send('\n<li>Key: '+row.key
  70. +' Value: '+row.value
  71. +' LineNo: '+row_number+'</li>');
  72. }
  73. return '</ul><p>FirstKey: '+ firstKey + ' LastKey: '+ prevKey+'</p>';
  74. }),
  75. acceptSwitch: stringFun(function(head, req) {
  76. // respondWith takes care of setting the proper headers
  77. provides("html", function() {
  78. send("HTML <ul>");
  79. var row, num = 0;
  80. while (row = getRow()) {
  81. num ++;
  82. send('\n<li>Key: '
  83. +row.key+' Value: '+row.value
  84. +' LineNo: '+num+'</li>');
  85. }
  86. // tail
  87. return '</ul>';
  88. });
  89. provides("xml", function() {
  90. send('<feed xmlns="http://www.w3.org/2005/Atom">'
  91. +'<title>Test XML Feed</title>');
  92. while (row = getRow()) {
  93. var entry = new XML('<entry/>');
  94. entry.id = row.id;
  95. entry.title = row.key;
  96. entry.content = row.value;
  97. send(entry);
  98. }
  99. return "</feed>";
  100. });
  101. }),
  102. qsParams: stringFun(function(head, req) {
  103. return toJSON(req.query) + "\n";
  104. }),
  105. stopIter: stringFun(function(req) {
  106. send("head");
  107. var row, row_number = 0;
  108. while(row = getRow()) {
  109. if(row_number > 2) break;
  110. send(" " + row_number);
  111. row_number += 1;
  112. };
  113. return " tail";
  114. }),
  115. stopIter2: stringFun(function(head, req) {
  116. provides("html", function() {
  117. send("head");
  118. var row, row_number = 0;
  119. while(row = getRow()) {
  120. if(row_number > 2) break;
  121. send(" " + row_number);
  122. row_number += 1;
  123. };
  124. return " tail";
  125. });
  126. }),
  127. tooManyGetRows : stringFun(function() {
  128. send("head");
  129. var row;
  130. while(row = getRow()) {
  131. send(row.key);
  132. };
  133. getRow();
  134. getRow();
  135. getRow();
  136. row = getRow();
  137. return "after row: "+toJSON(row);
  138. }),
  139. emptyList: stringFun(function() {
  140. return " ";
  141. }),
  142. rowError : stringFun(function(head, req) {
  143. send("head");
  144. var row = getRow();
  145. send(fooBarBam); // intentional error
  146. return "tail";
  147. }),
  148. docReference : stringFun(function(head, req) {
  149. send("head");
  150. var row = getRow();
  151. send(row.doc.integer);
  152. return "tail";
  153. }),
  154. secObj: stringFun(function(head, req) {
  155. return toJSON(req.secObj);
  156. })
  157. }
  158. };
  159. var viewOnlyDesignDoc = {
  160. _id:"_design/views",
  161. language: "javascript",
  162. views : {
  163. basicView : {
  164. map : stringFun(function(doc) {
  165. emit(-doc.integer, doc.string);
  166. })
  167. }
  168. }
  169. };
  170. var erlListDoc = {
  171. _id: "_design/erlang",
  172. language: "erlang",
  173. lists: {
  174. simple:
  175. 'fun(Head, {Req}) -> ' +
  176. ' Send(<<"[">>), ' +
  177. ' Fun = fun({Row}, Sep) -> ' +
  178. ' Val = couch_util:get_value(<<"key">>, Row, 23), ' +
  179. ' Send(list_to_binary(Sep ++ integer_to_list(Val))), ' +
  180. ' {ok, ","} ' +
  181. ' end, ' +
  182. ' {ok, _} = FoldRows(Fun, ""), ' +
  183. ' Send(<<"]">>) ' +
  184. 'end.'
  185. }
  186. };
  187. T(db.save(designDoc).ok);
  188. var docs = makeDocs(0, 10);
  189. db.bulkSave(docs);
  190. var view = db.view('lists/basicView');
  191. T(view.total_rows == 10);
  192. // standard get
  193. var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/basicBasic/basicView");
  194. T(xhr.status == 200, "standard get should be 200");
  195. T(/head0123456789tail/.test(xhr.responseText));
  196. // test that etags are available
  197. var etag = xhr.getResponseHeader("etag");
  198. xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/basicBasic/basicView", {
  199. headers: {"if-none-match": etag}
  200. });
  201. T(xhr.status == 304);
  202. // confirm ETag changes with different POST bodies
  203. xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/basicBasic/basicView",
  204. {body: JSON.stringify({keys:[1]})}
  205. );
  206. var etag1 = xhr.getResponseHeader("etag");
  207. xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/basicBasic/basicView",
  208. {body: JSON.stringify({keys:[2]})}
  209. );
  210. var etag2 = xhr.getResponseHeader("etag");
  211. T(etag1 != etag2, "POST to map _list generates key-depdendent ETags");
  212. // test the richness of the arguments
  213. xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/basicJSON/basicView?update_seq=true");
  214. T(xhr.status == 200, "standard get should be 200");
  215. var resp = JSON.parse(xhr.responseText);
  216. TEquals(10, resp.head.total_rows);
  217. TEquals(0, resp.head.offset);
  218. TEquals(11, resp.head.update_seq);
  219. T(resp.rows.length == 10);
  220. TEquals(resp.rows[0], {"id": "0","key": 0,"value": "0"});
  221. TEquals(resp.req.info.db_name, "test_suite_db");
  222. TEquals(resp.req.method, "GET");
  223. TEquals(resp.req.path, [
  224. "test_suite_db",
  225. "_design",
  226. "lists",
  227. "_list",
  228. "basicJSON",
  229. "basicView"
  230. ]);
  231. T(resp.req.headers.Accept);
  232. T(resp.req.headers.Host);
  233. T(resp.req.headers["User-Agent"]);
  234. T(resp.req.cookie);
  235. // get with query params
  236. xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView?startkey=3&endkey=8");
  237. T(xhr.status == 200, "with query params");
  238. T(!(/Key: 1/.test(xhr.responseText)));
  239. T(/FirstKey: 3/.test(xhr.responseText));
  240. T(/LastKey: 8/.test(xhr.responseText));
  241. // with 0 rows
  242. var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView?startkey=30");
  243. T(xhr.status == 200, "0 rows");
  244. T(/<\/ul>/.test(xhr.responseText));
  245. //too many Get Rows
  246. var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/tooManyGetRows/basicView");
  247. T(xhr.status == 200, "tooManyGetRows");
  248. T(/9after row: null/.test(xhr.responseText));
  249. // reduce with 0 rows
  250. var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?startkey=30");
  251. T(xhr.status == 200, "reduce 0 rows");
  252. T(/LastKey: undefined/.test(xhr.responseText));
  253. // when there is a reduce present, but not used
  254. var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?reduce=false");
  255. T(xhr.status == 200, "reduce false");
  256. T(/Key: 1/.test(xhr.responseText));
  257. // when there is a reduce present, and used
  258. xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true");
  259. T(xhr.status == 200, "group reduce");
  260. T(/Key: 1/.test(xhr.responseText));
  261. // there should be etags on reduce as well
  262. var etag = xhr.getResponseHeader("etag");
  263. T(etag, "Etags should be served with reduce lists");
  264. xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true", {
  265. headers: {"if-none-match": etag}
  266. });
  267. T(xhr.status == 304);
  268. // confirm ETag changes with different POST bodies
  269. xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true",
  270. {body: JSON.stringify({keys:[1]})}
  271. );
  272. var etag1 = xhr.getResponseHeader("etag");
  273. xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true",
  274. {body: JSON.stringify({keys:[2]})}
  275. );
  276. var etag2 = xhr.getResponseHeader("etag");
  277. T(etag1 != etag2, "POST to reduce _list generates key-depdendent ETags");
  278. // verify the etags expire correctly
  279. var docs = makeDocs(11, 12);
  280. db.bulkSave(docs);
  281. xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=true", {
  282. headers: {"if-none-match": etag}
  283. });
  284. T(xhr.status == 200, "reduce etag");
  285. // empty list
  286. var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/emptyList/basicView");
  287. T(xhr.responseText.match(/^ $/));
  288. xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/emptyList/withReduce?group=true");
  289. T(xhr.responseText.match(/^ $/));
  290. // multi-key fetch
  291. var xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/simpleForm/basicView", {
  292. body: '{"keys":[2,4,5,7]}'
  293. });
  294. T(xhr.status == 200, "multi key");
  295. T(!(/Key: 1 /.test(xhr.responseText)));
  296. T(/Key: 2/.test(xhr.responseText));
  297. T(/FirstKey: 2/.test(xhr.responseText));
  298. T(/LastKey: 7/.test(xhr.responseText));
  299. // multi-key fetch with GET
  300. var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView" +
  301. "?keys=[2,4,5,7]");
  302. T(xhr.status == 200, "multi key");
  303. T(!(/Key: 1 /.test(xhr.responseText)));
  304. T(/Key: 2/.test(xhr.responseText));
  305. T(/FirstKey: 2/.test(xhr.responseText));
  306. T(/LastKey: 7/.test(xhr.responseText));
  307. // no multi-key fetch allowed when group=false
  308. xhr = CouchDB.request("POST", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?group=false", {
  309. body: '{"keys":[2,4,5,7]}'
  310. });
  311. T(xhr.status == 400);
  312. T(/query_parse_error/.test(xhr.responseText));
  313. var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/rowError/basicView");
  314. T(/ReferenceError/.test(xhr.responseText));
  315. // with include_docs and a reference to the doc.
  316. var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/docReference/basicView?include_docs=true");
  317. T(xhr.responseText.match(/head0tail/));
  318. // now with extra qs params
  319. var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/qsParams/basicView?foo=blam");
  320. T(xhr.responseText.match(/blam/));
  321. var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter/basicView");
  322. // T(xhr.getResponseHeader("Content-Type") == "text/plain");
  323. T(xhr.responseText.match(/^head 0 1 2 tail$/) && "basic stop");
  324. xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter2/basicView", {
  325. headers : {
  326. "Accept" : "text/html"
  327. }
  328. });
  329. T(xhr.responseText.match(/^head 0 1 2 tail$/) && "stop 2");
  330. // aborting iteration with reduce
  331. var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter/withReduce?group=true");
  332. T(xhr.responseText.match(/^head 0 1 2 tail$/) && "reduce stop");
  333. xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter2/withReduce?group=true", {
  334. headers : {
  335. "Accept" : "text/html"
  336. }
  337. });
  338. T(xhr.responseText.match(/^head 0 1 2 tail$/) && "reduce stop 2");
  339. // with accept headers for HTML
  340. xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/acceptSwitch/basicView", {
  341. headers: {
  342. "Accept": 'text/html'
  343. }
  344. });
  345. T(xhr.getResponseHeader("Content-Type") == "text/html; charset=utf-8");
  346. T(xhr.responseText.match(/HTML/));
  347. T(xhr.responseText.match(/Value/));
  348. // now with xml
  349. xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/acceptSwitch/basicView", {
  350. headers: {
  351. "Accept": 'application/xml'
  352. }
  353. });
  354. T(xhr.getResponseHeader("Content-Type") == "application/xml");
  355. T(xhr.responseText.match(/XML/));
  356. T(xhr.responseText.match(/entry/));
  357. // Test we can run lists and views from separate docs.
  358. T(db.save(viewOnlyDesignDoc).ok);
  359. var url = "/test_suite_db/_design/lists/_list/simpleForm/views/basicView" +
  360. "?startkey=-3";
  361. xhr = CouchDB.request("GET", url);
  362. T(xhr.status == 200, "multiple design docs.");
  363. T(!(/Key: -4/.test(xhr.responseText)));
  364. T(/FirstKey: -3/.test(xhr.responseText));
  365. T(/LastKey: 0/.test(xhr.responseText));
  366. // Test we do multi-key requests on lists and views in separate docs.
  367. var url = "/test_suite_db/_design/lists/_list/simpleForm/views/basicView";
  368. xhr = CouchDB.request("POST", url, {
  369. body: '{"keys":[-2,-4,-5,-7]}'
  370. });
  371. T(xhr.status == 200, "multi key separate docs");
  372. T(!(/Key: -3/.test(xhr.responseText)));
  373. T(/Key: -7/.test(xhr.responseText));
  374. T(/FirstKey: -2/.test(xhr.responseText));
  375. T(/LastKey: -7/.test(xhr.responseText));
  376. // Test if secObj is available
  377. var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/secObj/basicView");
  378. T(xhr.status == 200, "standard get should be 200");
  379. var resp = JSON.parse(xhr.responseText);
  380. T(typeof(resp) == "object");
  381. var erlViewTest = function() {
  382. T(db.save(erlListDoc).ok);
  383. var url = "/test_suite_db/_design/erlang/_list/simple/views/basicView" +
  384. "?startkey=-3";
  385. xhr = CouchDB.request("GET", url);
  386. T(xhr.status == 200, "multiple languages in design docs.");
  387. var list = JSON.parse(xhr.responseText);
  388. T(list.length == 4);
  389. for(var i = 0; i < list.length; i++)
  390. {
  391. T(list[i] + 3 == i);
  392. }
  393. };
  394. run_on_modified_server([{
  395. section: "native_query_servers",
  396. key: "erlang",
  397. value: "{couch_native_process, start_link, []}"
  398. }], erlViewTest);
  399. // COUCHDB-1113
  400. var ddoc = {
  401. _id: "_design/test",
  402. views: {
  403. me: {
  404. map: (function(doc) { emit(null,null)}).toString()
  405. }
  406. },
  407. lists: {
  408. you: (function(head, req) {
  409. var row;
  410. while(row = getRow()) {
  411. send(row);
  412. }
  413. }).toString()
  414. }
  415. };
  416. db.save(ddoc);
  417. var resp = CouchDB.request("GET", "/" + db.name + "/_design/test/_list/you/me", {
  418. headers: {
  419. "Content-Type": "application/x-www-form-urlencoded"
  420. }
  421. });
  422. TEquals(200, resp.status, "should return a 200 response");
  423. };