From 1dcf511dee75b827b8049bbef8d1002b41321324 Mon Sep 17 00:00:00 2001 From: Fernando Velasquez Date: Tue, 7 Apr 2026 12:39:30 -0400 Subject: [PATCH 1/7] Added support for JSON output for test results --- cli/index.ts | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/cli/index.ts b/cli/index.ts index f0548a72e..8c2160bbf 100644 --- a/cli/index.ts +++ b/cli/index.ts @@ -146,7 +146,7 @@ const credentialsOption: INamedOption = { const jsonOutputOption: INamedOption = { name: "json", option: { - describe: "Outputs a JSON representation of the compiled project.", + describe: "Outputs a JSON representation of the compiled project or test results.", type: "boolean", default: false } @@ -503,9 +503,11 @@ export function runCli() { format: `test [${projectDirMustExistOption.name}]`, description: "Run the dataform project's unit tests.", positionalOptions: [projectDirMustExistOption], - options: [credentialsOption, timeoutOption, ...ProjectConfigOptions.allYargsOptions], + options: [credentialsOption, timeoutOption, jsonOutputOption, ...ProjectConfigOptions.allYargsOptions], processFn: async argv => { - print("Compiling...\n"); + if (!argv[jsonOutputOption.name]) { + print("Compiling...\n"); + } const compiledGraph = await compile({ projectDir: argv[projectDirMustExistOption.name], projectConfigOverride: ProjectConfigOptions.constructProjectConfigOverride(argv), @@ -515,7 +517,9 @@ export function runCli() { printCompiledGraphErrors(compiledGraph.graphErrors, argv[quietCompileOption.name]); return 1; } - printSuccess("Compiled successfully.\n"); + if (!argv[jsonOutputOption.name]) { + printSuccess("Compiled successfully.\n"); + } const readCredentials = credentials.read( getCredentialsPath(argv[projectDirOption.name], argv[credentialsOption.name]) ); @@ -525,10 +529,17 @@ export function runCli() { return 1; } - print(`Running ${compiledGraph.tests.length} unit tests...\n`); + if (!argv[jsonOutputOption.name]) { + print(`Running ${compiledGraph.tests.length} unit tests...\n`); + } const dbadapter = new BigQueryDbAdapter(readCredentials); const testResults = await test(dbadapter, compiledGraph.tests); - testResults.forEach(testResult => printTestResult(testResult)); + // Print all results as JSON if the option is set. + if (!argv[jsonOutputOption.name]) { + testResults.forEach(testResult => printTestResult(testResult)); // There is no option to output test results as JSON in this command, so pass false. + } else { + print(prettyJsonStringify(testResults)); + } return testResults.every(testResult => testResult.successful) ? 0 : 1; } }, @@ -619,11 +630,11 @@ export function runCli() { } if (argv[runTestsOptionName]) { - print(`Running ${compiledGraph.tests.length} unit tests...\n`); + print(`Running ${compiledGraph.tests.length} unit tests...\n`); const testResults = await test(dbadapter, compiledGraph.tests); testResults.forEach(testResult => printTestResult(testResult)); if (testResults.some(testResult => !testResult.successful)) { - printError("\nUnit tests did not pass; aborting run."); + printError("\nUnit tests did not pass; aborting run."); return 1; } printSuccess("Unit tests completed successfully.\n"); From 17ddfb35ee940d03f4073876dca05140164ea2f1 Mon Sep 17 00:00:00 2001 From: Fernando Velasquez Date: Mon, 13 Apr 2026 17:56:27 -0400 Subject: [PATCH 2/7] Fixed whitespace --- cli/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/index.ts b/cli/index.ts index 8c2160bbf..5b74d1534 100644 --- a/cli/index.ts +++ b/cli/index.ts @@ -630,11 +630,11 @@ export function runCli() { } if (argv[runTestsOptionName]) { - print(`Running ${compiledGraph.tests.length} unit tests...\n`); + print(`Running ${compiledGraph.tests.length} unit tests...\n`); const testResults = await test(dbadapter, compiledGraph.tests); testResults.forEach(testResult => printTestResult(testResult)); if (testResults.some(testResult => !testResult.successful)) { - printError("\nUnit tests did not pass; aborting run."); + printError("\nUnit tests did not pass; aborting run."); return 1; } printSuccess("Unit tests completed successfully.\n"); From 93559257f5fc3135379ae2f9ede23c6d4c5cfe37 Mon Sep 17 00:00:00 2001 From: Fernando Velasquez Date: Mon, 13 Apr 2026 18:09:37 -0400 Subject: [PATCH 3/7] Removed comment --- cli/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/index.ts b/cli/index.ts index 5b74d1534..452d71f5f 100644 --- a/cli/index.ts +++ b/cli/index.ts @@ -534,10 +534,10 @@ export function runCli() { } const dbadapter = new BigQueryDbAdapter(readCredentials); const testResults = await test(dbadapter, compiledGraph.tests); - // Print all results as JSON if the option is set. if (!argv[jsonOutputOption.name]) { - testResults.forEach(testResult => printTestResult(testResult)); // There is no option to output test results as JSON in this command, so pass false. + testResults.forEach(testResult => printTestResult(testResult)); } else { + // Print all results as JSON if the option is set. print(prettyJsonStringify(testResults)); } return testResults.every(testResult => testResult.successful) ? 0 : 1; From 14992787073eda8e6c14f5f368bc2db75afe27f4 Mon Sep 17 00:00:00 2001 From: Fernando Velasquez Date: Thu, 23 Apr 2026 16:21:15 -0400 Subject: [PATCH 4/7] Added e2e test for JSON unit tests --- cli/index_run_e2e_test.ts | 76 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/cli/index_run_e2e_test.ts b/cli/index_run_e2e_test.ts index ba675834a..45b3a61f1 100644 --- a/cli/index_run_e2e_test.ts +++ b/cli/index_run_e2e_test.ts @@ -489,4 +489,80 @@ SELECT 1 as id }); }); }); + + test("golden with unit tests", async () => { + const projectDir = tmpDirFixture.createNewTmpDir(); + const npmCacheDir = tmpDirFixture.createNewTmpDir(); + const workflowSettingsPath = path.join(projectDir, "workflow_settings.yaml"); + const packageJsonPath = path.join(projectDir, "package.json"); + + // Initialize a project using the CLI, don't install packages. + await getProcessResult( + execFile(nodePath, [cliEntryPointPath, "init", projectDir, DEFAULT_DATABASE, DEFAULT_LOCATION]) + ); + + // Install packages manually to get around bazel read-only sandbox issues. + const workflowSettings = dataform.WorkflowSettings.create( + loadYaml(fs.readFileSync(workflowSettingsPath, "utf8")) + ); + delete workflowSettings.dataformCoreVersion; + fs.writeFileSync(workflowSettingsPath, dumpYaml(workflowSettings)); + fs.writeFileSync( + packageJsonPath, + `{ + "dependencies":{ + "@dataform/core": "${version}" + } +}` + ); + await getProcessResult( + execFile(npmPath, [ + "install", + "--prefix", + projectDir, + "--cache", + npmCacheDir, + corePackageTarPath + ]) + ); + + // Write a simple file to the project. + const filePath = path.join(projectDir, "definitions", "example.sqlx"); + fs.ensureFileSync(filePath); + fs.writeFileSync( + filePath, + ` +config { type: "table" } +select 1 +` + ); + // Write a simple test to the project. + const unitTestPath = path.join(projectDir, "definitions", "example_test.sqlx"); + fs.ensureFileSync(unitTestPath); + fs.writeFileSync( + unitTestPath, + ` +config { type: "test", dataset: "example" } +select 1 +` + ); + + // Run tests using the CLI. + const testResult = await getProcessResult( + execFile(nodePath, [ + cliEntryPointPath, + "test", + projectDir, + "--json", + ]) + ); + + expect(testResult.exitCode).equals(0); + + expect(JSON.parse(testResult.stdout)).deep.equals([ { + "name": "example_test", + "successful": true, + "messages": [] + }]); + }); }); From 443d645e1330c842d3fbaf3bb9ecd5d53df25b5f Mon Sep 17 00:00:00 2001 From: Fernando Velasquez Date: Thu, 23 Apr 2026 16:58:35 -0400 Subject: [PATCH 5/7] Add credentials to e2e test --- cli/index_run_e2e_test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cli/index_run_e2e_test.ts b/cli/index_run_e2e_test.ts index 45b3a61f1..f7ede34de 100644 --- a/cli/index_run_e2e_test.ts +++ b/cli/index_run_e2e_test.ts @@ -553,6 +553,8 @@ select 1 cliEntryPointPath, "test", projectDir, + "--credentials", + CREDENTIALS_PATH, "--json", ]) ); From f18af2005524172658ec7c46806dbedf3e009f2d Mon Sep 17 00:00:00 2001 From: Fernando Velasquez Date: Thu, 23 Apr 2026 17:04:59 -0400 Subject: [PATCH 6/7] Add failed unit test case --- cli/index_run_e2e_test.ts | 83 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 2 deletions(-) diff --git a/cli/index_run_e2e_test.ts b/cli/index_run_e2e_test.ts index f7ede34de..8d9929871 100644 --- a/cli/index_run_e2e_test.ts +++ b/cli/index_run_e2e_test.ts @@ -490,7 +490,7 @@ SELECT 1 as id }); }); - test("golden with unit tests", async () => { + test("golden with successful unit test", async () => { const projectDir = tmpDirFixture.createNewTmpDir(); const npmCacheDir = tmpDirFixture.createNewTmpDir(); const workflowSettingsPath = path.join(projectDir, "workflow_settings.yaml"); @@ -564,7 +564,86 @@ select 1 expect(JSON.parse(testResult.stdout)).deep.equals([ { "name": "example_test", "successful": true, - "messages": [] + }]); + }); + + test("golden with failed unit test", async () => { + const projectDir = tmpDirFixture.createNewTmpDir(); + const npmCacheDir = tmpDirFixture.createNewTmpDir(); + const workflowSettingsPath = path.join(projectDir, "workflow_settings.yaml"); + const packageJsonPath = path.join(projectDir, "package.json"); + + // Initialize a project using the CLI, don't install packages. + await getProcessResult( + execFile(nodePath, [cliEntryPointPath, "init", projectDir, DEFAULT_DATABASE, DEFAULT_LOCATION]) + ); + + // Install packages manually to get around bazel read-only sandbox issues. + const workflowSettings = dataform.WorkflowSettings.create( + loadYaml(fs.readFileSync(workflowSettingsPath, "utf8")) + ); + delete workflowSettings.dataformCoreVersion; + fs.writeFileSync(workflowSettingsPath, dumpYaml(workflowSettings)); + fs.writeFileSync( + packageJsonPath, + `{ + "dependencies":{ + "@dataform/core": "${version}" + } +}` + ); + await getProcessResult( + execFile(npmPath, [ + "install", + "--prefix", + projectDir, + "--cache", + npmCacheDir, + corePackageTarPath + ]) + ); + + // Write a simple file to the project. + const filePath = path.join(projectDir, "definitions", "example.sqlx"); + fs.ensureFileSync(filePath); + fs.writeFileSync( + filePath, + ` +config { type: "table" } +select 1 +` + ); + // Write a simple test to the project. + const unitTestPath = path.join(projectDir, "definitions", "example_test.sqlx"); + fs.ensureFileSync(unitTestPath); + fs.writeFileSync( + unitTestPath, + ` +config { type: "test", dataset: "example" } +select 2 +` + ); + + // Run tests using the CLI. + const testResult = await getProcessResult( + execFile(nodePath, [ + cliEntryPointPath, + "test", + projectDir, + "--credentials", + CREDENTIALS_PATH, + "--json", + ]) + ); + + expect(testResult.exitCode).equals(0); + + expect(JSON.parse(testResult.stdout)).deep.equals([{ + "name": "example_test", + "successful": false, + messages: [ + "Expected:\n1\nReceived:\n2" + ] }]); }); }); From 12eff7ee776cc4c7acc975ab5801a3219529217b Mon Sep 17 00:00:00 2001 From: Fernando Velasquez Date: Thu, 23 Apr 2026 17:10:31 -0400 Subject: [PATCH 7/7] Fix test definition --- cli/index_run_e2e_test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/index_run_e2e_test.ts b/cli/index_run_e2e_test.ts index 8d9929871..6accdfcb9 100644 --- a/cli/index_run_e2e_test.ts +++ b/cli/index_run_e2e_test.ts @@ -636,13 +636,13 @@ select 2 ]) ); - expect(testResult.exitCode).equals(0); + expect(testResult.exitCode).equals(1); expect(JSON.parse(testResult.stdout)).deep.equals([{ "name": "example_test", "successful": false, messages: [ - "Expected:\n1\nReceived:\n2" + "For row 0 and column \"f0_\": expected \"2\", but saw \"1\"." ] }]); });