apps/bot: add vitest

This commit is contained in:
vchikalkin 2024-12-23 18:30:55 +03:00
parent 44d5c77037
commit 23e29f90cd
7 changed files with 103 additions and 35 deletions

View File

@ -16,7 +16,8 @@
"start": "node dist/src/index.js",
"dev": "dotenv -e ../../.env.local tsx watch src/index.ts",
"lint": "eslint",
"lint-staged": "lint-staged"
"lint-staged": "lint-staged",
"test:unit": "vitest"
},
"devDependencies": {
"@repo/eslint-config": "workspace:*",
@ -29,6 +30,8 @@
"lint-staged": "catalog:",
"rimraf": "catalog:",
"tsx": "catalog:",
"typescript": "catalog:"
"typescript": "catalog:",
"vite-tsconfig-paths": "catalog:",
"vitest": "catalog:"
}
}

View File

@ -1,4 +1,5 @@
import { getClientWithToken } from '../apollo/client';
import { createApolloClient, getClientWithToken } from '../apollo/client';
import { env as environment } from '../config/env';
import * as GQL from '@repo/graphql/types';
export async function createCustomer(variables: GQL.CreateCustomerMutationVariables) {
@ -18,3 +19,17 @@ export async function getCustomer(variables: GQL.GetCustomerQueryVariables) {
variables,
});
}
export async function login() {
const { mutate } = createApolloClient();
const response = await mutate({
mutation: GQL.LoginDocument,
variables: {
identifier: environment.LOGIN_GRAPHQL,
password: environment.PASSWORD_GRAPHQL,
},
});
return response;
}

View File

@ -1,5 +1,5 @@
import { env as environment } from '../config/env';
import { getToken } from '../utils/jwt';
import { getToken } from '../config/token';
import { ApolloClient, InMemoryCache } from '@apollo/client/core';
type Parameters_ = { token: null | string | undefined };

View File

@ -0,0 +1,13 @@
import { login } from '../api/query';
import { isTokenExpired } from '../utils/jwt';
export const token: null | string = null;
export async function getToken() {
if (!token || isTokenExpired(token)) {
const response = await login();
return response?.data?.login.jwt;
}
return token;
}

View File

@ -0,0 +1,61 @@
/* eslint-disable unicorn/consistent-function-scoping */
import { isTokenExpired } from './jwt';
import * as jwt from 'jsonwebtoken';
import { afterEach, describe, expect, it, vi } from 'vitest';
describe('isTokenExpired', () => {
const mockDateNow = (timestamp: number) => {
vi.spyOn(Date, 'now').mockReturnValue(timestamp);
};
afterEach(() => {
vi.restoreAllMocks(); // Сбрасываем все моки после каждого теста
});
it('should return true if the token is expired', () => {
const token = jwt.sign({}, 'secret', { expiresIn: -10 }); // Токен с истекшим временем
mockDateNow(Date.now());
const result = isTokenExpired(token);
expect(result).toBe(true);
});
it('should return false if the token is not expired', () => {
const token = jwt.sign({}, 'secret', { expiresIn: 3_600 }); // Токен с временем жизни 1 час
mockDateNow(Date.now());
const result = isTokenExpired(token);
expect(result).toBe(false);
});
it('should return true if the token will expire within the threshold', () => {
const threshold = 300; // 5 минут
const token = jwt.sign({}, 'secret', { expiresIn: threshold - 10 }); // Токен с временем жизни чуть меньше порога
mockDateNow(Date.now());
const result = isTokenExpired(token, threshold);
expect(result).toBe(true);
});
it('should return false if the token will expire outside the threshold', () => {
const threshold = 300; // 5 минут
const token = jwt.sign({}, 'secret', { expiresIn: threshold + 100 }); // Токен с временем жизни больше порога
mockDateNow(Date.now());
const result = isTokenExpired(token, threshold);
expect(result).toBe(false);
});
it('should return true if the token is invalid', () => {
const invalidToken = 'invalid.token.string';
const result = isTokenExpired(invalidToken);
expect(result).toBe(true);
});
it("should throw an error if the token doesn't have an exp field", () => {
const token = jwt.sign({ data: 'no exp' }, 'secret', { noTimestamp: true }); // Токен без exp
const result = isTokenExpired(token);
expect(result).toBe(true); // Ожидается, что вернётся true
});
});

View File

@ -1,22 +1,6 @@
import { createApolloClient } from '../apollo/client';
import { env as environment } from '../config/env';
import * as GQL from '@repo/graphql/types';
import * as jwt from 'jsonwebtoken';
const token: null | string = null;
export async function getToken() {
if (!token || isTokenExpired()) {
const response = await login();
return response?.data?.login.jwt;
}
return token;
}
function isTokenExpired(threshold: number = 300) {
if (!token) throw new Error('Token is missing');
export function isTokenExpired(token: string, threshold: number = 300) {
try {
const decoded = jwt.decode(token);
@ -32,17 +16,3 @@ function isTokenExpired(threshold: number = 300) {
return true;
}
}
async function login() {
const { mutate } = createApolloClient();
const response = await mutate({
mutation: GQL.LoginDocument,
variables: {
identifier: environment.LOGIN_GRAPHQL,
password: environment.PASSWORD_GRAPHQL,
},
});
return response;
}

6
pnpm-lock.yaml generated
View File

@ -213,6 +213,12 @@ importers:
typescript:
specifier: 'catalog:'
version: 5.7.2
vite-tsconfig-paths:
specifier: 'catalog:'
version: 5.1.4(typescript@5.7.2)(vite@5.4.11(@types/node@20.17.8))
vitest:
specifier: 'catalog:'
version: 2.1.8(@types/node@20.17.8)(jsdom@25.0.1)
apps/web:
dependencies: