From bb4fcd50394ded5c2c2320ff7a843c843cd2e3a2 Mon Sep 17 00:00:00 2001 From: VictorG265 Date: Mon, 22 Dec 2025 15:26:59 +0200 Subject: [PATCH] feat: Add user creation endpoint --- .gitlab-ci.yml | 121 +++++++++++++++++++++++++++++++++++-- app.js | 23 +++++++ package.json | 5 +- tests/integration.tests.js | 38 ++++++++++++ tests/user.test.js | 31 ++++++++++ 5 files changed, 212 insertions(+), 6 deletions(-) create mode 100644 tests/integration.tests.js create mode 100644 tests/user.test.js diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5f1cc6b..64bd8af 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -54,17 +54,128 @@ build_docker: - develop - merge_requests -# Deploy to staging (manual) +# Deploy to staging deploy_staging: stage: deploy image: alpine:latest script: - - echo "Deploying to staging environment..." - - echo "Application version: $CI_COMMIT_SHA" - - echo "Deployed at: $(date)" + - echo "Deploying to staging..." + - echo "Environment: staging" + - echo "Version: $CI_COMMIT_SHA" + - echo "Branch: $CI_COMMIT_REF_NAME" + - echo "Author: $GITLAB_USER_NAME" environment: name: staging url: https://staging.example.com + on_stop: stop_staging when: manual only: - - main \ No newline at end of file + - main + +# Stop staging environment +stop_staging: + stage: deploy + image: alpine:latest + script: + - echo "Stopping staging environment..." + environment: + name: staging + action: stop + when: manual + only: + - main + +# Deploy to production +deploy_production: + stage: deploy + image: alpine:latest + script: + - echo "Deploying to production..." + - echo "Environment: production" + - echo "Version: $CI_COMMIT_SHA" + environment: + name: production + url: https://production.example.com + when: manual + only: + - tags + dependencies: + - build_docker + + stages: + - install + - lint + - test + - build + - deploy + +# Lint code +lint: + stage: lint + image: node:${NODE_VERSION} + dependencies: + - install_dependencies + script: + - npm run lint + allow_failure: false + + # Security scan +security_scan: + stage: test + image: node:${NODE_VERSION} + dependencies: + - install_dependencies + script: + - npm audit --audit-level moderate + allow_failure: true + only: + - main + - merge_requests + +# Integration tests +integration_tests: + stage: test + image: node:${NODE_VERSION} + dependencies: + - install_dependencies + script: + - npm run test:integration + artifacts: + reports: + junit: junit-integration.xml + paths: + - coverage-integration/ + expire_in: 1 week + only: + - main + - merge_requests + + cache: + paths: + - node_modules/ + - .npm/ + key: + files: + - package-lock.json + policy: pull-push + +test: + stage: test + image: node:${NODE_VERSION} + dependencies: + - install_dependencies + script: + - npm test + coverage: '/Coverage: \d+\.\d+%/' + artifacts: + when: always + reports: + junit: junit.xml + coverage_report: + coverage_format: cobertura + path: coverage/cobertura-coverage.xml + paths: + - coverage/ + - junit.xml + expire_in: 30 days + coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/' \ No newline at end of file diff --git a/app.js b/app.js index b39090c..42b5df3 100644 --- a/app.js +++ b/app.js @@ -48,4 +48,27 @@ app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); }); +// Add user endpoint +app.post('/api/users', (req, res) => { + const { name, email } = req.body; + + if (!name || !email) { + return res.status(400).json({ + error: 'Name and email are required' + }); + } + + const newUser = { + id: Date.now(), + name, + email, + createdAt: new Date().toISOString() + }; + + res.status(201).json({ + message: 'User created successfully', + user: newUser + }); +}); + module.exports = app; \ No newline at end of file diff --git a/package.json b/package.json index bf91709..bdf6afc 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,8 @@ "scripts": { "start": "node app.js", "test": "jest", + "test:integration": "jest --testPathPattern=integration", + "test:unit": "jest --testPathPattern=unit", "test:watch": "jest --watch", "lint": "eslint .", "build": "npm run lint && npm test" @@ -21,4 +23,5 @@ "jest": { "testEnvironment": "node" } -} \ No newline at end of file +} + diff --git a/tests/integration.tests.js b/tests/integration.tests.js new file mode 100644 index 0000000..74519d8 --- /dev/null +++ b/tests/integration.tests.js @@ -0,0 +1,38 @@ +const request = require('supertest'); +const app = require('../app'); + +describe('Integration Tests', () => { + let server; + + beforeAll((done) => { + server = app.listen(0, () => { + done(); + }); + }); + + afterAll((done) => { + server.close(done); + }); + + test('should handle concurrent requests', async () => { + const promises = []; + for (let i = 0; i < 10; i++) { + promises.push(request(app).get('/api/data')); + } + + const responses = await Promise.all(promises); + + responses.forEach(response => { + expect(response.status).toBe(200); + expect(response.body).toHaveProperty('data'); + }); + }); + + test('should maintain data consistency', async () => { + const response1 = await request(app).get('/api/data'); + const response2 = await request(app).get('/api/data'); + + expect(response1.body.total).toBe(response2.body.total); + expect(response1.body.data.length).toBe(response2.body.data.length); + }); +}); \ No newline at end of file diff --git a/tests/user.test.js b/tests/user.test.js new file mode 100644 index 0000000..6f469fd --- /dev/null +++ b/tests/user.test.js @@ -0,0 +1,31 @@ +const request = require('supertest'); +const app = require('../app'); + +describe('User Endpoints', () => { + test('POST /api/users should create a new user', async () => { + const userData = { + name: 'John Doe', + email: 'john@example.com' + }; + + const response = await request(app) + .post('/api/users') + .send(userData) + .expect('Content-Type', /json/) + .expect(201); + + expect(response.body).toHaveProperty('message', 'User created successfully'); + expect(response.body).toHaveProperty('user'); + expect(response.body.user).toHaveProperty('name', userData.name); + expect(response.body.user).toHaveProperty('email', userData.email); + }); + + test('POST /api/users should return 400 for missing data', async () => { + const response = await request(app) + .post('/api/users') + .send({ name: 'John Doe' }) + .expect(400); + + expect(response.body).toHaveProperty('error'); + }); +}); \ No newline at end of file -- GitLab