Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions test/controllers/sitemaps_controller_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# frozen_string_literal: true
require "test_helper"

describe SitemapsController do
let(:convention) { create(:convention) }

def sitemap_urls
xml = Nokogiri.XML(response.body)
xml.remove_namespaces!
xml.xpath("//url").map { |url| [url.at_xpath("loc").text, url.at_xpath("lastmod")&.text] }
end

def locations
sitemap_urls.map(&:first)
end

describe "GET show for a convention" do
setup { set_convention convention }

it "is a sitemap in XML" do
get :show

assert_response :success
assert_equal "application/xml", response.media_type
assert_equal "urlset", Nokogiri.XML(response.body).root.name
assert_equal "http://www.sitemaps.org/schemas/sitemap/0.9", Nokogiri.XML(response.body).root.namespace.href
end

it "is empty for a convention with no pages and no events" do
get :show

assert_empty locations
end

it "lists pages with when they were last changed" do
page = create(:page, parent: convention, slug: "about")

get :show

assert_equal [["http://#{convention.domain}/pages/about", page.updated_at.xmlschema]], sitemap_urls
end

it "lists the root page at the root of the site" do
page = create(:page, parent: convention, slug: "welcome")
convention.update!(root_page: page)

get :show

assert_equal ["http://#{convention.domain}/"], locations
end

it "leaves out pages hidden from search" do
create(:page, parent: convention, slug: "visible")
create(:page, parent: convention, slug: "secret", hidden_from_search: true)

get :show

assert_equal ["http://#{convention.domain}/pages/visible"], locations
end

it "lists the events pages, the schedule and each active event once there are events" do
event = create(:event, convention:, title: "Dragon Hunt")
create(:run, event:)

get :show

assert_includes locations, "http://#{convention.domain}/events"
assert_includes locations, "http://#{convention.domain}/events/schedule"
assert_includes locations, "http://#{convention.domain}/events/#{event.to_param}"
end

it "says the events lists were last changed when the latest event or run was" do
event = create(:event, convention:)
run = create(:run, event:)
run.update_columns(updated_at: 1.day.from_now) # rubocop:disable Rails/SkipsModelValidations

get :show

events_entry = sitemap_urls.find { |location, _lastmod| location.end_with?("/events") }
assert_equal run.reload.updated_at.xmlschema, events_entry.last
end

it "leaves out dropped events" do
create(:event, convention:, title: "Kept")
dropped = create(:event, convention:, title: "Dropped", status: "dropped")

get :show

assert_not_includes locations, "http://#{convention.domain}/events/#{dropped.to_param}"
end

it "leaves out the event pages when every event has been dropped" do
create(:event, convention:, status: "dropped")

get :show

assert_not_includes locations, "http://#{convention.domain}/events"
end

it "does not include another convention's pages" do
create(:page, parent: create(:convention), slug: "elsewhere")

get :show

assert_empty locations
end
end

describe "GET show for the root site" do
setup { @request.host = "root.example.com" }

it "lists the root site's pages and none of the convention things" do
root_site = create(:root_site)
root_site.pages.create!(name: "About us", slug: "about-us", content: "Hello")

get :show

assert_response :success
assert_includes locations, "http://root.example.com/pages/about-us"
assert_not(locations.any? { |location| location.include?("/events") })
end
end
end
131 changes: 131 additions & 0 deletions test/lib/intercode/virtual_host_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# frozen_string_literal: true
require "test_helper"

class Intercode::VirtualHostTest < ActiveSupport::TestCase
describe Intercode::VirtualHostConstraint do
let(:constraint) { Intercode::VirtualHostConstraint.new }

it "matches requests that were mapped to a convention" do
convention = create(:convention)
request = ActionDispatch::Request.new("intercode.convention" => convention)

assert constraint.matches?(request)
end

it "does not match requests for the root site" do
assert_not constraint.matches?(ActionDispatch::Request.new({}))
assert_not constraint.matches?(ActionDispatch::Request.new("intercode.convention" => nil))
end
end

describe ".with_virtual_host_domain" do
it "overrides the domain inside the block, and gives it back afterwards" do
assert_nil Intercode.overridden_virtual_host_domain

Intercode.with_virtual_host_domain("example.com") do
assert_equal "example.com", Intercode.overridden_virtual_host_domain
end

assert_nil Intercode.overridden_virtual_host_domain
end

it "gives it back even if the block raises" do
assert_raises(RuntimeError) { Intercode.with_virtual_host_domain("example.com") { raise "boom" } }

assert_nil Intercode.overridden_virtual_host_domain
end

it "returns what the block returns" do
assert_equal 42, Intercode.with_virtual_host_domain("example.com") { 42 }
end
end

describe Intercode::FindVirtualHost do
let(:downstream) { ->(env) { [200, {}, [env["intercode.convention"]&.name.to_s]] } }
let(:middleware) { Intercode::FindVirtualHost.new(downstream) }
let(:convention) { create(:convention, name: "Mapped Con", domain: "mapped.example.com") }

def request_env(host: "mapped.example.com", path: "/", **extra)
Rack::MockRequest.env_for("http://#{host}#{path}", extra)
end

def found_convention(env)
middleware.call(env)
env["intercode.convention"]
end

it "maps a request to the convention with its domain" do
convention

assert_equal convention, found_convention(request_env)
end

