Skip to content
This repository has been archived by the owner on Jun 15, 2024. It is now read-only.

Commit

Permalink
Implement StepResultUpdateConsumer and QueryStepResultsSource protocols
Browse files Browse the repository at this point in the history
  • Loading branch information
flosell committed Oct 16, 2016
1 parent 8f92b1b commit dfe823d
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 64 deletions.
20 changes: 14 additions & 6 deletions src/clj/lambdacd/internal/default_pipeline_state.clj
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,32 @@
(defrecord DefaultPipelineState [state-atom structure-atom home-dir max-builds]
old-pipeline-state/PipelineStateComponent
(update [self build-number step-id new-step-result]
(if (not (nil? state-atom)) ; convenience for tests: if no state exists we just do nothing
(let [new-state (swap! state-atom #(->> %
(update-step-result-in-state build-number step-id new-step-result)
(truncate-build-history home-dir max-builds)))]
(persistence/write-build-history home-dir build-number new-state))))
(protocols/consume-step-result-update self build-number step-id new-step-result))
(get-all [self]
@state-atom)
(get-internal-state [self]
state-atom)
(next-build-number [self]
(inc (most-recent-build-number-in-state @state-atom)))

protocols/PipelineStructureConsumer
(consume-pipeline-structure [self build-number pipeline-structure-representation]
(swap! structure-atom #(assoc % build-number pipeline-structure-representation))
(persistence/write-pipeline-structure home-dir build-number pipeline-structure-representation))
protocols/PipelineStructureSource
(get-pipeline-structure [self build-number]
(get @structure-atom build-number)))
(get @structure-atom build-number))

protocols/StepResultUpdateConsumer
(consume-step-result-update [self build-number step-id step-result]
(if (not (nil? state-atom)) ; convenience for tests: if no state exists we just do nothing
(let [new-state (swap! state-atom #(->> %
(update-step-result-in-state build-number step-id step-result)
(truncate-build-history home-dir max-builds)))]
(persistence/write-build-history home-dir build-number new-state))))
protocols/QueryStepResultsSource
(get-step-results [self build-number]
(get @state-atom build-number)))

