diff options
| author | adambrangenberg <adabran06@gmail.com> | 2025-09-14 11:52:21 +0200 |
|---|---|---|
| committer | adambrangenberg <adabran06@gmail.com> | 2025-09-14 11:52:21 +0200 |
| commit | f8b26f8699d391d1558d95ccd907133403cb2b73 (patch) | |
| tree | 72789f15fd5755e5a34b0344db29b6cf4942fc7d | |
login works, ig. Will remake everything now
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | CHANGELOG.md | 11 | ||||
| -rw-r--r-- | LICENSE | 26 | ||||
| -rw-r--r-- | README.md | 1 | ||||
| -rw-r--r-- | Setup.hs | 2 | ||||
| -rw-r--r-- | app/Main.hs | 6 | ||||
| -rw-r--r-- | jamaa.cabal | 91 | ||||
| -rw-r--r-- | package.yaml | 71 | ||||
| -rw-r--r-- | src/Lib.hs | 323 | ||||
| -rw-r--r-- | src/PostLoginsResponseLib.hs | 10 | ||||
| -rw-r--r-- | src/QueryUserLib.hs | 11 | ||||
| -rw-r--r-- | src/RenameUtils.hs | 19 | ||||
| -rw-r--r-- | stack.yaml | 67 | ||||
| -rw-r--r-- | stack.yaml.lock | 13 | ||||
| -rw-r--r-- | test/Spec.hs | 19 |
15 files changed, 671 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8ee1bf9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.stack-work diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a951ca8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog for `jamaa` + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to the +[Haskell Package Versioning Policy](https://pvp.haskell.org/). + +## Unreleased + +## 0.1.0.0 - YYYY-MM-DD @@ -0,0 +1,26 @@ +Copyright 2025 Adam Brangenberg + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..dc0259e --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# jamaa diff --git a/Setup.hs b/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/app/Main.hs b/app/Main.hs new file mode 100644 index 0000000..570484b --- /dev/null +++ b/app/Main.hs @@ -0,0 +1,6 @@ +module Main (main) where + +import Lib + +main :: IO () +main = startApp diff --git a/jamaa.cabal b/jamaa.cabal new file mode 100644 index 0000000..383cccd --- /dev/null +++ b/jamaa.cabal @@ -0,0 +1,91 @@ +cabal-version: 2.2 + +-- This file has been generated from package.yaml by hpack version 0.37.0. +-- +-- see: https://github.com/sol/hpack + +name: jamaa +version: 0.1.0.0 +description: Please see the README on GitHub at <https://github.com/adambrangenberg/jamaa#readme> +homepage: https://github.com/adambrangenberg/jamaa#readme +bug-reports: https://github.com/adambrangenberg/jamaa/issues +author: Adam Brangenberg +maintainer: adam@adamwv.de +copyright: Adam Brangenberg +license: BSD-3-Clause +license-file: LICENSE +build-type: Simple +extra-source-files: + README.md + CHANGELOG.md + +source-repository head + type: git + location: https://github.com/adambrangenberg/jamaa + +library + exposed-modules: + Lib + PostLoginsResponseLib + QueryUserLib + RenameUtils + other-modules: + Paths_jamaa + autogen-modules: + Paths_jamaa + hs-source-dirs: + src + ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints + build-depends: + MissingH + , aeson + , base >=4.7 && <5 + , servant-server + , wai + , wai-extra + , warp + default-language: Haskell2010 + +executable jamaa-exe + main-is: Main.hs + other-modules: + Paths_jamaa + autogen-modules: + Paths_jamaa + hs-source-dirs: + app + ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N + build-depends: + MissingH + , aeson + , base + , jamaa + , servant-server + , wai + , wai-extra + , warp + default-language: Haskell2010 + +test-suite jamaa-test + type: exitcode-stdio-1.0 + main-is: Spec.hs + other-modules: + Paths_jamaa + autogen-modules: + Paths_jamaa + hs-source-dirs: + test + ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N + build-depends: + MissingH + , aeson + , base + , hspec + , hspec-wai + , hspec-wai-json + , jamaa + , servant-server + , wai + , wai-extra + , warp + default-language: Haskell2010 diff --git a/package.yaml b/package.yaml new file mode 100644 index 0000000..6507574 --- /dev/null +++ b/package.yaml @@ -0,0 +1,71 @@ +name: jamaa +version: 0.1.0.0 +github: "adambrangenberg/jamaa" +license: BSD-3-Clause +author: "Adam Brangenberg" +maintainer: "adam@adamwv.de" +copyright: "Adam Brangenberg" + +extra-source-files: + - README.md + - CHANGELOG.md + +# Metadata used when publishing your package +# synopsis: Short description of your package +# category: Web + +# To avoid duplicated efforts in documentation and dealing with the +# complications of embedding Haddock markup inside cabal files, it is +# common to point users to the README.md file. +description: Please see the README on GitHub at <https://github.com/adambrangenberg/jamaa#readme> + +dependencies: + - base >= 4.7 && < 5 + - aeson + - servant-server + - wai + - warp + - wai-extra + - MissingH + +ghc-options: + - -Wall + - -Wcompat + - -Widentities + - -Wincomplete-record-updates + - -Wincomplete-uni-patterns + - -Wmissing-export-lists + - -Wmissing-home-modules + - -Wpartial-fields + - -Wredundant-constraints + +library: + source-dirs: src + +executables: + jamaa-exe: + main: Main.hs + source-dirs: app + ghc-options: + - -threaded + - -rtsopts + - -with-rtsopts=-N + dependencies: + - base + - jamaa + +tests: + jamaa-test: + main: Spec.hs + source-dirs: test + ghc-options: + - -threaded + - -rtsopts + - -with-rtsopts=-N + dependencies: + - base + - jamaa + - hspec + - hspec-wai + - hspec-wai-json + - aeson diff --git a/src/Lib.hs b/src/Lib.hs new file mode 100644 index 0000000..ed1d92d --- /dev/null +++ b/src/Lib.hs @@ -0,0 +1,323 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE OverloadedLabels #-} + +module Lib + ( startApp + , app + , type (:>) -- Syntax for importing type operator + , type (:<|>) + ) where + +import Data.Aeson +import Data.Aeson.TH +import Network.Wai +import Network.Wai.Handler.Warp +import Network.Wai.Middleware.RequestLogger (logStdoutDev) -- ⚠️ This is the key import! +import Servant +import RenameUtils (typeFieldModifier, dotFieldModifier, replaceUsername, replaceRoomId) +import QueryUserLib +import PostLoginsResponseLib + +type JURI = String + +data EmptyObj = EmptyObj {} deriving (Eq, Show) + +$(deriveJSON defaultOptions ''EmptyObj) + +data UserInfo = UserInfo + { displayname :: String + , avatar_url :: String + } deriving (Eq, Show) + +$(deriveJSON defaultOptions ''UserInfo) + +data OAuth2InfoHolder = OAuth2InfoHolder + { issuer :: String + , account :: String + } deriving (Eq, Show) + +data BaseURLHolder = BaseURLHolder + { base_url :: JURI + } deriving (Eq, Show) + +data WellKnown = WellKnown + { m__homeserver :: BaseURLHolder + , m__identity_server :: BaseURLHolder + , org__matrix__msc2965__authentication :: OAuth2InfoHolder + } deriving (Eq, Show) + +$(deriveJSON defaultOptions ''BaseURLHolder) +$(deriveJSON defaultOptions ''OAuth2InfoHolder) +$(deriveJSON defaultOptions { fieldLabelModifier = dotFieldModifier } ''WellKnown) + +data Versions = Versions + { versions :: [String] + } deriving (Eq, Show) + +$(deriveJSON defaultOptions ''Versions) + +data LoginFlow = LoginFlow + { type__ :: String + } deriving (Eq, Show) + +data GetLoginsResponse = GetLoginsResponse + { flows :: [LoginFlow] + } deriving (Eq, Show) + +$(deriveJSON defaultOptions {fieldLabelModifier = typeFieldModifier} ''LoginFlow) +$(deriveJSON defaultOptions ''GetLoginsResponse) + +$(deriveJSON defaultOptions ''PostLoginsResponse) + +type UserId = String + +data RegisterResponse = RegisterResponse + { user_id :: UserId + } deriving (Eq, Show) + +$(deriveJSON defaultOptions ''RegisterResponse) + +data MASAviableable = MASAviableable + { available :: Bool + } deriving (Eq, Show) + +$(deriveJSON defaultOptions ''MASAviableable) + +data Synced = Synced + { synced :: Bool + } deriving (Eq, Show) + +$(deriveJSON defaultOptions ''Synced) + +$(deriveJSON defaultOptions ''QueryUser) + +data Rooms = Rooms + { join :: JoinedRooms + } deriving (Eq, Show) + +data JoinedRooms = JoinedRooms + {slay :: JoinedRoom + } deriving (Eq, Show) + +data JoinedRoom = JoinedRoom + { timeline :: Timeline + } deriving (Eq, Show) + +data Timeline = Timeline + { events :: [Message] + } deriving (Eq, Show) + +data Message = Message + { content :: TextMessageContent + , event_id :: String + , origin_server_ts :: String + , sender :: String + , type___ :: String + , state_key :: String + } deriving (Eq, Show) + +data TextMessageContent = TextMessageContent + { body :: String + , msgtype :: String + , creator :: String + , membership :: String + , join_rule :: String + } deriving (Eq, Show) + +data Sync = Sync + { next_batch :: String + , rooms :: Rooms + } deriving (Eq, Show) + +$(deriveJSON defaultOptions ''TextMessageContent) +$(deriveJSON defaultOptions { fieldLabelModifier = typeFieldModifier} ''Message) +$(deriveJSON defaultOptions ''Timeline) +$(deriveJSON defaultOptions ''JoinedRoom) +$(deriveJSON defaultOptions { fieldLabelModifier = replaceRoomId } ''JoinedRooms) +$(deriveJSON defaultOptions ''Rooms) +$(deriveJSON defaultOptions ''Sync) + +data Keys = Keys + { one_time_key_counts :: EmptyObj + } deriving (Eq, Show) + +$(deriveJSON defaultOptions ''Keys) + +data KeysHolder = KeysHolder + { username :: [String] + } deriving (Eq, Show) + +data KeysQuery = KeysQuery + { device_keys :: KeysHolder + , master_keys :: EmptyObj + , self_signing_keys :: EmptyObj + , user_signing_keys :: EmptyObj + } deriving (Eq, Show) + +$(deriveJSON defaultOptions { fieldLabelModifier = replaceUsername } ''KeysHolder) +$(deriveJSON defaultOptions ''KeysQuery) + +data RoomId = RoomId + {room_id :: String + } deriving (Eq, Show) + +$(deriveJSON defaultOptions ''RoomId) + +data GetRooms = GetRooms + { chunk :: [Message] + , end :: String + , start :: String + } deriving (Eq, Show) + +$(deriveJSON defaultOptions ''GetRooms) + +type API = ".well-known" :> "matrix" :> "client" :> Get '[JSON] WellKnown + :<|> "_matrix" :> "client" :> "versions" :> Get '[JSON] Versions + :<|> "_matrix" :> "client" :> "v3" :> "login" :> Get '[JSON] GetLoginsResponse + :<|> "_matrix" :> "client" :> "r0" :> "login" :> Get '[JSON] GetLoginsResponse + :<|> "_matrix" :> "client" :> "v3" :> "login" :> Post '[JSON] PostLoginsResponse + :<|> "_matrix" :> "client" :> "r0" :> "login" :> Post '[JSON] PostLoginsResponse + :<|> "_matrix" :> "client" :> "v3" :> "register" :> Post '[JSON] RegisterResponse + :<|> "_synapse" :> "mas" :> "is_localpart_available" :> Get '[JSON] MASAviableable + :<|> "_synapse" :> "mas" :> "provision_user" :> Post '[JSON] RegisterResponse + :<|> "_synapse" :> "mas" :> "sync_devices" :> Post '[JSON] Synced + :<|> "_synapse" :> "mas" :> "query_user" :> Get '[JSON] QueryUser + :<|> "_matrix" :> "client" :> "v3" :> "profile" :> "@username:localhost:8080" :> Get '[JSON] UserInfo + :<|> "_matrix" :> "client" :> "r0" :> "profile" :> "@username:localhost:8080" :> Get '[JSON] UserInfo + :<|> "_matrix" :> "client" :> "v3" :> "sync" :> Get '[JSON] Sync + :<|> "_matrix" :> "client" :> "r0" :> "sync" :> Get '[JSON] Sync + :<|> "_matrix" :> "client" :> "v3" :> "keys" :> "query" :> Post '[JSON] Keys + :<|> "_matrix" :> "client" :> "r0" :> "keys" :> "query" :> Post '[JSON] Keys + :<|> "_matrix" :> "client" :> "v3" :> "keys" :> "upload" :> Post '[JSON] KeysQuery + :<|> "_matrix" :> "client" :> "r0" :> "keys" :> "upload" :> Post '[JSON] KeysQuery + :<|> "_matrix" :> "client" :> "v3" :> "createRoom" :> Post '[JSON] RoomId + :<|> "_matrix" :> "client" :> "r0" :> "createRoom" :> Post '[JSON] RoomId + :<|> "_matrix" :> "client" :> "v3" :> "rooms" :> "!slay:localhost:8080" :> "messages" :> Get '[JSON] GetRooms + :<|> "_matrix" :> "client" :> "r0" :> "rooms" :> "!slay:localhost:8080" :> "messages" :> Get '[JSON] GetRooms + :<|> "_matrix" :> "client" :> "v3" :> "rooms" :> "!slay:localhost:8080" :> "members" :> Get '[JSON] GetRooms + :<|> "_matrix" :> "client" :> "r0" :> "rooms" :> "!slay:localhost:8080" :> "members" :> Get '[JSON] GetRooms + +startApp :: IO () +startApp = run 8080 (logStdoutDev app) + +app :: Application +app = serve api server + +api :: Proxy API +api = Proxy + +server :: Server API +server = wellKnownSuccess + :<|> versionsSuccess + :<|> getLoginSuccess + :<|> getLoginSuccess + :<|> postLoginSuccess + :<|> postLoginSuccess + :<|> registerResponse + :<|> masLocalpartAviableable + :<|> masProvision + :<|> masSynced + :<|> masQueryUser + :<|> getProfile + :<|> getProfile + :<|> sync + :<|> sync + :<|> keys + :<|> keys + :<|> keysQuery + :<|> keysQuery + :<|> createRoom + :<|> createRoom + :<|> getRoomContent + :<|> getRoomContent + :<|> getRoomMembers + :<|> getRoomMembers + +wellKnownSuccess :: Handler WellKnown +wellKnownSuccess = return (WellKnown + (BaseURLHolder "http://localhost:8080") + (BaseURLHolder "https://vector.im") + (OAuth2InfoHolder "http://localhost:8000/" "http://localhost:8000/account/") + ) + +versionsSuccess :: Handler Versions +versionsSuccess = return (Versions ["1.10"]) + +getLoginSuccess :: Handler GetLoginsResponse +getLoginSuccess = return (GetLoginsResponse [LoginFlow "m.login.sso"]) + +postLoginSuccess :: Handler PostLoginsResponse +postLoginSuccess = return (makePostLoginsResponse "token" "tammy" "@username:localhost:8080") + +registerResponse :: Handler RegisterResponse +registerResponse = return (RegisterResponse "@username:localhost:8080") + +masLocalpartAviableable :: Handler MASAviableable +masLocalpartAviableable = return (MASAviableable True) + +masProvision :: Handler RegisterResponse +masProvision = return (RegisterResponse "@username:localhost:8080") + +masSynced :: Handler Synced +masSynced = return (Synced True) + +masQueryUser :: Handler QueryUser +masQueryUser = return (makeQueryUser "username" "@username:localhost:8080" "username" False) + +getProfile :: Handler UserInfo +getProfile = return (UserInfo "username" "mxc://matrix.org/SDGdghriugerRg") + +getMessageContentTempl :: String -> String -> String -> String -> Message +getMessageContentTempl msgtype eventId time state_key = (Message + ( + TextMessageContent + "Hiii" + "m.text" + "@username:localhost:8000" + "join" + "public" + ) + eventId + time + "@username:localhost:8000" + msgtype + state_key + ) + +messages :: [Message] +messages = [ + getMessageContentTempl "m.room.create" "$make" "1757771812" "", + -- getMessageContentTempl "m.room.join_rules", + getMessageContentTempl "m.room.member" "$join" "1757771814" "@username:localhost:8000", + getMessageContentTempl "m.room.message" "$msg" "1757771816" "" + ] + +sync :: Handler Sync +sync = return (Sync + "a" + (Rooms ( + JoinedRooms ( + JoinedRoom ( + Timeline messages + ) + ) + )) + ) + + +keys :: Handler Keys +keys = return (Keys EmptyObj) + +keysQuery :: Handler KeysQuery +keysQuery = return (KeysQuery (KeysHolder []) EmptyObj EmptyObj EmptyObj) + +createRoom :: Handler RoomId +createRoom = return (RoomId "!slay:localhost:8080") + +getRoomContent :: Handler GetRooms +getRoomContent = return (GetRooms messages "a" "a") + +getRoomMembers :: Handler GetRooms +getRoomMembers = return (GetRooms [getMessageContentTempl "m.room.member" "$join" "1757771814" "@username:localhost:8080"] "a" "a") diff --git a/src/PostLoginsResponseLib.hs b/src/PostLoginsResponseLib.hs new file mode 100644 index 0000000..51617b1 --- /dev/null +++ b/src/PostLoginsResponseLib.hs @@ -0,0 +1,10 @@ +module PostLoginsResponseLib (PostLoginsResponse, makePostLoginsResponse) where + +data PostLoginsResponse = PostLoginsResponse + { access_token :: String + , device_id :: String + , user_id :: String + } deriving (Eq, Show) + +makePostLoginsResponse :: String -> String -> String -> PostLoginsResponse +makePostLoginsResponse = PostLoginsResponse diff --git a/src/QueryUserLib.hs b/src/QueryUserLib.hs new file mode 100644 index 0000000..5677ac5 --- /dev/null +++ b/src/QueryUserLib.hs @@ -0,0 +1,11 @@ +module QueryUserLib (QueryUser, makeQueryUser) where + +data QueryUser = QueryUser + { localpart :: String + , user_id :: String + , display_name :: String + , is_guest :: Bool + } deriving (Eq, Show) + +makeQueryUser :: String -> String -> String -> Bool -> QueryUser +makeQueryUser = QueryUser diff --git a/src/RenameUtils.hs b/src/RenameUtils.hs new file mode 100644 index 0000000..cdf36d2 --- /dev/null +++ b/src/RenameUtils.hs @@ -0,0 +1,19 @@ +module RenameUtils where + +import Data.List.Utils (startswith, replace) + +typeFieldModifier :: String -> String +typeFieldModifier "type__" = "type" +typeFieldModifier "type___" = "type" +typeFieldModifier name = name + +dotFieldModifier :: String -> String +dotFieldModifier = replace "__" "." + +replaceUsername :: String -> String +replaceUsername "username" = "@username:localhost:8080" +replaceUsername name = name + +replaceRoomId :: String -> String +replaceRoomId "slay" = "!slay:localhost:8080" +replaceRoomId name = name diff --git a/stack.yaml b/stack.yaml new file mode 100644 index 0000000..7b821dd --- /dev/null +++ b/stack.yaml @@ -0,0 +1,67 @@ +# This file was automatically generated by 'stack init' +# +# Some commonly used options have been documented as comments in this file. +# For advanced use and comprehensive documentation of the format, please see: +# https://docs.haskellstack.org/en/stable/yaml_configuration/ + +# A 'specific' Stackage snapshot or a compiler version. +# A snapshot resolver dictates the compiler version and the set of packages +# to be used for project dependencies. For example: +# +# snapshot: lts-22.28 +# snapshot: nightly-2024-07-05 +# snapshot: ghc-9.6.6 +# +# The location of a snapshot can be provided as a file or url. Stack assumes +# a snapshot provided as a file might change, whereas a url resource does not. +# +# snapshot: ./custom-snapshot.yaml +# snapshot: https://example.com/snapshots/2024-01-01.yaml +snapshot: + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/24/9.yaml + +# User packages to be built. +# Various formats can be used as shown in the example below. +# +# packages: +# - some-directory +# - https://example.com/foo/bar/baz-0.0.2.tar.gz +# subdirs: +# - auto-update +# - wai +packages: +- . +# Dependency packages to be pulled from upstream that are not in the snapshot. +# These entries can reference officially published versions as well as +# forks / in-progress versions pinned to a git hash. For example: +# +# extra-deps: +# - acme-missiles-0.3 +# - git: https://github.com/commercialhaskell/stack.git +# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a +# +# extra-deps: [] + +# Override default flag values for project packages and extra-deps +# flags: {} + +# Extra package databases containing global packages +# extra-package-dbs: [] + +# Control whether we use the GHC we find on the path +# system-ghc: true +# +# Require a specific version of Stack, using version ranges +# require-stack-version: -any # Default +# require-stack-version: ">=3.1" +# +# Override the architecture used by Stack, especially useful on Windows +# arch: i386 +# arch: x86_64 +# +# Extra directories used by Stack for building +# extra-include-dirs: [/path/to/dir] +# extra-lib-dirs: [/path/to/dir] +# +# Allow a newer minor version of GHC than the snapshot specifies +# compiler-check: newer-minor diff --git a/stack.yaml.lock b/stack.yaml.lock new file mode 100644 index 0000000..1a24f91 --- /dev/null +++ b/stack.yaml.lock @@ -0,0 +1,13 @@ +# This file was autogenerated by Stack. +# You should not edit this file by hand. +# For more information, please see the documentation at: +# https://docs.haskellstack.org/en/stable/lock_files + +packages: [] +snapshots: +- completed: + sha256: 188228e10dbb5b533bae584049b112e72000902e64b17348679a69f92fbc0d32 + size: 726076 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/24/9.yaml + original: + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/24/9.yaml diff --git a/test/Spec.hs b/test/Spec.hs new file mode 100644 index 0000000..1065caf --- /dev/null +++ b/test/Spec.hs @@ -0,0 +1,19 @@ +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE OverloadedStrings #-} +module Main (main) where + +import Lib (app) +import Test.Hspec +import Test.Hspec.Wai + +main :: IO () +main = hspec spec + +spec :: Spec +spec = with (return app) $ do + describe "GET /users" $ do + it "responds with 200" $ do + get "/users" `shouldRespondWith` 200 + it "responds with [User]" $ do + let users = "[{\"userId\":1,\"userFirstName\":\"Isaac\",\"userLastName\":\"Newton\"},{\"userId\":2,\"userFirstName\":\"Albert\",\"userLastName\":\"Einstein\"}]" + get "/users" `shouldRespondWith` users |