it "maps a request for an unknown domain to no convention, meaning the root site" do
convention

assert_nil found_convention(request_env(host: "unknown.example.com"))
end

it "passes the request on to the app" do
convention

assert_equal [200, {}, ["Mapped Con"]], middleware.call(request_env)
end

it "prefers a convention domain header from a front-end proxy to the host" do
convention

env = request_env(:host => "proxy.internal", "HTTP_X_INTERCODE_CONVENTION_DOMAIN" => "mapped.example.com")

assert_equal convention, found_convention(env)
end

it "prefers an overridden domain to both" do
convention
other = create(:convention, domain: "other.example.com")

Intercode.with_virtual_host_domain("other.example.com") do
env = request_env(:host => "mapped.example.com", "HTTP_X_INTERCODE_CONVENTION_DOMAIN" => "mapped.example.com")

assert_equal other, found_convention(env)
end
end

it "leaves a convention that an earlier layer already chose alone" do
convention
chosen = create(:convention, domain: "chosen.example.com")

env = request_env("intercode.convention" => chosen)

assert_equal chosen, found_convention(env)
end

it "does not look up a convention for asset requests" do
convention
env = request_env(path: "#{Rails.application.config.assets.prefix}/application.js")

middleware.call(env)

assert_not env.key?("intercode.convention")
end

it "says how it mapped the request when debugging is turned on" do
convention
messages = []
original = ENV.fetch("FIND_VIRTUAL_HOST_DEBUG", nil)
ENV["FIND_VIRTUAL_HOST_DEBUG"] = "1"

Rails
.logger
.stub(:info, ->(message) { messages << message }) do
middleware.call(request_env)
middleware.call(request_env(host: "unknown.example.com"))
end

assert_match(/mapped.example.com mapped\s+to\s+Mapped Con/m, messages.first)
assert_match(/unknown.example.com mapped to root site/, messages.second)
ensure
ENV["FIND_VIRTUAL_HOST_DEBUG"] = original
end
end
end
110 changes: 110 additions & 0 deletions test/models/event_proposal/timeblock_preference_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# frozen_string_literal: true
require "test_helper"

class EventProposal::TimeblockPreferenceTest < ActiveSupport::TestCase
let(:start) { Time.utc(2026, 6, 5, 18, 0, 0) }
let(:finish) { Time.utc(2026, 6, 5, 22, 0, 0) }

def preference(**attrs)
EventProposal::TimeblockPreference.new({ start:, finish:, label: "Evening", ordinality: "1" }.merge(attrs))
end

describe "times" do
it "keeps times as they are" do
assert_equal start, preference.start
assert_equal finish, preference.finish
end

it "reads ISO 8601 strings" do
parsed = preference(start: "2026-06-05T18:00:00Z", finish: "2026-06-05T22:00:00-04:00")

assert_equal start, parsed.start
assert_equal Time.utc(2026, 6, 6, 2, 0, 0), parsed.finish
end

it "raises for a string that is not an ISO 8601 time" do
assert_raises(ArgumentError) { preference(start: "next tuesday") }
end

it "can be changed afterwards" do
pref = preference
pref.start = "2026-06-06T10:00:00Z"
pref.finish = Time.utc(2026, 6, 6, 12)

assert_equal Time.utc(2026, 6, 6, 10), pref.start
assert_equal Time.utc(2026, 6, 6, 12), pref.finish
end
end

describe "#attributes and serialization" do
it "has the four attributes" do
assert_equal({ start:, finish:, label: "Evening", ordinality: "1" }, preference.attributes)
end

it "serializes to JSON" do
json = preference.as_json

assert_equal "Evening", json["label"]
assert_equal "1", json["ordinality"]
assert_equal start.as_json, json["start"]
end
end

describe "#ordinality_description" do
{
"1" => "1st Choice",
"2" => "2nd Choice",
"3" => "3rd Choice",
"X" => "Not Available"
}.each do |ordinality, description|
it "describes #{ordinality} as #{description}" do
assert_equal description, preference(ordinality:).ordinality_description
end
end

it "understands ordinalities given as numbers" do
assert_equal "2nd Choice", preference(ordinality: 2).ordinality_description
end

it "has no description for anything else" do
assert_nil preference(ordinality: "4").ordinality_description
assert_nil preference(ordinality: nil).ordinality_description
end
end

describe "#==" do
it "is equal to another preference with the same attributes" do
assert_equal preference, preference
end

it "is not equal when any attribute differs" do
assert_not_equal preference, preference(label: "Other")
assert_not_equal preference, preference(ordinality: "2")
assert_not_equal preference, preference(start: start + 1.hour)
assert_not_equal preference, preference(finish: finish + 1.hour)
end

it "is equal to a hash of its attributes, with string or symbol keys" do
assert_equal preference, { start:, finish:, label: "Evening", ordinality: "1" }
assert_equal preference,
{ "start" => "2026-06-05T18:00:00Z", "finish" => finish, "label" => "Evening", "ordinality" => "1" }
end

it "is equal to controller parameters with its attributes" do
params =
ActionController::Parameters.new(
start: "2026-06-05T18:00:00Z",
finish: "2026-06-05T22:00:00Z",
label: "Evening",
ordinality: "1"
)

assert_equal preference, params
end

it "is not equal to something else" do
assert_not_equal preference, "Evening"
assert_not_equal preference, nil
end
end
end
Loading