session tokens stored in redis now; tests extended
This commit is contained in:
+1
-3
@@ -32,13 +32,11 @@
|
|||||||
(.click ($ :#preview-button)
|
(.click ($ :#preview-button)
|
||||||
(fn [e]
|
(fn [e]
|
||||||
(xhr [:post "/preview"]
|
(xhr [:post "/preview"]
|
||||||
{:session-key (val $session-key)
|
{:draft (val $draft)}
|
||||||
:draft (val $draft)}
|
|
||||||
(fn [json-map]
|
(fn [json-map]
|
||||||
(let [m (js->clj (JSON/parse json-map))]
|
(let [m (js->clj (JSON/parse json-map))]
|
||||||
(do
|
(do
|
||||||
(inner $preview (m "preview"))
|
(inner $preview (m "preview"))
|
||||||
(val $session-key (m "session-key"))
|
|
||||||
(show $preview-start-line)
|
(show $preview-start-line)
|
||||||
(scroll-to $preview-start-line)))))))
|
(scroll-to $preview-start-line)))))))
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
(ns NoteHub.storage
|
(ns NoteHub.storage
|
||||||
(:use [NoteHub.settings]
|
(:use [NoteHub.settings]
|
||||||
|
[noir.util.crypt :only [encrypt]]
|
||||||
[noir.options :only [dev-mode?]])
|
[noir.options :only [dev-mode?]])
|
||||||
(:require [clj-redis.client :as redis]))
|
(:require [clj-redis.client :as redis]))
|
||||||
|
|
||||||
@@ -12,11 +13,26 @@
|
|||||||
; DB hierarchy levels
|
; DB hierarchy levels
|
||||||
(def note "note")
|
(def note "note")
|
||||||
(def views "views")
|
(def views "views")
|
||||||
|
(def sessions "sessions")
|
||||||
|
|
||||||
; Concatenates all fields to a string
|
; Concatenates all fields to a string
|
||||||
(defn- build-key [[year month day] title]
|
(defn- build-key [[year month day] title]
|
||||||
(print-str year month day title))
|
(print-str year month day title))
|
||||||
|
|
||||||
|
(defn create-session
|
||||||
|
"Creates a random session token"
|
||||||
|
[]
|
||||||
|
(let [token (encrypt (str (rand-int Integer/MAX_VALUE)))]
|
||||||
|
(do (redis/sadd db sessions token) token)))
|
||||||
|
|
||||||
|
(defn invalidate-session
|
||||||
|
"Invalidates given session"
|
||||||
|
[token]
|
||||||
|
; Jedis is buggy & returns an NPE for token == nil
|
||||||
|
(when token
|
||||||
|
(let [was-valid (redis/sismember db sessions token)]
|
||||||
|
(do (redis/srem db sessions token) was-valid))))
|
||||||
|
|
||||||
(defn set-note
|
(defn set-note
|
||||||
"Creates a note with the given title and text in the given date namespace"
|
"Creates a note with the given title and text in the given date namespace"
|
||||||
[date title text]
|
[date title text]
|
||||||
|
|||||||
+10
-15
@@ -11,10 +11,8 @@
|
|||||||
[ring.util.codec :only [url-encode]]
|
[ring.util.codec :only [url-encode]]
|
||||||
[hiccup.core]
|
[hiccup.core]
|
||||||
[hiccup.util :only [escape-html]]
|
[hiccup.util :only [escape-html]]
|
||||||
[noir.session :only [flash-put! flash-get]]
|
|
||||||
[noir.response :only [redirect status]]
|
[noir.response :only [redirect status]]
|
||||||
[noir.core :only [defpage render]]
|
[noir.core :only [defpage render]]
|
||||||
[noir.util.crypt :only [encrypt]]
|
|
||||||
[cheshire.core]
|
[cheshire.core]
|
||||||
[noir.statuses])
|
[noir.statuses])
|
||||||
(:import
|
(:import
|
||||||
@@ -29,12 +27,6 @@
|
|||||||
(defn md-to-html [md-text]
|
(defn md-to-html [md-text]
|
||||||
(.markdownToHtml md-processor md-text))
|
(.markdownToHtml md-processor md-text))
|
||||||
|
|
||||||
; Creates a random session token
|
|
||||||
(defn- get-flash-key []
|
|
||||||
(let [k (encrypt (str (rand-int Integer/MAX_VALUE)))]
|
|
||||||
(do (flash-put! k true)
|
|
||||||
(print-str k))))
|
|
||||||
|
|
||||||
; Sets a custom message for each needed HTTP status.
|
; Sets a custom message for each needed HTTP status.
|
||||||
; The message to be assigned is extracted with a dynamically generated key
|
; The message to be assigned is extracted with a dynamically generated key
|
||||||
(doseq [code [400 404 500]]
|
(doseq [code [400 404 500]]
|
||||||
@@ -54,16 +46,20 @@
|
|||||||
(layout params title [:article (md-to-html md-text)]))
|
(layout params title [:article (md-to-html md-text)]))
|
||||||
(status 404 (get-page 404))))
|
(status 404 (get-page 404))))
|
||||||
|
|
||||||
|
(defn get-date
|
||||||
|
"Returns today's date"
|
||||||
|
[]
|
||||||
|
(map #(+ (second %) (.get (Calendar/getInstance) (first %)))
|
||||||
|
{Calendar/YEAR 0, Calendar/MONTH 1, Calendar/DAY_OF_MONTH 0}))
|
||||||
|
|
||||||
; Routes
|
; Routes
|
||||||
; ======
|
; ======
|
||||||
|
|
||||||
; This function answers to an AJAX request: it gets a session key and a markdown text.
|
; This function answers to an AJAX request: it gets a session key and a markdown text.
|
||||||
; It returns the html code of the provided markdown and a new session key.
|
; It returns the html code of the provided markdown and a new session key.
|
||||||
(defpage [:post "/preview"] {:keys [session-key draft]}
|
(defpage [:post "/preview"] {:keys [session-key draft]}
|
||||||
(when (flash-get session-key)
|
|
||||||
(generate-string
|
(generate-string
|
||||||
{:session-key (get-flash-key)
|
{:preview (md-to-html draft)}))
|
||||||
:preview (md-to-html draft)})))
|
|
||||||
|
|
||||||
; Landing Page
|
; Landing Page
|
||||||
(defpage "/" {}
|
(defpage "/" {}
|
||||||
@@ -90,7 +86,7 @@
|
|||||||
(layout {:js true} (get-message :new-note)
|
(layout {:js true} (get-message :new-note)
|
||||||
[:div.central-element
|
[:div.central-element
|
||||||
(form-to [:post "/post-note"]
|
(form-to [:post "/post-note"]
|
||||||
(hidden-field :session-key (get-flash-key))
|
(hidden-field :session-key (create-session))
|
||||||
(hidden-field {:id :session-value} :session-value)
|
(hidden-field {:id :session-value} :session-value)
|
||||||
(text-area {:class :max-width} :draft (get-message :loading))
|
(text-area {:class :max-width} :draft (get-message :loading))
|
||||||
[:div#buttons.hidden
|
[:div#buttons.hidden
|
||||||
@@ -130,7 +126,7 @@
|
|||||||
; New Note Posting — the most "complex" function in the entire app ;)
|
; New Note Posting — the most "complex" function in the entire app ;)
|
||||||
(defpage [:post "/post-note"] {:keys [draft session-key session-value]}
|
(defpage [:post "/post-note"] {:keys [draft session-key session-value]}
|
||||||
; first we collect all info needed to evaluate the validity of the note creation request
|
; first we collect all info needed to evaluate the validity of the note creation request
|
||||||
(let [valid-session (flash-get session-key) ; was the note posted from a newly generated form?
|
(let [valid-session (invalidate-session session-key) ; was the note posted from a newly generated form?
|
||||||
valid-draft (not (ccs/blank? draft)) ; has the note a meaningful content?
|
valid-draft (not (ccs/blank? draft)) ; has the note a meaningful content?
|
||||||
; is the hash code correct?
|
; is the hash code correct?
|
||||||
valid-hash (try
|
valid-hash (try
|
||||||
@@ -142,8 +138,7 @@
|
|||||||
; if yes, we compute the current date, extract a title string from the text,
|
; if yes, we compute the current date, extract a title string from the text,
|
||||||
; which will be a part of the url and look whether this title is free today;
|
; which will be a part of the url and look whether this title is free today;
|
||||||
; if not, append "-n", where "n" is the next free number
|
; if not, append "-n", where "n" is the next free number
|
||||||
(let [[year month day] (map #(+ (second %) (.get (Calendar/getInstance) (first %)))
|
(let [[year month day] (get-date)
|
||||||
{Calendar/YEAR 0, Calendar/MONTH 1, Calendar/DAY_OF_MONTH 0})
|
|
||||||
untrimmed-line (filter #(or (= \- %) (Character/isLetterOrDigit %))
|
untrimmed-line (filter #(or (= \- %) (Character/isLetterOrDigit %))
|
||||||
(-> draft ccs/split-lines first (sreplace " " "-") lower-case))
|
(-> draft ccs/split-lines first (sreplace " " "-") lower-case))
|
||||||
trim (fn [s] (apply str (drop-while #(= \- %) s)))
|
trim (fn [s] (apply str (drop-while #(= \- %) s)))
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
(def test-title "Some title.")
|
(def test-title "Some title.")
|
||||||
(def test-note "This is a test note.")
|
(def test-note "This is a test note.")
|
||||||
|
|
||||||
|
(deftest storage
|
||||||
(testing "Storage"
|
(testing "Storage"
|
||||||
(testing "of correct note creation"
|
(testing "of correct note creation"
|
||||||
(is (= (do
|
(is (= (do
|
||||||
@@ -18,10 +19,19 @@
|
|||||||
"2")))
|
"2")))
|
||||||
(testing "of the note access"
|
(testing "of the note access"
|
||||||
(is (not= (get-note date test-title) "any text")))
|
(is (not= (get-note date test-title) "any text")))
|
||||||
|
(testing "session management"
|
||||||
|
(let [s1 (create-session)
|
||||||
|
s2 (create-session)
|
||||||
|
s3 (create-session)]
|
||||||
|
(is (invalidate-session s1))
|
||||||
|
(is (not (invalidate-session (str s1 s2))))
|
||||||
|
(is (invalidate-session s2))
|
||||||
|
(is (not (invalidate-session "wrongtoken")))
|
||||||
|
(is (invalidate-session s3))))
|
||||||
(testing "of note existence"
|
(testing "of note existence"
|
||||||
(is (note-exists? date test-title))
|
(is (note-exists? date test-title))
|
||||||
(is (not (do
|
(is (not (do
|
||||||
(delete-note date test-title)
|
(delete-note date test-title)
|
||||||
(note-exists? date test-title))))
|
(note-exists? date test-title))))
|
||||||
(is (not (note-exists? [2013 06 03] test-title)))
|
(is (not (note-exists? [2013 06 03] test-title)))
|
||||||
(is (not (note-exists? date "some title")))))
|
(is (not (note-exists? date "some title"))))))
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
(ns NoteHub.test.views.pages
|
(ns NoteHub.test.views.pages
|
||||||
|
(:require [NoteHub.crossover.lib :as lib])
|
||||||
(:use [NoteHub.views.pages]
|
(:use [NoteHub.views.pages]
|
||||||
[noir.util.test]
|
[noir.util.test]
|
||||||
|
[clojure.contrib.string :only [substring?]]
|
||||||
[NoteHub.views.common :only [url]]
|
[NoteHub.views.common :only [url]]
|
||||||
[NoteHub.storage]
|
[NoteHub.storage]
|
||||||
[clojure.test]))
|
[clojure.test]))
|
||||||
|
|
||||||
(def date [2012 6 3])
|
(def date [2012 6 3])
|
||||||
(def test-title "some-title")
|
(def test-title "some-title")
|
||||||
(def test-note "# This is a test note.\nHello _world_.")
|
(def test-note "# This is a test note.\nHello _world_. Motörhead, тест.")
|
||||||
|
|
||||||
(defn create-testnote-fixture [f]
|
(defn create-testnote-fixture [f]
|
||||||
(set-note date test-title test-note)
|
(set-note date test-title test-note)
|
||||||
@@ -29,22 +31,43 @@
|
|||||||
(md-to-html "#_hellö_ __world__\ntest `code`")))))
|
(md-to-html "#_hellö_ __world__\ntest `code`")))))
|
||||||
(deftest export-test
|
(deftest export-test
|
||||||
(testing "Markdown export"
|
(testing "Markdown export"
|
||||||
(has-body (send-request (url 2012 6 3 "some-title" "export")) test-note)))
|
(is (has-body (send-request (url 2012 6 3 "some-title" "export")) test-note))))
|
||||||
|
|
||||||
|
(deftest note-creation
|
||||||
|
(let [session-key (create-session)
|
||||||
|
date (get-date)
|
||||||
|
; TODO: replace note generation by a function from pages.clj
|
||||||
|
title "this-is-a-test-note"
|
||||||
|
[year month day] date]
|
||||||
|
(testing "Note creation"
|
||||||
|
(is (has-status
|
||||||
|
(send-request
|
||||||
|
[:post "/post-note"]
|
||||||
|
{:session-key session-key
|
||||||
|
:draft test-note
|
||||||
|
:session-value (str (lib/hash #(.codePointAt % 0)
|
||||||
|
(str test-note session-key)))}) 302))
|
||||||
|
(is (note-exists? date title))
|
||||||
|
(is (substring? "Hello <em>world</em>"
|
||||||
|
((send-request (url year month day title)) :body)))
|
||||||
|
(is (do
|
||||||
|
(delete-note date title)
|
||||||
|
(not (note-exists? date title)))))))
|
||||||
|
|
||||||
(deftest requests
|
(deftest requests
|
||||||
(testing "HTTP Status"
|
(testing "HTTP Status"
|
||||||
(testing "of a wrong access"
|
(testing "of a wrong access"
|
||||||
(has-status (send-request "/wrong-page") 404)
|
(is (has-status (send-request "/wrong-page") 404))
|
||||||
(has-status (send-request (url 2012 6 3 "lol" "stat")) 404)
|
(is (has-status (send-request (url 2012 6 3 "lol" "stat")) 404))
|
||||||
(has-status (send-request (url 2012 6 3 "lol" "export")) 404)
|
(is (has-status (send-request (url 2012 6 3 "lol" "export")) 404))
|
||||||
(has-status (send-request (url 2012 6 3 "lol")) 404)
|
(is (has-status (send-request (url 2012 6 3 "lol")) 404))
|
||||||
(has-status (send-request (url 2012 6 4 "wrong-title")) 404))
|
(is (has-status (send-request (url 2012 6 4 "wrong-title")) 404)))
|
||||||
(testing "of corrupt note-post"
|
(testing "of corrupt note-post"
|
||||||
(has-status (send-request [:post "/post-note"]) 400))
|
(is (has-status (send-request [:post "/post-note"]) 400)))
|
||||||
(testing "valid accesses"
|
(testing "valid accesses"
|
||||||
(has-status (send-request "/new") 200)
|
(is (has-status (send-request "/new") 200))
|
||||||
(has-status (send-request (url 2012 6 3 "some-title")) 200)
|
(is (has-status (send-request (url 2012 6 3 "some-title")) 200))
|
||||||
(has-status (send-request (url 2012 6 3 "some-title" "export")) 200)
|
(is (has-status (send-request (url 2012 6 3 "some-title" "export")) 200))
|
||||||
(has-status (send-request (url 2012 6 3 "some-title" "stat")) 200)
|
(is (has-status (send-request (url 2012 6 3 "some-title" "stat")) 200))
|
||||||
(has-status (send-request (url 2012 6 3 "some-title")) 200)
|
(is (has-status (send-request (url 2012 6 3 "some-title")) 200))
|
||||||
(has-status (send-request "/") 200))))
|
(is (has-status (send-request "/") 200)))))
|
||||||
|
|||||||
Reference in New Issue
Block a user