From 97d7548775d764dc8f23e72f55631e2a31233883 Mon Sep 17 00:00:00 2001 From: artmove57 Date: Sun, 26 Apr 2026 23:12:56 +0300 Subject: [PATCH 1/3] Add API and mocked API registration tests with coverage/runtime notes --- README.md | 25 ++++++++++++++++- app.mock.test.js | 70 +++++++++++++++++++++++++++++++----------------- app.test.js | 70 +++++++++++++++++++++++++++++++----------------- 3 files changed, 116 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index c3d6d34..68b1145 100644 --- a/README.md +++ b/README.md @@ -54,4 +54,27 @@ Jest documentation: * Käivita: `npm test -- app.mock.test.js` * Lisa coverage info * Lisa testide jooksuaja info - * Võrdle jooksumisaega (Ülesanne 1 vs Ülesanne 2) \ No newline at end of file + * Võrdle jooksumisaega (Ülesanne 1 vs Ülesanne 2) + +## Test Results + +### Ülesanne 1: API testid (`app.test.js`) + +- Test Suites: `1 passed, 1 total` +- Tests: `6 passed, 6 total` +- Coverage (All files): `100% statements`, `100% branches`, `100% functions`, `100% lines` +- Runtime: approximately `12s+` + +Note: `validateEmail` has an intentional ~2 second busy wait per request, so integration tests without mocking are significantly slower. + +### Ülesanne 2: Mocked API testid (`app.mock.test.js`) + +- Test Suites: `1 passed, 1 total` +- Tests: `6 passed, 6 total` +- Coverage (All files): `100% statements`, `100% branches`, `100% functions`, `100% lines` +- Runtime: `1.968s` + +### Runtime Comparison + +- Non-mocked API tests are much slower because real email validation blocks ~2 seconds on every request. +- Mocked API tests remove that delay and run about `6x` faster in this project setup. \ No newline at end of file diff --git a/app.mock.test.js b/app.mock.test.js index 79b9449..6bff9bd 100644 --- a/app.mock.test.js +++ b/app.mock.test.js @@ -16,44 +16,66 @@ jest.mock('./validation/validateEmail', () => { const validateEmail = require('./validation/validateEmail') const app = createApp(validateUsername, validatePassword, validateEmail) -describe('given correct username and password', () => { - test('return status 200', async () => { +const validPayload = { + username: 'User.Name99', + password: 'Password123', + email: 'student@example.com' +} + +describe('POST /users', () => { + test('returns success response for valid user', async () => { + const response = await request(app).post('/users').send(validPayload) + + expect(response.statusCode).toBe(200) + expect(response.body).toEqual({ userId: '1', message: 'Valid User' }) + }) + + test('returns status 400 when username is shorter than 6 characters', async () => { const response = await request(app).post('/users').send({ - username: 'Username', - password: 'Password123', - email: 'student@example.com' + ...validPayload, + username: 'user1' }) - expect(response.statusCode).toBe(200) + + expect(response.statusCode).toBe(400) + expect(response.body).toEqual({ error: 'Invalid User' }) }) - test('returns userId', async () => { + test('returns status 400 when username contains disallowed characters', async () => { const response = await request(app).post('/users').send({ - username: 'Username', - password: 'Password123', - email: 'student@example.com' + ...validPayload, + username: 'User_name' }) - expect(response.body.userId).toBeDefined(); + + expect(response.statusCode).toBe(400) }) - // test response content type? - // test response message - // test response user id value - // ... -}) + test('returns status 400 when password does not meet complexity rules', async () => { + const response = await request(app).post('/users').send({ + ...validPayload, + password: 'password' + }) + + expect(response.statusCode).toBe(400) + }) + + test('returns status 400 when email has no at-symbol', async () => { + const response = await request(app).post('/users').send({ + ...validPayload, + email: 'student.example.com' + }) + + expect(response.statusCode).toBe(400) + }) -describe('given incorrect or missing username and password', () => { - test('return status 400', async () => { + test('returns only error payload for invalid user', async () => { const response = await request(app).post('/users').send({ username: 'user', password: 'password', email: 'not-an-email' }) + expect(response.statusCode).toBe(400) + expect(response.body).toEqual({ error: 'Invalid User' }) + expect(response.body.userId).toBeUndefined() }) - - // test response message - // test that response does NOT have userId - // test incorrect username or password according to requirements - // test missing username or password - // ... }) \ No newline at end of file diff --git a/app.test.js b/app.test.js index f1b561d..b335fa3 100644 --- a/app.test.js +++ b/app.test.js @@ -6,44 +6,66 @@ const validateEmail = require('./validation/validateEmail') const app = createApp(validateUsername, validatePassword, validateEmail) -describe('given correct username and password', () => { - test('return status 200', async () => { +const validPayload = { + username: 'User.Name99', + password: 'Password123', + email: 'student@example.com' +} + +describe('POST /users', () => { + test('returns success response for valid user', async () => { + const response = await request(app).post('/users').send(validPayload) + + expect(response.statusCode).toBe(200) + expect(response.body).toEqual({ userId: '1', message: 'Valid User' }) + }) + + test('returns status 400 when username is shorter than 6 characters', async () => { const response = await request(app).post('/users').send({ - username: 'Username', - password: 'Password123', - email: 'student@example.com' + ...validPayload, + username: 'user1' }) - expect(response.statusCode).toBe(200) + + expect(response.statusCode).toBe(400) + expect(response.body).toEqual({ error: 'Invalid User' }) }) - test('returns userId', async () => { + test('returns status 400 when username contains disallowed characters', async () => { const response = await request(app).post('/users').send({ - username: 'Username', - password: 'Password123', - email: 'student@example.com' + ...validPayload, + username: 'User_name' }) - expect(response.body.userId).toBeDefined(); + + expect(response.statusCode).toBe(400) }) - // test response content type? - // test response message - // test response user id value - // ... -}) + test('returns status 400 when password does not meet complexity rules', async () => { + const response = await request(app).post('/users').send({ + ...validPayload, + password: 'password' + }) -describe('given incorrect or missing username and password', () => { - test('return status 400', async () => { + expect(response.statusCode).toBe(400) + }) + + test('returns status 400 when email has no at-symbol', async () => { + const response = await request(app).post('/users').send({ + ...validPayload, + email: 'student.example.com' + }) + + expect(response.statusCode).toBe(400) + }) + + test('returns only error payload for invalid user', async () => { const response = await request(app).post('/users').send({ username: 'user', password: 'password', email: 'not-an-email' }) + expect(response.statusCode).toBe(400) + expect(response.body).toEqual({ error: 'Invalid User' }) + expect(response.body.userId).toBeUndefined() }) - - // test response message - // test that response does NOT have userId - // test incorrect username or password according to requirements - // test missing username or password - // ... }) \ No newline at end of file From 810cabdab7c1d41122ae4f1078d8209c1673b81b Mon Sep 17 00:00:00 2001 From: artmove57 Date: Sun, 26 Apr 2026 23:16:40 +0300 Subject: [PATCH 2/3] docs: refresh mocked test runtime after verification --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 68b1145..54cf189 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ Note: `validateEmail` has an intentional ~2 second busy wait per request, so int - Test Suites: `1 passed, 1 total` - Tests: `6 passed, 6 total` - Coverage (All files): `100% statements`, `100% branches`, `100% functions`, `100% lines` -- Runtime: `1.968s` +- Runtime: `1.704s` ### Runtime Comparison From a37289451c76ca7218f950f32c16b6cb983e5b3b Mon Sep 17 00:00:00 2001 From: artmove57 Date: Sun, 26 Apr 2026 23:18:39 +0300 Subject: [PATCH 3/3] chore: save current workspace changes --- package-lock.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index f07de70..f5a8977 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,6 +48,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1211,6 +1212,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1566,6 +1568,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -2131,6 +2134,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.35.0.tgz", "integrity": "sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw==", "dev": true, + "peer": true, "dependencies": { "@eslint/eslintrc": "^2.0.0", "@eslint/js": "8.35.0", @@ -5920,6 +5924,7 @@ "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "peer": true, "requires": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -6749,7 +6754,8 @@ "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true + "dev": true, + "peer": true }, "acorn-jsx": { "version": "5.3.2", @@ -6999,6 +7005,7 @@ "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "peer": true, "requires": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -7393,6 +7400,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.35.0.tgz", "integrity": "sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw==", "dev": true, + "peer": true, "requires": { "@eslint/eslintrc": "^2.0.0", "@eslint/js": "8.35.0",