Skip to content

Commit 5088569

Browse files
fix(timeline): match responses
* feat(timeline): add service unit tests * feat(timeline): add controller unit tests * fix(timeline): add 20 as default value for pagination limit * fix(timeline): add reposted by dto to response body * fix(timeline): add is_following attribute to response body
1 parent dc799f6 commit 5088569

File tree

5 files changed

+655
-27
lines changed

5 files changed

+655
-27
lines changed

src/timeline/dto/timeline-pagination.dto.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { IsBoolean, IsInt, IsOptional, Max, Min, MIN } from 'class-validator';
33

44
export class TimelinePaginationDto {
55
@ApiProperty({
6-
default: 50,
6+
default: 20,
77

8-
example: 50,
8+
example: 20,
99
required: false,
1010

1111
description: 'Return Specific Number of tweets',
@@ -14,7 +14,7 @@ export class TimelinePaginationDto {
1414
@IsInt()
1515
@Min(1)
1616
@Max(100)
17-
limit?: number;
17+
limit?: number = 20;
1818

1919
@ApiProperty({
2020
required: false,
Lines changed: 256 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,273 @@
11
import { Test, TestingModule } from '@nestjs/testing';
22
import { TimelineController } from './timeline.controller';
33
import { TimelineService } from './timeline.service';
4+
import { TimelinePaginationDto } from './dto/timeline-pagination.dto';
5+
import { UserResponseDTO } from 'src/tweets/dto/user-response.dto';
6+
import { RepostedByUserDTO, TweetResponseDTO } from 'src/tweets/dto';
7+
import { TimelineResponseDto } from './dto/timeline-response.dto';
48

59
describe('TimelineController', () => {
610
let controller: TimelineController;
11+
let service: jest.MockedObject<TimelineService>;
12+
13+
const mock_service = {
14+
getFollowingTimeline: jest.fn(),
15+
getForyouTimeline: jest.fn(),
16+
};
717

818
beforeEach(async () => {
919
const module: TestingModule = await Test.createTestingModule({
1020
controllers: [TimelineController],
11-
providers: [TimelineService],
21+
providers: [
22+
{
23+
provide: TimelineService,
24+
useValue: mock_service,
25+
},
26+
],
1227
}).compile();
1328

1429
controller = module.get<TimelineController>(TimelineController);
30+
service = module.get(TimelineService);
31+
});
32+
33+
afterEach(() => {
34+
jest.clearAllMocks();
35+
});
36+
37+
describe('GET /following', () => {
38+
const user_id = 'user-1';
39+
const pagination: TimelinePaginationDto = {
40+
limit: 10,
41+
cursor: null,
42+
};
43+
it('should return following timeline successfully', async () => {
44+
const user_response: UserResponseDTO = {
45+
id: 'user-456',
46+
username: 'amira',
47+
name: 'Amira',
48+
verified: true,
49+
avatar_url: 'https://example.com/avatar.jpg',
50+
bio: 'Software developer and tech enthusiast',
51+
followers: 1250,
52+
following: 340,
53+
is_following: true,
54+
cover_url: 'https://example.com/cover.jpg',
55+
};
56+
57+
const parent_tweet: TweetResponseDTO = {
58+
tweet_id: 'tweet-parent-789',
59+
content: 'This is the original tweet being replied to',
60+
type: 'tweet',
61+
images: ['https://example.com/image1.jpg'],
62+
videos: [],
63+
user: user_response,
64+
conversation_id: 'conv-123',
65+
likes_count: 245,
66+
reposts_count: 67,
67+
replies_count: 34,
68+
quotes_count: 12,
69+
views_count: 5420,
70+
is_liked: false,
71+
is_reposted: false,
72+
created_at: new Date('2024-11-01T10:00:00Z'),
73+
updated_at: new Date('2024-11-01T10:00:00Z'),
74+
};
75+
76+
const reposted_by: RepostedByUserDTO = {
77+
repost_id: 'repost-999',
78+
id: 'user-repost-111',
79+
name: 'Nada',
80+
reposted_at: new Date('2024-11-02T08:30:00Z'),
81+
};
82+
83+
const mock_tweets: TweetResponseDTO[] = [
84+
{
85+
tweet_id: 'tweet-reply-001',
86+
content: 'Great point! I completely agree with this perspective.',
87+
type: 'reply',
88+
images: [],
89+
videos: [],
90+
parent_tweet_id: 'tweet-parent-789',
91+
user: {
92+
id: 'user-789',
93+
username: 'techguru',
94+
name: 'Tech Guru',
95+
verified: false,
96+
avatar_url: 'https://example.com/tech-avatar.jpg',
97+
bio: 'Technology analyst',
98+
followers: 3400,
99+
following: 890,
100+
is_following: true,
101+
cover_url: 'https://example.com/tech-cover.jpg',
102+
},
103+
parent_tweet: parent_tweet,
104+
reposted_by: reposted_by,
105+
conversation_id: 'conv-123',
106+
likes_count: 89,
107+
reposts_count: 23,
108+
replies_count: 12,
109+
quotes_count: 5,
110+
views_count: 1820,
111+
is_liked: true,
112+
is_reposted: false,
113+
created_at: new Date('2024-11-02T09:15:00Z'),
114+
updated_at: new Date('2024-11-02T09:15:00Z'),
115+
},
116+
];
117+
const mock_response: TimelineResponseDto = {
118+
tweets: mock_tweets,
119+
next_cursor: 'cursor-following-123',
120+
has_more: false,
121+
timestamp: new Date().toISOString(),
122+
count: 1,
123+
};
124+
125+
service.getFollowingTimeline.mockResolvedValue(mock_response);
126+
127+
const result = await controller.getFollowingTimeline(user_id, pagination);
128+
129+
expect(service.getFollowingTimeline).toHaveBeenCalledWith(user_id, pagination);
130+
expect(service.getFollowingTimeline).toHaveBeenCalledTimes(1);
131+
expect(result.has_more).toBe(false);
132+
});
133+
134+
it('should return empty timeline when user follows no one or did not post anything', async () => {
135+
const mock_response: TimelineResponseDto = {
136+
tweets: [],
137+
next_cursor: null,
138+
has_more: false,
139+
timestamp: new Date().toISOString(),
140+
count: 0,
141+
};
142+
143+
service.getFollowingTimeline.mockResolvedValue(mock_response);
144+
145+
const result = await controller.getFollowingTimeline(user_id, pagination);
146+
147+
expect(service.getFollowingTimeline).toHaveBeenCalledWith(user_id, pagination);
148+
expect(result.tweets).toHaveLength(0);
149+
expect(result.has_more).toBe(false);
150+
expect(result.next_cursor).toBeNull();
151+
});
152+
153+
//TODO: More tests to be added
15154
});
155+
describe('GET / For-you', () => {
156+
const user_id = 'user-1';
157+
const pagination: TimelinePaginationDto = {
158+
limit: 10,
159+
cursor: null,
160+
};
161+
it('should return for you timeline successfully', async () => {
162+
const user_response: UserResponseDTO = {
163+
id: 'user-456',
164+
username: 'amira',
165+
name: 'Amira',
166+
verified: true,
167+
avatar_url: 'https://example.com/avatar.jpg',
168+
bio: 'Software developer and tech enthusiast',
169+
followers: 1250,
170+
following: 340,
171+
is_following: true,
172+
cover_url: 'https://example.com/cover.jpg',
173+
};
174+
175+
const parent_tweet: TweetResponseDTO = {
176+
tweet_id: 'tweet-parent-789',
177+
content: 'This is the original tweet being replied to',
178+
type: 'tweet',
179+
images: ['https://example.com/image1.jpg'],
180+
videos: [],
181+
user: user_response,
182+
conversation_id: 'conv-123',
183+
likes_count: 245,
184+
reposts_count: 67,
185+
replies_count: 34,
186+
quotes_count: 12,
187+
views_count: 5420,
188+
is_liked: false,
189+
is_reposted: false,
190+
created_at: new Date('2024-11-01T10:00:00Z'),
191+
updated_at: new Date('2024-11-01T10:00:00Z'),
192+
};
193+
194+
const reposted_by: RepostedByUserDTO = {
195+
repost_id: 'repost-999',
196+
id: 'user-repost-111',
197+
name: 'Nada',
198+
reposted_at: new Date('2024-11-02T08:30:00Z'),
199+
};
200+
201+
const mock_tweets: TweetResponseDTO[] = [
202+
{
203+
tweet_id: 'tweet-reply-001',
204+
content: 'Great point! I completely agree with this perspective.',
205+
type: 'reply',
206+
images: [],
207+
videos: [],
208+
parent_tweet_id: 'tweet-parent-789',
209+
user: {
210+
id: 'user-789',
211+
username: 'techguru',
212+
name: 'Tech Guru',
213+
verified: false,
214+
avatar_url: 'https://example.com/tech-avatar.jpg',
215+
bio: 'Technology analyst',
216+
followers: 3400,
217+
following: 890,
218+
is_following: true,
219+
cover_url: 'https://example.com/tech-cover.jpg',
220+
},
221+
parent_tweet: parent_tweet,
222+
reposted_by: reposted_by,
223+
conversation_id: 'conv-123',
224+
likes_count: 89,
225+
reposts_count: 23,
226+
replies_count: 12,
227+
quotes_count: 5,
228+
views_count: 1820,
229+
is_liked: true,
230+
is_reposted: false,
231+
created_at: new Date('2024-11-02T09:15:00Z'),
232+
updated_at: new Date('2024-11-02T09:15:00Z'),
233+
},
234+
];
235+
const mock_response: TimelineResponseDto = {
236+
tweets: mock_tweets,
237+
next_cursor: 'cursor-following-123',
238+
has_more: false,
239+
timestamp: new Date().toISOString(),
240+
count: 1,
241+
};
242+
243+
service.getForyouTimeline.mockResolvedValue(mock_response);
244+
245+
const result = await controller.getForyouTimeline(user_id, pagination);
246+
247+
expect(service.getForyouTimeline).toHaveBeenCalledWith(user_id, pagination);
248+
expect(service.getForyouTimeline).toHaveBeenCalledTimes(1);
249+
expect(result.has_more).toBe(false);
250+
});
251+
252+
it('should return empty timeline when no tweets exist', async () => {
253+
const mock_response: TimelineResponseDto = {
254+
tweets: [],
255+
next_cursor: null,
256+
has_more: false,
257+
timestamp: new Date().toISOString(),
258+
count: 0,
259+
};
260+
261+
service.getForyouTimeline.mockResolvedValue(mock_response);
262+
263+
const result = await controller.getForyouTimeline(user_id, pagination);
264+
265+
expect(service.getForyouTimeline).toHaveBeenCalledWith(user_id, pagination);
266+
expect(result.tweets).toHaveLength(0);
267+
expect(result.has_more).toBe(false);
268+
expect(result.next_cursor).toBeNull();
269+
});
16270

17-
it('should be defined', () => {
18-
expect(controller).toBeDefined();
271+
//TODO: More tests to be added
19272
});
20273
});

0 commit comments

Comments
 (0)