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
2 changes: 2 additions & 0 deletions config/sus.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@

require "covered/sus"
include Covered::Sus

ENV["TRACES_BACKEND"] ||= "traces/backend/test"
7 changes: 7 additions & 0 deletions lib/traces/provider/utopia.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2026, by Samuel Williams.

require_relative "utopia/content/middleware"
require_relative "utopia/static/middleware"
20 changes: 20 additions & 0 deletions lib/traces/provider/utopia/content/middleware.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2026, by Samuel Williams.

require_relative "../../../../utopia/content/middleware"

require "traces/provider"

Traces::Provider(Utopia::Content::Middleware) do
def respond(link, request, localization: request.localization)
attributes = {
"link.key" => link.key,
"link.href" => link.href,
"link.locale" => localization&.locale,
}

Traces.trace("utopia.content.middleware.respond", attributes: attributes){super}
end
end
19 changes: 19 additions & 0 deletions lib/traces/provider/utopia/static/middleware.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2026, by Samuel Williams.

require_relative "../../../../utopia/static/middleware"

require "traces/provider"

Traces::Provider(Utopia::Static::Middleware) do
def respond(request, path, extension, content_type, localization: request.localization)
attributes = {
path: path,
locale: localization&.locale,
}

Traces.trace("utopia.static.respond", attributes: attributes){super}
end
end
12 changes: 0 additions & 12 deletions lib/utopia/content/middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

require "xrb/template"
require "concurrent/map"
require "traces/provider"

module Utopia
module Content
Expand Down Expand Up @@ -226,16 +225,5 @@ def relative_tag(name, node)
end
end

Traces::Provider(Middleware) do
def respond(link, request, localization: request.localization)
attributes = {
"link.key" => link.key,
"link.href" => link.href,
"link.locale" => localization&.locale,
}

Traces.trace("utopia.content.middleware.respond", attributes: attributes){super}
end
end
end
end
12 changes: 0 additions & 12 deletions lib/utopia/static/middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
require_relative "mime_types"
require_relative "../localization/resolver"

require "traces/provider"

module Utopia
module Static
DEFAULT_CACHE_CONTROL = "public, max-age=3600".freeze
Expand Down Expand Up @@ -138,15 +136,5 @@ def call(request)
end
end

Traces::Provider(Static) do
def respond(request, path, extension, content_type, localization: request.localization)
attributes = {
path: path,
locale: localization&.locale,
}

Traces.trace("utopia.static.respond", attributes: attributes){super}
end
end
end
end
55 changes: 55 additions & 0 deletions test/traces/provider/utopia/content/middleware.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2026, by Samuel Williams.

require "traces/provider/utopia"

require "utopia/content/link"
require "utopia/localization/preferences"
require "utopia/request"

describe Utopia::Content::Middleware do
let(:middleware) do
subject.new(
Protocol::HTTP::Middleware::NotFound,
root: File.expand_path(".", __dir__),
)
end

let(:request) {Utopia::Request["GET", "/example"]}
let(:localization) do
Utopia::Localization::Preferences.new(
all_locales: ["en"],
preferred_locales: ["en"],
default_locale: "en",
)
end

it "traces content responses" do
traces = []

mock(Traces) do |mock|
mock.wrap(:trace) do |original, name, attributes: nil, &block|
traces << [name, attributes]
original.call(name, attributes: attributes, &block)
end
end

link = Utopia::Content::Link.new(:virtual, "example", "en", "/example", uri: "/target")
response = middleware.respond(link, request, localization: localization)

expect(response.status).to be == 307
expect(traces).to be == [[
"utopia.content.middleware.respond",
{
"link.key" => "example.en",
"link.href" => "/target",
"link.locale" => "en",
},
]]
ensure
response&.close
middleware.close
end
end
40 changes: 40 additions & 0 deletions test/traces/provider/utopia/static/middleware.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2026, by Samuel Williams.

require "traces/provider/utopia/static/middleware"

require "utopia/request"

describe Utopia::Static::Middleware do
let(:root) {File.expand_path("../../../../utopia/.static", __dir__)}
let(:middleware) {subject.new(Protocol::HTTP::Middleware::NotFound, root: root)}
let(:request) {Utopia::Request["GET", "/test.txt"]}

it "traces static responses" do
traces = []

mock(Traces) do |mock|
mock.wrap(:trace) do |original, name, attributes: nil, &block|
traces << [name, attributes]
original.call(name, attributes: attributes, &block)
end
end

path = request.url.path
response = middleware.respond(request, path, ".txt", "text/plain", localization: nil)

