public void testDocWithAttachment() throws IOException {

    String inlineTextString = "Inline text string created by cblite functional test";

    send("PUT", "/db", Status.CREATED, null);

    Map<String, Object> attachment = new HashMap<String, Object>();
    attachment.put("content_type", "text/plain");
    attachment.put(
        "data", "SW5saW5lIHRleHQgc3RyaW5nIGNyZWF0ZWQgYnkgY2JsaXRlIGZ1bmN0aW9uYWwgdGVzdA==");

    Map<String, Object> attachments = new HashMap<String, Object>();
    attachments.put("inline.txt", attachment);

    Map<String, Object> docWithAttachment = new HashMap<String, Object>();
    docWithAttachment.put("_id", "docWithAttachment");
    docWithAttachment.put("text", inlineTextString);
    docWithAttachment.put("_attachments", attachments);

    Map<String, Object> result =
        (Map<String, Object>)
            sendBody("PUT", "/db/docWithAttachment", docWithAttachment, Status.CREATED, null);

    Map expChanges = new HashMap<String, Map<String, Object>>();
    List changesResults = new ArrayList();
    Map docChanges = new HashMap<String, Object>();
    docChanges.put("id", "docWithAttachment");
    docChanges.put("seq", 1);
    List lChanges = new ArrayList<Map<String, Object>>();
    HashMap mChanges = new HashMap<String, Object>();
    mChanges.put("rev", result.get("rev"));
    lChanges.add(mChanges);
    docChanges.put("changes", lChanges);
    changesResults.add(docChanges);
    expChanges.put("results", changesResults);
    expChanges.put("last_seq", 1);
    send("GET", "/db/_changes?feed=normal&heartbeat=300000&style=all_docs", Status.OK, expChanges);

    result = (Map<String, Object>) send("GET", "/db/docWithAttachment", Status.OK, null);
    Map<String, Object> attachmentsResult = (Map<String, Object>) result.get("_attachments");
    Map<String, Object> attachmentResult =
        (Map<String, Object>) attachmentsResult.get("inline.txt");

    // there should be either a content_type or content-type field.
    // https://github.com/couchbase/couchbase-lite-android-core/issues/12
    // content_type becomes null for attachments in responses, should be as set in Content-Type
    String contentTypeField = (String) attachmentResult.get("content_type");
    ;
    assertTrue(attachmentResult.containsKey("content_type"));
    assertNotNull(contentTypeField);

    URLConnection conn = sendRequest("GET", "/db/docWithAttachment/inline.txt", null, null);
    String contentType = conn.getHeaderField("Content-Type");
    assertNotNull(contentType);
    assertTrue(contentType.contains("text/plain"));

    StringWriter writer = new StringWriter();
    InputStream is = conn.getInputStream();
    IOUtils.copy(is, writer, "UTF-8");
    is.close();
    String responseString = writer.toString();
    assertTrue(responseString.contains(inlineTextString));
  }
  /** https://github.com/couchbase/couchbase-lite-java-core/issues/293 */
  public void testTotalRowsAttributeOnViewQuery() throws CouchbaseLiteException {
    send("PUT", "/db", Status.CREATED, null);

    // PUT:
    Map<String, Object> result;
    Map<String, Object> doc1 = new HashMap<String, Object>();
    doc1.put("message", "hello");
    result = (Map<String, Object>) sendBody("PUT", "/db/doc1", doc1, Status.CREATED, null);
    String revID = (String) result.get("rev");
    Map<String, Object> doc3 = new HashMap<String, Object>();
    doc3.put("message", "bonjour");
    result = (Map<String, Object>) sendBody("PUT", "/db/doc3", doc3, Status.CREATED, null);
    String revID3 = (String) result.get("rev");
    Map<String, Object> doc2 = new HashMap<String, Object>();
    doc2.put("message", "guten tag");
    result = (Map<String, Object>) sendBody("PUT", "/db/doc2", doc2, Status.CREATED, null);
    String revID2 = (String) result.get("rev");

    Database db = manager.getDatabase("db");
    View view = db.getView("design/view");
    view.setMapReduce(
        new Mapper() {
          @Override
          public void map(Map<String, Object> document, Emitter emitter) {
            emitter.emit(document.get("message"), null);
          }
        },
        null,
        "1");

    // Build up our expected result
    Map<String, Object> row1 = new HashMap<String, Object>();
    row1.put("id", "doc1");
    row1.put("key", "hello");
    Map<String, Object> row2 = new HashMap<String, Object>();
    row2.put("id", "doc2");
    row2.put("key", "guten tag");
    Map<String, Object> row3 = new HashMap<String, Object>();
    row3.put("id", "doc3");
    row3.put("key", "bonjour");

    List<Map<String, Object>> expectedRows = new ArrayList<Map<String, Object>>();
    expectedRows.add(row3);
    expectedRows.add(row2);
    // expectedRows.add(row1);

    Map<String, Object> expectedResult = new HashMap<String, Object>();
    expectedResult.put("offset", 0);
    expectedResult.put("total_rows", 3);
    expectedResult.put("rows", expectedRows);

    // Query the view and check the result:
    send("GET", "/db/_design/design/_view/view?limit=2", Status.OK, expectedResult);

    // Check the ETag:
    URLConnection conn = sendRequest("GET", "/db/_design/design/_view/view", null, null);
    String etag = conn.getHeaderField("Etag");
    assertEquals(String.format("\"%d\"", view.getLastSequenceIndexed()), etag);

    // Try a conditional GET:
    Map<String, String> headers = new HashMap<String, String>();
    headers.put("If-None-Match", etag);
    conn = sendRequest("GET", "/db/_design/design/_view/view", headers, null);
    assertEquals(Status.NOT_MODIFIED, conn.getResponseCode());

    // Update the database:
    Map<String, Object> doc4 = new HashMap<String, Object>();
    doc4.put("message", "aloha");
    result = (Map<String, Object>) sendBody("PUT", "/db/doc4", doc4, Status.CREATED, null);

    // Try a conditional GET:
    conn = sendRequest("GET", "/db/_design/design/_view/view?limit=2", headers, null);
    assertEquals(Status.OK, conn.getResponseCode());
    result = (Map<String, Object>) parseJSONResponse(conn);
    assertEquals(2, ((List) result.get("rows")).size());
    assertEquals(4, result.get("total_rows"));
  }