445 lines
16 KiB
JavaScript
445 lines
16 KiB
JavaScript
const request = require('supertest');
|
|
const app = require('../../app');
|
|
const { InboxItem, User } = require('../../models');
|
|
const { createTestUser } = require('../helpers/testUtils');
|
|
|
|
describe('Inbox Routes', () => {
|
|
let user, agent;
|
|
|
|
beforeEach(async () => {
|
|
user = await createTestUser({
|
|
email: 'test@example.com',
|
|
});
|
|
|
|
// Create authenticated agent
|
|
agent = request.agent(app);
|
|
await agent.post('/api/login').send({
|
|
email: 'test@example.com',
|
|
password: 'password123',
|
|
});
|
|
});
|
|
|
|
describe('POST /api/inbox', () => {
|
|
it('should create a new inbox item', async () => {
|
|
const inboxData = {
|
|
content: 'Remember to buy groceries',
|
|
source: 'web',
|
|
};
|
|
|
|
const response = await agent.post('/api/inbox').send(inboxData);
|
|
|
|
expect(response.status).toBe(201);
|
|
expect(response.body.content).toBe(inboxData.content);
|
|
expect(response.body.source).toBe(inboxData.source);
|
|
expect(response.body.status).toBe('added');
|
|
expect(response.body.uid).toBeDefined();
|
|
expect(typeof response.body.uid).toBe('string');
|
|
});
|
|
|
|
it('should require authentication', async () => {
|
|
const inboxData = {
|
|
content: 'Test content',
|
|
};
|
|
|
|
const response = await request(app)
|
|
.post('/api/inbox')
|
|
.send(inboxData);
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.body.error).toBe('Authentication required');
|
|
});
|
|
|
|
it('should require content', async () => {
|
|
const inboxData = {};
|
|
|
|
const response = await agent.post('/api/inbox').send(inboxData);
|
|
|
|
expect(response.status).toBe(400);
|
|
expect(response.body.error).toBe('Content is required');
|
|
});
|
|
});
|
|
|
|
describe('GET /api/inbox', () => {
|
|
let inboxItem1, inboxItem2;
|
|
|
|
beforeEach(async () => {
|
|
inboxItem1 = await InboxItem.create({
|
|
content: 'First item',
|
|
status: 'added',
|
|
source: 'test',
|
|
user_id: user.id,
|
|
});
|
|
|
|
inboxItem2 = await InboxItem.create({
|
|
content: 'Second item',
|
|
status: 'processed',
|
|
source: 'test',
|
|
user_id: user.id,
|
|
});
|
|
});
|
|
|
|
it('should get all user inbox items', async () => {
|
|
const response = await agent.get('/api/inbox');
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(Array.isArray(response.body)).toBe(true);
|
|
expect(response.body.length).toBe(1); // Only items with status 'added' are returned
|
|
expect(response.body.map((i) => i.uid)).toContain(inboxItem1.uid);
|
|
});
|
|
|
|
it('should only return items with added status', async () => {
|
|
const response = await agent.get('/api/inbox');
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.length).toBe(1);
|
|
expect(response.body[0].uid).toBe(inboxItem1.uid);
|
|
expect(response.body[0].status).toBe('added');
|
|
});
|
|
|
|
it('should return inbox items ordered by created_at DESC (newest first)', async () => {
|
|
// Create additional items with slight delay to ensure different timestamps
|
|
const item1 = await InboxItem.create({
|
|
content: 'First item (oldest)',
|
|
status: 'added',
|
|
source: 'test',
|
|
user_id: user.id,
|
|
});
|
|
|
|
// Small delay to ensure different timestamps
|
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
|
|
const item2 = await InboxItem.create({
|
|
content: 'Second item',
|
|
status: 'added',
|
|
source: 'test',
|
|
user_id: user.id,
|
|
});
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
|
|
const item3 = await InboxItem.create({
|
|
content: 'Third item (newest)',
|
|
status: 'added',
|
|
source: 'test',
|
|
user_id: user.id,
|
|
});
|
|
|
|
const response = await agent.get('/api/inbox');
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.length).toBe(4); // Including the item from beforeEach
|
|
|
|
// Check that items are ordered by newest first
|
|
expect(response.body[0].uid).toBe(item3.uid);
|
|
expect(response.body[1].uid).toBe(item2.uid);
|
|
expect(response.body[2].uid).toBe(item1.uid);
|
|
|
|
// Verify the content matches expected order
|
|
expect(response.body[0].content).toBe('Third item (newest)');
|
|
expect(response.body[1].content).toBe('Second item');
|
|
expect(response.body[2].content).toBe('First item (oldest)');
|
|
});
|
|
|
|
it('should require authentication', async () => {
|
|
const response = await request(app).get('/api/inbox');
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.body.error).toBe('Authentication required');
|
|
});
|
|
|
|
it('should support pagination with limit and offset', async () => {
|
|
// Create 25 inbox items
|
|
const items = [];
|
|
for (let i = 1; i <= 25; i++) {
|
|
const item = await InboxItem.create({
|
|
content: `Item ${i}`,
|
|
status: 'added',
|
|
source: 'test',
|
|
user_id: user.id,
|
|
});
|
|
items.push(item);
|
|
// Small delay to ensure different timestamps
|
|
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
}
|
|
|
|
// Test first page (default limit of 20)
|
|
const response1 = await agent.get('/api/inbox?limit=20&offset=0');
|
|
expect(response1.status).toBe(200);
|
|
expect(response1.body.items.length).toBe(20);
|
|
expect(response1.body.pagination.total).toBe(26); // 25 + 1 from beforeEach
|
|
expect(response1.body.pagination.hasMore).toBe(true);
|
|
expect(response1.body.pagination.offset).toBe(0);
|
|
expect(response1.body.pagination.limit).toBe(20);
|
|
|
|
// Test second page
|
|
const response2 = await agent.get('/api/inbox?limit=20&offset=20');
|
|
expect(response2.status).toBe(200);
|
|
expect(response2.body.items.length).toBe(6); // Remaining items
|
|
expect(response2.body.pagination.total).toBe(26);
|
|
expect(response2.body.pagination.hasMore).toBe(false);
|
|
expect(response2.body.pagination.offset).toBe(20);
|
|
});
|
|
|
|
it('should support loading more than 20 items at once', async () => {
|
|
// Create 30 inbox items
|
|
for (let i = 1; i <= 30; i++) {
|
|
await InboxItem.create({
|
|
content: `Item ${i}`,
|
|
status: 'added',
|
|
source: 'test',
|
|
user_id: user.id,
|
|
});
|
|
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
}
|
|
|
|
// Request 40 items (should get all 31: 30 + 1 from beforeEach)
|
|
const response = await agent.get('/api/inbox?limit=40&offset=0');
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.items.length).toBe(31);
|
|
expect(response.body.pagination.total).toBe(31);
|
|
expect(response.body.pagination.hasMore).toBe(false);
|
|
expect(response.body.pagination.limit).toBe(40);
|
|
});
|
|
|
|
it('should return items in newest-first order when paginating', async () => {
|
|
// Create 25 inbox items
|
|
for (let i = 1; i <= 25; i++) {
|
|
await InboxItem.create({
|
|
content: `Item ${i}`,
|
|
status: 'added',
|
|
source: 'test',
|
|
user_id: user.id,
|
|
});
|
|
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
}
|
|
|
|
// Get first page
|
|
const response = await agent.get('/api/inbox?limit=20&offset=0');
|
|
expect(response.status).toBe(200);
|
|
|
|
// Verify newest items are first
|
|
const items = response.body.items;
|
|
expect(items[0].content).toBe('Item 25'); // Newest
|
|
expect(items[19].content).toBe('Item 6'); // 20th item
|
|
});
|
|
});
|
|
|
|
describe('GET /api/inbox/:uid', () => {
|
|
let inboxItem;
|
|
|
|
beforeEach(async () => {
|
|
inboxItem = await InboxItem.create({
|
|
content: 'Test content',
|
|
source: 'test',
|
|
user_id: user.id,
|
|
});
|
|
});
|
|
|
|
it('should get inbox item by uid', async () => {
|
|
const response = await agent.get(`/api/inbox/${inboxItem.uid}`);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.uid).toBe(inboxItem.uid);
|
|
expect(response.body.content).toBe(inboxItem.content);
|
|
});
|
|
|
|
it('should return 400 for invalid uid format', async () => {
|
|
const response = await agent.get('/api/inbox/invalid-uid');
|
|
|
|
expect(response.status).toBe(400);
|
|
expect(response.body.error).toBe('Invalid UID');
|
|
});
|
|
|
|
it('should return 404 for non-existent inbox item', async () => {
|
|
const response = await agent.get('/api/inbox/abcd1234efghijk');
|
|
|
|
expect(response.status).toBe(404);
|
|
expect(response.body.error).toBe('Inbox item not found.');
|
|
});
|
|
|
|
it("should not allow access to other user's inbox items", async () => {
|
|
const bcrypt = require('bcrypt');
|
|
const otherUser = await User.create({
|
|
email: 'other@example.com',
|
|
password_digest: await bcrypt.hash('password123', 10),
|
|
});
|
|
|
|
const otherInboxItem = await InboxItem.create({
|
|
content: 'Other content',
|
|
source: 'test',
|
|
user_id: otherUser.id,
|
|
});
|
|
|
|
const response = await agent.get(
|
|
`/api/inbox/${otherInboxItem.uid}`
|
|
);
|
|
|
|
expect(response.status).toBe(404);
|
|
expect(response.body.error).toBe('Inbox item not found.');
|
|
});
|
|
|
|
it('should require authentication', async () => {
|
|
const response = await request(app).get(
|
|
`/api/inbox/${inboxItem.uid}`
|
|
);
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.body.error).toBe('Authentication required');
|
|
});
|
|
});
|
|
|
|
describe('PATCH /api/inbox/:uid', () => {
|
|
let inboxItem;
|
|
|
|
beforeEach(async () => {
|
|
inboxItem = await InboxItem.create({
|
|
content: 'Test content',
|
|
status: 'added',
|
|
source: 'test',
|
|
user_id: user.id,
|
|
});
|
|
});
|
|
|
|
it('should update inbox item', async () => {
|
|
const updateData = {
|
|
content: 'Updated content',
|
|
status: 'processed',
|
|
};
|
|
|
|
const response = await agent
|
|
.patch(`/api/inbox/${inboxItem.uid}`)
|
|
.send(updateData);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.content).toBe(updateData.content);
|
|
expect(response.body.status).toBe(updateData.status);
|
|
});
|
|
|
|
it('should return 400 for invalid uid format', async () => {
|
|
const response = await agent
|
|
.patch('/api/inbox/invalid-uid')
|
|
.send({ content: 'Updated' });
|
|
|
|
expect(response.status).toBe(400);
|
|
expect(response.body.error).toBe('Invalid UID');
|
|
});
|
|
|
|
it('should return 404 for non-existent inbox item', async () => {
|
|
const response = await agent
|
|
.patch('/api/inbox/abcd1234efghijk')
|
|
.send({ content: 'Updated' });
|
|
|
|
expect(response.status).toBe(404);
|
|
expect(response.body.error).toBe('Inbox item not found.');
|
|
});
|
|
|
|
it('should require authentication', async () => {
|
|
const response = await request(app)
|
|
.patch(`/api/inbox/${inboxItem.uid}`)
|
|
.send({ content: 'Updated' });
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.body.error).toBe('Authentication required');
|
|
});
|
|
});
|
|
|
|
describe('DELETE /api/inbox/:uid', () => {
|
|
let inboxItem;
|
|
|
|
beforeEach(async () => {
|
|
inboxItem = await InboxItem.create({
|
|
content: 'Test content',
|
|
source: 'test',
|
|
user_id: user.id,
|
|
});
|
|
});
|
|
|
|
it('should delete inbox item', async () => {
|
|
const response = await agent.delete(`/api/inbox/${inboxItem.uid}`);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.message).toBe(
|
|
'Inbox item successfully deleted'
|
|
);
|
|
|
|
// Verify inbox item status is updated to deleted
|
|
const deletedItem = await InboxItem.findOne({
|
|
where: { uid: inboxItem.uid },
|
|
});
|
|
expect(deletedItem).not.toBeNull();
|
|
expect(deletedItem.status).toBe('deleted');
|
|
});
|
|
|
|
it('should return 400 for invalid uid format', async () => {
|
|
const response = await agent.delete('/api/inbox/invalid-uid');
|
|
|
|
expect(response.status).toBe(400);
|
|
expect(response.body.error).toBe('Invalid UID');
|
|
});
|
|
|
|
it('should return 404 for non-existent inbox item', async () => {
|
|
const response = await agent.delete('/api/inbox/abcd1234efghijk');
|
|
|
|
expect(response.status).toBe(404);
|
|
expect(response.body.error).toBe('Inbox item not found.');
|
|
});
|
|
|
|
it('should require authentication', async () => {
|
|
const response = await request(app).delete(
|
|
`/api/inbox/${inboxItem.uid}`
|
|
);
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.body.error).toBe('Authentication required');
|
|
});
|
|
});
|
|
|
|
describe('PATCH /api/inbox/:uid/process', () => {
|
|
let inboxItem;
|
|
|
|
beforeEach(async () => {
|
|
inboxItem = await InboxItem.create({
|
|
content: 'Test content',
|
|
status: 'added',
|
|
source: 'test',
|
|
user_id: user.id,
|
|
});
|
|
});
|
|
|
|
it('should process inbox item', async () => {
|
|
const response = await agent.patch(
|
|
`/api/inbox/${inboxItem.uid}/process`
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.status).toBe('processed');
|
|
});
|
|
|
|
it('should return 404 for non-existent inbox item', async () => {
|
|
const response = await agent.patch(
|
|
'/api/inbox/invalid-uid/process'
|
|
);
|
|
|
|
expect(response.status).toBe(400);
|
|
expect(response.body.error).toBe('Invalid UID');
|
|
});
|
|
|
|
it('should return 404 for non-existent item', async () => {
|
|
const response = await agent.patch(
|
|
'/api/inbox/abcd1234efghijk/process'
|
|
);
|
|
|
|
expect(response.status).toBe(404);
|
|
expect(response.body.error).toBe('Inbox item not found.');
|
|
});
|
|
|
|
it('should require authentication', async () => {
|
|
const response = await request(app).patch(
|
|
`/api/inbox/${inboxItem.uid}/process`
|
|
);
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.body.error).toBe('Authentication required');
|
|
});
|
|
});
|
|
});
|