context("block parsing")

test_that("trimws works", {
  expect_equal(trimws("    hi there \t  "), "hi there")
  expect_equal(trimws("hi there\t"), "hi there")
  expect_equal(trimws("hi "), "hi")
})

test_that("plumbBlock works", {
  lines <- c(
    "#* Plumber comment not reached",
    "NULL",
    "#* Plumber comments",
    "#* Plumber description",
    "#* second line",
    "",
    "  ",
    "# Normal comments",
    "#' @get /",
    "#' @post /",
    "#' @filter test",
    "#' @serializer json")
  b <- plumbBlock(length(lines), lines)
  expect_length(b$paths, 2)
  # Paths order follow original code
  expect_equal(b$paths[[1]], list(verb="GET", path="/"))
  expect_equal(b$paths[[2]], list(verb="POST", path="/"))
  expect_equal(b$filter, "test")
  expect_equal(b$comments, "Plumber comments")
  expect_equal(b$description, "Plumber description\nsecond line")

  # due to covr changing some code, the return answer is very strange
  # the tests below should be skipped on covr
  testthat::skip_on_covr()

  expect_equal_functions(b$serializer, serializer_json())
})

test_that("plumbBlock images", {
  lines <- c("#'@png")
  expect_warning({
    b <- plumbBlock(length(lines), lines)
  })
  expect_equal(b$serializer, serializer_png())

  lines <- c("#'@jpeg")
  expect_warning({
    b <- plumbBlock(length(lines), lines)
  })
  expect_equal(b$serializer, serializer_jpeg())
  lines <- c("#'@png")
  expect_warning({
    b <- plumbBlock(length(lines), lines)
  })
  expect_equal(b$serializer, serializer_png())

  # Whitespace is fine
  lines <- c("#' @jpeg    \t ")
  expect_warning({
    b <- plumbBlock(length(lines), lines)
  })
  expect_equal(b$serializer, serializer_jpeg())

  # No whitespace is fine
  lines <- c("#' @jpeg(w=1)")
  expect_warning({
    b <- plumbBlock(length(lines), lines)
  })
  expect_equal(b$serializer, serializer_jpeg(w=1), check.environment=FALSE)

  # Additional chars after name don't count as image tags
  lines <- c("#' @jpegs")
  expect_error(
    expect_warning({plumbBlock(length(lines), lines)}),
    "Supplemental arguments to the serializer"
  )

  # Properly formatted arguments work
  lines <- c("#'@jpeg (width=100)")
  expect_warning({
    b <- plumbBlock(length(lines), lines)
  })
  expect_equal(b$serializer, serializer_jpeg(width = 100), check.environment=FALSE)

  # Ill-formatted arguments return a meaningful error
  lines <- c("#'@jpeg width=100")
  expect_error(
    expect_warning({plumbBlock(length(lines), lines)}),
    "Supplemental arguments to the serializer"
  )
})

test_that("Block can't be multiple mutually exclusive things", {

  srcref <- c(3,4)
  addE <- function(){ fail() }
  addF <- function(){ fail() }
  addA <- function(){ fail() }
  expect_error({
    evaluateBlock(srcref, c("#' @get /", "#' @assets /", "function(){}"),
                  function(){}, addE, addF, addA)
  }, "A single function can only be")

})

test_that("Block can't contain duplicate tags", {
  lines <- c("#* @tag test",
            "#* @tag test")
  expect_error(plumbBlock(length(lines), lines), "Duplicate tag specified.")
})

test_that("@json parameters work", {

  # due to covr changing some code, the return answer is very strange
  testthat::skip_on_covr()

  plumb_block_check <- function(lines) {
    if (grepl("@json", lines, fixed = TRUE)) {
      expect_warning(
        plumbBlock(length(lines), lines)
      )
    } else {
      plumbBlock(length(lines), lines)
    }
  }
  expect_block_fn <- function(lines, fn) {
    expect_equal_functions(plumb_block_check(lines)$serializer, fn)
  }
  expect_block_error <- function(lines, ...) {
    expect_error({
      plumb_block_check(lines)
    }, ...)
  }

  expect_block_fn("#' @serializer json", serializer_json())
  expect_block_fn("#' @json", serializer_json())
  expect_block_fn("#' @json()", serializer_json())
  expect_block_fn("#' @serializer unboxedJSON", serializer_unboxed_json())

  expect_block_fn("#' @serializer json list(na = 'string')", serializer_json(na = 'string'))
  expect_block_fn("#' @json(na = 'string')", serializer_json(na = 'string'))

  expect_block_fn("#* @serializer unboxedJSON list(na = \"string\")", serializer_unboxed_json(na = 'string'))
  expect_block_fn("#' @json(auto_unbox = TRUE, na = 'string')", serializer_json(auto_unbox = TRUE, na = 'string'))


  expect_block_fn("#' @json (    auto_unbox = TRUE, na = 'string'    )", serializer_json(auto_unbox = TRUE, na = 'string'))
  expect_block_fn("#' @json (auto_unbox          =       TRUE    ,      na      =      'string'   )             ", serializer_json(auto_unbox = TRUE, na = 'string'))
  expect_block_fn("#' @serializer json list   (      auto_unbox          =       TRUE    ,      na      =      'string'   )             ", serializer_json(auto_unbox = TRUE, na = 'string'))


  expect_block_error("#' @serializer json list(na = 'string'")
  expect_block_error("#' @json(na = 'string'", "must be surrounded by parentheses")
  expect_block_error("#' @json (na = 'string'", "must be surrounded by parentheses")
  expect_block_error("#' @json ( na = 'string'", "must be surrounded by parentheses")
  expect_block_error("#' @json na = 'string')", "must be surrounded by parentheses")
  expect_block_error("#' @json list(na = 'string')", "must be surrounded by parentheses")

})


