Update dependencies and fix tests
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npm test
|
||||
npx lint-staged
|
||||
pnpm test
|
||||
pnpx lint-staged
|
||||
|
||||
14
README.md
14
README.md
@@ -31,21 +31,21 @@ ENV NODE_ENV="production"
|
||||
|
||||
COPY package-lock.json package.json ./
|
||||
|
||||
RUN apk update && apk add python3=3.11.10-r1 build-base=0.5-r3 && npm ci
|
||||
RUN apk update && apk add python3=3.11.10-r1 build-base=0.5-r3 && pnpm i
|
||||
|
||||
COPY . .
|
||||
|
||||
CMD [ "npm", "start" ]
|
||||
CMD [ "pnpm", "start" ]
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
You can proceed [to building the application](https://docs.docker.com/get-started/workshop/02_our_app/#build-the-apps-image).
|
||||
|
||||
### npm
|
||||
### pnpm
|
||||
|
||||
```bash
|
||||
npm install -g soul-cli
|
||||
pnpm install -g @tabshift/soul-cli
|
||||
```
|
||||
|
||||
### 1. Running Soul
|
||||
@@ -180,8 +180,8 @@ git clone https://github.com/thevahidal/soul # Clone project
|
||||
cp .env.sample .env # Duplicate sample environment variables
|
||||
vim .env # Update the environment variables
|
||||
|
||||
npm install # Install dependencies
|
||||
npm run dev # Start the dev server
|
||||
pnpm install # Install dependencies
|
||||
pnpm run dev # Start the dev server
|
||||
```
|
||||
|
||||
## Testing
|
||||
@@ -193,7 +193,7 @@ npm run dev # Start the dev server
|
||||
5. Use the following command to run the tests:
|
||||
|
||||
```
|
||||
npm run test
|
||||
pnpm run test
|
||||
```
|
||||
|
||||
Make sure to replace the placeholders with the appropriate values for your environment.
|
||||
|
||||
8162
package-lock.json
generated
8162
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
36
package.json
36
package.json
@@ -28,33 +28,33 @@
|
||||
"homepage": "https://github.com/tabshift-gh/soul-server#readme",
|
||||
"dependencies": {
|
||||
"bcrypt": "^5.1.1",
|
||||
"better-sqlite3": "^8.1.0",
|
||||
"body-parser": "^1.20.2",
|
||||
"check-password-strength": "^2.0.7",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"better-sqlite3": "^12.4.1",
|
||||
"body-parser": "^1.20.3",
|
||||
"check-password-strength": "^2.0.10",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.0.3",
|
||||
"express": "^4.18.2",
|
||||
"express-rate-limit": "^6.7.0",
|
||||
"dotenv": "^16.6.1",
|
||||
"express": "^4.21.2",
|
||||
"express-rate-limit": "^6.11.2",
|
||||
"express-winston": "^4.2.0",
|
||||
"joi": "^17.8.3",
|
||||
"joi": "^17.13.3",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"soul-studio": "^0.0.1",
|
||||
"swagger-ui-express": "^4.6.1",
|
||||
"winston": "^3.8.2",
|
||||
"ws": "^8.12.1",
|
||||
"yargs": "^17.7.1"
|
||||
"swagger-ui-express": "^4.6.3",
|
||||
"winston": "^3.18.3",
|
||||
"ws": "^8.18.3",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-prettier": "^9.1.2",
|
||||
"husky": "^8.0.3",
|
||||
"jest": "^29.4.3",
|
||||
"nodemon": "^3.1.3",
|
||||
"jest": "^29.7.0",
|
||||
"nodemon": "^3.1.10",
|
||||
"prettier": "3.1.0",
|
||||
"supertest": "^6.3.3",
|
||||
"swagger-autogen": "^2.23.1"
|
||||
"supertest": "^6.3.4",
|
||||
"swagger-autogen": "^2.23.7"
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "node",
|
||||
|
||||
4792
pnpm-lock.yaml
generated
Normal file
4792
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
6
pnpm-workspace.yaml
Normal file
6
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
ignoredBuiltDependencies:
|
||||
- '@scarf/scarf'
|
||||
|
||||
onlyBuiltDependencies:
|
||||
- bcrypt
|
||||
- better-sqlite3
|
||||
@@ -1,364 +1,437 @@
|
||||
const supertest = require('supertest')
|
||||
const supertest = require("supertest");
|
||||
|
||||
const app = require('../index')
|
||||
const config = require('../config')
|
||||
const { generateToken } = require('../utils')
|
||||
const { testData } = require('../tests/testData')
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(global, "setInterval");
|
||||
const app = require("../index");
|
||||
const config = require("../config");
|
||||
const { generateToken } = require("../utils");
|
||||
const { testData } = require("../tests/testData");
|
||||
|
||||
const requestWithSupertest = supertest(app)
|
||||
const requestWithSupertest = supertest(app);
|
||||
|
||||
describe('Auth Endpoints', () => {
|
||||
describe('User Endpoints', () => {
|
||||
it('POST /tables/_users/rows should register a user', async () => {
|
||||
describe("Auth Endpoints", () => {
|
||||
if (!config.auth) return;
|
||||
describe("User Endpoints", () => {
|
||||
it("POST /tables/_users/rows should register a user", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', userId: 1, isSuperuser: true },
|
||||
{
|
||||
username: config.initialUserUsername,
|
||||
userId: 1,
|
||||
isSuperuser: true,
|
||||
},
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.post('/api/tables/_users/rows')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.post("/api/tables/_users/rows")
|
||||
.set("Cookie", [`accessToken=${accessToken}`])
|
||||
.send({
|
||||
fields: {
|
||||
username: testData.users.user1.username,
|
||||
password: testData.strongPassword
|
||||
}
|
||||
})
|
||||
username: testData.users.frozenUser1.username,
|
||||
password: testData.strongPassword,
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(201)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
expect(res.status).toEqual(201);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
|
||||
expect(res.body).toHaveProperty('message')
|
||||
expect(res.body.message).toBe('Row Inserted')
|
||||
expect(res.body).toHaveProperty("message");
|
||||
expect(res.body.message).toBe("Row Inserted");
|
||||
|
||||
expect(res.body).not.toHaveProperty('password')
|
||||
expect(res.body).not.toHaveProperty('hashed_password')
|
||||
expect(res.body).not.toHaveProperty('salt')
|
||||
})
|
||||
expect(res.body).not.toHaveProperty("password");
|
||||
expect(res.body).not.toHaveProperty("hashed_password");
|
||||
expect(res.body).not.toHaveProperty("salt");
|
||||
});
|
||||
|
||||
it('POST /tables/_users/rows should throw 400 error if username is not passed', async () => {
|
||||
it("POST /tables/_users/rows should throw 400 error if username is not passed", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: config.initialUserUsername, isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.post('/api/tables/_users/rows')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.post("/api/tables/_users/rows")
|
||||
.set("Cookie", [`accessToken=${accessToken}`])
|
||||
.send({
|
||||
fields: { password: testData.strongPassword }
|
||||
})
|
||||
fields: { password: testData.strongPassword },
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(400)
|
||||
expect(res.body.message).toBe('username is required')
|
||||
expect(res.status).toEqual(400);
|
||||
expect(res.body.message).toBe("username is required");
|
||||
|
||||
expect(res.body).not.toHaveProperty('password')
|
||||
expect(res.body).not.toHaveProperty('hashed_password')
|
||||
expect(res.body).not.toHaveProperty('salt')
|
||||
})
|
||||
expect(res.body).not.toHaveProperty("password");
|
||||
expect(res.body).not.toHaveProperty("hashed_password");
|
||||
expect(res.body).not.toHaveProperty("salt");
|
||||
});
|
||||
|
||||
it('POST /tables/_users/rows should throw 400 error if the password is not strong', async () => {
|
||||
it("POST /tables/_users/rows should throw 400 error if the password is not strong", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: config.initialUserUsername, isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.post('/api/tables/_users/rows')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.post("/api/tables/_users/rows")
|
||||
.set("Cookie", [`accessToken=${accessToken}`])
|
||||
.send({
|
||||
fields: {
|
||||
username: testData.users.user2.username,
|
||||
password: testData.weakPassword
|
||||
}
|
||||
})
|
||||
username: testData.users.weakPasswordUser.username,
|
||||
password: testData.weakPassword,
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(400)
|
||||
expect(res.status).toEqual(400);
|
||||
expect(res.body.message).toBe(
|
||||
'This password is weak, it should be at least 8 characters long and contain a combination of lowercase letters, uppercase letters, numbers, and special characters'
|
||||
)
|
||||
"This password is weak, it should be at least 8 characters long and contain a combination of lowercase letters, uppercase letters, numbers, and special characters",
|
||||
);
|
||||
|
||||
expect(res.body).not.toHaveProperty('password')
|
||||
expect(res.body).not.toHaveProperty('hashed_password')
|
||||
expect(res.body).not.toHaveProperty('salt')
|
||||
})
|
||||
expect(res.body).not.toHaveProperty("password");
|
||||
expect(res.body).not.toHaveProperty("hashed_password");
|
||||
expect(res.body).not.toHaveProperty("salt");
|
||||
});
|
||||
|
||||
it('POST /tables/_users/rows should throw 409 error if the username is taken', async () => {
|
||||
it("POST /tables/_users/rows should throw 409 error if the username is taken", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: config.initialUserUsername, isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.post('/api/tables/_users/rows')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.post("/api/tables/_users/rows")
|
||||
.set("Cookie", [`accessToken=${accessToken}`])
|
||||
.send({
|
||||
fields: {
|
||||
username: testData.users.user1.username,
|
||||
password: testData.strongPassword
|
||||
}
|
||||
})
|
||||
username: testData.users.frozenUser1.username,
|
||||
password: testData.strongPassword,
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(409)
|
||||
expect(res.body.message).toBe('This username is taken')
|
||||
expect(res.status).toEqual(409);
|
||||
expect(res.body.message).toBe("This username is taken");
|
||||
|
||||
expect(res.body).not.toHaveProperty('password')
|
||||
expect(res.body).not.toHaveProperty('hashed_password')
|
||||
expect(res.body).not.toHaveProperty('salt')
|
||||
})
|
||||
expect(res.body).not.toHaveProperty("password");
|
||||
expect(res.body).not.toHaveProperty("hashed_password");
|
||||
expect(res.body).not.toHaveProperty("salt");
|
||||
});
|
||||
|
||||
it('GET /tables/_users/rows should return list of users', async () => {
|
||||
it("GET /tables/_users/rows should return list of users", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: config.initialUserUsername, isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.get('/api/tables/_users/rows')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.get("/api/tables/_users/rows")
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.body.data[0]).toHaveProperty('id')
|
||||
expect(res.body.data[0]).toHaveProperty('username')
|
||||
expect(res.body.data[0]).toHaveProperty('is_superuser')
|
||||
expect(res.body.data[0]).toHaveProperty('createdAt')
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.body.data[0]).toHaveProperty("id");
|
||||
expect(res.body.data[0]).toHaveProperty("username");
|
||||
expect(res.body.data[0]).toHaveProperty("is_superuser");
|
||||
expect(res.body.data[0]).toHaveProperty("createdAt");
|
||||
|
||||
expect(res.body.data[0]).not.toHaveProperty('password')
|
||||
expect(res.body.data[0]).not.toHaveProperty('hashed_password')
|
||||
expect(res.body.data[0]).not.toHaveProperty('salt')
|
||||
})
|
||||
expect(res.body.data[0]).not.toHaveProperty("password");
|
||||
expect(res.body.data[0]).not.toHaveProperty("hashed_password");
|
||||
expect(res.body.data[0]).not.toHaveProperty("salt");
|
||||
});
|
||||
|
||||
it('GET /tables/_users/rows/:id should retrive a single user', async () => {
|
||||
it("GET /tables/_users/rows/:id should retrive a single user", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: config.initialUserUsername, isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.get('/api/tables/_users/rows/1')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.get("/api/tables/_users/rows/1")
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.body.data[0]).toHaveProperty('id')
|
||||
expect(res.body.data[0]).toHaveProperty('username')
|
||||
expect(res.body.data[0]).toHaveProperty('is_superuser')
|
||||
expect(res.body.data[0]).toHaveProperty('createdAt')
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.body.data[0]).toHaveProperty("id");
|
||||
expect(res.body.data[0]).toHaveProperty("username");
|
||||
expect(res.body.data[0]).toHaveProperty("is_superuser");
|
||||
expect(res.body.data[0]).toHaveProperty("createdAt");
|
||||
|
||||
expect(res.body.data[0]).not.toHaveProperty('password')
|
||||
expect(res.body.data[0]).not.toHaveProperty('hashed_password')
|
||||
expect(res.body.data[0]).not.toHaveProperty('salt')
|
||||
})
|
||||
expect(res.body.data[0]).not.toHaveProperty("password");
|
||||
expect(res.body.data[0]).not.toHaveProperty("hashed_password");
|
||||
expect(res.body.data[0]).not.toHaveProperty("salt");
|
||||
});
|
||||
|
||||
it('PUT /tables/_users/rows/:id should update a user', async () => {
|
||||
it("PUT /tables/_users/rows/:id should update a user", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: config.initialUserUsername, isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.put('/api/tables/_users/rows/1')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
const createUserRes = await requestWithSupertest
|
||||
.post("/api/tables/_users/rows")
|
||||
.set("Cookie", [`accessToken=${accessToken}`])
|
||||
.send({
|
||||
fields: {
|
||||
username: testData.users.user3.username
|
||||
}
|
||||
})
|
||||
username: testData.users.changeNameUser.username,
|
||||
password: testData.strongPassword,
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
|
||||
expect(res.body).toHaveProperty('message')
|
||||
expect(res.body.message).toBe('Row updated')
|
||||
|
||||
expect(res.body).not.toHaveProperty('password')
|
||||
expect(res.body).not.toHaveProperty('hashed_password')
|
||||
expect(res.body).not.toHaveProperty('salt')
|
||||
})
|
||||
|
||||
it('DELETE /tables/_users/rows/:id should remove a user', async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
expect(createUserRes.status).toEqual(201);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.delete('/api/tables/_users/rows/2')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.put("/api/tables/_users/rows/" + createUserRes.body.rowId)
|
||||
.set("Cookie", [`accessToken=${accessToken}`])
|
||||
.send({
|
||||
fields: {
|
||||
username: testData.users.changeNameUser.username + "2",
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(400)
|
||||
expect(res.body.message).toBe('FOREIGN KEY constraint failed')
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
|
||||
expect(res.body).not.toHaveProperty('password')
|
||||
expect(res.body).not.toHaveProperty('hashed_password')
|
||||
expect(res.body).not.toHaveProperty('salt')
|
||||
})
|
||||
})
|
||||
expect(res.body).toHaveProperty("message");
|
||||
expect(res.body.message).toBe("Row updated");
|
||||
|
||||
describe('Obtain Access Token Endpoint', () => {
|
||||
it('POST /auth/token/obtain should return an access token and refresh token values and a success message', async () => {
|
||||
const res = await requestWithSupertest.post('/api/auth/token/obtain').send({
|
||||
fields: {
|
||||
username: testData.users.user1.username,
|
||||
password: testData.strongPassword
|
||||
}
|
||||
})
|
||||
expect(res.body).not.toHaveProperty("password");
|
||||
expect(res.body).not.toHaveProperty("hashed_password");
|
||||
expect(res.body).not.toHaveProperty("salt");
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(201)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
expect(res.body).toHaveProperty('message')
|
||||
expect(res.body.message).toBe('Success')
|
||||
it("DELETE /tables/_users/rows/:id should remove a user", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: config.initialUserUsername, isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
"1H",
|
||||
);
|
||||
|
||||
expect(res.headers['set-cookie']).toBeDefined()
|
||||
expect(res.headers['set-cookie']).toEqual(
|
||||
const createUserRes = await requestWithSupertest
|
||||
.post("/api/tables/_users/rows")
|
||||
.set("Cookie", [`accessToken=${accessToken}`])
|
||||
.send({
|
||||
fields: {
|
||||
username: testData.users.changeNameUser.username,
|
||||
password: testData.strongPassword,
|
||||
},
|
||||
});
|
||||
|
||||
expect(createUserRes.status).toEqual(201);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.delete("/api/tables/_users/rows/" + createUserRes.body.rowId)
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
|
||||
expect(res.status).toEqual(400);
|
||||
expect(res.body.message).toBe("FOREIGN KEY constraint failed");
|
||||
|
||||
expect(res.body).not.toHaveProperty("password");
|
||||
expect(res.body).not.toHaveProperty("hashed_password");
|
||||
expect(res.body).not.toHaveProperty("salt");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Obtain Access Token Endpoint", () => {
|
||||
it("POST /auth/token/obtain should return an access token and refresh token values and a success message", async () => {
|
||||
const res = await requestWithSupertest
|
||||
.post("/api/auth/token/obtain")
|
||||
.send({
|
||||
fields: {
|
||||
username: testData.users.frozenUser1.username,
|
||||
password: testData.strongPassword,
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(201);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
expect(res.body).toHaveProperty("message");
|
||||
expect(res.body.message).toBe("Success");
|
||||
|
||||
expect(res.headers["set-cookie"]).toBeDefined();
|
||||
expect(res.headers["set-cookie"]).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.stringContaining('refreshToken='),
|
||||
expect.stringContaining('accessToken=')
|
||||
])
|
||||
)
|
||||
expect.stringContaining("refreshToken="),
|
||||
expect.stringContaining("accessToken="),
|
||||
]),
|
||||
);
|
||||
|
||||
expect(res.body).not.toHaveProperty('password')
|
||||
expect(res.body).not.toHaveProperty('hashed_password')
|
||||
expect(res.body).not.toHaveProperty('salt')
|
||||
})
|
||||
expect(res.body).not.toHaveProperty("password");
|
||||
expect(res.body).not.toHaveProperty("hashed_password");
|
||||
expect(res.body).not.toHaveProperty("salt");
|
||||
});
|
||||
|
||||
it('POST /auth/token/obtain should throw a 401 error if the username does not exist in the DB', async () => {
|
||||
const res = await requestWithSupertest.post('/api/auth/token/obtain').send({
|
||||
fields: {
|
||||
username: testData.invalidUsername,
|
||||
password: testData.strongPassword
|
||||
}
|
||||
})
|
||||
it("POST /auth/token/obtain should throw a 401 error if the username does not exist in the DB", async () => {
|
||||
const res = await requestWithSupertest
|
||||
.post("/api/auth/token/obtain")
|
||||
.send({
|
||||
fields: {
|
||||
username: testData.invalidUsername,
|
||||
password: testData.strongPassword,
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(401)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
expect(res.body).toHaveProperty('message')
|
||||
expect(res.body.message).toBe('Invalid username or password')
|
||||
expect(res.status).toEqual(401);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
expect(res.body).toHaveProperty("message");
|
||||
expect(res.body.message).toBe("Invalid username or password");
|
||||
|
||||
expect(res.body).not.toHaveProperty('password')
|
||||
expect(res.body).not.toHaveProperty('hashed_password')
|
||||
expect(res.body).not.toHaveProperty('salt')
|
||||
})
|
||||
expect(res.body).not.toHaveProperty("password");
|
||||
expect(res.body).not.toHaveProperty("hashed_password");
|
||||
expect(res.body).not.toHaveProperty("salt");
|
||||
});
|
||||
|
||||
it('POST /auth/token/obtain should throw a 401 error if the password is invalid', async () => {
|
||||
const res = await requestWithSupertest.post('/api/auth/token/obtain').send({
|
||||
fields: {
|
||||
username: testData.users.user1.username,
|
||||
password: testData.invalidPassword
|
||||
}
|
||||
})
|
||||
it("POST /auth/token/obtain should throw a 401 error if the password is invalid", async () => {
|
||||
const res = await requestWithSupertest
|
||||
.post("/api/auth/token/obtain")
|
||||
.send({
|
||||
fields: {
|
||||
username: testData.users.frozenUser1.username,
|
||||
password: testData.invalidPassword,
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(401)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
expect(res.body).toHaveProperty('message')
|
||||
expect(res.body.message).toBe('Invalid username or password')
|
||||
expect(res.status).toEqual(401);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
expect(res.body).toHaveProperty("message");
|
||||
expect(res.body.message).toBe("Invalid username or password");
|
||||
|
||||
expect(res.body).not.toHaveProperty('password')
|
||||
expect(res.body).not.toHaveProperty('hashed_password')
|
||||
expect(res.body).not.toHaveProperty('salt')
|
||||
})
|
||||
})
|
||||
expect(res.body).not.toHaveProperty("password");
|
||||
expect(res.body).not.toHaveProperty("hashed_password");
|
||||
expect(res.body).not.toHaveProperty("salt");
|
||||
});
|
||||
});
|
||||
|
||||
describe('Refresh Access Token Endpoint', () => {
|
||||
it('GET /auth/token/refresh should refresh the access and refresh tokens', async () => {
|
||||
describe("Refresh Access Token Endpoint", () => {
|
||||
it("GET /auth/token/refresh should refresh the access and refresh tokens", async () => {
|
||||
const refreshToken = await generateToken(
|
||||
{ username: 'John', userId: 1, isSuperuser: true },
|
||||
{
|
||||
username: config.initialUserUsername,
|
||||
userId: 1,
|
||||
isSuperuser: true,
|
||||
},
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.get('/api/auth/token/refresh')
|
||||
.set('Cookie', [`refreshToken=${refreshToken}`])
|
||||
.get("/api/auth/token/refresh")
|
||||
.set("Cookie", [`refreshToken=${refreshToken}`]);
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
expect(res.body).toHaveProperty('message')
|
||||
expect(res.body.message).toBe('Success')
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
expect(res.body).toHaveProperty("message");
|
||||
expect(res.body.message).toBe("Success");
|
||||
|
||||
expect(res.headers['set-cookie']).toBeDefined()
|
||||
expect(res.headers['set-cookie']).toEqual(
|
||||
expect(res.headers["set-cookie"]).toBeDefined();
|
||||
expect(res.headers["set-cookie"]).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.stringContaining('refreshToken='),
|
||||
expect.stringContaining('accessToken=')
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
expect.stringContaining("refreshToken="),
|
||||
expect.stringContaining("accessToken="),
|
||||
]),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Change Password Endpoint', () => {
|
||||
it('PUT /auth/change-password/ should change a password', async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', userId: 2, isSuperuser: true },
|
||||
describe("Change Password Endpoint", () => {
|
||||
it("PUT /auth/change-password/ should change a password", async () => {
|
||||
let accessToken = await generateToken(
|
||||
{
|
||||
username: config.initialUserUsername,
|
||||
userId: 1,
|
||||
isSuperuser: true,
|
||||
},
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const createUserRes = await requestWithSupertest
|
||||
.post("/api/tables/_users/rows")
|
||||
.set("Cookie", [`accessToken=${accessToken}`])
|
||||
.send({
|
||||
fields: {
|
||||
username: testData.users.changePasswordUser.username,
|
||||
password: testData.strongPassword,
|
||||
},
|
||||
});
|
||||
|
||||
expect(createUserRes.status).toEqual(201);
|
||||
|
||||
accessToken = await generateToken(
|
||||
{
|
||||
username: config.initialUserUsername,
|
||||
userId: createUserRes.body.rowId,
|
||||
isSuperuser: true,
|
||||
},
|
||||
config.tokenSecret,
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.put('/api/auth/change-password')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.put("/api/auth/change-password")
|
||||
.set("Cookie", [`accessToken=${accessToken}`])
|
||||
.send({
|
||||
fields: {
|
||||
currentPassword: testData.strongPassword,
|
||||
newPassword: testData.strongPassword2
|
||||
}
|
||||
})
|
||||
newPassword: testData.strongPassword2,
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
expect(res.body).toHaveProperty('message')
|
||||
expect(res.body.message).toBe('Password updated successfully')
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
expect(res.body).toHaveProperty("message");
|
||||
expect(res.body.message).toBe("Password updated successfully");
|
||||
|
||||
expect(res.body).not.toHaveProperty('password')
|
||||
expect(res.body).not.toHaveProperty('hashed_password')
|
||||
expect(res.body).not.toHaveProperty('salt')
|
||||
expect(res.body).not.toHaveProperty("password");
|
||||
expect(res.body).not.toHaveProperty("hashed_password");
|
||||
expect(res.body).not.toHaveProperty("salt");
|
||||
|
||||
// check if the password is really updated
|
||||
const res2 = await requestWithSupertest.post('/api/auth/token/obtain').send({
|
||||
fields: {
|
||||
username: testData.users.user1.username,
|
||||
password: testData.strongPassword2
|
||||
}
|
||||
})
|
||||
const res2 = await requestWithSupertest
|
||||
.post("/api/auth/token/obtain")
|
||||
.send({
|
||||
fields: {
|
||||
username: testData.users.changePasswordUser.username,
|
||||
password: testData.strongPassword2,
|
||||
},
|
||||
});
|
||||
|
||||
expect(res2.status).toEqual(201)
|
||||
expect(res2.type).toEqual(expect.stringContaining('json'))
|
||||
expect(res2.body).toHaveProperty('message')
|
||||
expect(res2.body.message).toBe('Success')
|
||||
})
|
||||
expect(res2.status).toEqual(201);
|
||||
expect(res2.type).toEqual(expect.stringContaining("json"));
|
||||
expect(res2.body).toHaveProperty("message");
|
||||
expect(res2.body.message).toBe("Success");
|
||||
});
|
||||
|
||||
it('PUT /auth/change-password/ should throw 401 error if the current password is not valid', async () => {
|
||||
it("PUT /auth/change-password/ should throw 401 error if the current password is not valid", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', userId: 2, isSuperuser: true },
|
||||
{
|
||||
username: config.initialUserUsername,
|
||||
userId: 2,
|
||||
isSuperuser: true,
|
||||
},
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.put('/api/auth/change-password')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.put("/api/auth/change-password")
|
||||
.set("Cookie", [`accessToken=${accessToken}`])
|
||||
.send({
|
||||
fields: {
|
||||
currentPassword: testData.invalidPassword,
|
||||
newPassword: testData.strongPassword2
|
||||
}
|
||||
})
|
||||
newPassword: testData.strongPassword2,
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(401)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
expect(res.body).toHaveProperty('message')
|
||||
expect(res.body.message).toBe('Invalid current password')
|
||||
expect(res.status).toEqual(401);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
expect(res.body).toHaveProperty("message");
|
||||
expect(res.body.message).toBe("Invalid current password");
|
||||
|
||||
expect(res.body).not.toHaveProperty('password')
|
||||
expect(res.body).not.toHaveProperty('hashed_password')
|
||||
expect(res.body).not.toHaveProperty('salt')
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(res.body).not.toHaveProperty("password");
|
||||
expect(res.body).not.toHaveProperty("hashed_password");
|
||||
expect(res.body).not.toHaveProperty("salt");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,54 +1,67 @@
|
||||
const { rowService, authService } = require('../../services')
|
||||
const { apiConstants, dbConstants, responseMessages, authConstants } = require('../../constants')
|
||||
const config = require('../../config')
|
||||
const { hashPassword, checkPasswordStrength, comparePasswords } = require('../../utils')
|
||||
const { rowService, authService } = require("../../services");
|
||||
const {
|
||||
apiConstants,
|
||||
dbConstants,
|
||||
responseMessages,
|
||||
authConstants,
|
||||
} = require("../../constants");
|
||||
const config = require("../../config");
|
||||
const {
|
||||
hashPassword,
|
||||
checkPasswordStrength,
|
||||
comparePasswords,
|
||||
} = require("../../utils");
|
||||
|
||||
const { USERS_TABLE, USERS_ROLES_TABLE, tableFields } = dbConstants
|
||||
const { USERS_TABLE, USERS_ROLES_TABLE, tableFields } = dbConstants;
|
||||
|
||||
const { SALT_ROUNDS } = authConstants
|
||||
const { SALT_ROUNDS } = authConstants;
|
||||
|
||||
const { successMessage, errorMessage, infoMessage } = responseMessages
|
||||
const { successMessage, errorMessage, infoMessage } = responseMessages;
|
||||
|
||||
const updateSuperuser = async (fields) => {
|
||||
const { id, password, is_superuser } = fields
|
||||
let newHashedPassword, newSalt
|
||||
let fieldsString = ''
|
||||
const { id, password, is_superuser } = fields;
|
||||
let newHashedPassword, newSalt;
|
||||
let fieldsString = "";
|
||||
|
||||
try {
|
||||
// find the user by using the id field
|
||||
const users = authService.getUsersById({ userId: id })
|
||||
const users = authService.getUsersById({ userId: id });
|
||||
|
||||
// abort if the id is invalid
|
||||
if (users.length === 0) {
|
||||
console.log(errorMessage.USER_NOT_FOUND_ERROR)
|
||||
process.exit(1)
|
||||
console.log(errorMessage.USER_NOT_FOUND_ERROR);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// check if the is_superuser field is passed
|
||||
if (is_superuser !== undefined) {
|
||||
fieldsString = `${tableFields.IS_SUPERUSER} = '${is_superuser}'`
|
||||
fieldsString = `${tableFields.IS_SUPERUSER} = '${is_superuser}'`;
|
||||
}
|
||||
|
||||
// if the password is sent from the CLI, update it
|
||||
if (password) {
|
||||
// check if the password is weak
|
||||
if (
|
||||
[apiConstants.PASSWORD.TOO_WEAK, apiConstants.PASSWORD.WEAK].includes(
|
||||
checkPasswordStrength(password)
|
||||
)
|
||||
[
|
||||
apiConstants.PASSWORD.TOO_WEAK,
|
||||
apiConstants.PASSWORD.WEAK,
|
||||
].includes(checkPasswordStrength(password))
|
||||
) {
|
||||
console.log(errorMessage.WEAK_PASSWORD_ERROR)
|
||||
process.exit(1)
|
||||
console.log(errorMessage.WEAK_PASSWORD_ERROR);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
//hash the password
|
||||
const { hashedPassword, salt } = await hashPassword(password, SALT_ROUNDS)
|
||||
newHashedPassword = hashedPassword
|
||||
newSalt = salt
|
||||
const { hashedPassword, salt } = await hashPassword(
|
||||
password,
|
||||
SALT_ROUNDS,
|
||||
);
|
||||
newHashedPassword = hashedPassword;
|
||||
newSalt = salt;
|
||||
|
||||
fieldsString = `${fieldsString ? fieldsString + ', ' : ''} ${
|
||||
fieldsString = `${fieldsString ? fieldsString + ", " : ""} ${
|
||||
tableFields.HASHED_PASSWORD
|
||||
} = '${newHashedPassword}', ${tableFields.SALT} = '${newSalt}'`
|
||||
} = '${newHashedPassword}', ${tableFields.SALT} = '${newSalt}'`;
|
||||
}
|
||||
|
||||
// update the user
|
||||
@@ -56,15 +69,15 @@ const updateSuperuser = async (fields) => {
|
||||
tableName: USERS_TABLE,
|
||||
lookupField: tableFields.ID,
|
||||
fieldsString,
|
||||
pks: `${id}`
|
||||
})
|
||||
pks: `${id}`,
|
||||
});
|
||||
|
||||
console.log(successMessage.USER_UPDATE_SUCCESS)
|
||||
process.exit(1)
|
||||
console.log(successMessage.USER_UPDATE_SUCCESS);
|
||||
process.exit(1);
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const registerUser = async (req, res) => {
|
||||
/*
|
||||
@@ -80,22 +93,28 @@ const registerUser = async (req, res) => {
|
||||
}
|
||||
*/
|
||||
|
||||
const { username, password, ...optionalFields } = req.body.fields
|
||||
const { username, password, ...optionalFields } = req.body.fields;
|
||||
|
||||
try {
|
||||
if (!username) {
|
||||
return res.status(400).send({ message: errorMessage.USERNAME_REQUIRED_ERROR })
|
||||
return res
|
||||
.status(400)
|
||||
.send({ message: errorMessage.USERNAME_REQUIRED_ERROR });
|
||||
}
|
||||
|
||||
if (!password) {
|
||||
return res.status(400).send({ message: errorMessage.PASSWORD_REQUIRED_ERROR })
|
||||
return res
|
||||
.status(400)
|
||||
.send({ message: errorMessage.PASSWORD_REQUIRED_ERROR });
|
||||
}
|
||||
|
||||
// check if the username is taken
|
||||
const users = authService.getUsersByUsername({ username })
|
||||
const users = authService.getUsersByUsername({ username });
|
||||
|
||||
if (users.length > 0) {
|
||||
return res.status(409).send({ message: errorMessage.USERNAME_TAKEN_ERROR })
|
||||
return res
|
||||
.status(409)
|
||||
.send({ message: errorMessage.USERNAME_TAKEN_ERROR });
|
||||
|
||||
/*
|
||||
#swagger.responses[409] = {
|
||||
@@ -109,13 +128,14 @@ const registerUser = async (req, res) => {
|
||||
|
||||
// check if the password is weak
|
||||
if (
|
||||
[apiConstants.PASSWORD.TOO_WEAK, apiConstants.PASSWORD.WEAK].includes(
|
||||
checkPasswordStrength(password)
|
||||
)
|
||||
[
|
||||
apiConstants.PASSWORD.TOO_WEAK,
|
||||
apiConstants.PASSWORD.WEAK,
|
||||
].includes(checkPasswordStrength(password))
|
||||
) {
|
||||
return res.status(400).send({
|
||||
message: errorMessage.WEAK_PASSWORD_ERROR
|
||||
})
|
||||
message: errorMessage.WEAK_PASSWORD_ERROR,
|
||||
});
|
||||
|
||||
/*
|
||||
#swagger.responses[400] = {
|
||||
@@ -128,7 +148,10 @@ const registerUser = async (req, res) => {
|
||||
}
|
||||
|
||||
// hash the password
|
||||
const { salt, hashedPassword } = await hashPassword(password, SALT_ROUNDS)
|
||||
const { salt, hashedPassword } = await hashPassword(
|
||||
password,
|
||||
SALT_ROUNDS,
|
||||
);
|
||||
|
||||
// create the user
|
||||
const newUser = rowService.save({
|
||||
@@ -137,18 +160,18 @@ const registerUser = async (req, res) => {
|
||||
username,
|
||||
salt,
|
||||
hashed_password: hashedPassword,
|
||||
is_superuser: 'false',
|
||||
...optionalFields
|
||||
}
|
||||
})
|
||||
is_superuser: "false",
|
||||
...optionalFields,
|
||||
},
|
||||
});
|
||||
|
||||
// find the default role from the DB
|
||||
const defaultRole = authService.getDefaultRole()
|
||||
const defaultRole = authService.getDefaultRole();
|
||||
|
||||
if (defaultRole.length <= 0) {
|
||||
return res.status(500).send({
|
||||
message: errorMessage.DEFAULT_ROLE_NOT_CREATED_ERROR
|
||||
})
|
||||
message: errorMessage.DEFAULT_ROLE_NOT_CREATED_ERROR,
|
||||
});
|
||||
/*
|
||||
#swagger.responses[500] = {
|
||||
description: 'Server error',
|
||||
@@ -162,10 +185,16 @@ const registerUser = async (req, res) => {
|
||||
// create a role for the user
|
||||
rowService.save({
|
||||
tableName: USERS_ROLES_TABLE,
|
||||
fields: { user_id: newUser.lastInsertRowid, role_id: defaultRole[0].id }
|
||||
})
|
||||
fields: {
|
||||
user_id: newUser.lastInsertRowid,
|
||||
role_id: defaultRole[0].id,
|
||||
},
|
||||
});
|
||||
|
||||
res.status(201).send({ message: successMessage.ROW_INSERTED })
|
||||
res.status(201).send({
|
||||
message: successMessage.ROW_INSERTED,
|
||||
rowId: newUser.lastInsertRowid,
|
||||
});
|
||||
|
||||
/*
|
||||
#swagger.responses[201] = {
|
||||
@@ -176,10 +205,10 @@ const registerUser = async (req, res) => {
|
||||
}
|
||||
*/
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
res.status(500).send({ message: errorMessage.SERVER_ERROR })
|
||||
console.log(error);
|
||||
res.status(500).send({ message: errorMessage.SERVER_ERROR });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const changePassword = async (req, res) => {
|
||||
/*
|
||||
@@ -197,24 +226,31 @@ const changePassword = async (req, res) => {
|
||||
}
|
||||
*/
|
||||
|
||||
const userInfo = req.user
|
||||
const { currentPassword, newPassword } = req.body.fields
|
||||
const userInfo = req.user;
|
||||
const { currentPassword, newPassword } = req.body.fields;
|
||||
|
||||
try {
|
||||
// get the user from the Db
|
||||
const users = authService.getUsersById({ userId: userInfo.userId })
|
||||
const users = authService.getUsersById({ userId: userInfo.userId });
|
||||
|
||||
if (users.length <= 0) {
|
||||
return res.status(401).send({ message: errorMessage.USER_NOT_FOUND_ERROR })
|
||||
return res
|
||||
.status(401)
|
||||
.send({ message: errorMessage.USER_NOT_FOUND_ERROR });
|
||||
}
|
||||
|
||||
const user = users[0]
|
||||
const user = users[0];
|
||||
|
||||
// check if the users current password is valid
|
||||
const isMatch = await comparePasswords(currentPassword, user.hashed_password)
|
||||
const isMatch = await comparePasswords(
|
||||
currentPassword,
|
||||
user.hashed_password,
|
||||
);
|
||||
|
||||
if (!isMatch) {
|
||||
return res.status(401).send({ message: errorMessage.INVALID_CURRENT_PASSWORD_ERROR })
|
||||
return res
|
||||
.status(401)
|
||||
.send({ message: errorMessage.INVALID_CURRENT_PASSWORD_ERROR });
|
||||
/*
|
||||
#swagger.responses[401] = {
|
||||
description: 'User not found error',
|
||||
@@ -227,13 +263,14 @@ const changePassword = async (req, res) => {
|
||||
|
||||
// check if the new password is strong
|
||||
if (
|
||||
[apiConstants.PASSWORD.TOO_WEAK, apiConstants.PASSWORD.WEAK].includes(
|
||||
checkPasswordStrength(newPassword)
|
||||
)
|
||||
[
|
||||
apiConstants.PASSWORD.TOO_WEAK,
|
||||
apiConstants.PASSWORD.WEAK,
|
||||
].includes(checkPasswordStrength(newPassword))
|
||||
) {
|
||||
return res.status(400).send({
|
||||
message: errorMessage.WEAK_PASSWORD_ERROR
|
||||
})
|
||||
message: errorMessage.WEAK_PASSWORD_ERROR,
|
||||
});
|
||||
|
||||
/*
|
||||
#swagger.responses[400] = {
|
||||
@@ -246,23 +283,26 @@ const changePassword = async (req, res) => {
|
||||
}
|
||||
|
||||
// hash the password
|
||||
const { salt, hashedPassword } = await hashPassword(newPassword, SALT_ROUNDS)
|
||||
const { salt, hashedPassword } = await hashPassword(
|
||||
newPassword,
|
||||
SALT_ROUNDS,
|
||||
);
|
||||
|
||||
user.salt = salt
|
||||
user.hashed_password = hashedPassword
|
||||
user.salt = salt;
|
||||
user.hashed_password = hashedPassword;
|
||||
|
||||
// update the user
|
||||
rowService.update({
|
||||
tableName: USERS_TABLE,
|
||||
lookupField: tableFields.ID,
|
||||
fieldsString: `${tableFields.HASHED_PASSWORD} = '${hashedPassword}', ${tableFields.SALT} = '${salt}'`,
|
||||
pks: `${user.id}`
|
||||
})
|
||||
pks: `${user.id}`,
|
||||
});
|
||||
|
||||
res.status(200).send({
|
||||
message: successMessage.PASSWORD_UPDATE_SUCCESS,
|
||||
data: { id: user.id, username: user.username }
|
||||
})
|
||||
data: { id: user.id, username: user.username },
|
||||
});
|
||||
|
||||
/*
|
||||
#swagger.responses[200] = {
|
||||
@@ -273,51 +313,60 @@ const changePassword = async (req, res) => {
|
||||
}
|
||||
*/
|
||||
} catch (error) {
|
||||
res.status(500).send({ message: errorMessage.SERVER_ERROR })
|
||||
res.status(500).send({ message: errorMessage.SERVER_ERROR });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const createInitialUser = async () => {
|
||||
// extract some fields from the environment variables or from the CLI
|
||||
const { initialUserUsername: username, initialUserPassword: password } = config
|
||||
const { initialUserUsername: username, initialUserPassword: password } =
|
||||
config;
|
||||
|
||||
try {
|
||||
// check if there are users in the DB
|
||||
const users = authService.getAllUsers()
|
||||
const users = authService.getAllUsers();
|
||||
|
||||
if (users.length <= 0) {
|
||||
// check if initial users username is passed from the env or CLI
|
||||
if (!username) {
|
||||
console.error(errorMessage.INITIAL_USER_USERNAME_NOT_PASSED_ERROR)
|
||||
process.exit(1)
|
||||
console.error(
|
||||
errorMessage.INITIAL_USER_USERNAME_NOT_PASSED_ERROR,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// check if initial users password is passed from the env or CLI
|
||||
if (!password) {
|
||||
console.error(errorMessage.INITIAL_USER_PASSWORD_NOT_PASSED_ERROR)
|
||||
process.exit(1)
|
||||
console.error(
|
||||
errorMessage.INITIAL_USER_PASSWORD_NOT_PASSED_ERROR,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// check if the usernmae is taken
|
||||
const users = authService.getUsersByUsername({ username })
|
||||
const users = authService.getUsersByUsername({ username });
|
||||
|
||||
if (users.length > 0) {
|
||||
console.error(errorMessage.USERNAME_TAKEN_ERROR)
|
||||
process.exit(1)
|
||||
console.error(errorMessage.USERNAME_TAKEN_ERROR);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// check if the password is strong
|
||||
if (
|
||||
[apiConstants.PASSWORD.TOO_WEAK, apiConstants.PASSWORD.WEAK].includes(
|
||||
checkPasswordStrength(password)
|
||||
)
|
||||
[
|
||||
apiConstants.PASSWORD.TOO_WEAK,
|
||||
apiConstants.PASSWORD.WEAK,
|
||||
].includes(checkPasswordStrength(password))
|
||||
) {
|
||||
console.error(errorMessage.WEAK_PASSWORD_ERROR)
|
||||
process.exit(1)
|
||||
console.error(errorMessage.WEAK_PASSWORD_ERROR);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// hash the password
|
||||
const { hashedPassword, salt } = await hashPassword(password, SALT_ROUNDS)
|
||||
const { hashedPassword, salt } = await hashPassword(
|
||||
password,
|
||||
SALT_ROUNDS,
|
||||
);
|
||||
|
||||
// create the initial user
|
||||
const { lastInsertRowid: userId } = rowService.save({
|
||||
@@ -326,38 +375,38 @@ const createInitialUser = async () => {
|
||||
username,
|
||||
hashed_password: hashedPassword,
|
||||
salt,
|
||||
is_superuser: 'false'
|
||||
}
|
||||
})
|
||||
is_superuser: "false",
|
||||
},
|
||||
});
|
||||
|
||||
// get the default role from the DB
|
||||
const roles = authService.getDefaultRole()
|
||||
const roles = authService.getDefaultRole();
|
||||
|
||||
if (roles.length <= 0) {
|
||||
console.log(errorMessage.DEFAULT_ROLE_NOT_CREATED_ERROR)
|
||||
process.exit(1)
|
||||
console.log(errorMessage.DEFAULT_ROLE_NOT_CREATED_ERROR);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const defaultRoleId = roles[0].id
|
||||
const defaultRoleId = roles[0].id;
|
||||
|
||||
// create a _users_role for the initial user
|
||||
rowService.save({
|
||||
tableName: USERS_ROLES_TABLE,
|
||||
fields: { user_id: userId, role_id: defaultRoleId }
|
||||
})
|
||||
fields: { user_id: userId, role_id: defaultRoleId },
|
||||
});
|
||||
|
||||
console.log(successMessage.INITIAL_USER_CREATED_SUCCESS)
|
||||
console.log(successMessage.INITIAL_USER_CREATED_SUCCESS);
|
||||
} else {
|
||||
console.log(infoMessage.INITIAL_USER_ALREADY_CREATED)
|
||||
console.log(infoMessage.INITIAL_USER_ALREADY_CREATED);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
updateSuperuser,
|
||||
registerUser,
|
||||
changePassword,
|
||||
createInitialUser
|
||||
}
|
||||
createInitialUser,
|
||||
};
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
const supertest = require('supertest')
|
||||
const supertest = require("supertest");
|
||||
|
||||
const app = require('../index')
|
||||
const requestWithSupertest = supertest(app)
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(global, "setInterval");
|
||||
|
||||
describe('Root Endpoints', () => {
|
||||
it('GET / should return server version and timestamp', async () => {
|
||||
const res = await requestWithSupertest.get('/api')
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
expect(res.body).toHaveProperty('message')
|
||||
expect(res.body).toHaveProperty('data')
|
||||
expect(res.body.data).toHaveProperty('version')
|
||||
expect(res.body.data).toHaveProperty('timestamp')
|
||||
})
|
||||
})
|
||||
const app = require("../index");
|
||||
const requestWithSupertest = supertest(app);
|
||||
|
||||
describe('Health Endpoints', () => {
|
||||
it('GET /health should return server version and timestamp', async () => {
|
||||
const res = await requestWithSupertest.get('/api/health')
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.type).toEqual(expect.stringContaining('text'))
|
||||
})
|
||||
})
|
||||
describe("Root Endpoints", () => {
|
||||
it("GET / should return server version and timestamp", async () => {
|
||||
const res = await requestWithSupertest.get("/api");
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
expect(res.body).toHaveProperty("message");
|
||||
expect(res.body).toHaveProperty("data");
|
||||
expect(res.body.data).toHaveProperty("version");
|
||||
expect(res.body.data).toHaveProperty("timestamp");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Health Endpoints", () => {
|
||||
it("GET /health should return server version and timestamp", async () => {
|
||||
const res = await requestWithSupertest.get("/api/health");
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.type).toEqual(expect.stringContaining("text"));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,330 +1,338 @@
|
||||
const { not } = require('joi')
|
||||
const supertest = require('supertest')
|
||||
const { not } = require("joi");
|
||||
const supertest = require("supertest");
|
||||
|
||||
const app = require('../index')
|
||||
const config = require('../config')
|
||||
const { generateToken } = require('../utils')
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(global, "setInterval");
|
||||
const app = require("../index");
|
||||
const config = require("../config");
|
||||
const { generateToken } = require("../utils");
|
||||
|
||||
const requestWithSupertest = supertest(app)
|
||||
const requestWithSupertest = supertest(app);
|
||||
|
||||
function queryString(params) {
|
||||
const queryString = Object.keys(params)
|
||||
.map((key) => `${key}=${params[key]}`)
|
||||
.join('&')
|
||||
.join("&");
|
||||
|
||||
return queryString
|
||||
return queryString;
|
||||
}
|
||||
|
||||
describe('Rows Endpoints', () => {
|
||||
it('GET /tables/:name/rows should return a list of all rows', async () => {
|
||||
describe("Rows Endpoints", () => {
|
||||
it("GET /tables/:name/rows should return a list of all rows", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: "John", isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.get('/api/tables/users/rows')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.get("/api/tables/users/rows")
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
expect(res.body).toHaveProperty('data')
|
||||
expect(res.body.data).toEqual(expect.any(Array))
|
||||
expect(res.body.data[0]).toHaveProperty('id')
|
||||
expect(res.body.data[0]).toHaveProperty('firstName')
|
||||
expect(res.body.data[0]).toHaveProperty('lastName')
|
||||
})
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
expect(res.body).toHaveProperty("data");
|
||||
expect(res.body.data).toEqual(expect.any(Array));
|
||||
expect(res.body.data[0]).toHaveProperty("id");
|
||||
expect(res.body.data[0]).toHaveProperty("firstName");
|
||||
expect(res.body.data[0]).toHaveProperty("lastName");
|
||||
});
|
||||
|
||||
it('GET /tables/:name/rows?_limit=8&_schema=firstName,lastName&_ordering:-firstName&_page=2: should query the rows by the provided query params', async () => {
|
||||
it("GET /tables/:name/rows?_limit=8&_schema=firstName,lastName&_ordering:-firstName&_page=2: should query the rows by the provided query params", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: "John", isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const params = {
|
||||
_search: 'a',
|
||||
_ordering: '-firstName',
|
||||
_schema: 'firstName,lastName',
|
||||
_search: "a",
|
||||
_ordering: "-firstName",
|
||||
_schema: "firstName,lastName",
|
||||
_limit: 8,
|
||||
_page: 2
|
||||
}
|
||||
const query = queryString(params)
|
||||
_page: 2,
|
||||
};
|
||||
const query = queryString(params);
|
||||
const res = await requestWithSupertest
|
||||
.get(`/api/tables/users/rows?${query}`)
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
expect(res.body).toHaveProperty('data')
|
||||
expect(res.body.data).toEqual(expect.any(Array))
|
||||
expect(res.body.data[0]).toHaveProperty('firstName')
|
||||
expect(res.body.data[0]).toHaveProperty('lastName')
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
expect(res.body).toHaveProperty("data");
|
||||
expect(res.body.data).toEqual(expect.any(Array));
|
||||
expect(res.body.data[0]).toHaveProperty("firstName");
|
||||
expect(res.body.data[0]).toHaveProperty("lastName");
|
||||
|
||||
expect(res.body.next).toEqual(
|
||||
`/tables/users/rows?${queryString({
|
||||
...params,
|
||||
_page: params._page + 1
|
||||
}).toString()}`
|
||||
)
|
||||
_page: params._page + 1,
|
||||
}).toString()}`,
|
||||
);
|
||||
|
||||
expect(res.body.previous).toEqual(
|
||||
`/tables/users/rows?${queryString({
|
||||
...params,
|
||||
_page: params._page - 1
|
||||
}).toString()}`
|
||||
)
|
||||
})
|
||||
_page: params._page - 1,
|
||||
}).toString()}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('GET /tables/:name/rows: should return a null field', async () => {
|
||||
it("GET /tables/:name/rows: should return a null field", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: "John", isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.get('/api/tables/users/rows?_filters=firstName__null,lastName__notnull')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.get(
|
||||
"/api/tables/users/rows?_filters=firstName__null,lastName__notnull",
|
||||
)
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.body.data[0].firstName).toBeNull()
|
||||
expect(res.body.data[0].lastName).not.toBeNull()
|
||||
})
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.body.data[0].firstName).toBeNull();
|
||||
expect(res.body.data[0].lastName).not.toBeNull();
|
||||
});
|
||||
|
||||
it('GET /tables/:name/rows: should successfully retrieve users created after 2010-01-01 00:00:00.', async () => {
|
||||
it("GET /tables/:name/rows: should successfully retrieve users created after 2010-01-01 00:00:00.", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: "John", isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const date = '2010-01-01 00:00:00'
|
||||
const date = "2010-01-01 00:00:00";
|
||||
const res = await requestWithSupertest
|
||||
.get(`/api/tables/users/rows?_filters=createdAt__gte:${date}`)
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
|
||||
res.body.data.map((user) => {
|
||||
const createdAt = new Date(user.createdAt)
|
||||
const referenceDate = new Date(date)
|
||||
expect(createdAt.getTime()).toBeGreaterThan(referenceDate.getTime())
|
||||
})
|
||||
const createdAt = new Date(user.createdAt);
|
||||
const referenceDate = new Date(date);
|
||||
expect(createdAt.getTime()).toBeGreaterThan(
|
||||
referenceDate.getTime(),
|
||||
);
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.body.data[0]).toHaveProperty('id')
|
||||
expect(res.body.data[0]).toHaveProperty('firstName')
|
||||
expect(res.body.data[0]).toHaveProperty('lastName')
|
||||
expect(res.body.data[0]).toHaveProperty('createdAt')
|
||||
})
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.body.data[0]).toHaveProperty("id");
|
||||
expect(res.body.data[0]).toHaveProperty("firstName");
|
||||
expect(res.body.data[0]).toHaveProperty("lastName");
|
||||
expect(res.body.data[0]).toHaveProperty("createdAt");
|
||||
});
|
||||
|
||||
it('GET /tables/:name/rows: should successfully retrieve users created before 2008-01-20 00:00:00.', async () => {
|
||||
it("GET /tables/:name/rows: should successfully retrieve users created before 2008-01-20 00:00:00.", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: "John", isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const date = '2008-01-20 00:00:00'
|
||||
const date = "2008-01-20 00:00:00";
|
||||
const res = await requestWithSupertest
|
||||
.get(`/api/tables/users/rows?_filters=createdAt__lte:${date}`)
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
|
||||
res.body.data.map((user) => {
|
||||
const createdAt = new Date(user.createdAt)
|
||||
const referenceDate = new Date(date)
|
||||
expect(createdAt.getTime()).toBeLessThan(referenceDate.getTime())
|
||||
})
|
||||
const createdAt = new Date(user.createdAt);
|
||||
const referenceDate = new Date(date);
|
||||
expect(createdAt.getTime()).toBeLessThan(referenceDate.getTime());
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.body.data[0]).toHaveProperty('id')
|
||||
expect(res.body.data[0]).toHaveProperty('firstName')
|
||||
expect(res.body.data[0]).toHaveProperty('lastName')
|
||||
expect(res.body.data[0]).toHaveProperty('createdAt')
|
||||
})
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.body.data[0]).toHaveProperty("id");
|
||||
expect(res.body.data[0]).toHaveProperty("firstName");
|
||||
expect(res.body.data[0]).toHaveProperty("lastName");
|
||||
expect(res.body.data[0]).toHaveProperty("createdAt");
|
||||
});
|
||||
|
||||
it('GET /tables/:name/rows: should successfully retrieve users created at 2013-01-08 00:00:00', async () => {
|
||||
it("GET /tables/:name/rows: should successfully retrieve users created at 2013-01-08 00:00:00", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: "John", isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const date = '2013-01-08 00:00:00'
|
||||
const date = "2013-01-08 00:00:00";
|
||||
const res = await requestWithSupertest
|
||||
.get(`/api/tables/users/rows?_filters=createdAt__eq:${date}`)
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
|
||||
res.body.data.map((user) => {
|
||||
const createdAt = new Date(user.createdAt)
|
||||
const referenceDate = new Date(date)
|
||||
expect(createdAt.getTime()).toEqual(referenceDate.getTime())
|
||||
})
|
||||
const createdAt = new Date(user.createdAt);
|
||||
const referenceDate = new Date(date);
|
||||
expect(createdAt.getTime()).toEqual(referenceDate.getTime());
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.body.data[0]).toHaveProperty('id')
|
||||
expect(res.body.data[0]).toHaveProperty('firstName')
|
||||
expect(res.body.data[0]).toHaveProperty('lastName')
|
||||
expect(res.body.data[0]).toHaveProperty('createdAt')
|
||||
})
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.body.data[0]).toHaveProperty("id");
|
||||
expect(res.body.data[0]).toHaveProperty("firstName");
|
||||
expect(res.body.data[0]).toHaveProperty("lastName");
|
||||
expect(res.body.data[0]).toHaveProperty("createdAt");
|
||||
});
|
||||
|
||||
it('GET /tables/:name/rows: should successfully retrieve users created at 2007-01-08 00:00:00', async () => {
|
||||
it("GET /tables/:name/rows: should successfully retrieve users created at 2007-01-08 00:00:00", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: "John", isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const date = '2007-01-08 00:00:00'
|
||||
const date = "2007-01-08 00:00:00";
|
||||
const res = await requestWithSupertest
|
||||
.get(`/api/tables/users/rows?_filters=createdAt__eq:${date}`)
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
|
||||
//There are no users that are created at 2007-01-08 00:00:00 so the API should return empty data
|
||||
expect(res.body.data).toHaveLength(0)
|
||||
expect(res.status).toEqual(200)
|
||||
})
|
||||
expect(res.body.data).toHaveLength(0);
|
||||
expect(res.status).toEqual(200);
|
||||
});
|
||||
|
||||
it('GET /tables/:name/rows: should successfully retrieve users that are not created at 2021-01-08 00:00:00', async () => {
|
||||
it("GET /tables/:name/rows: should successfully retrieve users that are not created at 2021-01-08 00:00:00", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: "John", isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const date = '2021-01-08 00:00:00'
|
||||
const date = "2021-01-08 00:00:00";
|
||||
const res = await requestWithSupertest
|
||||
.get(`/api/tables/users/rows?_filters=createdAt__neq:${date}`)
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
|
||||
res.body.data.map((user) => {
|
||||
const createdAt = new Date(user.createdAt)
|
||||
const referenceDate = new Date(date)
|
||||
expect(createdAt.getTime()).not.toEqual(referenceDate.getTime())
|
||||
})
|
||||
const createdAt = new Date(user.createdAt);
|
||||
const referenceDate = new Date(date);
|
||||
expect(createdAt.getTime()).not.toEqual(referenceDate.getTime());
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.body.data[0]).toHaveProperty('id')
|
||||
expect(res.body.data[0]).toHaveProperty('firstName')
|
||||
expect(res.body.data[0]).toHaveProperty('lastName')
|
||||
expect(res.body.data[0]).toHaveProperty('createdAt')
|
||||
})
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.body.data[0]).toHaveProperty("id");
|
||||
expect(res.body.data[0]).toHaveProperty("firstName");
|
||||
expect(res.body.data[0]).toHaveProperty("lastName");
|
||||
expect(res.body.data[0]).toHaveProperty("createdAt");
|
||||
});
|
||||
|
||||
it('POST /tables/:name/rows should insert a new row and return the lastInsertRowid', async () => {
|
||||
it("POST /tables/:name/rows should insert a new row and return the lastInsertRowid", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: "John", isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.post('/api/tables/users/rows')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.send({ fields: { firstName: 'Jane', lastName: 'Doe' } })
|
||||
.post("/api/tables/users/rows")
|
||||
.set("Cookie", [`accessToken=${accessToken}`])
|
||||
.send({ fields: { firstName: "Jane", lastName: "Doe" } });
|
||||
|
||||
expect(res.status).toEqual(201)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
expect(res.body).toHaveProperty('data')
|
||||
})
|
||||
expect(res.status).toEqual(201);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
expect(res.body).toHaveProperty("data");
|
||||
});
|
||||
|
||||
it('GET /tables/:name/rows/:pks should return a row by its primary key', async () => {
|
||||
it("GET /tables/:name/rows/:pks should return a row by its primary key", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: "John", isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.get('/api/tables/users/rows/1')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.get("/api/tables/users/rows/1")
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
expect(res.body).toHaveProperty('data')
|
||||
expect(res.body.data[0]).toHaveProperty('id')
|
||||
expect(res.body.data[0]).toHaveProperty('firstName')
|
||||
expect(res.body.data[0]).toHaveProperty('lastName')
|
||||
})
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
expect(res.body).toHaveProperty("data");
|
||||
expect(res.body.data[0]).toHaveProperty("id");
|
||||
expect(res.body.data[0]).toHaveProperty("firstName");
|
||||
expect(res.body.data[0]).toHaveProperty("lastName");
|
||||
});
|
||||
|
||||
it('PUT /tables/:name/rows/:pks should update a row by its primary key and return the number of changes', async () => {
|
||||
it("PUT /tables/:name/rows/:pks should update a row by its primary key and return the number of changes", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: "John", isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
const res = await requestWithSupertest
|
||||
.put('/api/tables/users/rows/1')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.send({ fields: { firstName: 'Jane', lastName: 'Doe' } })
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
})
|
||||
.put("/api/tables/users/rows/1")
|
||||
.set("Cookie", [`accessToken=${accessToken}`])
|
||||
.send({ fields: { firstName: "Jane", lastName: "Doe" } });
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
});
|
||||
|
||||
it('DELETE /tables/:name/rows/:pks should delete a row by its primary key and return the number of changes', async () => {
|
||||
it("DELETE /tables/:name/rows/:pks should delete a row by its primary key and return the number of changes", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: "John", isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.delete('/api/tables/users/rows/1')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
})
|
||||
.delete("/api/tables/users/rows/1")
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
});
|
||||
|
||||
it('POST /tables/:name/rows should insert a new row if any of the value of the object being inserted is null', async () => {
|
||||
it("POST /tables/:name/rows should insert a new row if any of the value of the object being inserted is null", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: "John", isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
const res = await requestWithSupertest
|
||||
.post('/api/tables/users/rows')
|
||||
.post("/api/tables/users/rows")
|
||||
.send({
|
||||
fields: {
|
||||
firstName: null,
|
||||
lastName: 'Doe',
|
||||
lastName: "Doe",
|
||||
email: null,
|
||||
username: 'Jane'
|
||||
}
|
||||
username: "Jane",
|
||||
},
|
||||
})
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
expect(res.status).toEqual(201)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
expect(res.body).toHaveProperty('data')
|
||||
})
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
expect(res.status).toEqual(201);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
expect(res.body).toHaveProperty("data");
|
||||
});
|
||||
|
||||
it('GET /tables/:name/rows should return values if any of the IDs from the array match the user ID.', async () => {
|
||||
it("GET /tables/:name/rows should return values if any of the IDs from the array match the user ID.", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: "John", isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.get('/api/tables/users/rows?_filters=id:[2,3]')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.body).toHaveProperty('data')
|
||||
expect(res.body.data).toEqual(expect.any(Array))
|
||||
expect(res.body.data.length).toEqual(2)
|
||||
})
|
||||
.get("/api/tables/users/rows?_filters=id:[2,3]")
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.body).toHaveProperty("data");
|
||||
expect(res.body.data).toEqual(expect.any(Array));
|
||||
expect(res.body.data.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('GET /tables/:name/rows should return values if the provided ID matches the user ID.', async () => {
|
||||
it("GET /tables/:name/rows should return values if the provided ID matches the user ID.", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: "John", isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.get('/api/tables/users/rows?_filters=id:2,firstName:Michael,lastName:Lee')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.body).toHaveProperty('data')
|
||||
expect(res.body.data).toEqual(expect.any(Array))
|
||||
expect(res.body.data.length).toEqual(1)
|
||||
})
|
||||
})
|
||||
.get(
|
||||
"/api/tables/users/rows?_filters=id:2,firstName:Michael,lastName:Lee",
|
||||
)
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.body).toHaveProperty("data");
|
||||
expect(res.body.data).toEqual(expect.any(Array));
|
||||
expect(res.body.data.length).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,92 +1,94 @@
|
||||
const supertest = require('supertest')
|
||||
const supertest = require("supertest");
|
||||
|
||||
const app = require('../index')
|
||||
const { generateToken } = require('../utils')
|
||||
const config = require('../config')
|
||||
jest.useFakeTimers();
|
||||
jest.spyOn(global, "setInterval");
|
||||
const app = require("../index");
|
||||
const { generateToken } = require("../utils");
|
||||
const config = require("../config");
|
||||
|
||||
const requestWithSupertest = supertest(app)
|
||||
const requestWithSupertest = supertest(app);
|
||||
|
||||
describe('Tables Endpoints', () => {
|
||||
it('GET /tables should return a list of all tables', async () => {
|
||||
describe("Tables Endpoints", () => {
|
||||
it("GET /tables should return a list of all tables", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: "John", isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.get('/api/tables')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.get("/api/tables")
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
expect(res.body).toHaveProperty('data')
|
||||
expect(res.body.data).toEqual(expect.any(Array))
|
||||
expect(res.body.data[0]).toHaveProperty('name')
|
||||
})
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
expect(res.body).toHaveProperty("data");
|
||||
expect(res.body.data).toEqual(expect.any(Array));
|
||||
expect(res.body.data[0]).toHaveProperty("name");
|
||||
});
|
||||
|
||||
it('POST /tables should create a new table and return generated schema', async () => {
|
||||
it("POST /tables should create a new table and return generated schema", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: "John", isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.post('/api/tables')
|
||||
.post("/api/tables")
|
||||
.send({
|
||||
name: 'pets',
|
||||
name: "pets",
|
||||
autoAddCreatedAt: true,
|
||||
autoAddUpdatedAt: false,
|
||||
schema: [
|
||||
{
|
||||
name: 'owner',
|
||||
type: 'INTEGER',
|
||||
name: "owner",
|
||||
type: "INTEGER",
|
||||
foreignKey: {
|
||||
table: 'users',
|
||||
column: 'id',
|
||||
onDelete: 'CASCADE',
|
||||
onUpdate: 'CASCADE'
|
||||
}
|
||||
table: "users",
|
||||
column: "id",
|
||||
onDelete: "CASCADE",
|
||||
onUpdate: "CASCADE",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
type: 'TEXT',
|
||||
notNull: true
|
||||
name: "name",
|
||||
type: "TEXT",
|
||||
notNull: true,
|
||||
},
|
||||
{
|
||||
name: 'petId',
|
||||
name: "petId",
|
||||
unique: true,
|
||||
type: 'INTEGER'
|
||||
}
|
||||
]
|
||||
type: "INTEGER",
|
||||
},
|
||||
],
|
||||
})
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
|
||||
expect(res.status).toEqual(201)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
expect(res.body).toHaveProperty('data')
|
||||
expect(res.body.data).toHaveProperty('name')
|
||||
expect(res.body.data).toHaveProperty('schema')
|
||||
expect(res.body.data.schema).toEqual(expect.any(Array))
|
||||
expect(res.body.data.schema[0]).toHaveProperty('name')
|
||||
expect(res.body.data.schema[0]).toHaveProperty('cid')
|
||||
})
|
||||
expect(res.status).toEqual(201);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
expect(res.body).toHaveProperty("data");
|
||||
expect(res.body.data).toHaveProperty("name");
|
||||
expect(res.body.data).toHaveProperty("schema");
|
||||
expect(res.body.data.schema).toEqual(expect.any(Array));
|
||||
expect(res.body.data.schema[0]).toHaveProperty("name");
|
||||
expect(res.body.data.schema[0]).toHaveProperty("cid");
|
||||
});
|
||||
|
||||
it('GET /tables/:name should return schema of the table', async () => {
|
||||
it("GET /tables/:name should return schema of the table", async () => {
|
||||
const accessToken = await generateToken(
|
||||
{ username: 'John', isSuperuser: true },
|
||||
{ username: "John", isSuperuser: true },
|
||||
config.tokenSecret,
|
||||
'1H'
|
||||
)
|
||||
"1H",
|
||||
);
|
||||
|
||||
const res = await requestWithSupertest
|
||||
.get('/api/tables/users')
|
||||
.set('Cookie', [`accessToken=${accessToken}`])
|
||||
.get("/api/tables/users")
|
||||
.set("Cookie", [`accessToken=${accessToken}`]);
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.type).toEqual(expect.stringContaining('json'))
|
||||
expect(res.body).toHaveProperty('data')
|
||||
expect(res.body.data).toEqual(expect.any(Array))
|
||||
})
|
||||
})
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.type).toEqual(expect.stringContaining("json"));
|
||||
expect(res.body).toHaveProperty("data");
|
||||
expect(res.body.data).toEqual(expect.any(Array));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
const { dropTestDatabase } = require('.')
|
||||
const { dropTestDatabase } = require(".");
|
||||
|
||||
const globalTearDown = () => {
|
||||
console.log('Test suite finished')
|
||||
console.log('Dropping test database...')
|
||||
dropTestDatabase()
|
||||
}
|
||||
const globalTearDown = async () => {
|
||||
console.log("Test suite finished");
|
||||
console.log("Dropping test database...");
|
||||
await dropTestDatabase();
|
||||
};
|
||||
|
||||
module.exports = globalTearDown
|
||||
module.exports = globalTearDown;
|
||||
|
||||
@@ -1,75 +1,105 @@
|
||||
const testNames = [
|
||||
{ firstName: 'Emily', lastName: 'William', createdAt: '2008-01-08 00:00:00' },
|
||||
{ firstName: 'Michael', lastName: 'Lee', createdAt: '2009-01-08 00:00:00' },
|
||||
{ firstName: 'Sarah', lastName: 'Johnson', createdAt: '2010-01-08 00:00:00' },
|
||||
{ firstName: 'David', lastName: 'Chen', createdAt: '2011-01-08 00:00:00' },
|
||||
{
|
||||
firstName: 'Olivia',
|
||||
lastName: 'William',
|
||||
createdAt: '2012-01-08 00:00:00'
|
||||
firstName: "Emily",
|
||||
lastName: "William",
|
||||
createdAt: "2008-01-08 00:00:00",
|
||||
},
|
||||
{ firstName: 'William', lastName: 'Kim', createdAt: '2013-01-08 00:00:00' },
|
||||
{ firstName: 'Sophia', lastName: 'Singh', createdAt: '2013-02-08 00:00:00' },
|
||||
{ firstName: "Michael", lastName: "Lee", createdAt: "2009-01-08 00:00:00" },
|
||||
{
|
||||
firstName: 'James',
|
||||
lastName: 'Rodriguez',
|
||||
createdAt: '2013-03-08 00:00:00'
|
||||
firstName: "Sarah",
|
||||
lastName: "Johnson",
|
||||
createdAt: "2010-01-08 00:00:00",
|
||||
},
|
||||
{ firstName: 'Ava', lastName: 'Patel', createdAt: '2013-01-04 00:00:00' },
|
||||
{ firstName: "David", lastName: "Chen", createdAt: "2011-01-08 00:00:00" },
|
||||
{
|
||||
firstName: 'Benjamin',
|
||||
lastName: 'Garcia',
|
||||
createdAt: '2015-01-08 00:00:00'
|
||||
firstName: "Olivia",
|
||||
lastName: "William",
|
||||
createdAt: "2012-01-08 00:00:00",
|
||||
},
|
||||
{ firstName: "William", lastName: "Kim", createdAt: "2013-01-08 00:00:00" },
|
||||
{
|
||||
firstName: "Sophia",
|
||||
lastName: "Singh",
|
||||
createdAt: "2013-02-08 00:00:00",
|
||||
},
|
||||
{
|
||||
firstName: 'Isabella',
|
||||
lastName: 'Nguyen',
|
||||
createdAt: '2014-01-08 00:00:00'
|
||||
firstName: "James",
|
||||
lastName: "Rodriguez",
|
||||
createdAt: "2013-03-08 00:00:00",
|
||||
},
|
||||
{ firstName: 'Ethan', lastName: 'Lee', createdAt: '2016-01-08 00:00:00' },
|
||||
{ firstName: 'Mia', lastName: 'Wilson', createdAt: '2017-01-08 00:00:00' },
|
||||
{ firstName: "Ava", lastName: "Patel", createdAt: "2013-01-04 00:00:00" },
|
||||
{
|
||||
firstName: 'Alexander',
|
||||
lastName: 'William',
|
||||
createdAt: '2018-01-08 00:00:00'
|
||||
firstName: "Benjamin",
|
||||
lastName: "Garcia",
|
||||
createdAt: "2015-01-08 00:00:00",
|
||||
},
|
||||
{
|
||||
firstName: 'Charlotte',
|
||||
lastName: 'Hernandez',
|
||||
createdAt: '2019-01-08 00:00:00'
|
||||
firstName: "Isabella",
|
||||
lastName: "Nguyen",
|
||||
createdAt: "2014-01-08 00:00:00",
|
||||
},
|
||||
{ firstName: 'Liam', lastName: 'Gonzalez', createdAt: '2020-01-08 00:00:00' },
|
||||
{ firstName: 'Emma', lastName: 'Gomez', createdAt: '2021-01-08 00:00:00' },
|
||||
{ firstName: 'Noah', lastName: 'Perez', createdAt: '2021-01-08 00:00:00' },
|
||||
{ firstName: 'Avery', lastName: 'Ramirez', createdAt: '2023-02-08 00:00:00' },
|
||||
{ firstName: 'Jacob', lastName: 'Turner', createdAt: '2023-03-08 00:00:00' },
|
||||
{ firstName: "Ethan", lastName: "Lee", createdAt: "2016-01-08 00:00:00" },
|
||||
{ firstName: "Mia", lastName: "Wilson", createdAt: "2017-01-08 00:00:00" },
|
||||
{
|
||||
firstName: 'Abigail',
|
||||
lastName: 'Williams',
|
||||
createdAt: '2023-02-10 00:00:00'
|
||||
firstName: "Alexander",
|
||||
lastName: "William",
|
||||
createdAt: "2018-01-08 00:00:00",
|
||||
},
|
||||
{ firstName: 'Elijah', lastName: 'Hall', createdAt: '2023-04-02 00:00:00' },
|
||||
{ firstName: 'Mila', lastName: 'Flores', createdAt: '2023-05-13 00:00:00' },
|
||||
{
|
||||
firstName: 'Evelyn',
|
||||
lastName: 'Morales',
|
||||
createdAt: '2023-06-05 00:00:00'
|
||||
firstName: "Charlotte",
|
||||
lastName: "Hernandez",
|
||||
createdAt: "2019-01-08 00:00:00",
|
||||
},
|
||||
{ firstName: 'Logan', lastName: 'Collins', createdAt: '2023-06-07 00:00:00' },
|
||||
{ firstName: null, lastName: 'Flores', createdAt: '2023-06-09 00:00:00' }
|
||||
]
|
||||
{
|
||||
firstName: "Liam",
|
||||
lastName: "Gonzalez",
|
||||
createdAt: "2020-01-08 00:00:00",
|
||||
},
|
||||
{ firstName: "Emma", lastName: "Gomez", createdAt: "2021-01-08 00:00:00" },
|
||||
{ firstName: "Noah", lastName: "Perez", createdAt: "2021-01-08 00:00:00" },
|
||||
{
|
||||
firstName: "Avery",
|
||||
lastName: "Ramirez",
|
||||
createdAt: "2023-02-08 00:00:00",
|
||||
},
|
||||
{
|
||||
firstName: "Jacob",
|
||||
lastName: "Turner",
|
||||
createdAt: "2023-03-08 00:00:00",
|
||||
},
|
||||
{
|
||||
firstName: "Abigail",
|
||||
lastName: "Williams",
|
||||
createdAt: "2023-02-10 00:00:00",
|
||||
},
|
||||
{ firstName: "Elijah", lastName: "Hall", createdAt: "2023-04-02 00:00:00" },
|
||||
{ firstName: "Mila", lastName: "Flores", createdAt: "2023-05-13 00:00:00" },
|
||||
{
|
||||
firstName: "Evelyn",
|
||||
lastName: "Morales",
|
||||
createdAt: "2023-06-05 00:00:00",
|
||||
},
|
||||
{
|
||||
firstName: "Logan",
|
||||
lastName: "Collins",
|
||||
createdAt: "2023-06-07 00:00:00",
|
||||
},
|
||||
{ firstName: null, lastName: "Flores", createdAt: "2023-06-09 00:00:00" },
|
||||
];
|
||||
|
||||
const testData = {
|
||||
strongPassword: 'HeK34#C44DMJ',
|
||||
strongPassword2: 'Mk22#c9@Cv!K',
|
||||
weakPassword: '12345678',
|
||||
invalidUsername: 'invalid_username',
|
||||
invalidPassword: 'invalid_password',
|
||||
strongPassword: "HeK34#C44DMJ",
|
||||
strongPassword2: "Mk22#c9@Cv!K",
|
||||
weakPassword: "12345678",
|
||||
invalidUsername: "invalid_username",
|
||||
invalidPassword: "invalid_password",
|
||||
users: {
|
||||
user1: { username: 'Jane' },
|
||||
user2: { username: 'Mike' },
|
||||
user3: { username: 'John' }
|
||||
}
|
||||
}
|
||||
frozenUser1: { username: "Jane" },
|
||||
frozenUser2: { username: "Mary" },
|
||||
weakPasswordUser: { username: "Penny" },
|
||||
changeNameUser: { username: "Mike" },
|
||||
changePasswordUser: { username: "John" },
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = { testNames, testData }
|
||||
module.exports = { testNames, testData };
|
||||
|
||||
Reference in New Issue
Block a user