Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci: Add SSE contract test support #25

Merged
merged 25 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0b464de
rewrite buffered line reader logic for better efficiency and testability
eli-darkly Oct 5, 2021
2d69656
comment fixes
eli-darkly Oct 5, 2021
751eea9
fix race conditions in closing of connection
eli-darkly Oct 5, 2021
12d748e
make HTTP test server behavior more reliable in JRuby
eli-darkly Oct 5, 2021
dc15c7e
preliminary implementation of contract test service
eli-darkly Oct 6, 2021
f88f424
readme
eli-darkly Oct 6, 2021
ec36e02
misc improvements from PR feedback
eli-darkly Oct 6, 2021
faaf228
Merge branch 'master' into eb/sc-125613/contract-tests
eli-darkly Nov 9, 2021
2574d2f
use revised test runner
eli-darkly Nov 13, 2021
d7dbe89
fix readme link
eli-darkly Nov 16, 2021
44185a5
update for change in test harness callback protocol + refactoring
eli-darkly Nov 16, 2021
63accc6
use latest v1 release of test harness
eli-darkly Dec 29, 2021
690696d
Merge branch 'master' into eb/sc-125613/contract-tests
eli-darkly Dec 29, 2021
4787d2a
return correct event ID field in tests
eli-darkly Dec 29, 2021
5d161d7
Merge branch 'eb/sc-136139/jruby-9.2-tests' into eb/sc-125613/contrac…
eli-darkly Dec 29, 2021
52fa17b
use glassfish instead of thin in JRuby
eli-darkly Dec 29, 2021
3fc813a
Merge branch 'master' into eb/sc-125613/contract-tests
eli-darkly Dec 30, 2021
c0c49d5
rm debugging
eli-darkly Dec 30, 2021
26f7235
rm duplicate
eli-darkly Dec 30, 2021
303a4ee
fix variable name
eli-darkly Dec 30, 2021
a46f388
Merge branch 'main' into eb/sc-125613/contract-tests
keelerm84 Dec 14, 2023
0522f96
Add tests to GHA
keelerm84 Dec 14, 2023
1add08b
Disable runs for jruby for now
keelerm84 Dec 14, 2023
0f96d57
Re-trigger workflow
keelerm84 Jan 29, 2024
fac0cbb
Merge branch 'main' into eb/sc-125613/contract-tests
keelerm84 Jan 31, 2024
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
7 changes: 7 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,14 @@ jobs:
- run: bundle _2.2.10_ install
- run: mkdir ./rspec
- run: bundle _2.2.10_ exec rspec --format documentation --format RspecJunitFormatter -o ./rspec/rspec.xml spec

- store_test_results:
path: ./rspec
- store_artifacts:
path: ./rspec

- run: make build-contract-tests
- run:
command: make start-contract-test-service
background: true
- run: make run-contract-tests
20 changes: 20 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

TEMP_TEST_OUTPUT=/tmp/sse-contract-test-service.log

build-contract-tests:
@cd contract-tests && bundle _2.2.10_ install

start-contract-test-service:
@cd contract-tests && bundle _2.2.10_ exec ruby service.rb

start-contract-test-service-bg:
@echo "Test service output will be captured in $(TEMP_TEST_OUTPUT)"
@make start-contract-test-service >$(TEMP_TEST_OUTPUT) 2>&1 &

run-contract-tests:
@curl -s https://raw.githubusercontent.com/launchdarkly/sse-contract-tests/v1.0.0/downloader/run.sh \
| VERSION=v1 PARAMS="-url http://localhost:8000 -debug -stop-service-at-end" sh

contract-tests: build-contract-tests start-contract-test-service-bg run-contract-tests

.PHONY: build-contract-tests start-contract-test-service run-contract-tests contract-tests
10 changes: 10 additions & 0 deletions contract-tests/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
source 'https://rubygems.org'

gem 'ld-eventsource', path: '..'