(defn new-default-pipeline-state [config & {:keys [initial-state-for-testing]}]
(let [home-dir (:home-dir config)
Expand Down
114 changes: 56 additions & 58 deletions test/clj/lambdacd/internal/default_pipeline_state_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -15,91 +15,89 @@
(def no-home-dir nil)
(def keep-all-builds Integer/MAX_VALUE)

(defn- after-update [build id newstate]
(let [state (->DefaultPipelineState (atom clean-pipeline-state) (atom {}) no-home-dir keep-all-builds)]
(pipeline-state-record/update state build id newstate)
(pipeline-state-record/get-all state)))

(deftest general-pipeline-state-test
(testing "that the next buildnumber is the highest build-number currently in the pipeline-state"
(is (= 5 (next-build-number (->DefaultPipelineState (atom { 3 {} 4 {} 1 {}}) (atom {}) no-home-dir keep-all-builds))))
(is (= 1 (next-build-number (->DefaultPipelineState (atom clean-pipeline-state) (atom {}) no-home-dir keep-all-builds)))))
(testing "that a new pipeline-state will be set on update"
(is (= { 10 { [0] { :foo :bar }}} (tu/without-ts (after-update 10 [0] {:foo :bar})))))
(testing "that update will set a first-updated-at and most-recent-update-at timestamp"
(is (= 1 (next-build-number (->DefaultPipelineState (atom clean-pipeline-state) (atom {}) no-home-dir keep-all-builds))))))

(deftest pipeline-structure-state-test
(testing "that we can consume and retrieve pipeline-structure"
(let [component (new-default-pipeline-state {:home-dir (utils/create-temp-dir)})]
(protocols/consume-pipeline-structure component 1 {:some-pipeline :structure})
(is (= {:some-pipeline :structure} (protocols/get-pipeline-structure component 1)))))
(testing "that we return nil if pipeline-structure isn't there"
(let [component (new-default-pipeline-state {:home-dir (utils/create-temp-dir)})]
(is (= nil (protocols/get-pipeline-structure component 43)))))
(testing "that we can retrieve the pipeline-structure from disk"
(let [home (utils/create-temp-dir)
component (new-default-pipeline-state {:home-dir home})]
(protocols/consume-pipeline-structure component 1 {:some-pipeline :structure})
(let [other-component (new-default-pipeline-state {:home-dir home})]
(is (= {:some-pipeline :structure} (protocols/get-pipeline-structure other-component 1)))))))

(deftest step-result-state-test
(testing "that we can consume and retrieve step results"
(let [component (new-default-pipeline-state {:home-dir (utils/create-temp-dir)})]
(protocols/consume-step-result-update component 1 [1 1] {:some :step-result})
(is (= {[1 1] {:some :step-result}} (tu/without-ts (protocols/get-step-results component 1))))))(testing "that update will set a first-updated-at and most-recent-update-at timestamp"
(let [first-update-timestamp (t/minus (t/now) (t/minutes 1))
last-updated-timestamp (t/now)
state (->DefaultPipelineState (atom clean-pipeline-state) (atom {}) no-home-dir keep-all-builds)]
(t/do-at first-update-timestamp (pipeline-state-record/update state 10 [0] {:foo :bar}))
(t/do-at last-updated-timestamp (pipeline-state-record/update state 10 [0] {:foo :baz}))
(is (= {10 {[0] {:foo :baz :most-recent-update-at last-updated-timestamp :first-updated-at first-update-timestamp}}}
(pipeline-state-record/get-all state)))))
(testing "that updating will save the current state to the file-system"
(let [home-dir (utils/create-temp-dir)
step-result {:foo :bar}
state (->DefaultPipelineState (atom clean-pipeline-state) (atom {}) home-dir keep-all-builds)]
(t/do-at (t/epoch) (pipeline-state-record/update state 10 [0] step-result))
(is (= [{:step-id "0"
:step-result {:foo :bar
:most-recent-update-at (Date. 0)
:first-updated-at (Date. 0)}}]
(edn/read-string (slurp (str home-dir "/build-10/build-state.edn")))))))
(testing "truncate"
component (new-default-pipeline-state {:home-dir (utils/create-temp-dir)})]
(t/do-at first-update-timestamp (protocols/consume-step-result-update component 1 [1 1] {:some :step-result}))
(t/do-at last-updated-timestamp (protocols/consume-step-result-update component 1 [1 1] {:some :step-result}))
(is (= {[1 1] {:some :step-result :most-recent-update-at last-updated-timestamp :first-updated-at first-update-timestamp}} (protocols/get-step-results component 1)))))
(testing "truncating"
(testing "that updating will remove the oldest build if more than the maximum number of builds are written"
(let [max-builds 5
state (->DefaultPipelineState (atom clean-pipeline-state) (atom {}) no-home-dir max-builds)]
(doall (for [build (range 5)]
(pipeline-state-record/update state build [0] {:foo :baz :build build})))
(is (= {[0] {:foo :baz :build 0}} (tu/without-ts (get (pipeline-state-record/get-all state) 0))))
(is (= {[0] {:foo :baz :build 4}} (tu/without-ts (get (pipeline-state-record/get-all state) 4))))
component (new-default-pipeline-state {:home-dir (utils/create-temp-dir)
:max-builds max-builds})]
(doall (for [build (range max-builds)]
(protocols/consume-step-result-update component build [1 1] {:foo :bar :build build})))

(is (= {[1 1] {:foo :bar :build 0}} (tu/without-ts (protocols/get-step-results component 0))))
(is (= {[1 1] {:foo :bar :build 4}} (tu/without-ts (protocols/get-step-results component 4))))

(pipeline-state-record/update state 5 [0] {:foo :baz :build 5})
(protocols/consume-step-result-update component 5 [1 1] {:foo :bar :build 5})

(is (nil? (tu/without-ts (get (pipeline-state-record/get-all state) 0))))
(is (= {[0] {:foo :baz :build 4}} (tu/without-ts (get (pipeline-state-record/get-all state) 4))))
(is (= {[0] {:foo :baz :build 5}} (tu/without-ts (get (pipeline-state-record/get-all state) 5))))))
(is (= nil (protocols/get-step-results component 0)))
(is (= {[1 1] {:foo :bar :build 4}} (tu/without-ts (protocols/get-step-results component 4))))
(is (= {[1 1] {:foo :bar :build 5}} (tu/without-ts (protocols/get-step-results component 5))))))
(testing "that updating will remove outdated state from file-system"
(let [max-builds 5
home-dir (utils/create-temp-dir)
state (->DefaultPipelineState (atom clean-pipeline-state) (atom {}) home-dir max-builds)]
(doall (for [build (range 5)]
(pipeline-state-record/update state build [0] {:foo :baz :build build})))
home-dir (utils/create-temp-dir)
component (new-default-pipeline-state {:home-dir home-dir
:max-builds max-builds})]
(doall (for [build (range max-builds)]
(protocols/consume-step-result-update component build [1 1] {:foo :bar :build build})))

(is (.exists (io/file home-dir "build-0/build-state.edn")))
(is (.exists (io/file home-dir "build-4/build-state.edn")))
(is (not (.exists (io/file home-dir "build-5/build-state.edn"))))

(pipeline-state-record/update state 5 [0] {:foo :baz :build 5})
(protocols/consume-step-result-update component 5 [1 1] {:foo :bar :build 5})

(is (not (.exists (io/file home-dir "build-0/build-state.edn"))))
(is (.exists (io/file home-dir "build-4/build-state.edn")))
(is (.exists (io/file home-dir "build-5/build-state.edn")))))
(testing "that it will not accidently remove pipeline-structures for builds that arent truncated but don't have a state yet"
(let [max-builds 10
home-dir (utils/create-temp-dir)
state (->DefaultPipelineState (atom clean-pipeline-state) (atom {}) home-dir max-builds)
state (new-default-pipeline-state {:home-dir home-dir
:max-builds max-builds})
build-dir (io/file home-dir "build-6")
pipeline-structure-file (io/file build-dir "pipeine-structure.edn")]
(doall (for [build (range 5)]
(pipeline-state-record/update state build [0] {:foo :baz :build build})))
(doall (for [build (range max-builds)]
(protocols/consume-step-result-update state build [0] {:foo :baz :build build})))
(.mkdirs build-dir)
(spit pipeline-structure-file "test")
(is (.exists pipeline-structure-file))

(pipeline-state-record/update state 5 [0] {:foo :baz :build 5})
(protocols/consume-step-result-update state 5 [0] {:foo :baz :build 5})

(is (.exists pipeline-structure-file))))))

(deftest pipeline-structure-state-test
(testing "that we can consume and retrieve pipeline-structure"
(let [component (new-default-pipeline-state {:home-dir (utils/create-temp-dir)})]
(protocols/consume-pipeline-structure component 1 {:some-pipeline :structure})
(is (= {:some-pipeline :structure} (protocols/get-pipeline-structure component 1)))))
(testing "that we return nil if pipeline-structure isn't there"
(let [component (new-default-pipeline-state {:home-dir (utils/create-temp-dir)})]
(is (= nil (protocols/get-pipeline-structure component 43)))))
(is (.exists pipeline-structure-file)))))
(testing "that we can retrieve the pipeline-structure from disk"
(let [home (utils/create-temp-dir)
component (new-default-pipeline-state {:home-dir home})]
(protocols/consume-pipeline-structure component 1 {:some-pipeline :structure})
(let [other-component (new-default-pipeline-state {:home-dir home})]
(is (= {:some-pipeline :structure} (protocols/get-pipeline-structure other-component 1)))))))
(let [home-dir (utils/create-temp-dir)
component (new-default-pipeline-state {:home-dir home-dir})]
(protocols/consume-step-result-update component 1 [1 1] {:some :step-result})
(let [other-component (new-default-pipeline-state {:home-dir home-dir})]
(is (= {[1 1] {:some :step-result}} (tu/without-ts (protocols/get-step-results other-component 1))))))))

0 comments on commit dfe823d

Please sign in to comment.