Skip to content

Instantly share code, notes, and snippets.

@wandersoncferreira
Created September 24, 2020 12:18
Show Gist options
  • Save wandersoncferreira/eb53ded04eddd538ae7d3d6ba041d038 to your computer and use it in GitHub Desktop.
Save wandersoncferreira/eb53ded04eddd538ae7d3d6ba041d038 to your computer and use it in GitHub Desktop.
(ns secland.gen-contract
(:require [cadastro-de-pessoa.cnpj :as cnpj]
[clojure.test.check.generators :as gen]
[java-time :as java-time])
(:import java.time.ZoneId))
(defn date-converter [date]
(java-time/java-date
(.toInstant
(.atStartOfDay date (ZoneId/systemDefault)))))
(defn- add-days-to--raw
[date n-days]
(-> (.toInstant date)
(java-time/plus (java-time/days n-days))
(as-> fmt (date-converter fmt))))
(def add-days (memoize add-days-to--raw))
(def gen-cnpj (gen/fmap (fn [_] (cnpj/gen)) (gen/list gen/int)))
(def gen-datetime-components
(gen/hash-map
:year (gen/large-integer* {:min 1990 :max 2200})
:month (gen/large-integer* {:min 1 :max 12})
:day (gen/large-integer* {:min 1 :max 28})
:hour (gen/large-integer* {:min 0 :max 23})
:minute (gen/large-integer* {:min 0 :max 59})
:second (gen/large-integer* {:min 0 :max 59})
:millis (gen/large-integer* {:min 0 :max 1000})))
(defn convert-to-inst [value]
(java-time/java-date
(.toInstant
(.atZone (apply java-time/local-date-time (vals value))
(java.time.ZoneId/systemDefault)))))
(def gen-java-util-date
(gen/fmap convert-to-inst gen-datetime-components))
(defn merge-hashmap-gen [& args]
(gen/fmap (fn [args] (apply merge args)) (apply gen/tuple args)))
(def ^:private gen-issue-value (gen/fmap #(* 1000 %) (gen/frequency [[9 (gen/double* {:min 0
:infinite? false
:NaN? false})] [1 gen/double]])))
(defn- generate-present-value [issue-value]
(gen/frequency [[9 (gen/return issue-value)]
[1 (gen/fmap (fn [[denum issue-value]] (* issue-value (/ 1 denum)))
(gen/tuple (gen/large-integer* {:min 2 :max 10})
(gen/return issue-value)))]]))
(defn- generate-future-value [present-value]
(gen/frequency [[9 (gen/fmap (fn [[denum present-value]] (* present-value (inc (/ 1 denum))))
(gen/tuple (gen/large-integer* {:min 2 :max 10})
(gen/return present-value)))]
[1 (gen/fmap (fn [[denum present-value]] (* present-value (/ 1 denum)))
(gen/tuple (gen/large-integer* {:min 2 :max 10})
(gen/return present-value)))]]))
(def generate-installment-values
(gen/let [issue-value gen-issue-value
present-value (generate-present-value issue-value)
future-value (generate-future-value present-value)]
(gen/hash-map
:installment/issue-value (gen/return issue-value)
:installment/present-value (gen/return present-value)
:installment/future-value (gen/return future-value))))
(def gen-installment
(gen/not-empty (gen/list (merge-hashmap-gen
generate-installment-values
(gen/hash-map
:installment/status gen/keyword)))))
(defn- gen-acquisition-date [issue-date]
(gen/let [number-additional-days (gen/large-integer* {:min -20 :max 20})]
(gen/frequency [[8 (gen/return issue-date)] [2 (gen/return (add-days issue-date number-additional-days))]])))
(def ^:private gen-contract-body
(gen/hash-map
:contract/category (gen/elements [:ccb :assignment-agreement :trust-assignment-agreement :trade-note])
:contract/portfolio (gen/frequency [[4 (gen/return :default)] [2 (gen/return :secland)]
[2 (gen/return :testing)] [2 (gen/return :clj-secland)]])
:contract/lending-rate (gen/fmap #(BigDecimal. (/ % 1000.)) (gen/large-integer* {:min 1 :max 150}))
:contract/originator gen/keyword
:contract/assignor gen-cnpj
:contract/drawee gen-cnpj
:contract/indexer (gen/elements [:igpm :cdi :selic :none])
:contract/rating (gen/elements ["A" "B" "C" "D" "E" "F" "G" "H" "I"])
:contract/score (gen/large-integer* {:min 0 :max 1000})
:contract/recourse gen/boolean))
(defn- gen-contract--values [which-key list-installments]
(let [contract-value (reduce + (map which-key list-installments))]
(gen/frequency [[9 (gen/return contract-value)]
[1 (gen/return (* 0.7 contract-value))]])))
(def gen-contract
(gen/let [principal-id (gen/return "principal-id")
secundary-id (gen/return principal-id)
[installments first-pmt-number :as gen-inst] (gen/tuple gen-installment (gen/large-integer* {:min 1}))
issue-date gen-java-util-date]
(merge-hashmap-gen gen-contract-body
(gen/hash-map
:type-name (gen/return :contract)
:contract/issue-date (gen/return issue-date)
:contract/acquisition-date (gen-acquisition-date issue-date)
:contract/due-date (gen/return (add-days issue-date (count installments)))
:contract/issue-value (gen-contract--values :installment/issue-value installments)
:contract/future-value (gen-contract--values :installment/future-value installments)
:contract/present-value (gen-contract--values :installment/present-value installments)
:contract/principal-external-id (gen/return principal-id)
:contract/secundary-external-id (gen/return secundary-id)
:contract/number-of-installments (gen/return (count installments))
:contract/installments (gen/fmap (fn [[list-of-maps min-number]]
(map #(assoc %1
:installment/pmt-number %2
:installment/due-date (add-days issue-date (* 30 %3))
:installment/unique-id (str principal-id "_" %2)) list-of-maps (iterate inc min-number) (range 1 (inc (count installments)))))
(gen/return gen-inst))))))
(def gen-contract<->operation-id (merge-hashmap-gen gen-contract (gen/hash-map :operation-id (gen/fmap str gen/string-ascii))))
(def gen-list-of-contracts (gen/not-empty (gen/list gen-contract)))
(def gen-list-of-contracts<->operation-id (gen/not-empty (gen/list gen-contract<->operation-id)))
(comment
(require '[com.gfredericks.test.chuck.clojure-test :refer [checking]])
(require '[secland.securities.contracts :as contract])
(require '[clojure.spec.alpha :as s])
(require '[clojure.test :refer :all])
(deftest contract-generators
(checking "All the contracts generated must be spec'ed" 100
[ct gen-contract]
(s/assert ::contract/spec-contract ct)))
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment