   (:use midje.sweet))
 
 
-;; Note: ..t.. is used as an irrelevant type-repo
-
-;;; Utilities
-
-
-
-
-
-
-;;; Decompressors
-
-;; Note: in tests below, I'll use `list` when I want to signify that a
-;; collection of values correspond to the multiple condensed descriptions that
-;; `named` accepts.
-
-(fact dc:validate-description-types
-  (fact "passes through many things unchanged"
-    (subject/dc:validate-starting-descriptions (list {} [] )) => (list {} [] ))
-
-  (fact "errors in canonicalize context"
-    (subject/canonicalize ..t.. :a) => (throws #"maps or vectors")))
-
-
-(fact dc-expand-type-signifiers
-  (fact "passes through many things unchanged"
-    (subject/dc:expand-type-signifiers ..repo.. []) => []
-    (let [unchanged [ (path/requires :x [:y :z]) {:a even?} ]]
-      (subject/dc:expand-type-signifiers ..repo.. unchanged) => unchanged))
-  
-  
-  (fact "deep-traverses to find `includes`"
-    (let [point {[:x] [even?], [:y] [odd?]}
-          type-map {:Point point}]
-
-      (subject/dc:expand-type-signifiers type-map (list {[:a :b] (path/includes :Point)} ))
-      => [{[:a :b] point}]
-
-      (subject/dc:expand-type-signifiers type-map (list (path/requires :x [:a (path/includes :Point)])))
-      => [ (path/requires :x [:a point]) ]
-
-
-      (subject/dc:expand-type-signifiers type-map [
-         (path/requires :x (list :a (path/includes :Point)))
-         (path/requires (list :a [:b :c] (path/includes :Point)))
-         ]) => 
-           [ 
-            (path/requires :x [:a point])
-            (path/requires [:a [:b :c] point])
-           ]))
-
-  (fact "affects canonicalization"
+(def ps list) ; "partial type descriptions" - just to make top-level grouping easier to follow
+
+(facts "Part 1: steps in canonicalization explained"
+  (fact dc:expand-type-signifiers
+    ;; only use of the type map ..repo.. used when it's irrelevant
+    
+    (fact "passes through many things unchanged"
+      (subject/dc:expand-type-signifiers ..repo.. []) => []
+      (let [unchanged (ps (path/requires :x [:y :z])
+                          {:a even?})]
+        (subject/dc:expand-type-signifiers ..repo.. unchanged) => unchanged))
+    
+    (fact "deep-traverses to find `includes`"
+      (let [point {[:x] [even?], [:y] [odd?]}
+            type-map {:Point point}]
+        
+        (subject/dc:expand-type-signifiers type-map (ps {[:a :b]
+                                                         (path/includes :Point)} ))
+        => [{[:a :b] point}]
+        
+        (subject/dc:expand-type-signifiers type-map
+                                           (ps (path/requires :x [:a (path/includes :Point)])))
+        => [ (path/requires :x [:a point]) ]
+        
+        
+        (subject/dc:expand-type-signifiers type-map
+                                           (ps (path/requires :x [:a (path/includes :Point)])
+                                               {[:a [:b :c]] (path/includes :Point)}))
+        => (ps (path/requires :x [:a point])
+               {[:a [:b :c]] point}))))
+
+  (fact dc:validate-starting-descriptions
+    (subject/dc:validate-starting-descriptions (ps {} [] )) => (ps {} [])
+    (subject/dc:validate-starting-descriptions (ps :a)) => (throws #"maps or vectors"))
+
+  (fact dc:spread-collections-of-required-paths
+    (fact "passes maps through unchanged"
+      (subject/dc:spread-collections-of-required-paths (ps {} {:a 1})) => (just {} {:a 1}))
+    
+    (fact "splices vectors in"
+      (subject/dc:spread-collections-of-required-paths
+       (ps (path/requires [:l1a :l2a] [:l1b :l2b])
+             {:c even?}))
+      => (just [:l1a :l2a] [:l1b :l2b] {:c even?}))
+  
+    (fact "converts a single key to a singleton path"
+      (subject/dc:spread-collections-of-required-paths (ps (path/requires :a :b)
+                                                           {:c even?}))
+      => (just [:a] [:b] {:c even?})))
+
+
+  (fact dc:split-paths-ending-in-maps
+    (fact "doesn't care about maps or most vectors"
+      (subject/dc:split-paths-ending-in-maps (ps {} [:a :b] )) => (just {} [:a :b] ))
+    
+    (fact "paths ending in maps are split into a pure path and a map"
+      (subject/dc:split-paths-ending-in-maps (ps [:a {:b 1}] ))
+      => (just [:a]
+               {[:a] {:b 1}})))
+  
+
+  (fact dc:required-paths->maps
+    (subject/dc:required-paths->maps []) => []
+    (fact "doesn't care about maps"
+      (subject/dc:required-paths->maps (ps {:a 1} )) => (just {:a 1}))
+
+    (fact "produces one map for each incoming vector"
+      (subject/dc:required-paths->maps (ps [:a] [:b :c])) => (just {[:a] [pred/required-key]}
+                                                                   {[:b :c] [pred/required-key]} ))
+
+    (fact "forking paths are not processed yet"
+      (subject/dc:required-paths->maps (ps [:a [:b :c] :d]))
+      => (just {[:a [:b :c] :d] [pred/required-key]})))
+
+
+  (fact dc:flatten-maps
+    (subject/dc:flatten-maps []) => []
+
+    (fact "only cares about maps"
+      (subject/dc:flatten-maps (ps [:a :b] [:a])) => (just [:a :b] [:a]))
+
+    (fact "flattens individual maps"
+      (subject/dc:flatten-maps (ps {:a {:b even?}})) => [ { [:a :b] [even?] } ]))
+
+  
+
+
+
+
+    
+)
+
+
+
+
+(facts "Part 2: examples of use in canonicalization"
+  (fact dc:expand-type-signifiers
     (let [type-map {:Type (subject/canonicalize ..t.. [:a] {:a odd? :b even?})}]
       (subject/canonicalize type-map (path/includes :Type)) => {[:a] [pred/required-key odd?]
                                                                 [:b] [even?]}

      => {[:points] [pred/required-key]
          [:points ALL :color] [pred/required-key string?]
          [:points ALL :x] [pred/required-key integer?]
          [:points ALL :y] [pred/required-key integer?]})))


;;;; Let us not forget the simple cases

(fact "simple maps just have their keys and values vectorized"
  (subject/canonicalize ..t.. {:a even?}) => {[:a] [even?]}
  (subject/canonicalize ..t.. {:a even?, :b even?})
  => {[:a] [even?]
      [:b] [even?]})

(fact "simple vectors"
  (subject/canonicalize ..t.. [:a :b]) => {[:a] [pred/required-key]
                                           [:b] [pred/required-key]})
  

;;;; Canonicalize - random examples, probably redundant

(fact "already vectorized keys and values are left alone if flat"
  (subject/canonicalize ..t.. {[:a] even?}) => {[:a] [even?]}
  (subject/canonicalize ..t.. {[:a] [even?]}) => {[:a] [even?]}
  (subject/canonicalize ..t.. {[:a :b] even?}) => {[:a :b] [even?]})
  
(fact "forks are allowed in vectors"
  (subject/canonicalize ..t.. {[:a [:b1 :b2]] even?})
  => {[:a :b1] [even?]

      => {[:a :a] [pred/required-key odd?]
          [:a :b] [even?]
          [:a :c] [pos?]
          [:c] [pred/required-key]}))

  (fact dc:spread-collections-of-required-paths
    (subject/canonicalize ..t.. (path/requires :a :b :c [:d :e]))
    => (subject/canonicalize ..t.. (path/requires :a)
                                   (path/requires [:b])
                                   (path/requires :c) 
                                   (path/requires [:d :e])))

  (fact dc:split-paths-ending-in-maps
    (subject/canonicalize ..t.. (path/requires [:x {:a even?, :b even?}]))
    => (subject/canonicalize ..t.. (path/requires [:x]) {:x {:a even?, :b even?}})

    (subject/canonicalize ..t.. (path/requires [:x {:a even?, :b even?} ]))
    => {[:x] [pred/required-key]
        [:x :a] [even?]
