Last active
April 1, 2025 06:09
-
-
Save tingwei628/fb3161ba91ea8bbbeedafb4e572ccd69 to your computer and use it in GitHub Desktop.
system design - Twitter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
System Design - Twitter | |
1. User Registration | |
- Users can register with a unique username. | |
- Duplicate usernames are not allowed. | |
2. Follow & Unfollow | |
- Users can follow/unfollow others to control their feed. | |
3. Posting Tweets | |
- Tweets must be at least 200 characters. | |
- Each tweet has a unique ID, timestamp, and content. | |
4. Fetching Feeds | |
- Shows tweets from followed users, sorted by time (latest first). | |
- Only past tweets are displayed (no future timestamps). | |
- Limited to the 20 most recent tweets. | |
5. Testing | |
- Verify registration, follow/unfollow, and tweet validation. | |
- Ensure correct feed behavior and tweet filtering. | |
*/ | |
//====================== Test Cases ========================================== | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
// Test 1: User Registration | |
Console.WriteLine("Test 1: User Registration"); | |
User user1 = new("Alice"); | |
User user2 = new("Bob"); | |
DB.users.Add(user1); | |
DB.users.Add(user2); | |
Console.WriteLine(DB.users.Count == 2 ? "PASS" : "FAIL"); | |
// Test 2: Duplicate Registration | |
Console.WriteLine("Test 2: Duplicate Registration"); | |
Console.WriteLine(user1.Register("Alice") == false ? "PASS" : "FAIL"); | |
// Test 3: Follow & Unfollow | |
Console.WriteLine("Test 3: Follow & Unfollow"); | |
user1.Follow("Bob"); | |
Console.WriteLine(user1.Followings.Contains("Bob") ? "PASS" : "FAIL"); | |
user1.UnFollow("Bob"); | |
Console.WriteLine(user1.Followings.Contains("Bob") == false ? "PASS" : "FAIL"); | |
// Test 4: Post Tweet | |
Console.WriteLine("Test 4: Post Tweet"); | |
bool success = user1.Post("This is a short tweet."); // Should fail (length < 200) | |
Console.WriteLine(success == false ? "PASS" : "FAIL"); | |
success = user1.Post(new string('A', 201)); // Should succeed (length > 200) | |
Console.WriteLine(success == true ? "PASS" : "FAIL"); | |
// Test 5: Get Feeds (No Following) | |
Console.WriteLine("Test 5: Get Feeds (No Following)"); | |
var feeds = user1.GetFeeds(); | |
Console.WriteLine(!feeds.Any() ? "PASS" : "FAIL"); | |
// Test 6: Get Feeds (With Following) | |
Console.WriteLine("Test 6: Get Feeds (With Following)"); | |
user1.Follow("Bob"); | |
user2.Post(new string('B', 201)); // Bob posts a tweet | |
var feeds2 = user1.GetFeeds(); | |
Console.WriteLine(feeds2.Any() ? "PASS" : "FAIL"); | |
// Test 7: Unfollow and check feeds | |
Console.WriteLine("Test 7: Unfollow and check feeds"); | |
user1.UnFollow("Bob"); | |
var feeds3 = user1.GetFeeds(); | |
Console.WriteLine(!feeds3.Any() ? "PASS" : "FAIL"); | |
// Test 8: Multiple users posting and fetching feeds | |
Console.WriteLine("Test 8: Multiple users posting and fetching feeds"); | |
User user3 = new("Charlie"); | |
DB.users.Add(user3); | |
user1.Follow("Bob"); | |
user1.Follow("Charlie"); | |
user3.Post(new string('C', 201)); | |
var feeds4 = user1.GetFeeds(); | |
Console.WriteLine(feeds4.Count() == 2 ? "PASS" : "FAIL"); | |
// Test 9: Posting multiple tweets and retrieving latest 20 | |
Console.WriteLine("Test 9: Posting multiple tweets and retrieving latest 20"); | |
for (int i = 0; i < 25; i++) | |
{ | |
user2.Post(new string('D', 201)); | |
} | |
var feeds5 = user1.GetFeeds(); | |
Console.WriteLine(feeds5.Count() == 20 ? "PASS" : "FAIL"); | |
// Test 10: Ensure only past tweets are shown | |
Console.WriteLine("Test 10: Ensure only past tweets are shown"); | |
user3.tweets.Add(Guid.NewGuid().ToString(), (DateTime.Now.AddMinutes(10), "Future tweet")); | |
var feeds6 = user1.GetFeeds(); | |
Console.WriteLine(!feeds6.Contains("Future tweet") ? "PASS" : "FAIL"); | |
Console.WriteLine("All tests completed."); | |
//====================== Code ========================================== | |
static class DB | |
{ | |
public static List<User> users = []; | |
} | |
public class User | |
{ | |
public string UserName { get; set; } | |
public User(string userName) | |
{ | |
UserName = userName; | |
} | |
public HashSet<string> Followings = []; | |
public Dictionary<string, (DateTime, string)> tweets = []; | |
public bool Register(string userName) | |
{ | |
if (DB.users.Any(x => x.UserName == userName)) return false; | |
DB.users.Add(new User(userName)); | |
return true; | |
} | |
public void Follow(string userName) | |
{ | |
Followings.Add(userName); | |
} | |
public void UnFollow(string userName) | |
{ | |
Followings.Remove(userName); | |
} | |
public bool Post(string tweet) | |
{ | |
if (string.IsNullOrWhiteSpace(tweet) || tweet.Length < 200) return false; | |
string tweetId = Guid.NewGuid().ToString(); | |
DateTime tweetDate = new(); | |
tweets.Add(tweetId, (tweetDate, tweet)); | |
return true; | |
} | |
public IEnumerable<string> GetFeeds() | |
{ | |
if (Followings.Any() == false) return []; | |
var followings = DB.users.Where(x => Followings.Any(y => y == x.UserName)); | |
var latestTweets = followings.SelectMany(x => x.tweets) | |
.Where(x => x.Value.Item1 <= DateTime.Now) | |
.OrderByDescending(x => x.Value.Item1) | |
.Take(20) | |
.Select(x => x.Value.Item2); | |
return latestTweets; | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment