Skip to content

Commit 67a8fec

Browse files
committed
Merge larger test matrix & tests for auth and MongoDB Atlas
2 parents eb242e9 + 8872004 commit 67a8fec

File tree

7 files changed

+140
-36
lines changed

7 files changed

+140
-36
lines changed

.github/workflows/test.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: Test
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- master
8+
9+
concurrency:
10+
group: ${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
test:
15+
runs-on: ubuntu-latest
16+
strategy:
17+
fail-fast: false
18+
matrix:
19+
mongodb:
20+
- mongo:4.0
21+
- mongo:5.0
22+
- mongo:6.0
23+
- mongo:7.0
24+
- mongo_atlas
25+
ghc:
26+
- "8.10.4"
27+
- "9.4.7" # oldest version with HLS support
28+
- latest
29+
steps:
30+
- uses: actions/checkout@v3
31+
32+
- name: Setup Haskell tooling
33+
uses: haskell-actions/setup@v2
34+
with:
35+
enable-stack: true
36+
ghc-version: ${{ matrix.ghc }}
37+
stack-version: latest
38+
39+
- name: Setup container and run tests
40+
run: |
41+
# the job-level 'if' expression is evaluated before the matrix variable
42+
# so it cannot be used to configure this step
43+
if [[ ${{ matrix.mongodb }} = "mongo_atlas" ]]
44+
then
45+
export CONNECTION_STRING=${{ secrets.CONNECTION_STRING }}
46+
else
47+
docker run -d \
48+
-p 27017:27017 \
49+
-e MONGO_INITDB_ROOT_USERNAME=testadmin \
50+
-e MONGO_INITDB_ROOT_PASSWORD=123 \
51+
${{ matrix.mongodb }}
52+
fi
53+
# build & run tests
54+
export MONGO_VERSION=${{ matrix.mongodb }}
55+
stack test --fast

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ cabal.sandbox.config
33
.cabal-sandbox/
44
.stack-work/
55
dist-newstyle/*
6-
!dist-newstyle/config
6+
!dist-newstyle/config
7+
*.nix
8+
.vscode/*

stack.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@
1717
#
1818
# resolver: ./custom-snapshot.yaml
1919
# resolver: https://example.com/snapshots/2018-01-01.yaml
20-
resolver:
21-
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/21.yaml
22-
20+
resolver: lts-21.25 # for HLS support
2321
# User packages to be built.
2422
# Various formats can be used as shown in the example below.
2523
#

stack.yaml.lock

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
packages: []
77
snapshots:
88
- completed:
9-
size: 586110
10-
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/21.yaml
11-
sha256: ce4fb8d44f3c6c6032060a02e0ebb1bd29937c9a70101c1517b92a87d9515160
12-
original:
13-
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/21.yaml
9+
sha256: a81fb3877c4f9031e1325eb3935122e608d80715dc16b586eb11ddbff8671ecd
10+
size: 640086
11+
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/25.yaml
12+
original: lts-21.25

test/Main.hs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
module Main where
22

3-
import Database.MongoDB.Admin (serverVersion)
4-
import Database.MongoDB.Connection (connect, host)
5-
import Database.MongoDB.Query (access, slaveOk)
6-
import Data.Text (unpack)
3+
import Control.Exception (assert)
4+
import Control.Monad (when)
5+
import Data.Maybe (isJust)
6+
import qualified Spec
7+
import System.Environment (getEnv, lookupEnv)
78
import Test.Hspec.Runner
8-
import System.Environment (getEnv)
9-
import System.IO.Error (catchIOError)
109
import TestImport
11-
import qualified Spec
1210

1311
main :: IO ()
1412
main = do
15-
mongodbHost <- getEnv mongodbHostEnvVariable `catchIOError` (\_ -> return "localhost")
16-
p <- connect $ host mongodbHost
17-
version <- access p slaveOk "admin" serverVersion
18-
putStrLn $ "Running tests with mongodb version: " ++ (unpack version)
13+
version <- getEnv "MONGO_VERSION"
14+
when (version == "mongo_atlas") $ do
15+
connection_string <- lookupEnv "CONNECTION_STRING"
16+
pure $ assert (isJust connection_string) ()
1917
hspecWith defaultConfig Spec.spec

test/QuerySpec.hs

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import TestImport
77
import Control.Concurrent (threadDelay)
88
import Control.Exception
99
import Control.Monad (forM_, when)
10-
import System.Environment (getEnv)
10+
import System.Environment (getEnv, lookupEnv)
1111
import System.IO.Error (catchIOError)
1212
import qualified Data.List as L
1313

@@ -17,12 +17,26 @@ testDBName :: Database
1717
testDBName = "mongodb-haskell-test"
1818

1919
db :: Action IO a -> IO a
20-
db action = do
20+
db action = bracket start end inbetween
21+
where
22+
start = lookupEnv "CONNECTION_STRING" >>= getPipe
23+
end (_, pipe) = close pipe
24+
inbetween (testuser, pipe) = do
25+
logged_in <-
26+
access pipe master "admin" $ do
27+
auth (u_name testuser) (u_passwd testuser)
28+
assert logged_in $ pure ()
29+
access pipe master testDBName action
30+
getPipe Nothing = do
31+
let user = TestUser "testadmin" "123"
2132
mongodbHost <- getEnv mongodbHostEnvVariable `catchIOError` (\_ -> return "localhost")
2233
pipe <- connect (host mongodbHost)
23-
result <- access pipe master testDBName action
24-
close pipe
25-
return result
34+
pure (user, pipe)
35+
getPipe (Just cs) = do
36+
let creds = extractMongoAtlasCredentials . T.pack $ cs
37+
user = TestUser "testadmin" (atlas_password creds)
38+
pipe <- connectAtlas creds
39+
pure (user, pipe)
2640

2741
getWireVersion :: IO Int
2842
getWireVersion = db $ do
@@ -68,6 +82,8 @@ fineGrainedBigDocument = (flip map) [1..1000] $ \i -> (fromString $ "team" ++ (s
6882
hugeDocument :: Document
6983
hugeDocument = (flip map) [1..1000000] $ \i -> (fromString $ "team" ++ (show i)) =: ("team " ++ (show i) ++ " name")
7084

85+
data TestUser = TestUser {u_name :: T.Text, u_passwd :: T.Text}
86+
7187
spec :: Spec
7288
spec = around withCleanDatabase $ do
7389
describe "useDb" $ do
@@ -78,11 +94,13 @@ spec = around withCleanDatabase $ do
7894

7995
describe "collectionWithDot" $ do
8096
it "uses a collection with dots in the name" $ do
81-
let coll = "collection.with.dot"
82-
_id <- db $ insert coll ["name" =: "jack", "color" =: "blue"]
83-
Just doc <- db $ findOne (select ["name" =: "jack"] coll)
84-
doc !? "color" `shouldBe` (Just "blue")
85-
97+
-- Dots in collection names are disallowed from Mongo 6 on
98+
mongo_version <- getEnv "MONGO_VERSION"
99+
when (mongo_version `elem` ["mongo:5.0", "mongo:4.0"]) $ do
100+
let collec = "collection.with.dot"
101+
_id <- db $ insert collec ["name" =: "jack", "color" =: "blue"]
102+
Just doc <- db $ findOne (select ["name" =: "jack"] collec)
103+
doc !? "color" `shouldBe` Just "blue"
86104

87105
describe "insert" $ do
88106
it "inserts a document to the collection and returns its _id" $ do
@@ -497,4 +515,3 @@ spec = around withCleanDatabase $ do
497515
, sort = [ "_id" =: 1 ]
498516
}
499517
result `shouldBe` [["_id" =: "jane"], ["_id" =: "jill"], ["_id" =: "joe"]]
500-

test/TestImport.hs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
{-# LANGUAGE CPP #-}
2+
{-# LANGUAGE LambdaCase #-}
23

34
module TestImport (
45
module TestImport,
5-
module Export
6+
module Export,
67
) where
78

8-
import Test.Hspec as Export hiding (Selector)
9-
import Database.MongoDB as Export
10-
import Control.Monad.Trans as Export (MonadIO, liftIO)
9+
import Control.Exception (SomeException (SomeException), try)
10+
import Control.Monad.Trans as Export (MonadIO, liftIO)
11+
import qualified Data.Text as T
1112
import Data.Time (ParseTime, UTCTime)
1213
import qualified Data.Time as Time
14+
import Database.MongoDB as Export
15+
import Test.Hspec as Export hiding (Selector)
1316

1417
-- We support the old version of time because it's easier than trying to use
1518
-- only the new version and test older GHC versions.
@@ -20,7 +23,7 @@ import System.Locale (defaultTimeLocale, iso8601DateFormat)
2023
import Data.Maybe (fromJust)
2124
#endif
2225

23-
parseTime :: ParseTime t => String -> String -> t
26+
parseTime :: (ParseTime t) => String -> String -> t
2427
#if MIN_VERSION_time(1,5,0)
2528
parseTime = Time.parseTimeOrError True defaultTimeLocale
2629
#else
@@ -35,3 +38,35 @@ parseDateTime = parseTime (iso8601DateFormat (Just "%H:%M:%S"))
3538

3639
mongodbHostEnvVariable :: String
3740
mongodbHostEnvVariable = "HASKELL_MONGODB_TEST_HOST"
41+
42+
data MongoAtlas = MongoAtlas
43+
{ atlas_host :: T.Text
44+
, atlas_user :: T.Text
45+
, atlas_password :: T.Text
46+
}
47+
48+
extractMongoAtlasCredentials :: T.Text -> MongoAtlas
49+
extractMongoAtlasCredentials cs =
50+
let s = T.drop 14 cs
51+
[u, s'] = T.splitOn ":" s
52+
[p, h] = T.splitOn "@" s'
53+
in MongoAtlas h u p
54+
55+
connectAtlas :: MongoAtlas -> IO Pipe
56+
connectAtlas (MongoAtlas h _ _) = do
57+
repset <- openReplicaSetSRV' $ T.unpack h
58+
primaryOrSecondary repset >>= \case
59+
Just pipe -> pure pipe
60+
Nothing -> ioError $ error "Unable to acquire pipe from MongoDB Atlas' replicaset"
61+
where
62+
primaryOrSecondary rep =
63+
try (primary rep) >>= \case
64+
Left (SomeException err) -> do
65+
print $
66+
"Failed to acquire primary replica, reason:"
67+
++ show err
68+
++ ". Moving to second..."
69+
try (secondaryOk rep) >>= \case
70+
Left (SomeException _) -> pure Nothing
71+
Right pipe -> pure $ Just pipe
72+
Right pipe -> pure $ Just pipe

0 commit comments

Comments
 (0)