diff --git a/dc-agents/docker-compose.yaml b/dc-agents/docker-compose.yaml index 47a318491b8..a0d3d55e36e 100644 --- a/dc-agents/docker-compose.yaml +++ b/dc-agents/docker-compose.yaml @@ -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 diff --git a/dc-agents/sdk/docker-compose.yaml b/dc-agents/sdk/docker-compose.yaml index b1345b04bdb..0efbc5567c7 100644 --- a/dc-agents/sdk/docker-compose.yaml +++ b/dc-agents/sdk/docker-compose.yaml @@ -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: diff --git a/dc-agents/sqlite/.gitignore b/dc-agents/sqlite/.gitignore index 93b3e876b00..71a2af96480 100644 --- a/dc-agents/sqlite/.gitignore +++ b/dc-agents/sqlite/.gitignore @@ -1,4 +1,4 @@ node_modules dist ./*.sqlite -./dataset_clones/ \ No newline at end of file +dataset_clones/ diff --git a/dc-agents/sqlite/test/db.chinook.sqlite b/dc-agents/sqlite/dataset_templates/Chinook.sqlite similarity index 99% rename from dc-agents/sqlite/test/db.chinook.sqlite rename to dc-agents/sqlite/dataset_templates/Chinook.sqlite index f85341121de..8f343e61995 100644 Binary files a/dc-agents/sqlite/test/db.chinook.sqlite and b/dc-agents/sqlite/dataset_templates/Chinook.sqlite differ diff --git a/dc-agents/sqlite/test/db.sqlite b/dc-agents/sqlite/dataset_templates/Empty.sqlite similarity index 96% rename from dc-agents/sqlite/test/db.sqlite rename to dc-agents/sqlite/dataset_templates/Empty.sqlite index d42a85082db..18035df8546 100644 Binary files a/dc-agents/sqlite/test/db.sqlite and b/dc-agents/sqlite/dataset_templates/Empty.sqlite differ diff --git a/dc-agents/sqlite/src/datasets.ts b/dc-agents/sqlite/src/datasets.ts index a1bb291a759..5dd27abf223 100644 --- a/dc-agents/sqlite/src/datasets.ts +++ b/dc-agents/sqlite/src/datasets.ts @@ -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"); } diff --git a/server/lib/api-tests/src/Test/API/Metadata/TransparentDefaultsSpec.hs b/server/lib/api-tests/src/Test/API/Metadata/TransparentDefaultsSpec.hs index b0df1ba0d12..1db63c23546 100644 --- a/server/lib/api-tests/src/Test/API/Metadata/TransparentDefaultsSpec.hs +++ b/server/lib/api-tests/src/Test/API/Metadata/TransparentDefaultsSpec.hs @@ -79,7 +79,7 @@ addSource = name: myfoobar replace_configuration: false configuration: - db: /db.chinook.sqlite + value: {} |] listSourceKinds :: Value diff --git a/server/lib/api-tests/src/Test/DataConnector/AggregateQuerySpec.hs b/server/lib/api-tests/src/Test/DataConnector/AggregateQuerySpec.hs index 5b9433e0b4b..3d51fe89257 100644 --- a/server/lib/api-tests/src/Test/DataConnector/AggregateQuerySpec.hs +++ b/server/lib/api-tests/src/Test/DataConnector/AggregateQuerySpec.hs @@ -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 diff --git a/server/lib/api-tests/src/Test/DataConnector/MetadataApiSpec.hs b/server/lib/api-tests/src/Test/DataConnector/MetadataApiSpec.hs index efde739441a..e25c03ba28c 100644 --- a/server/lib/api-tests/src/Test/DataConnector/MetadataApiSpec.hs +++ b/server/lib/api-tests/src/Test/DataConnector/MetadataApiSpec.hs @@ -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 "_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 "_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 "_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 "_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 "_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 diff --git a/server/lib/api-tests/src/Test/DataConnector/QuerySpec.hs b/server/lib/api-tests/src/Test/DataConnector/QuerySpec.hs index aed3d1010a6..711b23cd564 100644 --- a/server/lib/api-tests/src/Test/DataConnector/QuerySpec.hs +++ b/server/lib/api-tests/src/Test/DataConnector/QuerySpec.hs @@ -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 diff --git a/server/lib/api-tests/src/Test/DataConnector/SelectPermissionsSpec.hs b/server/lib/api-tests/src/Test/DataConnector/SelectPermissionsSpec.hs index e24487deac1..b38185019d5 100644 --- a/server/lib/api-tests/src/Test/DataConnector/SelectPermissionsSpec.hs +++ b/server/lib/api-tests/src/Test/DataConnector/SelectPermissionsSpec.hs @@ -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 diff --git a/server/lib/dc-api/test/Command.hs b/server/lib/dc-api/test/Command.hs index 6b7ac4e538d..5d9dc7b4753 100644 --- a/server/lib/dc-api/test/Command.hs +++ b/server/lib/dc-api/test/Command.hs @@ -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 = diff --git a/server/lib/dc-api/test/Main.hs b/server/lib/dc-api/test/Main.hs index 624f3b02c08..44824d0df60 100644 --- a/server/lib/dc-api/test/Main.hs +++ b/server/lib/dc-api/test/Main.hs @@ -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)" diff --git a/server/lib/dc-api/test/README.md b/server/lib/dc-api/test/README.md index b7bd84191e1..395a049fecc 100644 --- a/server/lib/dc-api/test/README.md +++ b/server/lib/dc-api/test/README.md @@ -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: diff --git a/server/lib/dc-api/test/Test/AgentAPI.hs b/server/lib/dc-api/test/Test/AgentAPI.hs index 1eef34fed99..6663be705c8 100644 --- a/server/lib/dc-api/test/Test/AgentAPI.hs +++ b/server/lib/dc-api/test/Test/AgentAPI.hs @@ -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 diff --git a/server/lib/dc-api/test/Test/AgentTestContext.hs b/server/lib/dc-api/test/Test/AgentTestContext.hs index 0cfbed5123a..a584c0ffd6b 100644 --- a/server/lib/dc-api/test/Test/AgentTestContext.hs +++ b/server/lib/dc-api/test/Test/AgentTestContext.hs @@ -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 () diff --git a/server/lib/test-harness/src/Harness/Backend/DataConnector/Chinook.hs b/server/lib/test-harness/src/Harness/Backend/DataConnector/Chinook.hs index 9522f498953..8ad73144431 100644 --- a/server/lib/test-harness/src/Harness/Backend/DataConnector/Chinook.hs +++ b/server/lib/test-harness/src/Harness/Backend/DataConnector/Chinook.hs @@ -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 diff --git a/server/lib/test-harness/src/Harness/Backend/DataConnector/Chinook/Reference.hs b/server/lib/test-harness/src/Harness/Backend/DataConnector/Chinook/Reference.hs index a74c3974dad..2f76eeef726 100644 --- a/server/lib/test-harness/src/Harness/Backend/DataConnector/Chinook/Reference.hs +++ b/server/lib/test-harness/src/Harness/Backend/DataConnector/Chinook/Reference.hs @@ -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 + } diff --git a/server/lib/test-harness/src/Harness/Backend/DataConnector/Chinook/Sqlite.hs b/server/lib/test-harness/src/Harness/Backend/DataConnector/Chinook/Sqlite.hs index 987b0b05b76..22555d283fd 100644 --- a/server/lib/test-harness/src/Harness/Backend/DataConnector/Chinook/Sqlite.hs +++ b/server/lib/test-harness/src/Harness/Backend/DataConnector/Chinook/Sqlite.hs @@ -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 + } diff --git a/server/lib/test-harness/src/Harness/Backend/DataConnector/Sqlite.hs b/server/lib/test-harness/src/Harness/Backend/DataConnector/Sqlite.hs index d0846ef3d7f..ce038e0c101 100644 --- a/server/lib/test-harness/src/Harness/Backend/DataConnector/Sqlite.hs +++ b/server/lib/test-harness/src/Harness/Backend/DataConnector/Sqlite.hs @@ -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 diff --git a/server/lib/test-harness/src/Harness/DataConnectorAgent.hs b/server/lib/test-harness/src/Harness/DataConnectorAgent.hs new file mode 100644 index 00000000000..4fdb8d59774 --- /dev/null +++ b/server/lib/test-harness/src/Harness/DataConnectorAgent.hs @@ -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) diff --git a/server/lib/test-harness/src/Harness/GraphqlEngine.hs b/server/lib/test-harness/src/Harness/GraphqlEngine.hs index 029c17c3112..c6896720eda 100644 --- a/server/lib/test-harness/src/Harness/GraphqlEngine.hs +++ b/server/lib/test-harness/src/Harness/GraphqlEngine.hs @@ -290,7 +290,7 @@ startServerThread = do dataconnector: foobar: display_name: FOOBARDB - uri: "http://localhost:65007" |] + uri: "http://localhost:65005" |] thread <- Async.async ( runApp diff --git a/server/lib/test-harness/test-harness.cabal b/server/lib/test-harness/test-harness.cabal index f8aea02feeb..ba6388a7a68 100644 --- a/server/lib/test-harness/test-harness.cabal +++ b/server/lib/test-harness/test-harness.cabal @@ -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