Refactor Mock Data Connector Agent Integration Tests

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7531
GitOrigin-RevId: b5111a46ba42c22b110a021b8d0404b114edaba7
This commit is contained in:
Daniel Chambers 2023-01-17 17:43:07 +11:00 committed by hasura-bot
parent 8d6b9f70f1
commit 308d310d62
7 changed files with 1552 additions and 1640 deletions

View File

@ -7,17 +7,18 @@ module Test.DataConnector.MockAgent.AggregateQuerySpec (spec) where
import Data.Aeson qualified as Aeson
import Data.HashMap.Strict qualified as HashMap
import Data.List.NonEmpty qualified as NE
import Harness.Backend.DataConnector.Mock (TestCase (..))
import Harness.Backend.DataConnector.Mock (AgentRequest (..), MockRequestResults (..), mockAgentTest, mockQueryResponse)
import Harness.Backend.DataConnector.Mock qualified as Mock
import Harness.Quoter.Graphql (graphql)
import Harness.Quoter.Yaml (yaml)
import Harness.Test.BackendType qualified as BackendType
import Harness.Test.Fixture qualified as Fixture
import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
import Harness.Yaml (shouldBeYaml)
import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude
import Language.GraphQL.Draft.Syntax.QQ qualified as G
import Test.Hspec (SpecWith, describe, it)
import Test.Hspec (SpecWith, describe, shouldBe)
--------------------------------------------------------------------------------
@ -83,250 +84,249 @@ sourceMetadata =
--------------------------------------------------------------------------------
tests :: Fixture.Options -> SpecWith (TestEnvironment, Mock.MockAgentEnvironment)
tests opts = describe "Aggregate Query Tests" $ do
it "works with multiple nodes fields and through array relations" $
Mock.runTest opts $
let required =
Mock.TestCaseRequired
{ _givenRequired =
let response =
[ [ (API.FieldName "ArtistIds_Id", API.mkColumnFieldValue $ Aeson.Number 1),
(API.FieldName "ArtistNames_Name", API.mkColumnFieldValue $ Aeson.String "AC/DC"),
( API.FieldName "nodes_Albums",
API.mkRelationshipFieldValue $
rowsResponse
[ [(API.FieldName "nodes_Title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")],
[(API.FieldName "nodes_Title", API.mkColumnFieldValue $ Aeson.String "Let There Be Rock")]
]
)
]
]
in Mock.chinookMock {Mock._queryResponse = \_ -> Right (rowsResponse response)},
_whenRequestRequired =
[graphql|
query getArtist {
Artist_aggregate(limit: 1) {
ArtistIds: nodes {
Id: ArtistId
}
ArtistNames: nodes {
Name
}
nodes {
Albums: Albums_aggregate {
nodes {
Title
}
}
}
}
tests _opts = describe "Aggregate Query Tests" $ do
mockAgentTest "works with multiple nodes fields and through array relations" $ \performGraphqlRequest -> do
let headers = []
let graphqlRequest =
[graphql|
query getArtist {
Artist_aggregate(limit: 1) {
ArtistIds: nodes {
Id: ArtistId
}
ArtistNames: nodes {
Name
}
nodes {
Albums: Albums_aggregate {
nodes {
Title
}
|],
_thenRequired =
[yaml|
data:
Artist_aggregate:
ArtistIds:
- Id: 1
ArtistNames:
- Name: AC/DC
nodes:
- Albums:
nodes:
- Title: For Those About To Rock We Salute You
- Title: Let There Be Rock
|]
}
}
}
in (Mock.defaultTestCase required)
{ _whenQuery =
Just
( API.QueryRequest
{ _qrTable = API.TableName ("Artist" :| []),
_qrTableRelationships =
[ API.TableRelationships
{ _trSourceTable = API.TableName ("Artist" :| []),
_trRelationships =
HashMap.fromList
[ ( API.RelationshipName "Albums",
API.Relationship
{ _rTargetTable = API.TableName ("Album" :| []),
_rRelationshipType = API.ArrayRelationship,
_rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")]
}
)
]
}
],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "ArtistIds_Id", API.ColumnField (API.ColumnName "ArtistId") (API.ScalarType "number")),
(API.FieldName "ArtistNames_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")),
( API.FieldName "nodes_Albums",
API.RelField
( API.RelationshipField
(API.RelationshipName "Albums")
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "nodes_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string"))
],
_qAggregates = Nothing,
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
)
)
],
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
}
)
}
|]
let queryResponse =
rowsResponse
[ [ (API.FieldName "ArtistIds_Id", API.mkColumnFieldValue $ Aeson.Number 1),
(API.FieldName "ArtistNames_Name", API.mkColumnFieldValue $ Aeson.String "AC/DC"),
( API.FieldName "nodes_Albums",
API.mkRelationshipFieldValue $
rowsResponse
[ [(API.FieldName "nodes_Title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")],
[(API.FieldName "nodes_Title", API.mkColumnFieldValue $ Aeson.String "Let There Be Rock")]
]
)
]
]
let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
it "works with multiple aggregate fields and through array relations" $
Mock.runTest opts $
let required =
Mock.TestCaseRequired
{ _givenRequired =
let aggregates =
[ (API.FieldName "counts_count", Aeson.Number 2),
(API.FieldName "counts_uniqueBillingCountries", Aeson.Number 2),
(API.FieldName "ids_minimum_Id", Aeson.Number 1),
(API.FieldName "ids_max_InvoiceId", Aeson.Number 2)
]
rows =
[ [ ( API.FieldName "nodes_Lines",
API.mkRelationshipFieldValue $
aggregatesResponse
[ (API.FieldName "aggregate_count", Aeson.Number 2)
]
)
],
[ ( API.FieldName "nodes_Lines",
API.mkRelationshipFieldValue $
aggregatesResponse
[ (API.FieldName "aggregate_count", Aeson.Number 4)
]
)
]
]
in Mock.chinookMock {Mock._queryResponse = \_ -> Right (aggregatesAndRowsResponse aggregates rows)},
_whenRequestRequired =
[graphql|
query getInvoices {
Invoice_aggregate(limit: 2) {
counts: aggregate {
count
uniqueBillingCountries: count(column: BillingCountry, distinct: true)
}
ids: aggregate {
minimum: min {
Id: InvoiceId
}
max {
InvoiceId
}
}
nodes {
Lines: InvoiceLines_aggregate {
aggregate {
count
}
}
}
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
_mrrGraphqlResponse
`shouldBeYaml` [yaml|
data:
Artist_aggregate:
ArtistIds:
- Id: 1
ArtistNames:
- Name: AC/DC
nodes:
- Albums:
nodes:
- Title: For Those About To Rock We Salute You
- Title: Let There Be Rock
|]
_mrrRecordedRequest
`shouldBe` Just
( Query $
API.QueryRequest
{ _qrTable = API.TableName ("Artist" :| []),
_qrTableRelationships =
[ API.TableRelationships
{ _trSourceTable = API.TableName ("Artist" :| []),
_trRelationships =
HashMap.fromList
[ ( API.RelationshipName "Albums",
API.Relationship
{ _rTargetTable = API.TableName ("Album" :| []),
_rRelationshipType = API.ArrayRelationship,
_rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")]
}
)
]
}
],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "ArtistIds_Id", API.ColumnField (API.ColumnName "ArtistId") (API.ScalarType "number")),
(API.FieldName "ArtistNames_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")),
( API.FieldName "nodes_Albums",
API.RelField
( API.RelationshipField
(API.RelationshipName "Albums")
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "nodes_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string"))
],
_qAggregates = Nothing,
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
)
)
],
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
|],
_thenRequired =
[yaml|
data:
Invoice_aggregate:
counts:
count: 2
uniqueBillingCountries: 2
ids:
minimum:
Id: 1
max:
InvoiceId: 2
nodes:
- Lines:
aggregate:
count: 2
- Lines:
aggregate:
count: 4
|]
}
in (Mock.defaultTestCase required)
{ _whenQuery =
Just
( API.QueryRequest
{ _qrTable = API.TableName ("Invoice" :| []),
_qrTableRelationships =
[ API.TableRelationships
{ _trSourceTable = API.TableName ("Invoice" :| []),
_trRelationships =
HashMap.fromList
[ ( API.RelationshipName "InvoiceLines",
API.Relationship
{ _rTargetTable = API.TableName ("InvoiceLine" :| []),
_rRelationshipType = API.ArrayRelationship,
_rColumnMapping = HashMap.fromList [(API.ColumnName "InvoiceId", API.ColumnName "InvoiceId")]
}
)
]
}
],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ ( API.FieldName "nodes_Lines",
API.RelField
( API.RelationshipField
(API.RelationshipName "InvoiceLines")
API.Query
{ _qFields = Nothing,
_qAggregates =
Just $
HashMap.fromList
[(API.FieldName "aggregate_count", API.StarCount)],
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
)
)
],
_qAggregates =
Just $
HashMap.fromList
[ (API.FieldName "counts_count", API.StarCount),
(API.FieldName "counts_uniqueBillingCountries", API.ColumnCount (API.ColumnCountAggregate (API.ColumnName "BillingCountry") True)),
(API.FieldName "ids_minimum_Id", API.SingleColumn (singleColumnAggregateMin (API.ColumnName "InvoiceId"))),
(API.FieldName "ids_max_InvoiceId", API.SingleColumn (singleColumnAggregateMax (API.ColumnName "InvoiceId")))
],
_qLimit = Just 2,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
}
)
)
mockAgentTest "works with multiple aggregate fields and through array relations" $ \performGraphqlRequest -> do
let headers = []
let graphqlRequest =
[graphql|
query getInvoices {
Invoice_aggregate(limit: 2) {
counts: aggregate {
count
uniqueBillingCountries: count(column: BillingCountry, distinct: true)
}
ids: aggregate {
minimum: min {
Id: InvoiceId
}
max {
InvoiceId
}
}
nodes {
Lines: InvoiceLines_aggregate {
aggregate {
count
}
}
}
}
}
|]
let aggregates =
[ (API.FieldName "counts_count", Aeson.Number 2),
(API.FieldName "counts_uniqueBillingCountries", Aeson.Number 2),
(API.FieldName "ids_minimum_Id", Aeson.Number 1),
(API.FieldName "ids_max_InvoiceId", Aeson.Number 2)
]
rows =
[ [ ( API.FieldName "nodes_Lines",
API.mkRelationshipFieldValue $
aggregatesResponse
[ (API.FieldName "aggregate_count", Aeson.Number 2)
]
)
],
[ ( API.FieldName "nodes_Lines",
API.mkRelationshipFieldValue $
aggregatesResponse
[ (API.FieldName "aggregate_count", Aeson.Number 4)
]
)
]
]
let mockConfig = Mock.chinookMock & mockQueryResponse (aggregatesAndRowsResponse aggregates rows)
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
_mrrGraphqlResponse
`shouldBeYaml` [yaml|
data:
Invoice_aggregate:
counts:
count: 2
uniqueBillingCountries: 2
ids:
minimum:
Id: 1
max:
InvoiceId: 2
nodes:
- Lines:
aggregate:
count: 2
- Lines:
aggregate:
count: 4
|]
_mrrRecordedRequest
`shouldBe` Just
( Query $
API.QueryRequest
{ _qrTable = API.TableName ("Invoice" :| []),
_qrTableRelationships =
[ API.TableRelationships
{ _trSourceTable = API.TableName ("Invoice" :| []),
_trRelationships =
HashMap.fromList
[ ( API.RelationshipName "InvoiceLines",
API.Relationship
{ _rTargetTable = API.TableName ("InvoiceLine" :| []),
_rRelationshipType = API.ArrayRelationship,
_rColumnMapping = HashMap.fromList [(API.ColumnName "InvoiceId", API.ColumnName "InvoiceId")]
}
)
]
}
],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ ( API.FieldName "nodes_Lines",
API.RelField
( API.RelationshipField
(API.RelationshipName "InvoiceLines")
API.Query
{ _qFields = Nothing,
_qAggregates =
Just $
HashMap.fromList
[(API.FieldName "aggregate_count", API.StarCount)],
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
)
)
],
_qAggregates =
Just $
HashMap.fromList
[ (API.FieldName "counts_count", API.StarCount),
(API.FieldName "counts_uniqueBillingCountries", API.ColumnCount (API.ColumnCountAggregate (API.ColumnName "BillingCountry") True)),
(API.FieldName "ids_minimum_Id", API.SingleColumn (singleColumnAggregateMin (API.ColumnName "InvoiceId"))),
(API.FieldName "ids_max_InvoiceId", API.SingleColumn (singleColumnAggregateMax (API.ColumnName "InvoiceId")))
],
_qLimit = Just 2,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
}
)
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing

View File

@ -10,16 +10,17 @@ import Data.Aeson qualified as Aeson
import Data.ByteString (ByteString)
import Data.HashMap.Strict qualified as HashMap
import Data.List.NonEmpty qualified as NE
import Harness.Backend.DataConnector.Mock (TestCase (..))
import Harness.Backend.DataConnector.Mock (AgentRequest (..), MockRequestResults (..), mockAgentTest, mockQueryResponse)
import Harness.Backend.DataConnector.Mock qualified as Mock
import Harness.Quoter.Graphql (graphql)
import Harness.Quoter.Yaml (yaml)
import Harness.Test.BackendType qualified as BackendType
import Harness.Test.Fixture qualified as Fixture
import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
import Harness.Yaml (shouldBeYaml)
import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude
import Test.Hspec (SpecWith, describe, it)
import Test.Hspec (SpecWith, describe, shouldBe)
--------------------------------------------------------------------------------
@ -104,185 +105,183 @@ sourceMetadata =
--------------------------------------------------------------------------------
tests :: Fixture.Options -> SpecWith (TestEnvironment, Mock.MockAgentEnvironment)
tests opts = do
describe "Basic Tests" $ do
it "works with simple object query" $
Mock.runTest opts $
let required =
Mock.TestCaseRequired
{ _givenRequired =
let albums =
[ [ (API.FieldName "id", API.mkColumnFieldValue $ Aeson.Number 1),
(API.FieldName "title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
]
]
in Mock.chinookMock {Mock._queryResponse = \_ -> Right (rowsResponse albums)},
_whenRequestRequired =
[graphql|
query getAlbum {
albums(limit: 1) {
id
title
}
}
|],
_thenRequired =
[yaml|
data:
albums:
- id: 1
title: For Those About To Rock We Salute You
|]
}
in (Mock.defaultTestCase required)
{ _whenQuery =
Just
( API.QueryRequest
{ _qrTable = API.TableName ("Album" :| []),
_qrTableRelationships = [],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "id", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
(API.FieldName "title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string"))
],
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
}
)
tests _opts = describe "Basic Tests" $ do
mockAgentTest "works with simple object query" $ \performGraphqlRequest -> do
let headers = []
let graphqlRequest =
[graphql|
query getAlbum {
albums(limit: 1) {
id
title
}
}
|]
let queryResponse =
rowsResponse
[ [ (API.FieldName "id", API.mkColumnFieldValue $ Aeson.Number 1),
(API.FieldName "title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
]
]
let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
it "works with order_by id" $
Mock.runTest opts $
let required =
Mock.TestCaseRequired
{ _givenRequired =
let albums =
[ [ (API.FieldName "id", API.mkColumnFieldValue $ Aeson.Number 1),
(API.FieldName "title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
],
[ (API.FieldName "id", API.mkColumnFieldValue $ Aeson.Number 2),
(API.FieldName "title", API.mkColumnFieldValue $ Aeson.String "Balls to the Wall")
],
[ (API.FieldName "id", API.mkColumnFieldValue $ Aeson.Number 3),
(API.FieldName "title", API.mkColumnFieldValue $ Aeson.String "Restless and Wild")
]
]
in Mock.chinookMock {Mock._queryResponse = \_ -> Right (rowsResponse albums)},
_whenRequestRequired =
[graphql|
query getAlbum {
albums(limit: 3, order_by: {id: asc}) {
id
title
}
}
|],
_thenRequired =
[yaml|
data:
albums:
- id: 1
title: For Those About To Rock We Salute You
- id: 2
title: Balls to the Wall
- id: 3
title: Restless and Wild
|]
}
in (Mock.defaultTestCase required)
{ _whenQuery =
Just
( API.QueryRequest
{ _qrTable = API.TableName ("Album" :| []),
_qrTableRelationships = [],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "id", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
(API.FieldName "title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
],
_qAggregates = Nothing,
_qLimit = Just 3,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Just (API.OrderBy mempty (API.OrderByElement [] (API.OrderByColumn (API.ColumnName "AlbumId")) API.Ascending :| []))
}
}
)
}
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
it "works with an exists-based permissions filter" $
Mock.runTest opts $
let required =
Mock.TestCaseRequired
{ _givenRequired =
let albums =
[ [ (API.FieldName "CustomerId", API.mkColumnFieldValue $ Aeson.Number 1)
_mrrGraphqlResponse
`shouldBeYaml` [yaml|
data:
albums:
- id: 1
title: For Those About To Rock We Salute You
|]
_mrrRecordedRequest
`shouldBe` Just
( Query $
API.QueryRequest
{ _qrTable = API.TableName ("Album" :| []),
_qrTableRelationships = [],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "id", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
(API.FieldName "title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string"))
],
[ (API.FieldName "CustomerId", API.mkColumnFieldValue $ Aeson.Number 2)
],
[ (API.FieldName "CustomerId", API.mkColumnFieldValue $ Aeson.Number 3)
]
]
in Mock.chinookMock {Mock._queryResponse = \_ -> Right (rowsResponse albums)},
_whenRequestRequired =
[graphql|
query getCustomers {
Customer {
CustomerId
}
}
|],
_thenRequired =
[yaml|
data:
Customer:
- CustomerId: 1
- CustomerId: 2
- CustomerId: 3
|]
}
in (Mock.defaultTestCase required)
{ _whenRequestHeaders =
[ ("X-Hasura-Role", testRoleName),
("X-Hasura-EmployeeId", "1")
],
_whenQuery =
Just
( API.QueryRequest
{ _qrTable = API.TableName ("Customer" :| []),
_qrTableRelationships = [],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "CustomerId", API.ColumnField (API.ColumnName "CustomerId") $ API.ScalarType "number")
],
_qAggregates = Nothing,
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere =
Just $
API.Exists (API.UnrelatedTable $ API.TableName ("Employee" :| [])) $
API.ApplyBinaryComparisonOperator
API.Equal
(API.ComparisonColumn API.CurrentTable (API.ColumnName "EmployeeId") $ API.ScalarType "number")
(API.ScalarValue (Aeson.Number 1) $ API.ScalarType "number"),
_qOrderBy = Nothing
}
}
)
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
}
)
mockAgentTest "works with order_by id" $ \performGraphqlRequest -> do
let headers = []
let graphqlRequest =
[graphql|
query getAlbum {
albums(limit: 3, order_by: {id: asc}) {
id
title
}
}
|]
let queryResponse =
rowsResponse
[ [ (API.FieldName "id", API.mkColumnFieldValue $ Aeson.Number 1),
(API.FieldName "title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
],
[ (API.FieldName "id", API.mkColumnFieldValue $ Aeson.Number 2),
(API.FieldName "title", API.mkColumnFieldValue $ Aeson.String "Balls to the Wall")
],
[ (API.FieldName "id", API.mkColumnFieldValue $ Aeson.Number 3),
(API.FieldName "title", API.mkColumnFieldValue $ Aeson.String "Restless and Wild")
]
]
let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
_mrrGraphqlResponse
`shouldBeYaml` [yaml|
data:
albums:
- id: 1
title: For Those About To Rock We Salute You
- id: 2
title: Balls to the Wall
- id: 3
title: Restless and Wild
|]
_mrrRecordedRequest
`shouldBe` Just
( Query $
API.QueryRequest
{ _qrTable = API.TableName ("Album" :| []),
_qrTableRelationships = [],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "id", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
(API.FieldName "title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
],
_qAggregates = Nothing,
_qLimit = Just 3,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Just (API.OrderBy mempty (API.OrderByElement [] (API.OrderByColumn (API.ColumnName "AlbumId")) API.Ascending :| []))
}
}
)
mockAgentTest "works with an exists-based permissions filter" $ \performGraphqlRequest -> do
let headers =
[ ("X-Hasura-Role", testRoleName),
("X-Hasura-EmployeeId", "1")
]
let graphqlRequest =
[graphql|
query getCustomers {
Customer {
CustomerId
}
}
|]
let queryResponse =
rowsResponse
[ [ (API.FieldName "CustomerId", API.mkColumnFieldValue $ Aeson.Number 1)
],
[ (API.FieldName "CustomerId", API.mkColumnFieldValue $ Aeson.Number 2)
],
[ (API.FieldName "CustomerId", API.mkColumnFieldValue $ Aeson.Number 3)
]
]
let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
_mrrGraphqlResponse
`shouldBeYaml` [yaml|
data:
Customer:
- CustomerId: 1
- CustomerId: 2
- CustomerId: 3
|]
_mrrRecordedRequest
`shouldBe` Just
( Query $
API.QueryRequest
{ _qrTable = API.TableName ("Customer" :| []),
_qrTableRelationships = [],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "CustomerId", API.ColumnField (API.ColumnName "CustomerId") $ API.ScalarType "number")
],
_qAggregates = Nothing,
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere =
Just $
API.Exists (API.UnrelatedTable $ API.TableName ("Employee" :| [])) $
API.ApplyBinaryComparisonOperator
API.Equal
(API.ComparisonColumn API.CurrentTable (API.ColumnName "EmployeeId") $ API.ScalarType "number")
(API.ScalarValue (Aeson.Number 1) $ API.ScalarType "number"),
_qOrderBy = Nothing
}
}
)
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing

