Testing Sequential Slug Generation and History Tracking in FriendlyId
This test suite validates the sequential slug generation functionality in FriendlyId, focusing on how the library handles duplicate slugs by appending sequential numbers. It covers core slug generation, history tracking, and scoped slugging scenarios.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
norman/friendly_id
test/sequentially_slugged_test.rb
require "helper"
class Article < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: :sequentially_slugged
end
class SequentiallySluggedTest < TestCaseClass
include FriendlyId::Test
include FriendlyId::Test::Shared::Core
def model_class
Article
end
test "should generate numerically sequential slugs" do
transaction do
records = 12.times.map { model_class.create! name: "Some news" }
assert_equal "some-news", records[0].slug
(1...12).each { |i| assert_equal "some-news-#{i + 1}", records[i].slug }
end
end
test "should cope when slugs are missing from the sequence" do
transaction do
record_1 = model_class.create!(name: "A thing")
record_2 = model_class.create!(name: "A thing")
record_3 = model_class.create!(name: "A thing")
assert_equal "a-thing", record_1.slug
assert_equal "a-thing-2", record_2.slug
assert_equal "a-thing-3", record_3.slug
record_2.destroy
record_4 = model_class.create!(name: "A thing")
assert_equal "a-thing-4", record_4.slug
end
end
test "should cope with strange column names" do
model_class = Class.new(ActiveRecord::Base) do
self.table_name = "journalists"
extend FriendlyId
friendly_id :name, use: :sequentially_slugged, slug_column: "strange name"
end
transaction do
record_1 = model_class.create! name: "Lois Lane"
record_2 = model_class.create! name: "Lois Lane"
assert_equal "lois-lane", record_1.attributes["strange name"]
assert_equal "lois-lane-2", record_2.attributes["strange name"]
end
end
test "should correctly sequence slugs that end in a number" do
transaction do
record1 = model_class.create! name: "Peugeuot 206"
assert_equal "peugeuot-206", record1.slug
record2 = model_class.create! name: "Peugeuot 206"
assert_equal "peugeuot-206-2", record2.slug
end
end
test "should correctly sequence slugs that begin with a number" do
transaction do
record1 = model_class.create! name: "2010 to 2015 Records"
assert_equal "2010-to-2015-records", record1.slug
record2 = model_class.create! name: "2010 to 2015 Records"
assert_equal "2010-to-2015-records-2", record2.slug
end
end
test "should sequence with a custom sequence separator" do
model_class = Class.new(ActiveRecord::Base) do
self.table_name = "novelists"
extend FriendlyId
friendly_id :name, use: :sequentially_slugged, sequence_separator: ":"
end
transaction do
record_1 = model_class.create! name: "Julian Barnes"
record_2 = model_class.create! name: "Julian Barnes"
assert_equal "julian-barnes", record_1.slug
assert_equal "julian-barnes:2", record_2.slug
end
end
test "should not generate a slug when canidates set is empty" do
model_class = Class.new(ActiveRecord::Base) do
self.table_name = "cities"
extend FriendlyId
friendly_id :slug_candidates, use: [:sequentially_slugged]
def slug_candidates
[name, [name, code]]
end
end
transaction do
record = model_class.create!(name: nil, code: nil)
assert_nil record.slug
end
end
test "should not generate a slug when the sluggable attribute is blank" do
record = model_class.create!(name: "")
assert_nil record.slug
end
end
class SequentiallySluggedTestWithHistory < TestCaseClass
include FriendlyId::Test
include FriendlyId::Test::Shared::Core
class Article < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: [:sequentially_slugged, :history]
end
Journalist = Class.new(ActiveRecord::Base) do
extend FriendlyId
friendly_id :name, use: [:sequentially_slugged, :history], slug_column: "strange name"
end
def model_class
Article
end
test "should work with regeneration with history when slug already exists" do
transaction do
record1 = model_class.create! name: "Test name"
record2 = model_class.create! name: "Another test name"
assert_equal "test-name", record1.slug
assert_equal "another-test-name", record2.slug
record2.name = "Test name"
record2.slug = nil
record2.save!
assert_equal "test-name-2", record2.slug
end
end
test "should work with regeneration with history when 2 slugs already exists and the second is changed" do
transaction do
record1 = model_class.create! name: "Test name"
record2 = model_class.create! name: "Test name"
record3 = model_class.create! name: "Another test name"
assert_equal "test-name", record1.slug
assert_equal "test-name-2", record2.slug
assert_equal "another-test-name", record3.slug
record2.name = "One more test name"
record2.slug = nil
record2.save!
assert_equal "one-more-test-name", record2.slug
record3.name = "Test name"
record3.slug = nil
record3.save!
assert_equal "test-name-3", record3.slug
end
end
test "should cope with strange column names" do
transaction do
record_1 = Journalist.create! name: "Lois Lane"
record_2 = Journalist.create! name: "Lois Lane"
assert_equal "lois-lane", record_1.attributes["strange name"]
assert_equal "lois-lane-2", record_2.attributes["strange name"]
end
end
end
class City < ActiveRecord::Base
has_many :restaurants
end
class Restaurant < ActiveRecord::Base
extend FriendlyId
belongs_to :city
friendly_id :name, use: [:sequentially_slugged, :scoped, :history], scope: :city
end
class SequentiallySluggedTestWithScopedHistory < TestCaseClass
include FriendlyId::Test
include FriendlyId::Test::Shared::Core
def model_class
Restaurant
end
test "should work with regeneration with scoped history" do
transaction do
city1 = City.create!
City.create!
record1 = model_class.create! name: "Test name", city: city1
record2 = model_class.create! name: "Test name", city: city1
assert_equal "test-name", record1.slug
assert_equal "test-name-2", record2.slug
record2.name = "Another test name"
record2.slug = nil
record2.save!
record3 = model_class.create! name: "Test name", city: city1
assert_equal "test-name-3", record3.slug
end
end
end