graphql-engine/server/tests-hspec/Test/DataConnector/MockAgent/AggregateQuerySpec.hs
Daniel Chambers c209b60239 server: Support the namespacing of table names in Data Connectors
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/5329
GitOrigin-RevId: 5cf492bc2b09fef6250f4dd50f74f750f55ebe6a
2022-08-04 08:35:52 +00:00

337 lines
15 KiB
Haskell

{-# LANGUAGE QuasiQuotes #-}
module Test.DataConnector.MockAgent.AggregateQuerySpec
( spec,
)
where
import Data.Aeson qualified as Aeson
import Data.Aeson.KeyMap qualified as KM
import Data.HashMap.Strict qualified as HashMap
import Data.List.NonEmpty qualified as NE
import Harness.Backend.DataConnector (TestCase (..))
import Harness.Backend.DataConnector qualified as DataConnector
import Harness.Quoter.Graphql (graphql)
import Harness.Quoter.Yaml (yaml)
import Harness.Test.BackendType (BackendType (..), defaultBackendTypeString, defaultSource)
import Harness.Test.Context qualified as Context
import Harness.TestEnvironment (TestEnvironment)
import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude
import Test.Hspec (SpecWith, describe, it)
spec :: SpecWith TestEnvironment
spec =
Context.runWithLocalTestEnvironment
( NE.fromList
[ Context.Context
{ name = Context.Backend Context.DataConnector,
mkLocalTestEnvironment = DataConnector.mkLocalTestEnvironmentMock,
setup = DataConnector.setupMock sourceMetadata DataConnector.mockBackendConfig,
teardown = DataConnector.teardownMock,
customOptions = Nothing
}
]
)
tests
sourceMetadata :: Aeson.Value
sourceMetadata =
let source = defaultSource DataConnector
backendType = defaultBackendTypeString DataConnector
in [yaml|
name : *source
kind: *backendType
tables:
- table: [Album]
object_relationships:
- name: Artist
using:
manual_configuration:
remote_table: [Artist]
column_mapping:
ArtistId: ArtistId
- table: [Artist]
array_relationships:
- name: Albums
using:
manual_configuration:
remote_table: [Album]
column_mapping:
ArtistId: ArtistId
- table: [Invoice]
array_relationships:
- name: InvoiceLines
using:
manual_configuration:
remote_table: [InvoiceLine]
column_mapping:
InvoiceId: InvoiceId
- table: [InvoiceLine]
object_relationships:
- name: Invoice
using:
manual_configuration:
remote_table: [Invoice]
column_mapping:
InvoiceId: InvoiceId
configuration: {}
|]
--------------------------------------------------------------------------------
tests :: Context.Options -> SpecWith (TestEnvironment, DataConnector.MockAgentEnvironment)
tests opts = describe "Aggregate Query Tests" $ do
it "works with multiple nodes fields and through array relations" $
DataConnector.runMockedTest opts $
let required =
DataConnector.TestCaseRequired
{ _givenRequired =
let response =
[ [ ("ArtistIds_Id", API.mkColumnFieldValue $ Aeson.Number 1),
("ArtistNames_Name", API.mkColumnFieldValue $ Aeson.String "AC/DC"),
( "nodes_Albums",
API.mkRelationshipFieldValue $
rowsResponse
[ [("nodes_Title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")],
[("nodes_Title", API.mkColumnFieldValue $ Aeson.String "Let There Be Rock")]
]
)
]
]
in DataConnector.chinookMock {DataConnector._queryResponse = \_ -> rowsResponse response},
_whenRequestRequired =
[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 (DataConnector.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 $
KM.fromList
[ ("ArtistIds_Id", API.ColumnField (API.ColumnName "ArtistId")),
("ArtistNames_Name", API.ColumnField (API.ColumnName "Name")),
( "nodes_Albums",
API.RelField
( API.RelationshipField
(API.RelationshipName "Albums")
API.Query
{ _qFields =
Just $
KM.fromList
[ ("nodes_Title", API.ColumnField (API.ColumnName "Title"))
],
_qAggregates = Nothing,
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere = Just (API.And []),
_qOrderBy = Nothing
}
)
)
],
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere = Just (API.And []),
_qOrderBy = Nothing
}
}
)
}
it "works with multiple aggregate fields and through array relations" $
DataConnector.runMockedTest opts $
let required =
DataConnector.TestCaseRequired
{ _givenRequired =
let aggregates =
[ ("counts_count", API.Number 2),
("counts_uniqueBillingCountries", API.Number 2),
("ids_minimum_Id", API.Number 1),
("ids_max_InvoiceId", API.Number 2)
]
rows =
[ [ ( "nodes_Lines",
API.mkRelationshipFieldValue $
aggregatesResponse
[ ("aggregate_count", API.Number 2)
]
)
],
[ ( "nodes_Lines",
API.mkRelationshipFieldValue $
aggregatesResponse
[ ("aggregate_count", API.Number 4)
]
)
]
]
in DataConnector.chinookMock {DataConnector._queryResponse = \_ -> 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
}
}
}
}
}
|],
_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 (DataConnector.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 $
KM.fromList
[ ( "nodes_Lines",
API.RelField
( API.RelationshipField
(API.RelationshipName "InvoiceLines")
API.Query
{ _qFields = Nothing,
_qAggregates =
Just $
KM.fromList
[("aggregate_count", API.StarCount)],
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere = Just (API.And []),
_qOrderBy = Nothing
}
)
)
],
_qAggregates =
Just $
KM.fromList
[ ("counts_count", API.StarCount),
("counts_uniqueBillingCountries", API.ColumnCount (API.ColumnCountAggregate (API.ColumnName "BillingCountry") True)),
("ids_minimum_Id", API.SingleColumn (API.SingleColumnAggregate API.Min (API.ColumnName "InvoiceId"))),
("ids_max_InvoiceId", API.SingleColumn (API.SingleColumnAggregate API.Max (API.ColumnName "InvoiceId")))
],
_qLimit = Just 2,
_qOffset = Nothing,
_qWhere = Just (API.And []),
_qOrderBy = Nothing
}
}
)
}
rowsResponse :: [[(Aeson.Key, API.FieldValue)]] -> API.QueryResponse
rowsResponse rows = API.QueryResponse (Just $ KM.fromList <$> rows) Nothing
aggregatesResponse :: [(Aeson.Key, API.Value)] -> API.QueryResponse
aggregatesResponse aggregates = API.QueryResponse Nothing (Just $ KM.fromList aggregates)
aggregatesAndRowsResponse :: [(Aeson.Key, API.Value)] -> [[(Aeson.Key, API.FieldValue)]] -> API.QueryResponse
aggregatesAndRowsResponse aggregates rows = API.QueryResponse (Just $ KM.fromList <$> rows) (Just $ KM.fromList aggregates)