From 92338d15dd2b06a7eb1bec118d8609687a5fa541 Mon Sep 17 00:00:00 2001 From: aviaviavi Date: Tue, 22 May 2018 17:17:07 -0700 Subject: [PATCH 1/3] adding mixedContains to account for both contains and not contains --- curl-runnings.cabal | 4 ++-- examples/example-spec.json | 25 +++++++++++++++++-- examples/example-spec.yaml | 22 +++++++++++++---- package.yaml | 2 +- src/Testing/CurlRunnings.hs | 12 ++++++++++ src/Testing/CurlRunnings/Types.hs | 40 ++++++++++++++++++++++++++----- 6 files changed, 90 insertions(+), 15 deletions(-) diff --git a/curl-runnings.cabal b/curl-runnings.cabal index 47bccad..1683451 100644 --- a/curl-runnings.cabal +++ b/curl-runnings.cabal @@ -2,10 +2,10 @@ -- -- see: https://github.com/sol/hpack -- --- hash: e22614c4e739f355b79fac77eb1cf5c8111237f2adc4aebcccbbb3b118e8d817 +-- hash: 7bb221552afcd01d9eb59facb28651cb408688b95fd4fa710adee06dfa6e84ce name: curl-runnings -version: 0.6.0 +version: 0.7.0 synopsis: A framework for declaratively writing curl based API tests description: Please see the README on Github at category: Testing diff --git a/examples/example-spec.json b/examples/example-spec.json index f7f177f..47d72c8 100644 --- a/examples/example-spec.json +++ b/examples/example-spec.json @@ -66,12 +66,33 @@ "name": "test 5", "url": "http://your-url.com/other/path", "requestMethod": "GET", + "expectData": { + "contains": [ + { + "keyValueMatch": { + "key": "okay", + "value": true + } + } + ], + "notContains": [ + { + "valueMatch": false + } + ] + }, + "expectStatus": 200 + }, + { + "name": "test 6", + "url": "http://your-url.com/other/path", + "requestMethod": "GET", "headers": "Content-Type: application/json", "expectStatus": 200, "expectHeaders": "Content-Type: application/json; Hello: world" }, { - "name": "test 6", + "name": "test 7", "url": "http://your-url.com/other/path", "requestMethod": "GET", "headers": "Content-Type: application/json", @@ -83,7 +104,7 @@ ] }, { - "name": "test 7", + "name": "test 8", "url": "http://your-url.com/other/path", "requestMethod": "GET", "headers": "Content-Type: application/json", diff --git a/examples/example-spec.yaml b/examples/example-spec.yaml index be33c7a..6bd82b4 100644 --- a/examples/example-spec.yaml +++ b/examples/example-spec.yaml @@ -52,8 +52,9 @@ url: http://your-url.com/other/path requestMethod: GET expectData: - # In the `notContains` case of data validation, a list of matchers is specified. Currently, - # possible types are `valueMatch` | `keyValueMatch`. + # In the `notContains` case of data validation, a list of matchers is specified. If any + # matcher is found in the response payload, the test will fail. Currently, + # possible matchers are `valueMatch` | `keyValueMatch`. notContains: - keyValueMatch: key: okay @@ -62,6 +63,19 @@ expectStatus: 200 - name: test 5 + url: http://your-url.com/other/path + requestMethod: GET + expectData: + # you can have both a contains and a notContains block in your expectData + contains: + - keyValueMatch: + key: okay + value: true + notContains: + - valueMatch: false + expectStatus: 200 + +- name: test 6 url: http://your-url.com/other/path requestMethod: GET # Specify the headers you want to sent, just like the -H flag in a curl command @@ -72,7 +86,7 @@ # Header strings again match the -H syntax from curl expectHeaders: "Content-Type: application/json; Hello: world" -- name: test 6 +- name: test 7 url: http://your-url.com/other/path requestMethod: GET headers: "Content-Type: application/json" @@ -82,7 +96,7 @@ - key: "Key-With-Val-We-Dont-Care-About" -- name: test 7 +- name: test 8 url: http://your-url.com/other/path requestMethod: GET headers: "Content-Type: application/json" diff --git a/package.yaml b/package.yaml index 503eda3..c354764 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: curl-runnings -version: 0.6.0 +version: 0.7.0 github: aviaviavi/curl-runnings license: MIT author: Avi Press diff --git a/src/Testing/CurlRunnings.hs b/src/Testing/CurlRunnings.hs index 90dd8bc..a23f32a 100644 --- a/src/Testing/CurlRunnings.hs +++ b/src/Testing/CurlRunnings.hs @@ -208,6 +208,18 @@ checkBody state curlCase@(CurlCase _ _ _ _ _ (Just (NotContains subexprs)) _ _) DataFailure curlCase (NotContains updatedMatcher) (Just receivedBody) else Nothing +-- | We are checking for both contains and notContains vals, and we have a payload to check +checkBody state curlCase@(CurlCase _ _ _ _ _ (Just m@(MixedContains subexprs)) _ _) receivedBody = + let failure = join $ + find + isJust + (map + (\subexpr -> + checkBody state curlCase {expectData = Just subexpr} receivedBody) + subexprs) + in + fmap (\_ -> DataFailure curlCase m receivedBody ) failure + -- | We expected a body but didn't get one checkBody _ curlCase@(CurlCase _ _ _ _ _ (Just anything) _ _) Nothing = Just $ DataFailure curlCase anything Nothing diff --git a/src/Testing/CurlRunnings/Types.hs b/src/Testing/CurlRunnings/Types.hs index 5d7a959..9d5c04c 100644 --- a/src/Testing/CurlRunnings/Types.hs +++ b/src/Testing/CurlRunnings/Types.hs @@ -5,6 +5,7 @@ module Testing.CurlRunnings.Types ( AssertionFailure(..) + , CaseResult(..) , CurlSuite(..) , CurlCase(..) @@ -62,9 +63,12 @@ instance ToJSON HttpMethod data JsonMatcher -- | Performs `==` = Exactly Value - -- | A list of matchers to make assertions about some subset of the response. + -- | A list of matchers to make assertions that certains values exist in the response | Contains [JsonSubExpr] + -- | A list of matchers to make assertions that certains values do not exist in the response | NotContains [JsonSubExpr] + -- | We're specifiying both Contains and NotContains matchers + | MixedContains [JsonMatcher] deriving (Show, Generic) instance ToJSON JsonMatcher @@ -72,10 +76,24 @@ instance ToJSON JsonMatcher instance FromJSON JsonMatcher where parseJSON (Object v) | isJust $ H.lookup "exactly" v = Exactly <$> v .: "exactly" + | isJust (H.lookup "contains" v) && isJust (H.lookup "notContains" v) = do + c <- Contains <$> v .: "contains" + n <- NotContains <$> v .: "notContains" + return $ MixedContains [c, n] | isJust $ H.lookup "contains" v = Contains <$> v .: "contains" | isJust $ H.lookup "notContains" v = NotContains <$> v .: "notContains" parseJSON invalid = typeMismatch "JsonMatcher" invalid +-- | Simple predicate to check value constructor type +isContains :: JsonMatcher -> Bool +isContains (Contains _) = True +isContains _ = False + +-- | Simple predicate to check value constructor type +isNotContains :: JsonMatcher -> Bool +isNotContains (NotContains _) = True +isNotContains _ = False + -- | A representation of a single header data Header = Header T.Text @@ -124,14 +142,14 @@ data QueryError instance Show QueryError where show (QueryParseError t q) = printf "error parsing query %s: %s" q $ T.unpack t show (NullPointer full part) = printf "null pointer in %s at %s" (T.unpack full) $ T.unpack part - show (QueryTypeMismatch message val) = printf "type error: %s %s" (message) $ show val + show (QueryTypeMismatch message val) = printf "type error: %s %s" message $ show val show (QueryValidationError message) = printf "invalid query: %s" message parseHeader :: T.Text -> Either T.Text Header parseHeader str = case map T.strip $ T.splitOn ":" str of [key, val] -> Right $ Header key val - anythingElse -> Left . T.pack $ "bad header found: " ++ (show anythingElse) + anythingElse -> Left . T.pack $ "bad header found: " ++ show anythingElse parseHeaders :: T.Text -> Either T.Text Headers parseHeaders str = @@ -241,13 +259,14 @@ data AssertionFailure -- | Something went wrong with a test case json query | QueryFailure CurlCase QueryError + -- | Something else | UnexpectedFailure colorizeExpects :: String -> String colorizeExpects t = - let expectedColor = makeRed "Excpected:" + let expectedColor = makeRed "Expected:" actualColor = makeRed "Actual:" replacedExpected = T.replace "Expected:" (T.pack expectedColor) (T.pack t) in T.unpack $ T.replace "Actual:" (T.pack actualColor) replacedExpected @@ -292,6 +311,14 @@ instance Show AssertionFailure where (url curlCase) (B8.unpack (encodePretty expectedVals)) (B8.unpack (encodePretty receivedVal)) + (MixedContains expectedVals) -> + colorizeExpects $ + printf + "JSON response from %s didn't satisfy the matcher. Expected: %s to each be subvalues and %s not to be subvalues in: %s" + (url curlCase) + (B8.unpack (encodePretty (filter isContains expectedVals))) + (B8.unpack (encodePretty (filter isNotContains expectedVals))) + (B8.unpack (encodePretty receivedVal)) show (HeaderFailure curlCase expected receivedHeaders) = colorizeExpects $ printf @@ -366,6 +393,7 @@ data Index | ArrayIndex Integer deriving (Show) +printOriginalQuery :: Index -> String printOriginalQuery (CaseResultIndex t) = "SUITE[" ++ show t ++ "]" printOriginalQuery (KeyIndex key) = "." ++ T.unpack key printOriginalQuery (ArrayIndex i) = printf "[%d]" i @@ -392,8 +420,8 @@ data InterpolatedQuery printQueryString :: InterpolatedQuery -> String printQueryString (LiteralText t) = show t printQueryString (InterpolatedQuery raw (Query indexes)) = - printf "%s$<%s>" raw (concat $ map show indexes) -printQueryString (NonInterpolatedQuery (Query indexes)) = printf "$<%s>" (concat $ map show indexes) + printf "%s$<%s>" raw $ concatMap show indexes +printQueryString (NonInterpolatedQuery (Query indexes)) = printf "$<%s>" (concatMap show indexes) -- | The full string in which a query appears, eg "prefix-${{SUITE[0].key.another_key[0].last_key}}" type FullQueryText = T.Text From e71a39df07b0a650638df14d9f3eeae3b9a9f145 Mon Sep 17 00:00:00 2001 From: aviaviavi Date: Tue, 22 May 2018 19:10:08 -0700 Subject: [PATCH 2/3] whitespace --- src/Testing/CurlRunnings/Types.hs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Testing/CurlRunnings/Types.hs b/src/Testing/CurlRunnings/Types.hs index 9d5c04c..30ddc49 100644 --- a/src/Testing/CurlRunnings/Types.hs +++ b/src/Testing/CurlRunnings/Types.hs @@ -5,7 +5,6 @@ module Testing.CurlRunnings.Types ( AssertionFailure(..) - , CaseResult(..) , CurlSuite(..) , CurlCase(..) @@ -259,7 +258,6 @@ data AssertionFailure -- | Something went wrong with a test case json query | QueryFailure CurlCase QueryError - -- | Something else | UnexpectedFailure From dbaa32e35c50613856b984364bcfa6f769d1221f Mon Sep 17 00:00:00 2001 From: aviaviavi Date: Tue, 22 May 2018 19:19:09 -0700 Subject: [PATCH 3/3] spelling --- examples/example-spec.yaml | 2 +- src/Testing/CurlRunnings/Types.hs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/example-spec.yaml b/examples/example-spec.yaml index 6bd82b4..ebf85b4 100644 --- a/examples/example-spec.yaml +++ b/examples/example-spec.yaml @@ -82,7 +82,7 @@ # IE "key: value; key: value; ..." headers: "Content-Type: application/json" expectStatus: 200 - # The response must constain at least these headers exactly. + # The response must contain at least these headers exactly. # Header strings again match the -H syntax from curl expectHeaders: "Content-Type: application/json; Hello: world" diff --git a/src/Testing/CurlRunnings/Types.hs b/src/Testing/CurlRunnings/Types.hs index 30ddc49..e42f280 100644 --- a/src/Testing/CurlRunnings/Types.hs +++ b/src/Testing/CurlRunnings/Types.hs @@ -62,9 +62,9 @@ instance ToJSON HttpMethod data JsonMatcher -- | Performs `==` = Exactly Value - -- | A list of matchers to make assertions that certains values exist in the response + -- | A list of matchers to make assertions that contains values exist in the response | Contains [JsonSubExpr] - -- | A list of matchers to make assertions that certains values do not exist in the response + -- | A list of matchers to make assertions that contains values do not exist in the response | NotContains [JsonSubExpr] -- | We're specifiying both Contains and NotContains matchers | MixedContains [JsonMatcher]