From 929b7101afd1516c0e82492f02baf66049751ae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 23 Oct 2023 10:18:20 +0200 Subject: [PATCH 01/30] Added util to read local test xlsx file to Workbook/Sheet type --- libs/extensions/tabular/exec/test/util.ts | 39 +++++++++++++++++++ .../tabular/exec/tsconfig.spec.json | 3 +- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 libs/extensions/tabular/exec/test/util.ts diff --git a/libs/extensions/tabular/exec/test/util.ts b/libs/extensions/tabular/exec/test/util.ts new file mode 100644 index 000000000..3ee482362 --- /dev/null +++ b/libs/extensions/tabular/exec/test/util.ts @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as path from 'path'; + +import { Workbook } from '@jvalue/jayvee-execution'; +import * as exceljs from 'exceljs'; + +export async function createWorkbookFromLocalExcelFile( + fileName: string, +): Promise { + const workBookFromFile = new exceljs.Workbook(); + await workBookFromFile.xlsx.readFile(path.resolve(__dirname, fileName)); + + const workbook = new Workbook(); + + workBookFromFile.eachSheet((workSheet) => { + const workSheetDataArray: string[][] = []; + workSheet.eachRow((row, rowNumber) => { + const cellValues: string[] = []; + + // ExcelJS Rows and Columns are indexed from 1 + // We reduce their index to match Sheets being zero indexed + row.eachCell( + { includeEmpty: true }, + (cell: exceljs.Cell, colNumber: number) => { + cellValues[colNumber - 1] = cell.text; + }, + ); + + workSheetDataArray[rowNumber - 1] = cellValues; + }); + + workbook.addSheet(workSheetDataArray, workSheet.name); + }); + + return workbook; +} diff --git a/libs/extensions/tabular/exec/tsconfig.spec.json b/libs/extensions/tabular/exec/tsconfig.spec.json index 6668655fc..ab8d1573c 100644 --- a/libs/extensions/tabular/exec/tsconfig.spec.json +++ b/libs/extensions/tabular/exec/tsconfig.spec.json @@ -9,6 +9,7 @@ "jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", - "src/**/*.d.ts" + "src/**/*.d.ts", + "test/**/*.ts" ] } From 01ef6313525745a79dd7dc0498dc64af2868998f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 23 Oct 2023 10:19:29 +0200 Subject: [PATCH 02/30] Added tests for cell-range-selector --- .../lib/cell-range-selector-executor.spec.ts | 178 ++++++++++++++++++ .../test-A1:C16.xlsx | Bin 0 -> 5904 bytes .../test-B1:C2.xlsx | Bin 0 -> 6085 bytes .../test-empty.xlsx | Bin 0 -> 5410 bytes .../valid-A1:A4.jv | 18 ++ .../valid-A1:C*.jv | 18 ++ .../valid-A1:E4.jv | 18 ++ 7 files changed, 232 insertions(+) create mode 100644 libs/extensions/tabular/exec/src/lib/cell-range-selector-executor.spec.ts create mode 100644 libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-A1:C16.xlsx create mode 100644 libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-B1:C2.xlsx create mode 100644 libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-empty.xlsx create mode 100644 libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:A4.jv create mode 100644 libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:C*.jv create mode 100644 libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:E4.jv diff --git a/libs/extensions/tabular/exec/src/lib/cell-range-selector-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/cell-range-selector-executor.spec.ts new file mode 100644 index 000000000..5eb544fd7 --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/cell-range-selector-executor.spec.ts @@ -0,0 +1,178 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as path from 'path'; + +import * as R from '@jvalue/jayvee-execution'; +import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { TabularLangExtension } from '@jvalue/jayvee-extensions/tabular/lang'; +import { + BlockDefinition, + IOType, + createJayveeServices, + useExtension, +} from '@jvalue/jayvee-language-server'; +import { + ParseHelperOptions, + TestLangExtension, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { createWorkbookFromLocalExcelFile } from '../../test/util'; + +import { CellRangeSelectorExecutor } from './cell-range-selector-executor'; + +describe('Validation of CellRangeSelectorExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise>; + + let locator: AstNodeLocator; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/cell-range-selector-executor/', + ); + + async function readTestWorkbook(fileName: string): Promise { + const absoluteFileName = path.resolve( + __dirname, + '../../test/assets/cell-range-selector-executor/', + fileName, + ); + return await createWorkbookFromLocalExcelFile(absoluteFileName); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.Sheet, + ): Promise> { + const document = await parse(input, { validationChecks: 'all' }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new CellRangeSelectorExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, [block]), + ); + } + + beforeAll(() => { + // Register extensions + useExtension(new TabularLangExtension()); + useExtension(new TestLangExtension()); + // Create language services + const services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid selector', async () => { + const text = readJvTestAsset('valid-A1:C*.jv'); + + const testWorkbook = await readTestWorkbook('test-A1:C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getHeaderRow()).toEqual(['0', 'Test', 'true']); + expect(result.right.getData()).toEqual( + expect.arrayContaining([ + ['0', 'Test', 'true'], + ['1', 'Test', 'false'], + ['15', 'Test', 'true'], + ]), + ); + } + }); + + it('should diagnose no error on empty column', async () => { + const text = readJvTestAsset('valid-A1:C*.jv'); + + const testWorkbook = await readTestWorkbook('test-B1:C2.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(2); + expect(result.right.getHeaderRow()).toEqual(['', 'Test', 'true']); + expect(result.right.getData()).toEqual([ + ['', 'Test', 'true'], + ['', 'Test', 'false'], + ]); + } + }); + + it('should diagnose no error on selector out of bounds', async () => { + const text = readJvTestAsset('valid-A1:E4.jv'); + + const testWorkbook = await readTestWorkbook('test-A1:C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'The specified cell range does not fit the sheet', + ); + } + }); + + it('should diagnose no error on selector on empty sheet', async () => { + const text = readJvTestAsset('valid-A1:C*.jv'); + + const testWorkbook = await readTestWorkbook('test-empty.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'The specified cell range does not fit the sheet', + ); + } + }); + + it('should diagnose no error on single column selector on empty sheet', async () => { + const text = readJvTestAsset('valid-A1:A4.jv'); + + const testWorkbook = await readTestWorkbook('test-empty.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'The specified cell range does not fit the sheet', + ); + } + }); +}); diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-A1:C16.xlsx b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-A1:C16.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..0544fa2814c07cf9c2736a400489785567ac1b4d GIT binary patch literal 5904 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 literal 0 HcmV?d00001 diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:A4.jv b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:A4.jv new file mode 100644 index 000000000..666e6e2ef --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:A4.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype CellRangeSelector { + select: range A1:A4; + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:C*.jv b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:C*.jv new file mode 100644 index 000000000..9f6c914be --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:C*.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype CellRangeSelector { + select: range A1:C*; + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:E4.jv b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:E4.jv new file mode 100644 index 000000000..605210d2d --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:E4.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype CellRangeSelector { + select: range A1:E4; + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} From 6c264f916622db63e0062677c4afe0ebce960fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 23 Oct 2023 10:19:52 +0200 Subject: [PATCH 03/30] Added fix for 'structuredclone is not defined' --- libs/extensions/tabular/exec/jest.config.ts | 1 + .../tabular/exec/src/jsdom-environment-fix.ts | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 libs/extensions/tabular/exec/src/jsdom-environment-fix.ts diff --git a/libs/extensions/tabular/exec/jest.config.ts b/libs/extensions/tabular/exec/jest.config.ts index e593333a4..9844a26bb 100644 --- a/libs/extensions/tabular/exec/jest.config.ts +++ b/libs/extensions/tabular/exec/jest.config.ts @@ -5,6 +5,7 @@ export default { displayName: 'extensions-tabular-exec', preset: '../../../../jest.preset.js', + testEnvironment: './src/jsdom-environment-fix.ts', globals: { 'ts-jest': { tsconfig: '/tsconfig.spec.json', diff --git a/libs/extensions/tabular/exec/src/jsdom-environment-fix.ts b/libs/extensions/tabular/exec/src/jsdom-environment-fix.ts new file mode 100644 index 000000000..c18ba7718 --- /dev/null +++ b/libs/extensions/tabular/exec/src/jsdom-environment-fix.ts @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import JSDOMEnvironment from 'jest-environment-jsdom'; + +// https://github.com/jsdom/jsdom/issues/3363 +export default class FixJSDOMEnvironment extends JSDOMEnvironment { + constructor(...args: ConstructorParameters) { + super(...args); + + this.global.structuredClone = structuredClone; + } +} From f5fe0aac9a55e2d8c6e31a94ff7d502ce21262be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 23 Oct 2023 10:20:14 +0200 Subject: [PATCH 04/30] Added tests for cell-writer --- .../exec/src/lib/cell-writer-executor.spec.ts | 147 ++++++++++++++++++ .../cell-writer-executor/test-A1:C16.xlsx | Bin 0 -> 5904 bytes .../cell-writer-executor/test-empty.xlsx | Bin 0 -> 5410 bytes .../valid-cell-range-writer.jv | 19 +++ .../valid-single-cell-writer.jv | 19 +++ 5 files changed, 185 insertions(+) create mode 100644 libs/extensions/tabular/exec/src/lib/cell-writer-executor.spec.ts create mode 100644 libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-A1:C16.xlsx create mode 100644 libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-empty.xlsx create mode 100644 libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-cell-range-writer.jv create mode 100644 libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-single-cell-writer.jv diff --git a/libs/extensions/tabular/exec/src/lib/cell-writer-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/cell-writer-executor.spec.ts new file mode 100644 index 000000000..5f843e392 --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/cell-writer-executor.spec.ts @@ -0,0 +1,147 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as path from 'path'; + +import * as R from '@jvalue/jayvee-execution'; +import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { TabularLangExtension } from '@jvalue/jayvee-extensions/tabular/lang'; +import { + BlockDefinition, + IOType, + createJayveeServices, + useExtension, +} from '@jvalue/jayvee-language-server'; +import { + ParseHelperOptions, + TestLangExtension, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { createWorkbookFromLocalExcelFile } from '../../test/util'; + +import { CellWriterExecutor } from './cell-writer-executor'; + +describe('Validation of CellWriterExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise>; + + let locator: AstNodeLocator; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/cell-writer-executor/', + ); + + async function readTestWorkbook(fileName: string): Promise { + const absoluteFileName = path.resolve( + __dirname, + '../../test/assets/cell-writer-executor/', + fileName, + ); + return await createWorkbookFromLocalExcelFile(absoluteFileName); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.Sheet, + ): Promise> { + const document = await parse(input, { validationChecks: 'all' }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new CellWriterExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, [block]), + ); + } + + beforeAll(() => { + // Register extensions + useExtension(new TabularLangExtension()); + useExtension(new TestLangExtension()); + // Create language services + const services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid single cell writer', async () => { + const text = readJvTestAsset('valid-single-cell-writer.jv'); + + const testWorkbook = await readTestWorkbook('test-A1:C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getHeaderRow()).toEqual(['16', 'Test', 'true']); + expect(result.right.getData()).toEqual( + expect.arrayContaining([ + ['16', 'Test', 'true'], + ['1', 'Test', 'false'], + ['15', 'Test', 'true'], + ]), + ); + } + }); + + it('should diagnose no error on valid cell range writer', async () => { + const text = readJvTestAsset('valid-cell-range-writer.jv'); + + const testWorkbook = await readTestWorkbook('test-A1:C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getHeaderRow()).toEqual(['16', 'Test2', '']); + expect(result.right.getData()).toEqual( + expect.arrayContaining([ + ['16', 'Test2', ''], + ['1', 'Test', 'false'], + ['15', 'Test', 'true'], + ]), + ); + } + }); + + it('should diagnose no error on single cell writer on empty sheet', async () => { + const text = readJvTestAsset('valid-single-cell-writer.jv'); + + const testWorkbook = await readTestWorkbook('test-empty.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'Some specified cells do not exist in the sheet', + ); + } + }); +}); diff --git a/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-A1:C16.xlsx b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-A1:C16.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..0544fa2814c07cf9c2736a400489785567ac1b4d GIT binary patch literal 5904 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 literal 0 HcmV?d00001 diff --git a/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-cell-range-writer.jv b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-cell-range-writer.jv new file mode 100644 index 000000000..4ce5bece6 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-cell-range-writer.jv @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype CellWriter { + write: ["16", "Test2", ""]; + at: range A1:C1; + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-single-cell-writer.jv b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-single-cell-writer.jv new file mode 100644 index 000000000..a7390b52e --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-single-cell-writer.jv @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype CellWriter { + write: ["16"]; + at: cell A1; + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} From f7db0e343f959db6de00442d90f9e2b9c1449318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 23 Oct 2023 10:27:34 +0200 Subject: [PATCH 05/30] Fixed test names --- .../exec/src/lib/cell-range-selector-executor.spec.ts | 6 +++--- .../tabular/exec/src/lib/cell-writer-executor.spec.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/extensions/tabular/exec/src/lib/cell-range-selector-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/cell-range-selector-executor.spec.ts index 5eb544fd7..a230f3356 100644 --- a/libs/extensions/tabular/exec/src/lib/cell-range-selector-executor.spec.ts +++ b/libs/extensions/tabular/exec/src/lib/cell-range-selector-executor.spec.ts @@ -125,7 +125,7 @@ describe('Validation of CellRangeSelectorExecutor', () => { } }); - it('should diagnose no error on selector out of bounds', async () => { + it('should diagnose error on selector out of bounds', async () => { const text = readJvTestAsset('valid-A1:E4.jv'); const testWorkbook = await readTestWorkbook('test-A1:C16.xlsx'); @@ -142,7 +142,7 @@ describe('Validation of CellRangeSelectorExecutor', () => { } }); - it('should diagnose no error on selector on empty sheet', async () => { + it('should diagnose error on selector on empty sheet', async () => { const text = readJvTestAsset('valid-A1:C*.jv'); const testWorkbook = await readTestWorkbook('test-empty.xlsx'); @@ -159,7 +159,7 @@ describe('Validation of CellRangeSelectorExecutor', () => { } }); - it('should diagnose no error on single column selector on empty sheet', async () => { + it('should diagnose error on single column selector on empty sheet', async () => { const text = readJvTestAsset('valid-A1:A4.jv'); const testWorkbook = await readTestWorkbook('test-empty.xlsx'); diff --git a/libs/extensions/tabular/exec/src/lib/cell-writer-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/cell-writer-executor.spec.ts index 5f843e392..28479475f 100644 --- a/libs/extensions/tabular/exec/src/lib/cell-writer-executor.spec.ts +++ b/libs/extensions/tabular/exec/src/lib/cell-writer-executor.spec.ts @@ -128,7 +128,7 @@ describe('Validation of CellWriterExecutor', () => { } }); - it('should diagnose no error on single cell writer on empty sheet', async () => { + it('should diagnose error on single cell writer on empty sheet', async () => { const text = readJvTestAsset('valid-single-cell-writer.jv'); const testWorkbook = await readTestWorkbook('test-empty.xlsx'); From 024f6d3461397426bd47deeb3fd1c768ba7d8855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 23 Oct 2023 10:58:20 +0200 Subject: [PATCH 06/30] Added missing license files --- .../cell-range-selector-executor/test-A1:C16.xlsx.license | 3 +++ .../cell-range-selector-executor/test-B1:C2.xlsx.license | 3 +++ .../cell-range-selector-executor/test-empty.xlsx.license | 3 +++ .../test/assets/cell-writer-executor/test-A1:C16.xlsx.license | 3 +++ .../test/assets/cell-writer-executor/test-empty.xlsx.license | 3 +++ 5 files changed, 15 insertions(+) create mode 100644 libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-A1:C16.xlsx.license create mode 100644 libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-B1:C2.xlsx.license create mode 100644 libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-empty.xlsx.license create mode 100644 libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-A1:C16.xlsx.license create mode 100644 libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-empty.xlsx.license diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-A1:C16.xlsx.license b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-A1:C16.xlsx.license new file mode 100644 index 000000000..17c5d2bad --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-A1:C16.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-B1:C2.xlsx.license b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-B1:C2.xlsx.license new file mode 100644 index 000000000..17c5d2bad --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-B1:C2.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-empty.xlsx.license b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-empty.xlsx.license new file mode 100644 index 000000000..17c5d2bad --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/test-empty.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-A1:C16.xlsx.license b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-A1:C16.xlsx.license new file mode 100644 index 000000000..17c5d2bad --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-A1:C16.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-empty.xlsx.license b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-empty.xlsx.license new file mode 100644 index 000000000..17c5d2bad --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/test-empty.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only From 1971864b12bee429bb67e77d6f236b7ac4f3b44e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 23 Oct 2023 10:58:41 +0200 Subject: [PATCH 07/30] Added column-deleter tests --- .../src/lib/column-deleter-executor.spec.ts | 168 ++++++++++++++++++ .../column-deleter-executor/test-A1:B2.xlsx | Bin 0 -> 5963 bytes .../test-A1:B2.xlsx.license | 3 + .../column-deleter-executor/test-A1:C16.xlsx | Bin 0 -> 5904 bytes .../test-A1:C16.xlsx.license | 3 + .../valid-duplicate-column.jv | 18 ++ .../valid-multiple-column-deleter.jv | 18 ++ .../valid-single-column-deleter.jv | 18 ++ 8 files changed, 228 insertions(+) create mode 100644 libs/extensions/tabular/exec/src/lib/column-deleter-executor.spec.ts create mode 100644 libs/extensions/tabular/exec/test/assets/column-deleter-executor/test-A1:B2.xlsx create mode 100644 libs/extensions/tabular/exec/test/assets/column-deleter-executor/test-A1:B2.xlsx.license create mode 100644 libs/extensions/tabular/exec/test/assets/column-deleter-executor/test-A1:C16.xlsx create mode 100644 libs/extensions/tabular/exec/test/assets/column-deleter-executor/test-A1:C16.xlsx.license create mode 100644 libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-duplicate-column.jv create mode 100644 libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-multiple-column-deleter.jv create mode 100644 libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-single-column-deleter.jv diff --git a/libs/extensions/tabular/exec/src/lib/column-deleter-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/column-deleter-executor.spec.ts new file mode 100644 index 000000000..e9e6ba32e --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/column-deleter-executor.spec.ts @@ -0,0 +1,168 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as path from 'path'; + +import * as R from '@jvalue/jayvee-execution'; +import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { TabularLangExtension } from '@jvalue/jayvee-extensions/tabular/lang'; +import { + BlockDefinition, + IOType, + createJayveeServices, + useExtension, +} from '@jvalue/jayvee-language-server'; +import { + ParseHelperOptions, + TestLangExtension, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { createWorkbookFromLocalExcelFile } from '../../test/util'; + +import { ColumnDeleterExecutor } from './column-deleter-executor'; + +describe('Validation of ColumnDeleterExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise>; + + let locator: AstNodeLocator; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/column-deleter-executor/', + ); + + async function readTestWorkbook(fileName: string): Promise { + const absoluteFileName = path.resolve( + __dirname, + '../../test/assets/column-deleter-executor/', + fileName, + ); + return await createWorkbookFromLocalExcelFile(absoluteFileName); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.Sheet, + ): Promise> { + const document = await parse(input, { validationChecks: 'all' }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new ColumnDeleterExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, [block]), + ); + } + + beforeAll(() => { + // Register extensions + useExtension(new TabularLangExtension()); + useExtension(new TestLangExtension()); + // Create language services + const services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid single column deleter', async () => { + const text = readJvTestAsset('valid-single-column-deleter.jv'); + + const testWorkbook = await readTestWorkbook('test-A1:C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(2); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getHeaderRow()).toEqual(['Test', 'true']); + expect(result.right.getData()).toEqual( + expect.arrayContaining([ + ['Test', 'true'], + ['Test', 'false'], + ['Test', 'true'], + ]), + ); + } + }); + + it('should diagnose no error on valid multiple column deleter', async () => { + const text = readJvTestAsset('valid-multiple-column-deleter.jv'); + + const testWorkbook = await readTestWorkbook('test-A1:C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(1); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getHeaderRow()).toEqual(['Test']); + expect(result.right.getData()).toEqual( + expect.arrayContaining([['Test'], ['Test'], ['Test']]), + ); + } + }); + + it('should diagnose error on deleting non existing column', async () => { + const text = readJvTestAsset('valid-multiple-column-deleter.jv'); + + const testWorkbook = await readTestWorkbook('test-A1:B2.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'The specified column C does not exist in the sheet', + ); + } + }); + + it('should diagnose only one column deletion on duplicate columns', async () => { + const text = readJvTestAsset('valid-duplicate-column.jv'); + + const testWorkbook = await readTestWorkbook('test-A1:C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(2); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getHeaderRow()).toEqual(['Test', 'true']); + expect(result.right.getData()).toEqual( + expect.arrayContaining([ + ['Test', 'true'], + ['Test', 'false'], + ['Test', 'true'], + ]), + ); + } + }); +}); diff --git a/libs/extensions/tabular/exec/test/assets/column-deleter-executor/test-A1:B2.xlsx b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/test-A1:B2.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..3465ea34d220774609e8f2f20a78490c8151e05b GIT binary patch literal 5963 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 iKeZoH>)#zPf&E{DURe(D4p TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-multiple-column-deleter.jv b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-multiple-column-deleter.jv new file mode 100644 index 000000000..c129b7639 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-multiple-column-deleter.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype ColumnDeleter { + delete: [column A, column C]; + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-single-column-deleter.jv b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-single-column-deleter.jv new file mode 100644 index 000000000..0f6f33474 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-single-column-deleter.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype ColumnDeleter { + delete: [column A]; + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} From 1e53ac3fc3df61b8d9e2fded1b80550d426ea80d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 23 Oct 2023 10:58:54 +0200 Subject: [PATCH 08/30] Added row-deleter tests --- .../exec/src/lib/row-deleter-executor.spec.ts | 163 ++++++++++++++++++ .../row-deleter-executor/test-A1:B2.xlsx | Bin 0 -> 5963 bytes .../test-A1:B2.xlsx.license | 3 + .../row-deleter-executor/test-A1:C16.xlsx | Bin 0 -> 5904 bytes .../test-A1:C16.xlsx.license | 3 + .../valid-duplicate-row.jv | 18 ++ .../valid-multiple-row-deleter.jv | 18 ++ .../valid-single-row-deleter.jv | 18 ++ 8 files changed, 223 insertions(+) create mode 100644 libs/extensions/tabular/exec/src/lib/row-deleter-executor.spec.ts create mode 100644 libs/extensions/tabular/exec/test/assets/row-deleter-executor/test-A1:B2.xlsx create mode 100644 libs/extensions/tabular/exec/test/assets/row-deleter-executor/test-A1:B2.xlsx.license create mode 100644 libs/extensions/tabular/exec/test/assets/row-deleter-executor/test-A1:C16.xlsx create mode 100644 libs/extensions/tabular/exec/test/assets/row-deleter-executor/test-A1:C16.xlsx.license create mode 100644 libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-duplicate-row.jv create mode 100644 libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-multiple-row-deleter.jv create mode 100644 libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-single-row-deleter.jv diff --git a/libs/extensions/tabular/exec/src/lib/row-deleter-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/row-deleter-executor.spec.ts new file mode 100644 index 000000000..c91382169 --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/row-deleter-executor.spec.ts @@ -0,0 +1,163 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as path from 'path'; + +import * as R from '@jvalue/jayvee-execution'; +import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { TabularLangExtension } from '@jvalue/jayvee-extensions/tabular/lang'; +import { + BlockDefinition, + IOType, + createJayveeServices, + useExtension, +} from '@jvalue/jayvee-language-server'; +import { + ParseHelperOptions, + TestLangExtension, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { createWorkbookFromLocalExcelFile } from '../../test/util'; + +import { RowDeleterExecutor } from './row-deleter-executor'; + +describe('Validation of RowDeleterExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise>; + + let locator: AstNodeLocator; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/row-deleter-executor/', + ); + + async function readTestWorkbook(fileName: string): Promise { + const absoluteFileName = path.resolve( + __dirname, + '../../test/assets/row-deleter-executor/', + fileName, + ); + return await createWorkbookFromLocalExcelFile(absoluteFileName); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.Sheet, + ): Promise> { + const document = await parse(input, { validationChecks: 'all' }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new RowDeleterExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, [block]), + ); + } + + beforeAll(() => { + // Register extensions + useExtension(new TabularLangExtension()); + useExtension(new TestLangExtension()); + // Create language services + const services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid single row deleter', async () => { + const text = readJvTestAsset('valid-single-row-deleter.jv'); + + const testWorkbook = await readTestWorkbook('test-A1:C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(15); + expect(result.right.getHeaderRow()).toEqual(['1', 'Test', 'false']); + expect(result.right.getData()).not.toEqual( + expect.arrayContaining([['0', 'Test', 'true']]), + ); + } + }); + + it('should diagnose no error on valid multiple row deleter', async () => { + const text = readJvTestAsset('valid-multiple-row-deleter.jv'); + + const testWorkbook = await readTestWorkbook('test-A1:C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(14); + expect(result.right.getHeaderRow()).toEqual(['1', 'Test', 'false']); + expect(result.right.getData()).not.toEqual( + expect.arrayContaining([ + ['0', 'Test', 'true'], + ['4', 'Test', 'true'], + ]), + ); + } + }); + + it('should diagnose error on deleting non existing row', async () => { + const text = readJvTestAsset('valid-multiple-row-deleter.jv'); + + const testWorkbook = await readTestWorkbook('test-A1:B2.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'The specified row 5 does not exist in the sheet', + ); + } + }); + + it('should diagnose only one row deletion on duplicate rows', async () => { + const text = readJvTestAsset('valid-duplicate-row.jv'); + + const testWorkbook = await readTestWorkbook('test-A1:C16.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(15); + expect(result.right.getHeaderRow()).toEqual(['1', 'Test', 'false']); + expect(result.right.getData()).not.toEqual( + expect.arrayContaining([['0', 'Test', 'true']]), + ); + } + }); +}); diff --git a/libs/extensions/tabular/exec/test/assets/row-deleter-executor/test-A1:B2.xlsx b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/test-A1:B2.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..3465ea34d220774609e8f2f20a78490c8151e05b GIT binary patch literal 5963 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 iKeZoH>)#zPf&E{DURe(D4p TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-multiple-row-deleter.jv b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-multiple-row-deleter.jv new file mode 100644 index 000000000..9276db92f --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-multiple-row-deleter.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype RowDeleter { + delete: [row 1, row 5]; + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-single-row-deleter.jv b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-single-row-deleter.jv new file mode 100644 index 000000000..df7bad681 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-single-row-deleter.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype RowDeleter { + delete: [row 1]; + } + + block TestLoader oftype TestFileLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} From 7d75217973e3e358cf22f0c4088eb006fe82f27a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 23 Oct 2023 11:41:14 +0200 Subject: [PATCH 09/30] - Moved file-util into @jvalue/jayvee-execution due to required usage in tabular extension tests - Extracted splitLines into string-util in @jvalue/jayvee-execution for the same reason --- .../src => execution/src/lib/util}/file-util.ts | 3 ++- libs/execution/src/lib/util/index.ts | 2 ++ libs/execution/src/lib/util/string-util.ts | 14 ++++++++++++++ .../std/exec/src/archive-interpreter-executor.ts | 7 ++----- .../std/exec/src/http-extractor-executor.ts | 8 +++----- .../std/exec/src/text-file-interpreter-executor.ts | 12 +----------- 6 files changed, 24 insertions(+), 22 deletions(-) rename libs/{extensions/std/exec/src => execution/src/lib/util}/file-util.ts (95%) create mode 100644 libs/execution/src/lib/util/string-util.ts diff --git a/libs/extensions/std/exec/src/file-util.ts b/libs/execution/src/lib/util/file-util.ts similarity index 95% rename from libs/extensions/std/exec/src/file-util.ts rename to libs/execution/src/lib/util/file-util.ts index d82ef000c..23e449997 100644 --- a/libs/extensions/std/exec/src/file-util.ts +++ b/libs/execution/src/lib/util/file-util.ts @@ -2,9 +2,10 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { FileExtension, MimeType } from '@jvalue/jayvee-execution'; import * as mime from 'mime-types'; +import { FileExtension, MimeType } from '../types'; + export function inferMimeTypeFromContentTypeString( fileExtension: string | undefined, ): MimeType | undefined { diff --git a/libs/execution/src/lib/util/index.ts b/libs/execution/src/lib/util/index.ts index 28697b9ea..9e67d5d9d 100644 --- a/libs/execution/src/lib/util/index.ts +++ b/libs/execution/src/lib/util/index.ts @@ -3,3 +3,5 @@ // SPDX-License-Identifier: AGPL-3.0-only export * from './implements-static-decorator'; +export * from './file-util'; +export * from './string-util'; diff --git a/libs/execution/src/lib/util/string-util.ts b/libs/execution/src/lib/util/string-util.ts new file mode 100644 index 000000000..55b7d78b0 --- /dev/null +++ b/libs/execution/src/lib/util/string-util.ts @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export function splitLines(textContent: string, lineBreak: RegExp): string[] { + const lines = textContent.split(lineBreak); + + // There may be an additional empty line due to the previous splitting + if (lines[lines.length - 1] === '') { + lines.splice(lines.length - 1, 1); + } + + return lines; +} diff --git a/libs/extensions/std/exec/src/archive-interpreter-executor.ts b/libs/extensions/std/exec/src/archive-interpreter-executor.ts index bf78fe65d..98569e8c8 100644 --- a/libs/extensions/std/exec/src/archive-interpreter-executor.ts +++ b/libs/extensions/std/exec/src/archive-interpreter-executor.ts @@ -18,15 +18,12 @@ import { MimeType, err, implementsStatic, + inferFileExtensionFromFileExtensionString, + inferMimeTypeFromContentTypeString, } from '@jvalue/jayvee-execution'; import { IOType, PrimitiveValuetypes } from '@jvalue/jayvee-language-server'; import * as JSZip from 'jszip'; -import { - inferFileExtensionFromFileExtensionString, - inferMimeTypeFromContentTypeString, -} from './file-util'; - @implementsStatic() export class ArchiveInterpreterExecutor extends AbstractBlockExecutor< IOType.FILE, diff --git a/libs/extensions/std/exec/src/http-extractor-executor.ts b/libs/extensions/std/exec/src/http-extractor-executor.ts index 03534cfcb..2017c7887 100644 --- a/libs/extensions/std/exec/src/http-extractor-executor.ts +++ b/libs/extensions/std/exec/src/http-extractor-executor.ts @@ -16,16 +16,14 @@ import { MimeType, None, implementsStatic, + inferFileExtensionFromContentTypeString, + inferFileExtensionFromFileExtensionString, + inferMimeTypeFromContentTypeString, } from '@jvalue/jayvee-execution'; import { IOType, PrimitiveValuetypes } from '@jvalue/jayvee-language-server'; import { http, https } from 'follow-redirects'; import { AstNode } from 'langium'; -import { - inferFileExtensionFromContentTypeString, - inferFileExtensionFromFileExtensionString, - inferMimeTypeFromContentTypeString, -} from './file-util'; import { createBackoffStrategy, isBackoffStrategyHandle, diff --git a/libs/extensions/std/exec/src/text-file-interpreter-executor.ts b/libs/extensions/std/exec/src/text-file-interpreter-executor.ts index 3efe2e9c1..e3fb113e2 100644 --- a/libs/extensions/std/exec/src/text-file-interpreter-executor.ts +++ b/libs/extensions/std/exec/src/text-file-interpreter-executor.ts @@ -12,6 +12,7 @@ import { ExecutionContext, TextFile, implementsStatic, + splitLines, } from '@jvalue/jayvee-execution'; import { IOType, PrimitiveValuetypes } from '@jvalue/jayvee-language-server'; @@ -57,14 +58,3 @@ export class TextFileInterpreterExecutor extends AbstractBlockExecutor< return R.ok(new TextFile(file.name, file.extension, file.mimeType, lines)); } } - -function splitLines(textContent: string, lineBreak: RegExp): string[] { - const lines = textContent.split(lineBreak); - - // There may be an additional empty line due to the previous splitting - if (lines[lines.length - 1] === '') { - lines.splice(lines.length - 1, 1); - } - - return lines; -} From 40b708a9d5dd67a01270f5e759672504445ffe71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 23 Oct 2023 11:44:03 +0200 Subject: [PATCH 10/30] - Added new file-util in @jvalue/jayvee-execution/test containing the functions to create BinaryFile and TextFile from local files - Renamed utils.ts into test-infrastructure-util.ts and moved into utils subdirectory --- libs/execution/test/utils/file-util.ts | 44 +++++++++++++++++++ libs/execution/test/utils/index.ts | 6 +++ .../test-infrastructure-util.ts} | 2 +- 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 libs/execution/test/utils/file-util.ts create mode 100644 libs/execution/test/utils/index.ts rename libs/execution/test/{utils.ts => utils/test-infrastructure-util.ts} (98%) diff --git a/libs/execution/test/utils/file-util.ts b/libs/execution/test/utils/file-util.ts new file mode 100644 index 000000000..35ace2292 --- /dev/null +++ b/libs/execution/test/utils/file-util.ts @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { readFileSync } from 'fs'; +import * as path from 'path'; + +import { + BinaryFile, + FileExtension, + MimeType, + TextFile, + inferFileExtensionFromFileExtensionString, + inferMimeTypeFromContentTypeString, + splitLines, +} from '../../src'; + +export function createBinaryFileFromLocalFile(fileName: string): BinaryFile { + const extName = path.extname(fileName); + const mimeType = + inferMimeTypeFromContentTypeString(extName) || + MimeType.APPLICATION_OCTET_STREAM; + const fileExtension = + inferFileExtensionFromFileExtensionString(extName) || FileExtension.NONE; + const file = readFileSync(path.resolve(__dirname, fileName)); + return new BinaryFile(path.basename(fileName), fileExtension, mimeType, file); +} + +export function createTextFileFromLocalFile(fileName: string): TextFile { + const extName = path.extname(fileName); + const mimeType = + inferMimeTypeFromContentTypeString(extName) || + MimeType.APPLICATION_OCTET_STREAM; + const fileExtension = + inferFileExtensionFromFileExtensionString(extName) || FileExtension.NONE; + const fileContent = readFileSync(path.resolve(__dirname, fileName), 'utf-8'); + + return new TextFile( + path.basename(fileName), + fileExtension, + mimeType, + splitLines(fileContent, /\r?\n/), + ); +} diff --git a/libs/execution/test/utils/index.ts b/libs/execution/test/utils/index.ts new file mode 100644 index 000000000..144030b35 --- /dev/null +++ b/libs/execution/test/utils/index.ts @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +export * from './test-infrastructure-util'; +export * from './file-util'; diff --git a/libs/execution/test/utils.ts b/libs/execution/test/utils/test-infrastructure-util.ts similarity index 98% rename from libs/execution/test/utils.ts rename to libs/execution/test/utils/test-infrastructure-util.ts index 639c4239e..ff641eaf1 100644 --- a/libs/execution/test/utils.ts +++ b/libs/execution/test/utils/test-infrastructure-util.ts @@ -17,7 +17,7 @@ import { StackNode, blockExecutorRegistry, constraintExecutorRegistry, -} from '../src'; +} from '../../src'; export function clearBlockExecutorRegistry() { blockExecutorRegistry.clear(); From c8f2695ce50b1b43eab0d4e340473cf07dd7e653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 23 Oct 2023 12:16:23 +0200 Subject: [PATCH 11/30] - Moved jsdom-environment-fix.ts from ./src into ./test - Added same fix for setImmediate which is used inside 'fast-csv' (used inside csv-interpreter) --- libs/extensions/tabular/exec/jest.config.ts | 2 +- .../tabular/exec/src/jsdom-environment-fix.ts | 14 -------------- .../exec/test/jsdom-environment-fix.ts | 19 +++++++++++++++++++ 3 files changed, 20 insertions(+), 15 deletions(-) delete mode 100644 libs/extensions/tabular/exec/src/jsdom-environment-fix.ts create mode 100644 libs/extensions/tabular/exec/test/jsdom-environment-fix.ts diff --git a/libs/extensions/tabular/exec/jest.config.ts b/libs/extensions/tabular/exec/jest.config.ts index 9844a26bb..a5a6a3c7d 100644 --- a/libs/extensions/tabular/exec/jest.config.ts +++ b/libs/extensions/tabular/exec/jest.config.ts @@ -5,7 +5,7 @@ export default { displayName: 'extensions-tabular-exec', preset: '../../../../jest.preset.js', - testEnvironment: './src/jsdom-environment-fix.ts', + testEnvironment: './test/jsdom-environment-fix.ts', globals: { 'ts-jest': { tsconfig: '/tsconfig.spec.json', diff --git a/libs/extensions/tabular/exec/src/jsdom-environment-fix.ts b/libs/extensions/tabular/exec/src/jsdom-environment-fix.ts deleted file mode 100644 index c18ba7718..000000000 --- a/libs/extensions/tabular/exec/src/jsdom-environment-fix.ts +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg -// -// SPDX-License-Identifier: AGPL-3.0-only - -import JSDOMEnvironment from 'jest-environment-jsdom'; - -// https://github.com/jsdom/jsdom/issues/3363 -export default class FixJSDOMEnvironment extends JSDOMEnvironment { - constructor(...args: ConstructorParameters) { - super(...args); - - this.global.structuredClone = structuredClone; - } -} diff --git a/libs/extensions/tabular/exec/test/jsdom-environment-fix.ts b/libs/extensions/tabular/exec/test/jsdom-environment-fix.ts new file mode 100644 index 000000000..d5af83010 --- /dev/null +++ b/libs/extensions/tabular/exec/test/jsdom-environment-fix.ts @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import JSDOMEnvironment from 'jest-environment-jsdom'; + +// @nrwl/jest/preset loads in jsdom environment which does not support a few node.js functions. +// This class adds these node.js functions. +export default class JSDOMEnvironmentFix extends JSDOMEnvironment { + constructor(...args: ConstructorParameters) { + super(...args); + + // https://github.com/jsdom/jsdom/issues/3363 + this.global.structuredClone = structuredClone; + // https://github.com/jestjs/jest/issues/11204 + // https://github.com/jestjs/jest/issues/12622 + this.global.setImmediate = setImmediate; + } +} From 0550e12f3d74b47857d074865360f092b0dd6630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 23 Oct 2023 12:29:42 +0200 Subject: [PATCH 12/30] Fixed extractor and loader types --- .../test/assets/cell-range-selector-executor/valid-A1:A4.jv | 4 ++-- .../test/assets/cell-range-selector-executor/valid-A1:C*.jv | 4 ++-- .../test/assets/cell-range-selector-executor/valid-A1:E4.jv | 4 ++-- .../assets/cell-writer-executor/valid-cell-range-writer.jv | 4 ++-- .../assets/cell-writer-executor/valid-single-cell-writer.jv | 4 ++-- .../assets/column-deleter-executor/valid-duplicate-column.jv | 4 ++-- .../column-deleter-executor/valid-multiple-column-deleter.jv | 4 ++-- .../column-deleter-executor/valid-single-column-deleter.jv | 4 ++-- .../test/assets/row-deleter-executor/valid-duplicate-row.jv | 4 ++-- .../assets/row-deleter-executor/valid-multiple-row-deleter.jv | 4 ++-- .../assets/row-deleter-executor/valid-single-row-deleter.jv | 4 ++-- 11 files changed, 22 insertions(+), 22 deletions(-) diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:A4.jv b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:A4.jv index 666e6e2ef..4fa94a1c9 100644 --- a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:A4.jv +++ b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:A4.jv @@ -4,14 +4,14 @@ pipeline TestPipeline { - block TestExtractor oftype TestFileExtractor { + block TestExtractor oftype TestSheetExtractor { } block TestBlock oftype CellRangeSelector { select: range A1:A4; } - block TestLoader oftype TestFileLoader { + block TestLoader oftype TestSheetLoader { } TestExtractor -> TestBlock -> TestLoader; diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:C*.jv b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:C*.jv index 9f6c914be..0ed56a14a 100644 --- a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:C*.jv +++ b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:C*.jv @@ -4,14 +4,14 @@ pipeline TestPipeline { - block TestExtractor oftype TestFileExtractor { + block TestExtractor oftype TestSheetExtractor { } block TestBlock oftype CellRangeSelector { select: range A1:C*; } - block TestLoader oftype TestFileLoader { + block TestLoader oftype TestSheetLoader { } TestExtractor -> TestBlock -> TestLoader; diff --git a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:E4.jv b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:E4.jv index 605210d2d..e004eb928 100644 --- a/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:E4.jv +++ b/libs/extensions/tabular/exec/test/assets/cell-range-selector-executor/valid-A1:E4.jv @@ -4,14 +4,14 @@ pipeline TestPipeline { - block TestExtractor oftype TestFileExtractor { + block TestExtractor oftype TestSheetExtractor { } block TestBlock oftype CellRangeSelector { select: range A1:E4; } - block TestLoader oftype TestFileLoader { + block TestLoader oftype TestSheetLoader { } TestExtractor -> TestBlock -> TestLoader; diff --git a/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-cell-range-writer.jv b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-cell-range-writer.jv index 4ce5bece6..327d9dbaa 100644 --- a/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-cell-range-writer.jv +++ b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-cell-range-writer.jv @@ -4,7 +4,7 @@ pipeline TestPipeline { - block TestExtractor oftype TestFileExtractor { + block TestExtractor oftype TestSheetExtractor { } block TestBlock oftype CellWriter { @@ -12,7 +12,7 @@ pipeline TestPipeline { at: range A1:C1; } - block TestLoader oftype TestFileLoader { + block TestLoader oftype TestSheetLoader { } TestExtractor -> TestBlock -> TestLoader; diff --git a/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-single-cell-writer.jv b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-single-cell-writer.jv index a7390b52e..844a8fd88 100644 --- a/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-single-cell-writer.jv +++ b/libs/extensions/tabular/exec/test/assets/cell-writer-executor/valid-single-cell-writer.jv @@ -4,7 +4,7 @@ pipeline TestPipeline { - block TestExtractor oftype TestFileExtractor { + block TestExtractor oftype TestSheetExtractor { } block TestBlock oftype CellWriter { @@ -12,7 +12,7 @@ pipeline TestPipeline { at: cell A1; } - block TestLoader oftype TestFileLoader { + block TestLoader oftype TestSheetLoader { } TestExtractor -> TestBlock -> TestLoader; diff --git a/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-duplicate-column.jv b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-duplicate-column.jv index 270394398..b3b6a6ae4 100644 --- a/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-duplicate-column.jv +++ b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-duplicate-column.jv @@ -4,14 +4,14 @@ pipeline TestPipeline { - block TestExtractor oftype TestFileExtractor { + block TestExtractor oftype TestSheetExtractor { } block TestBlock oftype ColumnDeleter { delete: [column A, column A]; } - block TestLoader oftype TestFileLoader { + block TestLoader oftype TestSheetLoader { } TestExtractor -> TestBlock -> TestLoader; diff --git a/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-multiple-column-deleter.jv b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-multiple-column-deleter.jv index c129b7639..6a2aa56d2 100644 --- a/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-multiple-column-deleter.jv +++ b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-multiple-column-deleter.jv @@ -4,14 +4,14 @@ pipeline TestPipeline { - block TestExtractor oftype TestFileExtractor { + block TestExtractor oftype TestSheetExtractor { } block TestBlock oftype ColumnDeleter { delete: [column A, column C]; } - block TestLoader oftype TestFileLoader { + block TestLoader oftype TestSheetLoader { } TestExtractor -> TestBlock -> TestLoader; diff --git a/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-single-column-deleter.jv b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-single-column-deleter.jv index 0f6f33474..ad5f6733b 100644 --- a/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-single-column-deleter.jv +++ b/libs/extensions/tabular/exec/test/assets/column-deleter-executor/valid-single-column-deleter.jv @@ -4,14 +4,14 @@ pipeline TestPipeline { - block TestExtractor oftype TestFileExtractor { + block TestExtractor oftype TestSheetExtractor { } block TestBlock oftype ColumnDeleter { delete: [column A]; } - block TestLoader oftype TestFileLoader { + block TestLoader oftype TestSheetLoader { } TestExtractor -> TestBlock -> TestLoader; diff --git a/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-duplicate-row.jv b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-duplicate-row.jv index 5d2e70f93..cbb08f0f9 100644 --- a/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-duplicate-row.jv +++ b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-duplicate-row.jv @@ -4,14 +4,14 @@ pipeline TestPipeline { - block TestExtractor oftype TestFileExtractor { + block TestExtractor oftype TestSheetExtractor { } block TestBlock oftype RowDeleter { delete: [row 1, row 1]; } - block TestLoader oftype TestFileLoader { + block TestLoader oftype TestSheetLoader { } TestExtractor -> TestBlock -> TestLoader; diff --git a/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-multiple-row-deleter.jv b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-multiple-row-deleter.jv index 9276db92f..d1384e505 100644 --- a/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-multiple-row-deleter.jv +++ b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-multiple-row-deleter.jv @@ -4,14 +4,14 @@ pipeline TestPipeline { - block TestExtractor oftype TestFileExtractor { + block TestExtractor oftype TestSheetExtractor { } block TestBlock oftype RowDeleter { delete: [row 1, row 5]; } - block TestLoader oftype TestFileLoader { + block TestLoader oftype TestSheetLoader { } TestExtractor -> TestBlock -> TestLoader; diff --git a/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-single-row-deleter.jv b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-single-row-deleter.jv index df7bad681..cbb6610ff 100644 --- a/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-single-row-deleter.jv +++ b/libs/extensions/tabular/exec/test/assets/row-deleter-executor/valid-single-row-deleter.jv @@ -4,14 +4,14 @@ pipeline TestPipeline { - block TestExtractor oftype TestFileExtractor { + block TestExtractor oftype TestSheetExtractor { } block TestBlock oftype RowDeleter { delete: [row 1]; } - block TestLoader oftype TestFileLoader { + block TestLoader oftype TestSheetLoader { } TestExtractor -> TestBlock -> TestLoader; From a75aeffd488f5a55b78ecc2d81f8f9a1842ae175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 23 Oct 2023 12:30:16 +0200 Subject: [PATCH 13/30] Added test for csv-interpreter --- .../src/lib/csv-interpreter-executor.spec.ts | 99 +++++++++++++++++++ .../valid-csv-interpreter.jv | 17 ++++ .../csv-interpreter-executor/valid-csv.csv | 2 + 3 files changed, 118 insertions(+) create mode 100644 libs/extensions/tabular/exec/src/lib/csv-interpreter-executor.spec.ts create mode 100644 libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv-interpreter.jv create mode 100644 libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv.csv diff --git a/libs/extensions/tabular/exec/src/lib/csv-interpreter-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/csv-interpreter-executor.spec.ts new file mode 100644 index 000000000..8405f395f --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/csv-interpreter-executor.spec.ts @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as path from 'path'; + +import * as R from '@jvalue/jayvee-execution'; +import { + createTextFileFromLocalFile, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; +import { TabularLangExtension } from '@jvalue/jayvee-extensions/tabular/lang'; +import { + BlockDefinition, + IOType, + createJayveeServices, + useExtension, +} from '@jvalue/jayvee-language-server'; +import { + ParseHelperOptions, + TestLangExtension, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { CSVInterpreterExecutor } from './csv-interpreter-executor'; + +describe('Validation of CSVInterpreterExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise>; + + let locator: AstNodeLocator; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/csv-interpreter-executor/', + ); + + function readTestFile(fileName: string): R.TextFile { + const absoluteFileName = path.resolve( + __dirname, + '../../test/assets/csv-interpreter-executor/', + fileName, + ); + return createTextFileFromLocalFile(absoluteFileName); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.TextFile, + ): Promise> { + const document = await parse(input, { validationChecks: 'all' }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new CSVInterpreterExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, [block]), + ); + } + + beforeAll(() => { + // Register extensions + useExtension(new TabularLangExtension()); + useExtension(new TestLangExtension()); + // Create language services + const services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid csv file', async () => { + const text = readJvTestAsset('valid-csv-interpreter.jv'); + + const testCsv = readTestFile('valid-csv.csv'); + const result = await parseAndExecuteExecutor(text, testCsv); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(2); + expect(result.right.getNumberOfRows()).toEqual(2); + expect(result.right.getData()).toEqual([ + ['Test', 'true'], + ['Test', 'false'], + ]); + } + }); +}); diff --git a/libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv-interpreter.jv b/libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv-interpreter.jv new file mode 100644 index 000000000..856c67eb0 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv-interpreter.jv @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestTextFileExtractor { + } + + block TestBlock oftype CSVInterpreter { + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv.csv b/libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv.csv new file mode 100644 index 000000000..086eb44a1 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv.csv @@ -0,0 +1,2 @@ +Test,true +Test,false From ea613de4546456c0b2a0723e456be45e6cf667a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 23 Oct 2023 12:39:17 +0200 Subject: [PATCH 14/30] Added tests for sheet-picker-executor --- .../src/lib/sheet-picker-executor.spec.ts | 116 ++++++++++++++++++ .../sheet-picker-executor/test-A1:C16.xlsx | Bin 0 -> 5904 bytes .../valid-custom-sheet-name.jv | 18 +++ .../valid-sheet-picker.jv | 18 +++ 4 files changed, 152 insertions(+) create mode 100644 libs/extensions/tabular/exec/src/lib/sheet-picker-executor.spec.ts create mode 100644 libs/extensions/tabular/exec/test/assets/sheet-picker-executor/test-A1:C16.xlsx create mode 100644 libs/extensions/tabular/exec/test/assets/sheet-picker-executor/valid-custom-sheet-name.jv create mode 100644 libs/extensions/tabular/exec/test/assets/sheet-picker-executor/valid-sheet-picker.jv diff --git a/libs/extensions/tabular/exec/src/lib/sheet-picker-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/sheet-picker-executor.spec.ts new file mode 100644 index 000000000..9dfba7ea0 --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/sheet-picker-executor.spec.ts @@ -0,0 +1,116 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as path from 'path'; + +import * as R from '@jvalue/jayvee-execution'; +import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { TabularLangExtension } from '@jvalue/jayvee-extensions/tabular/lang'; +import { + BlockDefinition, + IOType, + createJayveeServices, + useExtension, +} from '@jvalue/jayvee-language-server'; +import { + ParseHelperOptions, + TestLangExtension, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { createWorkbookFromLocalExcelFile } from '../../test/util'; + +import { SheetPickerExecutor } from './sheet-picker-executor'; + +describe('Validation of SheetPickerExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise>; + + let locator: AstNodeLocator; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/sheet-picker-executor/', + ); + + async function readTestWorkbook(fileName: string): Promise { + const absoluteFileName = path.resolve( + __dirname, + '../../test/assets/sheet-picker-executor/', + fileName, + ); + return await createWorkbookFromLocalExcelFile(absoluteFileName); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.Workbook, + ): Promise> { + const document = await parse(input, { validationChecks: 'all' }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new SheetPickerExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, [block]), + ); + } + + beforeAll(() => { + // Register extensions + useExtension(new TabularLangExtension()); + useExtension(new TestLangExtension()); + // Create language services + const services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid workbook', async () => { + const text = readJvTestAsset('valid-sheet-picker.jv'); + + const testWorkbook = await readTestWorkbook('test-A1:C16.xlsx'); + const result = await parseAndExecuteExecutor(text, testWorkbook); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.SHEET); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getHeaderRow()).toEqual(['0', 'Test', 'true']); + expect(result.right.getData()).toEqual( + expect.arrayContaining([ + ['0', 'Test', 'true'], + ['1', 'Test', 'false'], + ['15', 'Test', 'true'], + ]), + ); + } + }); + + it('should diagnose error on sheet not found', async () => { + const text = readJvTestAsset('valid-custom-sheet-name.jv'); + + const testWorkbook = await readTestWorkbook('test-A1:C16.xlsx'); + const result = await parseAndExecuteExecutor(text, testWorkbook); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'Workbook does not contain a sheet named MyCustomSheet', + ); + } + }); +}); diff --git a/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/test-A1:C16.xlsx b/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/test-A1:C16.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..0544fa2814c07cf9c2736a400489785567ac1b4d GIT binary patch literal 5904 zcmaJ_WmJ^y)}~`dx{>ZqDQN_yK|-WMha4Cfy1To(q?GOs2?6OwK)OSalo0s9_ghDf z=N$K}_00UZ_kQNS_O(-44jus)1_cELCV^K&1LlDM?|$n;L10^Uw)VU$D_al zcR-HoYPCP-^EAD_tU9hnAG@=#vr^xFKrC+<0=MW5oW@q)$}+`j5Q7o${2Q0U1{_NAyszj z9W2+FxyIJ$Z9({1U_L)F;b?=IV1TVoO;!HX~?qCE8ADNa0TBe{$ILc2^^0Z*EQW{2Ab>XhVW%Bo^NP7mS| zVrLuRH$i1NWE6`w2ZY7DTT8&fz$pJuT!`;ZIDy&!fyNO6wJ?G}EZCeZ!T-RdB7O%G zX7eVV(Lv2#uAd6aCN`iUe;eMfxmF%5W6EcFbXBNeN`jJ&oY^&CZW3VFKWVe|iT?Rc zv#Ro20TjFL>2wP`x4nf7Jp=W@KGw68ae_)+if9KC#`1w>by+TaWQAmmt+Cugn~^jF zFlfZMOgrvaNWYJN%4_GjD%c#+MwNiVp}^~Q^h{x#NXONdaO98__}PpgHYS0N$Q;L`mIDpKM*7;=ZaH6msf`I! zZ};r&*(&gO1->R14V~iCy|&1N)zVIfFO<>JA(4@5$447#atA2gfD$A82>1=}K6}au zHseLI@zFGA{064_34oCUQoq%!gM<>^EBW1)7Z-iq#S&orR>(2H+^VM$Rw`O0HHA9a zcyq2Bh8>LPRdYfwj(NdD5ORtFJQgKvWRXIDoK9}8{Kc0riX^zDMDopyxh4Qr$^97_ zBxSgnZWc9B;8>%qx{a89_2OnE_vEQDZ6f~rQQ12E%7Z9s`>wpVdhJ-)x_oMsI4Vod zPFKhY&t*tv^(9A$zr-(|!LWGKPye#g9`%x@1x zD4f=uh`P@~D!cUT86d~4xJ0~XP6SvG^BNWg#u5H+5r*>*bL>n(mLT?DPtN=Jnvd6o z%u(P59Q3wymU5jY@mwZ?-tYxC7=p)6IvJc}=%y!&x(1i~A zy={%k4m_m2^^$m-)leg>=mq?Zz2!+3;@U8sNE8# zlZybu^~7kLR>v+s|g!lD>3dHW2VEjfr?vGs=9%7hVX zn8S%9>)JPmUV;3^!nfyri|FV6Ij%Q#8Xmk$+fW&H$1$G{YmpA6oFIEA3_PEr)vfR< zZg=rY6*tmPCRHf-fkb)6E39$BG=?BFP)NJHJ zNu}8o*`&fuVP`pZLo^|1%t)+*3Bv8G+_S!15atgT+_c-YYz;021@&}_v;Ie**l%9I ziaPrC{czD-Mnok?t1)D+J25$$=RTe*_%3pkSL!;Mm1?@zumEi%E5T@340)jDCdlTdwQe3%-+gOq%)>x-x+Ct$yvC1?qjxtO-ZEU!k}8vOZ(i$P&(}Co9R=M0F=P& z;Y9F|8Z1@C8k1KnK3uIpWDPF_;H}`uWW>5oz44j~kZ$7pJdb(rD=r+peHSpw+iaR{@IIf*wfi(>R8!X)MkBTW#NmX@_n zUUe7i*8b2)QFoFAgQWt|v%8}aaUC(kr!W)OQpvm1A5XQa%nIulB@3|Qa}GTMm3bQX z!=ozn<`mYkz!aD`ae#;|d$NxRqi~)0mlXTX@Bt#9g2#esWw^(gq6O)KReZJ;J2~?# zH)?bGUnGq&SG{bd+ns58y3OR=SnkX-ay4vP*2JqSEat4(-@qXgr?W!rr0;){MMTSJ za{hA39~bbVaCQ@q;%rv$0a7==V5m`LzW>!z~O~`nW{g+iz zp`NCXTw@1Wc_gO{XYW9k`DDcq_|D9G(ic9?-D|ufy$bH%NKg2?B-@&TKz6q5zrKED z*OsJ3+f;tcp2dSQ1E;O=Ns6C>HS?bZ5-m)|XmARqlU-xQ2^A3D#gxbXJgvhPm8TR= z8bWin9+2JJH_@nV+uK>S9Vn*8iaB~~nG^ih7TY;{bLn*LVW_RLO!F~}{;g#j zrkUs0pS4eI4r!<*;tF*=9Wv>E_?a!! zuP^W3PKz^^S%4)FgEq^0S6L@GC`|V`Dt!t-zrHY#N=p z#4V#xW0%mGLq1x1O;^b0pv+Rr_Ao=uOYZJA1T-sCiI0VnuL#M{&H88P8IaP(FFQmL z3SSW;2unt-prNX-t1MqfaI7$6PaHJ$|dz z!zT(&Qr7x$4w`AVE?g1)9Hy&1?Q?EE=JaKvoG#H_Oqol4r91riX9u@xf|0ss^;w#z zS?t;F4jU0bcM*3t(lFZJ(y*LNCSwpUuGzTvpk~c+Jf^6bTyfIx@~To`dSgVaE>ug5 zv~^TWDvF3$p-1&ey7PFU%1EM;qy}k13zplj8&cA6p4= ze62^^Cjqa`3h^dbHIyMOJ^}bF z?@L}Qt5#-N4f@Q!TQ@1NGoAFAtwiLp2kzjry|06cKa(Svm|}a+sHa@#BHpqNB-U!n zs`V0AK=f^-ePnKtM3=4VdMSsfFrjPSt@TQ>JfkO_o;L6}F4t@ZOMQryifxNo3D;OV zhJnf)qv{0}?&%y*QRMi-=aaXDfw3{;zG-WcQYdWyh05W?qele`&6mDA2Ms>Zwi@hY z*r6Hm-iAV5!O3Puxf0w0$-OOR&OWgzIwoUub#&U0esh4K_Uxy04jVWRSu*OFudu9k zI?7%=z8y8`&}@W!z!>O@XQR9i1WSF=v>$*_su1ng@gUj|nDEWM z$rvbep5Un%xN=>s{aypzNwd}*jOV+ByU&~F$gNvP2rw|I_bdW7%pvjX<*J~=`){{ zZU)E3V;@24(9xAJ4{=ek&)heN4jyalDv2)psP{K6A2l{Azq=2~T^s(thg__?6K^4J zl%Wu7TlO~)DCoXL3Rme@yq|54@uClck73lLWSGRagJq?0z3YiwQ;D&AD(lMHdSp`K zBrv2!_J+bt99xF+uNseP4T`~Piti|B$QTw-KJ=wEvuxh@IpHT`7n|d*t-&P7hZcqH z#_=I#$mg_YA?ylJgH%gxE1yp=<|UM68}nKN)_c|8msCGSSk)fMNA-Ocl7GT;aY-dD1l74vf~OmSl?hHG)-Rt%<+woB8NPmV zXlL8bExiqDWhKjOdg=aQwqQjpWbcb~9~uY3vVdksnL*hLGao$H!W1#ArWSCHS=z6a6KNmoHm@Y#aV%Y--kU} zT4C?~Q!!qQZFuLKxxWtAm=FD9Xl;G(p7baM$S*ct;i-4k$b`NQR3@%An?*Gfv@@+^ zlGEvpW2a?gy7hp4vLuwbH$C;{DsT7m)(Q+IlZm$4Dge*GKelgQ>tv=SUQVs6t7L6f z({vPzN~;R_5-Tk*3b%_22Mit+p>3n02@xhFv$2}{N~FUfw9d5`^cabDX)<`!OX01a zI+{15cRYohC*nJxjlJ}(zbw7tk5874C&@jg$lVqhUN50?f{FF}aj><$M>Sc)3l_9a zeG%p1m+0R;^u#l)eK+efEaONEZY*X_2H({`-Z}<~Qec}E&h)>pX0ednH>5v0GvX;C zw~1dB`d;u7Y8!w3E0AC2l!HGM4nyOwz9hQ^&F`=8ryP3 zbM+|kDSA|fQhzmdaXmVWZ5|>t*NeAiA8Ef7>4=*j45H;lOpkCSn!y8@^J-a-&&`?5 z2{S7JTc9>isfJf%aZ2t>6rDfOWqB>!plu&_(MTE08lAE&e1@o!)9DJXn5_=~qV^sX7i~6hIj?VU^%AVc}Rdhl$)+O|FqIguf*7 zeEnJ&ncg+%2{J;uKJU^HDPPQu;RM!_sgUCE)qGdL?FfRovAB?HC~$qx_shn@Cj8-> zqVP`jx64jA`fV;^1xUdkT^eqIVBF}-wF%6GP?$s2ro{33S<)S3+T>6y@>b8 z2Nn(&=J)RL;r8u*_xSJfU=#VL;=^^+{c`TN5Z=A&f3NEPwDWK|algF!En;`EcxUI~ zD(g?>hg0W0%YKW?U3s}v{&So0r-6qZ{QYqHTNaT1sXqT2RsU3dSj6sI{NFN;`maC! z9}WMXRvs3H`-1mdfOly8xAOO=g@>u@zJUCe>pKhoQBM9;e;C5|`R%u)-&y;Y`u}IU iKeZoH>)#zPf&E{DURe(D4p TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/valid-sheet-picker.jv b/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/valid-sheet-picker.jv new file mode 100644 index 000000000..40654c112 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/valid-sheet-picker.jv @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestWorkbookExtractor { + } + + block TestBlock oftype SheetPicker { + sheetName: "Sheet1"; + } + + block TestLoader oftype TestSheetLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} From affff535f8fc63706cccf197f82f7e0834907d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 23 Oct 2023 12:47:50 +0200 Subject: [PATCH 15/30] Added missing license file --- .../test/assets/sheet-picker-executor/test-A1:C16.xlsx.license | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 libs/extensions/tabular/exec/test/assets/sheet-picker-executor/test-A1:C16.xlsx.license diff --git a/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/test-A1:C16.xlsx.license b/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/test-A1:C16.xlsx.license new file mode 100644 index 000000000..17c5d2bad --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/sheet-picker-executor/test-A1:C16.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only From 6a59114a7756aea0fe9d5e41c7bb9ce436259110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 30 Oct 2023 09:01:09 +0100 Subject: [PATCH 16/30] Added tests for TableInterpreterExecutor --- .../lib/table-interpreter-executor.spec.ts | 265 ++++++++++++++++++ .../test-empty.xlsx | Bin 0 -> 5410 bytes .../test-empty.xlsx.license | 3 + .../test-with-header.xlsx | Bin 0 -> 6275 bytes .../test-with-header.xlsx.license | 3 + .../test-without-header.xlsx | Bin 0 -> 6234 bytes .../test-without-header.xlsx.license | 3 + .../valid-with-capitalized-header.jv | 23 ++ .../valid-with-header.jv | 23 ++ .../valid-without-header.jv | 23 ++ .../valid-wrong-valuetype-with-header.jv | 23 ++ .../valid-wrong-valuetype-without-header.jv | 23 ++ 12 files changed, 389 insertions(+) create mode 100644 libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts create mode 100644 libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-empty.xlsx create mode 100644 libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-empty.xlsx.license create mode 100644 libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-with-header.xlsx create mode 100644 libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-with-header.xlsx.license create mode 100644 libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-without-header.xlsx create mode 100644 libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-without-header.xlsx.license create mode 100644 libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-with-capitalized-header.jv create mode 100644 libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-with-header.jv create mode 100644 libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-without-header.jv create mode 100644 libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-wrong-valuetype-with-header.jv create mode 100644 libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-wrong-valuetype-without-header.jv diff --git a/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts new file mode 100644 index 000000000..4f543a5d0 --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts @@ -0,0 +1,265 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as path from 'path'; + +import * as R from '@jvalue/jayvee-execution'; +import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { TabularLangExtension } from '@jvalue/jayvee-extensions/tabular/lang'; +import { + BlockDefinition, + IOType, + createJayveeServices, + useExtension, +} from '@jvalue/jayvee-language-server'; +import { + ParseHelperOptions, + TestLangExtension, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { createWorkbookFromLocalExcelFile } from '../../test/util'; + +import { TableInterpreterExecutor } from './table-interpreter-executor'; + +describe('Validation of TableInterpreterExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise>; + + let locator: AstNodeLocator; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/table-interpreter-executor/', + ); + + async function readTestWorkbook(fileName: string): Promise { + const absoluteFileName = path.resolve( + __dirname, + '../../test/assets/table-interpreter-executor/', + fileName, + ); + return await createWorkbookFromLocalExcelFile(absoluteFileName); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.Sheet, + ): Promise> { + const document = await parse(input, { validationChecks: 'all' }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new TableInterpreterExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, [block]), + ); + } + + beforeAll(() => { + // Register extensions + useExtension(new TabularLangExtension()); + useExtension(new TestLangExtension()); + // Create language services + const services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + describe('validation of sheet with header', () => { + it('should diagnose no error on valid sheet', async () => { + const text = readJvTestAsset('valid-with-header.jv'); + + const testWorkbook = await readTestWorkbook('test-with-header.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getColumn('index')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining([0, 1, 2, 15]) as number[], + }), + ); + expect(result.right.getColumn('name')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining(['Test']) as string[], + }), + ); + expect(result.right.getColumn('flag')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining([true, false]) as boolean[], + }), + ); + } + }); + + it('should diagnose empty table on wrong header case', async () => { + const text = readJvTestAsset('valid-with-capitalized-header.jv'); + + const testWorkbook = await readTestWorkbook('test-with-header.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(0); + expect(result.right.getNumberOfRows()).toEqual(16); + } + }); + + it('should diagnose error on empty sheet', async () => { + const text = readJvTestAsset('valid-with-header.jv'); + + const testWorkbook = await readTestWorkbook('test-empty.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'The input sheet is empty and thus has no header', + ); + } + }); + + it('should diagnose skipping row on wrong cell valuetype', async () => { + const text = readJvTestAsset('valid-wrong-valuetype-with-header.jv'); + + const testWorkbook = await readTestWorkbook('test-with-header.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(0); + } + }); + }); + + describe('validation of sheet without header', () => { + it('should diagnose no error on valid sheet', async () => { + const text = readJvTestAsset('valid-without-header.jv'); + + const testWorkbook = await readTestWorkbook('test-without-header.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getColumn('index')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining([0, 1, 2, 15]) as number[], + }), + ); + expect(result.right.getColumn('name')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining(['Test']) as string[], + }), + ); + expect(result.right.getColumn('flag')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining([true, false]) as boolean[], + }), + ); + } + }); + + it('should diagnose no error on valid sheet with header', async () => { + const text = readJvTestAsset('valid-without-header.jv'); + + const testWorkbook = await readTestWorkbook('test-with-header.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getColumn('index')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining([0, 1, 2, 15]) as number[], + }), + ); + expect(result.right.getColumn('name')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining(['Test']) as string[], + }), + ); + expect(result.right.getColumn('flag')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining([true, false]) as boolean[], + }), + ); + } + }); + + it('should diagnose error on empty sheet', async () => { + const text = readJvTestAsset('valid-without-header.jv'); + + const testWorkbook = await readTestWorkbook('test-empty.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'There are 3 column definitions but the input sheet only has 0 columns', + ); + } + }); + + it('should diagnose skipping row on wrong cell valuetype', async () => { + const text = readJvTestAsset('valid-wrong-valuetype-without-header.jv'); + + const testWorkbook = await readTestWorkbook('test-without-header.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(0); + } + }); + }); +}); diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-empty.xlsx b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-empty.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..af7b184be1b847220d6c83fb152e1f4618a6c4d3 GIT binary patch literal 5410 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 literal 0 HcmV?d00001 diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-empty.xlsx.license b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-empty.xlsx.license new file mode 100644 index 000000000..17c5d2bad --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-empty.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-with-header.xlsx b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-with-header.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..9417e87a43e2556b5597c0f2b2e20557b922f2dd GIT binary patch literal 6275 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 literal 0 HcmV?d00001 diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-without-header.xlsx.license b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-without-header.xlsx.license new file mode 100644 index 000000000..17c5d2bad --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/test-without-header.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-with-capitalized-header.jv b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-with-capitalized-header.jv new file mode 100644 index 000000000..3d9929495 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-with-capitalized-header.jv @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype TableInterpreter { + header: true; + columns: [ + "Index" oftype integer, + "Name" oftype text, + "Flag" oftype boolean + ]; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-with-header.jv b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-with-header.jv new file mode 100644 index 000000000..cd368186b --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-with-header.jv @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype TableInterpreter { + header: true; + columns: [ + "index" oftype integer, + "name" oftype text, + "flag" oftype boolean + ]; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-without-header.jv b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-without-header.jv new file mode 100644 index 000000000..6fa8f5c1d --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-without-header.jv @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype TableInterpreter { + header: false; + columns: [ + "index" oftype integer, + "name" oftype text, + "flag" oftype boolean + ]; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-wrong-valuetype-with-header.jv b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-wrong-valuetype-with-header.jv new file mode 100644 index 000000000..f8ec03446 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-wrong-valuetype-with-header.jv @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype TableInterpreter { + header: true; + columns: [ + "index" oftype integer, + "name" oftype text, + "flag" oftype integer + ]; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-wrong-valuetype-without-header.jv b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-wrong-valuetype-without-header.jv new file mode 100644 index 000000000..ebc269171 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-wrong-valuetype-without-header.jv @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype TableInterpreter { + header: false; + columns: [ + "index" oftype integer, + "name" oftype text, + "flag" oftype integer + ]; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} From 031a4d39289fcbebd4bcf6138d9eb3db4d244c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 30 Oct 2023 09:01:51 +0100 Subject: [PATCH 17/30] Added missing license file --- .../test/assets/csv-interpreter-executor/valid-csv.csv.license | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv.csv.license diff --git a/libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv.csv.license b/libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv.csv.license new file mode 100644 index 000000000..17c5d2bad --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/csv-interpreter-executor/valid-csv.csv.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only From 11257eb9601921bd06c9e50d89c39fdc023ac817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 30 Oct 2023 11:02:08 +0100 Subject: [PATCH 18/30] Added tests for TableTransformExecutor --- .../lib/table-transformer-executor.spec.ts | 222 ++++++++++++++++++ .../test-empty.xlsx | Bin 0 -> 5410 bytes .../test-empty.xlsx.license | 3 + .../test-excel.xlsx | Bin 0 -> 6132 bytes .../test-excel.xlsx.license | 3 + .../valid-column-overwrite.jv | 26 ++ .../valid-column-type-change.jv | 26 ++ .../valid-missing-input-column.jv | 27 +++ .../valid-transfomer.jv | 26 ++ .../valid-transform-type-missmatch.jv | 26 ++ .../test-multiple-sheet-all-cell-formats.xlsx | Bin 0 -> 7978 bytes libs/extensions/tabular/exec/test/util.ts | 87 ++++++- 12 files changed, 445 insertions(+), 1 deletion(-) create mode 100644 libs/extensions/tabular/exec/src/lib/table-transformer-executor.spec.ts create mode 100644 libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-empty.xlsx create mode 100644 libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-empty.xlsx.license create mode 100644 libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-excel.xlsx create mode 100644 libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-excel.xlsx.license create mode 100644 libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-column-overwrite.jv create mode 100644 libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-column-type-change.jv create mode 100644 libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-missing-input-column.jv create mode 100644 libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-transfomer.jv create mode 100644 libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-transform-type-missmatch.jv create mode 100644 libs/extensions/tabular/exec/test/assets/test-multiple-sheet-all-cell-formats.xlsx diff --git a/libs/extensions/tabular/exec/src/lib/table-transformer-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/table-transformer-executor.spec.ts new file mode 100644 index 000000000..4ea0dce63 --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/table-transformer-executor.spec.ts @@ -0,0 +1,222 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as path from 'path'; + +import * as R from '@jvalue/jayvee-execution'; +import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { TabularLangExtension } from '@jvalue/jayvee-extensions/tabular/lang'; +import { + BlockDefinition, + IOType, + PrimitiveValuetypes, + createJayveeServices, + useExtension, +} from '@jvalue/jayvee-language-server'; +import { + ParseHelperOptions, + TestLangExtension, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { + ReducedColumnDefinitionEntry, + createTableFromLocalExcelFile, +} from '../../test/util'; + +import { TableTransformerExecutor } from './table-transformer-executor'; + +describe('Validation of TableTransformerExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise>; + + let locator: AstNodeLocator; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/table-transformer-executor/', + ); + + async function readTestExcelAllColumns() { + return readTestTable('test-excel.xlsx', [ + { + columnName: 'index', + sheetColumnIndex: 0, + valuetype: PrimitiveValuetypes.Integer, + }, + { + columnName: 'name', + sheetColumnIndex: 1, + valuetype: PrimitiveValuetypes.Text, + }, + { + columnName: 'flag', + sheetColumnIndex: 2, + valuetype: PrimitiveValuetypes.Boolean, + }, + ]); + } + async function readTestTable( + fileName: string, + columnDefinitions: ReducedColumnDefinitionEntry[], + ): Promise { + const absoluteFileName = path.resolve( + __dirname, + '../../test/assets/table-transformer-executor/', + fileName, + ); + return await createTableFromLocalExcelFile( + absoluteFileName, + columnDefinitions, + ); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.Table, + ): Promise> { + const document = await parse(input, { validationChecks: 'all' }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new TableTransformerExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, [block]), + ); + } + + beforeAll(() => { + // Register extensions + useExtension(new TabularLangExtension()); + useExtension(new TestLangExtension()); + // Create language services + const services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on valid table transform', async () => { + const text = readJvTestAsset('valid-transfomer.jv'); + + const testTable = await readTestExcelAllColumns(); + const result = await parseAndExecuteExecutor(text, testTable); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(4); + expect(result.right.getNumberOfRows()).toEqual(6); + expect(result.right.getColumn('index')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining([0, 1, 2, 5]) as number[], + }), + ); + expect(result.right.getColumn('index2')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining([0, 2, 4, 10]) as number[], + }), + ); + } + }); + + it('should diagnose no error on column overwrite', async () => { + const text = readJvTestAsset('valid-column-overwrite.jv'); + + const testTable = await readTestExcelAllColumns(); + const result = await parseAndExecuteExecutor(text, testTable); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(6); + expect(result.right.getColumn('index')).toEqual( + expect.objectContaining({ + values: expect.arrayContaining([0, 2, 4, 10]) as number[], + }), + ); + } + }); + + it('should diagnose no error on column type change', async () => { + const text = readJvTestAsset('valid-column-type-change.jv'); + + const testTable = await readTestExcelAllColumns(); + const result = await parseAndExecuteExecutor(text, testTable); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(3); + expect(result.right.getNumberOfRows()).toEqual(6); + expect(result.right.getColumn('index')).toEqual( + expect.objectContaining({ + values: [false, true, true, true, true, true], + valuetype: PrimitiveValuetypes.Boolean, + }), + ); + } + }); + + it('should diagnose no error on empty table', async () => { + const text = readJvTestAsset('valid-transfomer.jv'); + + const testTable = await readTestTable('test-empty.xlsx', [ + { + columnName: 'index', + sheetColumnIndex: 0, + valuetype: PrimitiveValuetypes.Integer, + }, + ]); + const result = await parseAndExecuteExecutor(text, testTable); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(2); + expect(result.right.getNumberOfRows()).toEqual(0); + expect(result.right.getColumn('index')?.values).toHaveLength(0); + expect(result.right.getColumn('index2')?.values).toHaveLength(0); + } + }); + + it('should diagnose error on missing input column', async () => { + const text = readJvTestAsset('valid-missing-input-column.jv'); + + const testTable = await readTestExcelAllColumns(); + const result = await parseAndExecuteExecutor(text, testTable); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'The specified input column "id" does not exist in the given table', + ); + } + }); + + it('should diagnose error on transform type missmatch', async () => { + const text = readJvTestAsset('valid-transform-type-missmatch.jv'); + + const testTable = await readTestExcelAllColumns(); + const result = await parseAndExecuteExecutor(text, testTable); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'Type text of column "name" is not convertible to type integer', + ); + } + }); +}); diff --git a/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-empty.xlsx b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-empty.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..af7b184be1b847220d6c83fb152e1f4618a6c4d3 GIT binary patch literal 5410 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 literal 0 HcmV?d00001 diff --git a/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-empty.xlsx.license b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-empty.xlsx.license new file mode 100644 index 000000000..17c5d2bad --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-empty.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-excel.xlsx b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/test-excel.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..8869be3b3295a65c8e97de17561b318956ff987c GIT binary patch literal 6132 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 TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-column-type-change.jv b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-column-type-change.jv new file mode 100644 index 000000000..a85a6b2ad --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-column-type-change.jv @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + transform ToBool { + from asInteger oftype integer; + to asBool oftype boolean; + + asBool: asInteger != 0; + } + + block TestExtractor oftype TestTableExtractor { + } + + block TestBlock oftype TableTransformer { + inputColumns: ['index']; + outputColumn: 'index'; + use: ToBool; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-missing-input-column.jv b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-missing-input-column.jv new file mode 100644 index 000000000..44e535ffc --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-missing-input-column.jv @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + transform Add { + from left oftype integer; + from right oftype integer; + to added oftype integer; + + added: left + right; + } + + block TestExtractor oftype TestTableExtractor { + } + + block TestBlock oftype TableTransformer { + inputColumns: ['index', 'id']; + outputColumn: 'add'; + use: Add; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-transfomer.jv b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-transfomer.jv new file mode 100644 index 000000000..856d8024b --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-transfomer.jv @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + transform Double { + from original oftype integer; + to double oftype integer; + + double: original * 2; + } + + block TestExtractor oftype TestTableExtractor { + } + + block TestBlock oftype TableTransformer { + inputColumns: ['index']; + outputColumn: 'index2'; + use: Double; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-transform-type-missmatch.jv b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-transform-type-missmatch.jv new file mode 100644 index 000000000..52675e73d --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-transformer-executor/valid-transform-type-missmatch.jv @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + transform Double { + from original oftype integer; + to double oftype integer; + + double: original * 2; + } + + block TestExtractor oftype TestTableExtractor { + } + + block TestBlock oftype TableTransformer { + inputColumns: ['name']; + outputColumn: 'index2'; + use: Double; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/test-multiple-sheet-all-cell-formats.xlsx b/libs/extensions/tabular/exec/test/assets/test-multiple-sheet-all-cell-formats.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..565c89c28a6809f31cda1162b110d17a4774ced7 GIT binary patch literal 7978 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 G-~I=+53m>j literal 0 HcmV?d00001 diff --git a/libs/extensions/tabular/exec/test/util.ts b/libs/extensions/tabular/exec/test/util.ts index 3ee482362..68180db6f 100644 --- a/libs/extensions/tabular/exec/test/util.ts +++ b/libs/extensions/tabular/exec/test/util.ts @@ -4,9 +4,20 @@ import * as path from 'path'; -import { Workbook } from '@jvalue/jayvee-execution'; +import { + Table, + TableRow, + Workbook, + parseValueToInternalRepresentation, +} from '@jvalue/jayvee-execution'; +import { + InternalValueRepresentation, + Valuetype, +} from '@jvalue/jayvee-language-server'; import * as exceljs from 'exceljs'; +import { ColumnDefinitionEntry } from '../src/lib/table-interpreter-executor'; + export async function createWorkbookFromLocalExcelFile( fileName: string, ): Promise { @@ -37,3 +48,77 @@ export async function createWorkbookFromLocalExcelFile( return workbook; } + +export type ReducedColumnDefinitionEntry = Pick< + ColumnDefinitionEntry, + 'sheetColumnIndex' | 'columnName' | 'valuetype' +>; + +/** + * Creates a Table from the first sheet of the excel file pointed to by {@link fileName} + * and parsed using the given column definitions. + * Note: The parsing is static and thus cannot detect runtime types! + * @param fileName + * @param columnDefinitions columns to be read from table (no header matching) + * @returns Table containing data of excel + */ +export async function createTableFromLocalExcelFile( + fileName: string, + columnDefinitions: ReducedColumnDefinitionEntry[], +): Promise { + const workBookFromFile = new exceljs.Workbook(); + await workBookFromFile.xlsx.readFile(path.resolve(__dirname, fileName)); + + const workSheet = workBookFromFile.worksheets[0] as exceljs.Worksheet; + const table = new Table(); + + columnDefinitions.forEach((columnDefinition) => { + table.addColumn(columnDefinition.columnName, { + values: [], + valuetype: columnDefinition.valuetype, + }); + }); + + workSheet.eachRow((row) => { + const tableRow = constructTableRow(row, columnDefinitions); + table.addRow(tableRow); + }); + + return table; +} +function constructTableRow( + row: exceljs.Row, + columnDefinitions: ReducedColumnDefinitionEntry[], +): TableRow { + const tableRow: TableRow = {}; + + row.eachCell( + { includeEmpty: true }, + (cell: exceljs.Cell, colNumber: number) => { + if (colNumber > columnDefinitions.length) return; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const columnDefinition = columnDefinitions[colNumber - 1]!; + const value = cell.text; + const valuetype = columnDefinition.valuetype; + + const parsedValue = parseAndValidatePrimitiveValue(value, valuetype); + if (parsedValue === undefined) { + return; + } + + tableRow[columnDefinition.columnName] = parsedValue; + }, + ); + return tableRow; +} +function parseAndValidatePrimitiveValue( + value: string, + valuetype: Valuetype, +): InternalValueRepresentation | undefined { + const parsedValue = parseValueToInternalRepresentation(value, valuetype); + if (parsedValue === undefined) { + return undefined; + } + + return parsedValue; +} From 81941d7beb0499c5f8ce46711a573822b4267548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 30 Oct 2023 11:26:52 +0100 Subject: [PATCH 19/30] Added tests for XLSXInterpreterExecutor --- .../src/lib/xlsx-interpreter-executor.spec.ts | 137 ++++++++++++++++++ .../xlsx-interpreter-executor/test-empty.xlsx | Bin 0 -> 5410 bytes .../test-empty.xlsx.license | 3 + .../xlsx-interpreter-executor/test-excel.xlsx | Bin 0 -> 6132 bytes .../test-excel.xlsx.license | 3 + .../test-multiple-sheets.xlsx | Bin 0 -> 8141 bytes .../test-multiple-sheets.xlsx.license | 3 + .../valid-excel-interpreter.jv | 16 ++ 8 files changed, 162 insertions(+) create mode 100644 libs/extensions/tabular/exec/src/lib/xlsx-interpreter-executor.spec.ts create mode 100644 libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-empty.xlsx create mode 100644 libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-empty.xlsx.license create mode 100644 libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-excel.xlsx create mode 100644 libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-excel.xlsx.license create mode 100644 libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-multiple-sheets.xlsx create mode 100644 libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-multiple-sheets.xlsx.license create mode 100644 libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/valid-excel-interpreter.jv diff --git a/libs/extensions/tabular/exec/src/lib/xlsx-interpreter-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/xlsx-interpreter-executor.spec.ts new file mode 100644 index 000000000..c2c230f8c --- /dev/null +++ b/libs/extensions/tabular/exec/src/lib/xlsx-interpreter-executor.spec.ts @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as path from 'path'; + +import * as R from '@jvalue/jayvee-execution'; +import { + createBinaryFileFromLocalFile, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; +import { TabularLangExtension } from '@jvalue/jayvee-extensions/tabular/lang'; +import { + BlockDefinition, + IOType, + createJayveeServices, + useExtension, +} from '@jvalue/jayvee-language-server'; +import { + ParseHelperOptions, + TestLangExtension, + expectNoParserAndLexerErrors, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; +import { NodeFileSystem } from 'langium/node'; + +import { XLSXInterpreterExecutor } from './xlsx-interpreter-executor'; + +describe('Validation of XLSXInterpreterExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise>; + + let locator: AstNodeLocator; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/xlsx-interpreter-executor/', + ); + + function readTestFile(fileName: string): R.BinaryFile { + const absoluteFileName = path.resolve( + __dirname, + '../../test/assets/xlsx-interpreter-executor/', + fileName, + ); + return createBinaryFileFromLocalFile(absoluteFileName); + } + + function expectSheetRowAndColumnSize( + sheet: R.Sheet | undefined, + cols: number, + rows: number, + ) { + expect(sheet?.getNumberOfColumns()).toEqual(cols); + expect(sheet?.getNumberOfRows()).toEqual(rows); + } + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.BinaryFile, + ): Promise> { + const document = await parse(input, { validationChecks: 'all' }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new XLSXInterpreterExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, [block]), + ); + } + + beforeAll(() => { + // Register extensions + useExtension(new TabularLangExtension()); + useExtension(new TestLangExtension()); + // Create language services + const services = createJayveeServices(NodeFileSystem).Jayvee; + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + + it('should diagnose no error on single sheet excel', async () => { + const text = readJvTestAsset('valid-excel-interpreter.jv'); + + const testFile = readTestFile('test-excel.xlsx'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.WORKBOOK); + const sheets = result.right.getSheets(); + expect(sheets.size).toEqual(1); + expectSheetRowAndColumnSize(sheets.get('Sheet1'), 3, 6); + } + }); + + it('should diagnose no error on multiple sheet excel', async () => { + const text = readJvTestAsset('valid-excel-interpreter.jv'); + + const testFile = readTestFile('test-multiple-sheets.xlsx'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.WORKBOOK); + const sheets = result.right.getSheets(); + expect(sheets.size).toEqual(3); + expectSheetRowAndColumnSize(sheets.get('Sheet1'), 3, 6); + expectSheetRowAndColumnSize(sheets.get('CustomSheet'), 0, 0); + expectSheetRowAndColumnSize(sheets.get('Sheet3'), 0, 0); + } + }); + + it('should diagnose no error on empty excel', async () => { + const text = readJvTestAsset('valid-excel-interpreter.jv'); + + const testFile = readTestFile('test-empty.xlsx'); + const result = await parseAndExecuteExecutor(text, testFile); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.WORKBOOK); + const sheets = result.right.getSheets(); + expect(sheets.size).toEqual(1); + expectSheetRowAndColumnSize(sheets.get('Sheet1'), 0, 0); + } + }); +}); diff --git a/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-empty.xlsx b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-empty.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..af7b184be1b847220d6c83fb152e1f4618a6c4d3 GIT binary patch literal 5410 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 literal 0 HcmV?d00001 diff --git a/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-empty.xlsx.license b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-empty.xlsx.license new file mode 100644 index 000000000..17c5d2bad --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-empty.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-excel.xlsx b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-excel.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..8869be3b3295a65c8e97de17561b318956ff987c GIT binary patch literal 6132 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 O|4_Wo;_MzjeEJ_A?d(zj literal 0 HcmV?d00001 diff --git a/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-multiple-sheets.xlsx.license b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-multiple-sheets.xlsx.license new file mode 100644 index 000000000..17c5d2bad --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/test-multiple-sheets.xlsx.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg + +SPDX-License-Identifier: AGPL-3.0-only diff --git a/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/valid-excel-interpreter.jv b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/valid-excel-interpreter.jv new file mode 100644 index 000000000..a267981e2 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/xlsx-interpreter-executor/valid-excel-interpreter.jv @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype XLSXInterpreter { + } + + block TestLoader oftype TestWorkbookLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} From aa99e1804b321478946261783d7a59ccfa01976f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 30 Oct 2023 12:15:26 +0100 Subject: [PATCH 20/30] removed unnecessary test file --- .../test-multiple-sheet-all-cell-formats.xlsx | Bin 7978 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 libs/extensions/tabular/exec/test/assets/test-multiple-sheet-all-cell-formats.xlsx diff --git a/libs/extensions/tabular/exec/test/assets/test-multiple-sheet-all-cell-formats.xlsx b/libs/extensions/tabular/exec/test/assets/test-multiple-sheet-all-cell-formats.xlsx deleted file mode 100644 index 565c89c28a6809f31cda1162b110d17a4774ced7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7978 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 G-~I=+53m>j From 2240655e1c0c000635aaa05177cfb3eaab6eae43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 30 Oct 2023 12:18:14 +0100 Subject: [PATCH 21/30] Added missing export of interface --- .../tabular/exec/src/lib/table-interpreter-executor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.ts b/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.ts index fb7da129c..77a4df8db 100644 --- a/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.ts +++ b/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.ts @@ -27,7 +27,7 @@ import { rowIndexToString, } from '@jvalue/jayvee-language-server'; -interface ColumnDefinitionEntry { +export interface ColumnDefinitionEntry { sheetColumnIndex: number; columnName: string; valuetype: Valuetype; From f941c08ed57a9d7987a7d149af3e367192b85f23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 30 Oct 2023 12:28:10 +0100 Subject: [PATCH 22/30] Added tests for TableInterpreterExecutor with empty columns input --- .../lib/table-interpreter-executor.spec.ts | 34 +++++++++++++++++++ .../valid-empty-columns-with-header.jv | 19 +++++++++++ .../valid-empty-columns-without-header.jv | 19 +++++++++++ 3 files changed, 72 insertions(+) create mode 100644 libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-empty-columns-with-header.jv create mode 100644 libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-empty-columns-without-header.jv diff --git a/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts index 4f543a5d0..2d1c2b165 100644 --- a/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts +++ b/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts @@ -111,6 +111,23 @@ describe('Validation of TableInterpreterExecutor', () => { } }); + it('should diagnose empty table on empty column parameter', async () => { + const text = readJvTestAsset('valid-empty-columns-with-header.jv'); + + const testWorkbook = await readTestWorkbook('test-with-header.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(0); + expect(result.right.getNumberOfRows()).toEqual(16); + } + }); + it('should diagnose empty table on wrong header case', async () => { const text = readJvTestAsset('valid-with-capitalized-header.jv'); @@ -228,6 +245,23 @@ describe('Validation of TableInterpreterExecutor', () => { } }); + it('should diagnose empty table on empty column parameter', async () => { + const text = readJvTestAsset('valid-empty-columns-without-header.jv'); + + const testWorkbook = await readTestWorkbook('test-without-header.xlsx'); + const result = await parseAndExecuteExecutor( + text, + testWorkbook.getSheetByName('Sheet1') as R.Sheet, + ); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.TABLE); + expect(result.right.getNumberOfColumns()).toEqual(0); + expect(result.right.getNumberOfRows()).toEqual(16); + } + }); + it('should diagnose error on empty sheet', async () => { const text = readJvTestAsset('valid-without-header.jv'); diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-empty-columns-with-header.jv b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-empty-columns-with-header.jv new file mode 100644 index 000000000..7e46a44c6 --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-empty-columns-with-header.jv @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype TableInterpreter { + header: true; + columns: []; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} diff --git a/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-empty-columns-without-header.jv b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-empty-columns-without-header.jv new file mode 100644 index 000000000..b6e423edc --- /dev/null +++ b/libs/extensions/tabular/exec/test/assets/table-interpreter-executor/valid-empty-columns-without-header.jv @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestSheetExtractor { + } + + block TestBlock oftype TableInterpreter { + header: false; + columns: []; + } + + block TestLoader oftype TestTableLoader { + } + + TestExtractor -> TestBlock -> TestLoader; +} From 9290f97d20ae9dcc802e9aaddc1acc92169ae8d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 13 Nov 2023 09:15:54 +0100 Subject: [PATCH 23/30] Fixed merge --- .../src/lib/util}/file-util.spec.ts | 2 +- libs/execution/test/utils/file-util.ts | 6 +-- .../src/archive-interpreter-executor.spec.ts | 7 +-- .../std/exec/src/file-picker-executor.spec.ts | 7 +-- .../src/gtfs-rt-interpreter-executor.spec.ts | 7 +-- libs/extensions/std/exec/src/string-util.ts | 14 ------ .../text-file-interpreter-executor.spec.ts | 7 +-- .../src/text-line-deleter-executor.spec.ts | 7 +-- .../src/text-range-selector-executor.spec.ts | 7 +-- libs/extensions/std/exec/test/index.ts | 1 - libs/extensions/std/exec/test/utils.ts | 47 ------------------- 11 files changed, 28 insertions(+), 84 deletions(-) rename libs/{extensions/std/exec/src => execution/src/lib/util}/file-util.spec.ts (97%) delete mode 100644 libs/extensions/std/exec/src/string-util.ts delete mode 100644 libs/extensions/std/exec/test/utils.ts diff --git a/libs/extensions/std/exec/src/file-util.spec.ts b/libs/execution/src/lib/util/file-util.spec.ts similarity index 97% rename from libs/extensions/std/exec/src/file-util.spec.ts rename to libs/execution/src/lib/util/file-util.spec.ts index decef2317..5d3074157 100644 --- a/libs/extensions/std/exec/src/file-util.spec.ts +++ b/libs/execution/src/lib/util/file-util.spec.ts @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { FileExtension, MimeType } from '@jvalue/jayvee-execution'; +import { FileExtension, MimeType } from '../types'; import { inferFileExtensionFromContentTypeString, diff --git a/libs/execution/test/utils/file-util.ts b/libs/execution/test/utils/file-util.ts index 35ace2292..be8689515 100644 --- a/libs/execution/test/utils/file-util.ts +++ b/libs/execution/test/utils/file-util.ts @@ -11,14 +11,14 @@ import { MimeType, TextFile, inferFileExtensionFromFileExtensionString, - inferMimeTypeFromContentTypeString, + inferMimeTypeFromFileExtensionString, splitLines, } from '../../src'; export function createBinaryFileFromLocalFile(fileName: string): BinaryFile { const extName = path.extname(fileName); const mimeType = - inferMimeTypeFromContentTypeString(extName) || + inferMimeTypeFromFileExtensionString(extName) || MimeType.APPLICATION_OCTET_STREAM; const fileExtension = inferFileExtensionFromFileExtensionString(extName) || FileExtension.NONE; @@ -29,7 +29,7 @@ export function createBinaryFileFromLocalFile(fileName: string): BinaryFile { export function createTextFileFromLocalFile(fileName: string): TextFile { const extName = path.extname(fileName); const mimeType = - inferMimeTypeFromContentTypeString(extName) || + inferMimeTypeFromFileExtensionString(extName) || MimeType.APPLICATION_OCTET_STREAM; const fileExtension = inferFileExtensionFromFileExtensionString(extName) || FileExtension.NONE; diff --git a/libs/extensions/std/exec/src/archive-interpreter-executor.spec.ts b/libs/extensions/std/exec/src/archive-interpreter-executor.spec.ts index 7b174ae11..ae4eb2fcd 100644 --- a/libs/extensions/std/exec/src/archive-interpreter-executor.spec.ts +++ b/libs/extensions/std/exec/src/archive-interpreter-executor.spec.ts @@ -5,7 +5,10 @@ import * as path from 'path'; import * as R from '@jvalue/jayvee-execution'; -import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { + createBinaryFileFromLocalFile, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; import { BlockDefinition, IOType, @@ -21,8 +24,6 @@ import { import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; -import { createBinaryFileFromLocalFile } from '../test'; - import { ArchiveInterpreterExecutor } from './archive-interpreter-executor'; describe('Validation of ArchiveInterpreterExecutor', () => { diff --git a/libs/extensions/std/exec/src/file-picker-executor.spec.ts b/libs/extensions/std/exec/src/file-picker-executor.spec.ts index 8fd8e3578..f89119a76 100644 --- a/libs/extensions/std/exec/src/file-picker-executor.spec.ts +++ b/libs/extensions/std/exec/src/file-picker-executor.spec.ts @@ -5,7 +5,10 @@ import * as path from 'path'; import * as R from '@jvalue/jayvee-execution'; -import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { + createBinaryFileFromLocalFile, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; import { BlockDefinition, IOType, @@ -21,8 +24,6 @@ import { import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; -import { createBinaryFileFromLocalFile } from '../test'; - import { FilePickerExecutor } from './file-picker-executor'; describe('Validation of FilePickerExecutor', () => { diff --git a/libs/extensions/std/exec/src/gtfs-rt-interpreter-executor.spec.ts b/libs/extensions/std/exec/src/gtfs-rt-interpreter-executor.spec.ts index 75d8ae8d9..65cdcfb86 100644 --- a/libs/extensions/std/exec/src/gtfs-rt-interpreter-executor.spec.ts +++ b/libs/extensions/std/exec/src/gtfs-rt-interpreter-executor.spec.ts @@ -5,7 +5,10 @@ import * as path from 'path'; import * as R from '@jvalue/jayvee-execution'; -import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { + createBinaryFileFromLocalFile, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; import { BlockDefinition, IOType, @@ -21,8 +24,6 @@ import { import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; -import { createBinaryFileFromLocalFile } from '../test'; - import { GtfsRTInterpreterExecutor } from './gtfs-rt-interpreter-executor'; describe('Validation of GtfsRTInterpreterExecutor', () => { diff --git a/libs/extensions/std/exec/src/string-util.ts b/libs/extensions/std/exec/src/string-util.ts deleted file mode 100644 index 55b7d78b0..000000000 --- a/libs/extensions/std/exec/src/string-util.ts +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg -// -// SPDX-License-Identifier: AGPL-3.0-only - -export function splitLines(textContent: string, lineBreak: RegExp): string[] { - const lines = textContent.split(lineBreak); - - // There may be an additional empty line due to the previous splitting - if (lines[lines.length - 1] === '') { - lines.splice(lines.length - 1, 1); - } - - return lines; -} diff --git a/libs/extensions/std/exec/src/text-file-interpreter-executor.spec.ts b/libs/extensions/std/exec/src/text-file-interpreter-executor.spec.ts index 342bceeab..0c9fd6462 100644 --- a/libs/extensions/std/exec/src/text-file-interpreter-executor.spec.ts +++ b/libs/extensions/std/exec/src/text-file-interpreter-executor.spec.ts @@ -5,7 +5,10 @@ import * as path from 'path'; import * as R from '@jvalue/jayvee-execution'; -import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { + createBinaryFileFromLocalFile, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; import { BlockDefinition, IOType, @@ -21,8 +24,6 @@ import { import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; -import { createBinaryFileFromLocalFile } from '../test'; - import { TextFileInterpreterExecutor } from './text-file-interpreter-executor'; describe('Validation of TextFileInterpreterExecutor', () => { diff --git a/libs/extensions/std/exec/src/text-line-deleter-executor.spec.ts b/libs/extensions/std/exec/src/text-line-deleter-executor.spec.ts index 8ca8a191e..156151fce 100644 --- a/libs/extensions/std/exec/src/text-line-deleter-executor.spec.ts +++ b/libs/extensions/std/exec/src/text-line-deleter-executor.spec.ts @@ -5,7 +5,10 @@ import * as path from 'path'; import * as R from '@jvalue/jayvee-execution'; -import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { + createTextFileFromLocalFile, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; import { BlockDefinition, IOType, @@ -21,8 +24,6 @@ import { import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; -import { createTextFileFromLocalFile } from '../test'; - import { TextLineDeleterExecutor } from './text-line-deleter-executor'; describe('Validation of TextLineDeleterExecutor', () => { diff --git a/libs/extensions/std/exec/src/text-range-selector-executor.spec.ts b/libs/extensions/std/exec/src/text-range-selector-executor.spec.ts index 73e333359..b9ad72c15 100644 --- a/libs/extensions/std/exec/src/text-range-selector-executor.spec.ts +++ b/libs/extensions/std/exec/src/text-range-selector-executor.spec.ts @@ -5,7 +5,10 @@ import * as path from 'path'; import * as R from '@jvalue/jayvee-execution'; -import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; +import { + createTextFileFromLocalFile, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; import { BlockDefinition, IOType, @@ -21,8 +24,6 @@ import { import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; -import { createTextFileFromLocalFile } from '../test'; - import { TextRangeSelectorExecutor } from './text-range-selector-executor'; describe('Validation of TextRangeSelectorExecutor', () => { diff --git a/libs/extensions/std/exec/test/index.ts b/libs/extensions/std/exec/test/index.ts index cfe12da6f..460aca6a3 100644 --- a/libs/extensions/std/exec/test/index.ts +++ b/libs/extensions/std/exec/test/index.ts @@ -3,4 +3,3 @@ // SPDX-License-Identifier: AGPL-3.0-only export * from './mocks'; -export * from './utils'; diff --git a/libs/extensions/std/exec/test/utils.ts b/libs/extensions/std/exec/test/utils.ts deleted file mode 100644 index 7b7d43d5b..000000000 --- a/libs/extensions/std/exec/test/utils.ts +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg -// -// SPDX-License-Identifier: AGPL-3.0-only - -import { readFileSync } from 'fs'; -import * as path from 'path'; - -import { - BinaryFile, - FileExtension, - MimeType, - TextFile, -} from '@jvalue/jayvee-execution'; - -import { - inferFileExtensionFromFileExtensionString, - inferMimeTypeFromFileExtensionString, -} from '../src/file-util'; -import { splitLines } from '../src/string-util'; - -export function createBinaryFileFromLocalFile(fileName: string): BinaryFile { - const extName = path.extname(fileName); - const mimeType = - inferMimeTypeFromFileExtensionString(extName) || - MimeType.APPLICATION_OCTET_STREAM; - const fileExtension = - inferFileExtensionFromFileExtensionString(extName) || FileExtension.NONE; - const file = readFileSync(path.resolve(__dirname, fileName)); - return new BinaryFile(path.basename(fileName), fileExtension, mimeType, file); -} - -export function createTextFileFromLocalFile(fileName: string): TextFile { - const extName = path.extname(fileName); - const mimeType = - inferMimeTypeFromFileExtensionString(extName) || - MimeType.APPLICATION_OCTET_STREAM; - const fileExtension = - inferFileExtensionFromFileExtensionString(extName) || FileExtension.NONE; - const fileContent = readFileSync(path.resolve(__dirname, fileName), 'utf-8'); - - return new TextFile( - path.basename(fileName), - fileExtension, - mimeType, - splitLines(fileContent, /\r?\n/), - ); -} From 078c4a913c938c53c25390c077007e6b63addc61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 13 Nov 2023 09:31:39 +0100 Subject: [PATCH 24/30] Applied new TestExtension loading --- .../lib/cell-range-selector-executor.spec.ts | 12 +++--- .../exec/src/lib/cell-writer-executor.spec.ts | 12 +++--- .../src/lib/column-deleter-executor.spec.ts | 12 +++--- .../src/lib/csv-interpreter-executor.spec.ts | 12 +++--- .../exec/src/lib/row-deleter-executor.spec.ts | 12 +++--- .../src/lib/sheet-picker-executor.spec.ts | 12 +++--- .../lib/table-interpreter-executor.spec.ts | 12 +++--- .../lib/table-transformer-executor.spec.ts | 12 +++--- .../src/lib/xlsx-interpreter-executor.spec.ts | 12 +++--- .../test/test-extension/TestBlockTypes.jv | 43 +++++++++++++++++++ 10 files changed, 88 insertions(+), 63 deletions(-) create mode 100644 libs/extensions/tabular/exec/test/test-extension/TestBlockTypes.jv diff --git a/libs/extensions/tabular/exec/src/lib/cell-range-selector-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/cell-range-selector-executor.spec.ts index a230f3356..c39d6a3c4 100644 --- a/libs/extensions/tabular/exec/src/lib/cell-range-selector-executor.spec.ts +++ b/libs/extensions/tabular/exec/src/lib/cell-range-selector-executor.spec.ts @@ -6,17 +6,15 @@ import * as path from 'path'; import * as R from '@jvalue/jayvee-execution'; import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; -import { TabularLangExtension } from '@jvalue/jayvee-extensions/tabular/lang'; import { BlockDefinition, IOType, createJayveeServices, - useExtension, } from '@jvalue/jayvee-language-server'; import { ParseHelperOptions, - TestLangExtension, expectNoParserAndLexerErrors, + loadTestExtensions, parseHelper, readJvTestAssetHelper, } from '@jvalue/jayvee-language-server/test'; @@ -67,12 +65,12 @@ describe('Validation of CellRangeSelectorExecutor', () => { ); } - beforeAll(() => { - // Register extensions - useExtension(new TabularLangExtension()); - useExtension(new TestLangExtension()); + beforeAll(async () => { // Create language services const services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); locator = services.workspace.AstNodeLocator; // Parse function for Jayvee (without validation) parse = parseHelper(services); diff --git a/libs/extensions/tabular/exec/src/lib/cell-writer-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/cell-writer-executor.spec.ts index 28479475f..99ac1d493 100644 --- a/libs/extensions/tabular/exec/src/lib/cell-writer-executor.spec.ts +++ b/libs/extensions/tabular/exec/src/lib/cell-writer-executor.spec.ts @@ -6,17 +6,15 @@ import * as path from 'path'; import * as R from '@jvalue/jayvee-execution'; import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; -import { TabularLangExtension } from '@jvalue/jayvee-extensions/tabular/lang'; import { BlockDefinition, IOType, createJayveeServices, - useExtension, } from '@jvalue/jayvee-language-server'; import { ParseHelperOptions, - TestLangExtension, expectNoParserAndLexerErrors, + loadTestExtensions, parseHelper, readJvTestAssetHelper, } from '@jvalue/jayvee-language-server/test'; @@ -67,12 +65,12 @@ describe('Validation of CellWriterExecutor', () => { ); } - beforeAll(() => { - // Register extensions - useExtension(new TabularLangExtension()); - useExtension(new TestLangExtension()); + beforeAll(async () => { // Create language services const services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); locator = services.workspace.AstNodeLocator; // Parse function for Jayvee (without validation) parse = parseHelper(services); diff --git a/libs/extensions/tabular/exec/src/lib/column-deleter-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/column-deleter-executor.spec.ts index e9e6ba32e..7150e817e 100644 --- a/libs/extensions/tabular/exec/src/lib/column-deleter-executor.spec.ts +++ b/libs/extensions/tabular/exec/src/lib/column-deleter-executor.spec.ts @@ -6,17 +6,15 @@ import * as path from 'path'; import * as R from '@jvalue/jayvee-execution'; import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; -import { TabularLangExtension } from '@jvalue/jayvee-extensions/tabular/lang'; import { BlockDefinition, IOType, createJayveeServices, - useExtension, } from '@jvalue/jayvee-language-server'; import { ParseHelperOptions, - TestLangExtension, expectNoParserAndLexerErrors, + loadTestExtensions, parseHelper, readJvTestAssetHelper, } from '@jvalue/jayvee-language-server/test'; @@ -67,12 +65,12 @@ describe('Validation of ColumnDeleterExecutor', () => { ); } - beforeAll(() => { - // Register extensions - useExtension(new TabularLangExtension()); - useExtension(new TestLangExtension()); + beforeAll(async () => { // Create language services const services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); locator = services.workspace.AstNodeLocator; // Parse function for Jayvee (without validation) parse = parseHelper(services); diff --git a/libs/extensions/tabular/exec/src/lib/csv-interpreter-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/csv-interpreter-executor.spec.ts index 8405f395f..c4a8b938e 100644 --- a/libs/extensions/tabular/exec/src/lib/csv-interpreter-executor.spec.ts +++ b/libs/extensions/tabular/exec/src/lib/csv-interpreter-executor.spec.ts @@ -9,17 +9,15 @@ import { createTextFileFromLocalFile, getTestExecutionContext, } from '@jvalue/jayvee-execution/test'; -import { TabularLangExtension } from '@jvalue/jayvee-extensions/tabular/lang'; import { BlockDefinition, IOType, createJayveeServices, - useExtension, } from '@jvalue/jayvee-language-server'; import { ParseHelperOptions, - TestLangExtension, expectNoParserAndLexerErrors, + loadTestExtensions, parseHelper, readJvTestAssetHelper, } from '@jvalue/jayvee-language-server/test'; @@ -68,12 +66,12 @@ describe('Validation of CSVInterpreterExecutor', () => { ); } - beforeAll(() => { - // Register extensions - useExtension(new TabularLangExtension()); - useExtension(new TestLangExtension()); + beforeAll(async () => { // Create language services const services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); locator = services.workspace.AstNodeLocator; // Parse function for Jayvee (without validation) parse = parseHelper(services); diff --git a/libs/extensions/tabular/exec/src/lib/row-deleter-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/row-deleter-executor.spec.ts index c91382169..03bea0917 100644 --- a/libs/extensions/tabular/exec/src/lib/row-deleter-executor.spec.ts +++ b/libs/extensions/tabular/exec/src/lib/row-deleter-executor.spec.ts @@ -6,17 +6,15 @@ import * as path from 'path'; import * as R from '@jvalue/jayvee-execution'; import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; -import { TabularLangExtension } from '@jvalue/jayvee-extensions/tabular/lang'; import { BlockDefinition, IOType, createJayveeServices, - useExtension, } from '@jvalue/jayvee-language-server'; import { ParseHelperOptions, - TestLangExtension, expectNoParserAndLexerErrors, + loadTestExtensions, parseHelper, readJvTestAssetHelper, } from '@jvalue/jayvee-language-server/test'; @@ -67,12 +65,12 @@ describe('Validation of RowDeleterExecutor', () => { ); } - beforeAll(() => { - // Register extensions - useExtension(new TabularLangExtension()); - useExtension(new TestLangExtension()); + beforeAll(async () => { // Create language services const services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); locator = services.workspace.AstNodeLocator; // Parse function for Jayvee (without validation) parse = parseHelper(services); diff --git a/libs/extensions/tabular/exec/src/lib/sheet-picker-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/sheet-picker-executor.spec.ts index 9dfba7ea0..4af35e9f5 100644 --- a/libs/extensions/tabular/exec/src/lib/sheet-picker-executor.spec.ts +++ b/libs/extensions/tabular/exec/src/lib/sheet-picker-executor.spec.ts @@ -6,17 +6,15 @@ import * as path from 'path'; import * as R from '@jvalue/jayvee-execution'; import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; -import { TabularLangExtension } from '@jvalue/jayvee-extensions/tabular/lang'; import { BlockDefinition, IOType, createJayveeServices, - useExtension, } from '@jvalue/jayvee-language-server'; import { ParseHelperOptions, - TestLangExtension, expectNoParserAndLexerErrors, + loadTestExtensions, parseHelper, readJvTestAssetHelper, } from '@jvalue/jayvee-language-server/test'; @@ -67,12 +65,12 @@ describe('Validation of SheetPickerExecutor', () => { ); } - beforeAll(() => { - // Register extensions - useExtension(new TabularLangExtension()); - useExtension(new TestLangExtension()); + beforeAll(async () => { // Create language services const services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); locator = services.workspace.AstNodeLocator; // Parse function for Jayvee (without validation) parse = parseHelper(services); diff --git a/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts index 2d1c2b165..ff21a70e4 100644 --- a/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts +++ b/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts @@ -6,17 +6,15 @@ import * as path from 'path'; import * as R from '@jvalue/jayvee-execution'; import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; -import { TabularLangExtension } from '@jvalue/jayvee-extensions/tabular/lang'; import { BlockDefinition, IOType, createJayveeServices, - useExtension, } from '@jvalue/jayvee-language-server'; import { ParseHelperOptions, - TestLangExtension, expectNoParserAndLexerErrors, + loadTestExtensions, parseHelper, readJvTestAssetHelper, } from '@jvalue/jayvee-language-server/test'; @@ -67,12 +65,12 @@ describe('Validation of TableInterpreterExecutor', () => { ); } - beforeAll(() => { - // Register extensions - useExtension(new TabularLangExtension()); - useExtension(new TestLangExtension()); + beforeAll(async () => { // Create language services const services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); locator = services.workspace.AstNodeLocator; // Parse function for Jayvee (without validation) parse = parseHelper(services); diff --git a/libs/extensions/tabular/exec/src/lib/table-transformer-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/table-transformer-executor.spec.ts index 4ea0dce63..a2763b1ff 100644 --- a/libs/extensions/tabular/exec/src/lib/table-transformer-executor.spec.ts +++ b/libs/extensions/tabular/exec/src/lib/table-transformer-executor.spec.ts @@ -6,18 +6,16 @@ import * as path from 'path'; import * as R from '@jvalue/jayvee-execution'; import { getTestExecutionContext } from '@jvalue/jayvee-execution/test'; -import { TabularLangExtension } from '@jvalue/jayvee-extensions/tabular/lang'; import { BlockDefinition, IOType, PrimitiveValuetypes, createJayveeServices, - useExtension, } from '@jvalue/jayvee-language-server'; import { ParseHelperOptions, - TestLangExtension, expectNoParserAndLexerErrors, + loadTestExtensions, parseHelper, readJvTestAssetHelper, } from '@jvalue/jayvee-language-server/test'; @@ -96,12 +94,12 @@ describe('Validation of TableTransformerExecutor', () => { ); } - beforeAll(() => { - // Register extensions - useExtension(new TabularLangExtension()); - useExtension(new TestLangExtension()); + beforeAll(async () => { // Create language services const services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); locator = services.workspace.AstNodeLocator; // Parse function for Jayvee (without validation) parse = parseHelper(services); diff --git a/libs/extensions/tabular/exec/src/lib/xlsx-interpreter-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/xlsx-interpreter-executor.spec.ts index c2c230f8c..f289ea96d 100644 --- a/libs/extensions/tabular/exec/src/lib/xlsx-interpreter-executor.spec.ts +++ b/libs/extensions/tabular/exec/src/lib/xlsx-interpreter-executor.spec.ts @@ -9,17 +9,15 @@ import { createBinaryFileFromLocalFile, getTestExecutionContext, } from '@jvalue/jayvee-execution/test'; -import { TabularLangExtension } from '@jvalue/jayvee-extensions/tabular/lang'; import { BlockDefinition, IOType, createJayveeServices, - useExtension, } from '@jvalue/jayvee-language-server'; import { ParseHelperOptions, - TestLangExtension, expectNoParserAndLexerErrors, + loadTestExtensions, parseHelper, readJvTestAssetHelper, } from '@jvalue/jayvee-language-server/test'; @@ -77,12 +75,12 @@ describe('Validation of XLSXInterpreterExecutor', () => { ); } - beforeAll(() => { - // Register extensions - useExtension(new TabularLangExtension()); - useExtension(new TestLangExtension()); + beforeAll(async () => { // Create language services const services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); locator = services.workspace.AstNodeLocator; // Parse function for Jayvee (without validation) parse = parseHelper(services); diff --git a/libs/extensions/tabular/exec/test/test-extension/TestBlockTypes.jv b/libs/extensions/tabular/exec/test/test-extension/TestBlockTypes.jv new file mode 100644 index 000000000..ba08556c4 --- /dev/null +++ b/libs/extensions/tabular/exec/test/test-extension/TestBlockTypes.jv @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestSheetExtractor { + input inPort oftype None; + output outPort oftype Sheet; +} + +builtin blocktype TestSheetLoader { + input inPort oftype Sheet; + output outPort oftype None; +} + +builtin blocktype TestTextFileExtractor { + input inPort oftype None; + output outPort oftype TextFile; +} + +builtin blocktype TestFileExtractor { + input inPort oftype None; + output outPort oftype File; +} + +builtin blocktype TestWorkbookExtractor { + input inPort oftype None; + output outPort oftype Workbook; +} + +builtin blocktype TestWorkbookLoader { + input inPort oftype Workbook; + output outPort oftype None; +} + +builtin blocktype TestTableExtractor { + input inPort oftype None; + output outPort oftype Table; +} + +builtin blocktype TestTableLoader { + input inPort oftype Table; + output outPort oftype None; +} From baf7e1ea73b39cea14ad828c0bb83b410ade5f90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 13 Nov 2023 11:18:21 +0100 Subject: [PATCH 25/30] - Replaced @typescript-eslint/unbound-method with jest/unbound-method for test files - Fixed an jest ts type issue preventing jest.mock returns to use mockFn.mockRejectedValueOnce etc --- .eslintrc.json | 7 ++++++- .../rdbms/exec/test/mocks/postgres-loader-executor-mock.ts | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index ca3801f25..65f42ce94 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -43,10 +43,15 @@ }, { "files": ["*.spec.ts", "*.spec.tsx", "*.spec.js", "*.spec.jsx"], + "plugins": ["jest"], "env": { "jest": true }, - "rules": {} + "rules": { + // you should turn the original rule off *only* for test files + "@typescript-eslint/unbound-method": "off", + "jest/unbound-method": "error" + } } ] } diff --git a/libs/extensions/rdbms/exec/test/mocks/postgres-loader-executor-mock.ts b/libs/extensions/rdbms/exec/test/mocks/postgres-loader-executor-mock.ts index 6aba1c095..3c7d0cd77 100644 --- a/libs/extensions/rdbms/exec/test/mocks/postgres-loader-executor-mock.ts +++ b/libs/extensions/rdbms/exec/test/mocks/postgres-loader-executor-mock.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { BlockExecutorMock } from '@jvalue/jayvee-execution/test'; import { Client } from 'pg'; -type MockedPgClient = jest.Mocked>; +type MockedPgClient = jest.Mocked; export class PostgresLoaderExecutorMock implements BlockExecutorMock { private _pgClient: MockedPgClient | undefined; @@ -26,7 +26,7 @@ export class PostgresLoaderExecutorMock implements BlockExecutorMock { ) => void = defaultPostgresMockRegistration, ) { // setup pg mock - this._pgClient = new Client(); + this._pgClient = new Client() as MockedPgClient; registerMocks(this._pgClient); } restore() { From 8d53fad9a0633fb42ddb546c9ea34449e09d5786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 13 Nov 2023 11:26:07 +0100 Subject: [PATCH 26/30] Added tests for PostgresLoaderExecutor --- .../src/lib/postgres-loader-executor.spec.ts | 167 ++++++++++++++++++ .../valid-postgres-loader.jv | 20 +++ .../test/test-extension/TestBlockTypes.jv | 8 + 3 files changed, 195 insertions(+) create mode 100644 libs/extensions/rdbms/exec/src/lib/postgres-loader-executor.spec.ts create mode 100644 libs/extensions/rdbms/exec/test/assets/postgres-loader-executor/valid-postgres-loader.jv create mode 100644 libs/extensions/rdbms/exec/test/test-extension/TestBlockTypes.jv diff --git a/libs/extensions/rdbms/exec/src/lib/postgres-loader-executor.spec.ts b/libs/extensions/rdbms/exec/src/lib/postgres-loader-executor.spec.ts new file mode 100644 index 000000000..56785beb2 --- /dev/null +++ b/libs/extensions/rdbms/exec/src/lib/postgres-loader-executor.spec.ts @@ -0,0 +1,167 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as path from 'path'; + +import * as R from '@jvalue/jayvee-execution'; +import { + constructTable, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; +import { + BlockDefinition, + IOType, + PrimitiveValuetypes, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import { Client } from 'pg'; + +import { PostgresLoaderExecutor } from './postgres-loader-executor'; + +jest.mock('pg', () => { + const mClient = { + connect: jest.fn(), + query: jest.fn(), + end: jest.fn(), + }; + return { Client: jest.fn(() => mClient) }; +}); + +describe('Validation of PostgresLoaderExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise>; + + let locator: AstNodeLocator; + let pgClient: jest.Mocked; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/postgres-loader-executor/', + ); + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.Table, + ): Promise> { + const document = await parse(input, { validationChecks: 'all' }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new PostgresLoaderExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, [block]), + ); + } + + beforeAll(async () => { + // Create language services + const services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + beforeEach(() => { + pgClient = new Client() as jest.Mocked; + }); + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should diagnose no error on valid loader config', async () => { + const text = readJvTestAsset('valid-postgres-loader.jv'); + + const inputTable = constructTable( + [ + { + columnName: 'Column1', + column: { + values: ['value 1'], + valuetype: PrimitiveValuetypes.Text, + }, + }, + { + columnName: 'Column2', + column: { + values: [20.2], + valuetype: PrimitiveValuetypes.Decimal, + }, + }, + ], + 1, + ); + const result = await parseAndExecuteExecutor(text, inputTable); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.NONE); + expect(pgClient.connect).toBeCalledTimes(1); + expect(pgClient.query).nthCalledWith(1, 'DROP TABLE IF EXISTS "Test";'); + expect(pgClient.query).nthCalledWith( + 2, + `CREATE TABLE IF NOT EXISTS "Test" ("Column1" text,"Column2" real);`, + ); + expect(pgClient.query).nthCalledWith( + 3, + `INSERT INTO "Test" ("Column1","Column2") VALUES ('value 1',20.2)`, + ); + expect(pgClient.end).toBeCalledTimes(1); + } + }); + + it('should diagnose error on pg client connect error', async () => { + const text = readJvTestAsset('valid-postgres-loader.jv'); + + const inputTable = constructTable( + [ + { + columnName: 'Column1', + column: { + values: ['value 1'], + valuetype: PrimitiveValuetypes.Text, + }, + }, + { + columnName: 'Column2', + column: { + values: [20.2], + valuetype: PrimitiveValuetypes.Decimal, + }, + }, + ], + 1, + ); + pgClient.connect.mockImplementation(() => { + throw new Error('Connection error'); + }); + const result = await parseAndExecuteExecutor(text, inputTable); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'Could not write to postgres database: Connection error', + ); + expect(pgClient.connect).toBeCalledTimes(1); + expect(pgClient.query).toBeCalledTimes(0); + expect(pgClient.end).toBeCalledTimes(1); + } + }); +}); diff --git a/libs/extensions/rdbms/exec/test/assets/postgres-loader-executor/valid-postgres-loader.jv b/libs/extensions/rdbms/exec/test/assets/postgres-loader-executor/valid-postgres-loader.jv new file mode 100644 index 000000000..93b769bc9 --- /dev/null +++ b/libs/extensions/rdbms/exec/test/assets/postgres-loader-executor/valid-postgres-loader.jv @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype PostgresLoader { + host: "localhost"; + port: 5432; + username: "postgres"; + password: "postgres"; + database: "TestDB"; + table: "Test"; + } + + TestExtractor -> TestBlock; +} diff --git a/libs/extensions/rdbms/exec/test/test-extension/TestBlockTypes.jv b/libs/extensions/rdbms/exec/test/test-extension/TestBlockTypes.jv new file mode 100644 index 000000000..e050777aa --- /dev/null +++ b/libs/extensions/rdbms/exec/test/test-extension/TestBlockTypes.jv @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +builtin blocktype TestTableExtractor { + input inPort oftype None; + output outPort oftype Table; +} From f9b29188c97526776b1f26cf566095e3f8b8ae43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 13 Nov 2023 13:20:32 +0100 Subject: [PATCH 27/30] Adjusted tests to table rowCount bugfix --- .../tabular/exec/src/lib/table-interpreter-executor.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts b/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts index ff21a70e4..feb52fac5 100644 --- a/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts +++ b/libs/extensions/tabular/exec/src/lib/table-interpreter-executor.spec.ts @@ -122,7 +122,7 @@ describe('Validation of TableInterpreterExecutor', () => { if (R.isOk(result)) { expect(result.right.ioType).toEqual(IOType.TABLE); expect(result.right.getNumberOfColumns()).toEqual(0); - expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getNumberOfRows()).toEqual(0); } }); @@ -139,7 +139,7 @@ describe('Validation of TableInterpreterExecutor', () => { if (R.isOk(result)) { expect(result.right.ioType).toEqual(IOType.TABLE); expect(result.right.getNumberOfColumns()).toEqual(0); - expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getNumberOfRows()).toEqual(0); } }); @@ -256,7 +256,7 @@ describe('Validation of TableInterpreterExecutor', () => { if (R.isOk(result)) { expect(result.right.ioType).toEqual(IOType.TABLE); expect(result.right.getNumberOfColumns()).toEqual(0); - expect(result.right.getNumberOfRows()).toEqual(16); + expect(result.right.getNumberOfRows()).toEqual(0); } }); From e6b224d3a84b4e06d21f2fbe868ed56ac88c3dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 20 Nov 2023 09:31:12 +0100 Subject: [PATCH 28/30] Fixed an jest ts type issue preventing jest.mock returns to use mockFn.mockRejectedValueOnce etc --- .../rdbms/exec/test/mocks/sqlite-loader-executor-mock.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/extensions/rdbms/exec/test/mocks/sqlite-loader-executor-mock.ts b/libs/extensions/rdbms/exec/test/mocks/sqlite-loader-executor-mock.ts index 888cce28b..566d379a7 100644 --- a/libs/extensions/rdbms/exec/test/mocks/sqlite-loader-executor-mock.ts +++ b/libs/extensions/rdbms/exec/test/mocks/sqlite-loader-executor-mock.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { BlockExecutorMock } from '@jvalue/jayvee-execution/test'; import * as sqlite3 from 'sqlite3'; -type MockedSqlite3Database = jest.Mocked>; +type MockedSqlite3Database = jest.Mocked; export class SQLiteLoaderExecutorMock implements BlockExecutorMock { private _sqliteClient: MockedSqlite3Database | undefined; @@ -26,7 +26,7 @@ export class SQLiteLoaderExecutorMock implements BlockExecutorMock { ) => void = defaultSQLiteMockRegistration, ) { // setup sqlite3 mock - this._sqliteClient = new sqlite3.Database('test'); + this._sqliteClient = new sqlite3.Database('test') as MockedSqlite3Database; registerMocks(this._sqliteClient); } restore() { From 0161f4a7a8c641bdebd2eea96f5ffb51aec78612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 20 Nov 2023 09:35:10 +0100 Subject: [PATCH 29/30] Added tests for SQLiteLoaderExecutor --- .../src/lib/sqlite-loader-executor.spec.ts | 200 ++++++++++++++++++ .../valid-sqlite-loader.jv | 16 ++ 2 files changed, 216 insertions(+) create mode 100644 libs/extensions/rdbms/exec/src/lib/sqlite-loader-executor.spec.ts create mode 100644 libs/extensions/rdbms/exec/test/assets/sqlite-loader-executor/valid-sqlite-loader.jv diff --git a/libs/extensions/rdbms/exec/src/lib/sqlite-loader-executor.spec.ts b/libs/extensions/rdbms/exec/src/lib/sqlite-loader-executor.spec.ts new file mode 100644 index 000000000..26613a35c --- /dev/null +++ b/libs/extensions/rdbms/exec/src/lib/sqlite-loader-executor.spec.ts @@ -0,0 +1,200 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import * as path from 'path'; + +import * as R from '@jvalue/jayvee-execution'; +import { + constructTable, + getTestExecutionContext, +} from '@jvalue/jayvee-execution/test'; +import { + BlockDefinition, + IOType, + PrimitiveValuetypes, + createJayveeServices, +} from '@jvalue/jayvee-language-server'; +import { + ParseHelperOptions, + expectNoParserAndLexerErrors, + loadTestExtensions, + parseHelper, + readJvTestAssetHelper, +} from '@jvalue/jayvee-language-server/test'; +import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; +import { NodeFileSystem } from 'langium/node'; +import * as sqlite3 from 'sqlite3'; + +import { SQLiteLoaderExecutor } from './sqlite-loader-executor'; + +type SqliteRunCallbackType = ( + result: sqlite3.RunResult, + err: Error | null, +) => void; +// eslint-disable-next-line no-var +var databaseMock: jest.Mock; +// eslint-disable-next-line no-var +var databaseRunMock: jest.Mock; +// eslint-disable-next-line no-var +var databaseCloseMock: jest.Mock; +jest.mock('sqlite3', () => { + databaseMock = jest.fn(); + databaseRunMock = jest.fn(); + databaseCloseMock = jest.fn(); + return { + Database: databaseMock, + }; +}); +function mockDatabaseDefault() { + const mockDB = { + close: databaseCloseMock, + run: databaseRunMock, + }; + databaseMock.mockImplementation(() => { + return mockDB; + }); +} + +describe('Validation of SQLiteLoaderExecutor', () => { + let parse: ( + input: string, + options?: ParseHelperOptions, + ) => Promise>; + + let locator: AstNodeLocator; + + const readJvTestAsset = readJvTestAssetHelper( + __dirname, + '../../test/assets/sqlite-loader-executor/', + ); + + async function parseAndExecuteExecutor( + input: string, + IOInput: R.Table, + ): Promise> { + const document = await parse(input, { validationChecks: 'all' }); + expectNoParserAndLexerErrors(document); + + const block = locator.getAstNode( + document.parseResult.value, + 'pipelines@0/blocks@1', + ) as BlockDefinition; + + return new SQLiteLoaderExecutor().doExecute( + IOInput, + getTestExecutionContext(locator, document, [block]), + ); + } + + beforeAll(async () => { + // Create language services + const services = createJayveeServices(NodeFileSystem).Jayvee; + await loadTestExtensions(services, [ + path.resolve(__dirname, '../../test/test-extension/TestBlockTypes.jv'), + ]); + locator = services.workspace.AstNodeLocator; + // Parse function for Jayvee (without validation) + parse = parseHelper(services); + }); + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should diagnose no error on valid loader config', async () => { + mockDatabaseDefault(); + databaseRunMock.mockImplementation( + (sql: string, callback: SqliteRunCallbackType) => { + callback( + { + lastID: 0, + changes: 0, + } as sqlite3.RunResult, + null, + ); + return this; + }, + ); + const text = readJvTestAsset('valid-sqlite-loader.jv'); + + const inputTable = constructTable( + [ + { + columnName: 'Column1', + column: { + values: ['value 1'], + valuetype: PrimitiveValuetypes.Text, + }, + }, + { + columnName: 'Column2', + column: { + values: [20.2], + valuetype: PrimitiveValuetypes.Decimal, + }, + }, + ], + 1, + ); + const result = await parseAndExecuteExecutor(text, inputTable); + + expect(R.isErr(result)).toEqual(false); + if (R.isOk(result)) { + expect(result.right.ioType).toEqual(IOType.NONE); + expect(databaseRunMock).toBeCalledTimes(3); + expect(databaseRunMock).nthCalledWith( + 1, + 'DROP TABLE IF EXISTS "Test";', + expect.any(Function), + ); + expect(databaseRunMock).nthCalledWith( + 2, + `CREATE TABLE IF NOT EXISTS "Test" ("Column1" text,"Column2" real);`, + expect.any(Function), + ); + expect(databaseRunMock).nthCalledWith( + 3, + `INSERT INTO "Test" ("Column1","Column2") VALUES ('value 1',20.2)`, + expect.any(Function), + ); + expect(databaseCloseMock).toBeCalledTimes(1); + } + }); + + it('should diagnose error on sqlite database open error', async () => { + databaseMock.mockImplementation(() => { + throw new Error('File not found'); + }); + const text = readJvTestAsset('valid-sqlite-loader.jv'); + + const inputTable = constructTable( + [ + { + columnName: 'Column1', + column: { + values: ['value 1'], + valuetype: PrimitiveValuetypes.Text, + }, + }, + { + columnName: 'Column2', + column: { + values: [20.2], + valuetype: PrimitiveValuetypes.Decimal, + }, + }, + ], + 1, + ); + const result = await parseAndExecuteExecutor(text, inputTable); + + expect(R.isOk(result)).toEqual(false); + if (R.isErr(result)) { + expect(result.left.message).toEqual( + 'Could not write to sqlite database: File not found', + ); + expect(databaseRunMock).toBeCalledTimes(0); + expect(databaseCloseMock).toBeCalledTimes(0); + } + }); +}); diff --git a/libs/extensions/rdbms/exec/test/assets/sqlite-loader-executor/valid-sqlite-loader.jv b/libs/extensions/rdbms/exec/test/assets/sqlite-loader-executor/valid-sqlite-loader.jv new file mode 100644 index 000000000..3b9354b53 --- /dev/null +++ b/libs/extensions/rdbms/exec/test/assets/sqlite-loader-executor/valid-sqlite-loader.jv @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +pipeline TestPipeline { + + block TestExtractor oftype TestFileExtractor { + } + + block TestBlock oftype SQLiteLoader { + table: "Test"; + file: "./test.db"; + } + + TestExtractor -> TestBlock; +} From 8206d8ff8d07f053f7fd3b68c3be04fea6f37b91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20M=C3=BCller?= Date: Mon, 20 Nov 2023 09:36:25 +0100 Subject: [PATCH 30/30] Refactored to match SQLiteLoaderExecutor tests --- .../src/lib/postgres-loader-executor.spec.ts | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/libs/extensions/rdbms/exec/src/lib/postgres-loader-executor.spec.ts b/libs/extensions/rdbms/exec/src/lib/postgres-loader-executor.spec.ts index 56785beb2..2b7c9a0c6 100644 --- a/libs/extensions/rdbms/exec/src/lib/postgres-loader-executor.spec.ts +++ b/libs/extensions/rdbms/exec/src/lib/postgres-loader-executor.spec.ts @@ -24,15 +24,23 @@ import { } from '@jvalue/jayvee-language-server/test'; import { AstNode, AstNodeLocator, LangiumDocument } from 'langium'; import { NodeFileSystem } from 'langium/node'; -import { Client } from 'pg'; import { PostgresLoaderExecutor } from './postgres-loader-executor'; +// eslint-disable-next-line no-var +var databaseConnectMock: jest.Mock; +// eslint-disable-next-line no-var +var databaseQueryMock: jest.Mock; +// eslint-disable-next-line no-var +var databaseEndMock: jest.Mock; jest.mock('pg', () => { + databaseConnectMock = jest.fn(); + databaseQueryMock = jest.fn(); + databaseEndMock = jest.fn(); const mClient = { - connect: jest.fn(), - query: jest.fn(), - end: jest.fn(), + connect: databaseConnectMock, + query: databaseQueryMock, + end: databaseEndMock, }; return { Client: jest.fn(() => mClient) }; }); @@ -44,7 +52,6 @@ describe('Validation of PostgresLoaderExecutor', () => { ) => Promise>; let locator: AstNodeLocator; - let pgClient: jest.Mocked; const readJvTestAsset = readJvTestAssetHelper( __dirname, @@ -79,9 +86,6 @@ describe('Validation of PostgresLoaderExecutor', () => { // Parse function for Jayvee (without validation) parse = parseHelper(services); }); - beforeEach(() => { - pgClient = new Client() as jest.Mocked; - }); afterEach(() => { jest.clearAllMocks(); }); @@ -113,17 +117,20 @@ describe('Validation of PostgresLoaderExecutor', () => { expect(R.isErr(result)).toEqual(false); if (R.isOk(result)) { expect(result.right.ioType).toEqual(IOType.NONE); - expect(pgClient.connect).toBeCalledTimes(1); - expect(pgClient.query).nthCalledWith(1, 'DROP TABLE IF EXISTS "Test";'); - expect(pgClient.query).nthCalledWith( + expect(databaseConnectMock).toBeCalledTimes(1); + expect(databaseQueryMock).nthCalledWith( + 1, + 'DROP TABLE IF EXISTS "Test";', + ); + expect(databaseQueryMock).nthCalledWith( 2, `CREATE TABLE IF NOT EXISTS "Test" ("Column1" text,"Column2" real);`, ); - expect(pgClient.query).nthCalledWith( + expect(databaseQueryMock).nthCalledWith( 3, `INSERT INTO "Test" ("Column1","Column2") VALUES ('value 1',20.2)`, ); - expect(pgClient.end).toBeCalledTimes(1); + expect(databaseEndMock).toBeCalledTimes(1); } }); @@ -149,7 +156,7 @@ describe('Validation of PostgresLoaderExecutor', () => { ], 1, ); - pgClient.connect.mockImplementation(() => { + databaseConnectMock.mockImplementation(() => { throw new Error('Connection error'); }); const result = await parseAndExecuteExecutor(text, inputTable); @@ -159,9 +166,9 @@ describe('Validation of PostgresLoaderExecutor', () => { expect(result.left.message).toEqual( 'Could not write to postgres database: Connection error', ); - expect(pgClient.connect).toBeCalledTimes(1); - expect(pgClient.query).toBeCalledTimes(0); - expect(pgClient.end).toBeCalledTimes(1); + expect(databaseConnectMock).toBeCalledTimes(1); + expect(databaseQueryMock).toBeCalledTimes(0); + expect(databaseEndMock).toBeCalledTimes(1); } }); });