From 7fd727f70a974f087d222108b399615ab4708ecf Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Mon, 4 Sep 2023 13:40:32 +1000 Subject: [PATCH 01/34] updated README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bad94d3..34b4661 100644 --- a/README.md +++ b/README.md @@ -82,8 +82,8 @@ The following excerpts from a GitHub Actions workflow demonstrate how to use the - name: Use test query string and expected results file, with auth object uses: ./ with: - test_query_file_path: './.github/workflows/workflow_scripts/google-example.iql' - data_file_path: './.github/workflows/workflow_scripts/google-example-data.jsonnet' + test_query_file_path: './.github/workflows/workflow_scripts/github-example.iql' + data_file_path: './.github/workflows/workflow_scripts/github-example-data.jsonnet' vars: TEST_ORG=${{ env.TEST_ORG }},TEST_REPO=${{ env.TEST_REPO }} expected_rows: 1 env: From bce47f650891638272e19250caa9f87c8701147e Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Tue, 5 Sep 2023 07:58:02 +1000 Subject: [PATCH 02/34] dryrun to show rendered query --- action.yml | 22 ++++++++++++++++++---- lib/utils.js | 42 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/action.yml b/action.yml index f316e48..64a991f 100644 --- a/action.yml +++ b/action.yml @@ -77,7 +77,24 @@ runs: DATA_FILE_PATH: ${{inputs.data_file_path}} VARS: ${{inputs.vars}} OUTPUT: 'json' - + + - name: dryrun stackql command + id: dryrun-query + shell: bash + run: | + ${{ env.STACKQL_DRYRUN_COMMAND }} + + - name: show rendered stackql query + uses: actions/github-script@v6 + with: + script: | + const path = require('path'); + const utilsPath = path.join(process.env.GITHUB_ACTION_PATH, 'lib', 'utils.js') + const {getStackqlCommand} = require(utilsPath) + showStackQLQuery(core) + env: + DRYRUN_RESULT: ${{steps.dryrun-query.outputs.stdout}} + - name: execute stackql command id: exec-query shell: bash @@ -97,9 +114,6 @@ runs: EXPECTED_RESULTS_STR: ${{ inputs.expected_results_str }} EXPECTED_RESULTS_FILE_PATH: ${{inputs.expected_results_file_path}} EXPECTED_ROWS: ${{inputs.expected_rows}} - - - branding: icon: 'terminal' diff --git a/lib/utils.js b/lib/utils.js index c9c6990..1ad8c12 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -28,6 +28,26 @@ function setupAuth(core) { core.exportVariable("AUTH", auth); } +async function showStackQLQuery(core) { + core = coreObj; + try { + let [ + dryRunResult, + ] = [ + process.env.DRYRUN_RESULT, + ]; + + if (!dryRunResult) { + core.setFailed("No Dryrun Output from stackql command"); + } + + core.info(`stackql query:\n${dryRunResult}`); + + } catch (e) { + core.setFailed(e); + } +} + async function getStackqlCommand(core) { const [query, queryFilePath, dataFilePath, vars, auth, output = "json"] = [ @@ -45,32 +65,43 @@ async function getStackqlCommand(core) { } let args = ["exec"]; + let dryRunArgs = ["exec", "-H"]; if (query) { args.push(`"${query}"`); + dryRunArgs.push(`"${query}"`); } else { - args.push("-i", queryFilePath); + args.push("-i", queryFilePath); + dryRunArgs.push("-i", queryFilePath); } - + if (checkEnvVarValid(dataFilePath)) { args.push(`--iqldata='${dataFilePath}'`); + dryRunArgs.push(`--iqldata='${dataFilePath}'`); } - + if (checkEnvVarValid(vars)) { args.push(`--var='${vars}'`); + dryRunArgs.push(`--var='${vars}'`); } if (checkEnvVarValid(auth)) { args.push(`--auth='${auth}'`); + dryRunArgs.push(`--auth='${auth}'`); } args.push(`--output='${output}'`); + dryRunArgs.push(`--output='text'`); try { - core.exportVariable('STACKQL_COMMAND', `stackql ${args.join(" ")}`) + const stackqlQuery = `stackql ${args.join(" ")}`; + const stackqlDryRunQuery = `stackql ${dryRunArgs.join(" ")}`; + core.exportVariable('STACKQL_COMMAND', stackqlQuery); + core.exportVariable('STACKQL_DRYRUN_COMMAND', stackqlDryRunQuery); + core.info(`stackql command:\n${stackqlQuery}`); } catch (error) { core.error(error); - core.setFailed("Error when executing stackql"); + core.setFailed("Error exporting stackql command"); } } @@ -88,4 +119,5 @@ const checkEnvVarValid = (variable) => { module.exports = { setupAuth, getStackqlCommand, + showStackQLQuery, }; From e74e599361edaedb2296ba73035959251df9ddaa Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Tue, 5 Sep 2023 08:00:05 +1000 Subject: [PATCH 03/34] dryrun to show rendered query --- action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action.yml b/action.yml index 64a991f..522e69c 100644 --- a/action.yml +++ b/action.yml @@ -90,7 +90,7 @@ runs: script: | const path = require('path'); const utilsPath = path.join(process.env.GITHUB_ACTION_PATH, 'lib', 'utils.js') - const {getStackqlCommand} = require(utilsPath) + const {showStackQLQuery} = require(utilsPath) showStackQLQuery(core) env: DRYRUN_RESULT: ${{steps.dryrun-query.outputs.stdout}} From 609138a80e0a70c47b002538b013d7d08d2d7d55 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Tue, 5 Sep 2023 08:01:41 +1000 Subject: [PATCH 04/34] dryrun to show rendered query --- lib/utils.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/utils.js b/lib/utils.js index 1ad8c12..4493c4f 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -29,7 +29,6 @@ function setupAuth(core) { } async function showStackQLQuery(core) { - core = coreObj; try { let [ dryRunResult, From 9aba197bd6924713fa1c69ba4689c6f36e1956ef Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Tue, 5 Sep 2023 08:07:22 +1000 Subject: [PATCH 05/34] dryrun to show rendered query --- action.yml | 1 - lib/utils.js | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/action.yml b/action.yml index 522e69c..f4e638b 100644 --- a/action.yml +++ b/action.yml @@ -42,7 +42,6 @@ runs: else echo "stackql_installed=false" >> $GITHUB_OUTPUT fi - - name: Setup StackQL uses: stackql/setup-stackql@v1.2.0 diff --git a/lib/utils.js b/lib/utils.js index 4493c4f..5210393 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -31,15 +31,18 @@ function setupAuth(core) { async function showStackQLQuery(core) { try { let [ + dryRunCommand, dryRunResult, ] = [ - process.env.DRYRUN_RESULT, + process.env.STACKQL_DRYRUN_COMMAND, + process.env.DRYRUN_RESULT, ]; if (!dryRunResult) { core.setFailed("No Dryrun Output from stackql command"); } + core.info(`stackql dryrun command:\n${dryRunCommand}`); core.info(`stackql query:\n${dryRunResult}`); } catch (e) { From f4c6dd85da508fa14269ca0cd2b128d8dc2ce76b Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Tue, 5 Sep 2023 08:19:04 +1000 Subject: [PATCH 06/34] dryrun to show rendered query --- lib/utils.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 5210393..dde7630 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -42,8 +42,8 @@ async function showStackQLQuery(core) { core.setFailed("No Dryrun Output from stackql command"); } - core.info(`stackql dryrun command:\n${dryRunCommand}`); - core.info(`stackql query:\n${dryRunResult}`); + core.info(`šŸš€ stackql dryrun command:\n${dryRunCommand}`); + core.info(`šŸš€ stackql query:\n${dryRunResult}`); } catch (e) { core.setFailed(e); @@ -100,7 +100,7 @@ async function getStackqlCommand(core) { const stackqlDryRunQuery = `stackql ${dryRunArgs.join(" ")}`; core.exportVariable('STACKQL_COMMAND', stackqlQuery); core.exportVariable('STACKQL_DRYRUN_COMMAND', stackqlDryRunQuery); - core.info(`stackql command:\n${stackqlQuery}`); + core.info(`šŸš€ stackql command:\n${stackqlQuery}`); } catch (error) { core.error(error); core.setFailed("Error exporting stackql command"); From 3169e00c7ec08b26d9808b3641b8999cd72d7820 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Tue, 5 Sep 2023 09:27:23 +1000 Subject: [PATCH 07/34] dryrun to show rendered query --- .github/workflows/stackql-assert.yml | 2 +- action.yml | 10 ++++++---- lib/assert.js | 17 ++++++++++------- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/.github/workflows/stackql-assert.yml b/.github/workflows/stackql-assert.yml index e35cc98..0473d19 100644 --- a/.github/workflows/stackql-assert.yml +++ b/.github/workflows/stackql-assert.yml @@ -60,7 +60,7 @@ jobs: # # Example `test_query_file_path` with `expected_rows` supplying `vars` using `jsonnet` config provided using `data_file_path` # - - name: Use test query string and expected results file, with auth object + - name: Use test query file with a jsonnet data file sourcing external vars and an expected row count uses: ./ with: test_query_file_path: './.github/workflows/workflow_scripts/github-example.iql' diff --git a/action.yml b/action.yml index f4e638b..f7ef0f0 100644 --- a/action.yml +++ b/action.yml @@ -33,7 +33,7 @@ inputs: runs: using: "composite" steps: - - name: Check StackQL is installed and set output + - name: check if stackql is installed and set output id: check-stackql shell: bash run: | @@ -43,13 +43,14 @@ runs: echo "stackql_installed=false" >> $GITHUB_OUTPUT fi - - name: Setup StackQL + - name: setup stackql uses: stackql/setup-stackql@v1.2.0 if: ${{steps.check-stackql.outputs.stackql_installed == 'false'}} with: use_wrapper: true - - name: Setup auth + - name: setup auth + if: (inputs.auth_obj_path != '') || (inputs.auth_str != '') id: setup-auth uses: actions/github-script@v6 with: @@ -92,7 +93,8 @@ runs: const {showStackQLQuery} = require(utilsPath) showStackQLQuery(core) env: - DRYRUN_RESULT: ${{steps.dryrun-query.outputs.stdout}} + # DRYRUN_RESULT: ${{steps.dryrun-query.outputs.stdout}} + DRYRUN_RESULT: ${{steps}} - name: execute stackql command id: exec-query diff --git a/lib/assert.js b/lib/assert.js index d1d1825..f498458 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -10,7 +10,7 @@ const parseResult = (resultStr, varName) => { const jsonObj = JSON.parse(jsonStr); // parse the JSON string into an object return jsonObj; } catch (error) { - throw(`Failed to parse ${varName} JSON + throw(`āŒ Failed to parse ${varName} JSON \nvalue: ${resultStr} \nerror: ${error}`); } @@ -34,18 +34,21 @@ const checkResult = (expectedResult, expectedRows, actualResult) => { let equality; let message; expectedRows = parseInt(expectedRows); + const successMessage = `āœ… StackQL Assert Successful`; + const failureMessage = `āŒ StackQL Assert Failed`; + // if only passed expectedRows, check expectedRows // if only passed expected result, only check expected result // if both passed, check both if (expectedRows) { equality = actualResult.length === expectedRows; if(equality) { - message = `============ StackQL Assert Successful (row count) ============ \n + message = `============ ${successMessage} (row count) ============ \n Expected Number of Rows: ${expectedRows} \n Actual Number of Rows: ${actualResult.length} \n `; } else { - message = `============ StackQL Assert Failed (row count) ============ \n + message = `============ ${failureMessage} (row count) ============ \n Expected Number of Rows: ${expectedRows} \n Actual Number of Rows: ${actualResult.length} \n Execution Result: ${JSON.stringify(actualResult)} \n @@ -56,9 +59,9 @@ const checkResult = (expectedResult, expectedRows, actualResult) => { if (expectedResult) { equality = JSON.stringify(expectedResult) === JSON.stringify(actualResult); if(equality) { - message = `============ StackQL Assert Successful (expected results) ============`; + message = `============ ${successMessage} (expected results) ============`; } else { - message = `============ StackQL Assert Failed (expected results) ============ \n + message = `============ ${failureMessage} (expected results) ============ \n Expected: ${JSON.stringify(expectedResult)}\n Actual: ${JSON.stringify(actualResult)} `; @@ -80,7 +83,7 @@ function checkParameters(expectedResultStr, expectedResultFilePath, expectedRows .map(param => param.name) if (missingParams.length === 3) { - const errorMessage = "Cannot find expected result, file path or expected rows"; + const errorMessage = "āŒ Cannot find expected result, file path or expected rows"; throw errorMessage; } } @@ -111,7 +114,7 @@ const assertResult = (coreObj) =>{ const actualResult = parseResult(execResultStr); if (!actualResult) { - core.setFailed("No Output from executing query"); + core.setFailed(`āŒ No Output from executing query`); } const {equality, message} = checkResult(expectedResult, expectedRows, actualResult); From 21965a94e0f8f3056b572cd85d63363b89037328 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Tue, 5 Sep 2023 09:29:39 +1000 Subject: [PATCH 08/34] dryrun to show rendered query --- lib/tests/assert.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tests/assert.test.js b/lib/tests/assert.test.js index d3d1956..b9841c9 100644 --- a/lib/tests/assert.test.js +++ b/lib/tests/assert.test.js @@ -133,7 +133,7 @@ describe('getExpectedResult', ()=>{ assertResult(coreObj) - expect(coreObj.setFailed).toHaveBeenCalledWith('Cannot find expected result, file path or expected rows') + expect(coreObj.setFailed).toHaveBeenCalledWith('āŒ Cannot find expected result, file path or expected rows') }); it('it should setFailed when actual result is not equal to expected result', () => { From d995c6b74d0642f8511a23c5ea84809c2e860f87 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Tue, 5 Sep 2023 09:35:56 +1000 Subject: [PATCH 09/34] dryrun to show rendered query --- lib/utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/utils.js b/lib/utils.js index dde7630..adb4a03 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -94,6 +94,7 @@ async function getStackqlCommand(core) { args.push(`--output='${output}'`); dryRunArgs.push(`--output='text'`); + dryRunArgs.push(`--dryrun`); try { const stackqlQuery = `stackql ${args.join(" ")}`; From 25d669f8cde71e4f5ee91e57684b457a67d97b1d Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Tue, 5 Sep 2023 09:40:02 +1000 Subject: [PATCH 10/34] dryrun to show rendered query --- action.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/action.yml b/action.yml index f7ef0f0..f19148c 100644 --- a/action.yml +++ b/action.yml @@ -93,8 +93,7 @@ runs: const {showStackQLQuery} = require(utilsPath) showStackQLQuery(core) env: - # DRYRUN_RESULT: ${{steps.dryrun-query.outputs.stdout}} - DRYRUN_RESULT: ${{steps}} + DRYRUN_RESULT: ${{steps.dryrun-query.outputs.stdout}} - name: execute stackql command id: exec-query From eae10df74ba856f04677516e56347a4f9d340644 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Tue, 5 Sep 2023 09:44:18 +1000 Subject: [PATCH 11/34] dryrun to show rendered query --- lib/utils.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index adb4a03..0a6f4b0 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -42,8 +42,7 @@ async function showStackQLQuery(core) { core.setFailed("No Dryrun Output from stackql command"); } - core.info(`šŸš€ stackql dryrun command:\n${dryRunCommand}`); - core.info(`šŸš€ stackql query:\n${dryRunResult}`); + core.info(`\nšŸš€ rendered stackql query:\n${dryRunResult}`); } catch (e) { core.setFailed(e); @@ -101,7 +100,6 @@ async function getStackqlCommand(core) { const stackqlDryRunQuery = `stackql ${dryRunArgs.join(" ")}`; core.exportVariable('STACKQL_COMMAND', stackqlQuery); core.exportVariable('STACKQL_DRYRUN_COMMAND', stackqlDryRunQuery); - core.info(`šŸš€ stackql command:\n${stackqlQuery}`); } catch (error) { core.error(error); core.setFailed("Error exporting stackql command"); From 1d93bccb4ea0fcf6a51b2e109eed5d87a6f88cca Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Sun, 28 Apr 2024 18:28:23 +1000 Subject: [PATCH 12/34] use stackql-exec --- action.yml | 121 ++++++++++++++--------------------------- lib/assert.js | 148 ++++++++++++-------------------------------------- 2 files changed, 76 insertions(+), 193 deletions(-) diff --git a/action.yml b/action.yml index f19148c..8b35fc3 100644 --- a/action.yml +++ b/action.yml @@ -1,120 +1,81 @@ name: 'StackQL Studios - StackQL Assert' -description: 'run StackQL query to test and audit your infrastructure.' +description: 'Run StackQL query to test and audit your infrastructure.' author: 'Yuncheng Yang, StackQL Studios' inputs: test_query: - description: stackql query to execute (need to supply either test_query or test_query_file_path) + description: 'StackQL query to execute (supply either test_query or test_query_file_path)' required: false test_query_file_path: - description: stackql query file to execute (need to supply either test_query or test_query_file_path) + description: 'StackQL query file to execute (supply either test_query or test_query_file_path)' required: false data_file_path: - description: path to data file to pass to the stackql query preprocessor (json or jsonnet file) + description: 'Path to data file to pass to the StackQL query preprocessor (JSON or Jsonnet file)' required: false vars: - description: comma delimited list of variables to pass to the stackql query preprocessor (supported with jsonnet config blocks or jsonnet data files only), accepts 'var1=val1,var2=val2', can be used to source environment variables into stackql queries + description: 'Comma delimited list of variables to pass to the StackQL query preprocessor (supported with Jsonnet config blocks or Jsonnet data files only)' required: false - expected_rows: - description: expected number of rows from executing test query + expected_rows: + description: 'Expected number of rows from executing test query' required: false expected_results_str: - description: expected result (as a json string) from executing test query, overrides expected_results_file_path + description: 'Expected result (as a JSON string) from executing test query, overrides expected_results_file_path' required: false expected_results_file_path: - description: json file with the expected result + description: 'JSON file with the expected result' required: false auth_obj_path: - description: the path of json file that stores stackql AUTH string (only required when using non-standard environment variable names) + description: 'Path of JSON file that stores StackQL AUTH string (only required when using non-standard environment variable names)' required: false auth_str: - description: stackql AUTH string (only required when using non-standard environment variable names) + description: 'StackQL AUTH string (only required when using non-standard environment variable names)' required: false runs: using: "composite" steps: - - name: check if stackql is installed and set output - id: check-stackql - shell: bash - run: | - if command -v stackql &> /dev/null; then - echo "stackql_installed=true" >> $GITHUB_OUTPUT - else - echo "stackql_installed=false" >> $GITHUB_OUTPUT - fi - - - name: setup stackql - uses: stackql/setup-stackql@v1.2.0 - if: ${{steps.check-stackql.outputs.stackql_installed == 'false'}} + - name: Setup StackQL + uses: stackql/setup-stackql@v2.1.0 with: use_wrapper: true - - name: setup auth - if: (inputs.auth_obj_path != '') || (inputs.auth_str != '') - id: setup-auth - uses: actions/github-script@v6 + - name: Execute StackQL Command (Dry Run) + id: exec-query-dry-run + uses: stackql/stackql-exec@v2.1.1 with: - script: | - const path = require('path'); - const utilsPath = path.join(process.env.GITHUB_ACTION_PATH, 'lib', 'utils.js') - const {setupAuth} = require(utilsPath) - setupAuth(core) - env: - AUTH_FILE_PATH: ${{ inputs.auth_obj_path }} - AUTH_STR: ${{inputs.auth_str}} - - - name: get stackql command - uses: actions/github-script@v6 - with: - script: | - const path = require('path'); - const utilsPath = path.join(process.env.GITHUB_ACTION_PATH, 'lib', 'utils.js') - const {getStackqlCommand} = require(utilsPath) - getStackqlCommand(core) - env: - QUERY_FILE_PATH: ${{ inputs.test_query_file_path }} - QUERY: ${{inputs.test_query}} - DATA_FILE_PATH: ${{inputs.data_file_path}} - VARS: ${{inputs.vars}} - OUTPUT: 'json' + query: ${{ inputs.test_query }} + query_file_path: ${{ inputs.test_query_file_path }} + data_file_path: ${{ inputs.data_file_path }} + vars: ${{ inputs.vars }} + auth_obj_path: ${{ inputs.auth_obj_path }} + auth_str: ${{ inputs.auth_str }} + dry_run: true - - name: dryrun stackql command - id: dryrun-query - shell: bash - run: | - ${{ env.STACKQL_DRYRUN_COMMAND }} - - - name: show rendered stackql query - uses: actions/github-script@v6 + - name: Execute StackQL Command + id: exec-query + uses: stackql/stackql-exec@v2.1.1 with: - script: | - const path = require('path'); - const utilsPath = path.join(process.env.GITHUB_ACTION_PATH, 'lib', 'utils.js') - const {showStackQLQuery} = require(utilsPath) - showStackQLQuery(core) - env: - DRYRUN_RESULT: ${{steps.dryrun-query.outputs.stdout}} + query: ${{ inputs.test_query }} + query_file_path: ${{ inputs.test_query_file_path }} + data_file_path: ${{ inputs.data_file_path }} + vars: ${{ inputs.vars }} + auth_obj_path: ${{ inputs.auth_obj_path }} + auth_str: ${{ inputs.auth_str }} + dry_run: false - - name: execute stackql command - id: exec-query - shell: bash - run: | - ${{ env.STACKQL_COMMAND }} - - - name: Check results - uses: actions/github-script@v6 + - name: Check Results + uses: actions/github-script@v7.0.1 with: script: | const path = require('path'); - const assertPath = path.join(process.env.GITHUB_ACTION_PATH, 'stackql-assert.js') + const assertPath = path.join(process.env.GITHUB_ACTION_PATH, 'lib', 'assert.js') const {assertResult} = require(assertPath) assertResult(core) env: - RESULT: ${{steps.exec-query.outputs.stdout}} + RESULT: ${{ steps.exec-query.outputs.stackql-query-results }} EXPECTED_RESULTS_STR: ${{ inputs.expected_results_str }} - EXPECTED_RESULTS_FILE_PATH: ${{inputs.expected_results_file_path}} - EXPECTED_ROWS: ${{inputs.expected_rows}} - + EXPECTED_RESULTS_FILE_PATH: ${{ inputs.expected_results_file_path }} + EXPECTED_ROWS: ${{ inputs.expected_rows }} + branding: icon: 'terminal' - color: 'green' \ No newline at end of file + color: 'green' diff --git a/lib/assert.js b/lib/assert.js index f498458..2cd9920 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -1,136 +1,58 @@ -let core; const fs = require("fs"); -const parseResult = (resultStr, varName) => { - const regex = /\[(.*)\]/; // match the first occurrence of square brackets and capture everything in between - const matches = resultStr.match(regex); - if (matches) { - const jsonStr = matches[0]; // extract the captured string - try { - const jsonObj = JSON.parse(jsonStr); // parse the JSON string into an object - return jsonObj; - } catch (error) { - throw(`āŒ Failed to parse ${varName} JSON - \nvalue: ${resultStr} - \nerror: ${error}`); - } +const parseResult = (resultStr, description = "result") => { + try { + return JSON.parse(resultStr); + } catch (error) { + throw new Error(`āŒ Failed to parse ${description} JSON: + Value: ${resultStr} + Error: ${error.message}`); } - return null; // return null if no JSON object was found in the string }; const getExpectedResult = (expectedResultStr, expectedResultFilePath) => { - let expectedResult; if (expectedResultStr) { - expectedResult = parseResult(expectedResultStr, "expectedResultStr"); + return parseResult(expectedResultStr, "expected results string"); } else if (expectedResultFilePath) { - const fileContent = fs.readFileSync(expectedResultFilePath).toString(); - expectedResult = parseResult(fileContent, "expectedResultFilePath"); + const fileContent = fs.readFileSync(expectedResultFilePath, 'utf-8'); + return parseResult(fileContent, "expected results file"); } - - return expectedResult; + throw new Error("No expected result provided."); }; -const checkResult = (expectedResult, expectedRows, actualResult) => { - let equality; - let message; - expectedRows = parseInt(expectedRows); - const successMessage = `āœ… StackQL Assert Successful`; - const failureMessage = `āŒ StackQL Assert Failed`; +const checkResult = (core, expected, actual, expectedRows) => { + const actualLength = actual.length; - // if only passed expectedRows, check expectedRows - // if only passed expected result, only check expected result - // if both passed, check both - if (expectedRows) { - equality = actualResult.length === expectedRows; - if(equality) { - message = `============ ${successMessage} (row count) ============ \n - Expected Number of Rows: ${expectedRows} \n - Actual Number of Rows: ${actualResult.length} \n - `; - } else { - message = `============ ${failureMessage} (row count) ============ \n - Expected Number of Rows: ${expectedRows} \n - Actual Number of Rows: ${actualResult.length} \n - Execution Result: ${JSON.stringify(actualResult)} \n - `; - } + if (expectedRows && actualLength !== parseInt(expectedRows)) { + core.error(`Expected rows: ${expectedRows}, got: ${actualLength}`); + return false; } - if (expectedResult) { - equality = JSON.stringify(expectedResult) === JSON.stringify(actualResult); - if(equality) { - message = `============ ${successMessage} (expected results) ============`; - } else { - message = `============ ${failureMessage} (expected results) ============ \n - Expected: ${JSON.stringify(expectedResult)}\n - Actual: ${JSON.stringify(actualResult)} - `; - } + if (expected && JSON.stringify(expected) !== JSON.stringify(actual)) { + core.error(`Expected results do not match actual results.\nExpected: ${JSON.stringify(expected)}\nActual: ${JSON.stringify(actual)}`); + return false; } - return { equality, message }; + return true; }; -function checkParameters(expectedResultStr, expectedResultFilePath, expectedRows) { - const params = [ - { name: "expectedResultStr", value: expectedResultStr }, - { name: "expectedResultFilePath", value: expectedResultFilePath }, - { name: "expectedRows", value: expectedRows }, - ]; - - const missingParams = params - .filter(param => !param.value || param.value === "undefined") - .map(param => param.name) - - if (missingParams.length === 3) { - const errorMessage = "āŒ Cannot find expected result, file path or expected rows"; - throw errorMessage; - } -} +function assertResult(core) { + try { + const { RESULT, EXPECTED_RESULTS_STR, EXPECTED_RESULTS_FILE_PATH, EXPECTED_ROWS } = process.env; + if (!RESULT) throw new Error("Result from StackQL execution is missing."); + const actualResult = parseResult(RESULT); + const expectedResult = getExpectedResult(EXPECTED_RESULTS_STR, EXPECTED_RESULTS_FILE_PATH); -const assertResult = (coreObj) =>{ - core = coreObj; - try { - let [ - execResultStr, - expectedResultStr, - expectedResultFilePath, - expectedRows, - ] = [ - process.env.RESULT, - process.env.EXPECTED_RESULTS_STR, - process.env.EXPECTED_RESULTS_FILE_PATH, - process.env.EXPECTED_ROWS, - ]; - - checkParameters(expectedResultStr, expectedResultFilePath, expectedRows) - - let expectedResult = getExpectedResult( - expectedResultStr, - expectedResultFilePath - ); - - const actualResult = parseResult(execResultStr); - - if (!actualResult) { - core.setFailed(`āŒ No Output from executing query`); - } - - const {equality, message} = checkResult(expectedResult, expectedRows, actualResult); - if (equality) { - core.info(message); - } else { - core.setFailed(message); - } - } catch (e) { - core.setFailed(e); + const resultSuccessful = checkResult(core, expectedResult, actualResult, EXPECTED_ROWS); + if (resultSuccessful) { + core.info("āœ… StackQL Assert Successful"); + } else { + core.setFailed("āŒ StackQL Assert Failed"); } + } catch (error) { + core.setFailed(`Assertion error: ${error.message}`); + } } -module.exports = { - assertResult, - parseResult, - checkResult, - getExpectedResult -}; +module.exports = { assertResult }; From 70532c698823c41bc65e00cb0671f8068a429de9 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Sun, 28 Apr 2024 18:32:20 +1000 Subject: [PATCH 13/34] use stackql-exec --- .github/workflows/stackql-assert.yml | 11 ++++++++++- .github/workflows/test.yml | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stackql-assert.yml b/.github/workflows/stackql-assert.yml index 0473d19..e9ad08f 100644 --- a/.github/workflows/stackql-assert.yml +++ b/.github/workflows/stackql-assert.yml @@ -1,4 +1,4 @@ -name: 'StackQL Assert' +name: 'stackql-assert' on: push: @@ -17,6 +17,15 @@ jobs: - name: Checkout uses: actions/checkout@v3 + # + # Pull required providers + # + - name: Use test query string and expected rows + uses: stackql/stackql-exec@v2.1.1 + with: + is_command: true + query: "REGISTRY PULL google" + # # Example `test_query` with `expected_rows` # diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ad90499..bd58ebf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: 'Build and Test' +name: 'npm test' on: push: pull_request: From 6a8344efd69d3feaac81d6f245524c7b4ea02235 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Sun, 28 Apr 2024 18:35:40 +1000 Subject: [PATCH 14/34] use stackql-exec --- .github/workflows/stackql-assert.yml | 3 +- lib/tests/utils.test.js | 140 +++++++++------------------ 2 files changed, 49 insertions(+), 94 deletions(-) diff --git a/.github/workflows/stackql-assert.yml b/.github/workflows/stackql-assert.yml index e9ad08f..2a64f17 100644 --- a/.github/workflows/stackql-assert.yml +++ b/.github/workflows/stackql-assert.yml @@ -20,7 +20,7 @@ jobs: # # Pull required providers # - - name: Use test query string and expected rows + - name: pull required providers uses: stackql/stackql-exec@v2.1.1 with: is_command: true @@ -33,7 +33,6 @@ jobs: uses: ./ with: test_query: | - REGISTRY PULL google; SELECT name FROM google.compute.instances WHERE project = 'stackql-demo' AND zone = 'australia-southeast1-a' AND name = 'stackql-demo-001'; diff --git a/lib/tests/utils.test.js b/lib/tests/utils.test.js index 4284027..ab73fb7 100644 --- a/lib/tests/utils.test.js +++ b/lib/tests/utils.test.js @@ -1,123 +1,79 @@ -const { setupAuth, getStackqlCommand } = require("../utils"); +const { assertResult, parseResult, getExpectedResult } = require('./assert'); +const fs = require('fs'); -describe("util", () => { +jest.mock('fs'); + +describe('assert.js functions', () => { let core; - const expectedAuth = '{ "google": { "type": "service_account", "credentialsfilepath": "sa-key.json" }}'; beforeEach(() => { core = { - setFailed: jest.fn(), info: jest.fn(), - exportVariable: jest.fn(), error: jest.fn(), + setFailed: jest.fn() }; + process.env.RESULT = JSON.stringify([{ id: 1, value: 'test' }]); + process.env.EXPECTED_ROWS = '1'; }); - describe("setupAuth", () => { - let AUTH_ENV = { - AUTH_STR: expectedAuth, - AUTH_FILE_PATH: "./lib/tests/test-auth.json", - }; - - beforeEach(() => { - jest.resetModules(); - process.env = { ...AUTH_ENV }; - }); + afterEach(() => { + jest.resetAllMocks(); + jest.restoreAllMocks(); + }); - afterEach(() => { - process.env = AUTH_ENV; + describe('parseResult', () => { + it('should correctly parse valid JSON', () => { + const input = JSON.stringify({ key: 'value' }); + expect(parseResult(input, 'valid JSON')).toEqual({ key: 'value' }); }); - it("should not throw an error when neither AUTH_STR or AUTH_FILE_PATH is set", () => { - process.env.AUTH_STR = undefined; - process.env.AUTH_FILE_PATH = undefined; - - setupAuth(core); - - expect(core.setFailed).not.toBeCalled(); + it('should throw an error on invalid JSON', () => { + const input = "invalid JSON"; + expect(() => parseResult(input, 'invalid JSON')).toThrow('Failed to parse invalid JSON JSON'); }); + }); - it("should set AUTH environment variable when AUTH_STR is set", () => { - process.env.AUTH_FILE_PATH = undefined; - - setupAuth(core); - - expect(core.exportVariable).toBeCalledWith("AUTH", expectedAuth); + describe('getExpectedResult', () => { + it('should return parsed result from string', () => { + const input = JSON.stringify({ key: 'value' }); + expect(getExpectedResult(input, null)).toEqual({ key: 'value' }); }); - it("should set AUTH environment variable when AUTH_FILE_PATH is set", () => { - process.env.AUTH_STR = undefined; - - setupAuth(core); - - expect(core.exportVariable).toBeCalledWith("AUTH", expectedAuth); + it('should return parsed result from file', () => { + const input = JSON.stringify({ key: 'value' }); + fs.readFileSync.mockReturnValue(input); + expect(getExpectedResult(null, 'path/to/file')).toEqual({ key: 'value' }); + expect(fs.readFileSync).toHaveBeenCalledWith('path/to/file', 'utf-8'); }); - it("should throw error when AUTH_FILE_PATH is set but file does not exist", () => { - process.env.AUTH_STR = undefined; - process.env.AUTH_FILE_PATH = "./failed-test-auth.json"; - - setupAuth(core); - - expect(core.setFailed).toBeCalledWith(`Cannot find auth file ${process.env.AUTH_FILE_PATH}`); + it('should throw an error if no input is provided', () => { + expect(() => getExpectedResult(null, null)).toThrow('No expected result provided.'); }); }); - describe("getStackqlCommand", () => { - const EXECUTE_ENV = { - QUERY: "test", - QUERY_FILE_PATH: "test-query.iql", - AUTH: "test-auth", - }; - - beforeEach(() => { - jest.resetModules(); - process.env = { ...EXECUTE_ENV }; + describe('assertResult', () => { + it('should log success if the expected rows and results match', () => { + process.env.EXPECTED_RESULTS_STR = process.env.RESULT; + assertResult(core); + expect(core.info).toHaveBeenCalledWith("āœ… StackQL Assert Successful"); }); - afterEach(() => { - process.env = EXECUTE_ENV; - jest.clearAllMocks(); + it('should fail if expected rows do not match', () => { + process.env.EXPECTED_ROWS = '2'; // Actual result will have only one item + assertResult(core); + expect(core.setFailed).toHaveBeenCalledWith(expect.stringContaining("Expected rows: 2, got: 1")); }); - it("should return error when there is neither query or query file path", () => { - process.env.QUERY = undefined; - process.env.QUERY_FILE_PATH = undefined; - - getStackqlCommand(core); - - expect(core.setFailed).toBeCalledWith("Either test_query or test_query_file_path need to be set"); + it('should fail if expected results do not match', () => { + process.env.EXPECTED_RESULTS_STR = JSON.stringify([{ id: 1, value: 'wrong' }]); + assertResult(core); + expect(core.setFailed).toHaveBeenCalledWith(expect.stringContaining("Expected results do not match actual results.")); }); - // it("should not return error when there is no AUTH", () => { - // process.env.AUTH = undefined; - - // getStackqlCommand(core); - - // expect(core.setFailed).not.toBeCalled(); - // expect(core.exportVariable).toBeCalledWith( - // "STACKQL_COMMAND", "stackql exec \"test\" --output='json'" - // ); - // }); - - it("should execute stackql with query file path", () => { - process.env.QUERY = undefined; - - getStackqlCommand(core); - - expect(core.exportVariable).toBeCalledWith( - "STACKQL_COMMAND", "stackql exec -i test-query.iql --auth='test-auth' --output='json'" - ); - }); - - it("should execute stackql with query", () => { - process.env.QUERY_FILE_PATH = undefined; - - getStackqlCommand(core); - - expect(core.exportVariable).toBeCalledWith( - "STACKQL_COMMAND", "stackql exec \"test\" --auth='test-auth' --output='json'" - ); + it('should handle errors during processing', () => { + process.env.RESULT = 'invalid json'; + assertResult(core); + expect(core.setFailed).toHaveBeenCalledWith(expect.stringContaining("Assertion error")); }); }); }); From d0e85a5da508e82de9fe68266efc95b7e4943d3f Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Sun, 28 Apr 2024 18:46:39 +1000 Subject: [PATCH 15/34] use stackql-exec --- .github/workflows/{stackql-assert.yml => stackql-assert-test.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{stackql-assert.yml => stackql-assert-test.yml} (100%) diff --git a/.github/workflows/stackql-assert.yml b/.github/workflows/stackql-assert-test.yml similarity index 100% rename from .github/workflows/stackql-assert.yml rename to .github/workflows/stackql-assert-test.yml From 5fac664da0286c6a531201cc25698da9626c595b Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Sun, 28 Apr 2024 19:06:53 +1000 Subject: [PATCH 16/34] use stackql-exec --- action.yml | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/action.yml b/action.yml index 8b35fc3..1ff4447 100644 --- a/action.yml +++ b/action.yml @@ -38,21 +38,9 @@ runs: with: use_wrapper: true - - name: Execute StackQL Command (Dry Run) - id: exec-query-dry-run - uses: stackql/stackql-exec@v2.1.1 - with: - query: ${{ inputs.test_query }} - query_file_path: ${{ inputs.test_query_file_path }} - data_file_path: ${{ inputs.data_file_path }} - vars: ${{ inputs.vars }} - auth_obj_path: ${{ inputs.auth_obj_path }} - auth_str: ${{ inputs.auth_str }} - dry_run: true - - name: Execute StackQL Command id: exec-query - uses: stackql/stackql-exec@v2.1.1 + uses: stackql/stackql-exec@v2.1.2 with: query: ${{ inputs.test_query }} query_file_path: ${{ inputs.test_query_file_path }} From 4d6454adc115e39c3f9fa75517da64c1f456a52a Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Sun, 28 Apr 2024 19:11:57 +1000 Subject: [PATCH 17/34] use stackql-exec --- .github/workflows/stackql-assert-test.yml | 2 +- lib/assert.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/stackql-assert-test.yml b/.github/workflows/stackql-assert-test.yml index 2a64f17..222760e 100644 --- a/.github/workflows/stackql-assert-test.yml +++ b/.github/workflows/stackql-assert-test.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4.1.4 # # Pull required providers diff --git a/lib/assert.js b/lib/assert.js index 2cd9920..156bc8c 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -39,6 +39,11 @@ const checkResult = (core, expected, actual, expectedRows) => { function assertResult(core) { try { const { RESULT, EXPECTED_RESULTS_STR, EXPECTED_RESULTS_FILE_PATH, EXPECTED_ROWS } = process.env; + core.info(`RESULT: ${RESULT}`); + core.info(`EXPECTED_RESULTS_STR: ${EXPECTED_RESULTS_STR}`); + core.info(`EXPECTED_RESULTS_FILE_PATH: ${EXPECTED_RESULTS_FILE_PATH}`); + core.info(`EXPECTED_ROWS: ${EXPECTED_ROWS}`); + if (!RESULT) throw new Error("Result from StackQL execution is missing."); const actualResult = parseResult(RESULT); From 740b3e596187308135495652750c6300e607580b Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Sun, 28 Apr 2024 19:14:08 +1000 Subject: [PATCH 18/34] use stackql-exec --- lib/assert.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/assert.js b/lib/assert.js index 156bc8c..b96ed18 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -47,6 +47,9 @@ function assertResult(core) { if (!RESULT) throw new Error("Result from StackQL execution is missing."); const actualResult = parseResult(RESULT); + + core.info("šŸ” Checking results..."); + const expectedResult = getExpectedResult(EXPECTED_RESULTS_STR, EXPECTED_RESULTS_FILE_PATH); const resultSuccessful = checkResult(core, expectedResult, actualResult, EXPECTED_ROWS); From fde1743a3b9470744d33e007ba6727c27f899665 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Sun, 28 Apr 2024 19:19:24 +1000 Subject: [PATCH 19/34] use stackql-exec --- .github/workflows/test.yml | 18 ------------------ lib/assert.js | 3 ++- 2 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index bd58ebf..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 'npm test' -on: - push: - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: Use Node.js 16 - uses: actions/setup-node@v3 - with: - node-version: 16.x - - run: npm ci - - run: npm test - \ No newline at end of file diff --git a/lib/assert.js b/lib/assert.js index b96ed18..f82946a 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -17,7 +17,7 @@ const getExpectedResult = (expectedResultStr, expectedResultFilePath) => { const fileContent = fs.readFileSync(expectedResultFilePath, 'utf-8'); return parseResult(fileContent, "expected results file"); } - throw new Error("No expected result provided."); + return null; }; const checkResult = (core, expected, actual, expectedRows) => { @@ -53,6 +53,7 @@ function assertResult(core) { const expectedResult = getExpectedResult(EXPECTED_RESULTS_STR, EXPECTED_RESULTS_FILE_PATH); const resultSuccessful = checkResult(core, expectedResult, actualResult, EXPECTED_ROWS); + if (resultSuccessful) { core.info("āœ… StackQL Assert Successful"); } else { From 353abe33ca71a231919842e27728f75eb062cc72 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Sun, 28 Apr 2024 19:22:50 +1000 Subject: [PATCH 20/34] use stackql-exec --- .github/workflows/stackql-assert-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stackql-assert-test.yml b/.github/workflows/stackql-assert-test.yml index 222760e..ae82946 100644 --- a/.github/workflows/stackql-assert-test.yml +++ b/.github/workflows/stackql-assert-test.yml @@ -24,7 +24,7 @@ jobs: uses: stackql/stackql-exec@v2.1.1 with: is_command: true - query: "REGISTRY PULL google" + query: "REGISTRY PULL google; REGISTRY PULL github" # # Example `test_query` with `expected_rows` From 17d6ddb2749ff0a9812556628dc5ada2409e1815 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Sun, 28 Apr 2024 19:24:11 +1000 Subject: [PATCH 21/34] use stackql-exec --- .github/workflows/workflow_scripts/github-example.iql | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/workflow_scripts/github-example.iql b/.github/workflows/workflow_scripts/github-example.iql index 68dadb5..5dc84fb 100644 --- a/.github/workflows/workflow_scripts/github-example.iql +++ b/.github/workflows/workflow_scripts/github-example.iql @@ -1,2 +1 @@ -REGISTRY PULL github; SELECT name FROM github.repos.repos WHERE org = '{{ .org }}' AND name = '{{ .repo }}'; From 95ab4fa4c47ffac7cb3be93c7b4fda215345a2c2 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Mon, 29 Apr 2024 08:02:11 +1000 Subject: [PATCH 22/34] updated setup-stackql version --- .github/workflows/stackql-assert-test.yml | 2 +- README.md | 1 - action.yml | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/stackql-assert-test.yml b/.github/workflows/stackql-assert-test.yml index ae82946..a73b85b 100644 --- a/.github/workflows/stackql-assert-test.yml +++ b/.github/workflows/stackql-assert-test.yml @@ -21,7 +21,7 @@ jobs: # Pull required providers # - name: pull required providers - uses: stackql/stackql-exec@v2.1.1 + uses: stackql/stackql-exec@v2.2.0 with: is_command: true query: "REGISTRY PULL google; REGISTRY PULL github" diff --git a/README.md b/README.md index 34b4661..f1d348e 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,6 @@ The following excerpts from a GitHub Actions workflow demonstrate how to use the uses: ./ with: test_query: | - REGISTRY PULL google; SELECT name FROM google.compute.instances WHERE project = 'stackql-demo' AND zone = 'australia-southeast1-a' AND name = 'stackql-demo-001'; diff --git a/action.yml b/action.yml index 1ff4447..1ea839b 100644 --- a/action.yml +++ b/action.yml @@ -34,13 +34,13 @@ runs: using: "composite" steps: - name: Setup StackQL - uses: stackql/setup-stackql@v2.1.0 + uses: stackql/setup-stackql@v2.2.0 with: use_wrapper: true - name: Execute StackQL Command id: exec-query - uses: stackql/stackql-exec@v2.1.2 + uses: stackql/stackql-exec@v2.2.0 with: query: ${{ inputs.test_query }} query_file_path: ${{ inputs.test_query_file_path }} From 306ceba6fbf7940a92c85eb8f564e34a6b291d1e Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Mon, 29 Apr 2024 08:31:51 +1000 Subject: [PATCH 23/34] updated to use exec and setup v2.2.1 --- .github/workflows/stackql-assert-test.yml | 2 +- README.md | 2 ++ action.yml | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/stackql-assert-test.yml b/.github/workflows/stackql-assert-test.yml index a73b85b..a7615e3 100644 --- a/.github/workflows/stackql-assert-test.yml +++ b/.github/workflows/stackql-assert-test.yml @@ -21,7 +21,7 @@ jobs: # Pull required providers # - name: pull required providers - uses: stackql/stackql-exec@v2.2.0 + uses: stackql/stackql-exec@v2.2.1 with: is_command: true query: "REGISTRY PULL google; REGISTRY PULL github" diff --git a/README.md b/README.md index f1d348e..c066007 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![StackQL Assert](https://github.com/stackql/stackql-assert/actions/workflows/stackql-assert-test.yml/badge.svg)](https://github.com/stackql/stackql-assert/actions/workflows/stackql-assert-test.yml) + # stackql-assert The `stackql/stackql-assert` action is an composite action that runs a `stackql` query and checks if the result matches an expected result diff --git a/action.yml b/action.yml index 1ea839b..4d31e12 100644 --- a/action.yml +++ b/action.yml @@ -34,13 +34,13 @@ runs: using: "composite" steps: - name: Setup StackQL - uses: stackql/setup-stackql@v2.2.0 + uses: stackql/setup-stackql@v2.2.1 with: use_wrapper: true - name: Execute StackQL Command id: exec-query - uses: stackql/stackql-exec@v2.2.0 + uses: stackql/stackql-exec@v2.2.1 with: query: ${{ inputs.test_query }} query_file_path: ${{ inputs.test_query_file_path }} From 817a6dbd1c073be8968f2ac93790bc8919d6894a Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Mon, 29 Apr 2024 08:36:22 +1000 Subject: [PATCH 24/34] updated to use exec and setup v2.2.1 --- README.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c066007..46322cc 100644 --- a/README.md +++ b/README.md @@ -6,19 +6,21 @@ The `stackql/stackql-assert` action is an composite action that runs a `stackql` # Usage +> This action uses the [setup-stackql](https://github.com/marketplace/actions/stackql-studios-setup-stackql) and [stackql-exec](https://github.com/marketplace/actions/stackql-studios-stackql-exec) actions + ## Provider Authentication Authentication to StackQL providers is done via environment variables source from GitHub Actions Secrets. To learn more about authentication, see the setup instructions for your provider or providers at the [StackQL Provider Registry Docs](https://stackql.io/registry). ## Inputs -- `test_query` - stackql query to execute **(need to supply either `test_query` or `test_query_file_path`)** -- `test_query_file_path` - stackql query file to execute **(need to supply either `test_query` or `test_query_file_path`)** -- `data_file_path` - (optional) path to data file to pass to the stackql query preprocessor (`json` or `jsonnet`) -- `vars` - (optional) comma delimited list of variables to pass to the stackql query preprocessor (supported with `jsonnet` config blocks or `jsonnet` data files only), accepts `var1=val1,var2=val2`, can be used to source environment variables into stackql queries -- `expected_rows` - (optional) Expected number of rows in the result. -- `expected_results_str` - (optional) Expected result (`json`) from executing test query, support object string (overrides `expected_results_file_path`) -- `expected_results_file_path` - (optional) Results file (`json`) that stores expected result, json is support -- `auth_obj_path` - (optional) the path of json file that stores stackql AUTH string **(only required when using non-standard environment variable names)** -- `auth_str` - (optional) stackql AUTH string **(only required when using non-standard environment variable names)** +- **`test_query`** - stackql query to execute *(need to supply either `test_query` or `test_query_file_path`)* +- **`test_query_file_path`** - stackql query file to execute *(need to supply either `test_query` or `test_query_file_path`)* +- **`data_file_path`** - (optional) path to data file to pass to the stackql query preprocessor (`json` or `jsonnet`) +- **`vars`** - (optional) comma delimited list of variables to pass to the stackql query preprocessor (supported with `jsonnet` config blocks or `jsonnet` data files only), accepts `var1=val1,var2=val2`, can be used to source environment variables into stackql queries +- **`expected_rows`** - (optional) Expected number of rows in the result. +- **`expected_results_str`** - (optional) Expected result (`json`) from executing test query, support object string (overrides `expected_results_file_path`) +- **`expected_results_file_path`** - (optional) Results file (`json`) that stores expected result, json is support +- **`auth_obj_path`** - (optional) the path of json file that stores stackql AUTH string *(only required when using non-standard environment variable names)* +- **`auth_str`** - (optional) stackql AUTH string *(only required when using non-standard environment variable names)* **__NOTE:__ one of `expected_rows`, `expected_results_str` or `expected_results_file_path` is required** @@ -30,7 +32,7 @@ Authentication to StackQL providers is done via environment variables source fro ## Expected Result - Use `expected_results_str` or `expected_results_file_path` or `expected_rows` to pass the expected result to the action. The expected result (`expected_results_str` or `expected_results_file_path`) should be a valid `json` object. The action will compare the result with the expected result. If the result is not the same as the expected result, the action will fail the step. - Either `expected_results_str` or `expected_results_file_path` or `expected_rows` are required. If `expected_results_str` and `expected_results_file_path` are provided, `expected_results_str` will be used. -- Expected result example can be found in [example workflow](./.github/workflows/stackql-assert.yml) and [example .json file](./.github/workflows/workflow_scripts) +- Expected result example can be found in [example workflow](./.github/workflows/stackql-assert-test.yml) and [example .json file](./.github/workflows/workflow_scripts) ## Examples The following excerpts from a GitHub Actions workflow demonstrate how to use the `stackql/stackql-assert` action. From 2daf5a01df97a677d117012b0347090d7feae27a Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Mon, 29 Apr 2024 11:04:13 +1000 Subject: [PATCH 25/34] remove unused --- lib/tests/assert.test.js | 207 --------------------------------------- lib/tests/test-auth.json | 1 - lib/tests/utils.test.js | 79 --------------- lib/utils.js | 124 ----------------------- stackql-assert.js | 4 - 5 files changed, 415 deletions(-) delete mode 100644 lib/tests/assert.test.js delete mode 100644 lib/tests/test-auth.json delete mode 100644 lib/tests/utils.test.js delete mode 100644 lib/utils.js delete mode 100644 stackql-assert.js diff --git a/lib/tests/assert.test.js b/lib/tests/assert.test.js deleted file mode 100644 index b9841c9..0000000 --- a/lib/tests/assert.test.js +++ /dev/null @@ -1,207 +0,0 @@ -const {checkResult, parseResult, assertResult, getExpectedResult} = require('../assert') - -describe('parseResult', ()=>{ - - it('should parsedResult correctly when it starts with register', ()=>{ - const resultString = `google provider, version 'v23.01.00116' successfully installed \n - [{"name":"stackql-demo-001","status":"TERMINATED"}]` - - const expected = [{"name":"stackql-demo-001","status":"TERMINATED"}] - - const actual = parseResult(resultString); - expect(actual).toEqual(expected); - }) - - it('should parsedResult correctly when it is only the object', ()=>{ - const resultString = `[{"name":"stackql-demo-001","status":"TERMINATED"}]` - - const expected = [{"name":"stackql-demo-001","status":"TERMINATED"}] - - const actual = parseResult(resultString); - expect(actual).toEqual(expected); - }) -}) - -describe('checkResult', ()=>{ - let expectedResult; - let actualResult; - beforeEach(()=>{ - expectedResult= [{"name":"stackql-demo-001","status":"TERMINATED"}] - actualResult= [{"name":"stackql-demo-001","status":"TERMINATED"}] - - }) - it('should return equality false when the result object does not match', ()=>{ - actualResult = [{"name":"stackql-demo-001","status":"RUNNING"}] - - const {equality} = checkResult(expectedResult, undefined, actualResult); - - expect(equality).toEqual(false); - }) - - it('should return equality true when the result object does match', ()=>{ - - const {equality} = checkResult(expectedResult, undefined, actualResult); - - expect(equality).toEqual(true); - }) - - it('should return equality false when expected row is not matching', ()=>{ - - const expectedRows = 2; - - const {equality} = checkResult(undefined, expectedRows, actualResult); - - expect(equality).toEqual(false); - }) - - it('should return equality true when expected row is matching', ()=>{ - const expectedRows = 1; - - const {equality} = checkResult(undefined, expectedRows, actualResult); - - expect(equality).toEqual(true); - }) - - it('should return equality false when expected row is matching, but result object does not match', ()=>{ - const expectedRows = 1; - actualResult = [{"name":"stackql-demo-001","status":"RUNNING"}] - - const {equality} = checkResult(expectedResult, expectedRows, actualResult); - - expect(equality).toEqual(false); - }) - - it('should return equality true when expected row is matching, but result object matches', ()=>{ - const expectedRows = 1; - actualResult = [{"name":"stackql-demo-001","status":"TERMINATED"}] - - const {equality} = checkResult(expectedResult, expectedRows, actualResult); - - expect(equality).toEqual(true); - }) -}) - -describe('getExpectedResult', ()=>{ - it('should return expectedResult when expectedResultStr is passed', ()=>{ - const expectedResultStr = `[{"name":"stackql-demo-001","status":"TERMINATED"}]` - const expectedResult = [{"name":"stackql-demo-001","status":"TERMINATED"}] - - const actual = getExpectedResult(expectedResultStr, undefined); - - expect(actual).toEqual(expectedResult); - }) - - it('should return expectedResult when expectedResultFilePath is passed', ()=>{ - const expectedResultFilePath = 'lib/tests/success-result.json' - const expectedResult = [{"name":"stackql-demo-001","status":"TERMINATED"}] - - const actual = getExpectedResult(undefined, expectedResultFilePath); - - expect(actual).toEqual(expectedResult); - }) - -}) - - describe('assertResult integration test', ()=>{ - let coreObj; - - const ACTION_ENV = { - RESULT: `google provider, version 'v23.01.00116' successfully installed \n - [{"name":"stackql-demo-001","status":"TERMINATED"}]`, - EXPECTED_RESULTS_STR: `[{"name":"stackql-demo-001","status":"TERMINATED"}]`, - EXPECTED_RESULTS_FILE_PATH: 'test.json', - EXPECTED_ROWS: 1 - } - - beforeEach(() => { - jest.resetModules() - process.env = {...ACTION_ENV} - coreObj = { - setFailed: jest.fn(), - info: jest.fn().mockImplementation((message)=>{console.log(message)}) - } - }) - afterEach(() => { - process.env = ACTION_ENV - }) - - - it('it should setFailed when there is expected results are undefined', () => { - process.env.EXPECTED_RESULTS_FILE_PATH = undefined - process.env.EXPECTED_RESULTS_STR = undefined - process.env.EXPECTED_ROWS = undefined - - assertResult(coreObj) - - expect(coreObj.setFailed).toHaveBeenCalledWith('āŒ Cannot find expected result, file path or expected rows') - }); - - it('it should setFailed when actual result is not equal to expected result', () => { - process.env.RESULT= "[{\"name\":\"stackql-demo-001\",\"status\":\"RUNNING\"}]" - - assertResult(coreObj) - - expect(coreObj.setFailed).toHaveBeenCalledWith(expect.stringContaining('StackQL Assert Failed')) - }); - - it('it should not setFailed when actual result equal to expected result', () => { - assertResult(coreObj) - - expect(coreObj.setFailed).not.toHaveBeenCalled() - expect(coreObj.info).toHaveBeenCalledWith(expect.stringContaining('StackQL Assert Successful')) - }); - - it('it should setFailed when actual result is not equal to expected result', () => { - process.env.EXPECTED_RESULTS_STR= undefined; - process.env.EXPECTED_RESULTS_FILE_PATH = 'lib/tests/failed-result.json' - - assertResult(coreObj) - - expect(coreObj.setFailed).toHaveBeenCalledWith(expect.stringContaining('StackQL Assert Failed')) - }); - - it('it should not setFailed when actual result equal to expected result in file', () => { - process.env.EXPECTED_RESULTS_STR= undefined; - process.env.EXPECTED_RESULTS_FILE_PATH = 'lib/tests/success-result.json' - - assertResult(coreObj) - - expect(coreObj.setFailed).not.toHaveBeenCalled() - expect(coreObj.info).toHaveBeenCalledWith(expect.stringContaining('StackQL Assert Successful')) - }) - - it('it should setFailed when actual result does not match expected rows', () => { - process.env.EXPECTED_RESULTS_STR= undefined; - process.env.EXPECTED_RESULTS_FILE_PATH = undefined; - process.env.EXPECTED_ROWS = 2 - - assertResult(coreObj) - - expect(coreObj.setFailed).toHaveBeenCalledWith(expect.stringContaining('StackQL Assert Failed')) - }); - - it('it should not setFailed when actual result match expected rows', ()=>{ - process.env.EXPECTED_RESULTS_STR= undefined; - process.env.EXPECTED_RESULTS_FILE_PATH = undefined; - process.env.EXPECTED_ROWS = 1 - - assertResult(coreObj) - - expect(coreObj.setFailed).not.toHaveBeenCalled() - expect(coreObj.info).toHaveBeenCalledWith(expect.stringContaining('StackQL Assert Successful')) - }) - - it('it should not setFailed when actual result match expected rows in string number', ()=>{ - process.env.EXPECTED_RESULTS_STR= undefined; - process.env.EXPECTED_RESULTS_FILE_PATH = undefined; - process.env.EXPECTED_ROWS = '1' - - assertResult(coreObj) - - expect(coreObj.setFailed).not.toHaveBeenCalled() - expect(coreObj.info).toHaveBeenCalledWith(expect.stringContaining('StackQL Assert Successful')) - }) - - - - }) diff --git a/lib/tests/test-auth.json b/lib/tests/test-auth.json deleted file mode 100644 index d8de066..0000000 --- a/lib/tests/test-auth.json +++ /dev/null @@ -1 +0,0 @@ -{ "google": { "type": "service_account", "credentialsfilepath": "sa-key.json" }} \ No newline at end of file diff --git a/lib/tests/utils.test.js b/lib/tests/utils.test.js deleted file mode 100644 index ab73fb7..0000000 --- a/lib/tests/utils.test.js +++ /dev/null @@ -1,79 +0,0 @@ -const { assertResult, parseResult, getExpectedResult } = require('./assert'); -const fs = require('fs'); - -jest.mock('fs'); - -describe('assert.js functions', () => { - let core; - - beforeEach(() => { - core = { - info: jest.fn(), - error: jest.fn(), - setFailed: jest.fn() - }; - process.env.RESULT = JSON.stringify([{ id: 1, value: 'test' }]); - process.env.EXPECTED_ROWS = '1'; - }); - - afterEach(() => { - jest.resetAllMocks(); - jest.restoreAllMocks(); - }); - - describe('parseResult', () => { - it('should correctly parse valid JSON', () => { - const input = JSON.stringify({ key: 'value' }); - expect(parseResult(input, 'valid JSON')).toEqual({ key: 'value' }); - }); - - it('should throw an error on invalid JSON', () => { - const input = "invalid JSON"; - expect(() => parseResult(input, 'invalid JSON')).toThrow('Failed to parse invalid JSON JSON'); - }); - }); - - describe('getExpectedResult', () => { - it('should return parsed result from string', () => { - const input = JSON.stringify({ key: 'value' }); - expect(getExpectedResult(input, null)).toEqual({ key: 'value' }); - }); - - it('should return parsed result from file', () => { - const input = JSON.stringify({ key: 'value' }); - fs.readFileSync.mockReturnValue(input); - expect(getExpectedResult(null, 'path/to/file')).toEqual({ key: 'value' }); - expect(fs.readFileSync).toHaveBeenCalledWith('path/to/file', 'utf-8'); - }); - - it('should throw an error if no input is provided', () => { - expect(() => getExpectedResult(null, null)).toThrow('No expected result provided.'); - }); - }); - - describe('assertResult', () => { - it('should log success if the expected rows and results match', () => { - process.env.EXPECTED_RESULTS_STR = process.env.RESULT; - assertResult(core); - expect(core.info).toHaveBeenCalledWith("āœ… StackQL Assert Successful"); - }); - - it('should fail if expected rows do not match', () => { - process.env.EXPECTED_ROWS = '2'; // Actual result will have only one item - assertResult(core); - expect(core.setFailed).toHaveBeenCalledWith(expect.stringContaining("Expected rows: 2, got: 1")); - }); - - it('should fail if expected results do not match', () => { - process.env.EXPECTED_RESULTS_STR = JSON.stringify([{ id: 1, value: 'wrong' }]); - assertResult(core); - expect(core.setFailed).toHaveBeenCalledWith(expect.stringContaining("Expected results do not match actual results.")); - }); - - it('should handle errors during processing', () => { - process.env.RESULT = 'invalid json'; - assertResult(core); - expect(core.setFailed).toHaveBeenCalledWith(expect.stringContaining("Assertion error")); - }); - }); -}); diff --git a/lib/utils.js b/lib/utils.js deleted file mode 100644 index 0a6f4b0..0000000 --- a/lib/utils.js +++ /dev/null @@ -1,124 +0,0 @@ -const fs = require("fs"); - -function setupAuth(core) { - let auth; - const fileName = process.env.AUTH_FILE_PATH; - const authStr = process.env.AUTH_STR; - - if (!checkEnvVarValid(fileName) && !checkEnvVarValid(authStr)) { - // core.info("Neither AUTH_FILE_PATH nor AUTH_STR is set. Proceeding using default provider environment variable names."); - return; - } - - if (checkEnvVarValid(fileName)) { - try { - // Read the contents of the JSON file into a string - auth = fs.readFileSync(fileName, "utf-8"); - } catch (error) { - core.error(error); - core.setFailed(`Cannot find auth file ${fileName}`); - return; - } - } - if (checkEnvVarValid(authStr)) { - auth = authStr - } - - core.info("Setting AUTH environment variable..."); - core.exportVariable("AUTH", auth); -} - -async function showStackQLQuery(core) { - try { - let [ - dryRunCommand, - dryRunResult, - ] = [ - process.env.STACKQL_DRYRUN_COMMAND, - process.env.DRYRUN_RESULT, - ]; - - if (!dryRunResult) { - core.setFailed("No Dryrun Output from stackql command"); - } - - core.info(`\nšŸš€ rendered stackql query:\n${dryRunResult}`); - - } catch (e) { - core.setFailed(e); - } -} - -async function getStackqlCommand(core) { - - const [query, queryFilePath, dataFilePath, vars, auth, output = "json"] = [ - process.env.QUERY, - process.env.QUERY_FILE_PATH, - process.env.DATA_FILE_PATH, - process.env.VARS, - process.env.AUTH, - process.env.OUTPUT, - ]; - - if (!checkEnvVarValid(query) && !checkEnvVarValid(queryFilePath)) { - core.setFailed("Either test_query or test_query_file_path need to be set"); - return; - } - - let args = ["exec"]; - let dryRunArgs = ["exec", "-H"]; - - if (query) { - args.push(`"${query}"`); - dryRunArgs.push(`"${query}"`); - } else { - args.push("-i", queryFilePath); - dryRunArgs.push("-i", queryFilePath); - } - - if (checkEnvVarValid(dataFilePath)) { - args.push(`--iqldata='${dataFilePath}'`); - dryRunArgs.push(`--iqldata='${dataFilePath}'`); - } - - if (checkEnvVarValid(vars)) { - args.push(`--var='${vars}'`); - dryRunArgs.push(`--var='${vars}'`); - } - - if (checkEnvVarValid(auth)) { - args.push(`--auth='${auth}'`); - dryRunArgs.push(`--auth='${auth}'`); - } - - args.push(`--output='${output}'`); - dryRunArgs.push(`--output='text'`); - dryRunArgs.push(`--dryrun`); - - try { - const stackqlQuery = `stackql ${args.join(" ")}`; - const stackqlDryRunQuery = `stackql ${dryRunArgs.join(" ")}`; - core.exportVariable('STACKQL_COMMAND', stackqlQuery); - core.exportVariable('STACKQL_DRYRUN_COMMAND', stackqlDryRunQuery); - } catch (error) { - core.error(error); - core.setFailed("Error exporting stackql command"); - } -} - -/** - * Checking if environment variable is not empty or undefined - * @param {*} variable - */ -const checkEnvVarValid = (variable) => { - if (!variable || variable === "" || variable === "undefined") { - return false; - } - return true; -}; - -module.exports = { - setupAuth, - getStackqlCommand, - showStackQLQuery, -}; diff --git a/stackql-assert.js b/stackql-assert.js deleted file mode 100644 index 9793059..0000000 --- a/stackql-assert.js +++ /dev/null @@ -1,4 +0,0 @@ -const { assertResult } = require('./lib/assert') -module.exports ={ - assertResult -} \ No newline at end of file From 40089a034578ddc5975509d865636d977b8d1a40 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Mon, 29 Apr 2024 11:55:17 +1000 Subject: [PATCH 26/34] fixed tests --- .github/workflows/npm-test.yml | 18 ++++ lib/assert.js | 10 +- lib/tests/assert.test.js | 190 +++++++++++++++++++++++++++++++++ 3 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/npm-test.yml create mode 100644 lib/tests/assert.test.js diff --git a/.github/workflows/npm-test.yml b/.github/workflows/npm-test.yml new file mode 100644 index 0000000..9265d38 --- /dev/null +++ b/.github/workflows/npm-test.yml @@ -0,0 +1,18 @@ +name: 'npm test' +on: + push: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4.1.4 + - name: Use Node.js 16 + uses: actions/setup-node@v4.0.2 + with: + node-version: 16.x + - run: npm ci + - run: npm test + \ No newline at end of file diff --git a/lib/assert.js b/lib/assert.js index f82946a..7c7332e 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -46,6 +46,9 @@ function assertResult(core) { if (!RESULT) throw new Error("Result from StackQL execution is missing."); + // if no EXPECTED_RESULTS_STR, EXPECTED_RESULTS_FILE_PATH or EXPECTED_ROWS, fail the action + if (!EXPECTED_RESULTS_STR && !EXPECTED_RESULTS_FILE_PATH && !EXPECTED_ROWS) throw new Error("āŒ Cannot find expected result, file path or expected rows"); + const actualResult = parseResult(RESULT); core.info("šŸ” Checking results..."); @@ -64,4 +67,9 @@ function assertResult(core) { } } -module.exports = { assertResult }; +module.exports = { + parseResult, + getExpectedResult, + checkResult, + assertResult +}; diff --git a/lib/tests/assert.test.js b/lib/tests/assert.test.js new file mode 100644 index 0000000..0bd4ffb --- /dev/null +++ b/lib/tests/assert.test.js @@ -0,0 +1,190 @@ +const {checkResult, parseResult, assertResult, getExpectedResult} = require('../assert') + +describe('parseResult', ()=>{ + + it('should parsedResult correctly', ()=>{ + const resultString = `[{"name":"stackql-demo-001","status":"TERMINATED"}]` + const expected = [{"name":"stackql-demo-001","status":"TERMINATED"}] + const actual = parseResult(resultString); + expect(actual).toEqual(expected); + }) +}) + +describe('getExpectedResult', ()=>{ + it('should return expectedResult when expectedResultStr is passed', ()=>{ + const expectedResultStr = `[{"name":"stackql-demo-001","status":"TERMINATED"}]` + const expectedResult = [{"name":"stackql-demo-001","status":"TERMINATED"}] + + const actual = getExpectedResult(expectedResultStr, undefined); + + expect(actual).toEqual(expectedResult); + }) + + it('should return expectedResult when expectedResultFilePath is passed', ()=>{ + const expectedResultFilePath = 'lib/tests/success-result.json' + const expectedResult = [{"name":"stackql-demo-001","status":"TERMINATED"}] + + const actual = getExpectedResult(undefined, expectedResultFilePath); + + expect(actual).toEqual(expectedResult); + }) + +}) + +describe('checkResult', () => { + const core = { + info: jest.fn(), + error: jest.fn(), + setFailed: jest.fn() + }; + + beforeEach(() => { + core.info.mockClear(); + core.error.mockClear(); + core.setFailed.mockClear(); + }); + + it('should return false and log an error when the actual length does not match expected rows', () => { + const expectedRows = "3"; + const actualResult = [{}, {}, {}, {}]; // 4 items, should not match expectedRows + + const result = checkResult(core, undefined, actualResult, expectedRows); + + expect(result).toBe(false); + expect(core.error).toHaveBeenCalledWith(`Expected rows: ${expectedRows}, got: ${actualResult.length}`); + }); + + it('should return true when the actual length matches expected rows', () => { + const expectedRows = "2"; + const actualResult = [{}, {}]; // 2 items, matches expectedRows + + const result = checkResult(core, undefined, actualResult, expectedRows); + + expect(result).toBe(true); + expect(core.error).not.toHaveBeenCalled(); + }); + + it('should return false and log an error when expected does not match actual and expectedRows is undefined', () => { + const expected = [{ name: "test1" }, { name: "test2" }]; + const actual = [{ name: "test1" }, { name: "test3" }]; + + const result = checkResult(core, expected, actual, undefined); + + expect(result).toBe(false); + expect(core.error).toHaveBeenCalledWith(`Expected results do not match actual results.\nExpected: ${JSON.stringify(expected)}\nActual: ${JSON.stringify(actual)}`); + }); + + it('should return true when expected matches actual and expectedRows is undefined', () => { + const expected = [{ name: "test1" }, { name: "test2" }]; + const actual = [{ name: "test1" }, { name: "test2" }]; + + const result = checkResult(core, expected, actual, undefined); + + expect(result).toBe(true); + expect(core.error).not.toHaveBeenCalled(); + }); +}); + +describe('assertResult', ()=>{ + let coreObj; + + const ACTION_ENV = { + RESULT: `[{"name":"stackql-demo-001","status":"TERMINATED"}]`, + EXPECTED_RESULTS_STR: `[{"name":"stackql-demo-001","status":"TERMINATED"}]`, + EXPECTED_RESULTS_FILE_PATH: 'test.json', + EXPECTED_ROWS: 1 + } + + beforeEach(() => { + jest.resetModules() + process.env = {...ACTION_ENV} + coreObj = { + setFailed: jest.fn(), + info: jest.fn(), + error: jest.fn(), + } + }) + afterEach(() => { + process.env = ACTION_ENV + }) + + + it('it should setFailed when there is expected results are undefined', () => { + process.env.EXPECTED_RESULTS_FILE_PATH = undefined + process.env.EXPECTED_RESULTS_STR = undefined + process.env.EXPECTED_ROWS = undefined + + assertResult(coreObj) + + expect(coreObj.setFailed).toHaveBeenCalledWith('Assertion error: āŒ Cannot find expected result, file path or expected rows') + }); + + it('it should setFailed when actual result is not equal to expected result', () => { + process.env.RESULT= "[{\"name\":\"stackql-demo-001\",\"status\":\"RUNNING\"}]" + + assertResult(coreObj) + + expect(coreObj.setFailed).toHaveBeenCalledWith(expect.stringContaining('StackQL Assert Failed')) + }); + + it('it should not setFailed when actual result equal to expected result', () => { + assertResult(coreObj) + + expect(coreObj.setFailed).not.toHaveBeenCalled() + expect(coreObj.info).toHaveBeenCalledWith(expect.stringContaining('StackQL Assert Successful')) + }); + + it('it should setFailed when actual result is not equal to expected result', () => { + process.env.EXPECTED_RESULTS_STR= undefined; + process.env.EXPECTED_RESULTS_FILE_PATH = 'lib/tests/failed-result.json' + + assertResult(coreObj) + + expect(coreObj.setFailed).toHaveBeenCalledWith(expect.stringContaining('StackQL Assert Failed')) + }); + + it('it should not setFailed when actual result equal to expected result in file', () => { + process.env.EXPECTED_RESULTS_STR= undefined; + process.env.EXPECTED_RESULTS_FILE_PATH = 'lib/tests/success-result.json' + + assertResult(coreObj) + + expect(coreObj.setFailed).not.toHaveBeenCalled() + expect(coreObj.info).toHaveBeenCalledWith(expect.stringContaining('StackQL Assert Successful')) + }) + + it('it should setFailed when actual result does not match expected rows', () => { + process.env.EXPECTED_RESULTS_STR= undefined; + process.env.EXPECTED_RESULTS_FILE_PATH = undefined; + process.env.EXPECTED_ROWS = 2 + + assertResult(coreObj) + + expect(coreObj.setFailed).toHaveBeenCalledWith(expect.stringContaining('StackQL Assert Failed')) + }); + + it('it should not setFailed when actual result match expected rows', ()=>{ + process.env.EXPECTED_RESULTS_STR= undefined; + process.env.EXPECTED_RESULTS_FILE_PATH = undefined; + process.env.EXPECTED_ROWS = 1 + + assertResult(coreObj) + + expect(coreObj.setFailed).not.toHaveBeenCalled() + expect(coreObj.info).toHaveBeenCalledWith(expect.stringContaining('StackQL Assert Successful')) + }) + + it('it should not setFailed when actual result match expected rows in string number', ()=>{ + process.env.EXPECTED_RESULTS_STR= undefined; + process.env.EXPECTED_RESULTS_FILE_PATH = undefined; + process.env.EXPECTED_ROWS = '1' + + assertResult(coreObj) + + expect(coreObj.setFailed).not.toHaveBeenCalled() + expect(coreObj.info).toHaveBeenCalledWith(expect.stringContaining('StackQL Assert Successful')) + }) + + + +}) From 55776d01d670e361f5dcd2539f3f03fce9394b4b Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Mon, 29 Apr 2024 11:56:47 +1000 Subject: [PATCH 27/34] fixed tests --- .github/workflows/npm-test.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/npm-test.yml b/.github/workflows/npm-test.yml index 9265d38..5d2106f 100644 --- a/.github/workflows/npm-test.yml +++ b/.github/workflows/npm-test.yml @@ -1,6 +1,5 @@ name: 'npm test' on: - push: pull_request: jobs: @@ -8,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.1.4 + - uses: actions/checkout@v4.1.3 - name: Use Node.js 16 uses: actions/setup-node@v4.0.2 with: From df54929819a6e8434cab94e9afa283dc5ea084d3 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Tue, 29 Oct 2024 08:30:21 +1100 Subject: [PATCH 28/34] name change --- README.md | 2 +- action.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 46322cc..4a4616d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ The `stackql/stackql-assert` action is an composite action that runs a `stackql` # Usage -> This action uses the [setup-stackql](https://github.com/marketplace/actions/stackql-studios-setup-stackql) and [stackql-exec](https://github.com/marketplace/actions/stackql-studios-stackql-exec) actions +> This action uses the [setup-stackql](https://github.com/marketplace/actions/setup-stackql) and [stackql-exec](https://github.com/marketplace/actions/stackql-exec) actions ## Provider Authentication Authentication to StackQL providers is done via environment variables source from GitHub Actions Secrets. To learn more about authentication, see the setup instructions for your provider or providers at the [StackQL Provider Registry Docs](https://stackql.io/registry). diff --git a/action.yml b/action.yml index 4d31e12..5f12609 100644 --- a/action.yml +++ b/action.yml @@ -1,4 +1,4 @@ -name: 'StackQL Studios - StackQL Assert' +name: 'stackql-assert' description: 'Run StackQL query to test and audit your infrastructure.' author: 'Yuncheng Yang, StackQL Studios' inputs: @@ -66,4 +66,4 @@ runs: branding: icon: 'terminal' - color: 'green' + color: 'blue' From 72d1189c96b1c63553ca38ab0c554101fb7c96d4 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Tue, 29 Oct 2024 11:00:23 +1100 Subject: [PATCH 29/34] updated tests --- .github/workflows/npm-test.yml | 9 ++++----- .github/workflows/stackql-assert-test.yml | 6 +++--- .../google-example-inline-jsonnet-results.json | 2 +- .../workflow_scripts/google-example-inline-jsonnet.iql | 2 +- .github/workflows/workflow_scripts/google-example.iql | 2 +- action.yml | 4 ++-- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/.github/workflows/npm-test.yml b/.github/workflows/npm-test.yml index 5d2106f..c5337b4 100644 --- a/.github/workflows/npm-test.yml +++ b/.github/workflows/npm-test.yml @@ -8,10 +8,9 @@ jobs: steps: - uses: actions/checkout@v4.1.3 - - name: Use Node.js 16 - uses: actions/setup-node@v4.0.2 + - name: Setup Node.js environment + uses: actions/setup-node@v4.1.0 with: - node-version: 16.x + node-version: 18 - run: npm ci - - run: npm test - \ No newline at end of file + - run: npm test \ No newline at end of file diff --git a/.github/workflows/stackql-assert-test.yml b/.github/workflows/stackql-assert-test.yml index a7615e3..eaf6851 100644 --- a/.github/workflows/stackql-assert-test.yml +++ b/.github/workflows/stackql-assert-test.yml @@ -21,7 +21,7 @@ jobs: # Pull required providers # - name: pull required providers - uses: stackql/stackql-exec@v2.2.1 + uses: stackql/stackql-exec@v2.2.3 with: is_command: true query: "REGISTRY PULL google; REGISTRY PULL github" @@ -35,7 +35,7 @@ jobs: test_query: | SELECT name FROM google.compute.instances - WHERE project = 'stackql-demo' AND zone = 'australia-southeast1-a' AND name = 'stackql-demo-001'; + WHERE project = 'stackql-integration-tests' AND zone = 'australia-southeast1-a' and name = 'stackql-test-001'; expected_rows: 1 env: GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }} @@ -47,7 +47,7 @@ jobs: uses: ./ with: test_query_file_path: './.github/workflows/workflow_scripts/google-example.iql' - expected_results_str: '[{"name":"stackql-demo-001"}]' + expected_results_str: '[{"name":"stackql-test-001"}]' env: GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }} diff --git a/.github/workflows/workflow_scripts/google-example-inline-jsonnet-results.json b/.github/workflows/workflow_scripts/google-example-inline-jsonnet-results.json index 8f652aa..ed8d431 100644 --- a/.github/workflows/workflow_scripts/google-example-inline-jsonnet-results.json +++ b/.github/workflows/workflow_scripts/google-example-inline-jsonnet-results.json @@ -1 +1 @@ -[{"name":"stackql-demo-001"}] \ No newline at end of file +[{"name":"stackql-test-001"}] \ No newline at end of file diff --git a/.github/workflows/workflow_scripts/google-example-inline-jsonnet.iql b/.github/workflows/workflow_scripts/google-example-inline-jsonnet.iql index bceabe0..e42aade 100644 --- a/.github/workflows/workflow_scripts/google-example-inline-jsonnet.iql +++ b/.github/workflows/workflow_scripts/google-example-inline-jsonnet.iql @@ -8,4 +8,4 @@ local zone = std.extVar("GOOGLE_ZONE"); >>> SELECT name FROM google.compute.instances -WHERE project = '{{ .project }}' and zone = '{{ .zone }}' and name = 'stackql-demo-001'; \ No newline at end of file +WHERE project = '{{ .project }}' and zone = '{{ .zone }}' and name = 'stackql-test-001'; \ No newline at end of file diff --git a/.github/workflows/workflow_scripts/google-example.iql b/.github/workflows/workflow_scripts/google-example.iql index a10d1cf..acbed77 100644 --- a/.github/workflows/workflow_scripts/google-example.iql +++ b/.github/workflows/workflow_scripts/google-example.iql @@ -1,3 +1,3 @@ SELECT name FROM google.compute.instances -WHERE project = 'stackql-demo' AND zone = 'australia-southeast1-a' AND name = 'stackql-demo-001'; \ No newline at end of file +WHERE project = 'stackql-integration-tests' AND zone = 'australia-southeast1-a' AND name = 'stackql-test-001'; \ No newline at end of file diff --git a/action.yml b/action.yml index 5f12609..1b2fc78 100644 --- a/action.yml +++ b/action.yml @@ -34,13 +34,13 @@ runs: using: "composite" steps: - name: Setup StackQL - uses: stackql/setup-stackql@v2.2.1 + uses: stackql/setup-stackql@v2.2.3 with: use_wrapper: true - name: Execute StackQL Command id: exec-query - uses: stackql/stackql-exec@v2.2.1 + uses: stackql/stackql-exec@v2.2.3 with: query: ${{ inputs.test_query }} query_file_path: ${{ inputs.test_query_file_path }} From 6dfcf6f65f46da53ab376bbfbb044477aa12cf21 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Sat, 14 Mar 2026 17:14:47 +1100 Subject: [PATCH 30/34] updated packages --- .github/workflows/npm-test.yml | 4 ++-- .github/workflows/stackql-assert-test.yml | 4 ++-- action.yml | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/npm-test.yml b/.github/workflows/npm-test.yml index c5337b4..837459c 100644 --- a/.github/workflows/npm-test.yml +++ b/.github/workflows/npm-test.yml @@ -7,9 +7,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.1.3 + - uses: actions/checkout@v6 - name: Setup Node.js environment - uses: actions/setup-node@v4.1.0 + uses: actions/setup-node@v6 with: node-version: 18 - run: npm ci diff --git a/.github/workflows/stackql-assert-test.yml b/.github/workflows/stackql-assert-test.yml index eaf6851..406cd0e 100644 --- a/.github/workflows/stackql-assert-test.yml +++ b/.github/workflows/stackql-assert-test.yml @@ -15,13 +15,13 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4.1.4 + uses: actions/checkout@v6 # # Pull required providers # - name: pull required providers - uses: stackql/stackql-exec@v2.2.3 + uses: stackql/stackql-exec@v2.3.0 with: is_command: true query: "REGISTRY PULL google; REGISTRY PULL github" diff --git a/action.yml b/action.yml index 1b2fc78..fb786f5 100644 --- a/action.yml +++ b/action.yml @@ -34,13 +34,13 @@ runs: using: "composite" steps: - name: Setup StackQL - uses: stackql/setup-stackql@v2.2.3 + uses: stackql/setup-stackql@v2.3.0 with: use_wrapper: true - name: Execute StackQL Command id: exec-query - uses: stackql/stackql-exec@v2.2.3 + uses: stackql/stackql-exec@v2.3.0 with: query: ${{ inputs.test_query }} query_file_path: ${{ inputs.test_query_file_path }} @@ -51,7 +51,7 @@ runs: dry_run: false - name: Check Results - uses: actions/github-script@v7.0.1 + uses: actions/github-script@v8 with: script: | const path = require('path'); From 18f75a21bfd95f179d252365644cc5d41664647b Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Sun, 15 Mar 2026 09:57:06 +1100 Subject: [PATCH 31/34] updated versions --- .github/workflows/npm-test.yml | 2 +- .github/workflows/stackql-assert-test.yml | 2 +- action.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/npm-test.yml b/.github/workflows/npm-test.yml index 837459c..e62b885 100644 --- a/.github/workflows/npm-test.yml +++ b/.github/workflows/npm-test.yml @@ -11,6 +11,6 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v6 with: - node-version: 18 + node-version: 24 - run: npm ci - run: npm test \ No newline at end of file diff --git a/.github/workflows/stackql-assert-test.yml b/.github/workflows/stackql-assert-test.yml index 406cd0e..e986037 100644 --- a/.github/workflows/stackql-assert-test.yml +++ b/.github/workflows/stackql-assert-test.yml @@ -21,7 +21,7 @@ jobs: # Pull required providers # - name: pull required providers - uses: stackql/stackql-exec@v2.3.0 + uses: stackql/stackql-exec@v2 with: is_command: true query: "REGISTRY PULL google; REGISTRY PULL github" diff --git a/action.yml b/action.yml index fb786f5..391c923 100644 --- a/action.yml +++ b/action.yml @@ -34,13 +34,13 @@ runs: using: "composite" steps: - name: Setup StackQL - uses: stackql/setup-stackql@v2.3.0 + uses: stackql/setup-stackql@v2 with: use_wrapper: true - name: Execute StackQL Command id: exec-query - uses: stackql/stackql-exec@v2.3.0 + uses: stackql/stackql-exec@v2 with: query: ${{ inputs.test_query }} query_file_path: ${{ inputs.test_query_file_path }} From 101b2761ff2182d16cf412c5e849c95bfab207ec Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 14 Mar 2026 23:16:33 +0000 Subject: [PATCH 32/34] Replace Google Compute instance tests with stable GCS bucket resource Switch all Google test references from google.compute.instances (stackql-integration-tests project, australia-southeast1-a zone) to google.storage.buckets (stackql project, stackql-public-releases bucket). Bucket properties (name, location, locationType) are stable and not subject to instance lifecycle costs. Remove GOOGLE_ZONE variable dependency throughout. https://claude.ai/code/session_019CH6RrcMGtdUafvyVCKCHE --- .github/workflows/stackql-assert-test.yml | 18 ++++++++--------- ...google-example-inline-jsonnet-results.json | 2 +- .../google-example-inline-jsonnet.iql | 9 ++++----- .../workflow_scripts/google-example.iql | 7 ++++--- lib/tests/assert.test.js | 20 +++++++++---------- lib/tests/failed-result.json | 2 +- lib/tests/success-result.json | 2 +- 7 files changed, 30 insertions(+), 30 deletions(-) diff --git a/.github/workflows/stackql-assert-test.yml b/.github/workflows/stackql-assert-test.yml index e986037..a890773 100644 --- a/.github/workflows/stackql-assert-test.yml +++ b/.github/workflows/stackql-assert-test.yml @@ -33,11 +33,12 @@ jobs: uses: ./ with: test_query: | - SELECT name - FROM google.compute.instances - WHERE project = 'stackql-integration-tests' AND zone = 'australia-southeast1-a' and name = 'stackql-test-001'; + SELECT name, location, locationType + FROM google.storage.buckets + WHERE project = 'stackql' + AND name = 'stackql-public-releases'; expected_rows: 1 - env: + env: GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }} # @@ -47,8 +48,8 @@ jobs: uses: ./ with: test_query_file_path: './.github/workflows/workflow_scripts/google-example.iql' - expected_results_str: '[{"name":"stackql-test-001"}]' - env: + expected_results_str: '[{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}]' + env: GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }} # @@ -59,11 +60,10 @@ jobs: with: test_query_file_path: './.github/workflows/workflow_scripts/google-example-inline-jsonnet.iql' expected_results_file_path: './.github/workflows/workflow_scripts/google-example-inline-jsonnet-results.json' - vars: GOOGLE_PROJECT=${{ env.GOOGLE_PROJECT }},GOOGLE_ZONE=${{ env.GOOGLE_ZONE }} - env: + vars: GOOGLE_PROJECT=${{ env.GOOGLE_PROJECT }} + env: GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }} GOOGLE_PROJECT: ${{ vars.GOOGLE_PROJECT }} - GOOGLE_ZONE: ${{ vars.GOOGLE_ZONE }} # # Example `test_query_file_path` with `expected_rows` supplying `vars` using `jsonnet` config provided using `data_file_path` diff --git a/.github/workflows/workflow_scripts/google-example-inline-jsonnet-results.json b/.github/workflows/workflow_scripts/google-example-inline-jsonnet-results.json index ed8d431..bc57467 100644 --- a/.github/workflows/workflow_scripts/google-example-inline-jsonnet-results.json +++ b/.github/workflows/workflow_scripts/google-example-inline-jsonnet-results.json @@ -1 +1 @@ -[{"name":"stackql-test-001"}] \ No newline at end of file +[{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}] diff --git a/.github/workflows/workflow_scripts/google-example-inline-jsonnet.iql b/.github/workflows/workflow_scripts/google-example-inline-jsonnet.iql index e42aade..b840d0b 100644 --- a/.github/workflows/workflow_scripts/google-example-inline-jsonnet.iql +++ b/.github/workflows/workflow_scripts/google-example-inline-jsonnet.iql @@ -1,11 +1,10 @@ <<>> -SELECT name -FROM google.compute.instances -WHERE project = '{{ .project }}' and zone = '{{ .zone }}' and name = 'stackql-test-001'; \ No newline at end of file +SELECT name, location, locationType +FROM google.storage.buckets +WHERE project = '{{ .project }}' +AND name = 'stackql-public-releases'; diff --git a/.github/workflows/workflow_scripts/google-example.iql b/.github/workflows/workflow_scripts/google-example.iql index acbed77..4b0b510 100644 --- a/.github/workflows/workflow_scripts/google-example.iql +++ b/.github/workflows/workflow_scripts/google-example.iql @@ -1,3 +1,4 @@ -SELECT name -FROM google.compute.instances -WHERE project = 'stackql-integration-tests' AND zone = 'australia-southeast1-a' AND name = 'stackql-test-001'; \ No newline at end of file +SELECT name, location, locationType +FROM google.storage.buckets +WHERE project = 'stackql' +AND name = 'stackql-public-releases'; diff --git a/lib/tests/assert.test.js b/lib/tests/assert.test.js index 0bd4ffb..3d78484 100644 --- a/lib/tests/assert.test.js +++ b/lib/tests/assert.test.js @@ -3,8 +3,8 @@ const {checkResult, parseResult, assertResult, getExpectedResult} = require('../ describe('parseResult', ()=>{ it('should parsedResult correctly', ()=>{ - const resultString = `[{"name":"stackql-demo-001","status":"TERMINATED"}]` - const expected = [{"name":"stackql-demo-001","status":"TERMINATED"}] + const resultString = `[{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}]` + const expected = [{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}] const actual = parseResult(resultString); expect(actual).toEqual(expected); }) @@ -12,8 +12,8 @@ describe('parseResult', ()=>{ describe('getExpectedResult', ()=>{ it('should return expectedResult when expectedResultStr is passed', ()=>{ - const expectedResultStr = `[{"name":"stackql-demo-001","status":"TERMINATED"}]` - const expectedResult = [{"name":"stackql-demo-001","status":"TERMINATED"}] + const expectedResultStr = `[{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}]` + const expectedResult = [{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}] const actual = getExpectedResult(expectedResultStr, undefined); @@ -22,7 +22,7 @@ describe('getExpectedResult', ()=>{ it('should return expectedResult when expectedResultFilePath is passed', ()=>{ const expectedResultFilePath = 'lib/tests/success-result.json' - const expectedResult = [{"name":"stackql-demo-001","status":"TERMINATED"}] + const expectedResult = [{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}] const actual = getExpectedResult(undefined, expectedResultFilePath); @@ -42,7 +42,7 @@ describe('checkResult', () => { core.info.mockClear(); core.error.mockClear(); core.setFailed.mockClear(); - }); + }); it('should return false and log an error when the actual length does not match expected rows', () => { const expectedRows = "3"; @@ -89,8 +89,8 @@ describe('assertResult', ()=>{ let coreObj; const ACTION_ENV = { - RESULT: `[{"name":"stackql-demo-001","status":"TERMINATED"}]`, - EXPECTED_RESULTS_STR: `[{"name":"stackql-demo-001","status":"TERMINATED"}]`, + RESULT: `[{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}]`, + EXPECTED_RESULTS_STR: `[{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}]`, EXPECTED_RESULTS_FILE_PATH: 'test.json', EXPECTED_ROWS: 1 } @@ -120,7 +120,7 @@ describe('assertResult', ()=>{ }); it('it should setFailed when actual result is not equal to expected result', () => { - process.env.RESULT= "[{\"name\":\"stackql-demo-001\",\"status\":\"RUNNING\"}]" + process.env.RESULT= "[{\"name\":\"stackql-public-releases\",\"location\":\"EU\",\"locationType\":\"multi-region\"}]" assertResult(coreObj) @@ -185,6 +185,6 @@ describe('assertResult', ()=>{ expect(coreObj.info).toHaveBeenCalledWith(expect.stringContaining('StackQL Assert Successful')) }) - + }) diff --git a/lib/tests/failed-result.json b/lib/tests/failed-result.json index 82e6d31..9d474e3 100644 --- a/lib/tests/failed-result.json +++ b/lib/tests/failed-result.json @@ -1 +1 @@ -[{"name":"stackql-demo-001","status":"RUNNING"}] \ No newline at end of file +[{"name":"stackql-public-releases","location":"EU","locationType":"multi-region"}] diff --git a/lib/tests/success-result.json b/lib/tests/success-result.json index 030276f..bc57467 100644 --- a/lib/tests/success-result.json +++ b/lib/tests/success-result.json @@ -1 +1 @@ -[{"name":"stackql-demo-001","status":"TERMINATED"}] \ No newline at end of file +[{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}] From 6b2050a3bf3cdd701010639a34db60e07f05a840 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 14 Mar 2026 23:38:49 +0000 Subject: [PATCH 33/34] Fix expected result key ordering to match stackql JSON output stackql serialises JSON keys alphabetically, so expected result strings must use the same order (location, locationType, name) to pass deep equality checks. https://claude.ai/code/session_019CH6RrcMGtdUafvyVCKCHE --- .github/workflows/stackql-assert-test.yml | 2 +- .../google-example-inline-jsonnet-results.json | 2 +- lib/tests/assert.test.js | 16 ++++++++-------- lib/tests/failed-result.json | 2 +- lib/tests/success-result.json | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/stackql-assert-test.yml b/.github/workflows/stackql-assert-test.yml index a890773..4cc9397 100644 --- a/.github/workflows/stackql-assert-test.yml +++ b/.github/workflows/stackql-assert-test.yml @@ -48,7 +48,7 @@ jobs: uses: ./ with: test_query_file_path: './.github/workflows/workflow_scripts/google-example.iql' - expected_results_str: '[{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}]' + expected_results_str: '[{"location":"US","locationType":"multi-region","name":"stackql-public-releases"}]' env: GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }} diff --git a/.github/workflows/workflow_scripts/google-example-inline-jsonnet-results.json b/.github/workflows/workflow_scripts/google-example-inline-jsonnet-results.json index bc57467..b6bd69d 100644 --- a/.github/workflows/workflow_scripts/google-example-inline-jsonnet-results.json +++ b/.github/workflows/workflow_scripts/google-example-inline-jsonnet-results.json @@ -1 +1 @@ -[{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}] +[{"location":"US","locationType":"multi-region","name":"stackql-public-releases"}] diff --git a/lib/tests/assert.test.js b/lib/tests/assert.test.js index 3d78484..9d5a34d 100644 --- a/lib/tests/assert.test.js +++ b/lib/tests/assert.test.js @@ -3,8 +3,8 @@ const {checkResult, parseResult, assertResult, getExpectedResult} = require('../ describe('parseResult', ()=>{ it('should parsedResult correctly', ()=>{ - const resultString = `[{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}]` - const expected = [{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}] + const resultString = `[{"location":"US","locationType":"multi-region","name":"stackql-public-releases"}]` + const expected = [{"location":"US","locationType":"multi-region","name":"stackql-public-releases"}] const actual = parseResult(resultString); expect(actual).toEqual(expected); }) @@ -12,8 +12,8 @@ describe('parseResult', ()=>{ describe('getExpectedResult', ()=>{ it('should return expectedResult when expectedResultStr is passed', ()=>{ - const expectedResultStr = `[{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}]` - const expectedResult = [{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}] + const expectedResultStr = `[{"location":"US","locationType":"multi-region","name":"stackql-public-releases"}]` + const expectedResult = [{"location":"US","locationType":"multi-region","name":"stackql-public-releases"}] const actual = getExpectedResult(expectedResultStr, undefined); @@ -22,7 +22,7 @@ describe('getExpectedResult', ()=>{ it('should return expectedResult when expectedResultFilePath is passed', ()=>{ const expectedResultFilePath = 'lib/tests/success-result.json' - const expectedResult = [{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}] + const expectedResult = [{"location":"US","locationType":"multi-region","name":"stackql-public-releases"}] const actual = getExpectedResult(undefined, expectedResultFilePath); @@ -89,8 +89,8 @@ describe('assertResult', ()=>{ let coreObj; const ACTION_ENV = { - RESULT: `[{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}]`, - EXPECTED_RESULTS_STR: `[{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}]`, + RESULT: `[{"location":"US","locationType":"multi-region","name":"stackql-public-releases"}]`, + EXPECTED_RESULTS_STR: `[{"location":"US","locationType":"multi-region","name":"stackql-public-releases"}]`, EXPECTED_RESULTS_FILE_PATH: 'test.json', EXPECTED_ROWS: 1 } @@ -120,7 +120,7 @@ describe('assertResult', ()=>{ }); it('it should setFailed when actual result is not equal to expected result', () => { - process.env.RESULT= "[{\"name\":\"stackql-public-releases\",\"location\":\"EU\",\"locationType\":\"multi-region\"}]" + process.env.RESULT= "[{\"location\":\"EU\",\"locationType\":\"multi-region\",\"name\":\"stackql-public-releases\"}]" assertResult(coreObj) diff --git a/lib/tests/failed-result.json b/lib/tests/failed-result.json index 9d474e3..f76fe22 100644 --- a/lib/tests/failed-result.json +++ b/lib/tests/failed-result.json @@ -1 +1 @@ -[{"name":"stackql-public-releases","location":"EU","locationType":"multi-region"}] +[{"location":"EU","locationType":"multi-region","name":"stackql-public-releases"}] diff --git a/lib/tests/success-result.json b/lib/tests/success-result.json index bc57467..b6bd69d 100644 --- a/lib/tests/success-result.json +++ b/lib/tests/success-result.json @@ -1 +1 @@ -[{"name":"stackql-public-releases","location":"US","locationType":"multi-region"}] +[{"location":"US","locationType":"multi-region","name":"stackql-public-releases"}] From 192918bcc93d6f65bed8db3dd93dc9221578af3f Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 14 Mar 2026 23:44:49 +0000 Subject: [PATCH 34/34] Fix key-order-sensitive comparison and hardcode stable project var assert.js: replace JSON.stringify deep equality with key-sorted comparison so JSON objects match regardless of key order. stackql-assert-test.yml: hardcode GOOGLE_PROJECT=stackql in the inline jsonnet test vars instead of pulling from the repo variable (which still pointed at stackql-integration-tests). https://claude.ai/code/session_019CH6RrcMGtdUafvyVCKCHE --- .github/workflows/stackql-assert-test.yml | 3 +-- lib/assert.js | 9 ++++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/stackql-assert-test.yml b/.github/workflows/stackql-assert-test.yml index 4cc9397..d6d615b 100644 --- a/.github/workflows/stackql-assert-test.yml +++ b/.github/workflows/stackql-assert-test.yml @@ -60,10 +60,9 @@ jobs: with: test_query_file_path: './.github/workflows/workflow_scripts/google-example-inline-jsonnet.iql' expected_results_file_path: './.github/workflows/workflow_scripts/google-example-inline-jsonnet-results.json' - vars: GOOGLE_PROJECT=${{ env.GOOGLE_PROJECT }} + vars: GOOGLE_PROJECT=stackql env: GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }} - GOOGLE_PROJECT: ${{ vars.GOOGLE_PROJECT }} # # Example `test_query_file_path` with `expected_rows` supplying `vars` using `jsonnet` config provided using `data_file_path` diff --git a/lib/assert.js b/lib/assert.js index 7c7332e..8f8b126 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -20,6 +20,13 @@ const getExpectedResult = (expectedResultStr, expectedResultFilePath) => { return null; }; +const sortedStringify = (obj) => JSON.stringify(obj, Object.keys(obj).sort()); + +const rowsMatch = (expected, actual) => { + if (expected.length !== actual.length) return false; + return expected.every((expectedRow, i) => sortedStringify(expectedRow) === sortedStringify(actual[i])); +}; + const checkResult = (core, expected, actual, expectedRows) => { const actualLength = actual.length; @@ -28,7 +35,7 @@ const checkResult = (core, expected, actual, expectedRows) => { return false; } - if (expected && JSON.stringify(expected) !== JSON.stringify(actual)) { + if (expected && !rowsMatch(expected, actual)) { core.error(`Expected results do not match actual results.\nExpected: ${JSON.stringify(expected)}\nActual: ${JSON.stringify(actual)}`); return false; }