Browse Source

merged with api branch

master
Christian Mueller 12 years ago
parent
commit
80a6b8fdc5
  1. 1
      .gitignore
  2. 18
      API.md
  3. 2
      LANDING.md
  4. 2
      messages
  5. 1
      project.clj
  6. 8
      resources/public/js/main.js
  7. 105
      src/NoteHub/api.clj
  8. 132
      src/NoteHub/storage.clj
  9. 53
      src/NoteHub/views/pages.clj
  10. 153
      test/NoteHub/test/api.clj
  11. 108
      test/NoteHub/test/storage.clj
  12. 25
      test/NoteHub/test/views/pages.clj

1
.gitignore vendored

@ -8,3 +8,4 @@ pom.xml @@ -8,3 +8,4 @@ pom.xml
.lein-*
.crossover-cljs
target/
.nrepl-port

18
API.md

@ -1,20 +1,29 @@ @@ -1,20 +1,29 @@
# NoteHub API
**Version 1.0, status: draft!**
**Version 1.0, status: released.**
## Prerequisites
The NoteHub API can only be used in combination with a __Publisher ID__ (PID) and __Publisher Secret Key__ (PSK), which can be issued [here](http://notehub.org/api/register). The PSK can be revoked at any moment in case of an API abuse.
The NoteHub API can only be used in combination with a __Publisher ID__ (PID) and __Publisher Secret Key__ (PSK), which can be requested [here](#registration). The PSK can be revoked at any moment in case of an API abuse.
A PID is a string chosen by the publisher and cannot be longer than 16 characters (e.g.: __notepadPlugin__). A PSK will be generated by the NoteHub API and can be a string of any length and content.
All API requests must be issued with one special parameter `version` denoting the expected version of the API as a string, e.g. `1.0` (see examples below). You should always put the version of this document as a `version` parameter.
## <a name="registration"></a>NoteHub API Access Request
To register as a publisher and gain access to NoteHub API, please [send](mailto:notehub@icloud.com?subject=NoteHub API Access Request&body=Please add [a] your contact information, [b] short usage explanation and [c] the URL of the resource or it's website.) an email with the following information about you: contact information, short description of what you want to do and an URL of the resource where the API will be used or its website.
## Note Retrieval
A simple `GET` request to the following URL:
http://notehub.org/api/note?version=1.0&title=<NOTE-ID>
http://notehub.org/api/note
with the following parameters:
Parameter | Explanation | Type
--- | --- | ---
`noteID` | Note-ID | **required**
`version` | Used API version | **required**
will return a JSON object containing following self explaining fields: `note`, `longURL`, `shortURL`, `statistics`, `status`.
@ -26,6 +35,7 @@ Example: @@ -26,6 +35,7 @@ Example:
shortURL: "http://notehub.org/0vrcp",
statistics: {
published: "2014-1-3",
edited: "2014-1-12",
views: 24
},
status: {
@ -36,7 +46,7 @@ Example: @@ -36,7 +46,7 @@ Example:
Hence, the status of the request can be evaluated by reading of the property `status.success`. The field `status.comment`might contain an error message, a warning or any other comments from the server.
The note ID is a string, containing the date of publishing and a few first words of the note (usually the header), e.g.: `2014/1/3/lorem-ipsum`. This ID will be generated by NoteHub automatically.
The note ID is a string, containing the date of publishing and a few first words of the note (usually the title), e.g.: `"2014 1 3 lorem-ipsum"`. This ID will be generated by NoteHub automatically.
## Note Creation

2
LANDING.md

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
## News
- January 2014: NoteHub API v1.0 [introduced](/api).
- January 2014: NoteHub 2.0 released: new theme, more performance, extended markdown
- September 2013: Solarized color theme [added](https://github.com/chmllr/NoteHub/pull/4) (thanks Brandon!) ([Demo](http://notehub.org/2012/6/16/how-notehub-is-built?theme=solarized))
@ -9,6 +10,7 @@ @@ -9,6 +10,7 @@
Send your feedback and comments directly to [@gravitydenier](http://twitter.com/gravitydenier) or open an [issue](https://github.com/chmllr/NoteHub/issues) on GitHub.
## Features
- **API**: Integrate the publishing functionality into your editor using the official [NoteHub API](/api).
- **Color Themes**: specify the color scheme by appending the corresponding parameter to the URL:
- [Dark](http://notehub.org/2012/6/16/how-notehub-is-built?theme=dark)
- [Solarized-Dark](http://notehub.org/2012/6/16/how-notehub-is-built?theme=solarized-dark)

2
messages

@ -16,9 +16,11 @@ enter-passwd = Password @@ -16,9 +16,11 @@ enter-passwd = Password
publish = Publish
update = Save
published = Published
edited = Edited
article-views = Article Views
statistics = Statistics
stats = statistics
export = export
edit = edit
short-url = short url
api-registration = NoteHub API Access Request

1
project.clj

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
:description "A free and anonymous hosting for markdown pages."
:dependencies [[org.clojure/clojure "1.5.1"]
[hiccup "1.0.0"]
[cheshire "5.3.1"]
[ring/ring-core "1.1.0"]
[clj-redis "0.0.12"]
[noir "1.3.0-beta1"]]

8
resources/public/js/main.js

@ -16,6 +16,10 @@ var timerDelay = iosDetected ? 800 : 400; @@ -16,6 +16,10 @@ var timerDelay = iosDetected ? 800 : 400;
var show = function(elem) { elem.style.display = "block" }
var $draft, $action, $preview, $password, $plain_password, $input_elems, $dashed_line, updatePreview;
function md2html(input){
return marked(input);
}
function loadPage() {
$draft = $("draft");
$action = $("action");
@ -31,7 +35,7 @@ function loadPage() { @@ -31,7 +35,7 @@ function loadPage() {
timer = setTimeout(function(){
show($dashed_line);
show($input_elems);
$preview.innerHTML = marked(content);
$preview.innerHTML = md2html(content);
}, delay);
};
if($action){
@ -47,7 +51,7 @@ function loadPage() { @@ -47,7 +51,7 @@ function loadPage() {
var mdDocs = document.getElementsByClassName("markdown");
for(var i = 0; i < mdDocs.length; i++){
mdDocs[i].innerHTML = marked(mdDocs[i].innerHTML);
mdDocs[i].innerHTML = md2html(mdDocs[i].innerHTML);
show(mdDocs[i]);
}
}

105
src/NoteHub/api.clj

@ -0,0 +1,105 @@ @@ -0,0 +1,105 @@
(ns NoteHub.api
(:import
[java.util Calendar])
(:use
[NoteHub.settings]
[clojure.string :rename {replace sreplace}
:only [replace blank? lower-case split-lines]])
(:require [NoteHub.storage :as storage]))
(def api-version "1.0")
(def domain "http://notehub.org/")
; Concatenates all fields to a string
(defn build-key
"Returns a storage-key for the given note coordinates"
[[year month day] title]
(print-str year month day title))
(defn get-date
"Returns today's date"
[]
(map #(+ (second %) (.get (Calendar/getInstance) (first %)))
{Calendar/YEAR 0, Calendar/MONTH 1, Calendar/DAY_OF_MONTH 0}))
(defn- create-response
([success] { :success success })
([success message & params]
(assoc (create-response success) :message (apply format message params))))
(defn- getURL [noteID & description]
(if description
(str domain (storage/get-short-url noteID))
(str domain (sreplace noteID #" " "/"))))
(let [md5Instance (java.security.MessageDigest/getInstance "MD5")]
(defn get-signature
"Returns the MD5 hash for the concatenation of all passed parameters"
[& args]
(let [input (apply str args)]
(do (.reset md5Instance)
(.update md5Instance (.getBytes input))
(.toString (new java.math.BigInteger 1 (.digest md5Instance)) 16)))))
(defn get-note [noteID]
{:note (storage/get-note noteID)
:longURL (getURL noteID)
:shortURL (getURL noteID :short)
:statistics (storage/get-note-statistics noteID)
:status (if (storage/note-exists? noteID)
(create-response true)
(create-response false "noteID '%s' unknown" noteID))})
(defn post-note
([note pid signature] (post-note note pid signature nil))
([note pid signature password]
(let [errors (filter identity
[(when-not (storage/valid-publisher? pid) "pid invalid")
(when-not (= signature
(get-signature pid (storage/get-psk pid) note))
"signature invalid")
(when (blank? note) "note is empty")])]
(if (empty? errors)
(let [[year month day] (get-date)
untrimmed-line (filter #(or (= \- %) (Character/isLetterOrDigit %))
(-> note split-lines first (sreplace " " "-") lower-case))
trim (fn [s] (apply str (drop-while #(= \- %) s)))
title-uncut (-> untrimmed-line trim reverse trim reverse)
max-length (get-setting :max-title-length #(Integer/parseInt %) 80)
proposed-title (apply str (take max-length title-uncut))
date [year month day]
title (first (drop-while #(storage/note-exists? (build-key date %))
(cons proposed-title
(map #(str proposed-title "-" (+ 2 %)) (range)))))
noteID (build-key date title)]
(do
(storage/add-note noteID note password)
(storage/create-short-url noteID)
{
:noteID noteID
:longURL (getURL noteID)
:shortURL (getURL noteID :short)
:status (create-response true)
}))
{:status (create-response false (first errors))}))))
(defn update-note [noteID note pid signature password]
(let [errors (filter identity
(seq
[(when-not (storage/valid-publisher? pid) "pid invalid")
(when-not (= signature
(get-signature pid (storage/get-psk pid) noteID note password))
"signature invalid")
(when (blank? note) "note is empty")
(when-not (storage/valid-password? noteID password) "password invalid")]))]
(if (empty? errors)
(do
(storage/edit-note noteID note)
{
:longURL (getURL noteID)
:shortURL (getURL noteID :short)
:status (create-response true)
})
{:status (create-response false (first errors))})))

132
src/NoteHub/storage.clj

@ -11,83 +11,113 @@ @@ -11,83 +11,113 @@
(when-not (dev-mode?)
{:url (get-setting :db-url)})))
(defn get-current-date []
(str (java.util.Date.)))
; DB hierarchy levels
(def note "note")
(def published "published")
(def edited "edited")
(def views "views")
(def password "password")
(def sessions "sessions")
(def short-url "short-url")
(def publisher "publisher")
(defn valid-publisher? [pid]
(redis/hexists db publisher pid))
(defn register-publisher [pid]
"Returns nil if given PID exists or a PSK otherwise"
(when (not (valid-publisher? pid))
(let [psk (encrypt (str (rand-int Integer/MAX_VALUE) pid))
_ (redis/hset db publisher pid psk)]
psk)))
; Concatenates all fields to a string
(defn build-key
"Returns a storage-key for the given note coordinates"
[[year month day] title]
(print-str year month day title))
(defn revoke-publisher [pid]
(redis/hdel db publisher pid))
(defn get-psk [pid]
(redis/hget db publisher pid))
(defn create-session
"Creates a random session token"
[]
(let [token (encrypt (str (rand-int Integer/MAX_VALUE)))]
(do (redis/sadd db sessions token) token)))
(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))))
(do (redis/srem db sessions token)
was-valid))))
; TODO: deprecated
(defn update-note
"Updates a note with the given store key if the specified password is correct"
[key text passwd]
(let [stored-password (redis/hget db password key)]
[noteID text passwd]
(let [stored-password (redis/hget db password noteID)]
(when (and stored-password (= passwd stored-password))
(redis/hset db note key text))))
(defn set-note
"Creates a note with the given title and text in the given date namespace"
([date title text] (set-note date title text nil))
([date title text passwd]
(let [key (build-key date title)]
(do
(redis/hset db note key text)
(when (not (blank? passwd))
(redis/hset db password key passwd))))))
(redis/hset db edited noteID (get-current-date))
(redis/hset db note noteID text))))
(defn edit-note
[noteID text]
(do
(redis/hset db edited noteID (get-current-date))
(redis/hset db note noteID text)))
(defn add-note
([noteID text] (add-note noteID text nil))
([noteID text passwd]
(do
(redis/hset db note noteID text)
(redis/hset db published noteID (get-current-date))
(when (not (blank? passwd))
(redis/hset db password noteID passwd)))))
(defn valid-password? [noteID passwd]
(let [stored (redis/hget db password noteID)]
(and stored (= stored passwd))))
(defn get-note
"Gets the note from the given date namespaces for the specified title"
[date title]
(let [key (build-key date title)
text (redis/hget db note key)]
[noteID]
(let [text (redis/hget db note noteID)]
(when text
(do
(redis/hincrby db views key 1)
(redis/hincrby db views noteID 1)
text))))
(defn get-note-views
"Returns the number of views for the specified date and note title"
[date title]
(redis/hget db views (build-key date title)))
[noteID]
(redis/hget db views noteID))
(defn get-note-statistics
"Return views, publishing and editing timestamp"
[noteID]
{ :views (redis/hget db views noteID)
:published (redis/hget db published noteID)
:edited (redis/hget db edited noteID) })
(defn note-exists?
"Returns true if the note with the specified title and date exists"
[date title]
(redis/hexists db note (build-key date title)))
[noteID]
(redis/hexists db note noteID))
(defn delete-note
"Deletes the note with the specified coordinates"
[date title]
(let [key (build-key date title)]
(doseq [kw [password views note]]
; TODO: delete short url by looking for the title
(redis/hdel db kw key))))
[noteID]
(doseq [kw [password views note published edited]]
; TODO: delete short url by looking for the title
(redis/hdel db kw noteID)))
(defn short-url-exists?
"Checks whether the provided short url is taken (for testing only)"
[url]
(redis/hexists db short-url url))
(defn get-short-url [noteID]
(redis/hget db short-url noteID))
(defn resolve-url
"Resolves short url by providing all metadata of the request"
[url]
@ -97,19 +127,19 @@ @@ -97,19 +127,19 @@
(defn delete-short-url
"Deletes a short url (for testing only)"
[key]
(let [value (redis/hget db short-url key)]
[noteID]
(let [value (redis/hget db short-url noteID)]
(do
(redis/hdel db short-url key)
(redis/hdel db short-url noteID)
(redis/hdel db short-url value))))
(defn create-short-url
"Creates a short url for the given request metadata or extracts
"Creates a short url for the given request metadata or noteID or extracts
one if it was already created"
[metadata]
(let [request (str (into (sorted-map) metadata))]
(if (short-url-exists? request)
(redis/hget db short-url request)
[arg]
(let [key (if (map? arg) (str (into (sorted-map) arg)) arg)]
(if (short-url-exists? key)
(redis/hget db short-url key)
(let [hash-stream (partition 5 (repeatedly #(rand-int 36)))
hash-to-string (fn [hash]
(apply str
@ -120,8 +150,8 @@ @@ -120,8 +150,8 @@
(remove short-url-exists?
(map hash-to-string hash-stream)))]
(do
; we create two mappings: request params -> short url and back,
; we create two mappings: key params -> short url and back,
; s.t. we can later easily check whether a short url already exists
(redis/hset db short-url url request)
(redis/hset db short-url request url)
(redis/hset db short-url url key)
(redis/hset db short-url key url)
url)))))

53
src/NoteHub/views/pages.clj

@ -1,7 +1,9 @@ @@ -1,7 +1,9 @@
(ns NoteHub.views.pages
(:require [hiccup.util :as util])
(:require [hiccup.util :as util]
[NoteHub.api :as api]
[cheshire.core :refer :all])
(:use
[NoteHub.storage]
[NoteHub.storage] ; TODO: delete this
[NoteHub.settings]
[NoteHub.views.common]
[clojure.string :rename {replace sreplace}
@ -12,9 +14,7 @@ @@ -12,9 +14,7 @@
[hiccup.element]
[noir.response :only [redirect status content-type]]
[noir.core :only [defpage defpartial]]
[noir.statuses])
(:import
[java.util Calendar]))
[noir.statuses]))
(defn get-hash
"A simple hash-function, which computes a hash from the text field
@ -60,12 +60,6 @@ @@ -60,12 +60,6 @@
links (interpose separator links)]
[:div#panel (map identity links)]))))
(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
; ======
@ -102,9 +96,10 @@ @@ -102,9 +96,10 @@
; Update Note Page
(defpage "/:year/:month/:day/:title/edit" {:keys [year month day title]}
(input-form "/update-note" :update
(html (hidden-field :key (build-key [year month day] title)))
(get-note [year month day] title) :enter-passwd))
(let [noteID (api/build-key [year month day] title)]
(input-form "/update-note" :update
(html (hidden-field :key noteID))
(get-note noteID) :enter-passwd)))
; New Note Page
(defpage "/new" {}
@ -118,16 +113,16 @@ @@ -118,16 +113,16 @@
(wrap
(create-short-url params)
(select-keys params [:title :theme :header-font :text-font])
(get-note [year month day] title)))
(get-note (api/build-key [year month day] title))))
; Provides Markdown of the specified note
(defpage "/:year/:month/:day/:title/export" {:keys [year month day title]}
(when-let [md-text (get-note [year month day] title)]
(when-let [md-text (get-note (api/build-key [year month day] title))]
(content-type "text/plain; charset=utf-8" md-text)))
; Provides the number of views of the specified note
(defpage "/:year/:month/:day/:title/stats" {:keys [year month day title]}
(when-let [views (get-note-views [year month day] title)]
(when-let [views (get-note-views (api/build-key [year month day] title))]
(layout (get-message :statistics)
[:table#stats.helvetica.central-element
[:tr
@ -158,7 +153,7 @@ @@ -158,7 +153,7 @@
; 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;
; if not, append "-n", where "n" is the next free number
(let [[year month day] (get-date)
(let [[year month day] (api/get-date)
untrimmed-line (filter #(or (= \- %) (Character/isLetterOrDigit %))
(-> draft split-lines first (sreplace " " "-") lower-case))
trim (fn [s] (apply str (drop-while #(= \- %) s)))
@ -167,11 +162,11 @@ @@ -167,11 +162,11 @@
; TODO: replace to ccs/take when it gets fixed
proposed-title (apply str (take max-length title-uncut))
date [year month day]
title (first (drop-while #(note-exists? date %)
title (first (drop-while #(note-exists? (api/build-key date %))
(cons proposed-title
(map #(str proposed-title "-" (+ 2 %)) (range)))))]
(do
(set-note date title draft password)
(add-note (api/build-key date title) draft password)
(redirect (url year month day title))))
(response 400))))
@ -184,4 +179,22 @@ @@ -184,4 +179,22 @@
long-url (if (empty? rest-params) core-url (util/url core-url rest-params))]
(redirect long-url))))
; Here lives the API
(defpage "/api" args
(let [title (get-message :api-registration)]
(layout title [:article.markdown (slurp "API.md")])))
(defpage [:get "/api/note"] {:keys [version noteID]}
(generate-string (api/get-note noteID)))
(defpage [:post "/api/note"] {:keys [version note pid signature password]}
(generate-string (api/post-note note pid signature password)))
(defpage [:put "/api/note"] {:keys [version noteID note pid signature password]}
(generate-string (api/update-note noteID note pid signature password)))

153
test/NoteHub/test/api.clj

@ -0,0 +1,153 @@ @@ -0,0 +1,153 @@
(ns NoteHub.test.api
(:require
[cheshire.core :refer :all]
[NoteHub.storage :as storage])
(:use [NoteHub.api]
[noir.util.test]
[clojure.test]))
(def note "hello world! This is a _test_ note!")
(def pid "somePlugin")
(def pid2 "somePlugin2")
(def note-title (str (apply print-str (get-date)) " hello-world-this-is-a-test-note"))
(def note-url (str domain (apply str (interpose "/" (get-date))) "/hello-world-this-is-a-test-note"))
(defn substring? [a b] (not (= nil (re-matches (re-pattern (str "(?s).*" a ".*")) b))))
(defmacro isnt [arg] `(is (not ~arg)))
(defn register-publisher-fixture [f]
(def psk (storage/register-publisher pid))
(f)
(storage/revoke-publisher pid)
(storage/revoke-publisher pid2)
(storage/delete-note note-title))
(use-fixtures :each register-publisher-fixture)
(deftest api
(testing "API"
(testing "publisher registration"
(is (storage/valid-publisher? pid))
(isnt (storage/valid-publisher? pid2))
(let [psk2 (storage/register-publisher pid2)]
(is (= psk2 (storage/get-psk pid2)))
(is (storage/valid-publisher? pid2))
(is (storage/revoke-publisher pid2))
(isnt (storage/valid-publisher? "any_PID"))
(isnt (storage/valid-publisher? pid2))))
(testing "note publishing & retrieval"
(isnt (:success (:status (get-note "some note id"))))
(let [post-response (post-note note pid (get-signature pid psk note))
get-response (get-note (:noteID post-response))]
(is (= "note is empty" (:message (:status (post-note "" pid (get-signature pid psk ""))))))
(is (:success (:status post-response)))
(is (:success (:status get-response)))
(is (= note (:note get-response)))
(is (= (:longURL post-response) (:longURL get-response) note-url))
(is (= (:shortURL post-response) (:shortURL get-response)))
(is (= "1" (get-in get-response [:statistics :views])))
(isnt (get-in get-response [:statistics :edited]))
(is (= "2" (get-in (get-note (:noteID post-response)) [:statistics :views])))))
(testing "creation with wrong signature"
(let [response (post-note note pid (get-signature pid2 psk note))]
(isnt (:success (:status response)))
(is (= "signature invalid" (:message (:status response)))))
(let [response (post-note note pid (get-signature pid2 psk "any note"))]
(isnt (:success (:status response)))
(is (= "signature invalid" (:message (:status response)))))
(isnt (:success (:status (post-note note pid (get-signature pid "random_psk" note)))))
(is (:success (:status (post-note note pid (get-signature pid psk note)))))
(let [randomPID "randomPID"
psk2 (storage/register-publisher randomPID)
_ (storage/revoke-publisher randomPID)
response (post-note note randomPID (get-signature randomPID psk2 note))]
(isnt (:success (:status response)))
(is (= (:message (:status response)) "pid invalid"))))
(testing "note update"
(let [post-response (post-note note pid (get-signature pid psk note) "passwd")
note-id (:noteID post-response)
new-note "a new note!"]
(is (:success (:status post-response)))
(is (:success (:status (get-note note-id))))
(is (= note (:note (get-note note-id))))
(let [update-response (update-note note-id new-note pid (get-signature pid psk new-note) "passwd")]
(isnt (:success (:status update-response)))
(is (= "signature invalid" (:message (:status update-response)))))
(is (= note (:note (get-note note-id))))
(let [update-response (update-note note-id new-note pid
(get-signature pid psk note-id new-note "passwd") "passwd")]
(is (= { :success true } (:status update-response)))
(isnt (= nil (get-in (get-note note-id) [:statistics :edited])))
(is (= new-note (:note (get-note note-id)))))
(let [update-response (update-note note-id "aaa" pid
(get-signature pid psk note-id "aaa" "pass") "pass")]
(isnt (:success (:status update-response)))
(is (= "password invalid" (:message (:status update-response)))))
(is (= new-note (:note (get-note note-id))))
(is (= new-note (:note (get-note note-id))))))))
(deftest api-note-creation
(testing "Note creation"
(let [response (send-request [:post "/api/note"]
{:note note
:pid pid
:signature (get-signature pid psk note)
:version "1.0"})
body (parse-string (:body response))
noteID (body "noteID")]
(is (has-status response 200))
(is (get-in body ["status" "success"]))
(is (= note ((parse-string
(:body (send-request [:get "/api/note"] {:version "1.0" :noteID noteID}))) "note")))
(is (do
(storage/delete-note noteID)
(not (storage/note-exists? noteID)))))))
(deftest note-update
(let [response (send-request [:post "/api/note"]
{:note note
:pid pid
:signature (get-signature pid psk note)
:version "1.0"
:password "qwerty"})
body (parse-string (:body response))
noteID (body "noteID")]
(testing "Note update"
(is (has-status response 200))
(is (get-in body ["status" "success"]))
(is (storage/note-exists? noteID))
(is (substring? "_test_ note"
((parse-string
(:body (send-request [:get "/api/note"] {:version "1.0" :noteID noteID}))) "note")))
(let [response (send-request [:put "/api/note"]
{:noteID noteID
:note "WRONG pass"
:pid pid
:signature (get-signature pid psk noteID "WRONG pass" "qwerty1")
:password "qwerty1"
:version "1.0"})
body (parse-string (:body response))]
(is (has-status response 200))
(isnt (get-in body ["status" "success"]))
(is (= "password invalid" (get-in body ["status" "message"])))
(isnt (get-in body ["statistics" "edited"]))
(is (substring? "_test_ note"
((parse-string
(:body (send-request [:get "/api/note"] {:version "1.0" :noteID noteID}))) "note"))))
(is (get-in (parse-string
(:body (send-request [:put "/api/note"]
{:noteID noteID
:note "UPDATED CONTENT"
:pid pid
:signature (get-signature pid psk noteID "UPDATED CONTENT" "qwerty")
:password "qwerty"
:version "1.0"}))) ["status" "success"]))
(isnt (= nil (((parse-string
(:body (send-request [:get "/api/note"] {:version "1.0" :noteID noteID})))
"statistics") "edited")))
(is (substring? "UPDATED CONTENT"
((parse-string
(:body (send-request [:get "/api/note"] {:version "1.0" :noteID noteID}))) "note")))
(is (do
(storage/delete-note noteID)
(not (storage/note-exists? noteID)))))))

108
test/NoteHub/test/storage.clj

@ -1,5 +1,8 @@ @@ -1,5 +1,8 @@
(ns NoteHub.test.storage
(:use [NoteHub.storage] [clojure.test]))
(:use [NoteHub.storage]
[NoteHub.api :only [build-key]]
[NoteHub.views.pages]
[clojure.test]))
(def date [2012 06 03])
(def test-title "Some title.")
@ -13,54 +16,55 @@ @@ -13,54 +16,55 @@
(deftest storage
(testing "Storage"
(testing "of short-url mechanism"
(let [url (create-short-url metadata)
url2 (create-short-url metadata)]
(is (short-url-exists? url))
(is (= url url2))
(is (= metadata (resolve-url url)))
(is (not (do
(delete-short-url url)
(short-url-exists? url))))))
(testing "of correct note creation"
(is (= (do
(set-note date test-title test-note)
(get-note date test-title))
test-note))
(is (= "1" (get-note-views date test-title)))
(is (= (do
(get-note date test-title)
(get-note-views date test-title))
"2")))
(testing "of note update"
(is (= (do
(set-note date test-title test-note "12345qwert")
(get-note date test-title))
test-note))
(is (= (do
(update-note (build-key date test-title) "update" "12345qwert")
(get-note date test-title))
"update"))
(is (= (do
(update-note (build-key date test-title) "not authorized" "44444")
(get-note date test-title))
"update")))
(testing "of the note access"
(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"
(is (note-exists? date test-title))
(is (not (do
(delete-note date test-title)
(note-exists? date test-title))))
(is (not (note-exists? [2013 06 03] test-title)))
(is (not (note-exists? date "some title"))))))
(testing "Storage"
(testing "of short-url mechanism"
(let [url (create-short-url metadata)
url2 (create-short-url metadata)]
(is (short-url-exists? url))
(is (= url url2))
(is (= metadata (resolve-url url)))
(is (not (do
(delete-short-url url)
(short-url-exists? url))))))
(testing "of correct note creation"
(is (= (do
(add-note (build-key date test-title) test-note)
(get-note (build-key date test-title)))
test-note))
(is (= "1" (get-note-views (build-key date test-title))))
(is (= (do
(get-note (build-key date test-title))
(get-note-views (build-key date test-title)))
"2")))
(testing "of note update"
(is (= (do
(add-note (build-key date test-title) test-note "12345qwert")
(get-note (build-key date test-title)))
test-note))
(is (valid-password? (build-key date test-title) "12345qwert"))
(is (= (do
(update-note (build-key date test-title) "update" "12345qwert")
(get-note (build-key date test-title)))
"update"))
(is (= (do
(update-note (build-key date test-title) "not authorized" "44444")
(get-note (build-key date test-title)))
"update")))
(testing "of the note access"
(is (not= (get-note (build-key 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"
(is (note-exists? (build-key date test-title)))
(is (not (do
(delete-note (build-key date test-title))
(note-exists? (build-key date test-title)))))
(is (not (note-exists? (build-key [2013 06 03] test-title))))
(is (not (note-exists? (build-key date "some title")))))))

25
test/NoteHub/test/views/pages.clj

@ -1,21 +1,20 @@ @@ -1,21 +1,20 @@
(ns NoteHub.test.views.pages
(:use [NoteHub.views.pages]
[NoteHub.api :only [build-key get-date]]
[noir.util.test]
[NoteHub.views.common :only [url]]
[NoteHub.storage]
[clojure.test]))
(defn substring? [a b]
(not (= nil
(re-matches (re-pattern (str "(?s).*" a ".*")) b))))
(defn substring? [a b] (not (= nil (re-matches (re-pattern (str "(?s).*" a ".*")) b))))
(def date [2012 6 3])
(def test-title "some-title")
(def test-note "# This is a test note.\nHello _world_. Motörhead, тест.")
(defn create-testnote-fixture [f]
(set-note date test-title test-note)
(add-note (build-key date test-title) test-note)
(f)
(delete-note date test-title))
(delete-note (build-key date test-title)))
(use-fixtures :each create-testnote-fixture)
@ -23,8 +22,8 @@ @@ -23,8 +22,8 @@
(deftest testing-fixture
(testing "Was a not created?"
(is (= (get-note date test-title) test-note))
(is (note-exists? date test-title))))
(is (= (get-note (build-key date test-title)) test-note))
(is (note-exists? (build-key date test-title)))))
(deftest export-test
(testing "Markdown export"
@ -42,12 +41,12 @@ @@ -42,12 +41,12 @@
{:session-key session-key
:draft test-note
:session-value (str (get-hash (str test-note session-key)))}) 302))
(is (note-exists? date title))
(is (note-exists? (build-key date title)))
(is (substring? "Hello _world_"
((send-request (url year month day title)) :body)))
(is (do
(delete-note date title)
(not (note-exists? date title)))))))
(delete-note (build-key date title))
(not (note-exists? (build-key date title))))))))
(deftest note-update
(let [session-key (create-session)
@ -62,7 +61,7 @@ @@ -62,7 +61,7 @@
:draft "test note"
:password "qwerty"
:session-value (str (get-hash (str "test note" session-key)))}) 302))
(is (note-exists? date title))
(is (note-exists? (build-key date title)))
(is (substring? "test note"
((send-request (url year month day title)) :body)))
(is (has-status
@ -82,8 +81,8 @@ @@ -82,8 +81,8 @@
(is (substring? "UPDATED CONTENT"
((send-request (url year month day title)) :body)))
(is (do
(delete-note date title)
(not (note-exists? date title)))))))
(delete-note (build-key date title))
(not (note-exists? (build-key date title))))))))
(deftest requests
(testing "HTTP Status"

Loading…
Cancel
Save