diff --git a/cli/index.ts b/cli/index.ts index f0548a72e..452d71f5f 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)); + if (!argv[jsonOutputOption.name]) { + 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; } }, diff --git a/cli/index_run_e2e_test.ts b/cli/index_run_e2e_test.ts index ba675834a..6accdfcb9 100644 --- a/cli/index_run_e2e_test.ts +++ b/cli/index_run_e2e_test.ts @@ -489,4 +489,161 @@ SELECT 1 as id }); }); }); + + test("golden with successful 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 1 +` + ); + + // 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": true, + }]); + }); + + 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(1); + + expect(JSON.parse(testResult.stdout)).deep.equals([{ + "name": "example_test", + "successful": false, + messages: [ + "For row 0 and column \"f0_\": expected \"2\", but saw \"1\"." + ] + }]); + }); });