diff --git a/deps.edn b/deps.edn index 8dc198a..3e1ddb6 100644 --- a/deps.edn +++ b/deps.edn @@ -1,19 +1,25 @@ {:paths ["src" "resources"] - :deps {org.clojure/clojure {:mvn/version "1.11.2"} + :deps {org.clojure/clojure {:mvn/version "1.11.2"} com.github.parenthesin/components {:mvn/version "0.2.5" :exclusions [prismatic/schema]} - com.github.seancorfield/honeysql {:mvn/version "2.6.1126"} - metosin/malli {:mvn/version "0.15.0"} - metosin/reitit-swagger {:mvn/version "0.6.0"} - org.postgresql/postgresql {:mvn/version "42.7.3"}} + org.clj-commons/digest {:mvn/version "1.4.100"} + com.github.seancorfield/honeysql {:mvn/version "2.6.1126"} + metosin/malli {:mvn/version "0.15.0"} + metosin/reitit-swagger {:mvn/version "0.6.0"} + org.postgresql/postgresql {:mvn/version "42.7.3"}} :aliases - {:dev {:extra-paths ["test" "dev"]} + {:dev {:extra-paths ["test" "dev"] + :extra-deps {org.clojars.bigsy/pg-embedded-clj {:mvn/version "1.0.1"} + lambdaisland/kaocha {:mvn/version "1.88.1376"} + lambdaisland/kaocha-cloverage {:mvn/version "1.1.89"} + nubank/matcher-combinators {:mvn/version "3.9.1"} + nubank/state-flow {:mvn/version "5.15.0"}}} :test {:extra-paths ["test"] :extra-deps {org.clojars.bigsy/pg-embedded-clj {:mvn/version "1.0.1"} - lambdaisland/kaocha {:mvn/version "1.88.1376"} - lambdaisland/kaocha-cloverage {:mvn/version "1.1.89"} - nubank/matcher-combinators {:mvn/version "3.9.1"} - nubank/state-flow {:mvn/version "5.15.0"}} + lambdaisland/kaocha {:mvn/version "1.88.1376"} + lambdaisland/kaocha-cloverage {:mvn/version "1.1.89"} + nubank/matcher-combinators {:mvn/version "3.9.1"} + nubank/state-flow {:mvn/version "5.15.0"}} :main-opts ["-m" "kaocha.runner"]} :clojure-lsp {:replace-deps {com.github.clojure-lsp/clojure-lsp-standalone {:mvn/version "2024.03.13-13.11.00"}} diff --git a/dev/user.clj b/dev/user.clj index 80c2c7d..4dafcd2 100644 --- a/dev/user.clj +++ b/dev/user.clj @@ -1,7 +1,7 @@ (ns user - (:require [malli.dev :as dev] - [malli.dev.pretty :as pretty] - [microservice-boilerplate.server])) + (:require [codes.clj.contest.submission-runner.server] + [malli.dev :as dev] + [malli.dev.pretty :as pretty])) (defn start [] diff --git a/src/codes/clj/contest/submission_runner/adapters/submission.clj b/src/codes/clj/contest/submission_runner/adapters/submission.clj new file mode 100644 index 0000000..11a6ea4 --- /dev/null +++ b/src/codes/clj/contest/submission_runner/adapters/submission.clj @@ -0,0 +1,12 @@ +(ns codes.clj.contest.submission-runner.adapters.submission + (:require [codes.clj.contest.submission-runner.wire.db.submission :as wire.db.submission] + [codes.clj.contest.submission-runner.wire.in.submission :as wire.in.submission])) + +(defn wire->internal + {:malli/schema [:=> [:cat wire.in.submission/Submission] wire.db.submission/Submission]} + [{:keys [id code code-hash language test-cases]}] + {:submission/id id + :submission/code code + :submission/code_hash code-hash + :submission/language language + :submission/test_cases test-cases}) diff --git a/src/codes/clj/contest/submission_runner/controllers/submission.clj b/src/codes/clj/contest/submission_runner/controllers/submission.clj new file mode 100644 index 0000000..2909275 --- /dev/null +++ b/src/codes/clj/contest/submission_runner/controllers/submission.clj @@ -0,0 +1,5 @@ +(ns codes.clj.contest.submission-runner.controllers.submission) + +(defn submit-code-execution! + [_submission] + (random-uuid)) diff --git a/src/codes/clj/contest/submission_runner/ports/http_in/submission.clj b/src/codes/clj/contest/submission_runner/ports/http_in/submission.clj new file mode 100644 index 0000000..047a98c --- /dev/null +++ b/src/codes/clj/contest/submission_runner/ports/http_in/submission.clj @@ -0,0 +1,12 @@ +(ns codes.clj.contest.submission-runner.ports.http-in.submission + (:require [codes.clj.contest.submission-runner.adapters.submission :as adapters.submission] + [codes.clj.contest.submission-runner.controllers.submission :as controllers.submission])) + +(defn submit-code-execution! + [{{submission :body} :parameters + _components :components}] + (let [id (-> submission + (adapters.submission/wire->internal) + (controllers.submission/submit-code-execution!))] + {:status 201 :body {:id id}})) + diff --git a/src/codes/clj/contest/submission_runner/routes.clj b/src/codes/clj/contest/submission_runner/routes.clj index 66ecc06..6ea9d7e 100644 --- a/src/codes/clj/contest/submission_runner/routes.clj +++ b/src/codes/clj/contest/submission_runner/routes.clj @@ -1,13 +1,16 @@ (ns codes.clj.contest.submission-runner.routes (:require [codes.clj.contest.submission-runner.ports.http-in :as ports.http-in] + [codes.clj.contest.submission-runner.ports.http-in.submission :as ports.http-in.submission] [codes.clj.contest.submission-runner.schemas.wire-in :as schemas.wire-in] + [codes.clj.contest.submission-runner.wire.in.submission :as wire.in.submission] + [codes.clj.contest.submission-runner.wire.out.submission :as wire.out.submission] [reitit.swagger :as swagger])) (def routes [["/swagger.json" {:get {:no-doc true - :swagger {:info {:title "btc-wallet" - :description "small sample using the microservice-boilerplate"}} + :swagger {:info {:title "Submission Runner" + :description "Submission Runner API"}} :handler (swagger/create-swagger-handler)}}] ["/wallet" @@ -32,4 +35,17 @@ :responses {201 {:body schemas.wire-in/WalletEntry} 400 {:body :string} 500 {:body :string}} - :handler ports.http-in/do-withdrawal!}}]]]) + :handler ports.http-in/do-withdrawal!}}]] + + ;; submitions routes + ["/code" + {:swagger {:tags ["Code Runner"]}} + ["/submission" + {:post {:summary "Submit to execution your code" + :parameters {:body wire.in.submission/Submission} + :responses {201 {:body wire.out.submission/SubmissionResult} + 400 {:body :string} + 500 {:body :string}} + :handler ports.http-in.submission/submit-code-execution!}}]]]) + + diff --git a/src/codes/clj/contest/submission_runner/wire/db/submission.clj b/src/codes/clj/contest/submission_runner/wire/db/submission.clj new file mode 100644 index 0000000..cc5ec92 --- /dev/null +++ b/src/codes/clj/contest/submission_runner/wire/db/submission.clj @@ -0,0 +1,13 @@ +(ns codes.clj.contest.submission-runner.wire.db.submission) + +(def Submission + [:map + [:submission/id string?] + [:submission/code string?] + [:submission/code_hash string?] + [:submission/language [:enum :clojure]] + [:submission/test_cases + [:map-of :keyword + [:map + [:input :any] + [:output :any]]]]]) diff --git a/src/codes/clj/contest/submission_runner/wire/in/submission.clj b/src/codes/clj/contest/submission_runner/wire/in/submission.clj new file mode 100644 index 0000000..6c33fe2 --- /dev/null +++ b/src/codes/clj/contest/submission_runner/wire/in/submission.clj @@ -0,0 +1,30 @@ +(ns codes.clj.contest.submission-runner.wire.in.submission) + +(def Submission + [:map + [:id string?] + [:code string?] + [:code-hash string?] + [:language [:enum :clojure]] + [:test-cases [:map-of :keyword + [:map + [:input :any] + [:output :any]]]]]) + +(comment + (require '[malli.generator :as m]) + (require '[malli.core :as m.core]) + (m/generate Submission) + (m.core/validate + Submission + {:code "(ns runner + (:require [clojure.test :refer [use-fixtures]]) + (defn my-sum [a b] (+ a b)))" + :language :clojure + :test-cases {:case-1 {:input "(my-sum 1 2)" + :output 3} + :case-2 {:input "(my-sum 2 3)" + :output 5}}}) + +; + ) diff --git a/src/codes/clj/contest/submission_runner/wire/out/submission.clj b/src/codes/clj/contest/submission_runner/wire/out/submission.clj new file mode 100644 index 0000000..cc33002 --- /dev/null +++ b/src/codes/clj/contest/submission_runner/wire/out/submission.clj @@ -0,0 +1,5 @@ +(ns codes.clj.contest.submission-runner.wire.out.submission) + +(def SubmissionResult + [:map + [:id uuid?]]) diff --git a/test/integration/codes/clj/contest/submission_runner/db_test.clj b/test/integration/codes/clj/contest/submission_runner/db/db_test.clj similarity index 96% rename from test/integration/codes/clj/contest/submission_runner/db_test.clj rename to test/integration/codes/clj/contest/submission_runner/db/db_test.clj index e2319bd..bcb7795 100644 --- a/test/integration/codes/clj/contest/submission_runner/db_test.clj +++ b/test/integration/codes/clj/contest/submission_runner/db/db_test.clj @@ -1,4 +1,4 @@ -(ns integration.codes.clj.contest.submission-runner.db-test +(ns integration.codes.clj.contest.submission-runner.db.db-test (:require [clojure.test :refer [use-fixtures]] [codes.clj.contest.submission-runner.db :as db] [com.stuartsierra.component :as component] diff --git a/test/integration/codes/clj/contest/submission_runner/submission_runner_test.clj b/test/integration/codes/clj/contest/submission_runner/submission_runner_test.clj new file mode 100644 index 0000000..108fd80 --- /dev/null +++ b/test/integration/codes/clj/contest/submission_runner/submission_runner_test.clj @@ -0,0 +1,40 @@ +(ns integration.codes.clj.contest.submission-runner.submission-runner-test + (:require [clj-commons.digest :as digest] + [clojure.test :refer [use-fixtures]] + [integration.codes.clj.contest.submission-runner.util :as util] + [matcher-combinators.matchers :as matchers] + [parenthesin.helpers.malli :as helpers.malli] + [parenthesin.helpers.state-flow.server.pedestal :as state-flow.server] + [state-flow.api :refer [defflow]] + [state-flow.assertions.matcher-combinators :refer [match?]] + [state-flow.core :as state-flow :refer [flow]])) + +(use-fixtures :once helpers.malli/with-intrumentation) + +(defflow + flow-integration-wallet-test + {:init util/start-system! + :cleanup util/stop-system! + :fail-fast? true} + + (flow "should receive an submission" + [:let [id (random-uuid) + code "(ns runner + (:require [clojure.string :as str])) + (defn my-sum [a b] (+ a b))" + submission-input {:id id + :code code + :code-hash (digest/md5 code) + :language :clojure + :test-cases {:case-1 {:input "(my-sum 1 2)" + :output 3} + :case-2 {:input "(my-sum 2 3)" + :output 5}}}] + return (state-flow.server/request! {:method :post + :uri "/code/submission" + :body submission-input})] + + (match? (matchers/embeds {:status 201 + :body {:id string?}}) + return))) + diff --git a/test/integration/codes/clj/contest/submission_runner/util.clj b/test/integration/codes/clj/contest/submission_runner/util.clj index 1332414..120fb0a 100644 --- a/test/integration/codes/clj/contest/submission_runner/util.clj +++ b/test/integration/codes/clj/contest/submission_runner/util.clj @@ -1,18 +1,38 @@ (ns integration.codes.clj.contest.submission-runner.util - (:require [com.stuartsierra.component :as component] + (:require [codes.clj.contest.submission-runner.routes :as routes] + [com.stuartsierra.component :as component] + [parenthesin.components.config.aero :as components.config] + [parenthesin.components.db.jdbc-hikari :as components.database] + [parenthesin.components.http.clj-http :as components.http] + [parenthesin.components.router.reitit-malli :as components.router] + [parenthesin.components.server.reitit-pedestal-jetty :as components.webserver] [parenthesin.helpers.logs :as logs] [parenthesin.helpers.migrations :as migrations] [pg-embedded-clj.core :as pg-emb])) +(defn- create-and-start-components! [] + (component/start-system + (component/system-map + :config (components.config/new-config) + :http (components.http/new-http-mock {}) + :router (components.router/new-router routes/routes) + :database (component/using (components.database/new-database) + [:config]) + :webserver (component/using (components.webserver/new-webserver) + [:config :http :router :database])))) + (defn start-system! - [system-start-fn] - (fn [] - (logs/setup :info :auto) - (pg-emb/init-pg) - (migrations/migrate (migrations/configuration-with-db)) - (system-start-fn))) + ([] + ((start-system! create-and-start-components!))) + ([system-start-fn] + (fn [] + (logs/setup :info :auto) + (pg-emb/init-pg) + (migrations/migrate (migrations/configuration-with-db)) + (system-start-fn)))) (defn stop-system! [system] (component/stop-system system) (pg-emb/halt-pg!)) + diff --git a/test/integration/codes/clj/contest/submission_runner/wallet_test.clj b/test/integration/codes/clj/contest/submission_runner/wallet_test.clj index 4c046ac..466a4e5 100644 --- a/test/integration/codes/clj/contest/submission_runner/wallet_test.clj +++ b/test/integration/codes/clj/contest/submission_runner/wallet_test.clj @@ -1,14 +1,7 @@ (ns integration.codes.clj.contest.submission-runner.wallet-test (:require [clojure.test :refer [use-fixtures]] - [codes.clj.contest.submission-runner.routes :as routes] - [com.stuartsierra.component :as component] [integration.codes.clj.contest.submission-runner.util :as util] [matcher-combinators.matchers :as matchers] - [parenthesin.components.config.aero :as components.config] - [parenthesin.components.db.jdbc-hikari :as components.database] - [parenthesin.components.http.clj-http :as components.http] - [parenthesin.components.router.reitit-malli :as components.router] - [parenthesin.components.server.reitit-pedestal-jetty :as components.webserver] [parenthesin.helpers.malli :as helpers.malli] [parenthesin.helpers.state-flow.http :as state-flow.http] [parenthesin.helpers.state-flow.server.pedestal :as state-flow.server] @@ -18,20 +11,9 @@ (use-fixtures :once helpers.malli/with-intrumentation) -(defn- create-and-start-components! [] - (component/start-system - (component/system-map - :config (components.config/new-config) - :http (components.http/new-http-mock {}) - :router (components.router/new-router routes/routes) - :database (component/using (components.database/new-database) - [:config]) - :webserver (component/using (components.webserver/new-webserver) - [:config :http :router :database])))) - (defflow flow-integration-wallet-test - {:init (util/start-system! create-and-start-components!) + {:init util/start-system! :cleanup util/stop-system! :fail-fast? true} (flow "should interact with system" diff --git a/test/unit/codes/clj/contest/submission_runner/adapters/submission_test.clj b/test/unit/codes/clj/contest/submission_runner/adapters/submission_test.clj new file mode 100644 index 0000000..15d7f77 --- /dev/null +++ b/test/unit/codes/clj/contest/submission_runner/adapters/submission_test.clj @@ -0,0 +1,44 @@ +(ns unit.codes.clj.contest.submission-runner.adapters.submission-test + (:require [clj-commons.digest :as digest] + [clojure.test :refer [deftest is testing use-fixtures]] + [clojure.test.check.clojure-test :refer [defspec]] + [clojure.test.check.properties :as properties] + [codes.clj.contest.submission-runner.adapters.submission :as adapters.submission] + [codes.clj.contest.submission-runner.wire.db.submission :as wire.db.submission] + [codes.clj.contest.submission-runner.wire.in.submission :as wire.in.submission] + [malli.core :as m] + [malli.generator :as mg] + [matcher-combinators.test :refer [match?]] + [parenthesin.helpers.malli :as helpers.malli])) + +(use-fixtures :once helpers.malli/with-intrumentation) + +(defspec wirer->internal-spec 50 + (properties/for-all [code (mg/generator wire.in.submission/Submission)] + (m/validate wire.db.submission/Submission (adapters.submission/wire->internal code)))) + +(def id (random-uuid)) +(def code "(ns runner + (:require [clojure.string :as str])) + (defn my-sum [a b] (+ a b))") + +(def code-submission {:id (str id) + :code code + :code-hash (digest/md5 code) + :language :clojure + :test-cases {:case-1 {:input "(my-sum 1 2)" + :output 3} + :case-2 {:input "(my-sum 2 3)" + :output 5}}}) + +(deftest wire->internal + (testing "adpater to db submission" + (is (match? {:submission/id (str id) + :submission/code code + :submission/code_hash (digest/md5 code) + :submission/language :clojure + :submission/test_cases {:case-1 {:input "(my-sum 1 2)" + :output 3} + :case-2 {:input "(my-sum 2 3)" + :output 5}}} + (adapters.submission/wire->internal code-submission)))))