-
-
Save prepor/50ac0bd32b6a6c95daac to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns flock.staff.test.fixtures | |
(:require [clojure.java.jdbc :as jdbc] | |
[clojure.string :as str] | |
[com.stuartsierra.component :as component] | |
[defcomponent :refer [defcomponent]] | |
[flock.staff.components :as components] | |
[flock.staff.time :as time] | |
[flock.staff.utils :as utils] | |
[plumbing.core :refer :all] | |
[slingshot.slingshot :refer [throw+]])) | |
(defn generate-string | |
([] (generate-string 8)) | |
([n] (let [chars (map char (concat (range 65 91) (range 97 123))) | |
chars* (take n (repeatedly #(rand-nth chars)))] | |
(apply str chars*)))) | |
(defn generate-email | |
[] | |
(str/lower-case (str (generate-string 8) "@" (generate-string 8) ".ru"))) | |
(defn generate-name | |
[] | |
(let [first-name (-> (generate-string 10) str/lower-case str/capitalize) | |
second-name (-> (generate-string 8) str/lower-case str/capitalize)] | |
(str first-name " " second-name))) | |
;; Фикстуры определяются следующим синтаксисом: | |
;; {:fixture-name [:db? :collection fixture-fn]} | |
;; :db по-умолчанию это :main-db. | |
;; fixture-fn это fnk где в параметрах определены фикстуры, от которых зависит текущая | |
;; при запросе на создание фикстуры будут сначала созданы фикстуры от которых | |
;; она зависит. уже созданные фикстуры кешируются по ключу, с которым они | |
;; запрашивались | |
;; fixture-fn возвращает мап, где если значение это функция, то она будет | |
;; вызвана (динамический параметры) | |
;; Создание фикстур просиходит следующим образом: | |
;; (make fixtures & specs) | |
;; где spec: | |
;; - keyword тогда будет созданна одноименная фикстура | |
;; - map {:fixture :fixture-name :as :fixture-key :merge {:title "hello} :depends {:site :new-site}} | |
;; Описание параметров map: | |
;; - :fixture обязательный параметр, "базовая" фикстура, единственный обязательный аргумент | |
;; - :as ключ с которым будет создана фикстура (возвращена с ним, закеширована и доступна для зависимостей) | |
;; - :merge параметры будут смержаны с теми, что вернет fixture-fn | |
;; - :depends определяет из каких именно ключей нужно брать зависимости для | |
;; фикстуры. Так, например, если фикстура зависит от site, то по-умолчанию, | |
;; будет искаться именно фикстура с ключем :site. Пример, наверное, будет | |
;; понятнее: | |
;; (make fixtures :site :campaign | |
;; {:fixture :site :as :new-site :merge {:title "good site!}} | |
;; {:fixture :campaign :as :new-campaign :depends {:site :new-site}}) | |
;; Этот вызов вернет мэп с 4 элементами: :site, :campaign, :new-site | |
;; и :new-campaign, при этом :campaign будет создана с :site, а :new-campaign | |
;; с :new-site | |
;; | |
;; Все созданные инстансы кешируются в компоненте, т.е., при рекомендуемом | |
;; ипсользовании (старте / стопе в use-fixtures тестов), будут закешированы в | |
;; рамках одного теста (а не одного вызова make, как можно было бы подумать) | |
(def repository | |
{:widget [:widgets | |
(fnk [site] | |
{:site_id (:id site) | |
:template nil | |
:type "Widget" | |
:filename "default" | |
:render_type nil | |
:created_at #(time/->sql-time) | |
:updated_at #(time/->sql-time)})] | |
:site [:sites | |
(fnk [country] | |
{:domain "some-domain" | |
:account_id 1 | |
:url "some-url" | |
:currency "rub" | |
:billing_enabled_at (time/->sql-time "2015-01-01") | |
:company_size 1 | |
:country_id (:id country) | |
:token #(generate-string 16) | |
:created_at #(time/->sql-time) | |
:updated_at #(time/->sql-time)})] | |
:country [:countries | |
(fnk [] | |
{:name "Russia" | |
:other_name "Russian Federation" | |
:locale "ru" | |
:currency "rur" | |
:timezone "Moscow" | |
:country_code "RU" | |
:created_at #(time/->sql-time) | |
:updated_at #(time/->sql-time)})] | |
:offer [:offers | |
(fnk [campaign site customer {parent-order nil}] | |
{:campaign_id (:id campaign) | |
:customer_id (:id customer) | |
:parent_order_id (:id parent-order) | |
:token #(generate-string 16) | |
:secret_token #(generate-string 16) | |
:site_id (:id site) | |
:created_at #(time/->sql-time)})] | |
:campaign [:campaigns | |
(fnk [site {coupon-source nil} {customer-segment nil} {campaigns-group nil}] | |
{:site_id (:id site) | |
:campaign_type "postcheckout" | |
:title "Test Campaign" | |
:state "online" | |
:landing_page_type "" | |
:friend_coupon_source_id (:id coupon-source) | |
:friend_reward_method "coupon" | |
:friend_reward_value 1000 | |
:friend_reward_unit "currency" | |
:publisher_reward_limit_type "" | |
:token #(generate-string 16) | |
:customer_segment_id (:id customer-segment) | |
:campaign_group_id (:id campaigns-group)})] | |
:campaigns-group [:campaigns_groups | |
(fnk [site] | |
{:site_id (:id site) | |
:state "online" | |
:created_at #(time/->sql-time) | |
:updated_at #(time/->sql-time)})] | |
:campaign-motivation [:campaign_motivations | |
(fnk [campaign motivation] | |
{:campaign_id (:id campaign) | |
:motivation_id (:id motivation) | |
:purpose nil})] | |
:customer [:customers | |
(fnk [{location nil}] | |
{:email #(generate-email) | |
:name #(generate-name) | |
:location_id (:id location)})] | |
:xname-customer [:customers | |
(fnk [] | |
{:email "xname@flocktory.com" | |
:name "XNAME"})] | |
:customer-segment [:customer_segments | |
(fnk [site] | |
{:site_id (:id site) | |
:locations {}})] | |
:customer-stat [:exchange_customer_stats | |
(fnk [site customer] | |
{:customer_id (:id customer) | |
:site_id (:id site) | |
:last_buy_at #(time/->sql-time) | |
:created_at #(time/->sql-time) | |
:updated_at #(time/->sql-time)})] | |
:order [:orders | |
(fnk [site customer] | |
{:site_id (:id site) | |
:price 100.00 | |
:customer_id (:id customer) | |
:order_id #(generate-string 10) | |
:confirmation_state "new" | |
:created_at #(time/->sql-time)})] | |
:reward [:rewards | |
(fnk [customer campaign] | |
{:customer_id (:id customer) | |
:campaign_id (:id campaign) | |
:type "Reward::Type" | |
:state "new"})] | |
:banner-session [:exchange_banner_sessions | |
(fnk [site customer] | |
{:source_site_id (:id site) | |
:customer_id (:id customer) | |
:token #(generate-string 16) | |
:created_at #(time/->sql-time) | |
:updated_at #(time/->sql-time)})] | |
:campaign-widget [:campaign_widgets | |
(fnk [campaign widget] | |
{:campaign_id (:id campaign) | |
:widget_id (:id widget) | |
:created_at (fn [v] (time/->sql-time)) | |
:updated_at (time/->sql-time)})] | |
:motivation [:motivations | |
(fnk [site {coupon-source nil}] | |
{:site_id (:id site) | |
:coupon_source_id (:id coupon-source) | |
:type (if coupon-source "coupon" "exchange") | |
:value "10" | |
:unit "%" | |
:created_at #(time/->sql-time) | |
:updated_at #(time/->sql-time)})] | |
:rate-limit [:rate_limits | |
(fnk [site] | |
{:site_id (:id site) | |
:unit "daily" | |
:value 1 | |
:is_active true | |
:scope {"product" "xmail" | |
"site" (-> site :id str) | |
"profile" "*"} | |
:created_at #(time/->sql-time) | |
:updated_at #(time/->sql-time)})] | |
:coupon-source [:coupon_sources | |
(fnk [site] | |
{:site_id (:id site) | |
:type "CouponSource::MultiUse" | |
:coupon_code "COUPON-CODE" | |
:unit "%" | |
:value "10"})] | |
:coupon [:coupons | |
(fnk [coupon-source] | |
{:coupon_source_id (:id coupon-source) | |
:code #(generate-string 10) | |
:state "unused"})] | |
:site-inactivity [:site_inactivities | |
(fnk [site] | |
{:site_id (:id site) | |
:inactivity (* 10 60) | |
:created_at #(time/->sql-time) | |
:updated_at #(time/->sql-time)})] | |
:utm-channel [:utm_channels | |
(fnk [site] | |
{:site_id (:id site) | |
:title "Utm Channel" | |
:created_at #(time/->sql-time) | |
:updated_at #(time/->sql-time)})] | |
:utm-channel-segment [:utm_channel_segments | |
(fnk [customer-segment utm-channel] | |
{:customer_segment_id (:id customer-segment) | |
:utm_channel_id (:id utm-channel) | |
:created_at #(time/->sql-time) | |
:updated_at #(time/->sql-time) | |
:match true})] | |
:new-widget [:widgets-db :widgets | |
(fnk [site] | |
{:id utils/uuid | |
:type "PrecheckoutGeneral" | |
:meta {:description "Good widget" | |
:site (str (:id site))}})] | |
:widget-version [:widgets-db :versions | |
(fnk [new-widget] | |
(let [content "Greate widget {{publisher_name}}"] | |
{:id utils/uuid | |
:content content | |
:widget_id (:id new-widget) | |
:sha (utils/sha content)}))] | |
:widget-version-attach [:widgets-db :usages | |
(fnk [widget-version campaign] | |
{:version_id (:id widget-version) | |
:key "campaign" | |
:value (str (:id campaign)) | |
:meta {"spot" "bottom" | |
"colors_json" "{\"title\":\"black\",\"background\":\"white\"}"}})] | |
:location [:locations | |
(fnk [] | |
{:city "Moscow" | |
:region "Moscow" | |
:country_code "RU"})]}) | |
(defn normalize-repository | |
[repository] | |
(for-map [[k v] repository | |
:let [[db-key table f] (if (= 2 (count v)) | |
(cons :main-db v) | |
v) | |
all-deps (utils/fnk-to-deps f) | |
deps (filter #(not (:optional (meta %))) all-deps) | |
optional (filter #(:optional (meta %)) all-deps)]] | |
k {:key k | |
:db-key db-key | |
:table table | |
:f f | |
:depends (for-map [d deps] (first d) (first d)) | |
:optional-depends (for-map [d optional] (first d) (first d))})) | |
(def normalized-repository (normalize-repository repository)) | |
(defn evaluate-res | |
[m] | |
(map-vals #(if (fn? %) (%) %) m)) | |
(defn normalize-spec | |
[normalized-repo spec] | |
(if (keyword? spec) | |
(normalized-repo spec) | |
(let [based-on (normalized-repo (:fixture spec))] | |
(when-not based-on | |
(throw+ {:type ::unknown-fixture :key (:fixture spec)})) | |
{:key (:as spec (:fixture spec)) | |
:depends (merge (:depends based-on) (:depends spec)) | |
:optional-depends (merge (:optional-depends based-on) (:depends spec)) | |
:db-key (:db-key based-on) | |
:table (:table based-on) | |
:f (fn [m] | |
(merge ((:f based-on) m) (:merge spec)))}))) | |
(defn make-one! | |
[fixtures repository spec] | |
(locking repository | |
(let [db ((:db-key spec) fixtures) | |
cache (:cache fixtures)] | |
(if-let [exists (get @cache (:key spec))] | |
exists | |
(let [deps-map (for-map [[d source] (:depends spec)] | |
d (when-let [spec (repository source)] | |
(make-one! fixtures repository spec) | |
;; (throw+ {:type ::unknown-dep :key source}) | |
)) | |
optionals (for-map [[d source] (:optional-depends spec) | |
:let [v (get @cache source)]] | |
d v) | |
data (evaluate-res ((:f spec) (merge deps-map optionals))) | |
data* (first (jdbc/insert! db (:table spec) data))] | |
(swap! cache assoc (:key spec) data*) | |
data*))))) | |
(defn make | |
[fixtures & specs] | |
(let [normalized (map #(normalize-spec (:repository fixtures) %) specs) | |
full-repository (reduce #(assoc %1 (:key %2) %2) | |
(:repository fixtures) normalized)] | |
(for-map [s normalized] | |
(:key s) (make-one! fixtures full-repository s)))) | |
(defcomponent fixtures [components/main-db] | |
[_] | |
(start [this] | |
(assoc this :cache (atom {}) | |
:repository (normalize-repository repository))) | |
(stop [this] this)) | |
(defn mk | |
[& [r]] | |
(fixtures {:repo r})) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment