| defmodule UpdateDocumentsTest do |
| use CouchTestCase |
| |
| @ddoc %{ |
| _id: "_design/update", |
| language: "javascript", |
| updates: %{ |
| hello: """ |
| function(doc, req) { |
| if (!doc) { |
| if (req.id) { |
| return [ |
| // Creates a new document with the PUT docid, |
| { _id : req.id, |
| reqs : [req] }, |
| // and returns an HTML response to the client. |
| "<p>New World</p>"]; |
| }; |
| // |
| return [null, "<p>Empty World</p>"]; |
| }; |
| // we can update the document inline |
| doc.world = "hello"; |
| // we can record aspects of the request or use them in application logic. |
| doc.reqs && doc.reqs.push(req); |
| doc.edited_by = req.userCtx; |
| return [doc, "<p>hello doc</p>"]; |
| } |
| """, |
| "in-place": """ |
| function(doc, req) { |
| var field = req.query.field; |
| var value = req.query.value; |
| var message = "set "+field+" to "+value; |
| doc[field] = value; |
| return [doc, message]; |
| } |
| """, |
| "form-update": """ |
| function(doc, req) { |
| for (var field in req.form) { |
| doc[field] = req.form[field]; |
| } |
| var message = "updated doc from form"; |
| return [doc, message]; |
| } |
| """, |
| "bump-counter": """ |
| function(doc, req) { |
| if (!doc.counter) doc.counter = 0; |
| doc.counter += 1; |
| var message = "<h1>bumped it!</h1>"; |
| return [doc, message]; |
| } |
| """, |
| error: """ |
| function(doc, req) { |
| superFail.badCrash; |
| } |
| """, |
| "get-uuid": """ |
| function(doc, req) { |
| return [null, req.uuid]; |
| } |
| """, |
| "code-n-bump": """ |
| function(doc,req) { |
| if (!doc.counter) doc.counter = 0; |
| doc.counter += 1; |
| var message = "<h1>bumped it!</h1>"; |
| resp = {"code": 302, "body": message} |
| return [doc, resp]; |
| } |
| """, |
| "resp-code": """ |
| function(doc,req) { |
| resp = {"code": 302} |
| return [null, resp]; |
| } |
| """, |
| "resp-code-and-json": """ |
| function(doc,req) { |
| resp = {"code": 302, "json": {"ok": true}} |
| return [{"_id": req["uuid"]}, resp]; |
| } |
| """, |
| binary: """ |
| function(doc, req) { |
| var resp = { |
| "headers" : { |
| "Content-Type" : "application/octet-stream" |
| }, |
| "base64" : "aGVsbG8gd29ybGQh" // "hello world!" encoded |
| }; |
| return [doc, resp]; |
| } |
| """, |
| empty: """ |
| function(doc, req) { |
| return [{}, 'oops']; |
| } |
| """ |
| } |
| } |
| |
| @document %{word: "plankton", name: "Rusty"} |
| |
| @tag :with_db |
| test "update error invalid path", context do |
| db_name = context[:db_name] |
| create_doc(db_name, @ddoc) |
| |
| resp = Couch.post("/#{db_name}/_design/update/_update/") |
| assert resp.status_code == 404 |
| assert resp.body["reason"] == "Invalid path." |
| end |
| |
| @tag :with_db |
| test "update document", context do |
| db_name = context[:db_name] |
| create_doc(db_name, @ddoc) |
| {:ok, resp} = create_doc(db_name, @document) |
| docid = resp.body["id"] |
| |
| resp = Couch.put("/#{db_name}/_design/update/_update/hello/#{docid}") |
| assert resp.status_code == 201 |
| assert resp.body == "<p>hello doc</p>" |
| assert String.contains?(resp.headers["Content-Type"], "charset=utf-8") |
| assert resp.headers["X-Couch-Id"] == docid |
| |
| resp = Couch.get("/#{db_name}/#{docid}") |
| assert resp.status_code == 200 |
| assert resp.body["world"] == "hello" |
| |
| # Fix for COUCHDB-379 |
| assert String.starts_with?(resp.headers["Server"], "CouchDB") |
| |
| resp = Couch.put("/#{db_name}/_design/update/_update/hello") |
| assert resp.status_code == 200 |
| assert resp.body == "<p>Empty World</p>" |
| end |
| |
| @tag :with_db |
| test "GET is not allowed", context do |
| db_name = context[:db_name] |
| create_doc(db_name, @ddoc) |
| |
| resp = Couch.get("/#{db_name}/_design/update/_update/hello") |
| assert resp.body["error"] == "method_not_allowed" |
| end |
| |
| @tag :with_db |
| test "doc can be created", context do |
| db_name = context[:db_name] |
| create_doc(db_name, @ddoc) |
| |
| resp = Couch.get("/#{db_name}/nonExistingDoc") |
| assert resp.status_code == 404 |
| |
| resp = Couch.put("/#{db_name}/_design/update/_update/hello/nonExistingDoc") |
| assert resp.status_code == 201 |
| assert resp.body == "<p>New World</p>" |
| |
| resp = Couch.get("/#{db_name}/nonExistingDoc") |
| assert resp.status_code == 200 |
| end |
| |
| @tag :with_db |
| test "in place update", context do |
| db_name = context[:db_name] |
| create_doc(db_name, @ddoc) |
| |
| {:ok, resp} = create_doc(db_name, @document) |
| docid = resp.body["id"] |
| |
| resp = |
| Couch.put( |
| "/#{db_name}/_design/update/_update/in-place/#{docid}?field=title&value=test" |
| ) |
| |
| assert resp.status_code == 201 |
| assert resp.body == "set title to test" |
| resp = Couch.get("/#{db_name}/#{docid}") |
| assert resp.status_code == 200 |
| assert resp.body["title"] == "test" |
| end |
| |
| @tag :with_db |
| test "form update via application/x-www-form-urlencoded", context do |
| db_name = context[:db_name] |
| create_doc(db_name, @ddoc) |
| |
| {:ok, resp} = create_doc(db_name, @document) |
| docid = resp.body["id"] |
| |
| resp = |
| Couch.put( |
| "/#{db_name}/_design/update/_update/form-update/#{docid}", |
| headers: ["Content-Type": "application/x-www-form-urlencoded"], |
| body: "formfoo=bar&formbar=foo" |
| ) |
| |
| assert resp.status_code == 201 |
| assert resp.body == "updated doc from form" |
| |
| resp = Couch.get("/#{db_name}/#{docid}") |
| assert resp.status_code == 200 |
| assert resp.body["formfoo"] == "bar" |
| assert resp.body["formbar"] == "foo" |
| end |
| |
| @tag :with_db |
| test "bump counter", context do |
| db_name = context[:db_name] |
| create_doc(db_name, @ddoc) |
| |
| {:ok, resp} = create_doc(db_name, @document) |
| docid = resp.body["id"] |
| |
| resp = |
| Couch.put("/#{db_name}/_design/update/_update/bump-counter/#{docid}", |
| headers: ["X-Couch-Full-Commit": "true"] |
| ) |
| |
| assert resp.status_code == 201 |
| assert resp.body == "<h1>bumped it!</h1>" |
| |
| resp = Couch.get("/#{db_name}/#{docid}") |
| assert resp.status_code == 200 |
| assert resp.body["counter"] == 1 |
| |
| resp = |
| Couch.put("/#{db_name}/_design/update/_update/bump-counter/#{docid}", |
| headers: ["X-Couch-Full-Commit": "true"] |
| ) |
| |
| newrev = resp.headers["X-Couch-Update-NewRev"] |
| |
| resp = Couch.get("/#{db_name}/#{docid}") |
| assert resp.status_code == 200 |
| assert resp.body["counter"] == 2 |
| assert resp.body["_rev"] == newrev |
| end |
| |
| @tag :with_db |
| test "Server provides UUID when POSTing without an ID in the URL", context do |
| db_name = context[:db_name] |
| create_doc(db_name, @ddoc) |
| resp = Couch.put("/#{db_name}/_design/update/_update/get-uuid/") |
| assert resp.status_code == 200 |
| assert String.length(resp.body) == 32 |
| end |
| |
| @tag :with_db |
| test "COUCHDB-1229 - allow slashes in doc ids for update handlers", context do |
| db_name = context[:db_name] |
| create_doc(db_name, @ddoc) |
| |
| create_doc(db_name, %{_id: "with/slash", counter: 1}) |
| |
| resp = Couch.put("/#{db_name}/_design/update/_update/bump-counter/with/slash") |
| assert resp.status_code == 201 |
| assert resp.body == "<h1>bumped it!</h1>" |
| |
| resp = Couch.get("/#{db_name}/with%2Fslash") |
| assert resp.status_code == 200 |
| assert resp.body["counter"] == 2 |
| end |
| |
| @tag :with_db |
| test "COUCHDB-648 - the code in the JSON response should be honored", context do |
| db_name = context[:db_name] |
| create_doc(db_name, @ddoc) |
| |
| {:ok, resp} = create_doc(db_name, @document) |
| docid = resp.body["id"] |
| |
| Couch.put("/#{db_name}/_design/update/_update/bump-counter/#{docid}") |
| Couch.put("/#{db_name}/_design/update/_update/bump-counter/#{docid}") |
| |
| resp = Couch.put("/#{db_name}/_design/update/_update/code-n-bump/#{docid}") |
| assert resp.status_code == 302 |
| assert resp.body == "<h1>bumped it!</h1>" |
| |
| resp = Couch.get("/#{db_name}/#{docid}") |
| assert resp.status_code == 200 |
| assert resp.body["counter"] == 3 |
| |
| resp = Couch.put("/#{db_name}/_design/update/_update/resp-code/") |
| assert resp.status_code == 302 |
| |
| resp = Couch.put("/#{db_name}/_design/update/_update/resp-code-and-json/") |
| assert resp.status_code == 302 |
| assert resp.body["ok"] == true |
| end |
| |
| @tag :with_db |
| test "base64 response", context do |
| db_name = context[:db_name] |
| create_doc(db_name, @ddoc) |
| |
| {:ok, resp} = create_doc(db_name, @document) |
| docid = resp.body["id"] |
| |
| resp = |
| Couch.put("/#{db_name}/_design/update/_update/binary/#{docid}", |
| body: "rubbish" |
| ) |
| |
| assert resp.status_code == 201 |
| assert resp.body == "hello world!" |
| assert String.contains?(resp.headers["Content-Type"], "application/octet-stream") |
| end |
| |
| @tag :with_db |
| test "Insert doc with empty id", context do |
| db_name = context[:db_name] |
| create_doc(db_name, @ddoc) |
| |
| resp = Couch.put("/#{db_name}/_design/update/_update/empty/foo") |
| assert resp.status_code == 400 |
| assert resp.body["reason"] == "Document id must not be empty" |
| end |
| end |