From 4c33ea38964b274df557ab65d4106da0aceb2d63 Mon Sep 17 00:00:00 2001 From: Vartan Benohanian Date: Wed, 17 Jun 2020 22:35:19 -0400 Subject: [PATCH] Add tests Signed-off-by: Vartan Benohanian --- testdata/user/comments.json | 89 ++++ testdata/user/get-multiple-by-id.json | 29 ++ testdata/user/get.json | 61 +++ testdata/user/overview.json | 200 +++++++++ testdata/user/submitted.json | 122 ++++++ things.go | 10 +- user.go | 28 +- user_test.go | 566 ++++++++++++++++++++++++++ 8 files changed, 1097 insertions(+), 8 deletions(-) create mode 100644 testdata/user/comments.json create mode 100644 testdata/user/get-multiple-by-id.json create mode 100644 testdata/user/get.json create mode 100644 testdata/user/overview.json create mode 100644 testdata/user/submitted.json create mode 100644 user_test.go diff --git a/testdata/user/comments.json b/testdata/user/comments.json new file mode 100644 index 0000000..7584363 --- /dev/null +++ b/testdata/user/comments.json @@ -0,0 +1,89 @@ +{ + "kind": "Listing", + "data": { + "modhash": null, + "dist": 1, + "children": [ + { + "kind": "t1", + "data": { + "total_awards_received": 0, + "approved_at_utc": null, + "edited": false, + "mod_reason_by": null, + "banned_by": null, + "author_flair_type": "text", + "removal_reason": null, + "link_id": "t3_d7ejpn", + "author_flair_template_id": null, + "likes": true, + "replies": "", + "user_reports": [], + "saved": false, + "id": "f0zsa37", + "banned_at_utc": null, + "mod_reason_title": null, + "gilded": 0, + "archived": true, + "no_follow": false, + "author": "v_95", + "num_comments": 89751, + "can_mod_post": false, + "created_utc": 1569101896.0, + "send_replies": true, + "parent_id": "t3_d7ejpn", + "score": 1, + "author_fullname": "t2_164ab8", + "over_18": false, + "treatment_tags": [], + "approved_by": null, + "mod_note": null, + "all_awardings": [], + "subreddit_id": "t5_2qh1f", + "body": "Thank you!", + "link_title": "I'm giving away an iPhone 11 Pro to a commenter at random to celebrate Apollo for Reddit's new iOS 13 update and as a thank you to the community! Just leave a comment on this post and the winner will be selected randomly and announced tomorrow at 8 PM GMT. Details inside, and good luck!", + "author_flair_css_class": null, + "name": "t1_f0zsa37", + "author_patreon_flair": false, + "downs": 0, + "author_flair_richtext": [], + "is_submitter": false, + "body_html": "<div class=\"md\"><p>Thank you!</p>\n</div>", + "gildings": {}, + "collapsed_reason": null, + "distinguished": null, + "associated_award": null, + "stickied": false, + "author_premium": false, + "can_gild": false, + "top_awarded_type": null, + "subreddit_name_prefixed": "r/apple", + "author_flair_text_color": null, + "score_hidden": false, + "permalink": "/r/apple/comments/d7ejpn/im_giving_away_an_iphone_11_pro_to_a_commenter_at/f0zsa37/", + "num_reports": null, + "link_permalink": "https://www.reddit.com/r/apple/comments/d7ejpn/im_giving_away_an_iphone_11_pro_to_a_commenter_at/", + "report_reasons": null, + "link_author": "iamthatis", + "subreddit": "apple", + "author_flair_text": null, + "link_url": "https://www.reddit.com/r/apple/comments/d7ejpn/im_giving_away_an_iphone_11_pro_to_a_commenter_at/", + "created": 1569130696.0, + "collapsed": false, + "awarders": [], + "controversiality": 0, + "locked": false, + "author_flair_background_color": null, + "collapsed_because_crowd_control": null, + "rte_mode": "markdown", + "mod_reports": [], + "quarantine": false, + "subreddit_type": "public", + "ups": 1 + } + } + ], + "after": "t1_f0zsa37", + "before": null + } +} diff --git a/testdata/user/get-multiple-by-id.json b/testdata/user/get-multiple-by-id.json new file mode 100644 index 0000000..d8167b2 --- /dev/null +++ b/testdata/user/get-multiple-by-id.json @@ -0,0 +1,29 @@ +{ + "t2_1": { + "comment_karma": 22223, + "created_utc": 1489284107, + "link_karma": 488, + "name": "test_user_1", + "profile_color": "", + "profile_img": "https://www.redditstatic.com/avatars/avatar_default_01_94E044.png", + "profile_over_18": false + }, + "t2_2": { + "comment_karma": 131948, + "created_utc": 1450635171, + "link_karma": 8277, + "name": "test_user_2", + "profile_color": "", + "profile_img": "https://www.redditstatic.com/avatars/avatar_default_16_25B79F.png", + "profile_over_18": false + }, + "t2_3": { + "comment_karma": 81918, + "created_utc": 1362411991, + "link_karma": 126887, + "name": "test_user_3", + "profile_color": "", + "profile_img": "https://www.redditstatic.com/avatars/avatar_default_18_46A508.png", + "profile_over_18": true + } +} diff --git a/testdata/user/get.json b/testdata/user/get.json new file mode 100644 index 0000000..46300da --- /dev/null +++ b/testdata/user/get.json @@ -0,0 +1,61 @@ +{ + "kind": "t2", + "data": { + "is_employee": false, + "icon_img": "https://www.redditstatic.com/avatars/avatar_default_16_25B79F.png", + "pref_show_snoovatar": false, + "name": "Test_User", + "is_friend": false, + "created": 1350583871.0, + "has_subscribed": true, + "hide_from_robots": false, + "created_utc": 1350555071.0, + "link_karma": 8239, + "comment_karma": 130514, + "is_gold": false, + "is_mod": true, + "verified": true, + "subreddit": { + "default_set": true, + "user_is_contributor": false, + "banner_img": "", + "restrict_posting": true, + "user_is_banned": false, + "free_form_reports": true, + "community_icon": null, + "show_media": true, + "icon_color": "#25B79F", + "user_is_muted": false, + "display_name": "u_Test_User", + "header_img": null, + "title": "", + "previous_names": [], + "over_18": false, + "icon_size": [256, 256], + "primary_color": "", + "icon_img": "https://www.redditstatic.com/avatars/avatar_default_16_25B79F.png", + "description": "", + "submit_link_label": "", + "header_size": null, + "restrict_commenting": false, + "subscribers": 0, + "submit_text_label": "", + "is_default_icon": true, + "link_flair_position": "", + "display_name_prefixed": "u/Test_User", + "key_color": "", + "name": "t5_test", + "is_default_banner": true, + "url": "/user/Test_User/", + "banner_size": null, + "user_is_moderator": false, + "public_description": "", + "link_flair_enabled": false, + "disable_contributor_requests": false, + "subreddit_type": "user", + "user_is_subscriber": false + }, + "has_verified_email": true, + "id": "test" + } +} diff --git a/testdata/user/overview.json b/testdata/user/overview.json new file mode 100644 index 0000000..5afc489 --- /dev/null +++ b/testdata/user/overview.json @@ -0,0 +1,200 @@ +{ + "kind": "Listing", + "data": { + "modhash": null, + "dist": 2, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": null, + "subreddit": "redditdev", + "selftext": "Talking about [this](https://www.reddit.com/dev/api/#GET_user_{username}_{where}) endpoint specifically.\n\nI'm building a Reddit API client, but don't have gold.", + "author_fullname": "t2_164ab8", + "saved": false, + "mod_reason_title": null, + "gilded": 0, + "clicked": false, + "title": "GET /user/{username}/gilded: does it return other user's things you've gilded, or your things that have been gilded? Does it return both comments and posts?", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/redditdev", + "hidden": false, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "thumbnail_height": null, + "top_awarded_type": null, + "hide_score": false, + "name": "t3_gczwql", + "quarantine": false, + "link_flair_text_color": "dark", + "upvote_ratio": 0.86, + "author_flair_background_color": null, + "subreddit_type": "public", + "ups": 9, + "total_awards_received": 0, + "media_embed": {}, + "thumbnail_width": null, + "author_flair_template_id": null, + "is_original_content": false, + "user_reports": [], + "secure_media": null, + "is_reddit_media_domain": false, + "is_meta": false, + "category": null, + "secure_media_embed": {}, + "link_flair_text": "Reddit API", + "can_mod_post": false, + "score": 9, + "approved_by": null, + "author_premium": false, + "thumbnail": "self", + "edited": false, + "author_flair_css_class": null, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": null, + "is_self": true, + "mod_note": null, + "created": 1588574785.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": null, + "banned_by": null, + "author_flair_type": "text", + "domain": "self.redditdev", + "allow_live_comments": false, + "selftext_html": "<!-- SC_OFF --><div class=\"md\"><p>Talking about <a href=\"https://www.reddit.com/dev/api/#GET_user_%7Busername%7D_%7Bwhere%7D\">this</a> endpoint specifically.</p>\n\n<p>I&#39;m building a Reddit API client, but don&#39;t have gold.</p>\n</div><!-- SC_ON -->", + "likes": true, + "suggested_sort": null, + "banned_at_utc": null, + "view_count": null, + "archived": false, + "no_follow": false, + "is_crosspostable": true, + "pinned": false, + "over_18": false, + "all_awardings": [], + "awarders": [], + "media_only": false, + "link_flair_template_id": "c4edd5ce-40e8-11e7-b814-0ef91bd65558", + "can_gild": false, + "spoiler": false, + "locked": false, + "author_flair_text": null, + "treatment_tags": [], + "rte_mode": "markdown", + "visited": false, + "removed_by": null, + "num_reports": null, + "distinguished": null, + "subreddit_id": "t5_2qizd", + "mod_reason_by": null, + "removal_reason": null, + "link_flair_background_color": "", + "id": "gczwql", + "is_robot_indexable": true, + "report_reasons": null, + "author": "v_95", + "discussion_type": null, + "num_comments": 2, + "send_replies": true, + "whitelist_status": "all_ads", + "contest_mode": false, + "mod_reports": [], + "author_patreon_flair": false, + "author_flair_text_color": null, + "permalink": "/r/redditdev/comments/gczwql/get_userusernamegilded_does_it_return_other_users/", + "parent_whitelist_status": "all_ads", + "stickied": false, + "url": "https://www.reddit.com/r/redditdev/comments/gczwql/get_userusernamegilded_does_it_return_other_users/", + "subreddit_subscribers": 37829, + "created_utc": 1588545985.0, + "num_crossposts": 0, + "media": null, + "is_video": false + } + }, + { + "kind": "t1", + "data": { + "total_awards_received": 0, + "approved_at_utc": null, + "edited": false, + "mod_reason_by": null, + "banned_by": null, + "author_flair_type": "text", + "removal_reason": null, + "link_id": "t3_d7ejpn", + "author_flair_template_id": null, + "likes": true, + "replies": "", + "user_reports": [], + "saved": false, + "id": "f0zsa37", + "banned_at_utc": null, + "mod_reason_title": null, + "gilded": 0, + "archived": true, + "no_follow": false, + "author": "v_95", + "num_comments": 89751, + "can_mod_post": false, + "created_utc": 1569101896.0, + "send_replies": true, + "parent_id": "t3_d7ejpn", + "score": 1, + "author_fullname": "t2_164ab8", + "over_18": false, + "treatment_tags": [], + "approved_by": null, + "mod_note": null, + "all_awardings": [], + "subreddit_id": "t5_2qh1f", + "body": "Thank you!", + "link_title": "I'm giving away an iPhone 11 Pro to a commenter at random to celebrate Apollo for Reddit's new iOS 13 update and as a thank you to the community! Just leave a comment on this post and the winner will be selected randomly and announced tomorrow at 8 PM GMT. Details inside, and good luck!", + "author_flair_css_class": null, + "name": "t1_f0zsa37", + "author_patreon_flair": false, + "downs": 0, + "author_flair_richtext": [], + "is_submitter": false, + "body_html": "<div class=\"md\"><p>Thank you!</p>\n</div>", + "gildings": {}, + "collapsed_reason": null, + "distinguished": null, + "associated_award": null, + "stickied": false, + "author_premium": false, + "can_gild": false, + "top_awarded_type": null, + "subreddit_name_prefixed": "r/apple", + "author_flair_text_color": null, + "score_hidden": false, + "permalink": "/r/apple/comments/d7ejpn/im_giving_away_an_iphone_11_pro_to_a_commenter_at/f0zsa37/", + "num_reports": null, + "link_permalink": "https://www.reddit.com/r/apple/comments/d7ejpn/im_giving_away_an_iphone_11_pro_to_a_commenter_at/", + "report_reasons": null, + "link_author": "iamthatis", + "subreddit": "apple", + "author_flair_text": null, + "link_url": "https://www.reddit.com/r/apple/comments/d7ejpn/im_giving_away_an_iphone_11_pro_to_a_commenter_at/", + "created": 1569130696.0, + "collapsed": false, + "awarders": [], + "controversiality": 0, + "locked": false, + "author_flair_background_color": null, + "collapsed_because_crowd_control": null, + "rte_mode": "markdown", + "mod_reports": [], + "quarantine": false, + "subreddit_type": "public", + "ups": 1 + } + } + ], + "after": "t1_f0zsa37", + "before": null + } +} diff --git a/testdata/user/submitted.json b/testdata/user/submitted.json new file mode 100644 index 0000000..a427d1d --- /dev/null +++ b/testdata/user/submitted.json @@ -0,0 +1,122 @@ +{ + "kind": "Listing", + "data": { + "modhash": null, + "dist": 1, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": null, + "subreddit": "redditdev", + "selftext": "Talking about [this](https://www.reddit.com/dev/api/#GET_user_{username}_{where}) endpoint specifically.\n\nI'm building a Reddit API client, but don't have gold.", + "author_fullname": "t2_164ab8", + "saved": false, + "mod_reason_title": null, + "gilded": 0, + "clicked": false, + "title": "GET /user/{username}/gilded: does it return other user's things you've gilded, or your things that have been gilded? Does it return both comments and posts?", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/redditdev", + "hidden": false, + "pwls": 6, + "link_flair_css_class": "", + "downs": 0, + "thumbnail_height": null, + "top_awarded_type": null, + "hide_score": false, + "name": "t3_gczwql", + "quarantine": false, + "link_flair_text_color": "dark", + "upvote_ratio": 0.86, + "author_flair_background_color": null, + "subreddit_type": "public", + "ups": 9, + "total_awards_received": 0, + "media_embed": {}, + "thumbnail_width": null, + "author_flair_template_id": null, + "is_original_content": false, + "user_reports": [], + "secure_media": null, + "is_reddit_media_domain": false, + "is_meta": false, + "category": null, + "secure_media_embed": {}, + "link_flair_text": "Reddit API", + "can_mod_post": false, + "score": 9, + "approved_by": null, + "author_premium": false, + "thumbnail": "self", + "edited": false, + "author_flair_css_class": null, + "author_flair_richtext": [], + "gildings": {}, + "content_categories": null, + "is_self": true, + "mod_note": null, + "created": 1588574785.0, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": null, + "banned_by": null, + "author_flair_type": "text", + "domain": "self.redditdev", + "allow_live_comments": false, + "selftext_html": "<!-- SC_OFF --><div class=\"md\"><p>Talking about <a href=\"https://www.reddit.com/dev/api/#GET_user_%7Busername%7D_%7Bwhere%7D\">this</a> endpoint specifically.</p>\n\n<p>I&#39;m building a Reddit API client, but don&#39;t have gold.</p>\n</div><!-- SC_ON -->", + "likes": true, + "suggested_sort": null, + "banned_at_utc": null, + "view_count": null, + "archived": false, + "no_follow": false, + "is_crosspostable": true, + "pinned": false, + "over_18": false, + "all_awardings": [], + "awarders": [], + "media_only": false, + "link_flair_template_id": "c4edd5ce-40e8-11e7-b814-0ef91bd65558", + "can_gild": false, + "spoiler": false, + "locked": false, + "author_flair_text": null, + "treatment_tags": [], + "rte_mode": "markdown", + "visited": false, + "removed_by": null, + "num_reports": null, + "distinguished": null, + "subreddit_id": "t5_2qizd", + "mod_reason_by": null, + "removal_reason": null, + "link_flair_background_color": "", + "id": "gczwql", + "is_robot_indexable": true, + "report_reasons": null, + "author": "v_95", + "discussion_type": null, + "num_comments": 2, + "send_replies": true, + "whitelist_status": "all_ads", + "contest_mode": false, + "mod_reports": [], + "author_patreon_flair": false, + "author_flair_text_color": null, + "permalink": "/r/redditdev/comments/gczwql/get_userusernamegilded_does_it_return_other_users/", + "parent_whitelist_status": "all_ads", + "stickied": false, + "url": "https://www.reddit.com/r/redditdev/comments/gczwql/get_userusernamegilded_does_it_return_other_users/", + "subreddit_subscribers": 37829, + "created_utc": 1588545985.0, + "num_crossposts": 0, + "media": null, + "is_video": false + } + } + ], + "after": "t3_gczwql", + "before": null + } +} diff --git a/things.go b/things.go index 201ee5c..376b712 100644 --- a/things.go +++ b/things.go @@ -176,14 +176,14 @@ func (l *Things) UnmarshalJSON(b []byte) error { // Comment is a comment posted by a user type Comment struct { - ID string `json:"id,omitempty"` - FullID string `json:"name,omitempty"` - ParentID string `json:"parent_id,omitempty"` - Permalink string `json:"permalink,omitempty"` - + ID string `json:"id,omitempty"` + FullID string `json:"name,omitempty"` Created *Timestamp `json:"created_utc,omitempty"` Edited *Timestamp `json:"edited,omitempty"` + ParentID string `json:"parent_id,omitempty"` + Permalink string `json:"permalink,omitempty"` + Body string `json:"body,omitempty"` Author string `json:"author,omitempty"` AuthorID string `json:"author_fullname,omitempty"` diff --git a/user.go b/user.go index 6485017..60d8ac0 100644 --- a/user.go +++ b/user.go @@ -43,7 +43,7 @@ var _ UserService = &UserServiceOp{} // User represents a Reddit user type User struct { - // is not the full ID, watch out + // this is not the full ID, watch out ID string `json:"id,omitempty"` Name string `json:"name,omitempty"` Created *Timestamp `json:"created_utc,omitempty"` @@ -105,13 +105,13 @@ func (s *UserServiceOp) GetMultipleByID(ctx context.Context, ids ...string) (map return nil, nil, err } - root := make(map[string]*UserShort) + root := new(map[string]*UserShort) resp, err := s.client.Do(ctx, req, root) if err != nil { return nil, resp, err } - return root, resp, nil + return *root, resp, nil } // UsernameAvailable checks whether a username is available for registration @@ -324,6 +324,28 @@ func (s *UserServiceOp) Gilded(ctx context.Context, opts ...SearchOptionSetter) return root.getLinks(), resp, nil } +// // Friend creates or updates a "friend" relationship +// // Request body contains JSON data with: +// // name: existing Reddit username +// // note: a string no longer than 300 characters +// func (s *UserServiceOp) Friend(ctx context.Context, username string, note string) (interface{}, *Response, error) { +// type request struct { +// Username string `url:"name"` +// Note string `url:"note"` +// } + +// path := fmt.Sprintf("api/v1/me/friends/%s", username) +// body := request{Username: username, Note: note} + +// _, err := s.client.NewRequest(http.MethodPut, path, body) +// if err != nil { +// return false, nil, err +// } + +// // todo: requires gold +// return nil, nil, nil +// } + // Friend creates or updates a "friend" relationship // Request body contains JSON data with: // name: existing Reddit username diff --git a/user_test.go b/user_test.go new file mode 100644 index 0000000..8004210 --- /dev/null +++ b/user_test.go @@ -0,0 +1,566 @@ +package geddit + +import ( + "fmt" + "net/http" + "net/url" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +var expectedUser = &User{ + ID: "test", + Name: "Test_User", + Created: &Timestamp{time.Date(2012, 10, 18, 10, 11, 11, 0, time.UTC)}, + + LinkKarma: 8239, + CommentKarma: 130514, + + HasVerifiedEmail: true, +} + +var expectedUsers = map[string]*UserShort{ + "t2_1": { + Name: "test_user_1", + Created: &Timestamp{time.Date(2017, 3, 12, 2, 1, 47, 0, time.UTC)}, + LinkKarma: 488, + CommentKarma: 22223, + NSFW: false, + }, + "t2_2": { + Name: "test_user_2", + Created: &Timestamp{time.Date(2015, 12, 20, 18, 12, 51, 0, time.UTC)}, + LinkKarma: 8277, + CommentKarma: 131948, + NSFW: false, + }, + "t2_3": { + Name: "test_user_3", + Created: &Timestamp{time.Date(2013, 3, 4, 15, 46, 31, 0, time.UTC)}, + LinkKarma: 126887, + CommentKarma: 81918, + NSFW: true, + }, +} + +var expectedPost = Link{ + ID: "gczwql", + FullID: "t3_gczwql", + Created: &Timestamp{time.Date(2020, 5, 3, 22, 46, 25, 0, time.UTC)}, + Edited: &Timestamp{time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)}, + + Permalink: "/r/redditdev/comments/gczwql/get_userusernamegilded_does_it_return_other_users/", + URL: "https://www.reddit.com/r/redditdev/comments/gczwql/get_userusernamegilded_does_it_return_other_users/", + + Title: "GET /user/{username}/gilded: does it return other user's things you've gilded, or your things that have been gilded? Does it return both comments and posts?", + Body: "Talking about [this](https://www.reddit.com/dev/api/#GET_user_{username}_{where}) endpoint specifically.\n\nI'm building a Reddit API client, but don't have gold.", + + Likes: Bool(true), + + Score: 9, + UpvoteRatio: 0.86, + NumberOfComments: 2, + + SubredditID: "t5_2qizd", + SubredditName: "redditdev", + SubredditNamePrefixed: "r/redditdev", + + AuthorID: "t2_164ab8", + AuthorName: "v_95", + + IsSelfPost: true, +} + +var expectedComment = Comment{ + ID: "f0zsa37", + FullID: "t1_f0zsa37", + Created: &Timestamp{time.Date(2019, 9, 21, 21, 38, 16, 0, time.UTC)}, + Edited: &Timestamp{time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)}, + + ParentID: "t3_d7ejpn", + Permalink: "/r/apple/comments/d7ejpn/im_giving_away_an_iphone_11_pro_to_a_commenter_at/f0zsa37/", + + Body: "Thank you!", + Author: "v_95", + AuthorID: "t2_164ab8", + + Subreddit: "apple", + SubredditNamePrefixed: "r/apple", + SubredditID: "t5_2qh1f", + + Likes: Bool(true), + + Score: 1, + Controversiality: 0, + + LinkID: "t3_d7ejpn", + LinkTitle: "I'm giving away an iPhone 11 Pro to a commenter at random to celebrate Apollo for Reddit's new iOS 13 update and as a thank you to the community! Just leave a comment on this post and the winner will be selected randomly and announced tomorrow at 8 PM GMT. Details inside, and good luck!", + LinkPermalink: "https://www.reddit.com/r/apple/comments/d7ejpn/im_giving_away_an_iphone_11_pro_to_a_commenter_at/", + LinkAuthor: "iamthatis", + LinkNumComments: 89751, +} + +func TestUserService_Get(t *testing.T) { + setup() + defer teardown() + + blob := readFileContents(t, "testdata/user/get.json") + + mux.HandleFunc("/user/Test_User/about", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + fmt.Fprint(w, blob) + }) + + user, _, err := client.User.Get(ctx, "Test_User") + assert.NoError(t, err) + assert.Equal(t, expectedUser, user) +} + +func TestUserService_GetMultipleByID(t *testing.T) { + setup() + defer teardown() + + blob := readFileContents(t, "testdata/user/get-multiple-by-id.json") + + mux.HandleFunc("/api/user_data_by_account_ids", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + + err := r.ParseForm() + assert.NoError(t, err) + assert.Equal(t, "t2_1,t2_2,t2_3", r.Form.Get("ids")) + + fmt.Fprint(w, blob) + }) + + users, _, err := client.User.GetMultipleByID(ctx, "t2_1", "t2_2", "t2_3") + assert.NoError(t, err) + assert.Equal(t, expectedUsers, users) +} + +func TestUserService_UsernameAvailable(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/api/username_available", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + + err := r.ParseForm() + assert.NoError(t, err) + + user := r.Form.Get("user") + assert.NotEmpty(t, user) + + result := user == "test123" + fmt.Fprint(w, result) + }) + + ok, _, err := client.User.UsernameAvailable(ctx, "test123") + assert.NoError(t, err) + assert.True(t, ok) + + ok, _, err = client.User.UsernameAvailable(ctx, "123test") + assert.NoError(t, err) + assert.False(t, ok) +} + +func TestUserService_Overview(t *testing.T) { + setup() + defer teardown() + + blob := readFileContents(t, "testdata/user/overview.json") + + mux.HandleFunc("/user/user1/overview", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + fmt.Fprint(w, blob) + }) + + posts, comments, _, err := client.User.Overview(ctx) + assert.NoError(t, err) + assert.NotNil(t, posts) + assert.NotNil(t, comments) + + assert.Len(t, posts.Links, 1) + assert.Equal(t, expectedPost, posts.Links[0]) + assert.Equal(t, "t1_f0zsa37", posts.After) + assert.Equal(t, "", posts.Before) + + assert.Len(t, comments.Comments, 1) + assert.Equal(t, expectedComment, comments.Comments[0]) + assert.Equal(t, "t1_f0zsa37", comments.After) + assert.Equal(t, "", comments.Before) +} + +func TestUserService_OverviewOf(t *testing.T) { + setup() + defer teardown() + + blob := readFileContents(t, "testdata/user/overview.json") + + mux.HandleFunc("/user/user2/overview", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + fmt.Fprint(w, blob) + }) + + posts, comments, _, err := client.User.OverviewOf(ctx, "user2") + assert.NoError(t, err) + assert.NotNil(t, posts) + assert.NotNil(t, comments) + + assert.Len(t, posts.Links, 1) + assert.Equal(t, expectedPost, posts.Links[0]) + assert.Equal(t, "t1_f0zsa37", posts.After) + assert.Equal(t, "", posts.Before) + + assert.Len(t, comments.Comments, 1) + assert.Equal(t, expectedComment, comments.Comments[0]) + assert.Equal(t, "t1_f0zsa37", comments.After) + assert.Equal(t, "", comments.Before) +} + +func TestUserService_Overview_Options(t *testing.T) { + setup() + defer teardown() + + blob := readFileContents(t, "testdata/user/overview.json") + + mux.HandleFunc("/user/user1/overview", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + + form := url.Values{} + form.Set("limit", "5") + form.Set("after", "t3_after") + form.Set("sort", SortTop.String()) + + err := r.ParseForm() + assert.NoError(t, err) + assert.Equal(t, form, r.Form) + + fmt.Fprint(w, blob) + }) + + _, _, _, err := client.User.Overview(ctx, SetLimit(5), SetAfter("t3_after"), SetSort(SortTop)) + assert.NoError(t, err) +} + +func TestUserService_Posts(t *testing.T) { + setup() + defer teardown() + + blob := readFileContents(t, "testdata/user/submitted.json") + + mux.HandleFunc("/user/user1/submitted", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + fmt.Fprint(w, blob) + }) + + posts, _, err := client.User.Posts(ctx) + assert.NoError(t, err) + assert.NotNil(t, posts) + + assert.Len(t, posts.Links, 1) + assert.Equal(t, expectedPost, posts.Links[0]) + assert.Equal(t, "t3_gczwql", posts.After) + assert.Equal(t, "", posts.Before) +} + +func TestUserService_PostsOf(t *testing.T) { + setup() + defer teardown() + + blob := readFileContents(t, "testdata/user/submitted.json") + + mux.HandleFunc("/user/user2/submitted", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + fmt.Fprint(w, blob) + }) + + posts, _, err := client.User.PostsOf(ctx, "user2") + assert.NoError(t, err) + assert.NotNil(t, posts) + + assert.Len(t, posts.Links, 1) + assert.Equal(t, expectedPost, posts.Links[0]) + assert.Equal(t, "t3_gczwql", posts.After) + assert.Equal(t, "", posts.Before) +} + +func TestUserService_Posts_Options(t *testing.T) { + setup() + defer teardown() + + blob := readFileContents(t, "testdata/user/submitted.json") + + mux.HandleFunc("/user/user1/submitted", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + + form := url.Values{} + form.Set("limit", "10") + form.Set("sort", SortNew.String()) + + err := r.ParseForm() + assert.NoError(t, err) + assert.Equal(t, form, r.Form) + + fmt.Fprint(w, blob) + }) + + _, _, err := client.User.Posts(ctx, SetLimit(10), SetSort(SortNew)) + assert.NoError(t, err) +} + +func TestUserService_Comments(t *testing.T) { + setup() + defer teardown() + + blob := readFileContents(t, "testdata/user/comments.json") + + mux.HandleFunc("/user/user1/comments", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + fmt.Fprint(w, blob) + }) + + comments, _, err := client.User.Comments(ctx) + assert.NoError(t, err) + assert.NotNil(t, comments) + + assert.Len(t, comments.Comments, 1) + assert.Equal(t, expectedComment, comments.Comments[0]) + assert.Equal(t, "t1_f0zsa37", comments.After) + assert.Equal(t, "", comments.Before) +} + +func TestUserService_CommentsOf(t *testing.T) { + setup() + defer teardown() + + blob := readFileContents(t, "testdata/user/comments.json") + + mux.HandleFunc("/user/user2/comments", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + fmt.Fprint(w, blob) + }) + + comments, _, err := client.User.CommentsOf(ctx, "user2") + assert.NoError(t, err) + assert.NotNil(t, comments) + + assert.Len(t, comments.Comments, 1) + assert.Equal(t, expectedComment, comments.Comments[0]) + assert.Equal(t, "t1_f0zsa37", comments.After) + assert.Equal(t, "", comments.Before) +} + +func TestUserService_Comments_Options(t *testing.T) { + setup() + defer teardown() + + blob := readFileContents(t, "testdata/user/comments.json") + + mux.HandleFunc("/user/user1/comments", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + + form := url.Values{} + form.Set("limit", "100") + form.Set("before", "t1_before") + + err := r.ParseForm() + assert.NoError(t, err) + assert.Equal(t, form, r.Form) + + fmt.Fprint(w, blob) + }) + + _, _, err := client.User.Comments(ctx, SetLimit(100), SetBefore("t1_before")) + assert.NoError(t, err) +} + +func TestUserService_Saved(t *testing.T) { + setup() + defer teardown() + + // we'll use this, similar payloads + blob := readFileContents(t, "testdata/user/overview.json") + + mux.HandleFunc("/user/user1/saved", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + fmt.Fprint(w, blob) + }) + + posts, comments, _, err := client.User.Saved(ctx) + assert.NoError(t, err) + assert.NotNil(t, posts) + assert.NotNil(t, comments) + + assert.Len(t, posts.Links, 1) + assert.Equal(t, expectedPost, posts.Links[0]) + assert.Equal(t, "t1_f0zsa37", posts.After) + assert.Equal(t, "", posts.Before) + + assert.Len(t, comments.Comments, 1) + assert.Equal(t, expectedComment, comments.Comments[0]) + assert.Equal(t, "t1_f0zsa37", comments.After) + assert.Equal(t, "", comments.Before) +} + +func TestUserService_Saved_Options(t *testing.T) { + setup() + defer teardown() + + // we'll use this, similar payloads + blob := readFileContents(t, "testdata/user/overview.json") + + mux.HandleFunc("/user/user1/saved", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + + form := url.Values{} + form.Set("limit", "50") + form.Set("sort", SortControversial.String()) + + err := r.ParseForm() + assert.NoError(t, err) + assert.Equal(t, form, r.Form) + + fmt.Fprint(w, blob) + }) + + _, _, _, err := client.User.Saved(ctx, SetLimit(50), SetSort(SortControversial)) + assert.NoError(t, err) +} +func TestUserService_Upvoted(t *testing.T) { + setup() + defer teardown() + + // we'll use this, similar payloads + blob := readFileContents(t, "testdata/user/submitted.json") + + mux.HandleFunc("/user/user1/upvoted", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + fmt.Fprint(w, blob) + }) + + posts, _, err := client.User.Upvoted(ctx) + assert.NoError(t, err) + assert.NotNil(t, posts) + + assert.Len(t, posts.Links, 1) + assert.Equal(t, expectedPost, posts.Links[0]) + assert.Equal(t, "t3_gczwql", posts.After) + assert.Equal(t, "", posts.Before) +} + +func TestUserService_Upvoted_Options(t *testing.T) { + setup() + defer teardown() + + // we'll use this, similar payloads + blob := readFileContents(t, "testdata/user/submitted.json") + + mux.HandleFunc("/user/user1/upvoted", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + + form := url.Values{} + form.Set("limit", "30") + form.Set("after", "t3_after") + + err := r.ParseForm() + assert.NoError(t, err) + assert.Equal(t, form, r.Form) + + fmt.Fprint(w, blob) + }) + + _, _, err := client.User.Upvoted(ctx, SetLimit(30), SetAfter("t3_after")) + assert.NoError(t, err) +} +func TestUserService_Downvoted(t *testing.T) { + setup() + defer teardown() + + // we'll use this, similar payloads + blob := readFileContents(t, "testdata/user/submitted.json") + + mux.HandleFunc("/user/user1/downvoted", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + fmt.Fprint(w, blob) + }) + + posts, _, err := client.User.Downvoted(ctx) + assert.NoError(t, err) + assert.NotNil(t, posts) + + assert.Len(t, posts.Links, 1) + assert.Equal(t, expectedPost, posts.Links[0]) + assert.Equal(t, "t3_gczwql", posts.After) + assert.Equal(t, "", posts.Before) +} + +func TestUserService_Downvoted_Options(t *testing.T) { + setup() + defer teardown() + + // we'll use this, similar payloads + blob := readFileContents(t, "testdata/user/submitted.json") + + mux.HandleFunc("/user/user1/downvoted", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + + form := url.Values{} + form.Set("limit", "20") + form.Set("before", "t3_before") + + err := r.ParseForm() + assert.NoError(t, err) + assert.Equal(t, form, r.Form) + + fmt.Fprint(w, blob) + }) + + _, _, err := client.User.Downvoted(ctx, SetLimit(20), SetBefore("t3_before")) + assert.NoError(t, err) +} + +func TestUserService_Hidden(t *testing.T) { + setup() + defer teardown() + + // we'll use this, similar payloads + blob := readFileContents(t, "testdata/user/submitted.json") + + mux.HandleFunc("/user/user1/hidden", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + fmt.Fprint(w, blob) + }) + + posts, _, err := client.User.Hidden(ctx) + assert.NoError(t, err) + assert.NotNil(t, posts) + + assert.Len(t, posts.Links, 1) + assert.Equal(t, expectedPost, posts.Links[0]) + assert.Equal(t, "t3_gczwql", posts.After) + assert.Equal(t, "", posts.Before) +} + +func TestUserService_Gilded(t *testing.T) { + setup() + defer teardown() + + // we'll use this, similar payloads + blob := readFileContents(t, "testdata/user/submitted.json") + + mux.HandleFunc("/user/user1/gilded", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + fmt.Fprint(w, blob) + }) + + posts, _, err := client.User.Gilded(ctx) + assert.NoError(t, err) + assert.NotNil(t, posts) + + assert.Len(t, posts.Links, 1) + assert.Equal(t, expectedPost, posts.Links[0]) + assert.Equal(t, "t3_gczwql", posts.After) + assert.Equal(t, "", posts.Before) +}