test_that("@html parameters produce an error", {
  # due to covr changing some code, the return answer is very strange
  testthat::skip_on_covr()

  plumb_block_check <- function(lines) {
    if (grepl("@html", lines, fixed = TRUE)) {
      expect_warning(
        plumbBlock(length(lines), lines)
      )
    } else {
      plumbBlock(length(lines), lines)
    }
  }
  expect_block_fn <- function(lines, fn) {
    expect_equal_functions(plumb_block_check(lines)$serializer, fn)
  }
  expect_block_error <- function(lines, ...) {
    expect_error({
      plumb_block_check(lines)
    }, ...)
  }

  expect_block_fn("#' @serializer html", serializer_html())

  expect_block_fn("#' @serializer html list()", serializer_html())
  expect_block_fn("#' @serializer html list(         )", serializer_html())
  expect_block_fn("#' @serializer html list     (         )     ", serializer_html())

  expect_block_fn("#' @html", serializer_html())
  expect_block_fn("#' @html()", serializer_html())
  expect_block_fn("#' @html ()", serializer_html())
  expect_block_fn("#' @html ( )", serializer_html())
  expect_block_fn("#' @html ( ) ", serializer_html())
  expect_block_fn("#' @html         (       )       ", serializer_html())

  expect_block_error("#' @serializer html list(key = \"val\")", "unused argument")
  expect_block_error("#' @html(key = \"val\")", "unused argument")
  expect_block_error("#' @html (key = \"val\")", "unused argument")

  expect_block_error("#' @html (key = \"val\")", "unused argument")
})

test_that("@parser parameters produce an error or not", {
  # due to covr changing some code, the return answer is very strange
  testthat::skip_on_covr()

  expect_block_parser <- function(lines, fn) {
    b <- plumbBlock(length(lines), lines)
    expect_equal(b$parsers, fn)
  }
  expect_block_error <- function(lines, ...) {
    expect_error({
      plumbBlock(length(lines), lines)
    }, ...)
  }


  expected <- list(octet = list())
  expect_block_parser("#' @parser octet",  expected)

  expect_block_parser("#' @parser octet list()", expected)
  expect_block_parser("#' @parser octet list(         )", expected)
  expect_block_parser("#' @parser octet list     (         )     ", expected)

  expect_error({
    evaluateBlock(
      srcref = 3, # which evaluates to line 2
      file = c("#' @get /test", "#' @parser octet list(key = \"val\")"),
      expr = as.expression(substitute(identity)),
      envir = new.env(),
      addEndpoint = function(a, b, ...) { stop("should not reach here")},
      addFilter = as.null,
      pr = pr()
    )
  }, "unused argument (key = \"val\")", fixed = TRUE)
})
test_that("Plumbing block use the right environment", {
  expect_silent(plumb(test_path("files/plumb-envir.R")))
})


test_that("device serializers produce a structure", {
  # due to covr changing some code, the return answer is very strange
  testthat::skip_on_covr()

  expect_s3_block <- function(lines, serializer_fn) {
    block <- plumbBlock(length(lines), lines)
    expect_s3_class(block$serializer, "plumber_endpoint_serializer")
    serializer_info <- serializer_fn()
    expect_equal(block$serializer$serializer, serializer_info$serializer)
    expect_true(is.function(block$serializer$serializer))
    expect_equal(block$serializer$preexec_hook, serializer_info$preexec_hook)
    expect_equal(block$serializer$postexec_hook, serializer_info$postexec_hook)
  }

  expect_s3_block("#' @serializer jpeg", serializer_jpeg)
  expect_s3_block("#' @serializer png", serializer_png)
  expect_s3_block("#' @serializer svg", serializer_svg)
  expect_s3_block("#' @serializer bmp", serializer_bmp)
  expect_s3_block("#' @serializer tiff", serializer_tiff)
  expect_s3_block("#' @serializer pdf", serializer_pdf)
})

test_that("Tags can contains space", {
  lines <- c("#* @tag 'test space'",
             "#* @tag \"test space2\"")
  expect_equal(plumbBlock(length(lines), lines)$tags, c("test space", "test space2"))
})

test_that("single character tag and response", {
  lines <- c(
    "#' @tag a",
    "#' @response 2 b",
    "#' @response 4 b c")
  b <- plumbBlock(length(lines), lines)
  expect_equal(b$tags, "a")
  expect_equal(b$responses, list(`2` = list(description = "b"), `4` = list(description = "b c")))
})

test_that("block respect original order of lines for comments, tags and responses", {
  lines <- c(
    "#' @tag aaa",
    "#' @tag bbb",
    "#' comments first line",
    "#' comments second line",
    "#' comments third line",
    "#' @response 200 ok",
    "#' @response 404 not ok")
  b <- plumbBlock(length(lines), lines)
  expect_equal(b$comments, "comments first line")
  expect_equal(b$description, "comments second line\ncomments third line")
  expect_equal(b$tags, c("aaa", "bbb"))
  expect_equal(b$responses, list(`200`=list(description="ok"), `404` = list(description="not ok")))
})

test_that("srcref values are set while plumbing from a file", {

  root <- plumb_api("plumber", "01-append")
  endpt <- root$endpoints[[1]][[1]]
  expect_s3_class(endpt$srcref, "srcref")

  root_with_no_srcref <- pr() %>% pr_get("/", force)
  endpt_with_no_srcref <- root_with_no_srcref$endpoints[[1]][[1]]
  expect_equal(endpt_with_no_srcref$srcref, NULL)
})


# TODO: more testing around filter, assets, endpoint, etc.
