diff --git a/app/assets/stylesheets/application.css.erb b/app/assets/stylesheets/application.css.erb index b12f9c04..309d3e38 100644 --- a/app/assets/stylesheets/application.css.erb +++ b/app/assets/stylesheets/application.css.erb @@ -1246,7 +1246,7 @@ ul.user_tree { color: var(--color-fg-contrast-4-5); } -/* /u/:username/standing */ +/* /~:username/standing */ /* https://codepen.io/sosuke/pen/Pjoqqp */ .unwarned { filter: invert(36%) sepia(52%) saturate(0%) hue-rotate(292deg) brightness(96%) contrast(85%); diff --git a/app/controllers/keybase_proofs_controller.rb b/app/controllers/keybase_proofs_controller.rb index d9d743b0..23d0f725 100644 --- a/app/controllers/keybase_proofs_controller.rb +++ b/app/controllers/keybase_proofs_controller.rb @@ -44,8 +44,8 @@ class KeybaseProofsController < ApplicationController @contacts = ["admin@#{Keybase.DOMAIN}"] @prefill_url = "#{new_keybase_proof_url}?kb_username=%{kb_username}&" \ "kb_signature=%{sig_hash}&kb_ua=%{kb_ua}&username=%{username}" - @profile_url = "#{u_url}/%{username}" - @check_url = "#{u_url}/%{username}.json" + @profile_url = "/~%{username}" + @check_url = "/~%{username}.json" @logo_black = "https://lobste.rs/small-black-logo.svg" @logo_full = "https://lobste.rs/full-color.logo.svg" @user_re = User.username_regex_s[1...-1] diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 34dba763..542a0d50 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -30,6 +30,35 @@ module UsersHelper end end + def styled_user_link user, content = nil, css_classes = [] + if content.is_a?(Story) && content.user_is_author? + css_classes.push 'user_is_author' + end + if content.is_a?(Comment) && content.story && + content.story.user_is_author? && content.story.user_id == user.id + css_classes.push 'user_is_author' + end + + if !user.is_active? + css_classes.push 'inactive_user' + end + if user.is_new? + css_classes.push 'new_user' + end + + link_to(user.username, user, class: css_classes) + end + + def user_karma(user) + if user.is_admin? + "(administrator)" + elsif user.is_moderator? + "(moderator" + else + "(#{user.karma})" + end + end + private def user_is_moderator? diff --git a/app/models/comment.rb b/app/models/comment.rb index 6092b843..2aa869fc 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -353,20 +353,6 @@ class Comment < ApplicationRecord self.updated_at && (self.updated_at - self.created_at > 1.minute) end - def html_class_for_user - c = [] - if !self.user.is_active? - c.push "inactive_user" - elsif self.user.is_new? - c.push "new_user" - elsif self.story && self.story.user_is_author? && - self.story.user_id == self.user_id - c.push "user_is_author" - end - - c.join("") - end - def is_deletable_by_user?(user) if user && user.is_moderator? return true diff --git a/app/models/story.rb b/app/models/story.rb index 648809cc..a052e58f 100644 --- a/app/models/story.rb +++ b/app/models/story.rb @@ -478,19 +478,6 @@ class Story < ApplicationRecord @hider_count ||= HiddenStory.where(:story_id => self.id).count end - def html_class_for_user - c = [] - if !self.user.is_active? - c.push "inactive_user" - elsif self.user.is_new? - c.push "new_user" - elsif self.user_is_author? - c.push "user_is_author" - end - - c.join("") - end - def is_flaggable? if self.created_at && self.score > FLAGGABLE_MIN_SCORE Time.current - self.created_at <= FLAGGABLE_DAYS.days diff --git a/app/views/about/about.html.erb b/app/views/about/about.html.erb index 6c1edc6b..00183cea 100644 --- a/app/views/about/about.html.erb +++ b/app/views/about/about.html.erb @@ -7,8 +7,8 @@

Lobsters is a computing-focused community centered around link aggregation and discussion, launched on July 3rd, 2012. - The administrator is Peter Bhat Harkins ("pushcx"), contact him with any support issues. - Lobsters was created by joshua stein + The administrator is Peter Bhat Harkins ("pushcx"), contact him with any support issues. + Lobsters was created by joshua stein with careful design touches to encourage a healthy community:

@@ -87,7 +87,7 @@

Invitation Tree

- The full user tree is public and each user's profile + The full <%= link_to 'user tree', users_tree_path %> is public and each user's profile shows who invited them. This provides some degree of accountability and helps identify voting rings.

@@ -113,7 +113,7 @@

- The quickest way to receive an invitation is to talk to someone you recognize from the site. + The quickest way to receive an invitation is to talk to someone you <%= link_to 'recognize from the site', users_tree_path %>. If you wrote a link that was posted, please reach out in chat, we'd love to have you join the discussion. Finally, if you can't find anyone you know in the invitation tree and didn't author something posted to the site, <% if Rails.application.allow_invitation_requests? %> @@ -176,7 +176,7 @@

Transparency Policy

All moderator actions on this site are visible to - everyone and the identities of those moderators are public. + everyone and the identities of those moderators are <%= link_to 'public', moderators_path %>. While the individual actions of a moderator may cause debate, there should be no question about if an action happened or who is responsible.

diff --git a/app/views/about/chat.html.erb b/app/views/about/chat.html.erb index 45be4e28..19316730 100644 --- a/app/views/about/chat.html.erb +++ b/app/views/about/chat.html.erb @@ -9,11 +9,11 @@ in #lobsters. This channel was originally created by - @kristof on freenode, and + @kristof on freenode, and stayed with the network on 2021-05-19 after that domain was lost. - @pushcx, - @355e3b (c355e3b on IRC), and - @aleph (Church on IRC) are channel operators. + @pushcx, + @355e3b (c355e3b on IRC), and + @aleph (Church on IRC) are channel operators.

diff --git a/app/views/comments/_comment.html.erb b/app/views/comments/_comment.html.erb index c43ab1a3..039b654a 100644 --- a/app/views/comments/_comment.html.erb +++ b/app/views/comments/_comment.html.erb @@ -49,13 +49,10 @@ class="comment <%= comment.current_vote ? (comment.current_vote[:vote] == 1 ? <% end %> <% if (@user && @user.show_avatars?) || !@user %> - <%= - avatar_img(comment.user, 16) %> + <%= link_to avatar_img(comment.user, 16), comment.user %> <% end %> - <%= - comment.user.username %> + <%= styled_user_link comment.user, comment %> <% if comment.hat %> <%= comment.hat.to_html_label %> diff --git a/app/views/comments/_threads.html.erb b/app/views/comments/_threads.html.erb index 35725dd5..6e211b9e 100644 --- a/app/views/comments/_threads.html.erb +++ b/app/views/comments/_threads.html.erb @@ -81,13 +81,10 @@ class="comment <%= comment.current_vote ? (comment.current_vote[:vote] == 1 ? <% end %> <% if (@user && @user.show_avatars?) || !@user %> - <%= - avatar_img(comment.user, 16) %> + <%= link_to avatar_img(comment.user, 16), comment.user %> <% end %> - <%= - comment.user.username %> + <%= styled_user_link comment.user, comment %> <% if comment.hat %> <%= comment.hat.to_html_label %> diff --git a/app/views/hats/index.html.erb b/app/views/hats/index.html.erb index 5b38e3c6..1e886f55 100644 --- a/app/views/hats/index.html.erb +++ b/app/views/hats/index.html.erb @@ -21,8 +21,7 @@ <% @hat_groups.keys.sort_by{|a| a.downcase }.each do |hg| %> <% @hat_groups[hg].sort_by{|hh| hh.user.username.downcase }.each do |hh| %> - <%= hh.user.username - %> + <%= styled_user_link hh.user %> <%= hh.to_html_label %> <% if hh.link.to_s.match(/^http/) %> diff --git a/app/views/hats/requests_index.html.erb b/app/views/hats/requests_index.html.erb index 24f622f7..55f7b91e 100644 --- a/app/views/hats/requests_index.html.erb +++ b/app/views/hats/requests_index.html.erb @@ -11,7 +11,7 @@

<%= f.label :user_id, "User:", :class => "required" %> - <%= hr.user.username %> + <%= styled_user_link hr.user %> <%= time_ago_in_words(hr.created_at) %>
diff --git a/app/views/invitations/build.html.erb b/app/views/invitations/build.html.erb index 06558256..07859102 100644 --- a/app/views/invitations/build.html.erb +++ b/app/views/invitations/build.html.erb @@ -1,6 +1,6 @@

- If you don't know (or can't find) an existing user from + If you don't know (or can't find) an <%= link_to 'existing_user', users_tree_path %> from whom to request an invitation, you can make a public request for one. This will display your name and memo to all other logged-in users who can then send you an invitation if they recognize you. diff --git a/app/views/login/index.html.erb b/app/views/login/index.html.erb index e9686369..f7b18a5d 100644 --- a/app/views/login/index.html.erb +++ b/app/views/login/index.html.erb @@ -36,7 +36,7 @@

