From 643412340162e854f31fba2f162d83b7ab1751d8 Mon Sep 17 00:00:00 2001 From: Mihajlo Medjedovic Date: Tue, 11 Oct 2022 18:37:20 +0200 Subject: [PATCH] feat: cli mock testing --- .gitignore | 2 - README.md | 4 + api/mocks/custom/.keep | 0 .../{generic/sas9 => sas9/generic}/logged-in | 0 .../{generic/sas9 => sas9/generic}/logged-out | 0 .../{generic/sas9 => sas9/generic}/login | 2 +- .../generic}/public-access-denied | 0 .../sas9 => sas9/generic}/sas-stored-process | 0 api/src/app.ts | 1 + api/src/controllers/mock-sas9.ts | 131 ++++++++++++++++-- api/src/routes/setupRoutes.ts | 2 +- api/src/routes/web/index.ts | 3 +- api/src/routes/web/sas9-web.ts | 39 +++++- 13 files changed, 166 insertions(+), 18 deletions(-) delete mode 100644 api/mocks/custom/.keep rename api/mocks/{generic/sas9 => sas9/generic}/logged-in (100%) rename api/mocks/{generic/sas9 => sas9/generic}/logged-out (100%) rename api/mocks/{generic/sas9 => sas9/generic}/login (91%) rename api/mocks/{generic/sas9 => sas9/generic}/public-access-denied (100%) rename api/mocks/{generic/sas9 => sas9/generic}/sas-stored-process (100%) diff --git a/.gitignore b/.gitignore index bb6d85e..9c567ce 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,6 @@ node_modules/ .env* sas/ sasjs_root/ -api/mocks/custom/* -!api/mocks/custom/.keep tmp/ build/ sasjsbuild/ diff --git a/README.md b/README.md index 7b1c173..174bd05 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,10 @@ PORT= # If not present, mocking function is disabled MOCK_SERVERTYPE= +# Path to mocking folder, it's sub directories should be: sas9, viya, sasjs +# Server will automatically use sub directory accordingly +STATIC_MOCK_LOCATION= + # ## Additional SAS Options # diff --git a/api/mocks/custom/.keep b/api/mocks/custom/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/api/mocks/generic/sas9/logged-in b/api/mocks/sas9/generic/logged-in similarity index 100% rename from api/mocks/generic/sas9/logged-in rename to api/mocks/sas9/generic/logged-in diff --git a/api/mocks/generic/sas9/logged-out b/api/mocks/sas9/generic/logged-out similarity index 100% rename from api/mocks/generic/sas9/logged-out rename to api/mocks/sas9/generic/logged-out diff --git a/api/mocks/generic/sas9/login b/api/mocks/sas9/generic/login similarity index 91% rename from api/mocks/generic/sas9/login rename to api/mocks/sas9/generic/login index a34b391..0e75b57 100644 --- a/api/mocks/generic/sas9/login +++ b/api/mocks/sas9/generic/login @@ -9,7 +9,7 @@
- + diff --git a/api/mocks/generic/sas9/public-access-denied b/api/mocks/sas9/generic/public-access-denied similarity index 100% rename from api/mocks/generic/sas9/public-access-denied rename to api/mocks/sas9/generic/public-access-denied diff --git a/api/mocks/generic/sas9/sas-stored-process b/api/mocks/sas9/generic/sas-stored-process similarity index 100% rename from api/mocks/generic/sas9/sas-stored-process rename to api/mocks/sas9/generic/sas-stored-process diff --git a/api/src/app.ts b/api/src/app.ts index 073b311..1e60be8 100644 --- a/api/src/app.ts +++ b/api/src/app.ts @@ -57,6 +57,7 @@ export default setProcessVariables().then(async () => { app.use(express.json({ limit: '100mb' })) app.use(express.static(path.join(__dirname, '../public'))) + app.use(express.urlencoded({ extended: true })) // Body parser is used for decoding the formdata on POST request. // Currently only place we use it is SAS9 Mock - POST /SASLogon/login diff --git a/api/src/controllers/mock-sas9.ts b/api/src/controllers/mock-sas9.ts index 8a78778..166daff 100644 --- a/api/src/controllers/mock-sas9.ts +++ b/api/src/controllers/mock-sas9.ts @@ -2,6 +2,11 @@ import { readFile } from '@sasjs/utils' import express from 'express' import path from 'path' import { Request, Post, Get } from 'tsoa' +import fs from 'fs' +import fse from 'fs-extra' +import dotenv from 'dotenv' + +dotenv.config() export interface Sas9Response { content: string @@ -16,9 +21,17 @@ export interface MockFileRead { export class MockSas9Controller { private loggedIn: string | undefined + private mocksPath = process.env.STATIC_MOCK_LOCATION || 'mocks' @Get('/SASStoredProcess') - public async sasStoredProcess(): Promise { + public async sasStoredProcess( + @Request() req: express.Request + ): Promise { + let username = req.query._username?.toString() || undefined + let password = req.query._password?.toString() || undefined + + if (username && password) this.loggedIn = req.body.username + if (!this.loggedIn) { return { content: '', @@ -26,17 +39,66 @@ export class MockSas9Controller { } } + let program = req.query._program?.toString() || undefined + let filePath: string[] = ['generic', 'sas-stored-process'] + + if (program) { + filePath = program.replace('/', '').split('/') + return await getMockResponseFromFile([ + process.cwd(), + this.mocksPath, + 'sas9', + ...filePath + ]) + } + return await getMockResponseFromFile([ process.cwd(), 'mocks', - 'generic', 'sas9', - 'sas-stored-process' + ...filePath + ]) + } + + @Get('/SASStoredProcess/do') + public async sasStoredProcessDoGet( + @Request() req: express.Request + ): Promise { + let username = req.query._username?.toString() || undefined + let password = req.query._password?.toString() || undefined + + if (username && password) this.loggedIn = username + + if (!this.loggedIn) { + return { + content: '', + redirect: '/SASLogon/login' + } + } + + let program = req.query._program?.toString() || undefined + let filePath: string[] = ['generic', 'sas-stored-process'] + + if (program) { + filePath = `${program}`.replace('/', '').split('/') + return await getMockResponseFromFile([ + process.cwd(), + this.mocksPath, + 'sas9', + ...filePath + ]) + } + + return await getMockResponseFromFile([ + process.cwd(), + 'mocks', + 'sas9', + ...filePath ]) } @Post('/SASStoredProcess/do/') - public async sasStoredProcessDo( + public async sasStoredProcessDoPost( @Request() req: express.Request ): Promise { if (!this.loggedIn) { @@ -55,13 +117,56 @@ export class MockSas9Controller { let program = req.query._program?.toString() || '' program = program.replace('/', '') + let debug = req.query._debug?.toString() + + let fileContents = '' + + if (program.includes('runner') && debug === 'log') { + if (req.files && req.files.length > 0) { + const regexRequest = /cli-tests-request-sas9-.*?\d*/g + const uploadFilePath = (req.files as any)[0].path + + fileContents = fs.readFileSync(uploadFilePath, 'utf8') + + let matched = fileContents.match(regexRequest)?.[0] + + if (matched) { + const testsFolderPath = path.join( + process.cwd(), + this.mocksPath, + 'sas9', + 'User Folders', + 'cli-tests', + 'sasdemo', + matched + ) + + if (!fs.existsSync(testsFolderPath)) fs.mkdirSync(testsFolderPath) + + fse.copySync( + path.join( + process.cwd(), + this.mocksPath, + 'sas9', + 'User Folders', + 'sasdemo', + 'services' + ), + path.join(testsFolderPath, 'services') + ) + } + } + } const content = await getMockResponseFromFile([ process.cwd(), - 'mocks', + this.mocksPath, + 'sas9', ...program.split('/') ]) + content.content += fileContents + if (content.error) { return content } @@ -85,8 +190,8 @@ export class MockSas9Controller { return await getMockResponseFromFile([ process.cwd(), 'mocks', - 'generic', 'sas9', + 'generic', 'logged-in' ]) } @@ -95,21 +200,27 @@ export class MockSas9Controller { return await getMockResponseFromFile([ process.cwd(), 'mocks', - 'generic', 'sas9', + 'generic', 'login' ]) } @Post('/SASLogon/login') public async loginPost(req: express.Request): Promise { + if (req.body.lt && req.body.lt !== 'validtoken') + return { + content: '', + redirect: '/SASLogon/login' + } + this.loggedIn = req.body.username return await getMockResponseFromFile([ process.cwd(), 'mocks', - 'generic', 'sas9', + 'generic', 'logged-in' ]) } @@ -122,8 +233,8 @@ export class MockSas9Controller { return await getMockResponseFromFile([ process.cwd(), 'mocks', - 'generic', 'sas9', + 'generic', 'public-access-denied' ]) } @@ -131,8 +242,8 @@ export class MockSas9Controller { return await getMockResponseFromFile([ process.cwd(), 'mocks', - 'generic', 'sas9', + 'generic', 'logged-out' ]) } diff --git a/api/src/routes/setupRoutes.ts b/api/src/routes/setupRoutes.ts index a31aae6..a189d1d 100644 --- a/api/src/routes/setupRoutes.ts +++ b/api/src/routes/setupRoutes.ts @@ -15,5 +15,5 @@ export const setupRoutes = (app: Express) => { appStreamRouter(req, res, next) }) - app.use('/', csrfProtection, webRouter) + app.use('/', webRouter) } diff --git a/api/src/routes/web/index.ts b/api/src/routes/web/index.ts index 7bd4b82..bcd3aa1 100644 --- a/api/src/routes/web/index.ts +++ b/api/src/routes/web/index.ts @@ -3,6 +3,7 @@ import sas9WebRouter from './sas9-web' import sasViyaWebRouter from './sasviya-web' import webRouter from './web' import { MOCK_SERVERTYPEType } from '../../utils' +import { csrfProtection } from '../../middlewares' const router = express.Router() @@ -18,7 +19,7 @@ switch (MOCK_SERVERTYPE) { break } default: { - router.use('/', webRouter) + router.use('/', csrfProtection, webRouter) } } diff --git a/api/src/routes/web/sas9-web.ts b/api/src/routes/web/sas9-web.ts index f48577a..797cedc 100644 --- a/api/src/routes/web/sas9-web.ts +++ b/api/src/routes/web/sas9-web.ts @@ -2,6 +2,12 @@ import express from 'express' import { generateCSRFToken } from '../../middlewares' import { WebController } from '../../controllers' import { MockSas9Controller } from '../../controllers/mock-sas9' +import fs from 'fs' +import multer from 'multer' +import path from 'path' +import dotenv from 'dotenv' + +dotenv.config() const sas9WebRouter = express.Router() const webController = new WebController() @@ -9,6 +15,12 @@ const webController = new WebController() // for example `isLoggedIn` and potentially more in future mocks const controller = new MockSas9Controller() +const mockPath = process.env.STATIC_MOCK_LOCATION || 'mocks' + +const upload = multer({ + dest: path.join(process.cwd(), mockPath, 'sas9', 'files-recieved') +}) + sas9WebRouter.get('/', async (req, res) => { let response try { @@ -27,7 +39,7 @@ sas9WebRouter.get('/', async (req, res) => { }) sas9WebRouter.get('/SASStoredProcess', async (req, res) => { - const response = await controller.sasStoredProcess() + const response = await controller.sasStoredProcess(req) if (response.redirect) { res.redirect(response.redirect) @@ -41,8 +53,29 @@ sas9WebRouter.get('/SASStoredProcess', async (req, res) => { } }) -sas9WebRouter.post('/SASStoredProcess/do/', async (req, res) => { - const response = await controller.sasStoredProcessDo(req) +sas9WebRouter.get('/SASStoredProcess/do/', async (req, res) => { + const response = await controller.sasStoredProcessDoGet(req) + + if (response.redirect) { + res.redirect(response.redirect) + return + } + + try { + res.send(response.content) + } catch (err: any) { + res.status(403).send(err.toString()) + } +}) + +sas9WebRouter.post('/SASStoredProcess/do/', upload.any(), async (req, res) => { + const response = await controller.sasStoredProcessDoPost(req) + + if (req.files) { + ;(req.files as any).forEach((file: any) => { + fs.renameSync(file.path, file.destination + '/' + file.fieldname) + }) + } if (response.redirect) { res.redirect(response.redirect)