gem 'sinatra', '~> 2.1'
# Sinatra can work with several server frameworks. In JRuby, we have to use glassfish (which
# is only available in JRuby). Otherwise we use thin (which is not available in JRuby).
gem 'glassfish', :platforms => :jruby
gem 'thin', :platforms => :ruby
gem 'json'
5 changes: 5 additions & 0 deletions contract-tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# SSE client contract test service

This directory contains an implementation of the cross-platform SSE testing protocol defined by https://github.com/launchdarkly/sse-contract-tests. See that project's `README` for details of this protocol, and the kinds of SSE client capabilities that are relevant to the contract tests. This code should not need to be updated unless the SSE client has added or removed such capabilities.

To run these tests locally, run `make contract-tests` from the project root directory. This downloads the correct version of the test harness tool automatically.
77 changes: 77 additions & 0 deletions contract-tests/service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
require 'ld-eventsource'
require 'json'
require 'logger'
require 'net/http'
require 'sinatra'

require './stream_entity.rb'

$log = Logger.new(STDOUT)
$log.formatter = proc {|severity, datetime, progname, msg|
"#{datetime.strftime('%Y-%m-%d %H:%M:%S.%3N')} #{severity} #{progname} #{msg}\n"
}

set :port, 8000
set :logging, false

streams = {}
streamCounter = 0

get '/' do
{
capabilities: [
'headers',
'last-event-id',
'read-timeout'
]
}.to_json
end

delete '/' do
$log.info("Test service has told us to exit")
Thread.new { sleep 1; exit }
return 204
end

post '/' do
opts = JSON.parse(request.body.read, :symbolize_names => true)
streamUrl = opts[:streamUrl]
callbackUrl = opts[:callbackUrl]
tag = "[#{opts[:tag]}]:"

if !streamUrl || !callbackUrl
$log.error("#{tag} Received request with incomplete parameters: #{opts}")
return 400
end

streamCounter += 1
streamId = streamCounter.to_s
streamResourceUrl = "/streams/#{streamId}"

$log.info("#{tag} Starting stream from #{streamUrl}")
$log.debug("#{tag} Parameters: #{opts}")

entity = nil
sse = SSE::Client.new(
streamUrl,
headers: opts[:headers] || {},
last_event_id: opts[:lastEventId],
read_timeout: opts[:readTimeoutMs].nil? ? nil : (opts[:readTimeoutMs].to_f / 1000),
reconnect_time: opts[:initialDelayMs].nil? ? nil : (opts[:initialDelayMs].to_f / 1000)
) do |sse|
entity = StreamEntity.new(sse, tag, callbackUrl)
end

streams[streamId] = entity

return [201, {"Location" => streamResourceUrl}, nil]
end

delete '/streams/:id' do |streamId|
entity = streams[streamId]
return 404 if entity.nil?
streams.delete(streamId)
entity.close

return 204
end
58 changes: 58 additions & 0 deletions contract-tests/stream_entity.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
require 'ld-eventsource'
require 'json'
require 'net/http'

set :port, 8000
set :logging, false

class StreamEntity
def initialize(sse, tag, callbackUrl)
@sse = sse
@tag = tag
@callbackUrl = callbackUrl
@callbackCounter = 0

sse.on_event { |event| self.on_event(event) }
sse.on_error { |error| self.on_error(error) }
end

def on_event(event)
$log.info("#{@tag} Received event from stream (#{event.type})")
message = {
kind: 'event',
event: {
type: event.type,
data: event.data,
id: event.last_event_id
}
}
self.send_message(message)
end

def on_error(error)
$log.info("#{@tag} Received error from stream: #{error}")
message = {
kind: 'error',
error: error
}
self.send_message(message)
end

def send_message(message)
@callbackCounter += 1
uri = "#{@callbackUrl}/#{@callbackCounter}"
begin
resp = Net::HTTP.post(URI(uri), JSON.generate(message))
if resp.code.to_i >= 300
$log.error("#{@tag} Callback to #{url} returned status #{resp.code}")
end
rescue => e
$log.error("#{@tag} Callback to #{url} failed: #{e}")
end
end

def close
@sse.close
$log.info("#{@tag} Test ended")
end
end