expect(response.status).to be == 200
expect(traces).to be == [[
"utopia.static.respond",
{
path: path,
locale: nil,
},
]]
ensure
response&.close
middleware.close
end
end
82 changes: 17 additions & 65 deletions test/utopia/static.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,23 @@
expect(cache_control).to be(:frozen?)
end

it "computes cache control from the served file" do
served_file = nil
application = Utopia::Application.build do
use Utopia::Static,
root: File.expand_path(".static", __dir__),
cache_control: proc{|file| served_file = file; "private, max-age=#{file.bytesize}"}
end

response = application.call(Protocol::HTTP::Request["GET", "/test.txt"])

expect(response.headers["cache-control"]).to be == ["private", "max-age=12"]
expect(served_file).to be_a(Utopia::Static::LocalFile)
ensure
response&.close
application.close
end

let(:middleware) do
root = File.expand_path(".static", __dir__)

Expand Down Expand Up @@ -263,69 +280,4 @@
end
end
end

describe Utopia::Static::LocalFile do
it "uses a consistent metadata snapshot" do
Dir.mktmpdir do |directory|
path = File.join(directory, "test.txt")
File.write(path, "Original")

file = subject.new(path)
mtime_date = file.mtime_date
etag = file.etag

File.write(path, "Updated content")

expect(file.bytesize).to be == 8
expect(file.mtime_date).to be == mtime_date
expect(file.etag).to be == etag
end
end

it "includes subsecond modification time in entity tags" do
Dir.mktmpdir do |directory|
path = File.join(directory, "test.txt")
File.write(path, "Content")

seconds = Time.now.to_i - 1
first_mtime = Time.at(seconds, 100_000_000, :nanosecond)
second_mtime = Time.at(seconds, 200_000_000, :nanosecond)

File.utime(first_mtime, first_mtime, path)
first = subject.new(path)

File.utime(second_mtime, second_mtime, path)
second = subject.new(path)

expect(first.mtime_date).to be == second.mtime_date
expect(first.etag).not.to be == second.etag
end
end
end

describe Utopia::Static::MIME_TYPES do
let(:extensions) {Utopia::Static::MimeTypeLoader.extensions_for(subject[:default])}
let(:script_extensions) {Utopia::Static::MimeTypeLoader.extensions_for(subject[:scripts])}

it "groups script extensions" do
expect(script_extensions).to have_keys(
".js" => be == "text/javascript",
".mjs" => be == "text/javascript",
".wasm" => be == "application/wasm",
)
end

it "should give the correct mime type" do
expect(extensions).to have_keys(
".txt" => be == "text/plain",
".mjs" => be == "text/javascript",
".wasm" => be == "application/wasm",
".webm" => be == "video/webm",
".weba" => be == "audio/webm",
".ogg" => be == "audio/vorbis",
".spx" => be == "audio/speex",
".html" => be == "text/html",
)
end
end
end
60 changes: 60 additions & 0 deletions test/utopia/static/local_file.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2026, by Samuel Williams.

require "tmpdir"

require "utopia/static/local_file"

describe Utopia::Static::LocalFile do
it "uses a consistent metadata snapshot" do
Dir.mktmpdir do |directory|
path = File.join(directory, "test.txt")
File.write(path, "Original")

file = subject.new(path)
mtime_date = file.mtime_date
etag = file.etag

File.write(path, "Updated content")

expect(file.bytesize).to be == 8
expect(file.mtime_date).to be == mtime_date
expect(file.etag).to be == etag
end
end

it "includes subsecond modification time in entity tags" do
Dir.mktmpdir do |directory|
path = File.join(directory, "test.txt")
File.write(path, "Content")

seconds = Time.now.to_i - 1
first_mtime = Time.at(seconds, 100_000_000, :nanosecond)
second_mtime = Time.at(seconds, 200_000_000, :nanosecond)

File.utime(first_mtime, first_mtime, path)
first = subject.new(path)

File.utime(second_mtime, second_mtime, path)
second = subject.new(path)

expect(first.mtime_date).to be == second.mtime_date
expect(first.etag).not.to be == second.etag
end
end

it "matches strong entity tags for range requests" do
Dir.mktmpdir do |directory|
path = File.join(directory, "test.txt")
File.write(path, "Content")

file = subject.new(path)
file.instance_variable_set(:@etag, '"strong"')

expect(file.send(:if_range?, '"strong"')).to be == true
expect(file.send(:if_range?, '"different"')).to be == false
end
end
end
Loading
Loading