Not a user yet? Read about how invitations work and see if you know - a current user of the site. + <%= link_to 'a current user', users_tree_path %> of the site. The chat room does not require an invitation.

<% end %> diff --git a/app/views/messages/index.html.erb b/app/views/messages/index.html.erb index 2c2467cd..55995e16 100644 --- a/app/views/messages/index.html.erb +++ b/app/views/messages/index.html.erb @@ -17,15 +17,13 @@
<% if @direction == :in %> <% if message.author %> - <%= - message.author.username %> + <%= styled_user_link message.author %> <% else %> <%= message.author_username %> <% end %> <%= message.hat.to_html_label if message.hat %> <% else %> - <%= - message.recipient.username %> + <%= styled_user_link message.recipient %> <% end %>
diff --git a/app/views/messages/show.html.erb b/app/views/messages/show.html.erb index fbce1a82..29be9f7b 100644 --- a/app/views/messages/show.html.erb +++ b/app/views/messages/show.html.erb @@ -4,14 +4,13 @@

From <% if @message.author %> - <%= @message.author.username %> + <%= styled_user_link @message.author %> <% else %> <%= @message.author_username %> <% end %> <%= @message.hat.to_html_label if @message.hat %> to - <%= - @message.recipient.username %> + <%= styled_user_link @message.recipient %> <%= time_ago_in_words_label(@message.created_at) %>

