Skip to content
This repository has been archived by the owner on Apr 12, 2022. It is now read-only.

test: Add unit/integration tests #7

Merged
merged 5 commits into from
Mar 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -304,3 +304,7 @@ GitHub.sublime-settings

# Build Directory
.build/

# Documents
docs/api/swagger.yaml
docs/code/
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ XCoins code review repository.
- [Prerequisites](#prerequisites)
- [Usage](#usage)
- [Build](#build)
- [Test](#test)
- [Code Style](#code-style)
- [Config](#config)
- [Common](#common-config)
Expand Down Expand Up @@ -51,6 +52,26 @@ Watch for changes and rebuild automatically.
npm run build:watch
```

### Test

Test the project. (Using _Jest_[^JEST_FOOTNOTE])

```shell
npm test
```

Test with code coverage report.

```shell
npm run test:coverage
```

Test in ci.

```shell
npm run test:ci
```

### Code Style

Check for code style issues in the project. (Using _ESLint_[^ESLINT_FOOTNOTE])
Expand Down Expand Up @@ -157,6 +178,11 @@ npm run docs:api
```
.
├── .build # Project (TypeScript) build directory
├── __tests__ # Test files
│ ├── controllers # API controller integration tests
│ ├── jest # Test utilities
│ ├── lib # Library unit tests
│ └── utils # Utility unit tests
├── docs # Static documents
│ ├── api # API documents
│ └── code # Code documents
Expand All @@ -170,6 +196,8 @@ npm run docs:api
└── utils # Project utilities
```

> Note: `__mocks__` directories are used by `Jest` to mock certain modules.

## Versioning

This project uses _SemVer_[^SEMVER_FOOTNOTE] for versioning. For the versions & changelogs available, see the releases
Expand All @@ -181,6 +209,7 @@ This section includes the issues, changes & improvements I've made, with the tho

### Issues

- No unit/integration tests.
- Missing documents.
- No usage document is provided in the `README.md` file.
- No comments or documents are provided for the project. (e.g. `jsdoc`, `tsdoc`, `typedoc`)
Expand Down Expand Up @@ -289,6 +318,8 @@ This section includes the issues, changes & improvements I've made, with the tho

[^TYPESCRIPT_FOOTNOTE]: [TypeScript](https://www.typescriptlang.org)

[^JEST_FOOTNOTE]: [Jest](https://jestjs.io)

[^ESLINT_FOOTNOTE]: [ESLint](https://eslint.org)

[^MONGODB_FOOTNOTE]: [MongoDB](https://www.mongodb.com)
Expand Down
135 changes: 135 additions & 0 deletions __tests__/controllers/v1/favorites/list.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { factory, http, serialize } from "#test";
import { orderBy } from "lodash";
import { Types } from "mongoose";

it("should respond with empty favorites", async () => {
await http()
.get("/v1/favorites")
.expect(200)
.expect({
favorites: [],
meta: {
skip: 0,
limit: 50,
page_count: 0,
total_count: 0,
},
});
});

it("should respond with the favorites", async () => {
const favorites = [
await factory.favorite(),
await factory.favorite(),
await factory.favorite(),
];

await http()
.get("/v1/favorites")
.expect(200)
.expect({
favorites: orderBy(serialize(favorites), ["created_at"], "desc"),
meta: {
skip: 0,
limit: 50,
page_count: favorites.length,
total_count: favorites.length,
},
});
});

it("should respond with the favorites (filtered by profile_id)", async () => {
const profile_id = new Types.ObjectId();

const [, ...favorites] = [
await factory.favorite(),
await factory.favorite({ profile_id }),
await factory.favorite({ profile_id }),
];

await http()
.get(`/v1/favorites/${profile_id}`)
.expect(200)
.expect({
favorites: orderBy(serialize(favorites), ["created_at"], "desc"),
meta: {
skip: 0,
limit: 50,
page_count: favorites.length,
total_count: favorites.length,
},
});
});

it("should respond with the favorites (while skipping 1 record)", async () => {
const favorites = [
await factory.favorite(),
await factory.favorite(),
await factory.favorite(),
];

await http()
.get("/v1/favorites")
.query({
skip: 1,
})
.expect(200)
.expect({
favorites: orderBy(serialize(favorites), ["created_at"], "desc").slice(1),
meta: {
skip: 1,
limit: 50,
page_count: favorites.length - 1,
total_count: favorites.length,
},
});
});

it("should respond with the favorites (while limiting 1 record)", async () => {
const favorites = [
await factory.favorite(),
await factory.favorite(),
await factory.favorite(),
];

await http()
.get("/v1/favorites")
.query({
limit: 1,
})
.expect(200)
.expect({
favorites: orderBy(serialize(favorites), ["created_at"], "desc").slice(0, 1),
meta: {
skip: 0,
limit: 1,
page_count: 1,
total_count: favorites.length,
},
});
});

it("should respond with the favorites (while skipping 1 & limiting 1 record)", async () => {
const favorites = [
await factory.favorite(),
await factory.favorite(),
await factory.favorite(),
];

await http()
.get("/v1/favorites")
.query({
skip: 1,
limit: 1,
})
.expect(200)
.expect({
favorites: orderBy(serialize(favorites), ["created_at"], "desc").slice(1, 2),
meta: {
skip: 1,
limit: 1,
page_count: 1,
total_count: favorites.length,
},
});
});
111 changes: 111 additions & 0 deletions __tests__/controllers/v1/profiles/list.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { factory, http, serialize } from "#test";
import { orderBy } from "lodash";

it("should respond with empty profiles", async () => {
await http()
.get("/v1/profiles")
.expect(200)
.expect({
profiles: [],
meta: {
skip: 0,
limit: 50,
page_count: 0,
total_count: 0,
},
});
});

it("should respond with the profiles", async () => {
const profiles = [
await factory.profile(),
await factory.profile(),
await factory.profile(),
];

await http()
.get("/v1/profiles")
.expect(200)
.expect({
profiles: orderBy(serialize(profiles), ["created_at"], "desc"),
meta: {
skip: 0,
limit: 50,
page_count: profiles.length,
total_count: profiles.length,
},
});
});

it("should respond with the profiles (while skipping 1 record)", async () => {
const profiles = [
await factory.profile(),
await factory.profile(),
await factory.profile(),
];

await http()
.get("/v1/profiles")
.query({
skip: 1,
})
.expect(200)
.expect({
profiles: orderBy(serialize(profiles), ["created_at"], "desc").slice(1),
meta: {
skip: 1,
limit: 50,
page_count: profiles.length - 1,
total_count: profiles.length,
},
});
});

it("should respond with the profiles (while limiting 1 record)", async () => {
const profiles = [
await factory.profile(),
await factory.profile(),
await factory.profile(),
];

await http()
.get("/v1/profiles")
.query({
limit: 1,
})
.expect(200)
.expect({
profiles: orderBy(serialize(profiles), ["created_at"], "desc").slice(0, 1),
meta: {
skip: 0,
limit: 1,
page_count: 1,
total_count: profiles.length,
},
});
});

it("should respond with the profiles (while skipping 1 & limiting 1 record)", async () => {
const profiles = [
await factory.profile(),
await factory.profile(),
await factory.profile(),
];

await http()
.get("/v1/profiles")
.query({
skip: 1,
limit: 1,
})
.expect(200)
.expect({
profiles: orderBy(serialize(profiles), ["created_at"], "desc").slice(1, 2),
meta: {
skip: 1,
limit: 1,
page_count: 1,
total_count: profiles.length,
},
});
});
57 changes: 57 additions & 0 deletions __tests__/controllers/v1/profiles/post.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { factory, http, serialize } from "#test";

it("should create a new profile", async () => {
const profile = {
email: "[email protected]",
name: "John Due",
nickname: "John",
} as const;

const { body } = await http()
.post("/v1/profiles")
.send({ profile })
.expect(200);

expect(body).toMatchObject({ profile });
});

it("should respond the already existing profile", async () => {
const data = {
email: "[email protected]",
name: "John Due",
nickname: "John",
} as const;

const profile = await factory.profile(data);

await http()
.post("/v1/profiles")
.send({ profile: data })
.expect(200)
.expect({ profile: serialize(profile) });
});

it("should fail creating a new profile", async () => {
const profile = {
email: "[email protected]",
name: "John Due",
nickname: 1,
} as const;

await http()
.post("/v1/profiles")
.send({ profile })
.expect(400)
.expect({
error: "Bad Request",
message: "Validation failed",
statusCode: 400,
validation: {
body: {
keys: ["profile.nickname"],
message: "\"profile.nickname\" must be a string",
source: "body",
},
},
});
});
Loading