From 1d93bccb4ea0fcf6a51b2e109eed5d87a6f88cca Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Sun, 28 Apr 2024 18:28:23 +1000 Subject: [PATCH 01/13] 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 02/13] 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 03/13] 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 04/13] 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 05/13] 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 06/13] 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 07/13] 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 08/13] 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 09/13] 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 10/13] 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 11/13] 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 12/13] 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 13/13] 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.