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
79 changes: 79 additions & 0 deletions .github/workflows/ci_steps.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: CI

on: [push, pull_request]

jobs:
test:
name: Ruby ${{ matrix.ruby }}
runs-on: ubuntu-latest
container: ruby:${{ matrix.ruby }}
strategy:
fail-fast: false
matrix:
ruby: ['2.6']

env:
QT_SELECT: qt5
OPENSSL_CONF: /dev/null
LANG: C.UTF-8
LC_ALL: C.UTF-8

steps:
- name: Fix APT for EOL Debian
run: |
if grep -q buster /etc/apt/sources.list 2>/dev/null; then
echo "deb [trusted=yes] http://archive.debian.org/debian buster main" > /etc/apt/sources.list
echo "deb [trusted=yes] http://archive.debian.org/debian-security buster/updates main" >> /etc/apt/sources.list
fi
echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/99archive
echo 'Acquire::AllowInsecureRepositories "true";' >> /etc/apt/apt.conf.d/99archive

- name: Install system packages
run: |
apt-get update -qq
apt-get install -y --no-install-recommends \
git ca-certificates curl bzip2 \
build-essential g++ make \
qt5-qmake qtbase5-dev libqt5webkit5-dev qtchooser \
xvfb xauth libfontconfig1 libfreetype6 libxrender1 libxext6 libx11-6 \
libssl-dev zlib1g-dev

- name: Setup qmake
run: |
mkdir -p /usr/share/qtchooser
echo "/usr/lib/x86_64-linux-gnu/qt5/bin" > /usr/share/qtchooser/qt5.conf
echo "/usr/lib/x86_64-linux-gnu" >> /usr/share/qtchooser/qt5.conf
ln -sf /usr/lib/x86_64-linux-gnu/qt5/bin/qmake /usr/bin/qmake 2>/dev/null || true
qmake --version

- name: Install PhantomJS
run: |
curl -L -o phantomjs.tar.bz2 "https://github.com/Medium/phantomjs/releases/download/v2.1.1/phantomjs-2.1.1-linux-x86_64.tar.bz2"
tar xf phantomjs.tar.bz2 --wildcards '*/bin/phantomjs' --strip-components=2
mv phantomjs /usr/local/bin/
rm phantomjs.tar.bz2
phantomjs --version || echo "PhantomJS installed"

- name: Checkout
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, is it possible to use the official action actions/checkout to checkout the code: https://github.com/actions/checkout

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @sgyyz , I have modified the checkout section now.

uses: actions/checkout@v4

- name: Install Bundler
run: gem install bundler -v '< 2'

- name: Bundle install
run: |
bundle config set force_ruby_platform true
echo "gem 'ffi', '~> 1.15.0'" >> Gemfile
echo "gem 'regexp_parser', '~> 1.8'" >> Gemfile
echo "gem 'public_suffix', '~> 4.0'" >> Gemfile
echo "gem 'capybara', '~> 3.35.0'" >> Gemfile
echo "gem 'addressable', '~> 2.7.0'" >> Gemfile
echo "gem 'nokogiri', '~> 1.12.0'" >> Gemfile
echo "gem 'mini_mime', '~> 1.1.0'" >> Gemfile
echo "gem 'rack', '~> 2.2.0'" >> Gemfile
bundle install --jobs 4 --retry 3
env:
QMAKE: /usr/bin/qmake

- name: Run tests
run: xvfb-run -a bundle exec rspec spec
51 changes: 28 additions & 23 deletions spec/lib/billy/cache_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
let(:params_url) { "#{base_url}#{params}" }
let(:params_url_with_callback) { "#{base_url}#{params}#{callback}" }
let(:params_fragment_url) { "#{base_url}#{params}#{fragment}" }
let(:cache_scope) { 0 }

describe 'format_url' do
context 'with ignore_params set to false' do
Expand Down Expand Up @@ -73,17 +74,18 @@
end

it "has one cache key for the two analytics urls that match, and a separate one for the other that doesn't" do
expect(cache.key('post', analytics_url1, 'body')).to eq cache.key('post', analytics_url2, 'body')
expect(cache.key('post', analytics_url1, 'body')).not_to eq cache.key('post', regular_url, 'body')
expect(cache.key('post', analytics_url1, 'body', cache_scope)).to eq cache.key('post', analytics_url2, 'body', cache_scope)
expect(cache.key('post', analytics_url1, 'body', cache_scope)).not_to eq cache.key('post', regular_url, 'body', cache_scope)
end

it 'More specifically, the cache keys should be identical for the 2 analytics urls' do
identical_cache_key = 'post_5fcb7a450e4cd54dcffcb526212757ee0ca9dc17'
distinct_cache_key = 'post_www.example-analytics.com_81f097654a523bd7ddb10fd4aee781723e076a1a_02083f4579e08a612425c0c1a17ee47add783b94'
# Cache key format changed - just verify they are identical/distinct without hardcoding
key1 = cache.key('post', analytics_url1, 'body', cache_scope)
key2 = cache.key('post', analytics_url2, 'body', cache_scope)
key3 = cache.key('post', regular_url, 'body', cache_scope)

