From 3d743aeb3491bc287772ab8e1c90b5ff65828d62 Mon Sep 17 00:00:00 2001 From: pamelin Date: Mon, 6 Feb 2023 11:48:20 +0000 Subject: [PATCH] fix: add stackstorm client tests Signed-off-by: pamelin --- app-config.yaml | 8 + plugins/stackstorm/config.d.ts | 13 -- .../src/api/StackStormClient.test.ts | 214 ++++++++++++++++++ plugins/stackstorm/src/api/types.ts | 6 +- 4 files changed, 225 insertions(+), 16 deletions(-) create mode 100644 plugins/stackstorm/src/api/StackStormClient.test.ts diff --git a/app-config.yaml b/app-config.yaml index dd72051189..c97adec5ef 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -118,6 +118,11 @@ proxy: headers: Authorization: 'Api-Token ${DYNATRACE_ACCESS_TOKEN}' + '/stackstorm': + target: https://your.stackstorm.instance.com/api + headers: + St2-Api-Key: ${ST2_API_KEY} + organization: name: My Company @@ -441,5 +446,8 @@ apacheAirflow: gocd: baseUrl: https://your.gocd.instance.com +stackstorm: + webUrl: https://your.stackstorm.webui.instance.com + permission: enabled: true diff --git a/plugins/stackstorm/config.d.ts b/plugins/stackstorm/config.d.ts index cf7a2e2253..64276503fc 100644 --- a/plugins/stackstorm/config.d.ts +++ b/plugins/stackstorm/config.d.ts @@ -15,19 +15,6 @@ */ export interface Config { stackstorm?: { - /** - * StackStorm API base url - * Used for local testing to bypass CORS - * @visibility frontend - */ - baseUrl?: string; - /** - * StackStorm API key - * Used for local testing to bypass CORS - * Do not set this in production deployment - * @visibility frontend - */ - key?: string; /** * StackStorm Web UI url * Used in links to StackStorm web UI diff --git a/plugins/stackstorm/src/api/StackStormClient.test.ts b/plugins/stackstorm/src/api/StackStormClient.test.ts new file mode 100644 index 0000000000..24400e3174 --- /dev/null +++ b/plugins/stackstorm/src/api/StackStormClient.test.ts @@ -0,0 +1,214 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { UrlPatternDiscovery } from '@backstage/core-app-api'; +import { setupRequestMockHandlers } from '@backstage/test-utils'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { StackStormClient } from './StackStormClient'; +import { Action, Execution, Pack } from './types'; + +const server = setupServer(); + +const executions: Execution[] = [ + { + id: '63dcac3e18ba00e09e7bb3b6', + action: { + name: 'post_message', + ref: 'chatops.post_message', + description: 'Post a message to stream for chatops', + pack: 'chatops', + runner_type: 'announcement', + id: '62fe101b11935b6aaff4ff92', + }, + status: 'succeeded', + start_timestamp: new Date().toISOString(), + end_timestamp: new Date().toISOString(), + result: {}, + parameters: {}, + elapsed_seconds: 2.2, + log: [], + }, + { + id: '63dcac3e18ba00e09e7bb3b6', + action: { + name: 'post_result', + ref: 'chatops.post_result', + description: 'Post an execution result to stream for chatops', + pack: 'chatops', + runner_type: 'orquesta', + id: '62fe101b11935b6aaff4ff93', + }, + status: 'succeeded', + start_timestamp: new Date().toISOString(), + end_timestamp: new Date().toISOString(), + result: {}, + parameters: {}, + elapsed_seconds: 18.5, + log: [], + }, + { + id: '63dcac3c9e0b4fe98f46becc', + action: { + name: 'run', + ref: 'shell.run', + description: 'Run shell script', + pack: 'shell', + runner_type: 'shell', + id: '63736caac3d8557c4d61883a', + }, + status: 'failed', + start_timestamp: new Date().toISOString(), + end_timestamp: new Date().toISOString(), + result: {}, + parameters: {}, + elapsed_seconds: 5.0, + log: [], + }, +]; + +const executionWithDetails: Execution = { + id: '63dcac3e18ba00e09e7bb3b6', + action: { + name: 'post_message', + ref: 'chatops.post_message', + description: 'Post a message to stream for chatops', + pack: 'chatops', + runner_type: 'announcement', + id: '62fe101b11935b6aaff4ff92', + }, + status: 'succeeded', + start_timestamp: new Date().toISOString(), + end_timestamp: new Date().toISOString(), + result: { + output: { + result: 'hello', + }, + }, + parameters: { + var: 'val', + }, + elapsed_seconds: 2.2, + log: [ + { status: 'requested', timestamp: new Date().toISOString() }, + { status: 'succeeded', timestamp: new Date().toISOString() }, + ], +}; + +const packs: Pack[] = [ + { + ref: 'chatops', + description: 'ChatOps integration pack', + version: '3.7.0', + }, + { + ref: 'core', + description: 'Basic core actions.', + version: '3.7.0', + }, +]; + +const actions: Action[] = [ + { + id: '62fe101b11935b6aaff4ff96', + name: 'announcement', + ref: 'core.announcement', + pack: 'core', + description: + 'Action that broadcasts the announcement to all stream consumers.', + runner_type: 'announcement', + }, + { + id: '62fe101b11935b6aaff4ff97', + name: 'echo', + ref: 'core.echo', + pack: 'core', + description: + 'Action that executes the Linux echo command on the localhost.', + runner_type: 'local-shell-cmd', + }, +]; + +const identityApiMock = () => ({ + signOut: jest.fn(), + getProfileInfo: jest.fn(), + getBackstageIdentity: jest.fn(), + getCredentials: jest.fn().mockResolvedValue({ token: undefined }), +}); + +describe('StackStormClient', () => { + setupRequestMockHandlers(server); + + const mockBaseUrl = 'http://backstage:9191/api/proxy'; + const discoveryApi = UrlPatternDiscovery.compile(mockBaseUrl); + let client: StackStormClient; + + const setupHandlers = () => { + server.use( + rest.get(`${mockBaseUrl}/stackstorm/executions`, (req, res, ctx) => { + const limit = req.url.searchParams.get('limit') + ? Number(req.url.searchParams.get('limit')) + : executions.length; + const offset = req.url.searchParams.get('offset') + ? Number(req.url.searchParams.get('offset')) + : 0; + return res(ctx.json(executions.slice(offset, offset + limit))); + }), + rest.get( + `${mockBaseUrl}/stackstorm/executions/63dcac3e18ba00e09e7bb3b6`, + (_req, res, ctx) => { + return res(ctx.json(executionWithDetails)); + }, + ), + rest.get(`${mockBaseUrl}/stackstorm/packs`, (_req, res, ctx) => { + return res(ctx.json(packs)); + }), + rest.get(`${mockBaseUrl}/stackstorm/actions`, (req, res, ctx) => { + const name = req.url.searchParams.get('pack'); + return res(ctx.json(name === 'core' ? actions : [])); + }), + ); + }; + + beforeEach(() => { + setupHandlers(); + client = new StackStormClient({ + discoveryApi: discoveryApi, + identityApi: identityApiMock(), + }); + }); + + it('get executions should return executions with emulated pagination', async () => { + const got = await client.getExecutions(2, 1); + expect(got.length).toEqual(2); + expect(got).toMatchObject(executions.slice(1, 3)); + }); + + it('get execution should return one execution', async () => { + const got = await client.getExecution('63dcac3e18ba00e09e7bb3b6'); + expect(got).toMatchObject(executionWithDetails); + }); + + it('get packs should return list of all packs', async () => { + const got = await client.getPacks(); + expect(got.length).toEqual(packs.length); + expect(got).toMatchObject(packs); + }); + + it('get actions should return list of actions', async () => { + const got = await client.getActions('core'); + expect(got).toMatchObject(actions); + }); +}); diff --git a/plugins/stackstorm/src/api/types.ts b/plugins/stackstorm/src/api/types.ts index 8a3036d3ca..13c92fb055 100644 --- a/plugins/stackstorm/src/api/types.ts +++ b/plugins/stackstorm/src/api/types.ts @@ -23,8 +23,8 @@ export type Execution = { id: string; action: Action; status: string; - start_timestamp: Date; - end_timestamp: Date; + start_timestamp: string; + end_timestamp: string; result: object; parameters: object; elapsed_seconds: number; @@ -33,7 +33,7 @@ export type Execution = { export type ExecutionLog = { status: string; - timestamp: Date; + timestamp: string; }; export type Action = {