Fork me on GitHub
#specmonstah
<
2020-07-09
>
codeasone16:07:47

I'm modelling a many-to-many user to team relationship with specmonstah and wondering if I'm missing a trick or engaging in any bad practice wrt. to the generation of test fixture data:

(s/def ::user (s/keys :req-un [::id
                               ::email
                               ::password]))

(s/def ::team (s/keys :req-un [::id]))

(s/def ::empty-map (s/with-gen map? #(gen/fmap (fn [_] {}) (s/gen integer?))))

(s/def ::team-user ::empty-map)

(def schema
  {:user {:prefix :u
          :spec ::user
          :relations {:industry-id [:industry :id]}}

   :team {:prefix :tm
          :spec ::team
          :relations {:primary-user-id [:user :id]}}

   :team-user {:prefix :tmu
               :spec ::team-user
               :relations {:team-id [:team :id]
                           :user-id [:user :id]}}})

;; Note: using Honey SQL
(defmulti insert-record (fn [db ent-type spec-gen] ent-type))

(defmethod insert-record :user
  [db ent-type spec-gen]
  (let [encrypted-password (hash-password db (:password spec-gen))
        attrs (-> spec-gen
                  (assoc :password encrypted-password))]
    {:insert-into :users
     :values [attrs]}))

(defn ent-type->table-name [ent-type]
  (keyword (inflections/plural (name ent-type))))

(defmethod insert-record :default [db ent-type spec-gen]
  {:insert-into (ent-type->table-name ent-type)
   :values [spec-gen]})

(defn insert
  ([db] (insert db {}))
  ([db options]
   (fn [_ {:keys [spec-gen ent-type]}]
     (database/execute! db (insert-record db ent-type spec-gen) options))))

;; Fixture helper
(defn fixture-data! [db specmonstah-query]
  (-> (sg/ent-db-spec-gen {:schema schema} specmonstah-query)
      (sm/visit-ents :insert (insert db))
      (sm/attr-map :spec-gen)))

;; Test driver
(deftest ^:system team-setup-test
  (st/system-test
   (fn [{:keys [database]}]
     (let [spec-data (tu/fixture-data!
                      database
                      {:team-user [[1]]})]
       ;; Creates a team of one user, who is also the team owner (primary)
       ;; Assertions etc...
       ))))
I'm including outline code for driver/fixture generation side of things, but my question relates to the schema definition in particular

codeasone16:07:21

Here's the physical model:

codeasone16:07:25

I don't feel great about generating team_users test data via (s/def ::team-user ::empty-map) and

:team-user {:prefix :tmu
               :spec ::team-user
               :relations {:team-id [:team :id]
                           :user-id [:user :id]}}
but can't think of any other way to represent this atm, would welcome suggestions

frankitox16:07:38

You're looking for something like this?

(s/def ::user (s/keys :req-un [:user/id
                               :user/email
                               :user/password]))
(s/def :team/id ::id)
(s/def :team/owner ::id)
(s/def ::team (s/keys :req-un [:team/id
                               :team/owner]))

(s/def :team-user/team-id ::id)
(s/def :team-user/user-id ::id)
(s/def ::team-user (s/keys :req-un [:team-user/team-id
                                    :team-user/user-id]))

(def schema
  {:user {:prefix :u
          :spec ::user}
   :team {:prefix :tm
          :spec ::team
          :relations {:owner [:user :id]}}
   :team-user {:prefix :tmu
               :spec ::team-user
               :relations {:team-id [:team :id]
                           :user-id [:user :id]}}})
(sg/ent-db-spec-gen-attr {:schema schema} {:team-user [[1]]})