View File

@ -9,16 +9,17 @@ module Test.DataConnector.MockAgent.ErrorSpec (spec) where
import Data.Aeson qualified as Aeson
import Data.HashMap.Strict qualified as HashMap
import Data.List.NonEmpty qualified as NE
import Harness.Backend.DataConnector.Mock (TestCase (..))
import Harness.Backend.DataConnector.Mock (AgentRequest (..), MockRequestResults (..), mockAgentTest)
import Harness.Backend.DataConnector.Mock qualified as Mock
import Harness.Quoter.Graphql (graphql)
import Harness.Quoter.Yaml (yaml)
import Harness.Test.BackendType qualified as BackendType
import Harness.Test.Fixture qualified as Fixture
import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
import Harness.Yaml (shouldBeYaml)
import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude
import Test.Hspec (SpecWith, describe, it)
import Test.Hspec (SpecWith, describe, shouldBe)
--------------------------------------------------------------------------------
@ -59,55 +60,54 @@ sourceMetadata =
--------------------------------------------------------------------------------
tests :: Fixture.Options -> SpecWith (TestEnvironment, Mock.MockAgentEnvironment)
tests opts = do
describe "Error Protocol Tests" $ do
it "handles returned errors correctly" $
Mock.runTest opts $
let errorResponse = API.ErrorResponse API.UncaughtError "Hello World!" [yaml| { foo: "bar" } |]
required =
Mock.TestCaseRequired
{ _givenRequired = Mock.chinookMock {Mock._queryResponse = \_ -> Left errorResponse},
_whenRequestRequired =
[graphql|
query getAlbum {
albums(limit: 1) {
id
title
}
}
|],
_thenRequired =
[yaml|
errors:
-
extensions:
code: "data-connector-error"
path: "$"
internal:
foo: "bar"
message: "UncaughtError: Hello World!"
|]
}
in (Mock.defaultTestCase required)
{ _whenQuery =
Just
( API.QueryRequest
{ _qrTable = API.TableName ("Album" :| []),
_qrTableRelationships = [],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "id", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
(API.FieldName "title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
],
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
}
)
tests _opts = describe "Error Protocol Tests" $ do
mockAgentTest "handles returned errors correctly" $ \performGraphqlRequest -> do
let headers = []
let graphqlRequest =
[graphql|
query getAlbum {
albums(limit: 1) {
id
title
}
}
|]
let errorResponse = API.ErrorResponse API.UncaughtError "Hello World!" [yaml| { foo: "bar" } |]
let mockConfig = Mock.chinookMock {Mock._queryResponse = \_ -> Left errorResponse}
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
_mrrGraphqlResponse
`shouldBeYaml` [yaml|
errors:
-
extensions:
code: "data-connector-error"
path: "$"
internal:
foo: "bar"
message: "UncaughtError: Hello World!"
|]
_mrrRecordedRequest
`shouldBe` Just
( Query $
API.QueryRequest
{ _qrTable = API.TableName ("Album" :| []),
_qrTableRelationships = [],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "id", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
(API.FieldName "title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
],
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
}
)

View File

@ -9,16 +9,17 @@ import Data.Aeson qualified as Aeson
import Data.ByteString (ByteString)
import Data.HashMap.Strict qualified as HashMap
import Data.List.NonEmpty qualified as NE
import Harness.Backend.DataConnector.Mock (TestCase (..))
import Harness.Backend.DataConnector.Mock (AgentRequest (..), MockRequestResults (..), mockAgentTest, mockQueryResponse)
import Harness.Backend.DataConnector.Mock qualified as Mock
import Harness.Quoter.Graphql (graphql)
import Harness.Quoter.Yaml (yaml)
import Harness.Test.BackendType qualified as BackendType
import Harness.Test.Fixture qualified as Fixture
import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
import Harness.Yaml (shouldBeYaml)
import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude
import Test.Hspec (SpecWith, describe, it)
import Test.Hspec (SpecWith, describe, shouldBe)
--------------------------------------------------------------------------------
@ -126,386 +127,384 @@ sourceMetadata =
--------------------------------------------------------------------------------
tests :: Fixture.Options -> SpecWith (TestEnvironment, Mock.MockAgentEnvironment)
tests opts = do
describe "Object Relationships Tests" $ do
it "works with multiple object relationships" $
Mock.runTest opts $
let required =
Mock.TestCaseRequired
{ _givenRequired =
let albums =
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock (We Salute You)"),
( API.FieldName "Genre",
API.mkRelationshipFieldValue $
rowsResponse
[ [(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock")]
]
tests _opts = describe "Object Relationships Tests" $ do
mockAgentTest "works with multiple object relationships" $ \performGraphqlRequest -> do
let headers = []
let graphqlRequest =
[graphql|
query getTrack {
Track(limit: 1) {
Name
Genre {
Name
}
MediaType {
Name
}
}
}
|]
let queryResponse =
rowsResponse
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock (We Salute You)"),
( API.FieldName "Genre",
API.mkRelationshipFieldValue $
rowsResponse
[ [(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock")]
]
),
( API.FieldName "MediaType",
API.mkRelationshipFieldValue $
rowsResponse
[ [(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "MPEG audio file")]
]
)
]
]
let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
_mrrGraphqlResponse
`shouldBeYaml` [yaml|
data:
Track:
- Genre:
Name: "Rock"
MediaType:
Name: "MPEG audio file"
Name: "For Those About To Rock (We Salute You)"
|]
_mrrRecordedRequest
`shouldBe` Just
( Query $
API.QueryRequest
{ _qrTable = API.TableName ("Track" :| []),
_qrTableRelationships =
[ API.TableRelationships
{ _trSourceTable = API.TableName ("Track" :| []),
_trRelationships =
HashMap.fromList
[ ( API.RelationshipName "Genre",
API.Relationship
{ _rTargetTable = API.TableName ("Genre" :| []),
_rRelationshipType = API.ObjectRelationship,
_rColumnMapping = HashMap.fromList [(API.ColumnName "GenreId", API.ColumnName "GenreId")]
}
),
( API.FieldName "MediaType",
API.mkRelationshipFieldValue $
rowsResponse
[ [(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "MPEG audio file")]
]
( API.RelationshipName "MediaType",
API.Relationship
{ _rTargetTable = API.TableName ("MediaType" :| []),
_rRelationshipType = API.ObjectRelationship,
_rColumnMapping =
HashMap.fromList
[(API.ColumnName "MediaTypeId", API.ColumnName "MediaTypeId")]
}
)
]
]
in Mock.chinookMock {Mock._queryResponse = \_ -> Right (rowsResponse albums)},
_whenRequestRequired =
[graphql|
query getTrack {
Track(limit: 1) {
Name
Genre {
Name
}
MediaType {
Name
}
}
}
|],
_thenRequired =
[yaml|
data:
Track:
- Genre:
Name: "Rock"
MediaType:
Name: "MPEG audio file"
Name: "For Those About To Rock (We Salute You)"
|]
}
in (Mock.defaultTestCase required)
{ _whenQuery =
Just
( API.QueryRequest
{ _qrTable = API.TableName ("Track" :| []),
_qrTableRelationships =
[ API.TableRelationships
{ _trSourceTable = API.TableName ("Track" :| []),
_trRelationships =
HashMap.fromList
[ ( API.RelationshipName "Genre",
API.Relationship
{ _rTargetTable = API.TableName ("Genre" :| []),
_rRelationshipType = API.ObjectRelationship,
_rColumnMapping = HashMap.fromList [(API.ColumnName "GenreId", API.ColumnName "GenreId")]
}
),
( API.RelationshipName "MediaType",
API.Relationship
{ _rTargetTable = API.TableName ("MediaType" :| []),
_rRelationshipType = API.ObjectRelationship,
_rColumnMapping =
HashMap.fromList
[(API.ColumnName "MediaTypeId", API.ColumnName "MediaTypeId")]
}
)
]
}
],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string"),
( API.FieldName "Genre",
API.RelField
( API.RelationshipField
(API.RelationshipName "Genre")
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
],
_qAggregates = Nothing,
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
)
),
( API.FieldName "MediaType",
API.RelField
( API.RelationshipField
(API.RelationshipName "MediaType")
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
],
_qAggregates = Nothing,
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
)
)
],
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
}
)
}
it "works with an order by that navigates relationships" $
Mock.runTest opts $
let required =
Mock.TestCaseRequired
{ _givenRequired =
let albums =
[ [ ( API.FieldName "Album",
API.mkRelationshipFieldValue $
rowsResponse
[ [ ( API.FieldName "Artist",
API.mkRelationshipFieldValue $
rowsResponse
[[(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Zeca Pagodinho")]]
)
]
]
],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string"),
( API.FieldName "Genre",
API.RelField
( API.RelationshipField
(API.RelationshipName "Genre")
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
],
_qAggregates = Nothing,
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
)
),
(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Camarão que Dorme e Onda Leva")
]
]
in Mock.chinookMock {Mock._queryResponse = \_ -> Right (rowsResponse albums)},
_whenRequestRequired =
[graphql|
query getTrack {
Track(order_by: [{Album: {Artist: {Name: desc}}}, { Name: asc }], limit: 1) {
Album {
Artist {
Name
}
}
Name
}
}
|],
_thenRequired =
[yaml|
data:
Track:
- Album:
Artist:
Name: Zeca Pagodinho
Name: Camarão que Dorme e Onda Leva
|]
}
in (Mock.defaultTestCase required)
{ _whenQuery =
Just
( API.QueryRequest
{ _qrTable = API.TableName ("Track" :| []),
_qrTableRelationships =
[ API.TableRelationships
{ _trSourceTable = API.TableName ("Track" :| []),
_trRelationships =
HashMap.fromList
[ ( API.RelationshipName "Album",
API.Relationship
{ _rTargetTable = API.TableName ("Album" :| []),
_rRelationshipType = API.ObjectRelationship,
_rColumnMapping = HashMap.fromList [(API.ColumnName "AlbumId", API.ColumnName "AlbumId")]
}
)
]
},
API.TableRelationships
{ _trSourceTable = API.TableName ("Album" :| []),
_trRelationships =
HashMap.fromList
[ ( API.RelationshipName "Artist",
API.Relationship
{ _rTargetTable = API.TableName ("Artist" :| []),
_rRelationshipType = API.ObjectRelationship,
_rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")]
}
)
]
}
( API.FieldName "MediaType",
API.RelField
( API.RelationshipField
(API.RelationshipName "MediaType")
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
],
_qAggregates = Nothing,
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
)
)
],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string"),
( API.FieldName "Album",
API.RelField
( API.RelationshipField
(API.RelationshipName "Album")
API.Query
{ _qFields =
Just $
HashMap.fromList
[ ( API.FieldName "Artist",
API.RelField
( API.RelationshipField
(API.RelationshipName "Artist")
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
],
_qAggregates = Nothing,
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
)
)
],
_qAggregates = Nothing,
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
)
)
],
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy =
Just $
API.OrderBy
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
}
)
mockAgentTest "works with an order by that navigates relationships" $ \performGraphqlRequest -> do
let headers = []
let graphqlRequest =
[graphql|
query getTrack {
Track(order_by: [{Album: {Artist: {Name: desc}}}, { Name: asc }], limit: 1) {
Album {
Artist {
Name
}
}
Name
}
}
|]
let queryResponse =
rowsResponse
[ [ ( API.FieldName "Album",
API.mkRelationshipFieldValue $
rowsResponse
[ [ ( API.FieldName "Artist",
API.mkRelationshipFieldValue $
rowsResponse
[[(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Zeca Pagodinho")]]
)
]
]
),
(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Camarão que Dorme e Onda Leva")
]
]
let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
_mrrGraphqlResponse
`shouldBeYaml` [yaml|
data:
Track:
- Album:
Artist:
Name: Zeca Pagodinho
Name: Camarão que Dorme e Onda Leva
|]
_mrrRecordedRequest
`shouldBe` Just
( Query $
API.QueryRequest
{ _qrTable = API.TableName ("Track" :| []),
_qrTableRelationships =
[ API.TableRelationships
{ _trSourceTable = API.TableName ("Track" :| []),
_trRelationships =
HashMap.fromList
[ ( API.RelationshipName "Album",
API.Relationship
{ _rTargetTable = API.TableName ("Album" :| []),
_rRelationshipType = API.ObjectRelationship,
_rColumnMapping = HashMap.fromList [(API.ColumnName "AlbumId", API.ColumnName "AlbumId")]
}
)
]
},
API.TableRelationships
{ _trSourceTable = API.TableName ("Album" :| []),
_trRelationships =
HashMap.fromList
[ ( API.RelationshipName "Artist",
API.Relationship
{ _rTargetTable = API.TableName ("Artist" :| []),
_rRelationshipType = API.ObjectRelationship,
_rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")]
}
)
]
}
],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string"),
( API.FieldName "Album",
API.RelField
( API.RelationshipField
(API.RelationshipName "Album")
API.Query
{ _qFields =
Just $
HashMap.fromList
[ ( API.FieldName "Artist",
API.RelField
( API.RelationshipField
(API.RelationshipName "Artist")
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
],
_qAggregates = Nothing,
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
)
)
],
_qAggregates = Nothing,
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
)
)
],
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy =
Just $
API.OrderBy
( HashMap.fromList
[ ( API.RelationshipName "Album",
API.OrderByRelation
Nothing
( HashMap.fromList
[ ( API.RelationshipName "Album",
[ ( API.RelationshipName "Artist",
API.OrderByRelation
Nothing
( HashMap.fromList
[ ( API.RelationshipName "Artist",
API.OrderByRelation
Nothing
mempty
)
]
)
)
]
)
( NE.fromList
[ API.OrderByElement [API.RelationshipName "Album", API.RelationshipName "Artist"] (API.OrderByColumn (API.ColumnName "Name")) API.Descending,
API.OrderByElement [] (API.OrderByColumn (API.ColumnName "Name")) API.Ascending
]
)
}
}
)
}
it "works with an order by that navigates a relationship with table permissions" $
Mock.runTest opts $
let required =
Mock.TestCaseRequired
{ _givenRequired =
let albums =
[ [ (API.FieldName "EmployeeId", API.mkColumnFieldValue $ Aeson.Number 3)
]
]
in Mock.chinookMock {Mock._queryResponse = \_ -> Right (rowsResponse albums)},
_whenRequestRequired =
[graphql|
query getEmployee {
Employee(limit: 1, order_by: {SupportRepForCustomers_aggregate: {count: desc}}) {
EmployeeId
}
}
|],
_thenRequired =
[yaml|
data:
Employee:
- EmployeeId: 3
|]
}
in (Mock.defaultTestCase required)
{ _whenRequestHeaders = [("X-Hasura-Role", testRoleName)],
_whenQuery =
Just
( API.QueryRequest
{ _qrTable = API.TableName ("Employee" :| []),
_qrTableRelationships =
[ API.TableRelationships
{ _trSourceTable = API.TableName ("Customer" :| []),
_trRelationships =
HashMap.fromList
[ ( API.RelationshipName "SupportRep",
API.Relationship
{ _rTargetTable = API.TableName ("Employee" :| []),
_rRelationshipType = API.ObjectRelationship,
_rColumnMapping = HashMap.fromList [(API.ColumnName "SupportRepId", API.ColumnName "EmployeeId")]
}
)
]
},
API.TableRelationships
{ _trSourceTable = API.TableName ("Employee" :| []),
_trRelationships =
HashMap.fromList
[ ( API.RelationshipName "SupportRepForCustomers",
API.Relationship
{ _rTargetTable = API.TableName ("Customer" :| []),
_rRelationshipType = API.ArrayRelationship,
_rColumnMapping = HashMap.fromList [(API.ColumnName "EmployeeId", API.ColumnName "SupportRepId")]
}
)
]
}
],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "EmployeeId", API.ColumnField (API.ColumnName "EmployeeId") $ API.ScalarType "number")
],
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere =
Just $
API.Exists (API.RelatedTable $ API.RelationshipName "SupportRepForCustomers") $
API.ApplyBinaryComparisonOperator
API.Equal
(API.ComparisonColumn API.CurrentTable (API.ColumnName "Country") $ API.ScalarType "string")
(API.AnotherColumn (API.ComparisonColumn API.QueryTable (API.ColumnName "Country") $ API.ScalarType "string")),
_qOrderBy =
Just $
API.OrderBy
( HashMap.fromList
[ ( API.RelationshipName "SupportRepForCustomers",
API.OrderByRelation
( Just $
API.Exists (API.RelatedTable $ API.RelationshipName "SupportRep") $
API.ApplyBinaryComparisonOperator
API.Equal
(API.ComparisonColumn API.CurrentTable (API.ColumnName "Country") $ API.ScalarType "string")
(API.AnotherColumn (API.ComparisonColumn API.QueryTable (API.ColumnName "Country") $ API.ScalarType "string"))
)
mempty
)
]
)
(API.OrderByElement [API.RelationshipName "SupportRepForCustomers"] API.OrderByStarCountAggregate API.Descending :| [])
}
}
)
)
]
)
( NE.fromList
[ API.OrderByElement [API.RelationshipName "Album", API.RelationshipName "Artist"] (API.OrderByColumn (API.ColumnName "Name")) API.Descending,
API.OrderByElement [] (API.OrderByColumn (API.ColumnName "Name")) API.Ascending
]
)
}
}
)
mockAgentTest "works with an order by that navigates a relationship with table permissions" $ \performGraphqlRequest -> do
let headers = [("X-Hasura-Role", testRoleName)]
let graphqlRequest =
[graphql|
query getEmployee {
Employee(limit: 1, order_by: {SupportRepForCustomers_aggregate: {count: desc}}) {
EmployeeId
}
}
|]
let queryResponse =
rowsResponse
[ [ (API.FieldName "EmployeeId", API.mkColumnFieldValue $ Aeson.Number 3)
]
]
let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
_mrrGraphqlResponse
`shouldBeYaml` [yaml|
data:
Employee:
- EmployeeId: 3
|]
_mrrRecordedRequest
`shouldBe` Just
( Query $
API.QueryRequest
{ _qrTable = API.TableName ("Employee" :| []),
_qrTableRelationships =
[ API.TableRelationships
{ _trSourceTable = API.TableName ("Customer" :| []),
_trRelationships =
HashMap.fromList
[ ( API.RelationshipName "SupportRep",
API.Relationship
{ _rTargetTable = API.TableName ("Employee" :| []),
_rRelationshipType = API.ObjectRelationship,
_rColumnMapping = HashMap.fromList [(API.ColumnName "SupportRepId", API.ColumnName "EmployeeId")]
}
)
]
},
API.TableRelationships
{ _trSourceTable = API.TableName ("Employee" :| []),
_trRelationships =
HashMap.fromList
[ ( API.RelationshipName "SupportRepForCustomers",
API.Relationship
{ _rTargetTable = API.TableName ("Customer" :| []),
_rRelationshipType = API.ArrayRelationship,
_rColumnMapping = HashMap.fromList [(API.ColumnName "EmployeeId", API.ColumnName "SupportRepId")]
}
)
]
}
],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "EmployeeId", API.ColumnField (API.ColumnName "EmployeeId") $ API.ScalarType "number")
],
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere =
Just $
API.Exists (API.RelatedTable $ API.RelationshipName "SupportRepForCustomers") $
API.ApplyBinaryComparisonOperator
API.Equal
(API.ComparisonColumn API.CurrentTable (API.ColumnName "Country") $ API.ScalarType "string")
(API.AnotherColumn (API.ComparisonColumn API.QueryTable (API.ColumnName "Country") $ API.ScalarType "string")),
_qOrderBy =
Just $
API.OrderBy
( HashMap.fromList
[ ( API.RelationshipName "SupportRepForCustomers",
API.OrderByRelation
( Just $
API.Exists (API.RelatedTable $ API.RelationshipName "SupportRep") $
API.ApplyBinaryComparisonOperator
API.Equal
(API.ComparisonColumn API.CurrentTable (API.ColumnName "Country") $ API.ScalarType "string")
(API.AnotherColumn (API.ComparisonColumn API.QueryTable (API.ColumnName "Country") $ API.ScalarType "string"))
)
mempty
)
]
)
(API.OrderByElement [API.RelationshipName "SupportRepForCustomers"] API.OrderByStarCountAggregate API.Descending :| [])
}
}
)
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing

View File

@ -8,16 +8,17 @@ module Test.DataConnector.MockAgent.TransformedConfigurationSpec (spec) where
import Data.Aeson qualified as Aeson
import Data.HashMap.Strict qualified as HashMap
import Data.List.NonEmpty qualified as NE
import Harness.Backend.DataConnector.Mock (TestCase (..))
import Harness.Backend.DataConnector.Mock (AgentRequest (..), MockRequestResults (..), mockAgentTest, mockQueryResponse)
import Harness.Backend.DataConnector.Mock qualified as Mock
import Harness.Quoter.Graphql (graphql)
import Harness.Quoter.Yaml (yaml)
import Harness.Test.BackendType qualified as BackendType
import Harness.Test.Fixture qualified as Fixture
import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
import Harness.Yaml (shouldBeYaml)
import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude
import Test.Hspec (SpecWith, describe, it)
import Test.Hspec (SpecWith, describe, shouldBe)
--------------------------------------------------------------------------------
@ -99,72 +100,66 @@ sourceMetadata =
--------------------------------------------------------------------------------
tests :: Fixture.Options -> SpecWith (TestEnvironment, Mock.MockAgentEnvironment)
tests opts = do
describe "Basic Tests" $ do
it "works with configuration transformation Kriti template" $
Mock.runTest opts $
let required =
Mock.TestCaseRequired
{ _givenRequired =
let albums =
[ [ (API.FieldName "id", API.mkColumnFieldValue $ Aeson.Number 1),
(API.FieldName "title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
]
]
in Mock.chinookMock {Mock._queryResponse = \_ -> Right (rowsResponse albums)},
_whenRequestRequired =
[graphql|
query getAlbum {
albums(limit: 1) {
id
title
}
}
|],
_thenRequired =
[yaml|
data:
albums:
- id: 1
title: For Those About To Rock We Salute You
|]
}
in (Mock.defaultTestCase required)
{ _whenQuery =
Just
( API.QueryRequest
{ _qrTable = API.TableName ("Album" :| []),
_qrTableRelationships = [],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "id", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
(API.FieldName "title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
],
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
}
),
_whenConfig =
-- TODO: Create a QQ for this purpose.
let conf =
Aeson.fromJSON
[yaml|
DEBUG:
config: "baz config default"
env: "bar env default"
session: "foo session default"
|]
in case conf of
Aeson.Success r -> Just r
_ -> error "Should parse."
tests _opts = describe "Transformed Configuration Tests" $ do
mockAgentTest "works with configuration transformation Kriti template" $ \performGraphqlRequest -> do
let headers = []
let graphqlRequest =
[graphql|
query getAlbum {
albums(limit: 1) {
id
title
}
}
|]
let queryResponse =
rowsResponse
[ [ (API.FieldName "id", API.mkColumnFieldValue $ Aeson.Number 1),
(API.FieldName "title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
]
]
let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
_mrrGraphqlResponse
`shouldBeYaml` [yaml|
data:
albums:
- id: 1
title: For Those About To Rock We Salute You
|]
_mrrRecordedRequest
`shouldBe` Just
( Query $
API.QueryRequest
{ _qrTable = API.TableName ("Album" :| []),
_qrTableRelationships = [],
_qrQuery =
API.Query
{ _qFields =
Just $
HashMap.fromList
[ (API.FieldName "id", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
(API.FieldName "title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
],
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
}
)
Aeson.toJSON _mrrRecordedRequestConfig
`shouldBeYaml` [yaml|
DEBUG:
config: "baz config default"
env: "bar env default"
session: "foo session default"
|]
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing

View File

@ -13,11 +13,7 @@ module Harness.Backend.DataConnector.Mock
-- * Mock Test Construction
MockConfig (..),
MockAgentEnvironment (..),
TestCase (..),
TestCaseRequired (..),
runTest,
mockAgentPort,
defaultTestCase,
chinookMock,
AgentRequest (..),
MockRequestResults (..),
@ -42,10 +38,9 @@ import Harness.Test.BackendType qualified as BackendType
import Harness.Test.Fixture qualified as Fixture
import Harness.Test.TestResource (AcquiredResource (..), Managed, mkTestResource)
import Harness.TestEnvironment (TestEnvironment (..))
import Harness.Yaml (shouldReturnYaml)
import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude
import Test.Hspec (Arg, Expectation, SpecWith, it, shouldBe)
import Test.Hspec (Arg, Expectation, SpecWith, it)
--------------------------------------------------------------------------------
@ -89,10 +84,10 @@ agentConfig =
let backendType = BackendType.backendTypeString backendTypeMetadata
agentUri = "http://127.0.0.1:" <> show mockAgentPort <> "/"
in [yaml|
dataconnector:
*backendType:
uri: *agentUri
|]
dataconnector:
*backendType:
uri: *agentUri
|]
--------------------------------------------------------------------------------
-- Mock Agent
@ -149,74 +144,6 @@ mkLocalTestEnvironment _ = mkTestResource do
teardownResource = Async.cancel maeThread
}
-- | Mock Agent test case input.
data TestCase = TestCase
{ -- | The Mock configuration for the agent
_given :: MockConfig,
-- | The Graphql Query to test
_whenRequest :: Aeson.Value,
-- | The headers to use on the Graphql Query request
_whenRequestHeaders :: RequestHeaders,
-- | The expected HGE 'API.Query' value to be provided to the
-- agent. A @Nothing@ value indicates that the 'API.Query'
-- assertion should be skipped.
_whenQuery :: Maybe API.QueryRequest,
-- | The expected HGE 'API.QueryHeaders' response and outgoing HGE 'API.QueryHeaders'
_whenConfig :: Maybe API.Config,
-- | The expected GQL response and outgoing HGE 'API.Query'
_then :: Aeson.Value
}
data TestCaseRequired = TestCaseRequired
{ -- | The Mock configuration for the agent
_givenRequired :: MockConfig,
-- | The Graphql Query to test
_whenRequestRequired :: Aeson.Value,
-- | The expected GQL response and outgoing HGE 'API.Query'
_thenRequired :: Aeson.Value
}
defaultTestCase :: TestCaseRequired -> TestCase
defaultTestCase TestCaseRequired {..} =
TestCase
{ _given = _givenRequired,
_whenRequest = _whenRequestRequired,
_whenRequestHeaders = [],
_whenQuery = Nothing,
_whenConfig = Nothing,
_then = _thenRequired
}
-- | Test runner for the Mock Agent. 'runMockedTest' sets the mocked
-- value in the agent, fires a GQL request, then asserts on the
-- expected response and 'API.Query' value.
runTest :: HasCallStack => Fixture.Options -> TestCase -> (TestEnvironment, MockAgentEnvironment) -> IO ()
runTest opts TestCase {..} (testEnvironment, MockAgentEnvironment {..}) = do
-- Set the Agent with the 'MockConfig'
I.writeIORef maeConfig _given
-- Execute the GQL Query and assert on the result
shouldReturnYaml
opts
( GraphqlEngine.postGraphqlWithHeaders
testEnvironment
_whenRequestHeaders
_whenRequest
)
_then
-- Read the logged 'API.QueryRequest' from the Agent
query <- (>>= \case Query query -> Just query; _ -> Nothing) <$> I.readIORef maeRecordedRequest
I.writeIORef maeRecordedRequest Nothing
-- Read the logged 'API.Config' from the Agent
queryConfig <- I.readIORef maeRecordedRequestConfig
I.writeIORef maeRecordedRequestConfig Nothing
-- Assert that the 'API.QueryRequest' was constructed how we expected.
for_ _whenQuery ((query `shouldBe`) . Just)
for_ _whenConfig ((queryConfig `shouldBe`) . Just)
data MockRequestResults = MockRequestResults
{ _mrrGraphqlResponse :: Aeson.Value,
_mrrRecordedRequest :: Maybe AgentRequest,
@ -231,11 +158,11 @@ mockMutationResponse :: API.MutationResponse -> MockConfig -> MockConfig
mockMutationResponse mutationResponse mockConfig =
mockConfig {_mutationResponse = \_ -> Right mutationResponse}
mockAgentTest :: String -> ((MockConfig -> RequestHeaders -> Aeson.Value -> IO MockRequestResults) -> Expectation) -> SpecWith (Arg ((TestEnvironment, MockAgentEnvironment) -> Expectation))
mockAgentTest :: HasCallStack => String -> ((MockConfig -> RequestHeaders -> Aeson.Value -> IO MockRequestResults) -> Expectation) -> SpecWith (Arg ((TestEnvironment, MockAgentEnvironment) -> Expectation))
mockAgentTest name testBody =
it name $ \env -> testBody (postMockAgentGraphqlWithHeaders env)
postMockAgentGraphqlWithHeaders :: (TestEnvironment, MockAgentEnvironment) -> MockConfig -> RequestHeaders -> Aeson.Value -> IO MockRequestResults
postMockAgentGraphqlWithHeaders :: HasCallStack => (TestEnvironment, MockAgentEnvironment) -> MockConfig -> RequestHeaders -> Aeson.Value -> IO MockRequestResults
postMockAgentGraphqlWithHeaders (testEnvironment, MockAgentEnvironment {..}) mockConfig requestHeaders graphqlRequest = do
-- Set the Agent with the 'MockConfig'
I.writeIORef maeConfig mockConfig