From 8d67e1d058e1be88b768ad9230e97da8dd7b23cd Mon Sep 17 00:00:00 2001 From: superaleks <47592680+superaleks@users.noreply.github.com> Date: Wed, 11 May 2022 17:17:08 +0200 Subject: [PATCH] [bitnami/spring-cloud-dataflow] Added the Spring Cloud Dataflow Helm chart tests (#9835) * Created the Spring Cloud Dataflow tests Signed-off-by: alukic * Trigger VIB for Spring Cloud Dataflow Signed-off-by: alukic * Trigger version bump too Signed-off-by: alukic * Removed comments Signed-off-by: alukic * Change the pipeline syntax Signed-off-by: alukic * Improved the tests, getting ready for the review' Signed-off-by: alukic * Improved the tests Signed-off-by: alukic * Added back the Goss tests Signed-off-by: alukic * Made Goss tests even more minimalistic Signed-off-by: alukic * Remove the test modification of files Signed-off-by: alukic * Passed trough linter Signed-off-by: alukic * Applied PR design suggestion Signed-off-by: alukic * Updated tests, fixing the flaw in the design Signed-off-by: alukic * Made changes in test design Signed-off-by: alukic * Removed the plugin Signed-off-by: alukic * Removed extra spaces Signed-off-by: alukic * Fix the formatting issues Signed-off-by: alukic * Remove a console log Signed-off-by: alukic * Changed the locator from CSS selector Signed-off-by: alukic * Applied the CR suggestions Signed-off-by: alukic * Added regex to check if there are numbers in version Signed-off-by: alukic * Improved code slightly Signed-off-by: alukic * Apply PR suggestions Signed-off-by: alukic * Applying PR suggestions Signed-off-by: alukic * Applying PR suggestions Signed-off-by: alukic * Locator change from CSS to text Signed-off-by: alukic * Formatting, locator change Signed-off-by: alukic * Used fixture instead of static value Signed-off-by: alukic * Replaced CSS selectors with text Signed-off-by: alukic * Apply review suggestions Signed-off-by: alukic * Reorganised the fixtures Signed-off-by: alukic * Removed the Plugins Signed-off-by: alukic * Fix a typo Signed-off-by: alukic * Non-o instead od 1001 user Signed-off-by: alukic * Changed the Goss test Signed-off-by: alukic * User ID test improvement, fixture fix Signed-off-by: alukic * Renamed files Signed-off-by: alukic * Applied peer review comments Signed-off-by: alukic --- .../cypress/cypress.json | 5 + .../cypress/fixtures/applications.json | 8 ++ .../cypress/cypress/fixtures/schedules.json | 7 + .../cypress/cypress/fixtures/streams.json | 6 + .../cypress/fixtures/task-to-import.json | 13 ++ .../cypress/cypress/fixtures/tasks.json | 5 + .../cypress/integration/prepare_app_state.js | 26 ++++ .../integration/spring_cloud_dataflow_spec.js | 121 ++++++++++++++++++ .../cypress/cypress/integration/utils.js | 1 + .../cypress/cypress/support/commands.js | 26 ++++ .../cypress/cypress/support/index.js | 20 +++ .vib/spring-cloud-dataflow/goss/goss.yaml | 22 ++++ .vib/spring-cloud-dataflow/vib-verify.json | 27 +++- 13 files changed, 285 insertions(+), 2 deletions(-) create mode 100644 .vib/spring-cloud-dataflow/cypress/cypress.json create mode 100644 .vib/spring-cloud-dataflow/cypress/cypress/fixtures/applications.json create mode 100644 .vib/spring-cloud-dataflow/cypress/cypress/fixtures/schedules.json create mode 100644 .vib/spring-cloud-dataflow/cypress/cypress/fixtures/streams.json create mode 100644 .vib/spring-cloud-dataflow/cypress/cypress/fixtures/task-to-import.json create mode 100644 .vib/spring-cloud-dataflow/cypress/cypress/fixtures/tasks.json create mode 100644 .vib/spring-cloud-dataflow/cypress/cypress/integration/prepare_app_state.js create mode 100644 .vib/spring-cloud-dataflow/cypress/cypress/integration/spring_cloud_dataflow_spec.js create mode 100644 .vib/spring-cloud-dataflow/cypress/cypress/integration/utils.js create mode 100644 .vib/spring-cloud-dataflow/cypress/cypress/support/commands.js create mode 100644 .vib/spring-cloud-dataflow/cypress/cypress/support/index.js create mode 100644 .vib/spring-cloud-dataflow/goss/goss.yaml diff --git a/.vib/spring-cloud-dataflow/cypress/cypress.json b/.vib/spring-cloud-dataflow/cypress/cypress.json new file mode 100644 index 0000000000..d900eab5a3 --- /dev/null +++ b/.vib/spring-cloud-dataflow/cypress/cypress.json @@ -0,0 +1,5 @@ +{ + "baseUrl": "http://localhost", + "defaultCommandTimeout": 30000, + "pageLoadTimeout": 240000 +} diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/applications.json b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/applications.json new file mode 100644 index 0000000000..061cfe650a --- /dev/null +++ b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/applications.json @@ -0,0 +1,8 @@ +{ + "newApplication": { + "streamApplicationType": "Stream application starters for Kafka/Maven", + "taskApplicationType": "Task application starters for Maven", + "streamApplication1": "mongodb", + "streamApplication2": "cassandra" + } +} diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/schedules.json b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/schedules.json new file mode 100644 index 0000000000..bee6cc078f --- /dev/null +++ b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/schedules.json @@ -0,0 +1,7 @@ +{ + "newSchedule": { + "name": "test-schedule", + "cronExpression": "*/5 * * * *", + "taskType": "timestamp" + } +} diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/streams.json b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/streams.json new file mode 100644 index 0000000000..5165ee0b45 --- /dev/null +++ b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/streams.json @@ -0,0 +1,6 @@ +{ + "newStream": { + "name": "test-stream", + "type": "mongodb | cassandra" + } +} diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/task-to-import.json b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/task-to-import.json new file mode 100644 index 0000000000..e351f44143 --- /dev/null +++ b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/task-to-import.json @@ -0,0 +1,13 @@ +{ + "date": 1650360393208, + "tasks": [ + { + "name": "test-task-imported", + "dslText": "timestamp", + "composed": false, + "status": "UNKNOWN", + "composedTaskElement": false, + "description": "" + } + ] +} diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/tasks.json b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/tasks.json new file mode 100644 index 0000000000..5b2633be25 --- /dev/null +++ b/.vib/spring-cloud-dataflow/cypress/cypress/fixtures/tasks.json @@ -0,0 +1,5 @@ +{ + "newTask": { + "name": "test-task" + } +} diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/integration/prepare_app_state.js b/.vib/spring-cloud-dataflow/cypress/cypress/integration/prepare_app_state.js new file mode 100644 index 0000000000..1b8f70d642 --- /dev/null +++ b/.vib/spring-cloud-dataflow/cypress/cypress/integration/prepare_app_state.js @@ -0,0 +1,26 @@ +/// +import { random } from './utils'; + +export const importAnApplication = (application) => { + cy.visit('/dashboard'); + cy.contains('button', 'Add application(s)').click(); + cy.contains('label', application).click(); + cy.contains('.btn-primary', 'Import').click(); + cy.get('.toast-container').should('contain', 'Application(s) Imported'); +}; + +export const createATask = (taskType) => { + cy.visit('/dashboard/#/tasks-jobs/tasks'); + cy.contains('button', 'Create task').click(); + cy.get('.CodeMirror-line').type(taskType); + cy.contains('#v-2', taskType); + cy.contains('button', 'Create task').click(); + cy.contains('.modal-content', 'Create task'); + cy.fixture('tasks').then((task) => { + cy.get('input[placeholder="Task Name"]').type( + `${task.newTask.name}-${random}` + ); + cy.get('input#desc').type('This is a task'); + cy.contains('button', 'Create the task').click(); + }); +}; diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/integration/spring_cloud_dataflow_spec.js b/.vib/spring-cloud-dataflow/cypress/cypress/integration/spring_cloud_dataflow_spec.js new file mode 100644 index 0000000000..8a5b971d0e --- /dev/null +++ b/.vib/spring-cloud-dataflow/cypress/cypress/integration/spring_cloud_dataflow_spec.js @@ -0,0 +1,121 @@ +/// +import { random } from './utils.js'; +import { importAnApplication, createATask } from './prepare_app_state.js'; + +it('allows getting Spring Cloud Dataflow info', () => { + cy.visit('/dashboard'); + cy.get('.signpost-trigger').click(); + cy.get('.signpost-content-body') + .invoke('text') + .should('match', /.*[0-9].*/); + cy.contains('button', 'More info').click(); + cy.contains('.modal-content', 'Core') + .and('contain', 'Dashboard') + .and('contain', 'Shell'); +}); + +it('allows a stream to be created and deployed', () => { + cy.fixture('applications').then((application) => { + importAnApplication(application.newApplication.streamApplicationType); + }); + cy.visit('dashboard/#/streams/list'); + cy.contains('button', 'Create stream(s)').click(); + cy.fixture('streams').then((stream) => { + cy.get('.CodeMirror-line').type(stream.newStream.type); + }); + + cy.fixture('applications').then((application) => { + cy.contains('#v-2', application.newApplication.streamApplication1).and( + 'contain', + application.newApplication.streamApplication2 + ); + }); + + cy.contains('button', 'Create stream(s)').click(); + cy.get('.modal-content').should('contain', 'Create Stream'); + cy.fixture('streams').then((stream) => { + cy.get('input[placeholder="Stream Name"]').type( + `${stream.newStream.name}-${random}` + ); + cy.contains('button', 'Create the stream').click(); + cy.contains( + '.datagrid-inner-wrapper', + `${stream.newStream.name}-${random}` + ); + cy.contains('.toast-container', 'successfully'); + cy.contains('clr-dg-cell', 'UNDEPLOYED') + .siblings('clr-dg-cell', stream.newStream.name) + .first() + .click(); + }); + + cy.contains('button#btn-deploy', 'Deploy stream').click(); + cy.contains('button', 'Deploy stream').click(); + cy.get('.toast-container').should('contain', 'Deploy success'); +}); + +it('allows a task to be scheduled and destroyed', () => { + cy.fixture('applications').then((application) => { + importAnApplication(application.newApplication.taskApplicationType); + }); + cy.fixture('schedules').then((schedule) => { + createATask(schedule.newSchedule.taskType); + }); + cy.visit('dashboard/#/tasks-jobs/tasks'); + cy.fixture('tasks').then((task) => { + cy.contains('clr-dg-cell', 'UNKNOWN') + .siblings('clr-dg-cell', task.newTask.name) + .first() + .click(); + }); + cy.contains('button', 'Schedule').click(); + cy.fixture('schedules').then((schedule) => { + cy.get('input[name="example"]') + .first() + .type(`${schedule.newSchedule.name}-${random}`); + + cy.get('input[name="example"]') + .last() + .type(`${schedule.newSchedule.cronExpression}`); + }); + + cy.contains('button', 'Create schedule(s)').click(); + cy.contains('.toast-container', 'Successfully'); + cy.contains('a', 'Tasks').click(); + cy.contains('clr-dg-cell', 'UNKNOWN') + .siblings('clr-dg-cell', 'test-task-') + .first() + .click(); + cy.contains('button', 'Destroy task').click(); + cy.contains('button', 'Destroy the task').click(); + cy.contains('1 task definition(s) destroyed.'); +}); + +it('allows importing a task from a file and destroying it ', () => { + cy.visit('/dashboard/#/manage/tools'); + cy.contains('a', 'Import tasks from a JSON file').click(); + cy.get('input[type="file"]').selectFile( + 'cypress/fixtures/task-to-import.json', + { force: true } + ); + cy.contains('button', 'Import').click(); + cy.contains('1 task(s) created'); + cy.get('.close').click(); + cy.visit('dashboard/#/tasks-jobs/tasks'); + cy.contains('button', 'Group Actions').click(); + cy.get('[aria-label="Select All"]').click({ force: true }); + cy.contains('button', 'Destroy task').click(); + cy.contains('.modal-content', 'Confirm Destroy Task'); + cy.contains('button', 'Destroy the task').click(); + cy.get('.toast-container').should('contain', 'destroyed'); +}); + +it('allows unregistering of an application', () => { + cy.fixture('applications').then((application) => { + importAnApplication(application.newApplication.streamApplicationType); + }); + cy.get('clr-dg-cell').siblings('clr-dg-cell', 'PROCESSOR').first().click(); + cy.contains('button', 'Unregister Application').click(); + cy.contains('button', 'Unregister the application').click(); + cy.contains('Successfully removed app'); +}); diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/integration/utils.js b/.vib/spring-cloud-dataflow/cypress/cypress/integration/utils.js new file mode 100644 index 0000000000..21de22ebf9 --- /dev/null +++ b/.vib/spring-cloud-dataflow/cypress/cypress/integration/utils.js @@ -0,0 +1 @@ +export let random = (Math.random() + 1).toString(36).substring(7); diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/support/commands.js b/.vib/spring-cloud-dataflow/cypress/cypress/support/commands.js new file mode 100644 index 0000000000..42dac69cdc --- /dev/null +++ b/.vib/spring-cloud-dataflow/cypress/cypress/support/commands.js @@ -0,0 +1,26 @@ +const CLICK_DELAY = 800; +const TYPE_DELAY = 200; + +for (const command of ['click']) { + Cypress.Commands.overwrite(command, (originalFn, ...args) => { + const origVal = originalFn(...args); + + return new Promise((resolve) => { + setTimeout(() => { + resolve(origVal); + }, CLICK_DELAY); + }); + }); +} + +for (const command of ['type']) { + Cypress.Commands.overwrite(command, (originalFn, ...args) => { + const origVal = originalFn(...args); + + return new Promise((resolve) => { + setTimeout(() => { + resolve(origVal); + }, TYPE_DELAY); + }); + }); +} diff --git a/.vib/spring-cloud-dataflow/cypress/cypress/support/index.js b/.vib/spring-cloud-dataflow/cypress/cypress/support/index.js new file mode 100644 index 0000000000..37a498fb5b --- /dev/null +++ b/.vib/spring-cloud-dataflow/cypress/cypress/support/index.js @@ -0,0 +1,20 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands'; + +// Alternatively you can use CommonJS syntax: +// require('./commands') diff --git a/.vib/spring-cloud-dataflow/goss/goss.yaml b/.vib/spring-cloud-dataflow/goss/goss.yaml new file mode 100644 index 0000000000..be85b3d8a4 --- /dev/null +++ b/.vib/spring-cloud-dataflow/goss/goss.yaml @@ -0,0 +1,22 @@ +file: + /opt/bitnami/spring-cloud-dataflow/conf/application.yml: + exists: true + filetype: symlink + mode: "0777" + owner: root + /bitnami/spring-cloud-dataflow: + exists: true + filetype: directory + mode: "0775" + owner: root +command: + user-id-test: + exec: if [ "$(id -u)" -eq 0 ]; then exit 1; fi + exit-status: 0 + stdout: [] + stderr: [] + java-test: + exec: java -version + exit-status: 0 + stdout: + stderr: [] diff --git a/.vib/spring-cloud-dataflow/vib-verify.json b/.vib/spring-cloud-dataflow/vib-verify.json index e7fb7b730e..270ef1be94 100644 --- a/.vib/spring-cloud-dataflow/vib-verify.json +++ b/.vib/spring-cloud-dataflow/vib-verify.json @@ -22,7 +22,7 @@ "url": "{SHA_ARCHIVE}", "path": "/bitnami/spring-cloud-dataflow" }, - "runtime_parameters": "InNlcnZlciI6CiAgInNlcnZpY2UiOgogICAgInBvcnQiOiA4MAogICAgInR5cGUiOiAiTG9hZEJhbGFuY2VyIg==", + "runtime_parameters": "c2VydmVyOgogIHNlcnZpY2U6CiAgICB0eXBlOiBMb2FkQmFsYW5jZXIKICAgIHBvcnQ6IDgwCg==", "target_platform": { "target_platform_id": "{VIB_ENV_TARGET_PLATFORM}", "size": { @@ -35,7 +35,9 @@ "action_id": "trivy", "params": { "threshold": "CRITICAL", - "vuln_type": ["OS"] + "vuln_type": [ + "OS" + ] } }, { @@ -44,6 +46,27 @@ "endpoint": "lb-spring-cloud-dataflow-server-http", "app_protocol": "HTTP" } + }, + { + "action_id": "goss", + "params": { + "resources": { + "path": "/.vib/spring-cloud-dataflow/goss" + }, + "remote": { + "workload": "deploy-spring-cloud-dataflow-server" + } + } + }, + { + "action_id": "cypress", + "params": { + "resources": { + "path": "/.vib/spring-cloud-dataflow/cypress" + }, + "endpoint": "lb-spring-cloud-dataflow-server-http", + "app_protocol": "HTTP" + } } ] }