Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testes para a função updateTrending #560

Closed
wants to merge 2 commits into from
Closed
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
11 changes: 8 additions & 3 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { getJestProjects } from '@nx/jest';

export default {
projects: getJestProjects(),
};
module.exports = {
preset: 'ts-jest', // Usando ts-jest para suportar TypeScript
testEnvironment: 'node', // Ambiente de teste para Node.js
moduleFileExtensions: ['js', 'json', 'ts'],
testMatch: ['**/tests/**/*.spec.ts', '**/tests/**/*.test.ts'], // Caminho para os testes
testPathIgnorePatterns: ['/node_modules/'], // Ignora a pasta node_modules
verbose: true, // Exibe detalhes adicionais ao executar os testes
};
3 changes: 2 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"prisma-reset": "cd ./libraries/nestjs-libraries/src/database/prisma && npx prisma db push --force-reset && npx prisma db push",
"docker-build": "./var/docker/docker-build.sh",
"docker-create": "./var/docker/docker-create.sh",
"postinstall": "npm run update-plugins && npm run prisma-generate"
"postinstall": "npm run update-plugins && npm run prisma-generate",
"test": "jest"
},
"private": true,
"dependencies": {
Expand Down Expand Up @@ -202,7 +203,7 @@
"eslint-plugin-jsx-a11y": "6.7.1",
"eslint-plugin-react": "7.32.2",
"eslint-plugin-react-hooks": "4.6.0",
"jest": "29.7.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "29.7.0",
"jest-environment-node": "^29.4.1",
"jsdom": "~22.1.0",
Expand Down
160 changes: 160 additions & 0 deletions tests/updateTrending.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { StarsService } from '@gitroom/nestjs-libraries/database/prisma/stars/stars.service';
import { HttpException } from '@nestjs/common';

// Mock das dependências
jest.mock('@gitroom/nestjs-libraries/database/prisma/stars/stars.repository');
jest.mock('@gitroom/nestjs-libraries/database/prisma/notifications/notification.service');
jest.mock('@gitroom/nestjs-libraries/bull-mq-transport-new/client');

const mockStarsRepository = {
getTrendingByLanguage: jest.fn(),
};

const mockNotificationService = {
inform: jest.fn(),
};

const mockReplaceOrAddTrending = jest.fn();

class YourService {
private _starsRepository;
private notificationService;
private replaceOrAddTrending;

constructor(
_starsRepository: any,
notificationService: any,
replaceOrAddTrending: any
) {
this._starsRepository = _starsRepository;
this.notificationService = notificationService;
this.replaceOrAddTrending = replaceOrAddTrending;
}
Comment on lines +19 to +32
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve type safety in the service class.

The service class uses any types which should be avoided for better type safety.

+interface TrendingItem {
+  name: string;
+  position: number;
+}
+
 class YourService {
-  private _starsRepository;
-  private notificationService;
-  private replaceOrAddTrending;
+  private _starsRepository: StarsRepository;
+  private notificationService: NotificationService;
+  private replaceOrAddTrending: (language: string, hash: string, arr: TrendingItem[]) => Promise<void>;

   constructor(
-    _starsRepository: any,
-    notificationService: any,
-    replaceOrAddTrending: any
+    _starsRepository: StarsRepository,
+    notificationService: NotificationService,
+    replaceOrAddTrending: (language: string, hash: string, arr: TrendingItem[]) => Promise<void>
   )

Committable suggestion skipped: line range outside the PR's diff.


async updateTrending(
language: string,
hash: string,
arr: Array<{ name: string; position: number }>
) {
const currentTrending = await this._starsRepository.getTrendingByLanguage(language);

if (currentTrending?.hash === hash) {
return;
}

if (currentTrending) {
const list: Array<{ name: string; position: number }> = JSON.parse(
currentTrending.trendingList
);
const removedFromTrending = list.filter(
(p) => !arr.find((a) => a.name === p.name)
);
const changedPosition = arr.filter((p) => {
const current = list.find((a) => a.name === p.name);
return current && current.position !== p.position;
});
if (removedFromTrending.length) {
await this.notificationService.inform(Inform.Removed, removedFromTrending, language);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Define the Inform enum for better code clarity.

The Inform enum is used but not defined, which could lead to maintenance issues.

+enum Inform {
+  Removed = 1,
+  Changed = 2,
+  New = 3
+}
+
 // ... rest of the code

Also applies to: 60-60, 68-68

}
if (changedPosition.length) {
await this.notificationService.inform(Inform.Changed, changedPosition, language);
}
}

const informNewPeople = arr.filter(
(p) => !currentTrending?.trendingList || currentTrending?.trendingList.indexOf(p.name) === -1
);

await this.notificationService.inform(Inform.New, informNewPeople, language);
await this.replaceOrAddTrending(language, hash, arr);
}
Comment on lines +34 to +70
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add input validation to updateTrending method.

The method should validate its inputs before processing to prevent invalid data.

   async updateTrending(
     language: string,
     hash: string,
     arr: Array<{ name: string; position: number }>
   ) {
+    if (!language || typeof language !== 'string') {
+      throw new Error('Invalid language parameter');
+    }
+    if (!hash || typeof hash !== 'string') {
+      throw new Error('Invalid hash parameter');
+    }
+    if (!Array.isArray(arr)) {
+      throw new Error('Invalid array parameter');
+    }
+    for (const item of arr) {
+      if (!item.name || typeof item.name !== 'string') {
+        throw new Error('Invalid item name');
+      }
+      if (typeof item.position !== 'number' || isNaN(item.position)) {
+        throw new Error('Invalid position value');
+      }
+    }
+
     const currentTrending = await this._starsRepository.getTrendingByLanguage(language);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async updateTrending(
language: string,
hash: string,
arr: Array<{ name: string; position: number }>
) {
const currentTrending = await this._starsRepository.getTrendingByLanguage(language);
if (currentTrending?.hash === hash) {
return;
}
if (currentTrending) {
const list: Array<{ name: string; position: number }> = JSON.parse(
currentTrending.trendingList
);
const removedFromTrending = list.filter(
(p) => !arr.find((a) => a.name === p.name)
);
const changedPosition = arr.filter((p) => {
const current = list.find((a) => a.name === p.name);
return current && current.position !== p.position;
});
if (removedFromTrending.length) {
await this.notificationService.inform(Inform.Removed, removedFromTrending, language);
}
if (changedPosition.length) {
await this.notificationService.inform(Inform.Changed, changedPosition, language);
}
}
const informNewPeople = arr.filter(
(p) => !currentTrending?.trendingList || currentTrending?.trendingList.indexOf(p.name) === -1
);
await this.notificationService.inform(Inform.New, informNewPeople, language);
await this.replaceOrAddTrending(language, hash, arr);
}
async updateTrending(
language: string,
hash: string,
arr: Array<{ name: string; position: number }>
) {
if (!language || typeof language !== 'string') {
throw new Error('Invalid language parameter');
}
if (!hash || typeof hash !== 'string') {
throw new Error('Invalid hash parameter');
}
if (!Array.isArray(arr)) {
throw new Error('Invalid array parameter');
}
for (const item of arr) {
if (!item.name || typeof item.name !== 'string') {
throw new Error('Invalid item name');
}
if (typeof item.position !== 'number' || isNaN(item.position)) {
throw new Error('Invalid position value');
}
}
const currentTrending = await this._starsRepository.getTrendingByLanguage(language);
if (currentTrending?.hash === hash) {
return;
}
if (currentTrending) {
const list: Array<{ name: string; position: number }> = JSON.parse(
currentTrending.trendingList
);
const removedFromTrending = list.filter(
(p) => !arr.find((a) => a.name === p.name)
);
const changedPosition = arr.filter((p) => {
const current = list.find((a) => a.name === p.name);
return current && current.position !== p.position;
});
if (removedFromTrending.length) {
await this.notificationService.inform(Inform.Removed, removedFromTrending, language);
}
if (changedPosition.length) {
await this.notificationService.inform(Inform.Changed, changedPosition, language);
}
}
const informNewPeople = arr.filter(
(p) => !currentTrending?.trendingList || currentTrending?.trendingList.indexOf(p.name) === -1
);
await this.notificationService.inform(Inform.New, informNewPeople, language);
await this.replaceOrAddTrending(language, hash, arr);
}

}

describe('updateTrending', () => {
let service: YourService;

beforeEach(() => {
service = new YourService(
mockStarsRepository,
mockNotificationService,
mockReplaceOrAddTrending
);
});

afterEach(() => {
jest.clearAllMocks();
});

test('CT1: Teste com data vazio', async () => {
const data: Array<{ name: string; position: number }> = [];

await service.updateTrending('en', 'hash1', data);

expect(mockStarsRepository.getTrendingByLanguage).not.toHaveBeenCalled();
expect(mockNotificationService.inform).not.toHaveBeenCalled();
expect(mockReplaceOrAddTrending).not.toHaveBeenCalled();
});
Comment on lines +88 to +96
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance empty data test case.

The test case for empty data should verify that the function handles the case gracefully, but it's currently not testing the actual behavior.

-  test('CT1: Teste com data vazio', async () => {
+  test('should not process updates when trending list is empty', async () => {
     const data: Array<{ name: string; position: number }> = [];
 
     await service.updateTrending('en', 'hash1', data);
 
-    expect(mockStarsRepository.getTrendingByLanguage).not.toHaveBeenCalled();
+    expect(mockStarsRepository.getTrendingByLanguage).toHaveBeenCalledWith('en');
     expect(mockNotificationService.inform).not.toHaveBeenCalled();
     expect(mockReplaceOrAddTrending).not.toHaveBeenCalled();
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test('CT1: Teste com data vazio', async () => {
const data: Array<{ name: string; position: number }> = [];
await service.updateTrending('en', 'hash1', data);
expect(mockStarsRepository.getTrendingByLanguage).not.toHaveBeenCalled();
expect(mockNotificationService.inform).not.toHaveBeenCalled();
expect(mockReplaceOrAddTrending).not.toHaveBeenCalled();
});
test('should not process updates when trending list is empty', async () => {
const data: Array<{ name: string; position: number }> = [];
await service.updateTrending('en', 'hash1', data);
expect(mockStarsRepository.getTrendingByLanguage).toHaveBeenCalledWith('en');
expect(mockNotificationService.inform).not.toHaveBeenCalled();
expect(mockReplaceOrAddTrending).not.toHaveBeenCalled();
});


test('CT2: Teste com data válida e valores corretos', async () => {
const data: Array<{ name: string; position: number }> = [
{ name: 'item1', position: 5 },
{ name: 'item2', position: 3 },
];

mockStarsRepository.getTrendingByLanguage.mockResolvedValue({
hash: 'hash1',
trendingList: JSON.stringify([{ name: 'item3', position: 6 }]),
});

await service.updateTrending('en', 'hash2', data);

expect(mockStarsRepository.getTrendingByLanguage).toHaveBeenCalledWith('en');
expect(mockNotificationService.inform).toHaveBeenCalledWith(
expect.any(Number),
expect.any(Array),
'en'
);
expect(mockReplaceOrAddTrending).toHaveBeenCalledWith('en', 'hash2', data);
});

test('CT3: Teste com data contendo valores inválidos', async () => {
const data: Array<{ name: string; position: number }> = [
{ name: 'item1', position: Number('string') }, // Garantir que seja número
{ name: 'item2', position: 3 },
];

mockStarsRepository.getTrendingByLanguage.mockResolvedValue({
hash: 'hash1',
trendingList: JSON.stringify([{ name: 'item3', position: 6 }]),
});

await service.updateTrending('en', 'hash2', data);

expect(mockStarsRepository.getTrendingByLanguage).toHaveBeenCalled();
expect(mockNotificationService.inform).not.toHaveBeenCalled();
expect(mockReplaceOrAddTrending).not.toHaveBeenCalled();
});
Comment on lines +120 to +136
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve invalid data test case.

The test case for invalid data should be more explicit about what makes the data invalid and what behavior is expected.

-  test('CT3: Teste com data contendo valores inválidos', async () => {
+  test('should not process updates when position values are invalid numbers', async () => {
     const data: Array<{ name: string; position: number }> = [
-      { name: 'item1', position: Number('string') }, // Garantir que seja número
+      { name: 'item1', position: NaN }, // Invalid position
       { name: 'item2', position: 3 },
     ];
 
     mockStarsRepository.getTrendingByLanguage.mockResolvedValue({
       hash: 'hash1',
       trendingList: JSON.stringify([{ name: 'item3', position: 6 }]),
     });
 
     await service.updateTrending('en', 'hash2', data);
 
     expect(mockStarsRepository.getTrendingByLanguage).toHaveBeenCalled();
-    expect(mockNotificationService.inform).not.toHaveBeenCalled();
-    expect(mockReplaceOrAddTrending).not.toHaveBeenCalled();
+    expect(() => service.updateTrending('en', 'hash2', data)).rejects.toThrow('Invalid position value');
   });

Committable suggestion skipped: line range outside the PR's diff.


test('CT4: Teste com dados válidos, garantindo a chamada de saveTrending', async () => {
const data: Array<{ name: string; position: number }> = [
{ name: 'item1', position: 5 },
{ name: 'item2', position: 3 },
{ name: 'item3', position: 7 },
];

mockStarsRepository.getTrendingByLanguage.mockResolvedValue({
hash: 'hash1',
trendingList: JSON.stringify([{ name: 'item3', position: 6 }]),
});

await service.updateTrending('en', 'hash2', data);

expect(mockStarsRepository.getTrendingByLanguage).toHaveBeenCalled();
expect(mockNotificationService.inform).toHaveBeenCalledWith(
expect.any(Number),
expect.any(Array),
'en'
);
expect(mockReplaceOrAddTrending).toHaveBeenCalledWith('en', 'hash2', data);
});
});
8 changes: 7 additions & 1 deletion tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,11 @@
"@gitroom/workers/*": ["apps/workers/src/*"],
}
},
"exclude": ["node_modules", "tmp"]
"exclude": ["node_modules", "tmp"],
"include": [
"tests/**/*.spec.ts",
"tests/**/*.test.ts",
"libs/**/*.ts",
"apps/**/*.ts"
]
}