Skip to content
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
2 changes: 1 addition & 1 deletion backend/model.nlp

Large diffs are not rendered by default.

38 changes: 38 additions & 0 deletions backend/package-lock.json

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

1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^11.1.19",
"@nestjs/platform-socket.io": "^11.1.17",
"@nestjs/serve-static": "^5.0.5",
"@nestjs/swagger": "^11.2.6",
"@nestjs/websockets": "^11.1.17",
"bcrypt": "^6.0.0",
Expand Down
8 changes: 7 additions & 1 deletion backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@ import { CategoryModule } from './modules/category/category.module';
import { TicketModule } from './modules/ticket/ticket.module';
import { EmailModule } from './modules/email/email.module';
import { FileModule } from './modules/file/file.module';
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';

@Module({
imports: [
ServeStaticModule.forRoot({
rootPath: join(process.cwd(), 'uploads'),
serveRoot: '/uploads',
}),
ConfigModule.forRoot({
isGlobal: true,
}),
Expand All @@ -34,4 +40,4 @@ import { FileModule } from './modules/file/file.module';
controllers: [],
providers: [],
})
export class AppModule {}
export class AppModule {}
18 changes: 12 additions & 6 deletions backend/src/modules/Messages/infra/message.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,28 @@ export type MessageDocument = HydratedDocument<Message>;
@Schema({ timestamps: true, collection: 'messages' })
export class Message {
@Prop({ required: true, type: String })
chatId: string;// Referência ao chamado
chatId: string;

@Prop({ required: true, type: String })
senderId: string; // Id do cliente ou atendente que enviou a mensagem
senderId: string;

@Prop({ required: true })
content: string; // Conteúdo do chat
content: string;

@Prop({ default: false })
isSystemMessage: boolean; // Flag para mensagens da URA digital
isSystemMessage: boolean;

@Prop({ required: false })
readAt?: Date; // Controle pra saber se a mensagem foi lida
readAt?: Date;

@Prop({ type: [String], default: [] })
fileIds?: string[]; // Arquivos anexados a mensagem
fileIds?: string[];

@Prop({ required: false, type: String })
attachmentUrl?: string;

@Prop({ required: false, type: String, default: 'TEXT' })
type?: string;
}

export const MessageSchema = SchemaFactory.createForClass(Message);
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export class MessageGateway
senderId: data.senderId,
content: data.content,
isSystemMessage: data.isSystemMessage || false,
attachmentUrl: data.attachmentUrl,
fileIds: data.fileIds || [],
});

Expand Down
132 changes: 36 additions & 96 deletions backend/src/modules/chat/application/chat.file-message.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ import { NotFoundException } from '@nestjs/common';
import type { IChatRepository } from '../domain/chat.repository';
import type { IMessageRepository } from '../../Messages/domain/message.repository';

/* ===========================
MOCKS
=========================== */

const mockChatRepository: jest.Mocked<IChatRepository> = {
create: jest.fn(),
findById: jest.fn(),
Expand All @@ -23,10 +19,6 @@ const mockMessageRepository: jest.Mocked<IMessageRepository> = {
findByChatId: jest.fn(),
};

/* ===========================
CONSTANTES
=========================== */

const CHAT_ID = 'chat-001';
const CLIENT_ID = 'user-001';

Expand All @@ -40,43 +32,24 @@ const mockChat = {
createdAt: new Date(),
};

/* ===========================
TESTES
=========================== */

describe('ChatService — envio de mensagem com arquivo', () => {
let service: ChatService;

beforeEach(async () => {
const module: TestingModule =
await Test.createTestingModule({
providers: [
ChatService,
{
provide: 'IChatRepository',
useValue: mockChatRepository,
},
{
provide: 'IMessageRepository',
useValue: mockMessageRepository,
},
],
}).compile();

service =
module.get<ChatService>(ChatService);

const module: TestingModule = await Test.createTestingModule({
providers: [
ChatService,
{ provide: 'IChatRepository', useValue: mockChatRepository },
{ provide: 'IMessageRepository', useValue: mockMessageRepository },
],
}).compile();

service = module.get<ChatService>(ChatService);
jest.clearAllMocks();
});

/* ===========================
TESTE PRINCIPAL
=========================== */

it('should send message with attached file', async () => {

const FILE_ID = 'file-001';

const mockSavedMessage = {
id: 'msg-001',
chatId: CHAT_ID,
Expand All @@ -87,45 +60,30 @@ describe('ChatService — envio de mensagem com arquivo', () => {
createdAt: new Date(),
};

/* Chat existe */
mockChatRepository
.findById
.mockResolvedValue(mockChat as any);

/* Mensagem salva */
mockMessageRepository
.create
.mockResolvedValue(mockSavedMessage as any);

const result =
await service.sendMessage(
CHAT_ID,
CLIENT_ID,
UserRole.CLIENT,
'Arquivo anexado',
[FILE_ID],
);
mockChatRepository.findById.mockResolvedValue(mockChat as any);
mockMessageRepository.create.mockResolvedValue(mockSavedMessage as any);

expect(result)
.toEqual(mockSavedMessage);
const result = await service.sendMessage(
CHAT_ID,
CLIENT_ID,
UserRole.CLIENT,
'Arquivo anexado',
[FILE_ID],
);

expect(
mockMessageRepository.create
).toHaveBeenCalledWith({
expect(result).toEqual(mockSavedMessage);
expect(mockMessageRepository.create).toHaveBeenCalledWith({
chatId: CHAT_ID,
senderId: CLIENT_ID,
content: 'Arquivo anexado',
isSystemMessage: false,
fileIds: [FILE_ID],
attachmentUrl: undefined,
type: 'TEXT',
});
});

/* ===========================
TESTE: SEM ARQUIVO
=========================== */

it('should send message without fileIds', async () => {

const mockSavedMessage = {
id: 'msg-002',
chatId: CHAT_ID,
Expand All @@ -136,45 +94,30 @@ describe('ChatService — envio de mensagem com arquivo', () => {
createdAt: new Date(),
};

mockChatRepository
.findById
.mockResolvedValue(mockChat as any);

mockMessageRepository
.create
.mockResolvedValue(mockSavedMessage as any);

const result =
await service.sendMessage(
CHAT_ID,
CLIENT_ID,
UserRole.CLIENT,
'Mensagem normal',
);
mockChatRepository.findById.mockResolvedValue(mockChat as any);
mockMessageRepository.create.mockResolvedValue(mockSavedMessage as any);

expect(result)
.toEqual(mockSavedMessage);
const result = await service.sendMessage(
CHAT_ID,
CLIENT_ID,
UserRole.CLIENT,
'Mensagem normal',
);

expect(
mockMessageRepository.create
).toHaveBeenCalledWith({
expect(result).toEqual(mockSavedMessage);
expect(mockMessageRepository.create).toHaveBeenCalledWith({
chatId: CHAT_ID,
senderId: CLIENT_ID,
content: 'Mensagem normal',
isSystemMessage: false,
fileIds: [],
attachmentUrl: undefined,
type: 'TEXT',
});
});

/* ===========================
TESTE: CHAT NÃO EXISTE
=========================== */

it('should throw NotFoundException when chat does not exist', async () => {

mockChatRepository
.findById
.mockResolvedValue(null);
mockChatRepository.findById.mockResolvedValue(null);

await expect(
service.sendMessage(
Expand All @@ -184,9 +127,6 @@ describe('ChatService — envio de mensagem com arquivo', () => {
'Teste',
['file-001'],
)
).rejects.toThrow(
NotFoundException,
);
).rejects.toThrow(NotFoundException);
});

});
Loading
Loading