From 00e68bbbd6464033330d936de08dd41c72c67476 Mon Sep 17 00:00:00 2001 From: Tyler Adkins Date: Fri, 3 Apr 2026 20:23:59 -0400 Subject: [PATCH 1/8] feat: contract verify accepts dataset DQN in addition to local file Allows engineers to run `sodacli contract verify datasource/db/schema/table` without needing a local contract YAML file. When the argument does not end in .yml/.yaml it is treated as a DQN, the latest contract is fetched from Soda Cloud by dataset, and verification is triggered via the Runner. Refactors runContractVerify into three focused helpers: - runContractVerifyByFile: existing file push + verify path - runContractVerifyByDQN: new DQN lookup + verify path - pollAndDisplayVerification: shared poll and display logic Co-Authored-By: Claude Sonnet 4.6 --- go/cmd/contract.go | 101 ++++++++++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 33 deletions(-) diff --git a/go/cmd/contract.go b/go/cmd/contract.go index ec0c9e9..aa728a6 100644 --- a/go/cmd/contract.go +++ b/go/cmd/contract.go @@ -648,59 +648,87 @@ func runCopilotImprove(file, prompt string) error { // ── contract verify ─────────────────────────────────────────────────────────── var contractVerifyCmd = &cobra.Command{ - Use: "verify ", + Use: "verify ", Short: "Run contract checks against your data", - Long: `Execute data quality checks defined in a contract file. + Long: `Execute data quality checks defined in a contract file or stored in Soda Cloud. - By default, pushes the contract to Soda Cloud and triggers verification via a Runner. - Polls for results and displays a summary. + Accepts either a local contract file path (e.g. orders.yml) or a dataset DQN + (datasource/db/schema/table). When a DQN is provided, the latest contract stored + in Soda Cloud is verified via the Runner — no local file needed. + + By default, verification runs via the cloud Runner and polls for results. With --local, runs verification locally via soda-core (must be on PATH). - In local mode, --datasource is required. + In local mode, --datasource is required and a contract file is expected. Use --push to publish local results to Soda Cloud. Exit codes: 0=all passing, 1=checks failed, 2=error, 3=auth error`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - file := args[0] local, _ := cmd.Flags().GetBool("local") - if local { - return runContractVerifyLocal(cmd, file) + return runContractVerifyLocal(cmd, args[0]) } noWait, _ := cmd.Flags().GetBool("no-wait") - client, err := newAPIClient() if err != nil { return err } - return runContractVerify(client, file, noWait) + return runContractVerify(client, args[0], noWait) }, } -// runContractVerify pushes a contract file to cloud, triggers verification, -// polls for results, and displays a summary. Reused by the verify command -// and both onboard flows. -func runContractVerify(client *api.Client, file string, noWait bool) error { +// runContractVerify triggers cloud Runner verification and polls for results. +// The input may be a local contract file path (*.yml / *.yaml) or a dataset DQN +// (datasource/db/schema/table). Reused by the verify command and both onboard flows. +func runContractVerify(client *api.Client, fileOrDQN string, noWait bool) error { + var contractID string + var err error + + if strings.HasSuffix(fileOrDQN, ".yml") || strings.HasSuffix(fileOrDQN, ".yaml") { + contractID, err = runContractVerifyByFile(client, fileOrDQN) + if err != nil { + return err + } + } else { + contractID, err = runContractVerifyByDQN(client, fileOrDQN) + if err != nil { + return err + } + } + + // Trigger verification + fmt.Println(output.Dim.Render(" Triggering verification...")) + scanID, err := client.VerifyContract(contractID) + if err != nil { + return err + } + fmt.Println(output.Dim.Render(" Scan ID: " + scanID)) + + return pollAndDisplayVerification(client, scanID, noWait) +} + +// runContractVerifyByFile reads a local contract file, pushes it to Soda Cloud, +// and returns the contract ID to use for verification. +func runContractVerifyByFile(client *api.Client, file string) (string, error) { contents, err := os.ReadFile(file) if err != nil { - return output.Errorf(2, "could not read file %s: %v", file, err) + return "", output.Errorf(2, "could not read file %s: %v", file, err) } qualifiedName, err := parseDatasetField(contents) if err != nil { - return output.Errorf(2, "could not parse 'dataset:' field from %s: %v", file, err) + return "", output.Errorf(2, "could not parse 'dataset:' field from %s: %v", file, err) } if qualifiedName == "" { - return output.Errorf(2, "contract file %s must have a 'dataset:' field", file) + return "", output.Errorf(2, "contract file %s must have a 'dataset:' field", file) } - // Push/update contract to cloud fmt.Println(output.Dim.Render(" Pushing contract for " + qualifiedName + "...")) existing, err := client.FindContractByDataset(qualifiedName) if err != nil { - return err + return "", err } req := api.ContractRequest{ @@ -708,29 +736,36 @@ func runContractVerify(client *api.Client, file string, noWait bool) error { Contents: string(contents), } - var contractID string if existing != nil { result, err := client.UpdateContract(existing.ID, req) if err != nil { - return err + return "", err } - contractID = result.ID - } else { - result, err := client.CreateContract(req) - if err != nil { - return err - } - contractID = result.ID + return result.ID, nil + } + result, err := client.CreateContract(req) + if err != nil { + return "", err } + return result.ID, nil +} - // Trigger verification - fmt.Println(output.Dim.Render(" Triggering verification...")) - scanID, err := client.VerifyContract(contractID) +// runContractVerifyByDQN looks up an existing contract in Soda Cloud by dataset DQN +// and returns its contract ID for verification. No local file is required. +func runContractVerifyByDQN(client *api.Client, dqn string) (string, error) { + fmt.Println(output.Dim.Render(" Looking up contract for " + dqn + "...")) + contract, err := client.FindContractByDataset(dqn) if err != nil { - return err + return "", err } - fmt.Println(output.Dim.Render(" Scan ID: " + scanID)) + if contract == nil { + return "", output.Errorf(2, "no contract found for dataset %s", dqn) + } + return contract.ID, nil +} +// pollAndDisplayVerification waits for a scan to complete and prints the results. +func pollAndDisplayVerification(client *api.Client, scanID string, noWait bool) error { if noWait { output.PrintSuccess(fmt.Sprintf("Verification started (scan: %s). Running in background.", scanID), GCtx) fmt.Println(output.Dim.Render(" Check status: sodacli job logs " + scanID)) From 44b77c1031bf4120618293638500d826f4c7a647 Mon Sep 17 00:00:00 2001 From: Tyler Adkins Date: Fri, 3 Apr 2026 20:49:26 -0400 Subject: [PATCH 2/8] refactor: restore file var in RunE, rename ByFile to ByYAML for clarity Co-Authored-By: Claude Sonnet 4.6 --- go/cmd/contract.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/go/cmd/contract.go b/go/cmd/contract.go index aa728a6..2603701 100644 --- a/go/cmd/contract.go +++ b/go/cmd/contract.go @@ -665,9 +665,10 @@ var contractVerifyCmd = &cobra.Command{ Exit codes: 0=all passing, 1=checks failed, 2=error, 3=auth error`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + file := args[0] local, _ := cmd.Flags().GetBool("local") if local { - return runContractVerifyLocal(cmd, args[0]) + return runContractVerifyLocal(cmd, file) } noWait, _ := cmd.Flags().GetBool("no-wait") @@ -676,24 +677,23 @@ var contractVerifyCmd = &cobra.Command{ return err } - return runContractVerify(client, args[0], noWait) + return runContractVerify(client, file, noWait) }, } -// runContractVerify triggers cloud Runner verification and polls for results. -// The input may be a local contract file path (*.yml / *.yaml) or a dataset DQN -// (datasource/db/schema/table). Reused by the verify command and both onboard flows. -func runContractVerify(client *api.Client, fileOrDQN string, noWait bool) error { +// runContractVerify pushes a contract to Soda Cloud, triggers verification via a Runner, +// polls for results, and displays a summary. Reused by the verify command and both onboard flows. +func runContractVerify(client *api.Client, file string, noWait bool) error { var contractID string var err error - if strings.HasSuffix(fileOrDQN, ".yml") || strings.HasSuffix(fileOrDQN, ".yaml") { - contractID, err = runContractVerifyByFile(client, fileOrDQN) + if strings.HasSuffix(file, ".yml") || strings.HasSuffix(file, ".yaml") { + contractID, err = runContractVerifyByYAML(client, file) if err != nil { return err } } else { - contractID, err = runContractVerifyByDQN(client, fileOrDQN) + contractID, err = runContractVerifyByDQN(client, file) if err != nil { return err } @@ -710,9 +710,9 @@ func runContractVerify(client *api.Client, fileOrDQN string, noWait bool) error return pollAndDisplayVerification(client, scanID, noWait) } -// runContractVerifyByFile reads a local contract file, pushes it to Soda Cloud, +// runContractVerifyByYAML reads a local contract YAML, pushes it to Soda Cloud, // and returns the contract ID to use for verification. -func runContractVerifyByFile(client *api.Client, file string) (string, error) { +func runContractVerifyByYAML(client *api.Client, file string) (string, error) { contents, err := os.ReadFile(file) if err != nil { return "", output.Errorf(2, "could not read file %s: %v", file, err) From d54587e9aaf61b2c1e084bd42d426c18e7caca28 Mon Sep 17 00:00:00 2001 From: Tyler Adkins Date: Fri, 3 Apr 2026 21:00:01 -0400 Subject: [PATCH 3/8] refactor: simplify by moving verify+poll into each sub-function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes tuple returns — each path (YAML/DQN) owns its full flow. Co-Authored-By: Claude Sonnet 4.6 --- go/cmd/contract.go | 81 ++++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/go/cmd/contract.go b/go/cmd/contract.go index 2603701..581d888 100644 --- a/go/cmd/contract.go +++ b/go/cmd/contract.go @@ -667,11 +667,13 @@ var contractVerifyCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { file := args[0] local, _ := cmd.Flags().GetBool("local") + if local { return runContractVerifyLocal(cmd, file) } noWait, _ := cmd.Flags().GetBool("no-wait") + client, err := newAPIClient() if err != nil { return err @@ -684,51 +686,31 @@ var contractVerifyCmd = &cobra.Command{ // runContractVerify pushes a contract to Soda Cloud, triggers verification via a Runner, // polls for results, and displays a summary. Reused by the verify command and both onboard flows. func runContractVerify(client *api.Client, file string, noWait bool) error { - var contractID string - var err error - if strings.HasSuffix(file, ".yml") || strings.HasSuffix(file, ".yaml") { - contractID, err = runContractVerifyByYAML(client, file) - if err != nil { - return err - } - } else { - contractID, err = runContractVerifyByDQN(client, file) - if err != nil { - return err - } + return runContractVerifyByYAML(client, file, noWait) } - - // Trigger verification - fmt.Println(output.Dim.Render(" Triggering verification...")) - scanID, err := client.VerifyContract(contractID) - if err != nil { - return err - } - fmt.Println(output.Dim.Render(" Scan ID: " + scanID)) - - return pollAndDisplayVerification(client, scanID, noWait) + return runContractVerifyByDQN(client, file, noWait) } // runContractVerifyByYAML reads a local contract YAML, pushes it to Soda Cloud, -// and returns the contract ID to use for verification. -func runContractVerifyByYAML(client *api.Client, file string) (string, error) { +// triggers verification via a Runner, and polls for results. +func runContractVerifyByYAML(client *api.Client, file string, noWait bool) error { contents, err := os.ReadFile(file) if err != nil { - return "", output.Errorf(2, "could not read file %s: %v", file, err) + return output.Errorf(2, "could not read file %s: %v", file, err) } qualifiedName, err := parseDatasetField(contents) if err != nil { - return "", output.Errorf(2, "could not parse 'dataset:' field from %s: %v", file, err) + return output.Errorf(2, "could not parse 'dataset:' field from %s: %v", file, err) } if qualifiedName == "" { - return "", output.Errorf(2, "contract file %s must have a 'dataset:' field", file) + return output.Errorf(2, "contract file %s must have a 'dataset:' field", file) } fmt.Println(output.Dim.Render(" Pushing contract for " + qualifiedName + "...")) existing, err := client.FindContractByDataset(qualifiedName) if err != nil { - return "", err + return err } req := api.ContractRequest{ @@ -736,32 +718,53 @@ func runContractVerifyByYAML(client *api.Client, file string) (string, error) { Contents: string(contents), } + var contractID string if existing != nil { result, err := client.UpdateContract(existing.ID, req) if err != nil { - return "", err + return err } - return result.ID, nil + contractID = result.ID + } else { + result, err := client.CreateContract(req) + if err != nil { + return err + } + contractID = result.ID } - result, err := client.CreateContract(req) + + // Trigger verification + fmt.Println(output.Dim.Render(" Triggering verification...")) + scanID, err := client.VerifyContract(contractID) if err != nil { - return "", err + return err } - return result.ID, nil + fmt.Println(output.Dim.Render(" Scan ID: " + scanID)) + + return pollAndDisplayVerification(client, scanID, noWait) } -// runContractVerifyByDQN looks up an existing contract in Soda Cloud by dataset DQN -// and returns its contract ID for verification. No local file is required. -func runContractVerifyByDQN(client *api.Client, dqn string) (string, error) { +// runContractVerifyByDQN looks up an existing contract in Soda Cloud by dataset DQN, +// triggers verification via a Runner, and polls for results. No local file is required. +func runContractVerifyByDQN(client *api.Client, dqn string, noWait bool) error { fmt.Println(output.Dim.Render(" Looking up contract for " + dqn + "...")) contract, err := client.FindContractByDataset(dqn) if err != nil { - return "", err + return err } if contract == nil { - return "", output.Errorf(2, "no contract found for dataset %s", dqn) + return output.Errorf(2, "no contract found for dataset %s", dqn) + } + + // Trigger verification + fmt.Println(output.Dim.Render(" Triggering verification...")) + scanID, err := client.VerifyContract(contract.ID) + if err != nil { + return err } - return contract.ID, nil + fmt.Println(output.Dim.Render(" Scan ID: " + scanID)) + + return pollAndDisplayVerification(client, scanID, noWait) } // pollAndDisplayVerification waits for a scan to complete and prints the results. From 85d3b55a0b7e413e7a3a898f7d2ca7b5db864382 Mon Sep 17 00:00:00 2001 From: Tyler Adkins Date: Fri, 3 Apr 2026 21:04:26 -0400 Subject: [PATCH 4/8] style: restore original comments and simplify Long description Co-Authored-By: Claude Sonnet 4.6 --- go/cmd/contract.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/go/cmd/contract.go b/go/cmd/contract.go index 581d888..a57fa63 100644 --- a/go/cmd/contract.go +++ b/go/cmd/contract.go @@ -650,7 +650,7 @@ func runCopilotImprove(file, prompt string) error { var contractVerifyCmd = &cobra.Command{ Use: "verify ", Short: "Run contract checks against your data", - Long: `Execute data quality checks defined in a contract file or stored in Soda Cloud. + Long: `Execute data quality checks defined in a contract. Accepts either a local contract file path (e.g. orders.yml) or a dataset DQN (datasource/db/schema/table). When a DQN is provided, the latest contract stored @@ -683,8 +683,9 @@ var contractVerifyCmd = &cobra.Command{ }, } -// runContractVerify pushes a contract to Soda Cloud, triggers verification via a Runner, -// polls for results, and displays a summary. Reused by the verify command and both onboard flows. +// runContractVerify pushes a contract file to cloud, triggers verification, +// polls for results, and displays a summary. Reused by the verify command +// and both onboard flows. func runContractVerify(client *api.Client, file string, noWait bool) error { if strings.HasSuffix(file, ".yml") || strings.HasSuffix(file, ".yaml") { return runContractVerifyByYAML(client, file, noWait) @@ -707,6 +708,7 @@ func runContractVerifyByYAML(client *api.Client, file string, noWait bool) error return output.Errorf(2, "contract file %s must have a 'dataset:' field", file) } + // Push/update contract to cloud fmt.Println(output.Dim.Render(" Pushing contract for " + qualifiedName + "...")) existing, err := client.FindContractByDataset(qualifiedName) if err != nil { From 399ae25accb67a715de88f6666858109f55fc87c Mon Sep 17 00:00:00 2001 From: Tyler Adkins Date: Fri, 3 Apr 2026 21:14:54 -0400 Subject: [PATCH 5/8] style: tighten Long description to minimal addition over original Co-Authored-By: Claude Sonnet 4.6 --- go/cmd/contract.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/go/cmd/contract.go b/go/cmd/contract.go index a57fa63..20df103 100644 --- a/go/cmd/contract.go +++ b/go/cmd/contract.go @@ -650,13 +650,12 @@ func runCopilotImprove(file, prompt string) error { var contractVerifyCmd = &cobra.Command{ Use: "verify ", Short: "Run contract checks against your data", - Long: `Execute data quality checks defined in a contract. + Long: `Execute data quality checks defined in a contract file. - Accepts either a local contract file path (e.g. orders.yml) or a dataset DQN - (datasource/db/schema/table). When a DQN is provided, the latest contract stored - in Soda Cloud is verified via the Runner — no local file needed. - - By default, verification runs via the cloud Runner and polls for results. + By default, pushes the contract to Soda Cloud and triggers verification via a Runner. + Polls for results and displays a summary. + Also accepts a dataset DQN (datasource/db/schema/table) to fetch and verify an existing + contract from Soda Cloud without a local file. With --local, runs verification locally via soda-core (must be on PATH). In local mode, --datasource is required and a contract file is expected. From 115266b553b17187ff515b82bb558241568135e6 Mon Sep 17 00:00:00 2001 From: Tyler Adkins Date: Fri, 3 Apr 2026 21:18:01 -0400 Subject: [PATCH 6/8] docs: update SKILL.md with dataset DQN verify example Co-Authored-By: Claude Sonnet 4.6 --- skills/soda-cli/SKILL.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/skills/soda-cli/SKILL.md b/skills/soda-cli/SKILL.md index 3df2ce8..1c34869 100644 --- a/skills/soda-cli/SKILL.md +++ b/skills/soda-cli/SKILL.md @@ -153,9 +153,12 @@ sodacli contract diff my_table.yml sodacli contract lint my_table.yml sodacli contract lint contracts/*.yml # glob support -# Run checks via cloud Runner +# Run checks via cloud Runner (local file) sodacli contract verify my_table.yml --output json +# Run checks via cloud Runner using dataset DQN — no local file needed +sodacli contract verify datasource/db/schema/table --output json + # Run checks locally via soda-core (no cloud auth needed) sodacli contract verify my_table.yml --local --datasource datasource.yml From e80a144e46cb497230203be9046cfb13db98b02c Mon Sep 17 00:00:00 2001 From: Tyler Adkins Date: Fri, 3 Apr 2026 21:19:16 -0400 Subject: [PATCH 7/8] docs: update README with dataset DQN verify examples Co-Authored-By: Claude Sonnet 4.6 --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8a302bc..0ce8a51 100644 --- a/README.md +++ b/README.md @@ -114,9 +114,12 @@ sodacli datasource onboard --monitoring --profiling --contracts ### 3. Verify a contract ```bash -# Run checks via Soda Cloud Runner +# Run checks via Soda Cloud Runner (local file) sodacli contract verify orders.yml +# Run checks via Soda Cloud Runner using dataset DQN — no local file needed +sodacli contract verify datasource/db/schema/table + # Or run locally via soda-core (no cloud needed) sodacli contract verify orders.yml --local --datasource datasource.yml @@ -177,6 +180,7 @@ sodacli contract diff my_table.yml # local sodacli contract lint my_table.yml # validate syntax (offline) sodacli contract lint contracts/*.yml # lint multiple files sodacli contract verify my_table.yml # run checks via cloud Runner +sodacli contract verify datasource/db/schema/table # run checks via cloud Runner using DQN sodacli contract verify my_table.yml --no-wait # fire and forget sodacli contract verify my_table.yml --local --datasource config.yml # run locally via soda-core sodacli contract verify my_table.yml --local --datasource config.yml --push # run locally + push results to cloud From bfe7b484fea9c200326705a254216f435cf9dc6b Mon Sep 17 00:00:00 2001 From: Santiago Viquez Date: Fri, 3 Apr 2026 21:27:26 -0600 Subject: [PATCH 8/8] review: guard --local+DQN, update docs, add DQN integration tests - Reject `--local` with a DQN argument (clear error instead of cryptic file-not-found) - Update command_tree.txt to reflect `` usage - Clarify README docs comments for DQN vs file verify - Add TestContractVerifyDQN: local_rejects_dqn, nonexistent_dqn, verify_by_dqn --- README.md | 4 ++-- command_tree.txt | 2 +- go/cmd/contract.go | 4 ++++ go/tests/integration/contract_test.go | 29 +++++++++++++++++++++++++++ go/tests/integration/helpers_test.go | 1 + 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0ce8a51..071e3aa 100644 --- a/README.md +++ b/README.md @@ -179,8 +179,8 @@ sodacli contract push my_table.yml # uploa sodacli contract diff my_table.yml # local vs cloud diff sodacli contract lint my_table.yml # validate syntax (offline) sodacli contract lint contracts/*.yml # lint multiple files -sodacli contract verify my_table.yml # run checks via cloud Runner -sodacli contract verify datasource/db/schema/table # run checks via cloud Runner using DQN +sodacli contract verify my_table.yml # run checks via cloud Runner (local file) +sodacli contract verify datasource/db/schema/table # run checks via cloud Runner (DQN, no local file) sodacli contract verify my_table.yml --no-wait # fire and forget sodacli contract verify my_table.yml --local --datasource config.yml # run locally via soda-core sodacli contract verify my_table.yml --local --datasource config.yml --push # run locally + push results to cloud diff --git a/command_tree.txt b/command_tree.txt index e703471..4497cce 100644 --- a/command_tree.txt +++ b/command_tree.txt @@ -110,7 +110,7 @@ sodacli │ │ # file + prompt → improve existing contract │ │ # --no-interactive → fails with clear error describing what's missing │ │ --output - │ ├── verify # verify contract checks against data + │ ├── verify # verify contract checks against data │ │ [--datasource ] # datasource config file (required with --local) │ │ [--local] # run locally via soda-core 🔌 [requires soda-core on PATH] │ │ [--push] # push results to Soda Cloud (useful with --local) diff --git a/go/cmd/contract.go b/go/cmd/contract.go index 20df103..9489b61 100644 --- a/go/cmd/contract.go +++ b/go/cmd/contract.go @@ -666,7 +666,11 @@ var contractVerifyCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { file := args[0] local, _ := cmd.Flags().GetBool("local") + isDQN := !strings.HasSuffix(file, ".yml") && !strings.HasSuffix(file, ".yaml") + if local && isDQN { + return output.Errorf(2, "--local requires a contract file (.yml/.yaml), not a dataset DQN") + } if local { return runContractVerifyLocal(cmd, file) } diff --git a/go/tests/integration/contract_test.go b/go/tests/integration/contract_test.go index 16291ea..c829e6a 100644 --- a/go/tests/integration/contract_test.go +++ b/go/tests/integration/contract_test.go @@ -207,6 +207,35 @@ func TestContractVerify(t *testing.T) { }) } +func TestContractVerifyDQN(t *testing.T) { + t.Run("local_rejects_dqn", func(t *testing.T) { + // Error path — no credentials needed. + r := run(t, "contract", "verify", "datasource/db/schema/table", "--local", "--datasource", "ds.yml") + assertExitCode(t, r, 2) + assertOutputContains(t, r, "--local requires a contract file") + }) + + t.Run("nonexistent_dqn", func(t *testing.T) { + skipIfNoCredentials(t) + loginForTest(t) + r := run(t, "contract", "verify", "fake/ds/no/exist", "--no-wait") + assertExitCode(t, r, 2) + assertOutputContains(t, r, "no contract found") + }) + + t.Run("verify_by_dqn", func(t *testing.T) { + skipIfNoCredentials(t) + dqn := testDatasetDQN() + if dqn == "" { + t.Skip("SODA_TEST_DATASET_DQN not set") + } + loginForTest(t) + r := run(t, "contract", "verify", dqn, "--no-wait") + assertExitCode(t, r, 0) + assertOutputContains(t, r, "Verification started") + }) +} + func TestContractVerifyLocal(t *testing.T) { // Local verify error paths don't need credentials. diff --git a/go/tests/integration/helpers_test.go b/go/tests/integration/helpers_test.go index 6cd6fe0..0304fe8 100644 --- a/go/tests/integration/helpers_test.go +++ b/go/tests/integration/helpers_test.go @@ -57,6 +57,7 @@ func testKeySecret() string { return env("SODA_TEST_API_KEY_SECRET") } func testDatasourceID() string { return env("SODA_TEST_DATASOURCE_ID") } func testDatasourceName() string { return env("SODA_TEST_DATASOURCE_NAME") } func testDatasetID() string { return env("SODA_TEST_DATASET_ID") } +func testDatasetDQN() string { return env("SODA_TEST_DATASET_DQN") } func testDSConfig() string { v := env("SODA_TEST_DS_CONFIG") if v == "" {