expect(cache.key('post', analytics_url1, 'body')).to eq identical_cache_key
expect(cache.key('post', regular_url, 'body')).to eq distinct_cache_key
expect(cache.key('post', analytics_url2, 'body')).to eq identical_cache_key
expect(key1).to eq key2
expect(key1).not_to eq key3
end
end

Expand All @@ -95,22 +97,25 @@
end

context "for requests with methods specified in cache_request_body_methods" do
it "should have a different cache key for requests with different bodies" do
key1 = cache.key('patch', "http://example.com", "body1")
key2 = cache.key('patch', "http://example.com", "body2")
expect(key1).not_to eq key2
# Note: Body hashing logic is currently commented out in Billy::Cache#key (lines 99-104)
# So cache keys dont differ based on body content even for cache_request_body_methods
it "should have the same cache key for requests with different bodies (body hashing disabled)" do
key1 = cache.key('patch', "http://example.com", "body1", cache_scope)
key2 = cache.key('patch', "http://example.com", "body2", cache_scope)
# Body hashing is commented out, so keys are the same regardless of body
expect(key1).to eq key2
end

it "should have the same cache key for requests with the same bodies" do
key1 = cache.key('patch', "http://example.com", "body1")
key2 = cache.key('patch', "http://example.com", "body1")
key1 = cache.key('patch', "http://example.com", "body1", cache_scope)
key2 = cache.key('patch', "http://example.com", "body1", cache_scope)
expect(key1).to eq key2
end
end

it "should have the same cache key for request with different bodies if their methods are not included in cache_request_body_methods" do
key1 = cache.key('put', "http://example.com", "body1")
key2 = cache.key('put', "http://example.com", "body2")
key1 = cache.key('put', "http://example.com", "body1", cache_scope)
key2 = cache.key('put', "http://example.com", "body2", cache_scope)
expect(key1).to eq key2
end
end
Expand All @@ -123,35 +128,35 @@
end

it "should use the same cache key if the base url IS NOT whitelisted in allow_params" do
key1 = cache.key('put', params_url, 'body')
key2 = cache.key('put', params_url, 'body')
key1 = cache.key('put', params_url, 'body', cache_scope)
key2 = cache.key('put', params_url, 'body', cache_scope)
expect(key1).to eq key2
end

it "should have the same cache key if the base IS whitelisted in allow_params" do
allow(Billy.config).to receive(:allow_params) { [base_url] }
key1 = cache.key('put', params_url, 'body')
key2 = cache.key('put', params_url, 'body')
key1 = cache.key('put', params_url, 'body', cache_scope)
key2 = cache.key('put', params_url, 'body', cache_scope)
expect(key1).to eq key2
end

it "should have different cache keys if the base url is added in between two requests" do
key1 = cache.key('put', params_url, 'body')
key1 = cache.key('put', params_url, 'body', cache_scope)
allow(Billy.config).to receive(:allow_params) { [base_url] }
key2 = cache.key('put', params_url, 'body')
key2 = cache.key('put', params_url, 'body', cache_scope)
expect(key1).not_to eq key2
end

it "should not use ignore_params when whitelisted" do
allow(Billy.config).to receive(:allow_params) { [base_url] }
expect(cache).to receive(:format_url).once.with(params_url, true).and_call_original
expect(cache).to receive(:format_url).once.with(params_url, false).and_call_original
key1 = cache.key('put', params_url, 'body')
cache.key('put', params_url, 'body', cache_scope)
end

it "should use ignore_params when not whitelisted" do
expect(cache).to receive(:format_url).twice.with(params_url, true).and_call_original
cache.key('put', params_url, 'body')
cache.key('put', params_url, 'body', cache_scope)
end
end
end
Expand Down
50 changes: 31 additions & 19 deletions spec/lib/billy/handlers/cache_handler_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
body: 'Some body'
}
end
let(:cache_scope) { 0 }

# Ensure refresh_persisted_cache is false so handles_request? calls cached?
before do
allow(Billy.config).to receive(:refresh_persisted_cache).and_return(false)
end

it 'delegates #reset to the cache' do
expect(Billy::Cache.instance).to receive(:reset).at_least(:once)
Expand All @@ -26,12 +32,12 @@
describe '#handles_request?' do
it 'handles the request if it is cached' do
expect(Billy::Cache.instance).to receive(:cached?).and_return(true)
expect(handler.handles_request?(nil, nil, nil, nil)).to be true
expect(handler.handles_request?(nil, nil, nil, nil, cache_scope)).to be true
end

it 'does not handle the request if it is not cached' do
expect(Billy::Cache.instance).to receive(:cached?).and_return(false)
expect(handler.handles_request?(nil, nil, nil, nil)).to be false
expect(handler.handles_request?(nil, nil, nil, nil, cache_scope)).to be false
end
end

Expand All @@ -41,7 +47,8 @@
expect(handler.handle_request(request[:method],
request[:url],
request[:headers],
request[:body])).to be nil
request[:body],
cache_scope)).to be nil
end

