Use Dataset Clones for all SQLite tests

[GDC-718]: https://hasurahq.atlassian.net/browse/GDC-718?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7743
GitOrigin-RevId: 6c3577c1d4ffd2212a72b6e1a24e0e384f2db046
This commit is contained in:
Daniel Chambers 2023-02-02 15:26:29 +11:00 committed by hasura-bot
parent 1fe7fe43b8
commit b012f2ebc7
23 changed files with 344 additions and 252 deletions

View File

@ -25,12 +25,13 @@ services:
ports:
- "65007:8100"
volumes:
- "./sqlite/test/db.chinook.sqlite:/db.chinook.sqlite"
- "./sqlite/test/db.sqlite:/db.sqlite"
- "./sqlite/dataset_templates:/app/sqlite/dataset_templates"
environment:
METRICS: y
PRETTY_PRINT_LOGS: y
LOG_LEVEL: debug
DATASETS: y
DATASET_DELETE: y
healthcheck:
test:
- CMD

View File

@ -69,7 +69,7 @@ services:
depends_on:
- reference-agent
image: "hasura/dc-agent-tests:${HASURA_VERSION}"
command: ["tests-dc-api", 'test', '-u', 'http://reference-agent:8100', '-s', '{}']
command: ["tests-dc-api", 'test', '--agent-base-url', 'http://reference-agent:8100']
restart: on-failure
swagger-ui:

View File

@ -1,4 +1,4 @@
node_modules
dist
./*.sqlite
./dataset_clones/
dataset_clones/

View File

@ -66,7 +66,7 @@ function mkTemplatePath(name: string): string {
if(name != safeName) {
throw(Error(`Template name ${name} is not valid.`));
}
return path.join(DATASET_TEMPLATES, safeName);
return path.join(DATASET_TEMPLATES, safeName + ".sqlite");
}
function mkClonePath(name: string): string {
@ -75,5 +75,5 @@ function mkClonePath(name: string): string {
if(name != safeName) {
throw(Error(`Template name ${name} is not valid.`));
}
return path.join(DATASET_CLONES, safeName);
return path.join(DATASET_CLONES, safeName + ".sqlite");
}

View File

@ -79,7 +79,7 @@ addSource =
name: myfoobar
replace_configuration: false
configuration:
db: /db.chinook.sqlite
value: {}
|]
listSourceKinds :: Value

View File

@ -13,13 +13,11 @@ import Data.Aeson qualified as Aeson
import Data.Aeson.Lens (key, _Array)
import Data.List.NonEmpty qualified as NE
import Data.Vector qualified as Vector
import Harness.Backend.DataConnector.Chinook qualified as Chinook
import Harness.Backend.DataConnector.Chinook.Reference qualified as Reference
import Harness.Backend.DataConnector.Chinook.Sqlite qualified as Sqlite
import Harness.GraphqlEngine qualified as GraphqlEngine
import Harness.Quoter.Graphql (graphql)
import Harness.Quoter.Yaml (yaml)
import Harness.Test.BackendType (BackendTypeConfig)
import Harness.Test.Fixture qualified as Fixture
import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
import Harness.TestEnvironment qualified as TE
@ -33,67 +31,14 @@ spec :: SpecWith GlobalTestEnvironment
spec =
Fixture.runWithLocalTestEnvironment
( NE.fromList
[ (Fixture.fixture $ Fixture.Backend Reference.backendTypeMetadata)
{ Fixture.setupTeardown = \(testEnvironment, _) ->
[ Chinook.setupAction (sourceMetadata Reference.backendTypeMetadata Reference.sourceConfiguration) Reference.agentConfig testEnvironment
]
},
(Fixture.fixture $ Fixture.Backend Sqlite.backendTypeMetadata)
{ Fixture.setupTeardown = \(testEnvironment, _) ->
[ Chinook.setupAction (sourceMetadata Sqlite.backendTypeMetadata Sqlite.sourceConfiguration) Sqlite.agentConfig testEnvironment
]
}
[ Reference.chinookFixture,
Sqlite.chinookFixture
]
)
tests
--------------------------------------------------------------------------------
sourceMetadata :: BackendTypeConfig -> Aeson.Value -> Aeson.Value
sourceMetadata backendTypeMetadata config =
let source = Fixture.backendSourceName backendTypeMetadata
backendTypeString = Fixture.backendTypeString backendTypeMetadata
in [yaml|
name : *source
kind: *backendTypeString
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: *config
|]
--------------------------------------------------------------------------------
tests :: Fixture.Options -> SpecWith (TestEnvironment, a)
tests opts = describe "Aggregate Query Tests" $ do
nodeTests opts

View File

@ -22,7 +22,7 @@ import Data.Aeson.KeyMap qualified as KM
import Data.Aeson.Lens
import Data.List.NonEmpty qualified as NE
import Data.Vector qualified as Vector
import Harness.Backend.DataConnector.Chinook (ChinookTestEnv)
import Harness.Backend.DataConnector.Chinook (ChinookTestEnv, NameFormatting (..))
import Harness.Backend.DataConnector.Chinook qualified as Chinook
import Harness.Backend.DataConnector.Chinook.Reference qualified as Reference
import Harness.Backend.DataConnector.Chinook.Sqlite qualified as Sqlite
@ -47,15 +47,15 @@ spec = do
Fixture.runWithLocalTestEnvironmentSingleSetup
( NE.fromList
[ Fixture
{ name = Fixture.Backend Reference.backendTypeMetadata,
mkLocalTestEnvironment = \_ -> pure $ Chinook.ChinookTestEnv Reference.sourceConfiguration id id id,
{ name = Fixture.Backend Reference.backendTypeConfig,
mkLocalTestEnvironment = Reference.mkChinookStaticTestEnvironment,
setupTeardown = \(testEnv, _localEnv) ->
[emptySetupAction testEnv],
customOptions = Nothing
},
Fixture
{ name = Fixture.Backend Sqlite.backendTypeMetadata,
mkLocalTestEnvironment = \_ -> pure $ Chinook.ChinookTestEnv Sqlite.sourceConfiguration id id Sqlite.formatForeignKeyName,
{ name = Fixture.Backend Sqlite.backendTypeConfig,
mkLocalTestEnvironment = Sqlite.mkChinookCloneTestEnvironment,
setupTeardown = \(testEnv, _localEnv) ->
[emptySetupAction testEnv],
customOptions = Nothing
@ -66,20 +66,8 @@ spec = do
Fixture.runWithLocalTestEnvironmentSingleSetup
( NE.fromList
[ Fixture
{ name = Fixture.Backend Reference.backendTypeMetadata,
mkLocalTestEnvironment = \_ -> pure $ Chinook.ChinookTestEnv Reference.sourceConfiguration id id id,
setupTeardown = \(testEnv, _localEnv) ->
[Chinook.setupAction Chinook.referenceSourceConfig Reference.agentConfig testEnv],
customOptions = Nothing
},
Fixture
{ name = Fixture.Backend Sqlite.backendTypeMetadata,
mkLocalTestEnvironment = \_ -> pure $ Chinook.ChinookTestEnv Sqlite.sourceConfiguration id id Sqlite.formatForeignKeyName,
setupTeardown = \(testEnv, _localEnv) ->
[Chinook.setupAction Chinook.sqliteSourceConfig Sqlite.agentConfig testEnv],
customOptions = Nothing
}
[ Reference.chinookFixture,
Sqlite.chinookFixture
]
)
schemaInspectionTests
@ -89,7 +77,7 @@ spec = do
schemaInspectionTests :: Fixture.Options -> SpecWith (TestEnvironment, ChinookTestEnv)
schemaInspectionTests opts = describe "Schema and Source Inspection" $ do
describe "get_source_tables" $ do
it "success" $ \(testEnvironment, Chinook.ChinookTestEnv {..}) -> do
it "success" $ \(testEnvironment, Chinook.ChinookTestEnv {nameFormatting = NameFormatting {..}}) -> do
let sortYamlArray :: J.Value -> IO J.Value
sortYamlArray (J.Array a) = pure $ J.Array (Vector.fromList (sort (Vector.toList a)))
sortYamlArray _ = fail "Should return Array"
@ -97,17 +85,17 @@ schemaInspectionTests opts = describe "Schema and Source Inspection" $ do
case BackendType.backendSourceName <$> getBackendTypeConfig testEnvironment of
Nothing -> pendingWith "Backend not found for testEnvironment"
Just sourceString -> do
let album = formatTableName ["Album"]
artist = formatTableName ["Artist"]
customer = formatTableName ["Customer"]
employee = formatTableName ["Employee"]
genre = formatTableName ["Genre"]
invoice = formatTableName ["Invoice"]
invoiceLine = formatTableName ["InvoiceLine"]
mediaType = formatTableName ["MediaType"]
playlist = formatTableName ["Playlist"]
playlistTrack = formatTableName ["PlaylistTrack"]
track = formatTableName ["Track"]
let album = _nfFormatTableName ["Album"]
artist = _nfFormatTableName ["Artist"]
customer = _nfFormatTableName ["Customer"]
employee = _nfFormatTableName ["Employee"]
genre = _nfFormatTableName ["Genre"]
invoice = _nfFormatTableName ["Invoice"]
invoiceLine = _nfFormatTableName ["InvoiceLine"]
mediaType = _nfFormatTableName ["MediaType"]
playlist = _nfFormatTableName ["Playlist"]
playlistTrack = _nfFormatTableName ["PlaylistTrack"]
track = _nfFormatTableName ["Track"]
shouldReturnYamlF
sortYamlArray
opts
@ -134,7 +122,7 @@ schemaInspectionTests opts = describe "Schema and Source Inspection" $ do
|]
describe "get_table_info" $ do
it "success" $ \(testEnvironment@TestEnvironment {}, Chinook.ChinookTestEnv {..}) -> do
it "success" $ \(testEnvironment@TestEnvironment {}, Chinook.ChinookTestEnv {nameFormatting = NameFormatting {..}}) -> do
let removeDescriptions (J.Object o) = J.Object (KM.delete "description" (removeDescriptions <$> o))
removeDescriptions (J.Array a) = J.Array (removeDescriptions <$> a)
removeDescriptions x = x
@ -153,12 +141,12 @@ schemaInspectionTests opts = describe "Schema and Source Inspection" $ do
case BackendType.backendSourceName <$> getBackendTypeConfig testEnvironment of
Nothing -> pendingWith "Backend not found for testEnvironment"
Just sourceString -> do
let album = formatTableName ["Album"]
artist = formatTableName ["Artist"]
albumId = formatColumnName "AlbumId"
artistId = formatColumnName "ArtistId"
title = formatColumnName "Title"
artistForeignKeys = formatForeignKeyName "Artist"
let album = _nfFormatTableName ["Album"]
artist = _nfFormatTableName ["Artist"]
albumId = _nfFormatColumnName "AlbumId"
artistId = _nfFormatColumnName "ArtistId"
title = _nfFormatColumnName "Title"
artistForeignKeys = _nfFormatForeignKeyName "Artist"
shouldReturnYamlF
(pure . removeDescriptions)
opts
@ -341,12 +329,12 @@ schemaCrudTests opts = describe "A series of actions to setup and teardown a sou
|]
describe "<kind>_track_table" $ do
it "success" $ \(testEnvironment, Chinook.ChinookTestEnv {..}) -> do
it "success" $ \(testEnvironment, Chinook.ChinookTestEnv {nameFormatting = NameFormatting {..}}) -> do
case (backendTypeString &&& backendSourceName) <$> TestEnvironment.getBackendTypeConfig testEnvironment of
Nothing -> pendingWith "Backend Type not found in testEnvironment"
Just (backendType, sourceName) -> do
let actionType = backendType <> "_track_table"
album = formatTableName ["Album"]
album = _nfFormatTableName ["Album"]
shouldReturnYaml
opts
( GraphqlEngine.postMetadata
@ -363,15 +351,15 @@ schemaCrudTests opts = describe "A series of actions to setup and teardown a sou
|]
describe "<kind>_create_object_relationship" $ do
it "success" $ \(testEnvironment@TestEnvironment {}, Chinook.ChinookTestEnv {..}) -> do
it "success" $ \(testEnvironment@TestEnvironment {}, Chinook.ChinookTestEnv {nameFormatting = NameFormatting {..}}) -> do
let foreignKeySupport = (getBackendTypeConfig testEnvironment >>= BackendType.parseCapabilities) <&> API._dscSupportsForeignKeys . API._cDataSchema
case (backendTypeString &&& backendSourceName) <$> getBackendTypeConfig testEnvironment of
Nothing -> pendingWith "Backend Type not found in testEnvironment"
Just (backendType, sourceName) -> do
let actionType = backendType <> "_track_table"
artistTable = formatTableName ["Artist"]
albumTable = formatTableName ["Album"]
artistId = formatColumnName "ArtistId"
artistTable = _nfFormatTableName ["Artist"]
albumTable = _nfFormatTableName ["Album"]
artistId = _nfFormatColumnName "ArtistId"
GraphqlEngine.postMetadata_
testEnvironment
[yaml|
@ -406,7 +394,7 @@ schemaCrudTests opts = describe "A series of actions to setup and teardown a sou
|]
describe "<kind>_create_array_relationship" $ do
it "success" $ \(testEnvironment@TestEnvironment {}, Chinook.ChinookTestEnv {..}) -> do
it "success" $ \(testEnvironment@TestEnvironment {}, Chinook.ChinookTestEnv {nameFormatting = NameFormatting {..}}) -> do
let foreignKeySupport = (getBackendTypeConfig testEnvironment >>= BackendType.parseCapabilities) <&> API._dscSupportsForeignKeys . API._cDataSchema
when
(foreignKeySupport == Just False)
@ -415,9 +403,9 @@ schemaCrudTests opts = describe "A series of actions to setup and teardown a sou
Nothing -> pendingWith "Backend Type not found in testEnvironment"
Just (backendType, sourceName) -> do
let actionType = backendType <> "_create_array_relationship"
albumTable = formatTableName ["Album"]
artistTable = formatTableName ["Artist"]
artistId = formatColumnName "ArtistId"
albumTable = _nfFormatTableName ["Album"]
artistTable = _nfFormatTableName ["Artist"]
artistId = _nfFormatColumnName "ArtistId"
shouldReturnYaml
opts
( GraphqlEngine.postMetadata
@ -440,14 +428,14 @@ schemaCrudTests opts = describe "A series of actions to setup and teardown a sou
|]
describe "export_metadata" $ do
it "produces the expected metadata structure" $ \(testEnvironment@TestEnvironment {}, Chinook.ChinookTestEnv {..}) -> do
it "produces the expected metadata structure" $ \(testEnvironment@TestEnvironment {}, Chinook.ChinookTestEnv {nameFormatting = NameFormatting {..}, ..}) -> do
case ((fold . backendServerUrl) &&& backendTypeString &&& backendSourceName) <$> TestEnvironment.getBackendTypeConfig testEnvironment of
Nothing -> pendingWith "Backend Type not found in testEnvironment"
Just (agentUrl, (backendType, sourceName)) -> do
let foreignKeySupport = (getBackendTypeConfig testEnvironment >>= BackendType.parseCapabilities) <&> API._dscSupportsForeignKeys . API._cDataSchema
albumTable = formatTableName ["Album"]
artistTable = formatTableName ["Artist"]
artistId = formatColumnName "ArtistId"
albumTable = _nfFormatTableName ["Album"]
artistTable = _nfFormatTableName ["Artist"]
artistId = _nfFormatColumnName "ArtistId"
shouldReturnYaml
opts
( GraphqlEngine.postMetadata
@ -500,7 +488,7 @@ schemaCrudTests opts = describe "A series of actions to setup and teardown a sou
|]
describe "<kind>_drop_relationship" $ do
it "success" $ \(testEnvironment@TestEnvironment {}, Chinook.ChinookTestEnv {..}) -> do
it "success" $ \(testEnvironment@TestEnvironment {}, Chinook.ChinookTestEnv {nameFormatting = NameFormatting {..}}) -> do
let foreignKeySupport = (getBackendTypeConfig testEnvironment >>= BackendType.parseCapabilities) <&> API._dscSupportsForeignKeys . API._cDataSchema
when
(foreignKeySupport == Just False)
@ -509,7 +497,7 @@ schemaCrudTests opts = describe "A series of actions to setup and teardown a sou
Nothing -> pendingWith "Backend Type not found in testEnvironment"
Just (backendType, sourceName) -> do
let actionType = backendType <> "_drop_relationship"
artistTable = formatTableName ["Artist"]
artistTable = _nfFormatTableName ["Artist"]
shouldReturnYaml
opts
( GraphqlEngine.postMetadata
@ -527,7 +515,7 @@ schemaCrudTests opts = describe "A series of actions to setup and teardown a sou
|]
describe "<kind>_untrack_table" $ do
it "success" $ \(testEnvironment@TestEnvironment {}, Chinook.ChinookTestEnv {..}) -> do
it "success" $ \(testEnvironment@TestEnvironment {}, Chinook.ChinookTestEnv {nameFormatting = NameFormatting {..}}) -> do
let foreignKeySupport = (getBackendTypeConfig testEnvironment >>= BackendType.parseCapabilities) <&> API._dscSupportsForeignKeys . API._cDataSchema
when
(foreignKeySupport == Just False)
@ -536,7 +524,7 @@ schemaCrudTests opts = describe "A series of actions to setup and teardown a sou
Nothing -> pendingWith "Backend Type not found in testEnvironment"
Just (backendType, sourceName) -> do
let actionType = backendType <> "_untrack_table"
artistTable = formatTableName ["Artist"]
artistTable = _nfFormatTableName ["Artist"]
shouldReturnYaml
opts
( GraphqlEngine.postMetadata

View File

@ -25,7 +25,6 @@ where
--------------------------------------------------------------------------------
import Data.List.NonEmpty qualified as NE
import Harness.Backend.DataConnector.Chinook qualified as Chinook
import Harness.Backend.DataConnector.Chinook.Reference qualified as Reference
import Harness.Backend.DataConnector.Chinook.Sqlite qualified as Sqlite
import Harness.GraphqlEngine qualified as GraphqlEngine
@ -46,14 +45,8 @@ spec :: SpecWith GlobalTestEnvironment
spec =
Fixture.runWithLocalTestEnvironment
( NE.fromList
[ (Fixture.fixture $ Fixture.Backend Reference.backendTypeMetadata)
{ Fixture.setupTeardown = \(testEnv, _) ->
[Chinook.setupAction Chinook.referenceSourceConfig Reference.agentConfig testEnv]
},
(Fixture.fixture $ Fixture.Backend Sqlite.backendTypeMetadata)
{ Fixture.setupTeardown = \(testEnv, _) ->
[Chinook.setupAction Chinook.sqliteSourceConfig Sqlite.agentConfig testEnv]
}
[ Reference.chinookFixture,
Sqlite.chinookFixture
]
)
tests
@ -169,7 +162,7 @@ paginationTests :: Fixture.Options -> SpecWith (TestEnvironment, a)
paginationTests opts =
describe "Pagination" $ do
it "works with pagination" $ \(testEnvironment, _) -> do
-- NOTE: We order by in this pagination test to ensure that the rows are ordered correctly (which they are not in db.chinook.sqlite)
-- NOTE: We order by in this pagination test to ensure that the rows are ordered correctly (which they are not in Sqlite Chinook)
shouldReturnYaml
opts
( GraphqlEngine.postGraphql

View File

@ -24,14 +24,8 @@ spec :: SpecWith GlobalTestEnvironment
spec =
Fixture.runWithLocalTestEnvironment
( NE.fromList
[ (Fixture.fixture $ Fixture.Backend Reference.backendTypeMetadata)
{ Fixture.setupTeardown = \(testEnv, _) ->
[Chinook.setupAction Chinook.referenceSourceConfig Reference.agentConfig testEnv]
},
(Fixture.fixture $ Fixture.Backend Sqlite.backendTypeMetadata)
{ Fixture.setupTeardown = \(testEnv, _) ->
[Chinook.setupAction Chinook.sqliteSourceConfig Sqlite.agentConfig testEnv]
}
[ Reference.chinookFixture,
Sqlite.chinookFixture
]
)
tests

View File

@ -10,6 +10,7 @@ module Command
SandwichArguments (..),
TestConfig (..),
AgentOptions (..),
AgentConfig (..),
NameCasing (..),
ExportDataConfig (..),
ExportFormat (..),
@ -55,9 +56,13 @@ data SensitiveOutputHandling
data AgentOptions = AgentOptions
{ _aoAgentBaseUrl :: BaseUrl,
_aoAgentConfig :: Maybe API.Config
_aoAgentConfig :: AgentConfig
}
data AgentConfig
= ManualConfig API.Config
| DatasetConfig (Maybe API.Config)
data NameCasing
= PascalCase
| Lowercase
@ -181,15 +186,23 @@ agentOptionsParser =
<> metavar "URL"
<> help "The base URL of the Data Connector agent to be tested"
)
<*> optional
( option
configValue
( long "agent-config"
<> short 's'
<> metavar "JSON"
<> help "The configuration JSON to be sent to the agent via the X-Hasura-DataConnector-Config header. If omitted, datasets will be used to load test data and provide this configuration dynamically"
)
)
<*> ( ManualConfig
<$> option
configValue
( long "agent-config"
<> metavar "JSON"
<> help "The configuration JSON to be sent to the agent via the X-Hasura-DataConnector-Config header. If omitted, datasets will be used to load test data and provide this configuration dynamically"
)
<|> DatasetConfig
<$> optional
( option
configValue
( long "merge-agent-config"
<> metavar "JSON"
<> help "Datasets will be used to load test data and provide configuration JSON to be sent to the agent via the X-Hasura-DataConnector-Config header. This config will be merged with the dataset-provided config before being sent to the agent."
)
)
)
exportDataConfigParser :: Parser ExportDataConfig
exportDataConfigParser =

View File

@ -2,7 +2,7 @@ module Main (main) where
--------------------------------------------------------------------------------
import Command (AgentOptions (..), Command (..), SandwichArguments (..), TestOptions (..), parseCommandLine)
import Command (AgentConfig (..), AgentOptions (..), Command (..), SandwichArguments (..), TestOptions (..), parseCommandLine)
import Control.Exception (bracket)
import Data.Aeson.Text (encodeToLazyText)
import Data.Foldable (for_)
@ -12,7 +12,7 @@ import Hasura.Backends.DataConnector.API (openApiSchema)
import Hasura.Backends.DataConnector.API qualified as API
import Servant.Client ((//))
import System.Environment (withArgs)
import Test.AgentAPI (guardCapabilitiesResponse, guardSchemaResponse)
import Test.AgentAPI (guardCapabilitiesResponse, guardSchemaResponse, mergeAgentConfig)
import Test.AgentClient (AgentIOClient (..), introduceAgentClient, mkAgentClientConfig, mkAgentIOClient)
import Test.AgentDatasets (DatasetCloneInfo (..), chinookTemplate, createClone, deleteClone, usesDataset)
import Test.AgentTestContext (AgentTestContext (..), introduceAgentTestContext)
@ -46,18 +46,18 @@ tests testData capabilitiesResponse@API.CapabilitiesResponse {..} = do
for_ (API._cMetrics _crCapabilities) \m -> Test.Specs.MetricsSpec.spec m
for_ (API._cExplain _crCapabilities) \_ -> Test.Specs.ExplainSpec.spec testData _crCapabilities
getChinookSchema :: API.Capabilities -> Maybe API.Config -> AgentIOClient -> IO API.SchemaResponse
getChinookSchema :: API.Capabilities -> AgentConfig -> AgentIOClient -> IO API.SchemaResponse
getChinookSchema API.Capabilities {..} manuallyProvidedConfig (AgentIOClient agentClient) = do
case manuallyProvidedConfig of
Just config -> (agentClient // API._schema) testSourceName config >>= guardSchemaResponse
Nothing ->
ManualConfig config -> (agentClient // API._schema) testSourceName config >>= guardSchemaResponse
DatasetConfig mergeConfig ->
if isJust _cDatasets
then
bracket
(createClone agentClient chinookTemplate)
(deleteClone agentClient)
( \DatasetCloneInfo {..} ->
(agentClient // API._schema) testSourceName _dciAgentConfig >>= guardSchemaResponse
(agentClient // API._schema) testSourceName (mergeAgentConfig _dciAgentConfig mergeConfig) >>= guardSchemaResponse
)
else fail $ "The agent does not support datasets, therefore an agent configuration must be provided on the command line (--agent-config)"

View File

@ -9,20 +9,22 @@ The executable also has the ability to export the OpenAPI spec of the Data Conne
### Running Tests
First, start your Data Connector agent and ensure it is populated with the [Chinook data set](https://github.com/lerocha/chinook-database/). For example, you could start the Reference Agent by following the instructions in [its README](../../dc-agents/reference/README.md).
To run the tests against the agent (for example), you must specify the agent's URL on the command line (`-u`), as well as the agent's configuration JSON (`-s`, sent in the `X-Hasura-DataConnector-Config` header):
To run the tests against the agent (for example), you must specify the agent's URL on the command line (`--agent-base-url`), as well as the agent's configuration JSON (`--agent-config`, sent in the `X-Hasura-DataConnector-Config` header):
```
cabal run test:tests-dc-api -- test -u "http://localhost:8100" -s '{}'
cabal run test:tests-dc-api -- test --agent-base-url "http://localhost:8100" --agent-config '{}'
```
The test suite will discover what capabilities the agent has by querying it. It will then tailor the tests that it will run to match only those capabilities that the agent has said it supports.
If your agent supports the datasets capability, you can omit the `--agent-config` argument and the test suite will clone the Chinook dataset template on the agent to run its test against. If you need to specify some additional configuration to be added to the cloned dataset's configuration, you can specify it using `--merge-agent-config`.
The test suite is implemented using the [Sandwich](https://codedownio.github.io/sandwich/) test framework. The standard Sandwich command line arguments can be passed by suffixing your command line with `sandwich` and then all following args will be passed to Sandwich.
For example, to run the Terminal UI mode of Sandwich, you could run:
```
cabal run test:tests-dc-api -- test -u "http://localhost:8100" -s '{}' sandwich --tui
cabal run test:tests-dc-api -- test --agent-base-url "http://localhost:8100" --agent-config '{}' sandwich --tui
```
By default Sandwich will write test results into a `test_runs` folder. Every test has a folder that will contain debug information, for example:

View File

@ -21,12 +21,15 @@ module Test.AgentAPI
explain,
getMetrics,
getSourceNameAndConfig,
mergeAgentConfig,
)
where
import Command (AgentConfig (..))
import Control.Monad.Catch (MonadThrow)
import Control.Monad.IO.Class (MonadIO)
import Control.Monad.Reader (MonadReader)
import Data.Aeson.KeyMap qualified as KeyMap
import Data.Maybe (isJust)
import Data.Text (Text)
import Hasura.Backends.DataConnector.API qualified as API
@ -108,13 +111,20 @@ supportsDatasets = isJust . API._cDatasets . API._crCapabilities
getSourceNameAndConfig :: (HasAgentTestContext context, HasDatasetContext context, MonadReader context m, MonadThrow m) => m (API.SourceName, API.Config)
getSourceNameAndConfig = do
AgentTestContext {..} <- getAgentTestContext
case _atcManualConfig of
Just config -> pure (_atcSourceName, config)
Nothing ->
case _atcAgentConfig of
ManualConfig config -> pure (_atcSourceName, config)
DatasetConfig mergeConfig ->
if supportsDatasets _atcCapabilitiesResponse
then do
cloneInfo <- _dcClone <$> getDatasetContext
case cloneInfo of
Just DatasetCloneInfo {..} -> pure (_atcSourceName, _dciAgentConfig)
Just DatasetCloneInfo {..} ->
let mergedConfig = mergeAgentConfig _dciAgentConfig mergeConfig
in pure (_atcSourceName, mergedConfig)
Nothing -> expectationFailure "Expected a dataset clone to have been created, because the agent supports datasets, but one wasn't"
else expectationFailure "An agent configuration is required to be provided if the agent does not support datasets"
mergeAgentConfig :: API.Config -> Maybe API.Config -> API.Config
mergeAgentConfig (API.Config configA) mergeConfig =
let configB = maybe mempty API.unConfig mergeConfig
in API.Config $ KeyMap.union configA configB

View File

@ -8,6 +8,7 @@ module Test.AgentTestContext
)
where
import Command (AgentConfig)
import Control.Monad.Reader.Class (MonadReader)
import GHC.Stack (HasCallStack)
import Hasura.Backends.DataConnector.API qualified as API
@ -20,7 +21,7 @@ data AgentTestContext = AgentTestContext
_atcCapabilitiesResponse :: API.CapabilitiesResponse,
-- | This is the configuration passed by the user on the command line which will
-- be used in preference to any dataset clone's config if it is specified
_atcManualConfig :: Maybe API.Config
_atcAgentConfig :: AgentConfig
}
introduceAgentTestContext :: forall context m. (Monad m) => AgentTestContext -> SpecFree (LabelValue "agent-test-context" AgentTestContext :> context) m () -> SpecFree context m ()

View File

@ -3,24 +3,27 @@
-- | Chinook Based Agent Fixtures used in DataConnector specific
-- Specs.
module Harness.Backend.DataConnector.Chinook
( setupAction,
referenceSourceConfig,
sqliteSourceConfig,
( ChinookTestEnv (..),
NameFormatting (..),
mkChinookCloneTestEnvironment,
mkChinookStaticTestEnvironment,
setupChinookSourceAction,
setupCustomSourceAction,
testRoleName,
ChinookTestEnv (..),
)
where
--------------------------------------------------------------------------------
import Control.Monad.Managed (Managed)
import Data.Aeson qualified as Aeson
import Data.ByteString (ByteString)
import Harness.Backend.DataConnector.Chinook.Reference qualified as Reference
import Harness.Backend.DataConnector.Chinook.Sqlite qualified as Sqlite
import Harness.DataConnectorAgent (createManagedClone)
import Harness.GraphqlEngine qualified as GraphqlEngine
import Harness.Quoter.Yaml (yaml)
import Harness.Test.Fixture qualified as Fixture
import Harness.TestEnvironment (TestEnvironment)
import Harness.TestEnvironment (TestEnvironment, getBackendTypeConfig)
import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude
--------------------------------------------------------------------------------
@ -28,26 +31,78 @@ import Hasura.Prelude
data ChinookTestEnv = ChinookTestEnv
{ -- | Default configuration JSON for the backend source.
backendSourceConfig :: Aeson.Value,
-- | Can be used to apply custom formatting to table names. Eg.,
-- adjusting the casing.
formatTableName :: [Text] -> [Text],
-- | Can be used to apply custom formatting to column names. Eg.,
-- adjusting the casing.
formatColumnName :: Text -> Text,
formatForeignKeyName :: Text -> Text
-- | Default configuration for the backend config that sets the agent configuration
backendAgentConfig :: Aeson.Value,
-- | Name formatting functions to correct for backend-specific naming rules
nameFormatting :: NameFormatting
}
setupAction :: Aeson.Value -> Aeson.Value -> TestEnvironment -> Fixture.SetupAction
setupAction sourceMetadata backendConfig' testEnv =
data NameFormatting = NameFormatting
{ -- | Can be used to apply custom formatting to table names. Eg.,
-- adjusting the casing.
_nfFormatTableName :: [Text] -> [Text],
-- | Can be used to apply custom formatting to column names. Eg.,
-- adjusting the casing.
_nfFormatColumnName :: Text -> Text,
_nfFormatForeignKeyName :: Text -> Text
}
--------------------------------------------------------------------------------
-- | Create a test environment that uses agent dataset cloning to clone a copy of the Chinook
-- DB for the test and use that as the source config configured in HGE.
-- This should be used with agents that support datasets.
mkChinookCloneTestEnvironment :: NameFormatting -> TestEnvironment -> Managed ChinookTestEnv
mkChinookCloneTestEnvironment nameFormatting testEnv = do
backendTypeConfig <- getBackendTypeConfig testEnv `onNothing` fail "Unable to find backend type config in this test environment"
agentUrl <- Fixture.backendServerUrl backendTypeConfig `onNothing` fail ("Backend " <> show (Fixture.backendType backendTypeConfig) <> " does not have a server url")
cloneConfig <- API._dccrConfig <$> createManagedClone agentUrl testEnv (API.DatasetTemplateName "Chinook")
let agentBackendConfig = mkAgentBackendConfig backendTypeConfig
let cloneConfigValue = Aeson.Object $ API.unConfig cloneConfig
let sourceConfig =
[yaml|
value: *cloneConfigValue
template:
timeout:
|]
pure $ ChinookTestEnv sourceConfig agentBackendConfig nameFormatting
-- | Create a test environment that uses the source config specified to connect to a specific DB on the agent
-- that contains the Chinook dataset.
-- This should be used with agents that do not support datasets.
mkChinookStaticTestEnvironment :: NameFormatting -> Aeson.Value -> TestEnvironment -> Managed ChinookTestEnv
mkChinookStaticTestEnvironment nameFormatting sourceConfig testEnv = do
backendTypeConfig <- getBackendTypeConfig testEnv `onNothing` fail "Unable to find backend type config in this test environment"
let agentBackendConfig = mkAgentBackendConfig backendTypeConfig
pure $ ChinookTestEnv sourceConfig agentBackendConfig nameFormatting
--------------------------------------------------------------------------------
-- | Sets up a source in HGE using the source returned by 'mkSourceMetadata'
setupCustomSourceAction ::
-- | Function that makes a source metadata, taking the 'BackendTypeConfig' and source's configuration as its input parameters
(Fixture.BackendTypeConfig -> Aeson.Value -> Aeson.Value) ->
(TestEnvironment, ChinookTestEnv) ->
Fixture.SetupAction
(setup sourceMetadata backendConfig' testEnv)
setupCustomSourceAction mkSourceMetadata testEnvs@(testEnv, _chinookTestEnv) =
Fixture.SetupAction
(setupCustomSource mkSourceMetadata testEnvs)
(const $ teardown testEnv)
-- | Setup the schema given source metadata and backend config.
setup :: Aeson.Value -> Aeson.Value -> TestEnvironment -> IO ()
setup sourceMetadata backendConfig' testEnvironment = do
-- | Sets up a source in HGE that contains tracked Chinook tables (see 'mkChinookSourceMetadata')
setupChinookSourceAction :: (TestEnvironment, ChinookTestEnv) -> Fixture.SetupAction
setupChinookSourceAction = setupCustomSourceAction mkChinookSourceMetadata
setupCustomSource ::
-- | Function that makes a source metadata, taking the 'BackendTypeConfig' and source's configuration as its input parameters
(Fixture.BackendTypeConfig -> Aeson.Value -> Aeson.Value) ->
(TestEnvironment, ChinookTestEnv) ->
IO ()
setupCustomSource mkSourceMetadata (testEnv, ChinookTestEnv {..}) = do
backendTypeConfig <- getBackendTypeConfig testEnv `onNothing` fail "Unable to find backend type config in this test environment"
let sourceMetadata = mkSourceMetadata backendTypeConfig backendSourceConfig
-- Clear and reconfigure the metadata
GraphqlEngine.setSource testEnvironment sourceMetadata (Just backendConfig')
GraphqlEngine.setSource testEnv sourceMetadata (Just backendAgentConfig)
-- | Teardown the schema and tracking in the most expected way.
teardown :: TestEnvironment -> IO ()
@ -56,15 +111,21 @@ teardown testEnvironment = do
--------------------------------------------------------------------------------
referenceSourceConfig :: Aeson.Value
referenceSourceConfig = mkChinookSourceConfig Reference.backendTypeMetadata Reference.sourceConfiguration
mkAgentBackendConfig :: Fixture.BackendTypeConfig -> Aeson.Value
mkAgentBackendConfig backendTypeConfig =
let backendType = Fixture.backendTypeString backendTypeConfig
uri = Fixture.backendServerUrl backendTypeConfig
in [yaml|
dataconnector:
*backendType:
uri: *uri
|]
sqliteSourceConfig :: Aeson.Value
sqliteSourceConfig = mkChinookSourceConfig Sqlite.backendTypeMetadata Sqlite.sourceConfiguration
--------------------------------------------------------------------------------
-- | Build a standard Chinook Source given an Agent specific @configuration@ field.
mkChinookSourceConfig :: Fixture.BackendTypeConfig -> Aeson.Value -> Aeson.Value
mkChinookSourceConfig backendTypeMetadata config =
mkChinookSourceMetadata :: Fixture.BackendTypeConfig -> Aeson.Value -> Aeson.Value
mkChinookSourceMetadata backendTypeMetadata config =
let source = Fixture.backendSourceName backendTypeMetadata
backendTypeString = Fixture.backendTypeString backendTypeMetadata
in [yaml|
@ -126,7 +187,7 @@ tables:
using:
manual_configuration:
remote_table: [Customer]
column_mapping:
column_mapping:
EmployeeId: SupportRepId
select_permissions:
- role: test-role
@ -161,7 +222,22 @@ tables:
SupportRep:
Country:
_ceq: [ "$", "Country" ]
- 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:
*config

View File

@ -4,23 +4,28 @@
--
-- NOTE: This module is intended to be imported qualified.
module Harness.Backend.DataConnector.Chinook.Reference
( agentConfig,
sourceConfiguration,
backendTypeMetadata,
( backendTypeConfig,
mkChinookStaticTestEnvironment,
chinookFixture,
)
where
--------------------------------------------------------------------------------
import Control.Monad.Managed (Managed)
import Data.Aeson qualified as Aeson
import Harness.Backend.DataConnector.Chinook (ChinookTestEnv, NameFormatting (..))
import Harness.Backend.DataConnector.Chinook qualified as Chinook
import Harness.Quoter.Yaml (yaml)
import Harness.Test.BackendType qualified as BackendType
import Harness.Test.Fixture (Fixture (..), FixtureName (..))
import Harness.TestEnvironment (TestEnvironment)
import Hasura.Prelude
--------------------------------------------------------------------------------
backendTypeMetadata :: BackendType.BackendTypeConfig
backendTypeMetadata =
backendTypeConfig :: BackendType.BackendTypeConfig
backendTypeConfig =
BackendType.BackendTypeConfig
{ backendType = BackendType.DataConnectorReference,
backendSourceName = "chinook_reference",
@ -75,21 +80,27 @@ backendTypeMetadata =
--------------------------------------------------------------------------------
-- | Reference Agent @backend_configs@ field.
agentConfig :: Aeson.Value
agentConfig =
let backendType = BackendType.backendTypeString backendTypeMetadata
in [yaml|
dataconnector:
*backendType:
uri: "http://127.0.0.1:65005/"
|]
mkChinookStaticTestEnvironment :: TestEnvironment -> Managed ChinookTestEnv
mkChinookStaticTestEnvironment = Chinook.mkChinookStaticTestEnvironment nameFormatting sourceConfiguration
nameFormatting :: NameFormatting
nameFormatting = NameFormatting id id id
-- | Reference Agent specific @sources@ entry @configuration@ field.
sourceConfiguration :: Aeson.Value
sourceConfiguration =
[yaml|
value: {}
template:
timeout:
|]
value: {}
template:
timeout:
|]
chinookFixture :: Fixture ChinookTestEnv
chinookFixture =
Fixture
{ name = Backend backendTypeConfig,
mkLocalTestEnvironment = mkChinookStaticTestEnvironment,
setupTeardown = \testEnvs ->
[Chinook.setupChinookSourceAction testEnvs],
customOptions = Nothing
}

View File

@ -4,24 +4,27 @@
--
-- NOTE: This module is intended to be imported qualified.
module Harness.Backend.DataConnector.Chinook.Sqlite
( agentConfig,
sourceConfiguration,
backendTypeMetadata,
formatForeignKeyName,
( backendTypeConfig,
mkChinookCloneTestEnvironment,
chinookFixture,
)
where
--------------------------------------------------------------------------------
import Data.Aeson qualified as Aeson
import Control.Monad.Managed (Managed)
import Harness.Backend.DataConnector.Chinook (ChinookTestEnv, NameFormatting (..))
import Harness.Backend.DataConnector.Chinook qualified as Chinook
import Harness.Quoter.Yaml (yaml)
import Harness.Test.BackendType qualified as BackendType
import Harness.Test.Fixture (Fixture (..), FixtureName (..))
import Harness.TestEnvironment
import Hasura.Prelude
--------------------------------------------------------------------------------
backendTypeMetadata :: BackendType.BackendTypeConfig
backendTypeMetadata =
backendTypeConfig :: BackendType.BackendTypeConfig
backendTypeConfig =
BackendType.BackendTypeConfig
{ backendType = BackendType.DataConnectorSqlite,
backendSourceName = "chinook_sqlite",
@ -94,28 +97,24 @@ backendTypeMetadata =
--------------------------------------------------------------------------------
-- | Reference Agent @backend_configs@ field.
agentConfig :: Aeson.Value
agentConfig =
let backendType = BackendType.backendTypeString backendTypeMetadata
in [yaml|
dataconnector:
*backendType:
uri: "http://127.0.0.1:65007/"
|]
mkChinookCloneTestEnvironment :: TestEnvironment -> Managed ChinookTestEnv
mkChinookCloneTestEnvironment = Chinook.mkChinookCloneTestEnvironment nameFormatting
-- | Sqlite Agent specific @sources@ entry @configuration@ field.
sourceConfiguration :: Aeson.Value
sourceConfiguration =
[yaml|
value:
db: "/db.chinook.sqlite"
template:
timeout:
|]
nameFormatting :: NameFormatting
nameFormatting = NameFormatting id id formatForeignKeyName
-- | Construct foreign key relationship names.
formatForeignKeyName :: Text -> Text
formatForeignKeyName = \case
"Artist" -> "ArtistId->Artist.ArtistId"
x -> x
chinookFixture :: Fixture ChinookTestEnv
chinookFixture =
Fixture
{ name = Backend backendTypeConfig,
mkLocalTestEnvironment = mkChinookCloneTestEnvironment,
setupTeardown = \testEnvs ->
[Chinook.setupChinookSourceAction testEnvs],
customOptions = Nothing
}

View File

@ -14,9 +14,11 @@ where
--------------------------------------------------------------------------------
import Data.Aeson qualified as Aeson
import Data.Aeson.KeyMap qualified as KeyMap
import Data.Text qualified as Text
import Data.Text.Extended qualified as Text (commaSeparated)
import Data.Time qualified as Time
import Harness.DataConnectorAgent (createClone, deleteClone)
import Harness.Exceptions
import Harness.GraphqlEngine qualified as GraphqlEngine
import Harness.Quoter.Yaml (yaml)
@ -25,7 +27,8 @@ import Harness.Test.Fixture qualified as Fixture
import Harness.Test.Permissions qualified as Permissions
import Harness.Test.Schema (SchemaName)
import Harness.Test.Schema qualified as Schema
import Harness.TestEnvironment (TestEnvironment)
import Harness.TestEnvironment (TestEnvironment (..))
import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude
--------------------------------------------------------------------------------
@ -83,18 +86,18 @@ setupTablesAction ts env =
(const $ teardown ts (env, ()))
-- | Metadata source information for the default Sqlite instance.
sourceMetadata :: Aeson.Value
sourceMetadata =
sourceMetadata :: API.Config -> Aeson.Value
sourceMetadata (API.Config config) =
let source = Fixture.backendSourceName backendTypeMetadata
backendType = BackendType.backendTypeString backendTypeMetadata
explicitMainConfig = KeyMap.insert "explicit_main_schema" (Aeson.Bool True) config
in [yaml|
name: *source
kind: *backendType
tables: []
configuration:
db: "/db.sqlite"
explicit_main_schema: true
|]
name: *source
kind: *backendType
tables: []
configuration:
value: *explicitMainConfig
|]
backendConfig :: Aeson.Value
backendConfig =
@ -102,15 +105,20 @@ backendConfig =
in [yaml|
dataconnector:
*backendType:
uri: "http://127.0.0.1:65007/"
uri: *sqliteAgentUri
|]
sqliteAgentUri :: String
sqliteAgentUri = "http://127.0.0.1:65007/"
-- | Setup the schema in the most expected way.
-- NOTE: Certain test modules may warrant having their own local version.
setup :: [Schema.Table] -> (TestEnvironment, ()) -> IO ()
setup tables (testEnvironment, _) = do
-- Create the database clone
cloneConfig <- API._dccrConfig <$> createClone sqliteAgentUri testEnvironment (API.DatasetTemplateName "Empty")
-- Clear and reconfigure the metadata
GraphqlEngine.setSource testEnvironment sourceMetadata (Just backendConfig)
GraphqlEngine.setSource testEnvironment (sourceMetadata cloneConfig) (Just backendConfig)
-- Setup and track tables
for_ tables $ \table -> do
createTable testEnvironment table
@ -164,11 +172,15 @@ teardown (reverse -> tables) (testEnvironment, _) = do
( forFinally_ tables $ \table ->
Schema.untrackRelationships table testEnvironment
)
-- Then teardown tables
( forFinally_ tables $ \table ->
finally
(untrackTable testEnvironment table)
(dropTable testEnvironment table)
( finally
-- Then teardown tables
( forFinally_ tables $ \table ->
finally
(untrackTable testEnvironment table)
(dropTable testEnvironment table)
)
-- Then delete the db clone
(deleteClone sqliteAgentUri testEnvironment)
)
-- | Call the Metadata API and pass in a Raw SQL statement to the

View File

@ -0,0 +1,43 @@
module Harness.DataConnectorAgent
( createClone,
deleteClone,
createManagedClone,
)
where
import Control.Exception.Lifted (finally, throwIO)
import Control.Monad.Managed (MonadManaged, managed)
import Harness.TestEnvironment (TestEnvironment (..))
import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude
import Network.HTTP.Client qualified as Http
import Servant (NamedRoutes, Proxy (..))
import Servant.Client (Client, ClientM, client, mkClientEnv, parseBaseUrl, runClientM, (//))
runDataConnectorAgentRequests :: String -> (Client ClientM (NamedRoutes API.Routes) -> ClientM a) -> IO a
runDataConnectorAgentRequests agentUri runWithClient = do
clientEnv <- mkClientEnv <$> Http.newManager Http.defaultManagerSettings <*> parseBaseUrl agentUri
results <- flip runClientM clientEnv $ do
runWithClient $ client (Proxy @(NamedRoutes API.Routes))
results `onLeft` throwIO
createClone :: String -> TestEnvironment -> API.DatasetTemplateName -> IO API.DatasetCreateCloneResponse
createClone agentUri TestEnvironment {..} templateName = runDataConnectorAgentRequests agentUri $ \dcClient -> do
let cloneName = API.DatasetCloneName (tshow uniqueTestId)
(dcClient // API._datasets // API._createClone) cloneName (API.DatasetCreateCloneRequest templateName)
deleteClone :: String -> TestEnvironment -> IO API.DatasetDeleteCloneResponse
deleteClone agentUri TestEnvironment {..} = runDataConnectorAgentRequests agentUri $ \dcClient -> do
let cloneName = API.DatasetCloneName (tshow uniqueTestId)
(dcClient // API._datasets // API._deleteClone) cloneName
withClone :: String -> TestEnvironment -> API.DatasetTemplateName -> (API.DatasetCreateCloneResponse -> IO a) -> IO a
withClone agentUri TestEnvironment {..} templateName useClone = runDataConnectorAgentRequests agentUri $ \dcClient -> do
let cloneName = API.DatasetCloneName (tshow uniqueTestId)
response <- (dcClient // API._datasets // API._createClone) cloneName (API.DatasetCreateCloneRequest templateName)
finally
(liftIO $ useClone response)
((dcClient // API._datasets // API._deleteClone) cloneName)
createManagedClone :: MonadManaged m => String -> TestEnvironment -> API.DatasetTemplateName -> m API.DatasetCreateCloneResponse
createManagedClone agentUri testEnvironment templateName = managed (withClone agentUri testEnvironment templateName)

View File

@ -290,7 +290,7 @@ startServerThread = do
dataconnector:
foobar:
display_name: FOOBARDB
uri: "http://localhost:65007" |]
uri: "http://localhost:65005" |]
thread <-
Async.async
( runApp

View File

@ -26,12 +26,14 @@ library
, hedgehog
, hspec
, hspec-core
, http-client
, http-conduit
, http-types
, insert-ordered-containers
, lens
, lens-aeson
, libyaml
, lifted-base
, managed
, morpheus-graphql
, mtl
@ -51,6 +53,7 @@ library
, safe
, safe-exceptions
, servant-server
, servant-client
, sop-core
, stm
, string-interpolate
@ -125,6 +128,7 @@ library
Harness.Backend.Postgres
Harness.Backend.Sqlserver
Harness.Constants
Harness.DataConnectorAgent
Harness.Env
Harness.Exceptions
Harness.GraphqlEngine