lobsters/lobsters

Replace last few `dependent: :destroy` with `:restrict_with_exception`

Open

#1.929 geöffnet am 24. Feb. 2026

Auf GitHub ansehen
 (2 Kommentare) (0 Reaktionen) (0 zugewiesene Personen)Ruby (961 Forks)user submission
buggood first issue

Repository-Metriken

Stars
 (4.680 Stars)
PR-Merge-Metriken
 (Durchschn. Merge 6T 5h) (24 gemergte PRs in 30 T)

Beschreibung

Follow-up to https://github.com/lobsters/lobsters/pull/1914, where almost all occurrences of dependent: :destroy were replaced with dependent: :restrict_with_exception.

Four occurrences of dependent: :destroy remain.

  • 3 in the Story model
  • 1 in the User model

Our goal with this issue is to change these to dependent: :restrict_with_exception, if possible.

When any of these is changed to dependent: :restrict_with_exception, the result is many test failures with output similar to this:

W, [2026-01-30T23:17:18.530247 #13476]  WARN -- : heinous_inline_partial initialized, found: {"app/views/comments/_threads.html.erb" => "app/views/comments/_comment.html.erb", "app/views/home/index.html.erb" => "app/views/stories/_listdetail.html.erb", "app/views/search/index.html.erb" => "app/views/stories/_listdetail.html.erb"}
........................................................F.........................................................................................................................................................................*.......................................................................................................................................................*..................................................................................................................................
An error occurred in an `after(:context)` hook.
Failure/Error: @stories.each(&:destroy!)

ActiveRecord::DeleteRestrictionError:
  Cannot delete record because of dependent taggings
# ./spec/models/search_spec.rb:62:in 'Array#each'
# ./spec/models/search_spec.rb:62:in 'block (2 levels) in <top (required)>'
...........F........F........................FFFFFFFFFFFF..........F...................FF................F.............................F.............................................................................................FFFFFF........FF........................................

Pending: (Failures listed here are expected and do not affect your suite's status)

  1) Submitting Stories new user submitting a new origin from a multi-author domain
     # Story submission approval - this would probably have a high false-positive rate
     Failure/Error: expect(page).to have_content "multiple authors"
       expected to find text "multiple authors" in "Active Recent Comments Submit Search Inbox Threads Saved username316 (0)\n✉\n1\nExample Story tag1 github.com/bob\nvia username316 just now | edit | hide |\n| caches\nArchive.org Archive.today Ghostarchive\n| no comments\n  Preview\nMarkdown formatting available\nemphasized text surround text with *asterisks* strong text surround text with **two asterisks** struck-through surround text with ~~two tilde characters~~ fixed width surround text with `backticks` linked text [linked text](http://example.com/) or just a bare URL to create without a title\nquoted text\nprefix text with >\npre text\nprefix text with at least    4 spaces @ or ~ Refer to users by prefixing their username with a @ or tilde\nAbout Tags Filter Moderation Log"
     # ./spec/features/submit_story_spec.rb:129:in 'block (3 levels) in <top (required)>'
     # ./spec/features/submit_story_spec.rb:130:in 'block (2 levels) in <top (required)>'

  2) ModMailMessage add some examples to (or delete) /Users/felipe.vogel/dev/lobsters/spec/models/mod_mail_message_spec.rb
     # Not yet implemented
     # ./spec/models/mod_mail_message_spec.rb:4