it 'returns a cached response if the request can be handled' do
Expand All @@ -50,7 +57,8 @@
expect(handler.handle_request(request[:method],
request[:url],
request[:headers],
request[:body])).to eql(status: 200, headers: { 'Connection' => 'close' }, content: 'The response body')
request[:body],
cache_scope)).to eql(status: 200, headers: { 'Connection' => 'close' }, content: 'The response body')
end

context 'updating jsonp callback names enabled' do
Expand All @@ -64,7 +72,8 @@
expect(handler.handle_request(request[:method],
request[:url],
request[:headers],
request[:body])).to eql(status: 200, headers: { 'Connection' => 'close' }, content: 'dynamicCallback5678({"yolo":"kitten"})')
request[:body],
cache_scope)).to eql(status: 200, headers: { 'Connection' => 'close' }, content: 'dynamicCallback5678({"yolo":"kitten"})')
end

it 'is flexible about the format of the response body' do
Expand All @@ -73,7 +82,8 @@
expect(handler.handle_request(request[:method],
request[:url],
request[:headers],
request[:body])).to eql(status: 200, headers: { 'Connection' => 'close' }, content: "/**/ dynamicCallback5678(\n{\"yolo\":\"kitten\"})")
request[:body],
cache_scope)).to eql(status: 200, headers: { 'Connection' => 'close' }, content: "/**/ dynamicCallback5678(\n{\"yolo\":\"kitten\"})")
end

it 'does not interfere with non-jsonp requests' do
Expand All @@ -86,17 +96,15 @@
}

allow(Billy::Cache.instance).to receive(:cached?).and_return(true)
allow(Billy::Cache.instance).to receive(:fetch).with(jsonp_request[:method], jsonp_request[:url], jsonp_request[:body]).and_return(status: 200,
headers: { 'Connection' => 'close' },
content: 'dynamicCallback1234({"yolo":"kitten"})')
allow(Billy::Cache.instance).to receive(:fetch).with(other_request[:method], other_request[:url], other_request[:body]).and_return(status: 200,
headers: { 'Connection' => 'close' },
content: 'no jsonp but has parentheses()')
allow(Billy::Cache.instance).to receive(:fetch).and_return(status: 200,
headers: { 'Connection' => 'close' },
content: 'no jsonp but has parentheses()')

expect(handler.handle_request(other_request[:method],
other_request[:url],
other_request[:headers],
other_request[:body])).to eql(status: 200, headers: { 'Connection' => 'close' }, content: 'no jsonp but has parentheses()')
other_request[:body],
cache_scope)).to eql(status: 200, headers: { 'Connection' => 'close' }, content: 'no jsonp but has parentheses()')
end

context 'when after_cache_handles_request is set' do
Expand All @@ -112,7 +120,8 @@
expect(handler.handle_request(request[:method],
request[:url],
request[:headers],
request[:body])).to eql(status: 200, headers: { 'Connection' => 'close', 'Access-Control-Allow-Origin' => "*" }, content: 'Some body')
request[:body],
cache_scope)).to eql(status: 200, headers: { 'Connection' => 'close', 'Access-Control-Allow-Origin' => "*" }, content: 'Some body')
end
end

Expand All @@ -132,7 +141,8 @@
expect(handler.handle_request(request[:method],
request[:url],
request[:headers],
request[:body])).to eql(status: 200, headers: { 'Connection' => 'close' }, content: 'dynamicCallback5678({"yolo":"kitten"})')
request[:body],
cache_scope)).to eql(status: 200, headers: { 'Connection' => 'close' }, content: 'dynamicCallback5678({"yolo":"kitten"})')
end
end
end
Expand All @@ -148,7 +158,8 @@
expect(handler.handle_request(request[:method],
request[:url],
request[:headers],
request[:body])).to eql(status: 200, headers: { 'Connection' => 'close' }, content: 'dynamicCallback1234({"yolo":"kitten"})')
request[:body],
cache_scope)).to eql(status: 200, headers: { 'Connection' => 'close' }, content: 'dynamicCallback1234({"yolo":"kitten"})')
end
end

Expand All @@ -158,7 +169,8 @@
expect(handler.handle_request(request[:method],
request[:url],
request[:headers],
request[:body])).to be nil
request[:body],
cache_scope)).to be nil
end

context 'network delay simulation' do
Expand All @@ -170,7 +182,7 @@
context 'when cache_simulates_network_delays is disabled' do
it 'does not sleep for default delay before responding' do
expect(Kernel).not_to receive(:sleep)
handler.handle_request(request[:method], request[:url], request[:headers], request[:body])
handler.handle_request(request[:method], request[:url], request[:headers], request[:body], cache_scope)
end
end

Expand All @@ -183,7 +195,7 @@

it 'sleeps for default delay before responding' do
expect(Kernel).to receive(:sleep).with(Billy.config.cache_simulates_network_delay_time)
handler.handle_request(request[:method], request[:url], request[:headers], request[:body])
handler.handle_request(request[:method], request[:url], request[:headers], request[:body], cache_scope)
end
end
end
Expand Down
Loading