diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..17a06f41 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,39 @@ +name: Tests + +on: + push: + branches: [main] + pull_request: + types: [opened, synchronize, reopened, auto_merge_enabled] + +# Allow parallel jobs on `main`, so that each commit is tested. For PRs, run only the latest commit. +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + test: + runs-on: ubuntu-latest + name: Node.js ${{ matrix.node }} + + strategy: + fail-fast: true + matrix: + node: [20, 18] + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + + - run: npm i + #replicating calesi folder structure + - run: cd .. && mkdir calesi + - run: cd ../calesi && mkdir plugins && cd plugins && mkdir attachments + - run: cp -r ./* ../calesi/plugins/attachments + - run: cd ../calesi && git clone https://github.com/cap-js/incidents-app.git && cd incidents-app && npm install --save ../plugins/attachments/ && cp -r xmpls/attachments.cds ./db && npm i + - run: cd ../calesi/plugins/attachments && npm run test + env: + FORCE_COLOR: true + - run: rm calesi diff --git a/lib/basic.js b/lib/basic.js index 49619a19..76677c02 100644 --- a/lib/basic.js +++ b/lib/basic.js @@ -76,16 +76,16 @@ module.exports = class AttachmentsService extends cds.Service { } async update(Attachments, key, data) { - DEBUG?.("Updating attachment for", Attachments.name, key) - return await UPDATE(Attachments, key).with(data) + DEBUG?.("Updating attachment for", Attachments.name, key); + return await UPDATE(Attachments, key).with(data); } async getStatus(Attachments, key) { - const result = await SELECT.from(Attachments, key).columns('status') - return result.status + const result = await SELECT.from(Attachments, key).columns('status'); + return result?.status; } async deleteInfectedAttachment(Attachments, key) { - return await UPDATE(Attachments, key).with({ content: null}) + return await UPDATE(Attachments, key).with({ content: null}); } }; diff --git a/lib/plugin.js b/lib/plugin.js index 491a781b..ae8639a6 100644 --- a/lib/plugin.js +++ b/lib/plugin.js @@ -59,6 +59,7 @@ cds.once("served", async function registerPluginHandlers() { if(req._path?.endsWith("content")) { const status = await AttachmentsSrv.getStatus(req.target, req.params.at(-1)); const scanEnabled = cds.env.requires?.attachments?.scan ?? true + //TODO: add error in case content doesn't exist if(scanEnabled && status !== 'Clean') { req.reject(403, 'Unable to download the attachment as scan status is not clean.'); } diff --git a/package.json b/package.json index 568126b4..4debe1c2 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "srv" ], "scripts": { - "lint": "npx eslint ." + "lint": "npx eslint .", + "test": "npx jest attachments.test.js" }, "dependencies": { "@aws-sdk/client-s3": "^3.400.0", diff --git a/tests/integration/attachments.test.js b/tests/integration/attachments.test.js index a27f8ed1..3f96a6b6 100644 --- a/tests/integration/attachments.test.js +++ b/tests/integration/attachments.test.js @@ -1,5 +1,6 @@ const cds = require("@sap/cds"); -const incidentsApp = require("path").resolve(__dirname, "./../../xmpl"); +const path = require("path"); +const incidentsApp = path.resolve(__dirname, "./../../../../incidents-app"); const { expect, axios, GET, POST, DELETE } = cds.test(incidentsApp); const { RequestSend } = require("../utils/api"); const { createReadStream } = cds.utils.fs; @@ -8,54 +9,21 @@ const { join } = cds.utils.path; axios.defaults.auth = { username: "alice" }; jest.setTimeout(5 * 60 * 1000); -const utils = new RequestSend(POST); +let utils = null; let sampleDocID = null; let incidentID = null; - -describe("Tests for mock data in xmpl attachments - in-memory db", () => { - beforeAll(() => { - sampleDocID = null; - incidentID = "3b23bb4b-4ac7-4a24-ac02-aa10cabd842c"; - }); - - //Reading the attachment list and checking for content - it("Reading attachments list", async () => { - //read attachments list for Incident - Inverter not functional - try { - const response = await GET( - `odata/v4/processor/Incidents(ID=${incidentID},IsActiveEntity=true)/attachments` - ); - //the mock data has two attachments in this incident - expect(response.status).to.equal(200); - expect(response.data.value.length).to.equal(2); - sampleDocID = response.data.value[0].ID; - //to make sure content is not read - expect(response.data.value[0].content).to.be.undefined; - } catch (err) { - expect(err).to.be.undefined; - } - }); - - //Reading the uploaded attachment content and that it exists - it("Reading the uploaded attachment document", async () => { - //checking the uploaded attachment document - - try { - const response = await GET( - `odata/v4/processor/Incidents(ID=${incidentID},IsActiveEntity=true)/attachments(up__ID=${incidentID},ID=${sampleDocID},IsActiveEntity=true)/content` - ); - expect(response.status).to.equal(200); - expect(response.data).to.not.be.undefined; - } catch (err) { - expect(err).to.be.undefined; - } - }); -}); +let db = null; +let attachmentsService = null; describe("Tests for uploading/deleting attachments through API calls - in-memory db", () => { beforeAll(async () => { + cds.env.requires.db.kind = "sql" + cds.env.requires.attachments.kind = "db"; + db = await cds.connect.to("sql:my.db"); + attachmentsService = await cds.connect.to("attachments"); sampleDocID = null; incidentID = "3ccf474c-3881-44b7-99fb-59a2a4668418"; + utils = new RequestSend(POST); }); //Draft mode uploading attachment @@ -72,7 +40,7 @@ describe("Tests for uploading/deleting attachments through API calls - in-memory createdAt: new Date( Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000 ), - createdBy: "alice", + createdBy: "alice" } ); @@ -96,13 +64,10 @@ describe("Tests for uploading/deleting attachments through API calls - in-memory ); //the data should have two attachments expect(response.status).to.equal(200); - expect(response.data.value.length).to.equal(2); + expect(response.data.value.length).to.equal(1); //to make sure content is not read expect(response.data.value[0].content).to.be.undefined; - sampleDocID = - response.data.value[0].filename == "sample.pdf" - ? response.data.value[0].ID - : response.data.value[1].ID; + sampleDocID = response.data.value[0].ID } catch (err) { expect(err).to.be.undefined; } diff --git a/tests/integration/content/attachments.cds b/tests/integration/content/attachments.cds new file mode 100644 index 00000000..be09e086 --- /dev/null +++ b/tests/integration/content/attachments.cds @@ -0,0 +1,6 @@ +using { sap.capire.incidents as my } from './schema'; +using { Attachments } from '@cap-js/attachments'; + +extend my.Incidents with { + attachments: Composition of many Attachments; +}