Failures:

  1) Github.enabled? should not be enabled
     Failure/Error: expect(described_class).not_to be_enabled
       Expected Github not to return a truthy result for enabled? or enableds?.
     # ./spec/extras/github_spec.rb:8:in 'block (3 levels) in <top (required)>'

  2) Story checks for a previously posted story with same url
     Failure/Error: expect(Story.count).to eq(0)
       Expected 5 to eq 0.
     # ./spec/models/story_spec.rb:78:in 'block (2 levels) in <top (required)>'

  3) Story doesn't log changed to derived field normalized_url
     Failure/Error: s = create(:story, url: "https://example.com/1")

     ActiveRecord::RecordInvalid:
       Validation failed: Url has already been submitted within the past 30 days
     # ./spec/models/story_spec.rb:291:in 'block (2 levels) in <top (required)>'

  4) Story scopes hidden exclude tags are empty
     Failure/Error: let(:story2) { create(:story, title: "Hello 2", url: "http://example.com/2", tags: [tag]) }

     ActiveRecord::RecordInvalid:
       Validation failed: Url has already been submitted within the past 30 days
     # ./spec/models/story_spec.rb:479:in 'block (4 levels) in <top (required)>'
     # ./spec/models/story_spec.rb:483:in 'block (4 levels) in <top (required)>'

  5) Story scopes hidden exclude tags are empty
     Failure/Error: let(:story2) { create(:story, title: "Hello 2", url: "http://example.com/2", tags: [tag]) }

     ActiveRecord::RecordInvalid:
       Validation failed: Url has already been submitted within the past 30 days
     # ./spec/models/story_spec.rb:479:in 'block (4 levels) in <top (required)>'
     # ./spec/models/story_spec.rb:483:in 'block (4 levels) in <top (required)>'

  6) Story scopes hidden exclude tags are empty
     Failure/Error: let(:story2) { create(:story, title: "Hello 2", url: "http://example.com/2", tags: [tag]) }

     ActiveRecord::RecordInvalid:
       Validation failed: Url has already been submitted within the past 30 days
     # ./spec/models/story_spec.rb:479:in 'block (4 levels) in <top (required)>'
     # ./spec/models/story_spec.rb:483:in 'block (4 levels) in <top (required)>'

  7) Story scopes hidden excluded tags are provided
     Failure/Error: let(:story2) { create(:story, title: "Hello 2", url: "http://example.com/2", tags: [tag]) }

     ActiveRecord::RecordInvalid:
       Validation failed: Url has already been submitted within the past 30 days
     # ./spec/models/story_spec.rb:479:in 'block (4 levels) in <top (required)>'
     # ./spec/models/story_spec.rb:483:in 'block (4 levels) in <top (required)>'

  8) Story scopes hidden excluded tags are provided
     Failure/Error: let(:story2) { create(:story, title: "Hello 2", url: "http://example.com/2", tags: [tag]) }

     ActiveRecord::RecordInvalid:
       Validation failed: Url has already been submitted within the past 30 days
     # ./spec/models/story_spec.rb:479:in 'block (4 levels) in <top (required)>'
     # ./spec/models/story_spec.rb:483:in 'block (4 levels) in <top (required)>'

  9) Story scopes hidden excluded tags are provided
     Failure/Error: let(:story2) { create(:story, title: "Hello 2", url: "http://example.com/2", tags: [tag]) }

     ActiveRecord::RecordInvalid:
       Validation failed: Url has already been submitted within the past 30 days
     # ./spec/models/story_spec.rb:479:in 'block (4 levels) in <top (required)>'
     # ./spec/models/story_spec.rb:483:in 'block (4 levels) in <top (required)>'

  10) Story scopes newest exclude tags are emtpy returns two stories
      Failure/Error: let!(:story1) { create :story, title: "Hello 1", url: "http://example.com/1", tags: [tag] }

      ActiveRecord::RecordInvalid:
        Validation failed: Url has already been submitted within the past 30 days
      # ./spec/models/story_spec.rb:518:in 'block (4 levels) in <top (required)>'

  11) Story scopes newest exclude tags are emtpy first story in a list is last created
      Failure/Error: let!(:story1) { create :story, title: "Hello 1", url: "http://example.com/1", tags: [tag] }

      ActiveRecord::RecordInvalid:
        Validation failed: Url has already been submitted within the past 30 days
      # ./spec/models/story_spec.rb:518:in 'block (4 levels) in <top (required)>'

  12) Story scopes newest exclude tags are emtpy last story in a list is first created
      Failure/Error: let!(:story1) { create :story, title: "Hello 1", url: "http://example.com/1", tags: [tag] }

      ActiveRecord::RecordInvalid:
        Validation failed: Url has already been submitted within the past 30 days
      # ./spec/models/story_spec.rb:518:in 'block (4 levels) in <top (required)>'

  13) Story scopes newest exclude tags are provided returns only one story without tag
      Failure/Error: let!(:story1) { create :story, title: "Hello 1", url: "http://example.com/1", tags: [tag] }

      ActiveRecord::RecordInvalid:
        Validation failed: Url has already been submitted within the past 30 days
      # ./spec/models/story_spec.rb:518:in 'block (4 levels) in <top (required)>'

  14) Story scopes active is ordered by most-recent comment
      Failure/Error: expect(Story.active(user)).to eq([newer_comment.story, older_comment.story])

        Expected #<ActiveRecord::Relation [#<Story id: 311, comments_count: 0, created_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43 -06:00 (CST)>, description: nil, domain_id: 191, flags: 0, hotness: -0.223466780881e5, is_deleted: false, is_moderated: false, last_comment_at: nil, last_edited_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43+(433029/500000) -06:00 (CST)>, markeddown_description: nil, mastodon_id: nil, merged_story_id: nil, normalized_url: "example.com/3", origin_id: nil, score: 1, short_id: "a6jqv7", stories_count: 0, title: "multitag term1 t1 t2", token: "story_01kg94asdpfxpayabhfv8chcrj", twitter_id: nil, unavailable_at: nil, updated_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:45+(1153/500000) -06:00 (CST)>, url: "https://example.com/3", user_id: 615, user_is_author: false, user_is_following: false>, #<Story id: 312, comments_count: 0, created_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43 -06:00 (CST)>, description: nil, domain_id: 191, flags: 0, hotness: -0.223466780881e5, is_deleted: false, is_moderated: false, last_comment_at: nil, last_edited_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43+(442871/500000) -06:00 (CST)>, markeddown_description: nil, mastodon_id: nil, merged_story_id: nil, normalized_url: "example.com/unique", origin_id: nil, score: 1, short_id: "c7i9vj", stories_count: 0, title: "unique", token: "story_01kg94asebe6dtjqqqf8j26d69", twitter_id: nil, unavailable_at: nil, updated_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:45+(6597/500000) -06:00 (CST)>, url: "https://example.com/unique", user_id: 616, user_is_author: false, user_is_following: false>, #<Story id: 313, comments_count: 0, created_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43 -06:00 (CST)>, description: nil, domain_id: 191, flags: 0, hotness: -0.223466780881e5, is_deleted: false, is_moderated: false, last_comment_at: nil, last_edited_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43+(898867/1000000) -06:00 (CST)>, markeddown_description: nil, mastodon_id: nil, merged_story_id: nil, normalized_url: "example.com/1", origin_id: nil, score: 1, short_id: "qvvsx2", stories_count: 0, title: "term1 domain1", token: "story_01kg94asesfpe8hpaygt8xcf0w", twitter_id: nil, unavailable_at: nil, updated_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:45+(10581/500000) -06:00 (CST)>, url: "https://example.com/1", user_id: 615, user_is_author: false, user_is_following: false>, #<Story id: 314, comments_count: 0, created_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43 -06:00 (CST)>, description: nil, domain_id: 191, flags: 0, hotness: -0.223466780881e5, is_deleted: false, is_moderated: false, last_comment_at: nil, last_edited_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43+(91269/100000) -06:00 (CST)>, markeddown_description: nil, mastodon_id: nil, merged_story_id: nil, normalized_url: "example.com/2", origin_id: nil, score: 1, short_id: "9vzlek", stories_count: 0, title: "term1 t2", token: "story_01kg94asf6ehfvbxn2khs6g7m7", twitter_id: nil, unavailable_at: nil, updated_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:45+(141/5000) -06:00 (CST)>, url: "https://example.com/2", user_id: 616, user_is_author: false, user_is_following: false>, #<Story id: 315, comments_count: 0, created_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43 -06:00 (CST)>, description: nil, domain_id: 192, flags: 0, hotness: -0.223466780881e5, is_deleted: false, is_moderated: false, last_comment_at: nil, last_edited_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43+(926099/1000000) -06:00 (CST)>, markeddown_description: nil, mastodon_id: nil, merged_story_id: nil, normalized_url: "lobste.rs/1", origin_id: nil, score: 1, short_id: "nbr9mz", stories_count: 0, title: "term1 domain2", token: "story_01kg94asfmex0t9jvvvkh9r64e", twitter_id: nil, unavailable_at: nil, updated_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:45+(551/15625) -06:00 (CST)>, url: "https://lobste.rs/1", user_id: 615, user_is_author: false, user_is_following: false>, #<Story id: 355, comments_count: 1, created_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:46 -06:00 (CST)>, description: nil, domain_id: 191, flags: 0, hotness: -0.223468542172e5, is_deleted: false, is_moderated: false, last_comment_at: nil, last_edited_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:46+(367373/500000) -06:00 (CST)>, markeddown_description: nil, mastodon_id: nil, merged_story_id: nil, normalized_url: "example.com/329", origin_id: nil, score: 1, short_id: "ut20lf", stories_count: 0, title: "story title 328", token: "story_01kg94aw75fe8vx1ntsq1c07ps", twitter_id: nil, unavailable_at: nil, updated_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:46+(160183/200000) -06:00 (CST)>, url: "http://example.com/329", user_id: 684, user_is_author: false, user_is_following: false>, #<Story id: 356, comments_count: 1, created_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:46 -06:00 (CST)>, description: nil, domain_id: 191, flags: 0, hotness: -0.223468542172e5, is_deleted: false, is_moderated: false, last_comment_at: nil, last_edited_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:46+(753951/1000000) -06:00 (CST)>, markeddown_description: nil, mastodon_id: nil, merged_story_id: nil, normalized_url: "example.com/330", origin_id: nil, score: 1, short_id: "wreacq", stories_count: 0, title: "story title 329", token: "story_01kg94aw7sfq6br2pvgesk8w8x", twitter_id: nil, unavailable_at: nil, updated_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:46+(6239/8000) -06:00 (CST)>, url: "http://example.com/330", user_id: 685, user_is_author: false, user_is_following: false>]>
           to eq [#<Story id: 355, comments_count: 1, created_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:46 -06:00 (CST)>, description: nil, domain_id: 191, flags: 0, hotness: -0.223468542172e5, is_deleted: false, is_moderated: false, last_comment_at: nil, last_edited_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:46+(367373/500000) -06:00 (CST)>, markeddown_description: nil, mastodon_id: nil, merged_story_id: nil, normalized_url: "example.com/329", origin_id: nil, score: 1, short_id: "ut20lf", stories_count: 0, title: "story title 328", token: "story_01kg94aw75fe8vx1ntsq1c07ps", twitter_id: nil, unavailable_at: nil, updated_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:46+(160183/200000) -06:00 (CST)>, url: "http://example.com/329", user_id: 684, user_is_author: false, user_is_following: false>, #<Story id: 356, comments_count: 1, created_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:46 -06:00 (CST)>, description: nil, domain_id: 191, flags: 0, hotness: -0.223468542172e5, is_deleted: false, is_moderated: false, last_comment_at: nil, last_edited_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:46+(753951/1000000) -06:00 (CST)>, markeddown_description: nil, mastodon_id: nil, merged_story_id: nil, normalized_url: "example.com/330", origin_id: nil, score: 1, short_id: "wreacq", stories_count: 0, title: "story title 329", token: "story_01kg94aw7sfq6br2pvgesk8w8x", twitter_id: nil, unavailable_at: nil, updated_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:46+(6239/8000) -06:00 (CST)>, url: "http://example.com/330", user_id: 685, user_is_author: false, user_is_following: false>]

        Diff:

          #<ActiveRecord::Relation [
        +   #<Story {
        +     id: 311,
        +     comments_count: 0,
        +     created_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: 0,
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     description: nil,
        +     domain_id: 191,
        +     flags: 0,
        +     hotness: -0.223466780881e5,
        +     is_deleted: false,
        +     is_moderated: false,
        +     last_comment_at: nil,
        +     last_edited_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: (433029/500000),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     markeddown_description: nil,
        +     mastodon_id: nil,
        +     merged_story_id: nil,
        +     normalized_url: "example.com/3",
        +     origin_id: nil,
        +     score: 1,
        +     short_id: "a6jqv7",
        +     stories_count: 0,
        +     title: "multitag term1 t1 t2",
        +     token: "story_01kg94asdpfxpayabhfv8chcrj",
        +     twitter_id: nil,
        +     unavailable_at: nil,
        +     updated_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 45,
        +       subsec: (1153/500000),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     url: "https://example.com/3",
        +     user_id: 615,
        +     user_is_author: false,
        +     user_is_following: false
        +   }>,
        +   #<Story {
        +     id: 312,
        +     comments_count: 0,
        +     created_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: 0,
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     description: nil,
        +     domain_id: 191,
        +     flags: 0,
        +     hotness: -0.223466780881e5,
        +     is_deleted: false,
        +     is_moderated: false,
        +     last_comment_at: nil,
        +     last_edited_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: (442871/500000),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     markeddown_description: nil,
        +     mastodon_id: nil,
        +     merged_story_id: nil,
        +     normalized_url: "example.com/unique",
        +     origin_id: nil,
        +     score: 1,
        +     short_id: "c7i9vj",
        +     stories_count: 0,
        +     title: "unique",
        +     token: "story_01kg94asebe6dtjqqqf8j26d69",
        +     twitter_id: nil,
        +     unavailable_at: nil,
        +     updated_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 45,
        +       subsec: (6597/500000),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     url: "https://example.com/unique",
        +     user_id: 616,
        +     user_is_author: false,
        +     user_is_following: false
        +   }>,
        +   #<Story {
        +     id: 313,
        +     comments_count: 0,
        +     created_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: 0,
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     description: nil,
        +     domain_id: 191,
        +     flags: 0,
        +     hotness: -0.223466780881e5,
        +     is_deleted: false,
        +     is_moderated: false,
        +     last_comment_at: nil,
        +     last_edited_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: (898867/1000000),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     markeddown_description: nil,
        +     mastodon_id: nil,
        +     merged_story_id: nil,
        +     normalized_url: "example.com/1",
        +     origin_id: nil,
        +     score: 1,
        +     short_id: "qvvsx2",
        +     stories_count: 0,
        +     title: "term1 domain1",
        +     token: "story_01kg94asesfpe8hpaygt8xcf0w",
        +     twitter_id: nil,
        +     unavailable_at: nil,
        +     updated_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 45,
        +       subsec: (10581/500000),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     url: "https://example.com/1",
        +     user_id: 615,
        +     user_is_author: false,
        +     user_is_following: false
        +   }>,
        +   #<Story {
        +     id: 314,
        +     comments_count: 0,
        +     created_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: 0,
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     description: nil,
        +     domain_id: 191,
        +     flags: 0,
        +     hotness: -0.223466780881e5,
        +     is_deleted: false,
        +     is_moderated: false,
        +     last_comment_at: nil,
        +     last_edited_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: (91269/100000),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     markeddown_description: nil,
        +     mastodon_id: nil,
        +     merged_story_id: nil,
        +     normalized_url: "example.com/2",
        +     origin_id: nil,
        +     score: 1,
        +     short_id: "9vzlek",
        +     stories_count: 0,
        +     title: "term1 t2",
        +     token: "story_01kg94asf6ehfvbxn2khs6g7m7",
        +     twitter_id: nil,
        +     unavailable_at: nil,
        +     updated_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 45,
        +       subsec: (141/5000),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     url: "https://example.com/2",
        +     user_id: 616,
        +     user_is_author: false,
        +     user_is_following: false
        +   }>,
        +   #<Story {
        +     id: 315,
        +     comments_count: 0,
        +     created_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: 0,
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     description: nil,
        +     domain_id: 192,
        +     flags: 0,
        +     hotness: -0.223466780881e5,
        +     is_deleted: false,
        +     is_moderated: false,
        +     last_comment_at: nil,
        +     last_edited_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: (926099/1000000),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     markeddown_description: nil,
        +     mastodon_id: nil,
        +     merged_story_id: nil,
        +     normalized_url: "lobste.rs/1",
        +     origin_id: nil,
        +     score: 1,
        +     short_id: "nbr9mz",
        +     stories_count: 0,
        +     title: "term1 domain2",
        +     token: "story_01kg94asfmex0t9jvvvkh9r64e",
        +     twitter_id: nil,
        +     unavailable_at: nil,
        +     updated_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 45,
        +       subsec: (551/15625),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     url: "https://lobste.rs/1",
        +     user_id: 615,
        +     user_is_author: false,
        +     user_is_following: false
        +   }>,
            #<Story {
              id: 355,
              comments_count: 1,
              created_at: #<ActiveSupport::TimeWithZone {
                year: 2026,
                month: 1,
                day: 30,
                hour: 22,
                min: 17,
                sec: 46,
                subsec: 0,
                zone: "CST",
                utc_offset: -21600
              }>,
              description: nil,
              domain_id: 191,
              flags: 0,
              hotness: -0.223468542172e5,
              is_deleted: false,
              is_moderated: false,
              last_comment_at: nil,
              last_edited_at: #<ActiveSupport::TimeWithZone {
                year: 2026,
                month: 1,
                day: 30,
                hour: 22,
                min: 17,
                sec: 46,
                subsec: (367373/500000),
                zone: "CST",
                utc_offset: -21600
              }>,
              markeddown_description: nil,
              mastodon_id: nil,
              merged_story_id: nil,
              normalized_url: "example.com/329",
              origin_id: nil,
              score: 1,
              short_id: "ut20lf",
              stories_count: 0,
              title: "story title 328",
              token: "story_01kg94aw75fe8vx1ntsq1c07ps",
              twitter_id: nil,
              unavailable_at: nil,
              updated_at: #<ActiveSupport::TimeWithZone {
                year: 2026,
                month: 1,
                day: 30,
                hour: 22,
                min: 17,
                sec: 46,
                subsec: (160183/200000),
                zone: "CST",
                utc_offset: -21600
              }>,
              url: "http://example.com/329",
              user_id: 684,
              user_is_author: false,
              user_is_following: false
            }>,
            #<Story {
              id: 356,
              comments_count: 1,
              created_at: #<ActiveSupport::TimeWithZone {
                year: 2026,
                month: 1,
                day: 30,
                hour: 22,
                min: 17,
                sec: 46,
                subsec: 0,
                zone: "CST",
                utc_offset: -21600
              }>,
              description: nil,
              domain_id: 191,
              flags: 0,
              hotness: -0.223468542172e5,
              is_deleted: false,
              is_moderated: false,
              last_comment_at: nil,
              last_edited_at: #<ActiveSupport::TimeWithZone {
                year: 2026,
                month: 1,
                day: 30,
                hour: 22,
                min: 17,
                sec: 46,
                subsec: (753951/1000000),
                zone: "CST",
                utc_offset: -21600
              }>,
              markeddown_description: nil,
              mastodon_id: nil,
              merged_story_id: nil,
              normalized_url: "example.com/330",
              origin_id: nil,
              score: 1,
              short_id: "wreacq",
              stories_count: 0,
              title: "story title 329",
              token: "story_01kg94aw7sfq6br2pvgesk8w8x",
              twitter_id: nil,
              unavailable_at: nil,
              updated_at: #<ActiveSupport::TimeWithZone {
                year: 2026,
                month: 1,
                day: 30,
                hour: 22,
                min: 17,
                sec: 46,
                subsec: (6239/8000),
                zone: "CST",
                utc_offset: -21600
              }>,
              url: "http://example.com/330",
              user_id: 685,
              user_is_author: false,
              user_is_following: false
            }>
          ]>
      # ./spec/models/story_spec.rb:556:in 'block (4 levels) in <top (required)>'

  15) Story scopes active does not show hidden stories
      Failure/Error: expect(Story.active(hidden_story_user)).to eq([normal_comment.story])

        Expected #<ActiveRecord::Relation [#<Story id: 311, comments_count: 0, created_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43 -06:00 (CST)>, description: nil, domain_id: 191, flags: 0, hotness: -0.223466780881e5, is_deleted: false, is_moderated: false, last_comment_at: nil, last_edited_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43+(433029/500000) -06:00 (CST)>, markeddown_description: nil, mastodon_id: nil, merged_story_id: nil, normalized_url: "example.com/3", origin_id: nil, score: 1, short_id: "a6jqv7", stories_count: 0, title: "multitag term1 t1 t2", token: "story_01kg94asdpfxpayabhfv8chcrj", twitter_id: nil, unavailable_at: nil, updated_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:45+(1153/500000) -06:00 (CST)>, url: "https://example.com/3", user_id: 615, user_is_author: false, user_is_following: false>, #<Story id: 312, comments_count: 0, created_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43 -06:00 (CST)>, description: nil, domain_id: 191, flags: 0, hotness: -0.223466780881e5, is_deleted: false, is_moderated: false, last_comment_at: nil, last_edited_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43+(442871/500000) -06:00 (CST)>, markeddown_description: nil, mastodon_id: nil, merged_story_id: nil, normalized_url: "example.com/unique", origin_id: nil, score: 1, short_id: "c7i9vj", stories_count: 0, title: "unique", token: "story_01kg94asebe6dtjqqqf8j26d69", twitter_id: nil, unavailable_at: nil, updated_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:45+(6597/500000) -06:00 (CST)>, url: "https://example.com/unique", user_id: 616, user_is_author: false, user_is_following: false>, #<Story id: 313, comments_count: 0, created_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43 -06:00 (CST)>, description: nil, domain_id: 191, flags: 0, hotness: -0.223466780881e5, is_deleted: false, is_moderated: false, last_comment_at: nil, last_edited_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43+(898867/1000000) -06:00 (CST)>, markeddown_description: nil, mastodon_id: nil, merged_story_id: nil, normalized_url: "example.com/1", origin_id: nil, score: 1, short_id: "qvvsx2", stories_count: 0, title: "term1 domain1", token: "story_01kg94asesfpe8hpaygt8xcf0w", twitter_id: nil, unavailable_at: nil, updated_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:45+(10581/500000) -06:00 (CST)>, url: "https://example.com/1", user_id: 615, user_is_author: false, user_is_following: false>, #<Story id: 314, comments_count: 0, created_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43 -06:00 (CST)>, description: nil, domain_id: 191, flags: 0, hotness: -0.223466780881e5, is_deleted: false, is_moderated: false, last_comment_at: nil, last_edited_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43+(91269/100000) -06:00 (CST)>, markeddown_description: nil, mastodon_id: nil, merged_story_id: nil, normalized_url: "example.com/2", origin_id: nil, score: 1, short_id: "9vzlek", stories_count: 0, title: "term1 t2", token: "story_01kg94asf6ehfvbxn2khs6g7m7", twitter_id: nil, unavailable_at: nil, updated_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:45+(141/5000) -06:00 (CST)>, url: "https://example.com/2", user_id: 616, user_is_author: false, user_is_following: false>, #<Story id: 315, comments_count: 0, created_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43 -06:00 (CST)>, description: nil, domain_id: 192, flags: 0, hotness: -0.223466780881e5, is_deleted: false, is_moderated: false, last_comment_at: nil, last_edited_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:43+(926099/1000000) -06:00 (CST)>, markeddown_description: nil, mastodon_id: nil, merged_story_id: nil, normalized_url: "lobste.rs/1", origin_id: nil, score: 1, short_id: "nbr9mz", stories_count: 0, title: "term1 domain2", token: "story_01kg94asfmex0t9jvvvkh9r64e", twitter_id: nil, unavailable_at: nil, updated_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:45+(551/15625) -06:00 (CST)>, url: "https://lobste.rs/1", user_id: 615, user_is_author: false, user_is_following: false>, #<Story id: 358, comments_count: 1, created_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:47 -06:00 (CST)>, description: nil, domain_id: 191, flags: 0, hotness: -0.223468542298e5, is_deleted: false, is_moderated: false, last_comment_at: nil, last_edited_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:46+(499483/500000) -06:00 (CST)>, markeddown_description: nil, mastodon_id: nil, merged_story_id: nil, normalized_url: "example.com/332", origin_id: nil, score: 1, short_id: "g9rmxj", stories_count: 0, title: "story title 331", token: "story_01kg94awfdfaga0y3vaayyha25", twitter_id: nil, unavailable_at: nil, updated_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:47+(39947/1000000) -06:00 (CST)>, url: "http://example.com/332", user_id: 690, user_is_author: false, user_is_following: false>]>
           to eq [#<Story id: 358, comments_count: 1, created_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:47 -06:00 (CST)>, description: nil, domain_id: 191, flags: 0, hotness: -0.223468542298e5, is_deleted: false, is_moderated: false, last_comment_at: nil, last_edited_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:46+(499483/500000) -06:00 (CST)>, markeddown_description: nil, mastodon_id: nil, merged_story_id: nil, normalized_url: "example.com/332", origin_id: nil, score: 1, short_id: "g9rmxj", stories_count: 0, title: "story title 331", token: "story_01kg94awfdfaga0y3vaayyha25", twitter_id: nil, unavailable_at: nil, updated_at: #<ActiveSupport::TimeWithZone 2026-01-30 22:17:47+(39947/1000000) -06:00 (CST)>, url: "http://example.com/332", user_id: 690, user_is_author: false, user_is_following: false>]

        Diff:

          #<ActiveRecord::Relation [
        +   #<Story {
        +     id: 311,
        +     comments_count: 0,
        +     created_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: 0,
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     description: nil,
        +     domain_id: 191,
        +     flags: 0,
        +     hotness: -0.223466780881e5,
        +     is_deleted: false,
        +     is_moderated: false,
        +     last_comment_at: nil,
        +     last_edited_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: (433029/500000),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     markeddown_description: nil,
        +     mastodon_id: nil,
        +     merged_story_id: nil,
        +     normalized_url: "example.com/3",
        +     origin_id: nil,
        +     score: 1,
        +     short_id: "a6jqv7",
        +     stories_count: 0,
        +     title: "multitag term1 t1 t2",
        +     token: "story_01kg94asdpfxpayabhfv8chcrj",
        +     twitter_id: nil,
        +     unavailable_at: nil,
        +     updated_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 45,
        +       subsec: (1153/500000),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     url: "https://example.com/3",
        +     user_id: 615,
        +     user_is_author: false,
        +     user_is_following: false
        +   }>,
        +   #<Story {
        +     id: 312,
        +     comments_count: 0,
        +     created_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: 0,
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     description: nil,
        +     domain_id: 191,
        +     flags: 0,
        +     hotness: -0.223466780881e5,
        +     is_deleted: false,
        +     is_moderated: false,
        +     last_comment_at: nil,
        +     last_edited_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: (442871/500000),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     markeddown_description: nil,
        +     mastodon_id: nil,
        +     merged_story_id: nil,
        +     normalized_url: "example.com/unique",
        +     origin_id: nil,
        +     score: 1,
        +     short_id: "c7i9vj",
        +     stories_count: 0,
        +     title: "unique",
        +     token: "story_01kg94asebe6dtjqqqf8j26d69",
        +     twitter_id: nil,
        +     unavailable_at: nil,
        +     updated_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 45,
        +       subsec: (6597/500000),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     url: "https://example.com/unique",
        +     user_id: 616,
        +     user_is_author: false,
        +     user_is_following: false
        +   }>,
        +   #<Story {
        +     id: 313,
        +     comments_count: 0,
        +     created_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: 0,
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     description: nil,
        +     domain_id: 191,
        +     flags: 0,
        +     hotness: -0.223466780881e5,
        +     is_deleted: false,
        +     is_moderated: false,
        +     last_comment_at: nil,
        +     last_edited_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: (898867/1000000),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     markeddown_description: nil,
        +     mastodon_id: nil,
        +     merged_story_id: nil,
        +     normalized_url: "example.com/1",
        +     origin_id: nil,
        +     score: 1,
        +     short_id: "qvvsx2",
        +     stories_count: 0,
        +     title: "term1 domain1",
        +     token: "story_01kg94asesfpe8hpaygt8xcf0w",
        +     twitter_id: nil,
        +     unavailable_at: nil,
        +     updated_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 45,
        +       subsec: (10581/500000),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     url: "https://example.com/1",
        +     user_id: 615,
        +     user_is_author: false,
        +     user_is_following: false
        +   }>,
        +   #<Story {
        +     id: 314,
        +     comments_count: 0,
        +     created_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: 0,
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     description: nil,
        +     domain_id: 191,
        +     flags: 0,
        +     hotness: -0.223466780881e5,
        +     is_deleted: false,
        +     is_moderated: false,
        +     last_comment_at: nil,
        +     last_edited_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: (91269/100000),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     markeddown_description: nil,
        +     mastodon_id: nil,
        +     merged_story_id: nil,
        +     normalized_url: "example.com/2",
        +     origin_id: nil,
        +     score: 1,
        +     short_id: "9vzlek",
        +     stories_count: 0,
        +     title: "term1 t2",
        +     token: "story_01kg94asf6ehfvbxn2khs6g7m7",
        +     twitter_id: nil,
        +     unavailable_at: nil,
        +     updated_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 45,
        +       subsec: (141/5000),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     url: "https://example.com/2",
        +     user_id: 616,
        +     user_is_author: false,
        +     user_is_following: false
        +   }>,
        +   #<Story {
        +     id: 315,
        +     comments_count: 0,
        +     created_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: 0,
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     description: nil,
        +     domain_id: 192,
        +     flags: 0,
        +     hotness: -0.223466780881e5,
        +     is_deleted: false,
        +     is_moderated: false,
        +     last_comment_at: nil,
        +     last_edited_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 43,
        +       subsec: (926099/1000000),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     markeddown_description: nil,
        +     mastodon_id: nil,
        +     merged_story_id: nil,
        +     normalized_url: "lobste.rs/1",
        +     origin_id: nil,
        +     score: 1,
        +     short_id: "nbr9mz",
        +     stories_count: 0,
        +     title: "term1 domain2",
        +     token: "story_01kg94asfmex0t9jvvvkh9r64e",
        +     twitter_id: nil,
        +     unavailable_at: nil,
        +     updated_at: #<ActiveSupport::TimeWithZone {
        +       year: 2026,
        +       month: 1,
        +       day: 30,
        +       hour: 22,
        +       min: 17,
        +       sec: 45,
        +       subsec: (551/15625),
        +       zone: "CST",
        +       utc_offset: -21600
        +     }>,
        +     url: "https://lobste.rs/1",
        +     user_id: 615,
        +     user_is_author: false,
        +     user_is_following: false
        +   }>,
            #<Story {
              id: 358,
              comments_count: 1,
              created_at: #<ActiveSupport::TimeWithZone {
                year: 2026,
                month: 1,
                day: 30,
                hour: 22,
                min: 17,
                sec: 47,
                subsec: 0,
                zone: "CST",
                utc_offset: -21600
              }>,
              description: nil,
              domain_id: 191,
              flags: 0,
              hotness: -0.223468542298e5,
              is_deleted: false,
              is_moderated: false,
              last_comment_at: nil,
              last_edited_at: #<ActiveSupport::TimeWithZone {
                year: 2026,
                month: 1,
                day: 30,
                hour: 22,
                min: 17,
                sec: 46,
                subsec: (499483/500000),
                zone: "CST",
                utc_offset: -21600
              }>,
              markeddown_description: nil,
              mastodon_id: nil,
              merged_story_id: nil,
              normalized_url: "example.com/332",
              origin_id: nil,
              score: 1,
              short_id: "g9rmxj",
              stories_count: 0,
              title: "story title 331",
              token: "story_01kg94awfdfaga0y3vaayyha25",
              twitter_id: nil,
              unavailable_at: nil,
              updated_at: #<ActiveSupport::TimeWithZone {
                year: 2026,
                month: 1,
                day: 30,
                hour: 22,
                min: 17,
                sec: 47,
                subsec: (39947/1000000),
                zone: "CST",
                utc_offset: -21600
              }>,
              url: "http://example.com/332",
              user_id: 690,
              user_is_author: false,
              user_is_following: false
            }>
          ]>
      # ./spec/models/story_spec.rb:568:in 'block (4 levels) in <top (required)>'

  16) Story scopes top selects stories from the given interval
      Failure/Error: expect(stories.count).to eq(1)
        Expected 6 to eq 1.
      # ./spec/models/story_spec.rb:683:in 'block (4 levels) in <top (required)>'

  17) User doesn't allow changing username in < 1.year
      Failure/Error: user = create(:user, username: "alice", created_at: 2.years.ago)

      ActiveRecord::RecordInvalid:
        Validation failed: Username has already been taken
      # ./spec/models/user_spec.rb:34:in 'block (2 levels) in <top (required)>'

  18) User doesn't allow changing to a username someone else stopped using in the last 5 years
      Failure/Error: old = create(:user, username: "alice", created_at: 7.years.ago)

      ActiveRecord::RecordInvalid:
        Validation failed: Username has already been taken
      # ./spec/models/user_spec.rb:45:in 'block (2 levels) in <top (required)>'

  19) Username can rename! users
      Failure/Error: user = create :user, username: "alice"

      ActiveRecord::RecordInvalid:
        Validation failed: Username has already been taken
      # ./spec/models/username_spec.rb:5:in 'block (2 levels) in <top (required)>'

  20) home #newest_by_user shows a merge icon for merged stories
      Failure/Error: alice = create(:user, username: "alice")

      ActiveRecord::RecordInvalid:
        Validation failed: Username has already been taken
      # ./spec/requests/home_spec.rb:41:in 'block (3 levels) in <top (required)>'

  21) stories all returns all subsmissions of the given url
      Failure/Error: let!(:story1) { create(:story, url: "https://example.com/1", created_at: 90.days.ago) }

      ActiveRecord::RecordInvalid:
        Validation failed: Url has already been submitted within the past 30 days
      # ./spec/requests/story_urls_spec.rb:7:in 'block (3 levels) in <top (required)>'

  22) stories all doesn't error if not given a url
      Failure/Error: let!(:story1) { create(:story, url: "https://example.com/1", created_at: 90.days.ago) }

      ActiveRecord::RecordInvalid:
        Validation failed: Url has already been submitted within the past 30 days
      # ./spec/requests/story_urls_spec.rb:7:in 'block (3 levels) in <top (required)>'

  23) stories all doesn't error if the url hasn't been submitted
      Failure/Error: let!(:story1) { create(:story, url: "https://example.com/1", created_at: 90.days.ago) }

      ActiveRecord::RecordInvalid:
        Validation failed: Url has already been submitted within the past 30 days
      # ./spec/requests/story_urls_spec.rb:7:in 'block (3 levels) in <top (required)>'

  24) stories latest redirects to latest subsmission of the given url
      Failure/Error: let!(:story1) { create(:story, url: "https://example.com/1", created_at: 90.days.ago) }

      ActiveRecord::RecordInvalid:
        Validation failed: Url has already been submitted within the past 30 days
      # ./spec/requests/story_urls_spec.rb:31:in 'block (3 levels) in <top (required)>'

  25) stories latest a url that hasn't been submitted redirects users to submit
      Failure/Error: let!(:story1) { create(:story, url: "https://example.com/1", created_at: 90.days.ago) }

      ActiveRecord::RecordInvalid:
        Validation failed: Url has already been submitted within the past 30 days
      # ./spec/requests/story_urls_spec.rb:31:in 'block (3 levels) in <top (required)>'

  26) stories latest a url that hasn't been submitted 404s for visitors
      Failure/Error: let!(:story1) { create(:story, url: "https://example.com/1", created_at: 90.days.ago) }

      ActiveRecord::RecordInvalid:
        Validation failed: Url has already been submitted within the past 30 days
      # ./spec/requests/story_urls_spec.rb:31:in 'block (3 levels) in <top (required)>'

  27) users controller tree displays all users
      Failure/Error: let!(:user) { create(:user, username: "alice") }

      ActiveRecord::RecordInvalid:
        Validation failed: Username has already been taken
      # ./spec/requests/users_controller_spec.rb:31:in 'block (3 levels) in <top (required)>'

  28) users controller tree lists mods
      Failure/Error: let!(:user) { create(:user, username: "alice") }

      ActiveRecord::RecordInvalid:
        Validation failed: Username has already been taken
      # ./spec/requests/users_controller_spec.rb:31:in 'block (3 levels) in <top (required)>'

Finished in 34.72 seconds (files took 5.29 seconds to load)
794 examples, 28 failures, 2 pending, 1 error occurred outside of examples

Failed examples:

rspec ./spec/extras/github_spec.rb:7 # Github.enabled? should not be enabled
rspec ./spec/models/story_spec.rb:77 # Story checks for a previously posted story with same url
rspec ./spec/models/story_spec.rb:288 # Story doesn't log changed to derived field normalized_url
rspec ./spec/models/story_spec.rb:495 # Story scopes hidden exclude tags are empty
rspec ./spec/models/story_spec.rb:497 # Story scopes hidden exclude tags are empty
rspec ./spec/models/story_spec.rb:499 # Story scopes hidden exclude tags are empty
rspec ./spec/models/story_spec.rb:508 # Story scopes hidden excluded tags are provided
rspec ./spec/models/story_spec.rb:510 # Story scopes hidden excluded tags are provided
rspec ./spec/models/story_spec.rb:512 # Story scopes hidden excluded tags are provided
rspec ./spec/models/story_spec.rb:525 # Story scopes newest exclude tags are emtpy returns two stories
rspec ./spec/models/story_spec.rb:529 # Story scopes newest exclude tags are emtpy first story in a list is last created
rspec ./spec/models/story_spec.rb:533 # Story scopes newest exclude tags are emtpy last story in a list is first created
rspec ./spec/models/story_spec.rb:541 # Story scopes newest exclude tags are provided returns only one story without tag
rspec ./spec/models/story_spec.rb:550 # Story scopes active is ordered by most-recent comment
rspec ./spec/models/story_spec.rb:559 # Story scopes active does not show hidden stories
rspec ./spec/models/story_spec.rb:677 # Story scopes top selects stories from the given interval
rspec ./spec/models/user_spec.rb:33 # User doesn't allow changing username in < 1.year
rspec ./spec/models/user_spec.rb:44 # User doesn't allow changing to a username someone else stopped using in the last 5 years
rspec ./spec/models/username_spec.rb:4 # Username can rename! users
rspec ./spec/requests/home_spec.rb:39 # home #newest_by_user shows a merge icon for merged stories
rspec ./spec/requests/story_urls_spec.rb:10 # stories all returns all subsmissions of the given url
rspec ./spec/requests/story_urls_spec.rb:18 # stories all doesn't error if not given a url
rspec ./spec/requests/story_urls_spec.rb:24 # stories all doesn't error if the url hasn't been submitted
rspec ./spec/requests/story_urls_spec.rb:34 # stories latest redirects to latest subsmission of the given url
rspec ./spec/requests/story_urls_spec.rb:41 # stories latest a url that hasn't been submitted redirects users to submit
rspec ./spec/requests/story_urls_spec.rb:49 # stories latest a url that hasn't been submitted 404s for visitors
rspec ./spec/requests/users_controller_spec.rb:34 # users controller tree displays all users
rspec ./spec/requests/users_controller_spec.rb:40 # users controller tree lists mods

Coverage report generated for RSpec to /Users/felipe.vogel/dev/lobsters/coverage.
Line Coverage: 63.33% (3773 / 5958)

Contributor Guide