diff --git a/.gitea/workflows/compile.yaml b/.gitea/workflows/compile.yaml index 2fa0821..457a567 100644 --- a/.gitea/workflows/compile.yaml +++ b/.gitea/workflows/compile.yaml @@ -14,4 +14,4 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - name: Build Container - run: docker build . + run: docker build --target compile . diff --git a/.gitea/workflows/docker-build.yaml b/.gitea/workflows/docker-build.yaml index c2abff3..09c917d 100644 --- a/.gitea/workflows/docker-build.yaml +++ b/.gitea/workflows/docker-build.yaml @@ -21,7 +21,7 @@ jobs: - name: Log in to the Container registry run: docker login -u ${{ env.USER }} -p ${{ secrets.TOKEN }} ${{ env.REGISTRY }} - name: Build Container - run: docker build -t "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" -t "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }}" . + run: docker build --target compile -t "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" -t "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }}" . env: version: $(cat package.json | awk 'match($0, /version/) {print $2}' | sed 's/[\",]//g') # extracts the version number from the package.json with bash magic - name: Push Container diff --git a/.gitea/workflows/test.yaml b/.gitea/workflows/test.yaml new file mode 100644 index 0000000..0fadd23 --- /dev/null +++ b/.gitea/workflows/test.yaml @@ -0,0 +1,18 @@ +name: Run unit tests +on: [pull_request] +env: + REGISTRY: gitea.brudi.xyz + IMAGE_NAME: ${{ gitea.repository }} + USER: ${{ gitea.actor }} +jobs: + test: + runs-on: ubuntu-latest + container: catthehacker/ubuntu:act-latest + permissions: + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Run Tests + run: docker build --target test . + diff --git a/Dockerfile b/Dockerfile index e765732..086648b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,21 @@ -FROM node:alpine as Build -ENV NODE_ENV=production +FROM node:alpine as files +ENV TZ="Europe/Berlin" WORKDIR /app - COPY [ "package-lock.json", "package.json", "index.ts", "tsconfig.json", "./" ] COPY server ./server +FROM files as proddependencies +ENV NODE_ENV=production RUN npm ci --omit=dev +FROM proddependencies as compile RUN npm run build CMD ["npm","run","start"] + +FROM files as dependencies +RUN npm ci + +FROM dependencies as test +COPY jest.config.js . +COPY tests ./tests +RUN npm run test diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..4086ac8 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,18 @@ +module.exports = { + 'roots': [ + '/tests', + '/server' + ], + 'transform': { + '^.+\\.tsx?$': 'ts-jest' + }, + 'testRegex': '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$', + 'moduleFileExtensions': [ + 'ts', + 'tsx', + 'js', + 'jsx', + 'json', + 'node' + ], +}; diff --git a/package-lock.json b/package-lock.json index f7c99e8..ef82566 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,12 +30,13 @@ "winston": "^3.8.2" }, "devDependencies": { - "@types/jest": "^29.5.0", + "@types/jest": "^29.5.2", "@typescript-eslint/eslint-plugin": "^5.58.0", "@typescript-eslint/parser": "^5.58.0", "eslint": "^8.38.0", "jest": "^29.5.0", "jest-cli": "^29.5.0", + "mockdate": "^3.0.5", "nodemon": "^2.0.22", "rimraf": "^5.0.0", "ts-jest": "^29.1.0" @@ -1568,9 +1569,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz", - "integrity": "sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==", + "version": "29.5.2", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.2.tgz", + "integrity": "sha512-mSoZVJF5YzGVCk+FsDxzDuH7s+SCkzrgKZzf0Z0T2WudhBUPoF6ktoTPC4R0ZoCPCV5xUvuU6ias5NvxcBcMMg==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -4989,6 +4990,12 @@ "node": ">=10" } }, + "node_modules/mockdate": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/mockdate/-/mockdate-3.0.5.tgz", + "integrity": "sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ==", + "dev": true + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -8139,9 +8146,9 @@ } }, "@types/jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz", - "integrity": "sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==", + "version": "29.5.2", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.2.tgz", + "integrity": "sha512-mSoZVJF5YzGVCk+FsDxzDuH7s+SCkzrgKZzf0Z0T2WudhBUPoF6ktoTPC4R0ZoCPCV5xUvuU6ias5NvxcBcMMg==", "dev": true, "requires": { "expect": "^29.0.0", @@ -10720,6 +10727,12 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, + "mockdate": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/mockdate/-/mockdate-3.0.5.tgz", + "integrity": "sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ==", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/package.json b/package.json index d4f0a57..715ae52 100644 --- a/package.json +++ b/package.json @@ -33,15 +33,18 @@ "debuggable": "node build/index.js --inspect-brk", "monitor": "nodemon build/index.js", "lint": "eslint . --ext .ts", - "lint-fix": "eslint . --ext .ts --fix" + "lint-fix": "eslint . --ext .ts --fix", + "test": "jest", + "test-watch": "jest --watch" }, "devDependencies": { - "@types/jest": "^29.5.0", + "@types/jest": "^29.5.2", "@typescript-eslint/eslint-plugin": "^5.58.0", "@typescript-eslint/parser": "^5.58.0", "eslint": "^8.38.0", "jest": "^29.5.0", "jest-cli": "^29.5.0", + "mockdate": "^3.0.5", "nodemon": "^2.0.22", "rimraf": "^5.0.0", "ts-jest": "^29.1.0" diff --git a/tests/helpers/date.test.ts b/tests/helpers/date.test.ts new file mode 100644 index 0000000..52f7ff9 --- /dev/null +++ b/tests/helpers/date.test.ts @@ -0,0 +1,16 @@ +import { GuildScheduledEvent } from "discord.js" +import { createDateStringFromEvent } from "../../server/helper/dateHelper" +import MockDate from 'mockdate' + +beforeAll(() => { + MockDate.set('01-01-2023') +}) + +function getTestDate(date: string): GuildScheduledEvent { + return { scheduledStartAt: new Date(date) } +} +test('createDateStringFromEvent - correct formatting', () => { + expect(createDateStringFromEvent(getTestDate('01-01-2023 12:30'), "")).toEqual('heute um 12:30') + expect(createDateStringFromEvent(getTestDate('01-02-2023 12:30'), "")).toEqual('am Montag 02.01 um 12:30') + expect(createDateStringFromEvent(getTestDate('01-03-2023 12:30'), "")).toEqual('am Dienstag 03.01 um 12:30') +}) diff --git a/tests/helpers/memberRoles.test.ts b/tests/helpers/memberRoles.test.ts new file mode 100644 index 0000000..6ff3a9c --- /dev/null +++ b/tests/helpers/memberRoles.test.ts @@ -0,0 +1,28 @@ +import { Collection, GuildMember, Role } from "discord.js" +import { filterRolesFromMemberUpdate } from "../../server/helper/roleFilter" + +function buildFakeRole(id: string, name: string): Role { + return { id, name } + +} +test('filterRolesFromMemberUpdate', () => { + const oldMemberRoles: Collection = new Collection() + oldMemberRoles.set('1', buildFakeRole('01', 'Role01')) + oldMemberRoles.set('2', buildFakeRole('02', 'Role02')) + + const newMemberRoles: Collection = new Collection() + newMemberRoles.set('1', buildFakeRole('01', 'Role01')) + newMemberRoles.set('2', buildFakeRole('02', 'Role02')) + newMemberRoles.set('3', buildFakeRole('03', 'Role03')) + + const oldMember: GuildMember = { roles: { cache: oldMemberRoles }, guild: { id: "guildid" } } + const newMember: GuildMember = { roles: { cache: newMemberRoles }, guild: { id: "guildid" } } + const output = filterRolesFromMemberUpdate(oldMember, newMember) + + const expectedAddedRoles: Collection = new Collection() + expectedAddedRoles.set('3', buildFakeRole('03', 'Role03')) + const expectedRemovedRoles: Collection = new Collection() + + expect(output.addedRoles).toEqual(expectedAddedRoles) + expect(output.removedRoles).toEqual(expectedRemovedRoles) +})