From 544f03409cb734e0900a350e1e6db2efe5de32e4 Mon Sep 17 00:00:00 2001 From: Lars Andersen Date: Sun, 27 Jun 2021 17:59:50 +0200 Subject: [PATCH 1/6] Improve code formatting and expand a comment --- src/refactor_nrepl/artifacts.clj | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/refactor_nrepl/artifacts.clj b/src/refactor_nrepl/artifacts.clj index 57b3901d..f8e9def5 100644 --- a/src/refactor_nrepl/artifacts.clj +++ b/src/refactor_nrepl/artifacts.clj @@ -17,13 +17,15 @@ (let [lm (.lastModified (io/file file))] (if (zero? lm) nil lm))) -;; structure here is {"prismatic/schem" ["0.1.1" "0.2.0" ...]} -(defonce artifacts (atom (if (.exists (io/file artifacts-file)) - (->> artifacts-file slurp edn/read-string (into {})) - {}) - :meta {:last-modified - (get-last-modified-from-file artifacts-file)})) - +;; structure here is (mostly) {"prismatic/schem" ["0.1.1" "0.2.0" ...]} +;; The exceptions are the mvn based artifacts. There's a ratelimit in place +;; for those artifacts so we get the available versions on demand instead. +(defonce artifacts + (atom (if (.exists (io/as-file artifacts-file)) + (->> artifacts-file slurp edn/read-string (into {})) + {}) + :meta {:last-modified + (get-last-modified-from-file artifacts-file)})) (def millis-per-day (* 24 60 60 1000)) (defn- get-proxy-opts From 918939b4b5ecec11f7da3432cdfa513cb432f584 Mon Sep 17 00:00:00 2001 From: Lars Andersen Date: Sun, 27 Jun 2021 18:00:33 +0200 Subject: [PATCH 2/6] Rethrow error if Clojars isn't available --- src/refactor_nrepl/artifacts.clj | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/refactor_nrepl/artifacts.clj b/src/refactor_nrepl/artifacts.clj index f8e9def5..ef1f18cd 100644 --- a/src/refactor_nrepl/artifacts.clj +++ b/src/refactor_nrepl/artifacts.clj @@ -109,10 +109,11 @@ [artifact] (let [{:keys [body status]} @(http/get (str "https://clojars.org/api/artifacts/" artifact))] - (when (= 200 status) - (->> (json/read-str body :key-fn keyword) - :recent_versions - (keep :version))))) + (if (= 200 status) + (map :version (:recent_versions (json/read-str body :key-fn keyword))) + (throw (ex-info (str "Unexpected response from Clojars") + {:status status + :body body}))))) (defn- get-artifacts-from-clojars! [] From 51143f5059147b34153ac3f89489e0351fcdde42 Mon Sep 17 00:00:00 2001 From: Lars Andersen Date: Fri, 25 Jun 2021 09:37:36 +0200 Subject: [PATCH 3/6] Bring hotload-dependency back Previously this op was built on top of alembic, but that lib no longer seems to be maintained at all so we're moving to pomegranate. `tools.deps` would've been another alternative by their `add-lib` operation has yet to be merged to the main branch. Once it's merged and has proven itself in the wild nothing prevents us from moving over, though, if that solution proves to be better in some way. --- CHANGELOG.md | 1 + project.clj | 7 ++ src/refactor_nrepl/artifacts.clj | 109 +++++++++++++++++++++---- test/refactor_nrepl/artifacts_test.clj | 18 ++++ 4 files changed, 118 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e778a51a..3208bb56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ * Build ASTs more robustly (by using locks, `require`, and ruling out certain namespaces like refactor-nrepl itself) * Improve `namespace-aliases` performance and make it return more accurate results. * Honor internal `future-cancel` calls, improving overall responsiveness and stability. +* [clojure-emacs/clj-refactor.el#466](https://github.com/clojure-emacs/clj-refactor.el/issues/466): Bring the `hotload-dependency` operation back. ### Bugs fixed diff --git a/project.clj b/project.clj index 3f744574..6045d699 100644 --- a/project.clj +++ b/project.clj @@ -5,6 +5,13 @@ :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[nrepl "0.8.3"] ^:inline-dep [http-kit "2.5.3"] + ^:inline-dep [clj-commons/pomegranate "1.2.1" + :exclusions + [org.slf4j/jcl-over-slf4j + org.tcrawley/dynapath]] + ;; Override conflicting dep in Pomegranate + ^:inlined-dep [org.apache.httpcomponents/httpclient + "4.5.9" :exclusions [commons-logging]] ^:inline-dep [org.clojure/data.json "2.3.1"] ^:inline-dep [org.clojure/tools.analyzer.jvm "1.1.0"] ^:inline-dep [org.clojure/tools.namespace "1.1.0" :exclusions [org.clojure/tools.reader]] diff --git a/src/refactor_nrepl/artifacts.clj b/src/refactor_nrepl/artifacts.clj index ef1f18cd..16251405 100644 --- a/src/refactor_nrepl/artifacts.clj +++ b/src/refactor_nrepl/artifacts.clj @@ -1,12 +1,18 @@ (ns refactor-nrepl.artifacts - (:require [clojure.data.json :as json] - [clojure - [edn :as edn] - [string :as str]] - [clojure.java.io :as io] - [org.httpkit.client :as http] - [version-clj.core :as versions]) - (:import java.util.zip.GZIPInputStream)) + (:require + [cemerick.pomegranate :as pomegranate] + [clojure.data.json :as json] + [clojure.edn :as edn] + [clojure.java.io :as io] + [clojure.string :as str] + [clojure.tools.namespace.find :as find] + [org.httpkit.client :as http] + [refactor-nrepl.ns.slam.hound.regrow :as slamhound-regrow] + [refactor-nrepl.ns.slam.hound.search :as slamhound] + [version-clj.core :as versions]) + (:import java.io.File + java.util.jar.JarFile + java.util.zip.GZIPInputStream)) (def artifacts-file (str (io/file (System/getProperty "java.io.tmpdir") "refactor-nrepl-artifacts-cache"))) @@ -139,19 +145,88 @@ (update-artifact-cache!)) (->> @artifacts keys list*)) +(defn- artifact-versions* [artifact-id] + (->> (or (get @artifacts artifact-id) + (seq (get-mvn-versions! artifact-id)) + (get-clojars-versions! artifact-id)) + distinct + versions/version-sort + reverse + list*)) + (defn artifact-versions "Returns a sorted list of artifact version strings. The list can either come from the artifacts cache, the maven search api or the clojars search api in that order." [{:keys [artifact]}] - (->> (or (get @artifacts artifact) - (seq (get-mvn-versions! artifact)) - (get-clojars-versions! artifact)) - distinct - versions/version-sort - reverse - list*)) + (artifact-versions* artifact)) + +(defn- jar-at-the-top-of-dependency-hierarchy [new-deps] + ;; We only need to consider the dep at the top of the hierarchy because when we + ;; require those namespaces the rest of the transitive deps will get pulled in + ;; too. + (letfn [(->jar [^File f] + (JarFile. f))] + (let [top-level-dep (-> new-deps keys first)] + (-> top-level-dep meta :file ->jar)))) + +(defn- make-resolve-missing-aware-of-new-deps! + "Once the deps are available on cp we still have to load them and + reset slamhound's cache to make resolve-missing work." + [^JarFile jar] + (doseq [new-namespace (find/find-namespaces-in-jarfile jar)] + (try + (require new-namespace) + (catch Exception _ + ;; I've seen this happen after adding core.async as a dependency. + ;; It also happens if you try to require namespaces that no longer work, + ;; like compojure.handler. + ;; A failure here isn't a big deal, it only means that resolve-missing + ;; isn't going to work until the namespace has been loaded manually. + ))) + (slamhound/reset) + (slamhound-regrow/clear-cache!)) + +(defn- parse-coordinates [coordinates-str] + (let [coords (try (->> coordinates-str edn/read-string (take 2) vec) + (catch Exception _))] + (if (and (= (count coords) 2) + (symbol? (first coords)) + (string? (second coords))) + coords + (throw (IllegalArgumentException. (str "Malformed dependency vector: " + coordinates-str)))))) + +(defn- ensure-coordinates-exist! + [[artifact-id artifact-version :as coordinates]] + (when (stale-cache?) + (update-artifact-cache!)) + (if-let [versions (artifact-versions* (str artifact-id))] + (if ((set versions) artifact-version) + coordinates + (throw (IllegalArgumentException. + (str "Version " artifact-version + " does not exist for " artifact-id + ". Available versions are " (pr-str (vec versions)))))) + (throw (IllegalArgumentException. (str "Can't find artifact '" + artifact-id "'"))))) + +(defn- add-dependencies! [coordinates] + ;; Just so we can mock this out during testing + (let [repos {"clojars" "https://clojars.org/repo" + "central" "https://repo1.maven.org/maven2/"}] + (pomegranate/add-dependencies + :coordinates [coordinates] :repositories repos))) + +(defn- hotload-dependency! [coordinates] + (-> (add-dependencies! coordinates) + jar-at-the-top-of-dependency-hierarchy + make-resolve-missing-aware-of-new-deps!)) (defn hotload-dependency - [] - (throw (IllegalArgumentException. "Temporarily disabled until a solution for java 10 is found."))) + [{:keys [coordinates]}] + (->> coordinates + parse-coordinates + ensure-coordinates-exist! + (hotload-dependency!) + (str/join " "))) diff --git a/test/refactor_nrepl/artifacts_test.clj b/test/refactor_nrepl/artifacts_test.clj index 83e35d54..d6de534e 100644 --- a/test/refactor_nrepl/artifacts_test.clj +++ b/test/refactor_nrepl/artifacts_test.clj @@ -75,3 +75,21 @@ (is (nil? (#'artifacts/edn-read-or-nil bad-form))) (is (= 'foo/bar (first (#'artifacts/edn-read-or-nil good-form)))) (is (= "1.1" (second (#'artifacts/edn-read-or-nil good-form)))))) + +(deftest hotload-dependency-throws-exceptions + (reset! artifacts/artifacts {"prismatic/schema" ["0.1"]}) + (with-redefs + [artifacts/make-resolve-missing-aware-of-new-deps! (fn [& _]) + artifacts/stale-cache? (constantly false) + artifacts/jar-at-the-top-of-dependency-hierarchy (fn [& _]) + artifacts/add-dependencies! (constantly true)] + (testing "Throws for non existing version" + (is (thrown? IllegalArgumentException + (artifacts/hotload-dependency + {:coordinates "[prismatic/schema \"1.0\"]"})))) + (testing "Throws for non existing artifact" + (is (thrown? IllegalArgumentException + (artifacts/hotload-dependency + {:coordinates "[imaginary \"1.0\"]"})))) + (testing "No exception when all is OK" + (is (artifacts/hotload-dependency {:coordinates "[prismatic/schema \"0.1\"]"}))))) From 0eecfc8e62aae39ef2983f9edf94b7f338e786be Mon Sep 17 00:00:00 2001 From: Benedek Fazekas Date: Sun, 8 Aug 2021 17:26:46 +0100 Subject: [PATCH 4/6] exclude `javax.inject` as a workaround for the error that happens when inlining dependencies. this way javax.* packages won't get prefixed --- project.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/project.clj b/project.clj index 6045d699..adf40718 100644 --- a/project.clj +++ b/project.clj @@ -8,7 +8,8 @@ ^:inline-dep [clj-commons/pomegranate "1.2.1" :exclusions [org.slf4j/jcl-over-slf4j - org.tcrawley/dynapath]] + org.tcrawley/dynapath + javax.inject]] ;; Override conflicting dep in Pomegranate ^:inlined-dep [org.apache.httpcomponents/httpclient "4.5.9" :exclusions [commons-logging]] From e99f9bd2b7ecde8d4478b84c62e6f50d2e9ab63e Mon Sep 17 00:00:00 2001 From: Benedek Fazekas Date: Sun, 8 Aug 2021 19:58:51 +0100 Subject: [PATCH 5/6] fix exception type for not found artefact `get-clojars-versions` threw a different exception than expected so it made sense to return nil when nothing found so the expected IAE would be thrown --- src/refactor_nrepl/artifacts.clj | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/refactor_nrepl/artifacts.clj b/src/refactor_nrepl/artifacts.clj index 16251405..660ad62e 100644 --- a/src/refactor_nrepl/artifacts.clj +++ b/src/refactor_nrepl/artifacts.clj @@ -115,11 +115,8 @@ [artifact] (let [{:keys [body status]} @(http/get (str "https://clojars.org/api/artifacts/" artifact))] - (if (= 200 status) - (map :version (:recent_versions (json/read-str body :key-fn keyword))) - (throw (ex-info (str "Unexpected response from Clojars") - {:status status - :body body}))))) + (when (= 200 status) + (map :version (:recent_versions (json/read-str body :key-fn keyword)))))) (defn- get-artifacts-from-clojars! [] From 3ea4fc388c15699e67cc098990a1577ee18316b6 Mon Sep 17 00:00:00 2001 From: Lars Andersen Date: Thu, 23 Sep 2021 13:33:21 +0200 Subject: [PATCH 6/6] Use tools.deps instead of pomegranate --- project.clj | 14 ++-- src/refactor_nrepl/add_lib.clj | 91 ++++++++++++++++++++++++++ src/refactor_nrepl/artifacts.clj | 78 +++++++++++----------- src/refactor_nrepl/core.clj | 4 ++ test/refactor_nrepl/artifacts_test.clj | 14 ++-- 5 files changed, 146 insertions(+), 55 deletions(-) create mode 100644 src/refactor_nrepl/add_lib.clj diff --git a/project.clj b/project.clj index adf40718..6bdb4438 100644 --- a/project.clj +++ b/project.clj @@ -5,14 +5,16 @@ :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[nrepl "0.8.3"] ^:inline-dep [http-kit "2.5.3"] - ^:inline-dep [clj-commons/pomegranate "1.2.1" + ^:inline-dep [org.clojure/tools.deps.alpha "0.12.1048" :exclusions - [org.slf4j/jcl-over-slf4j - org.tcrawley/dynapath + [ + ;; `javax.inject` is only needed for mvn repos + ;; in S3 and that lib doesn't work with + ;; mranderson at present, so we might as well + ;; exlude the related s3 libs + com.cognitect.aws/s3 + com.cognitect.aws/endpoints javax.inject]] - ;; Override conflicting dep in Pomegranate - ^:inlined-dep [org.apache.httpcomponents/httpclient - "4.5.9" :exclusions [commons-logging]] ^:inline-dep [org.clojure/data.json "2.3.1"] ^:inline-dep [org.clojure/tools.analyzer.jvm "1.1.0"] ^:inline-dep [org.clojure/tools.namespace "1.1.0" :exclusions [org.clojure/tools.reader]] diff --git a/src/refactor_nrepl/add_lib.clj b/src/refactor_nrepl/add_lib.clj new file mode 100644 index 00000000..3961d1fe --- /dev/null +++ b/src/refactor_nrepl/add_lib.clj @@ -0,0 +1,91 @@ +;; Copyright (c) Rich Hickey. All rights reserved. +;; The use and distribution terms for this software are covered by the +;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this distribution. +;; By using this software in any fashion, you are agreeing to be bound by +;; the terms of this license. +;; You must not remove this notice, or any other, from this software. + +(ns refactor-nrepl.add-lib + (:require + [clojure.java.io :as jio] + [clojure.set :as set] + [clojure.tools.deps.alpha :as deps] + [clojure.tools.deps.alpha.util.maven :as maven]) + (:import + clojure.lang.DynamicClassLoader + java.io.File)) + +(set! *warn-on-reflection* true) + +;; maintain basis + +(defn- read-basis + [] + (when-let [f (jio/file (System/getProperty "clojure.basis"))] + (if (and f (.exists f)) + (deps/slurp-deps f) + (throw (IllegalArgumentException. "No basis declared in clojure.basis system property"))))) + +(defonce ^:private init-basis (delay (read-basis))) + +(defn launch-basis + "Initial runtime basis at launch" + [] + @init-basis) + +(def ^:private runtime-basis + (atom nil)) + +(defn- reset-basis + [basis] + (reset! runtime-basis basis)) + +(defn current-basis + "Return the current runtime basis, which may have been modified since the launch." + [] + (or @runtime-basis (reset-basis @init-basis))) + +;; add-libs + +(defn- add-loader-url + "Add url string or URL to the highest level DynamicClassLoader url set." + [url] + (let [u (if (string? url) (java.net.URL. url) url) + loader (loop [loader (.getContextClassLoader (Thread/currentThread))] + (let [parent (.getParent loader)] + (if (instance? DynamicClassLoader parent) + (recur parent) + loader)))] + (if (instance? DynamicClassLoader loader) + (.addURL ^DynamicClassLoader loader u) + (throw (IllegalAccessError. "Context classloader is not a DynamicClassLoader"))))) + +(defn add-libs + "Add map of lib to coords to the current runtime environment. All transitive + dependencies will also be considered (in the context of the current set + of loaded dependencies) and new transitive dependencies will also be + loaded. Returns seq of all added libs or nil if couldn't be loaded. + Note that for successful use, you must be in a REPL environment where a + valid parent DynamicClassLoader can be found in which to add the new lib + urls. + Example: + (add-libs '{org.clojure/core.memoize {:mvn/version \"0.7.1\"}})" + [lib-coords] + (let [{:keys [libs] :as initial-basis} (current-basis)] + (if (empty? (set/difference (-> lib-coords keys set) (-> libs keys set))) + nil ;; already loaded + (let [updated-deps (reduce-kv (fn [m k v] (assoc m k (dissoc v :dependents :paths))) lib-coords libs) + updated-edn (merge (dissoc initial-basis :libs :classpath :deps) {:deps updated-deps}) + {updated-libs :libs :as updated-basis} + (deps/calc-basis + ;; No `:mvn/repos` are configured if Leiningen is in use so we have to add them here. + (merge {:mvn/repos maven/standard-repos} updated-edn) + (select-keys initial-basis [:resolve-args :cp-args])) + new-libs (select-keys updated-libs (set/difference (set (keys updated-libs)) (set (keys libs)))) + paths (mapcat :paths (vals new-libs)) + urls (->> paths (map jio/file) (map #(.toURL ^File %)))] + ;; TODO: multiple unsynchronized changes to runtime state - coordinate with lock? + (run! add-loader-url urls) + (reset-basis updated-basis) + (keys new-libs))))) diff --git a/src/refactor_nrepl/artifacts.clj b/src/refactor_nrepl/artifacts.clj index 660ad62e..bc6f91d2 100644 --- a/src/refactor_nrepl/artifacts.clj +++ b/src/refactor_nrepl/artifacts.clj @@ -1,18 +1,20 @@ (ns refactor-nrepl.artifacts (:require - [cemerick.pomegranate :as pomegranate] [clojure.data.json :as json] [clojure.edn :as edn] [clojure.java.io :as io] [clojure.string :as str] + [refactor-nrepl.add-lib :as add-lib] [clojure.tools.namespace.find :as find] [org.httpkit.client :as http] + [refactor-nrepl.core :as core] [refactor-nrepl.ns.slam.hound.regrow :as slamhound-regrow] [refactor-nrepl.ns.slam.hound.search :as slamhound] [version-clj.core :as versions]) - (:import java.io.File - java.util.jar.JarFile - java.util.zip.GZIPInputStream)) + (:import + java.io.File + java.util.jar.JarFile + java.util.zip.GZIPInputStream)) (def artifacts-file (str (io/file (System/getProperty "java.io.tmpdir") "refactor-nrepl-artifacts-cache"))) @@ -158,14 +160,23 @@ [{:keys [artifact]}] (artifact-versions* artifact)) -(defn- jar-at-the-top-of-dependency-hierarchy [new-deps] +(defn- jar-at-the-top-of-dependency-hierarchy [top-level-dep] ;; We only need to consider the dep at the top of the hierarchy because when we - ;; require those namespaces the rest of the transitive deps will get pulled in + ;; require those namespaces the rest of the transitive deps will get pulled in ;; too. (letfn [(->jar [^File f] (JarFile. f))] - (let [top-level-dep (-> new-deps keys first)] - (-> top-level-dep meta :file ->jar)))) + (let [artifact-name (-> top-level-dep core/suffix)] + (->> (core/jars-on-classpath) + ;; This isn't guaranteed but at least on my system the new stuff was + ;; added to the end and so this increases the likelihood of picking + ;; the right artifact + reverse + (map io/as-file) + (some (fn [f] + (when (.startsWith (.getName f) artifact-name) + f))) + ->jar)))) (defn- make-resolve-missing-aware-of-new-deps! "Once the deps are available on cp we still have to load them and @@ -185,45 +196,32 @@ (slamhound-regrow/clear-cache!)) (defn- parse-coordinates [coordinates-str] - (let [coords (try (->> coordinates-str edn/read-string (take 2) vec) + (let [coords (try (->> coordinates-str edn/read-string) (catch Exception _))] - (if (and (= (count coords) 2) - (symbol? (first coords)) - (string? (second coords))) - coords - (throw (IllegalArgumentException. (str "Malformed dependency vector: " + (cond + ;; Leiningen dependency vector + (vector? coords) + (hash-map (first coords) {:mvn/version (second coords)}) + ;; tools.deps map + (map? coords) coords + :else + (throw (IllegalArgumentException. (str "Malformed coordinates " coordinates-str)))))) -(defn- ensure-coordinates-exist! - [[artifact-id artifact-version :as coordinates]] - (when (stale-cache?) - (update-artifact-cache!)) - (if-let [versions (artifact-versions* (str artifact-id))] - (if ((set versions) artifact-version) - coordinates - (throw (IllegalArgumentException. - (str "Version " artifact-version - " does not exist for " artifact-id - ". Available versions are " (pr-str (vec versions)))))) - (throw (IllegalArgumentException. (str "Can't find artifact '" - artifact-id "'"))))) - (defn- add-dependencies! [coordinates] ;; Just so we can mock this out during testing - (let [repos {"clojars" "https://clojars.org/repo" - "central" "https://repo1.maven.org/maven2/"}] - (pomegranate/add-dependencies - :coordinates [coordinates] :repositories repos))) + (some-> coordinates + add-lib/add-libs + set + (apply (keys coordinates)))) (defn- hotload-dependency! [coordinates] - (-> (add-dependencies! coordinates) - jar-at-the-top-of-dependency-hierarchy - make-resolve-missing-aware-of-new-deps!)) + (some-> coordinates + add-dependencies! + jar-at-the-top-of-dependency-hierarchy + make-resolve-missing-aware-of-new-deps!)) (defn hotload-dependency [{:keys [coordinates]}] - (->> coordinates - parse-coordinates - ensure-coordinates-exist! - (hotload-dependency!) - (str/join " "))) + (when (->> coordinates parse-coordinates hotload-dependency!) + coordinates)) diff --git a/src/refactor_nrepl/core.clj b/src/refactor_nrepl/core.clj index 8260e1a3..0a5a372b 100644 --- a/src/refactor_nrepl/core.clj +++ b/src/refactor_nrepl/core.clj @@ -71,6 +71,10 @@ (-> s (.contains ".gitlibs")))))) (remove util/dir-outside-root-dir?))) +(defn jars-on-classpath [] + (->> (cp/classpath) + (filter misc/jar-file?))) + (defn project-root "Return the project root directory. diff --git a/test/refactor_nrepl/artifacts_test.clj b/test/refactor_nrepl/artifacts_test.clj index d6de534e..197818de 100644 --- a/test/refactor_nrepl/artifacts_test.clj +++ b/test/refactor_nrepl/artifacts_test.clj @@ -79,17 +79,13 @@ (deftest hotload-dependency-throws-exceptions (reset! artifacts/artifacts {"prismatic/schema" ["0.1"]}) (with-redefs - [artifacts/make-resolve-missing-aware-of-new-deps! (fn [& _]) - artifacts/stale-cache? (constantly false) - artifacts/jar-at-the-top-of-dependency-hierarchy (fn [& _]) - artifacts/add-dependencies! (constantly true)] + [artifacts/make-resolve-missing-aware-of-new-deps! (constantly true) + artifacts/stale-cache? (constantly false) + artifacts/jar-at-the-top-of-dependency-hierarchy (constantly true) + artifacts/add-dependencies! (constantly true)] (testing "Throws for non existing version" (is (thrown? IllegalArgumentException (artifacts/hotload-dependency - {:coordinates "[prismatic/schema \"1.0\"]"})))) - (testing "Throws for non existing artifact" - (is (thrown? IllegalArgumentException - (artifacts/hotload-dependency - {:coordinates "[imaginary \"1.0\"]"})))) + {:coordinates "obviously wrong"})))) (testing "No exception when all is OK" (is (artifacts/hotload-dependency {:coordinates "[prismatic/schema \"0.1\"]"})))))