Last active
April 26, 2016 00:09
-
-
Save avh4/7b732aa4ca5365d00157bdd80fa54841 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module FakeSet (Set, member, empty, union, remove, insert) where | |
type alias Set a = | |
List a | |
member : a -> Set a -> Bool | |
member expected set = | |
List.any ((==) expected) set | |
empty : Set a | |
empty = | |
[] | |
union : Set a -> Set a -> Set a | |
union = | |
(++) | |
remove : a -> Set a -> Set a | |
remove x = | |
List.filter ((/=) x) | |
insert : a -> Set a -> Set a | |
insert = | |
(::) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module SyncableTests (..) where | |
import ElmTest exposing (..) | |
import FakeSet as Set exposing (Set) | |
import Json.Decode as Json | |
import Json.Encode | |
import Http | |
import Syncable exposing (..) | |
import String | |
-- HTTP convenience | |
getRequest : String -> Http.Request | |
getRequest url = | |
{ verb = "GET" | |
, url = url | |
, headers = [] | |
, body = Http.empty | |
} | |
postRequest : String -> String -> Http.Request | |
postRequest url body = | |
{ verb = "POST" | |
, url = url | |
, headers = [] | |
, body = Http.string body | |
} | |
-- Testing | |
type alias TestContext model transaction = | |
{ state : SyncState model transaction | |
, effects : Set Http.Request | |
, errors : List String | |
} | |
startForTest : Syncable model transaction -> TestContext model transaction | |
startForTest syncable = | |
{ state = fst <| init syncable | |
, effects = snd <| init syncable | |
, errors = [] | |
} | |
assertHttpRequest : Http.Request -> TestContext model transaction -> Assertion | |
assertHttpRequest expected { effects } = | |
case Set.member expected effects of | |
True -> | |
pass | |
False -> | |
fail | |
("Expected an HTTP request to be made:" | |
++ "\n Expected: " | |
++ toString expected | |
++ "\n Actual: " | |
++ toString effects | |
) | |
stubResponse : | |
Http.Request | |
{ a : String | |
} | |
-> Result Http.Error String | |
-> TestContext model transaction | |
-> TestContext model transaction | |
stubResponse request response context = | |
case Set.member request context.effects of | |
True -> | |
{ context | |
| state = handleHttpResponse response context.state | |
, effects = | |
context.effects | |
|> Set.remove request | |
-- |> Set.union newEffects | |
} | |
False -> | |
{ context | errors = ("stubbed response was not made: " ++ toString request) :: context.errors } | |
currentModel : TestContext model transaction -> Result (List String) model | |
currentModel context = | |
case context.errors of | |
[] -> | |
Ok context.state.current | |
errors -> | |
Err errors | |
getSyncState : TestContext model transaction -> ExposedSyncState | |
getSyncState = | |
.state >> .syncState | |
applyUpdate : transaction -> TestContext model transaction -> TestContext model transaction | |
applyUpdate transaction context = | |
let | |
( newState, post ) = | |
handleLocalUpdate | |
transaction | |
context.state | |
in | |
{ context | |
| state = newState | |
, effects = Set.insert post context.effects | |
} | |
applyUpdates : List transaction -> TestContext model transaction -> TestContext model transaction | |
applyUpdates transactions context = | |
List.foldl applyUpdate context transactions | |
-- all' : Test | |
-- all' = | |
-- describe "Syncable" | |
-- (makeIt "" (++) Json.string Json.Encode.string "server") | |
-- [ describe "initial request" | |
-- (startForTest) | |
-- [ with "successful response" | |
-- ( stubResponse | |
-- (getRequest "server/") | |
-- (Ok """{"id":"f00", "transactions":["A", "B", "C"]}""") | |
-- ) | |
-- ] | |
-- ] | |
all : Test | |
all = | |
let | |
assertCurrentModel expected context = | |
case currentModel context of | |
Ok actual -> | |
assertEqual expected actual | |
Err errors -> | |
fail | |
<| String.join "\n " | |
<| [ "error occurred:", "ERRORS:" ] | |
++ errors | |
++ [ "PENDING EFFECTS:" ] | |
++ (List.map toString context.effects) | |
testApp = | |
makeIt "" (flip (++)) Json.string Json.Encode.string "server" | |
|> startForTest | |
afterInitialLoad = | |
testApp | |
|> stubResponse | |
(getRequest "server/") | |
(Ok """{"id":"f00", "transactions":[]}""") | |
in | |
suite | |
"Syncable" | |
[ testApp | |
|> applyUpdates [ "X", "Y" ] | |
|> assertCurrentModel "XY" | |
|> test "applies local transactions" | |
, testApp | |
|> stubResponse | |
(getRequest "server/") | |
(Ok """{}{"id":"f00", "transactions":["A", "B", "C"]}""") | |
(Ok """{"id":"f00", "transactions":["A", "B", "C"]}""") | |
|> assertCurrentModel "ABC" | |
|> test "applies initial transactions from the server" | |
, afterInitialLoad | |
|> applyUpdates [ "X" ] | |
|> assertHttpRequest (postRequest "server/txns" """{"base":"f00","transactions":["X"]}""") | |
|> test "sends local transactions to server" | |
, testApp | |
|> stubResponse | |
(getRequest "server/") | |
(Ok """{"id":"f00", "transactions":["A", "B", "C"]}""") | |
|> applyUpdates [ "X", "Y" ] | |
|> assertCurrentModel "ABCXY" | |
|> test "after initial load completes, applies local transactions" | |
, testApp | |
|> applyUpdates [ "X", "Y" ] | |
|> stubResponse | |
(getRequest "server/") | |
(Ok """{"id":"f00", "transactions":["A", "B", "C"]}""") | |
|> assertCurrentModel "ABCXY" | |
|> test "when initial load completes, previous local transactions are replayed" | |
, afterInitialLoad | |
|> applyUpdates [ "X" ] | |
|> stubResponse | |
(postRequest "server/txns" """{"base":"f00","transactions":["X"]}""") | |
(Ok """{"id":"f0x", "transactions":["X"]}""") | |
|> assertCurrentModel "X" | |
|> test "getting server confirmation of a local change" | |
, afterInitialLoad | |
|> applyUpdates [ "X", "Y", "Z" ] | |
|> stubResponse | |
(postRequest "server/txns" """{"base":"f00","transactions":["X"]}""") | |
(Ok """{"id":"f0x", "transactions":["X"]}""") | |
|> assertCurrentModel "XYZ" | |
|> test "getting server confirmation of a local change with more pending changes" | |
, afterInitialLoad | |
|> applyUpdates [ "W", "X", "Y", "Z" ] | |
|> stubResponse | |
(postRequest "server/txns" """{"base":"f00","transactions":["W","X"]}""") | |
(Ok """{"id":"f0w", "transactions":["W","X"]}""") | |
|> assertCurrentModel "WXYZ" | |
|> test "getting server confirmation for second of multiple transactions" | |
, afterInitialLoad | |
|> applyUpdates [ "W", "X", "Y", "Z" ] | |
|> stubResponse | |
(postRequest "server/txns" """{"base":"f00","transactions":["W"]}""") | |
(Ok """{"id":"f0w", "transactions":["W"]}""") | |
|> stubResponse | |
(postRequest "server/txns" """{"base":"f00","transactions":["W","X"]}""") | |
(Ok """{"id":"f0w", "transactions":["W","X"]}""") | |
|> assertCurrentModel "WXYZ" | |
|> test "getting server confirmation for multiple transactions" | |
, afterInitialLoad | |
|> applyUpdates [ "W", "X", "Y", "Z" ] | |
|> stubResponse | |
(postRequest "server/txns" """{"base":"f0x","transactions":["Y"]}""") | |
(Ok """{"id":"f0y", "transactions":["Y"]}""") | |
|> stubResponse | |
(postRequest "server/txns" """{"base":"f0y","transactions":["Z"]}""") | |
(Ok """{"id":"f0z", "transactions":["Z"]}""") | |
|> stubResponse | |
(postRequest "server/txns" """{"base":"f0w","transactions":["X"]}""") | |
(Ok """{"id":"f0x", "transactions":["X"]}""") | |
|> stubResponse | |
(postRequest "server/txns" """{"base":"f00","transactions":["W"]}""") | |
(Ok """{"id":"f0w", "transactions":["W"]}""") | |
|> assertCurrentModel "WXYZ" | |
|> test "getting server confirmation out of order" | |
, afterInitialLoad | |
|> applyUpdates [ "X", "X" ] | |
|> stubResponse | |
(postRequest "server/txns" """{"base":"f00","transactions":["X"]}""") | |
(Ok """{"id":"f0x", "transactions":["X"]}""") | |
|> assertCurrentModel "XX" | |
|> test "getting server confirmation for one of multiple equivalent transactions" | |
-- What is a real use case of having equivalent transactions that need to be distinct? Wouldn't a transaction value have either a generated id or a timestamp in a real data model? | |
, let | |
assertSyncState expected = | |
getSyncState >> assertEqual expected | |
in | |
suite | |
"sync state" | |
[ testApp | |
|> assertSyncState Connecting | |
|> test "initial state is connecting" | |
, testApp | |
|> stubResponse | |
(getRequest "server/") | |
(Ok """{"id":"f00", "transactions":[]}""") | |
|> assertSyncState Active | |
|> test "is active after successful initial load" | |
, testApp | |
|> stubResponse | |
(getRequest "server/") | |
(Err Http.NetworkError) | |
|> assertSyncState (HttpError Http.NetworkError) | |
|> test "when initial request fails (HTTP), shows not connected" | |
, testApp | |
|> stubResponse | |
(getRequest "server/") | |
(Ok "not json") | |
|> assertSyncState (JsonError "Unexpected token o") | |
|> test "when initial request fails (JSON), shows not connected" | |
] | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment