Giter VIP home page Giter VIP logo

clj-http-fake's Introduction

clj-http-fake's People

Contributors

valpackett avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

clj-http-fake's Issues

Query params with spaces are not handled correctly

Query params with spaces do not match the way they're supposed to.

Failing test here.

I think the best fix would be to stop doing string comparisons to match query strings, and instead parse requests as much as possible so we can do data comparisons and not worry about different ways of encoding things.

A short term fix could be something like this, but that might also break other things.

Please republish

Can you please update the version as pull request #32 is important to get our tests working with the newer clj-http client.

Can't Fake Calls Via Another Namespace

Hi, I'm trying to mock http calls from somewhere inside namespace (rather than explicitly like in the example). ie. My test goes...

(ns test.namespace
  (:use my.namespace
          clj-http.fake
          clojure.test))

(deftest test-something
  (is (= [] (this-function-calls-clj-http))))

Is this possible? Or do I need to use clj-http explicitly in the test to get the mocking?

Method support and different behaviours when no matching route

I was thinking it would be really nice to have a couple of extra features in clj-http-fake. In particular, I'd like to be able to support different routes for different http verbs so that I could specify different handlers for GET vs. POST. My current idea is to have something like:

(with-fake-routes
 [(get "http://example.com" (fn [req] ...))
  (post "http://example.com" (fn [req] ...))
  (any "http://example.com/resource" (fn [req ...))]
 (some-body))

Also, the current behaviour when no route is matched is to fall through to the real http implementation. Whilst this might be desired in many cases, often in my tests I want to be isolate myself from the outside world completely. I'd quite like the ability to configure cclj-http-fake to throw instead of passing through to the real request handler...following on from the above, this could be achieved with something like:

(with-fake-routes
 [(get "http://example.com" (fn [req] ...))
  (default :throw)]
 (some-body))

vs.

(with-fake-routes
 [(get "http://example.com" (fn [req] ...))
  (default :fall-through)]
 (some-body))

or something along those lines.

What do you think? I'm happy to make the changes, I just wanted to get your opinion first.

Thanks,
Toby

Release to Clojars

Hello.

Later features such as :query-params have not been released and are unavailable on Clojars. It's been a while since the last release, are you considering a new one soon?

Query strings with many params drastically increase runtime

First off, great library; it's made my life significantly easier. So I thought that I'd combine it with quick check.

I'm testing an API that takes at least 6 different parameters, sometimes more depending on the particular testing scenario, and I noticed that my test runtimes would increase dramatically, and to the point where they simply wouldn't terminate.

I eventually traced this back to the number of query parameters the API under test was producing. The long and short of it is, as I'm sure you're aware, that potential-query-strings-for combined with the cartesian-product function produce something like N! address-strings which were then being matched on, where N is the number of query parameters.

Here is a test to demonstrate the issue (it takes an extremely long time to complete):

(deftest matches-many-qparams
  (let [num-qparams 10]
    (is (= (with-fake-routes
             {#"http://test/\?.*"
              (fn [request]
                {:status 200 :headers {} :body "29RQPV"})}
             (:body (http/request
                      {:uri "http://test/"
                       :query-params (zipmap (map str (range 0 num-qparams))
                                             (range 0 num-qparams))
                       :method :get})))
           "29RQPV"))))

Also, since all the default implementations of RouteMatcher eventually delegate to the Pattern impl, they all suffer from this problem.

Of course, the workaround is to just supply my own implementation, but I feel like there's an opportunity to help my fellow users out.


I was curious if you would be open to a PR which:

  1. Improved string matching performance by reordering query params in a deterministic fashion. This should be doable for both address and request
  2. Extended the default Pattern implementation to first compute an address-string with deterministically ordered query params, and if matching failed, only then attempt to match with the whole set of address-strings. The idea is that it's not possible to rearrange address, but by eagerly producing an address-string we may be able to match quickly. In a similar vein, making potential-query-strings-for lazy may accomplish the same thing
  3. (I haven't thought much about the map case, yet)

request body is wrapped in StringEntity

Hi there,

I've been trying to use clj-http-fake to wrap POST requests with form data, using ring's wrap-params middleware to parse the form. However this fails because the request body is being wrapped in a StringEntity object which wrap-params blows up on. Here is my failing test:

(ns clj-http-fake-demo.core-test
  (:require [clojure.test :refer :all]
            [ring.middleware.params :refer (wrap-params)]
            [ring.util.response :refer (response status)]
            [clj-http.fake :refer (with-fake-routes)]
            [clj-http.client :as http]))

(deftest form-body-test
  (let [fake-server (wrap-params
                     (fn [req]
                       (-> (response (get-in req [:form-params "foo"] "null"))
                           (status 201))))]
    (with-fake-routes {#".*" fake-server}
      (let [resp (http/post "http://example.com/" {:form-params {:foo "bar"}})]
        (is (= (:status resp) 201))
        (is (= (:body resp) "bar"))))))

The stacktrace I get starts like this:

  actual: java.lang.IllegalArgumentException: Cannot open <#<StringEntity org.apache.http.entity.StringEntity@8864944>> as an InputStream.
 at clojure.java.io/fn (io.clj:173)
    clojure.java.io$fn__8577$G__8542__8584.invoke (io.clj:73)
    clojure.java.io/fn (io.clj:169)
    clojure.java.io$fn__8551$G__8546__8558.invoke (io.clj:73)
    clojure.java.io$reader.doInvoke (io.clj:106)
    clojure.lang.RestFn.applyTo (RestFn.java:139)
    clojure.core$apply.invoke (core.clj:619)
    clojure.core$slurp.doInvoke (core.clj:6278)
    clojure.lang.RestFn.invoke (RestFn.java:439)
    ring.middleware.params$assoc_form_params.invoke (params.clj:29)
    ring.middleware.params$params_request.doInvoke (params.clj:39)
    clojure.lang.RestFn.invoke (RestFn.java:423)
    ring.middleware.params$wrap_params$fn__5209.invoke (params.clj:58)
    clj_http.fake$try_intercept.invoke (fake.clj:116)

Which I believe is caused by a call to slurp in ring.middleware.params.

This has been discussed on irc before, where the suggestion was made that you could wrap the app in a custom middleware to fix the body to unwrap the StringEntity. That led me to write this test, which passes as expected:

(defn wrap-fix-body [app]
  (fn [req]
    (letfn [(fix-body [body]
              (if (isa? (class body) org.apache.http.entity.StringEntity)
                (.getContent body)
                body))]
      (app (update-in req [:body] fix-body)))))

(deftest fixed-form-body-test
  (let [fake-server (wrap-fix-body
                     (wrap-params
                      (fn [req]
                        (-> (response (get-in req [:form-params "foo"] "null"))
                            (status 201)))))]
    (with-fake-routes {#".*" fake-server}
      (let [resp (http/post "http://example.com/" {:form-params {:foo "bar"}})]
        (is (= (:status resp) 201))
        (is (= (:body resp) "bar"))))))

Is this something that clj-http-fake should be doing for the caller?

Supporting async requests from clj-http.

Hi!
Would be nice to add support for async requests from clj-http.
It requires some additions to the API that could be a bit challenging but still useful in asynchronous scenarios.
In my use-case it can improve things a lot in terms of tests backward-compatibility while moving certain parts of the application to clj-http/3.x and async requests.

Non-string response bodies don't work

Iโ€™m trying to test some code that looks like this:

(c/get
  "http://example.com"
  {:async?             true
   :as                 :transit+msgpack
   :accept             :transit+msgpack}
  (fn [r]
    ;; do something with (:body r), a map
    )
  (fn [e]
    ;; ...
    ))

It relies on clj-httpโ€™s :as key to parse the response as transit+msgpack and pass the result to the response function. However, clj-http.fake thinks the body is always a string:

                      clj-http.fake/handle-request-for-route                 fake.clj:  163
                                    clj-http.fake/body-bytes                 fake.clj:  144
                                    clj-http.fake/utf8-bytes                 fake.clj:  127
java.lang.ClassCastException: clojure.lang.PersistentVector cannot be cast to java.lang.String

clj-http.fake should only do this bytes thing if the body is a string.

I can make a PR for this later.

Misleading error message when calling URL that wasn't mocked

If I evaluate following code

(with-fake-routes
  {"http://google.com"
   (fn [request]
     {:status 200 :headers {} :body "R1BWm0"})}
  (:body (http/get "http://google.cz/")))

I get following error:

1. Unhandled clojure.lang.ArityException
   Wrong number of args (4) passed to: fake/try-intercept

                  AFn.java:  429  clojure.lang.AFn/throwArity
                  AFn.java:   44  clojure.lang.AFn/invoke
                  Var.java:  394  clojure.lang.Var/invoke
                  AFn.java:  165  clojure.lang.AFn/applyToHelper
                  Var.java:  700  clojure.lang.Var/applyTo
                  core.clj:  648  clojure.core/apply
                  core.clj:  641  clojure.core/apply
                 hooke.clj:   40  robert.hooke/compose-hooks/fn
               RestFn.java:  137  clojure.lang.RestFn/applyTo
                  core.clj:  646  clojure.core/apply
                  core.clj:  641  clojure.core/apply
                 hooke.clj:   46  robert.hooke/run-hooks
                 hooke.clj:   45  robert.hooke/run-hooks
                 hooke.clj:   54  robert.hooke/prepare-for-hooks/fn/fn
               RestFn.java:  137  clojure.lang.RestFn/applyTo
            AFunction.java:   29  clojure.lang.AFunction$1/doInvoke
               RestFn.java:  436  clojure.lang.RestFn/invoke
                  core.clj:  322  clj-http.core/request
                  core.clj:  321  clj-http.core/request
                  fake.clj:  173  clj-http.fake/try-intercept
                  fake.clj:  159  clj-http.fake/try-intercept
                  Var.java:  383  clojure.lang.Var/invoke
                  AFn.java:  156  clojure.lang.AFn/applyToHelper
                  Var.java:  700  clojure.lang.Var/applyTo
                  core.clj:  648  clojure.core/apply
                  core.clj:  641  clojure.core/apply
                 hooke.clj:   40  robert.hooke/compose-hooks/fn
               RestFn.java:  137  clojure.lang.RestFn/applyTo
                  core.clj:  646  clojure.core/apply
                  core.clj:  641  clojure.core/apply
                 hooke.clj:   46  robert.hooke/run-hooks
                 hooke.clj:   45  robert.hooke/run-hooks
                 hooke.clj:   54  robert.hooke/prepare-for-hooks/fn/fn
               RestFn.java:  137  clojure.lang.RestFn/applyTo
            AFunction.java:   29  clojure.lang.AFunction$1/doInvoke
               RestFn.java:  408  clojure.lang.RestFn/invoke
                  Var.java:  379  clojure.lang.Var/invoke
                client.clj: 1013  clj-http.client/wrap-request-timing/fn
                client.clj: 1046  clj-http.client/wrap-async-pooling/fn
               headers.clj:  147  clj-http.headers/wrap-header-map/fn
                client.clj:  773  clj-http.client/wrap-query-params/fn
                client.clj:  796  clj-http.client/wrap-basic-auth/fn
                client.clj:  813  clj-http.client/wrap-oauth/fn
                client.clj:  833  clj-http.client/wrap-user-info/fn
                client.clj:  965  clj-http.client/wrap-url/fn
                client.clj:  344  clj-http.client/wrap-redirects/fn
                client.clj:  397  clj-http.client/wrap-decompression/fn
                client.clj:  593  clj-http.client/wrap-input-coercion/fn
                client.clj:  648  clj-http.client/wrap-additional-header-parsing/fn
                client.clj:  537  clj-http.client/wrap-output-coercion/fn
                client.clj:  232  clj-http.client/wrap-exceptions/fn
                client.clj:  691  clj-http.client/wrap-accept/fn
                client.clj:  713  clj-http.client/wrap-accept-encoding/fn
                client.clj:  674  clj-http.client/wrap-content-type/fn
                client.clj:  915  clj-http.client/wrap-form-params/fn
                client.clj:  950  clj-http.client/wrap-nested-params/fn
                client.clj:  849  clj-http.client/wrap-method/fn
               cookies.clj:  131  clj-http.cookies/wrap-cookies/fn
                 links.clj:   63  clj-http.links/wrap-links/fn
                client.clj:  976  clj-http.client/wrap-unknown-host/fn
                client.clj: 1149  clj-http.client/request*
                client.clj: 1142  clj-http.client/request*
                client.clj: 1155  clj-http.client/get
                client.clj: 1151  clj-http.client/get
               RestFn.java:  410  clojure.lang.RestFn/invoke
...
             Compiler.java: 6927  clojure.lang.Compiler/eval
             Compiler.java: 6890  clojure.lang.Compiler/eval
                  core.clj: 3105  clojure.core/eval
                  core.clj: 3101  clojure.core/eval
                  main.clj:  240  clojure.main/repl/read-eval-print/fn
                  main.clj:  240  clojure.main/repl/read-eval-print
                  main.clj:  258  clojure.main/repl/fn
                  main.clj:  258  clojure.main/repl
                  main.clj:  174  clojure.main/repl
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   87  clojure.tools.nrepl.middleware.interruptible-eval/evaluate/fn
                  AFn.java:  152  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  core.clj:  646  clojure.core/apply
                  core.clj: 1881  clojure.core/with-bindings*
                  core.clj: 1881  clojure.core/with-bindings*
               RestFn.java:  425  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   85  clojure.tools.nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   55  clojure.tools.nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  222  clojure.tools.nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
    interruptible_eval.clj:  190  clojure.tools.nrepl.middleware.interruptible-eval/run-next/fn
                  AFn.java:   22  clojure.lang.AFn/run
   ThreadPoolExecutor.java: 1142  java.util.concurrent.ThreadPoolExecutor/runWorker
   ThreadPoolExecutor.java:  617  java.util.concurrent.ThreadPoolExecutor$Worker/run
               Thread.java:  745  java.lang.Thread/run

This error message is quite confusing. It would be great to know what's happening in this case.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.