@@ -57,6 +56,6 @@ <%= render partial: 'form', locals: { new_message: @new_message, replying: true } %> <% else %> - For help with this message, contact a moderator. + For help with this message, contact <%= link_to 'a moderator', moderators_path %>. <% end %>
diff --git a/app/views/moderations/_table.html.erb b/app/views/moderations/_table.html.erb index 0fcb5cd3..56e09c54 100644 --- a/app/views/moderations/_table.html.erb +++ b/app/views/moderations/_table.html.erb @@ -27,8 +27,7 @@ <%= link_to("Category: #{mod.category.category}", mod.category) %> <% elsif mod.user_id %> <% if mod.user %> - User - <%= mod.user.username %> + <%= link_to "User #{mod.user.username}", mod.user %> <% else %> User <%= mod.user_id %> (Deleted) <% end %> diff --git a/app/views/settings/index.html.erb b/app/views/settings/index.html.erb index 6c465a8b..297dce2f 100644 --- a/app/views/settings/index.html.erb +++ b/app/views/settings/index.html.erb @@ -1,5 +1,5 @@ <% content_for :subnav do %> - Public Profile + <%= link_to 'Public Profile', @user %> Filtered Tags | <%= link_post 'Logout', logout_path, confirm: 'Are you sure you want to logout?' %> <% end %> @@ -311,7 +311,7 @@
  • Your comments with negative scores will be deleted, and you can check "disown comments" below if you want all of your stories and comments to change to list - inactive-user instead of your username. + inactive-user instead of your username.
  • diff --git a/app/views/signup/index.html.erb b/app/views/signup/index.html.erb index 911954df..36c0c1af 100644 --- a/app/views/signup/index.html.erb +++ b/app/views/signup/index.html.erb @@ -6,8 +6,8 @@ <% else %>

    Signup is currently by invitation only to combat spam and increase - accountability. If you know a current user of the site, - ask them for an invitation, or + accountability. If you know <%= link_to 'a current user', users_tree_path %> + of the site, ask them for an invitation, or <% if Rails.application.allow_invitation_requests? %> request one publicly. <% else %> diff --git a/app/views/signup/invited.html.erb b/app/views/signup/invited.html.erb index c6449f8a..0b1174e5 100644 --- a/app/views/signup/invited.html.erb +++ b/app/views/signup/invited.html.erb @@ -14,8 +14,7 @@

    <%= f.label :invitation, "Invited By:", :class => "required" %> - <%= - @invitation.user.username %> + <%= link_to @invitation.user.username, user_path(@invitation.user), target: '_blank' %>

    <% end %> diff --git a/app/views/stories/_listdetail.html.erb b/app/views/stories/_listdetail.html.erb index 19a9830b..f7ba0e9d 100644 --- a/app/views/stories/_listdetail.html.erb +++ b/app/views/stories/_listdetail.html.erb @@ -66,16 +66,14 @@ class="story <%= story.vote && story.vote[:vote] == 1 ? "upvoted" : "" %> <% end %> <% if (@user && @user.show_avatars?) || !@user %> - <%= - avatar_img(ms.user, 16) %> + <%= link_to avatar_img(ms.user, 16), ms.user %> <% end %> <% if ms.user_is_author? %> authored by <% else %> via <% end %> - <%= ms.user.username %> + <%= styled_user_link ms.user, story, ['u-author', 'h-card'] %> <%= time_ago_in_words_label(ms.created_at) %> <% if ms.is_editable_by_user?(@user) %> @@ -98,8 +96,7 @@ class="story <%= story.vote && story.vote[:vote] == 1 ? "upvoted" : "" %>
    <% if (@user && @user.show_avatars?) || !@user %> - <%= - avatar_img(story.user, 16) %> + <%= link_to avatar_img(story.user, 16), story.user %> <% end %> <% if story.previewing %> <% if story.user_is_author? %> @@ -107,8 +104,8 @@ class="story <%= story.vote && story.vote[:vote] == 1 ? "upvoted" : "" %> <% else %> via <% end %> - <%= - story.user.username %> + + <%= styled_user_link story.user, story, ['u-author', 'h-card'] %> just now <% else %> <% if story.user_is_author? %> @@ -116,8 +113,7 @@ class="story <%= story.vote && story.vote[:vote] == 1 ? "upvoted" : "" %> <% else %> via <% end %> - <%= story.user.username %> + <%= styled_user_link story.user, story, ['u-author', 'h-card'] %> <%= time_ago_in_words_label(story.created_at) %> diff --git a/app/views/stories/_similar.html.erb b/app/views/stories/_similar.html.erb index 65cd3c07..8fbcdd57 100644 --- a/app/views/stories/_similar.html.erb +++ b/app/views/stories/_similar.html.erb @@ -3,7 +3,7 @@
  • <%= story.title %> <%= story.user_is_author? ? "authored by" : "via" %> - <%= story.user.username %> + <%= styled_user_link story.user, story %> <%= time_ago_in_words_label(story.created_at) %> | diff --git a/app/views/users/_invitationform.html.erb b/app/views/users/_invitationform.html.erb index 17d948ec..33417644 100644 --- a/app/views/users/_invitationform.html.erb +++ b/app/views/users/_invitationform.html.erb @@ -1,8 +1,8 @@

    -Invitations are unlimited, but persons you invite will be associated with your -account in the user tree and you may be responsible for them -if they cause problems. Please use your discretion when inviting persons you -don't personally know. +Invitations are unlimited, but persons you invite will be associated with your account +in the <%= link_to 'user tree', users_tree_path %> +and you may be responsible for them if they cause problems. +Please use your discretion when inviting persons you don't personally know.

    <%= form_with url: invitations_path, method: :post do |f| %> diff --git a/app/views/users/list.html.erb b/app/views/users/list.html.erb index 21b33b9d..7acd519b 100644 --- a/app/views/users/list.html.erb +++ b/app/views/users/list.html.erb @@ -6,20 +6,8 @@ diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 44d968d9..2a6c3383 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -39,9 +39,8 @@ <%= time_ago_in_words_label(@showing_user.created_at) %> <% if @showing_user.invited_by_user %> - by invitation from - <%= link_to @showing_user.invited_by_user.try(:username), - @showing_user.invited_by_user %> + by <%= link_to 'invitation', users_tree_path(anchor: @showing_user.username) %> from + <%= link_to @showing_user.invited_by_user.try(:username), @showing_user.invited_by_user %> <% end %>
    @@ -215,14 +214,14 @@ <% @showing_user.votes_for_others.limit(15).each do |v| %> <%= v.vote == 1 ? '+' : v.reason %> <% if v.comment_id %> - <%= v.comment.user.try(:username) %> + <%= v.comment.user.try(:username) %> <%= v.story.title %> comment:
    <%= v.comment.comment.split[0..10].join(' ') %> <% elsif v.story_id && !v.comment_id %> - <%= v.story.user.try(:username) %> + <%= v.story.user.try(:username) %> <%= v.story.title %> <% end %>

    diff --git a/app/views/users/tree.html.erb b/app/views/users/tree.html.erb index 8527583c..fb735076 100644 --- a/app/views/users/tree.html.erb +++ b/app/views/users/tree.html.erb @@ -4,9 +4,10 @@ <% if @newest %>

    Newest users: - <%= raw @newest.map{|u| "#{u.username} " << - "(#{u.karma})" }.join(", ") %> + <% @newest.each do |user| %> + <%= styled_user_link user %> + <%= user_karma(user) %><%= ',' if user != @newest.last %> + <% end %>

    <% end %> @@ -18,20 +19,9 @@ <% while subtree %> <% if (user = subtree.pop) %>
  • "> - - class="inactive_user" - <% elsif user.is_new? %> - class="new_user" - <% end %> - ><%= user.username %> - <% if user.is_admin? %> - (administrator) - <% elsif user.is_moderator? %> - (moderator) - <% else %> - (<%= user.karma %>) - <% end %> + <%= styled_user_link user %> + <%= user_karma(user) %> + <% if (children = @users_by_parent[user.id]) %> <% # drill down deeper in the tree %> <% ancestors << subtree %> diff --git a/config/routes.rb b/config/routes.rb index dd3e239d..d2623386 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -14,8 +14,6 @@ Rails.application.routes.draw do get "/active/page/:page" => "home#active" get "/newest" => "home#newest" get "/newest/page/:page" => "home#newest" - get "/newest/:user" => "home#newest_by_user" - get "/newest/:user/page/:page" => "home#newest_by_user" get "/recent" => "home#recent" get "/recent/page/:page" => "home#recent" get "/hidden" => "home#hidden" @@ -37,7 +35,6 @@ Rails.application.routes.draw do get "/top/:length/page/:page" => "home#top" get "/threads" => "comments#user_threads" - get "/threads/:user" => "comments#user_threads", :as => "user_threads" get "/replies" => "replies#all" get "/replies/page/:page" => "replies#all" @@ -133,20 +130,32 @@ Rails.application.routes.draw do get "/s/:id/(:title)" => "stories#show" - get "/u" => "users#tree" - get "/u/:username" => "users#show", :as => "user" - get "/u/:username/standing" => "users#standing", :as => "user_standing" + get "/users" => "users#tree", :as => "users_tree" + get "/~:username" => "users#show", :as => "user" + get "/~:username/standing" => "users#standing", :as => "user_standing" + get "/~:user/stories(/page/:page)" => "home#newest_by_user" + get "/~:user/threads" => "comments#user_threads", :as => "user_threads" + + post "/~:username/ban" => "users#ban", :as => "user_ban" + post "/~:username/unban" => "users#unban", :as => "user_unban" + post "/~:username/disable_invitation" => "users#disable_invitation", + :as => "user_disable_invite" + post "/~:username/enable_invitation" => "users#enable_invitation", + :as => "user_enable_invite" + + # 2023-07 redirect /u to /~username and /users (for tree) + get "/u", to: redirect("/users", status: 302) + get "/u/:username", to: redirect("/~%{username}", status: 302) + # we don't do /@alice but easy mistake with comments autolinking @alice + get "/@:username", to: redirect("/~%{username}", status: 302) + get "/u/:username/standing", to: redirect("~%{username}/standing", status: 302) + get "/newest/:user", to: redirect("~%{user}/stories", status: 302) + get "/newest/:user(/page/:page)", to: redirect("~%{user}/stories/page/%{page}", status: 302) + get "/threads/:user", to: redirect("~%{user}/threads", status: 302) get "/avatars/:username_size.png" => "avatars#show" post "/avatars/expire" => "avatars#expire" - post "/users/:username/ban" => "users#ban", :as => "user_ban" - post "/users/:username/unban" => "users#unban", :as => "user_unban" - post "/users/:username/disable_invitation" => "users#disable_invitation", - :as => "user_disable_invite" - post "/users/:username/enable_invitation" => "users#enable_invitation", - :as => "user_enable_invite" - get "/settings" => "settings#index" post "/settings" => "settings#update" post "/settings/delete_account" => "settings#delete_account", diff --git a/extras/markdowner.rb b/extras/markdowner.rb index 871d7b54..be37bd42 100644 --- a/extras/markdowner.rb +++ b/extras/markdowner.rb @@ -51,7 +51,7 @@ class Markdowner if User.exists?(:username => user[1..-1]) link = CommonMarker::Node.new(:link) - link.url = Rails.application.root_url + "u/#{user[1..-1]}" + link.url = Rails.application.root_url + "~#{user[1..-1]}" node.insert_after(link) link_text = CommonMarker::Node.new(:text) diff --git a/spec/models/markdowner_spec.rb b/spec/models/markdowner_spec.rb index 94e3034f..e7b5d0c5 100644 --- a/spec/models/markdowner_spec.rb +++ b/spec/models/markdowner_spec.rb @@ -10,7 +10,7 @@ describe Markdowner do create(:user, :username => "blahblah") expect(Markdowner.to_html("hi @blahblah test")) - .to eq("

    hi " + + .to eq("

    hi " + "@blahblah test

    \n") expect(Markdowner.to_html("hi @flimflam test")) @@ -46,8 +46,8 @@ describe Markdowner do .to eq("

    " + "ex

    \n") - expect(Markdowner.to_html("[ex](/u/abc)")) - .to eq("

    ex

    \n") + expect(Markdowner.to_html("[ex](/~abc)")) + .to eq("

    ex

    \n") end context "when images are not allowed" do diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb index 46652ff8..8794e54f 100644 --- a/spec/requests/users_controller_spec.rb +++ b/spec/requests/users_controller_spec.rb @@ -5,7 +5,7 @@ describe 'users controller' do it 'displays the username' do user = create(:user) - get "/u/#{user.username}" + get "/~#{user.username}" expect(response.body).to include(user.username) end @@ -29,7 +29,7 @@ describe 'users controller' do it "displays to the user" do sign_in bad_user - get "/u/#{bad_user.username}/standing" + get "/~#{bad_user.username}/standing" expect(response.body).to include("flags") expect(response.body).to include("You") end @@ -38,12 +38,12 @@ describe 'users controller' do user2 = create(:user) sign_in user2 - get "/u/#{bad_user.username}/standing" + get "/~#{bad_user.username}/standing" expect(response.status).to eq(302) end it "doesn't display to logged-out users" do - get "/u/#{bad_user.username}/standing" + get "/~#{bad_user.username}/standing" expect(response.status).to eq(302) end @@ -51,7 +51,7 @@ describe 'users controller' do mod = create(:user, :moderator) sign_in mod - get "/u/#{bad_user.username}/standing" + get "/~#{bad_user.username}/standing" expect(response.body).to include("flags") end end diff --git a/spec/routing/user_spec.rb b/spec/routing/user_spec.rb new file mode 100644 index 00000000..3e2c82f9 --- /dev/null +++ b/spec/routing/user_spec.rb @@ -0,0 +1,66 @@ +require 'rails_helper' + +# rubocop:disable RSpec/MultipleDescribes +describe 'user routing' do + it 'users#tree' do + assert_routing( + '/users', + controller: 'users', action: 'tree' + ) + end + + it 'users#show' do + assert_routing( + '/~alice', + controller: 'users', action: 'show', username: 'alice' + ) + end + + it 'home#stories' do + assert_routing( + '/~alice/stories', + controller: 'home', action: 'newest_by_user', user: 'alice' + ) + assert_routing( + '/~alice/stories/page/2', + controller: 'home', action: 'newest_by_user', user: 'alice', page: '2' + ) + end + + it 'comments#user_threads' do + assert_routing( + '/~alice/threads', + controller: 'comments', action: 'user_threads', user: 'alice' + ) + end +end + +# odd Rails limitation: you can redirect from routes but not test those from routing tests +describe 'user redirects', type: :request do + it 'old-style to tilde' do + expect(get('/u/alice')).to redirect_to('/~alice') + end + + it 'user tree' do + expect(get('/u')).to redirect_to('/users') + end + + it 'newest stories' do + expect(get('/newest/alice')).to redirect_to('/~alice/stories') + expect(get('/newest/alice/page/2')).to redirect_to('/~alice/stories/page/2') + end + + it 'threads' do + expect(get('/threads/alice')).to redirect_to('/~alice/threads') + end + + # 2023-07: I'm deploying with 302 redirects so I don't have to worry about 301s getting + # indefinitely cached in case of error, but after some time to build confidence, it's appropriate + # to 301. I'm putting a time bomb test in here to remind me to finalize. + it 'is temporarily temporary' do + expect(get('/u/alice')).to eq(302) + + expect(Time.zone.today).to be_before(Date.new(2024, 1, 1)) + end +end +# rubocop:enable RSpec/MultipleDescribes