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

Async handler support for Slack::RealTime::Config and Client #486

Merged
merged 27 commits into from
Aug 7, 2023

Conversation

milestruecar
Copy link
Contributor

Addresses #485

@dblock
Copy link
Collaborator

dblock commented Jun 27, 2023

LGTM, needs fixing tests, CHANGELOG, and maybe we can refactor the implementation a bit into private methods so that it's a bit more digestible?

@dblock
Copy link
Collaborator

dblock commented Jun 27, 2023

While you're at it, see if putting an Async.run in any event (on) handler works for you? I think I'd prefer that change, as this problem exists for any long running task as a response to a Slack message. It would address #237.

@milestruecar
Copy link
Contributor Author

milestruecar commented Jun 27, 2023

While you're at it, see if putting an Async.run in any event (on) handler works for you? I think I'd prefer that change, as this problem exists for any long running task as a response to a Slack message. It would address #237.

edit: nevermind that original question didn't make sense... do you mean adding Async.run to every on block in that file? or is there some lower level method i can wrap in Async.run to catch all those events?

edit again: i tried updating this

def run_handlers(type, data)
handlers = store.class.events[type.to_s]
handlers.each do |handler|
store.instance_exec(data, self, &handler)
end
rescue StandardError => e
to

handlers.each do |handler|
  Async.run { store.instance_exec(data, self, &handler) }
end

and it looks like it's working for my extremely narrow use case. Is that what you were after?

@dblock
Copy link
Collaborator

dblock commented Jun 28, 2023

to

handlers.each do |handler|
  Async.run { store.instance_exec(data, self, &handler) }
end

and it looks like it's working for my extremely narrow use case. Is that what you were after?

Right that's what I am after.

Should it wrap each handler or all handlers? Is there a change in exception handling when one handler fails with the wrapped code?

CHANGELOG.md Outdated
@@ -11,6 +11,7 @@
* [#475](https://github.com/slack-ruby-client/pulls/475): Update API from [slack-api-ref@977dad5](https://github.com/slack-ruby/slack-api-ref/commit/977dad5) - [@slack-ruby-ci-bot](https://github.com/apps/slack-ruby-ci-bot).
* [#476](https://github.com/slack-ruby-client/pulls/476): Update API from [slack-api-ref@d0b2989](https://github.com/slack-ruby/slack-api-ref/commit/d0b2989) - [@slack-ruby-ci-bot](https://github.com/apps/slack-ruby-ci-bot).
* [#478](https://github.com/slack-ruby-client/pulls/478): Update API from [slack-api-ref@d797055](https://github.com/slack-ruby/slack-api-ref/commit/d797055) - [@slack-ruby-ci-bot](https://github.com/apps/slack-ruby-ci-bot).
* [#486](https://github.com/slack-ruby/slack-ruby-client/pull/486): Async cache build for slack::realtime::stores::store - [@milestruecar](https://github.com/milestruecar).
Copy link
Collaborator

Choose a reason for hiding this comment

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

Capitalize and quote Slack::Realtime::... to match actual class name.

@dblock
Copy link
Collaborator

dblock commented Jun 28, 2023

If this "just works", then I'd like a spec that ensures that handlers are run inside an async block, and we should talk about backwards compatibility and something in the README that says that handlers execute async. Then, what could go wrong? :) Or maybe this should be optional behind a configuration option?

@milestruecar
Copy link
Contributor Author

to

handlers.each do |handler|
  Async.run { store.instance_exec(data, self, &handler) }
end

and it looks like it's working for my extremely narrow use case. Is that what you were after?

Right that's what I am after.

Should it wrap each handler or all handlers? Is there a change in exception handling when one handler fails with the wrapped code?

i guess it depends on expected behavior, this makes it... more async? if order of handler execution matters, it would be better to have the entire block wrapped in a single async task. if order of execution doesn't matter (and everyone is fine spinning up some arbitrary number of async tasks) then this implementation seemed to work as expected for me

@dblock
Copy link
Collaborator

dblock commented Jun 29, 2023

Good point on order of execution. Current behavior is that it's predictable, so at a minimum we should keep the current behavior. If we bump the major version we can change the default. How about introducing an option? We could call it async_handlers and it could take one of :none , :all and :each?

@milestruecar
Copy link
Contributor Author

milestruecar commented Jun 30, 2023

tried implementing an async_handlers option with none all and each but i can't tell if all and each are actually doing anything meaningfully different. everything seems to fire off in the same order, the only real difference I can see is the each option is throwing some uncaught eof errors that i'm having trouble chasing down while crawling through the conversations.list loop. current theory is that doing each handler async is running afoul of the retry logic, though i'm surprised i didn't see similar behavior with the code in this pr since it's the same

@milestruecar
Copy link
Contributor Author

milestruecar commented Jun 30, 2023

just having :all as the default behavior makes sense to me. i can't picture a scenario in which someone would want to opt into :none and block the client from receiving from slack while the RealTime::Stores::Store cache gets hydrated, and i'm having trouble creating a scenario in which :each does anything meaningfully different

@milestruecar
Copy link
Contributor Author

you were asking about exceptions earlier, here's an example of one of those eof errors in an async task:

    1m     warn: Async::Task [oid=0x5d5c] [ec=0x5d70] [pid=8722] [2023-06-30 12:48:29 -0700]
               | Task may have ended with unhandled exception.
               |   Faraday::SSLError: SSL_connect returned=1 errno=0 peeraddr=54.70.179.16:443 state=error: unexpected eof while reading
               |   → [local path]/.rbenv/versions/3.2.1/lib/ruby/3.2.0/net/protocol.rb:46 in `connect_nonblock'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/3.2.0/net/protocol.rb:46 in `ssl_socket_connect'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/3.2.0/net/http.rb:1342 in `connect'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/3.2.0/net/http.rb:1248 in `do_start'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/3.2.0/net/http.rb:1237 in `start'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-net_http-3.0.2/lib/faraday/adapter/net_http.rb:112 in `request_with_wrapped_block'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-net_http-3.0.2/lib/faraday/adapter/net_http.rb:102 in `perform_request'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-net_http-3.0.2/lib/faraday/adapter/net_http.rb:66 in `block in call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/adapter.rb:45 in `connection'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-net_http-3.0.2/lib/faraday/adapter/net_http.rb:65 in `call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/middleware.rb:17 in `call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/response/logger.rb:23 in `call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/middleware.rb:17 in `call'
               |     [local path]/slack-ruby-client/lib/slack/web/faraday/response/wrap_error.rb:16 in `call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/middleware.rb:17 in `call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/middleware.rb:17 in `call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/middleware.rb:17 in `call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/request/url_encoded.rb:25 in `call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-multipart-1.0.4/lib/faraday/multipart/middleware.rb:28 in `call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/rack_builder.rb:153 in `build_response'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/connection.rb:444 in `run_request'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/connection.rb:280 in `post'
               |     [local path]/slack-ruby-client/lib/slack/web/faraday/request.rb:25 in `request'
               |     [local path]/slack-ruby-client/lib/slack/web/faraday/request.rb:11 in `post'
               |     [local path]/slack-ruby-client/lib/slack/web/api/endpoints/conversations.rb:248 in `conversations_list'
               |     [local path]/slack-ruby-client/lib/slack/web/pagination/cursor.rb:25 in `block in each'
               |     [local path]/slack-ruby-client/lib/slack/web/pagination/cursor.rb:22 in `loop'
               |     [local path]/slack-ruby-client/lib/slack/web/pagination/cursor.rb:22 in `each'
               |     [local path]/slack-ruby-client/lib/slack/web/api/endpoints/conversations.rb:244 in `conversations_list'
               |     [local path]/slack-ruby-client/lib/slack/real_time/stores/store.rb:356 in `block in <class
               |     [local path]/slack-ruby-client/lib/slack/real_time/client.rb:240 in `instance_exec'
               |     [local path]/slack-ruby-client/lib/slack/real_time/client.rb:240 in `block (2 levels) in run_handlers'
               |     [local path]/slack-ruby-client/lib/slack/real_time/client.rb:239 in `each'
               |     [local path]/slack-ruby-client/lib/slack/real_time/client.rb:239 in `block in run_handlers'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/async-1.31.0/lib/async/task.rb:261 in `block in make_fiber'
               |   Caused by OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 peeraddr=54.70.179.16:443 state=error: unexpected eof while reading
               |   → [local path]/.rbenv/versions/3.2.1/lib/ruby/3.2.0/net/protocol.rb:46 in `connect_nonblock'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/3.2.0/net/protocol.rb:46 in `ssl_socket_connect'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/3.2.0/net/http.rb:1342 in `connect'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/3.2.0/net/http.rb:1248 in `do_start'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/3.2.0/net/http.rb:1237 in `start'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-net_http-3.0.2/lib/faraday/adapter/net_http.rb:112 in `request_with_wrapped_block'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-net_http-3.0.2/lib/faraday/adapter/net_http.rb:102 in `perform_request'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-net_http-3.0.2/lib/faraday/adapter/net_http.rb:66 in `block in call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/adapter.rb:45 in `connection'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-net_http-3.0.2/lib/faraday/adapter/net_http.rb:65 in `call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/middleware.rb:17 in `call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/response/logger.rb:23 in `call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/middleware.rb:17 in `call'
               |     [local path]/slack-ruby-client/lib/slack/web/faraday/response/wrap_error.rb:16 in `call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/middleware.rb:17 in `call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/middleware.rb:17 in `call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/middleware.rb:17 in `call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/request/url_encoded.rb:25 in `call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-multipart-1.0.4/lib/faraday/multipart/middleware.rb:28 in `call'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/rack_builder.rb:153 in `build_response'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/connection.rb:444 in `run_request'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/faraday-2.7.9/lib/faraday/connection.rb:280 in `post'
               |     [local path]/slack-ruby-client/lib/slack/web/faraday/request.rb:25 in `request'
               |     [local path]/slack-ruby-client/lib/slack/web/faraday/request.rb:11 in `post'
               |     [local path]/slack-ruby-client/lib/slack/web/api/endpoints/conversations.rb:248 in `conversations_list'
               |     [local path]/slack-ruby-client/lib/slack/web/pagination/cursor.rb:25 in `block in each'
               |     [local path]/slack-ruby-client/lib/slack/web/pagination/cursor.rb:22 in `loop'
               |     [local path]/slack-ruby-client/lib/slack/web/pagination/cursor.rb:22 in `each'
               |     [local path]/slack-ruby-client/lib/slack/web/api/endpoints/conversations.rb:244 in `conversations_list'
               |     [local path]/slack-ruby-client/lib/slack/real_time/stores/store.rb:356 in `block in <class
               |     [local path]/slack-ruby-client/lib/slack/real_time/client.rb:240 in `instance_exec'
               |     [local path]/slack-ruby-client/lib/slack/real_time/client.rb:240 in `block (2 levels) in run_handlers'
               |     [local path]/slack-ruby-client/lib/slack/real_time/client.rb:239 in `each'
               |     [local path]/slack-ruby-client/lib/slack/real_time/client.rb:239 in `block in run_handlers'
               |     [local path]/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/async-1.31.0/lib/async/task.rb:261 in `block in make_fiber'

@dblock
Copy link
Collaborator

dblock commented Jul 1, 2023

i can't picture a scenario in which someone would want to opt into :none and block the client from receiving from slack while the RealTime::Stores::Store cache gets hydrated, and i'm having trouble creating a scenario in which :each does anything meaningfully different

It's backwards incompatible, and would require a major version bump. I'm just trying to ask to default to the current behavior. Btw, not everybody uses stores. I disable that in my bots.

@milestruecar milestruecar changed the title Async cache build for Slack::RealTime::Stores::Store Async handler support for Slack::RealTime::Config and Client Jul 6, 2023
@milestruecar
Copy link
Contributor Author

there might be a cleaner way to implement this than a case statement. i tried a tricky metaprogramming thing first but it was pretty gross. idk what your preference is

Copy link
Collaborator

@dblock dblock left a comment

Choose a reason for hiding this comment

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

Looks good, just needs tests and README.

@@ -234,13 +234,24 @@ def dispatch(event)

def run_handlers(type, data)
handlers = store.class.events[type.to_s]
handlers.each do |handler|
store.instance_exec(data, self, &handler)
case @async_handlers
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use case async_handlers so it can be overwritten.

lib/slack/real_time/client.rb Outdated Show resolved Hide resolved
Copy link
Collaborator

@dblock dblock left a comment

Choose a reason for hiding this comment

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

Tiny nit and this is good to go!

Fix rubocop with rubocop -a ; rubocop --auto-gen-config.

README.md Outdated Show resolved Hide resolved
@milestruecar
Copy link
Contributor Author

do i need to somehow skip that test if CONCURRENCY isn't set? it seems like async isn't available with concurrency=none

@dblock
Copy link
Collaborator

dblock commented Jul 21, 2023

do i need to somehow skip that test if CONCURRENCY isn't set? it seems like async isn't available with concurrency=none

I see what's happening. This comes from the fact that we used to support different concurrency libraries. So the async handler option is something that may have different implementations depending on the async library. Rather than muting the test add a method in Slack::RealTime::Socket called run_async(&_block) that raises NotImplemented, then override it in Slack::RealTime::Concurrency::Async::Socket with Async.run ... or something similar to def start_async(client).

Then condition the test that actually runs async to the existence of the CONCURRENCY env.

@milestruecar
Copy link
Contributor Author

I'm not sure how to effectively test this since the socket is mocked

      context 'when config#async_handlers is :all', if: ENV['CONCURRENCY'] == 'async-websocket' do
          before do
            client.async_handlers = :all
            allow(socket).to receive(:run_async)
          end

          after do
            client.async_handlers = :none
          end

          it 'returns an Async::Task' do
            expect(client.send(:run_handlers, 'example', {})).to be_a Async::Task
          end
        end

this just returns nil for run_async

i know i can explicitly give a return allow(socket).to receive(:run_async).and_return(Async::Task) or something like that but then i'm not actually testing that method

spec/slack/real_time/client_spec.rb Show resolved Hide resolved
end

after do
client.store.class.events.merge!(@events)
client.async_handlers = :none
Copy link
Collaborator

Choose a reason for hiding this comment

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

Store the original value of async_handlers.

@events = client.store.class.events.dup
client.store.class.events.clear
client.async_handlers = :all
allow(socket).to receive(:run_async).and_return(Async.run { nil })
Copy link
Collaborator

Choose a reason for hiding this comment

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

This just replaces run_async with an implementation that doesn't run anything or test that the correct thing was called. In an actual test you're interested in the different behavior with :all vs. :none, so it could look something like this:

For :all

it 'runs tasks async' do
   expect(socket).to receive(:run_async)
   client.send(:run_handlers, 'example', {}))
end

For :none

it 'runs tasks async' do
   expect(socket).to_not receive(:run_async)
   client.send(:run_handlers, 'example', {}))
end

Similarly you have a good test already to ensure that run_handlers returns an Async task.

          it 'returns an Async::Task' do
            expect(client.send(:run_handlers, 'example', {})).to be_a Async::Task
          end

There should be the equivalent of that that does not return an Async::Task with :none.

Makes sense?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

that first and last points are what i was talking about in my last comment, it doesn't return an Async::Task out of the box because the socket is mocked. It just gets nil back

Copy link
Contributor Author

Choose a reason for hiding this comment

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

when i run without that explicit return i get this

Failures:

  1) Slack::RealTime::Client client with a full store #start! #run_handlers when config#async_handlers is :all returns an Async::Task
     Failure/Error: expect(client.send(:run_handlers, 'example', {})).to be_a Async::Task
       expected nil to be a kind of Async::Task
     # ./spec/slack/real_time/client_spec.rb:182:in `block (6 levels) in <top (required)>'

Copy link
Collaborator

@dblock dblock Aug 5, 2023

Choose a reason for hiding this comment

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

This was expected. In this test the socket client (the part that is actually async) is mocked rather than using the real concurrency class.

let(:ws) { double(Slack::RealTime::Concurrency::Mock::WebSocket, on: true) }

It therefore never invokes the real Slack::RealTime::Concurrency::Async::Socket and so you can't check the return of that.

The test here is good, it checks that 1) when async_handlers=:all we call run_async on the socket class, and 2) when async_handlers=:none we don't call it. If we want to check that run_async works as expected for the real Slack::RealTime::Concurrency::Async::Socket we take it out into a separate spec for that, which I added in milestruecar#1. I think we need to add tests there (in a future PR), which reminds me to add code coverage ;)

@dblock
Copy link
Collaborator

dblock commented Aug 7, 2023

Looks like some silliness in requires after my PR.


LoadError:
  cannot load such file -- async/websocket
# ./lib/slack/real_time/concurrency/async.rb:2:in `require'
# ./lib/slack/real_time/concurrency/async.rb:2:in `<top (required)>'
# ./spec/slack/real_time/concurrency/clients/async_spec.rb:4:in `require'
# ./spec/slack/real_time/concurrency/clients/async_spec.rb:4:in `<top (required)>'

LMK if you need help finishing this!

@milestruecar
Copy link
Contributor Author

Looks like some silliness in requires after my PR.


LoadError:
  cannot load such file -- async/websocket
# ./lib/slack/real_time/concurrency/async.rb:2:in `require'
# ./lib/slack/real_time/concurrency/async.rb:2:in `<top (required)>'
# ./spec/slack/real_time/concurrency/clients/async_spec.rb:4:in `require'
# ./spec/slack/real_time/concurrency/clients/async_spec.rb:4:in `<top (required)>'

LMK if you need help finishing this!

it's due to the new test file

RSpec.describe Slack::RealTime::Concurrency::Async::Socket, if: ENV['CONCURRENCY'] == 'async-websocket' do

i think it's trying to include that class regardless of ENV['CONCURRENCY'] on initialization, i'm not sure how to get around this, short of just fully excluding that file on rspec command line

@dblock
Copy link
Collaborator

dblock commented Aug 7, 2023

This is because we use Slack::RealTime::Concurrency::Async::Socket, and it auto-resolves async, requires it, then fails because the async-websocket library is not included in the Gemfile when CONCURRENCY is not set. To fix we change that to a string (so it doesn't resolve the class name), and condition it similarly to the other tests that skip with different values of CONCURRENCY for consistently mostly.

milestruecar#2

@coveralls
Copy link

coveralls commented Aug 7, 2023

Pull Request Test Coverage Report for Build 5790230078

  • 37 of 39 (94.87%) changed or added relevant lines in 6 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.02%) to 89.133%

Changes Missing Coverage Covered Lines Changed/Added Lines %
lib/slack/real_time/client.rb 6 7 85.71%
lib/slack/real_time/socket.rb 1 2 50.0%
Totals Coverage Status
Change from base Build 5771061999: 0.02%
Covered Lines: 4872
Relevant Lines: 5466

💛 - Coveralls

@milestruecar
Copy link
Contributor Author

This is because we use Slack::RealTime::Concurrency::Async::Socket, and it auto-resolves async, requires it, then fails because the async-websocket library is not included in the Gemfile when CONCURRENCY is not set. To fix we change that to a string (so it doesn't resolve the class name), and condition it similarly to the other tests that skip with different values of CONCURRENCY for consistently mostly.

milestruecar#2

i... did not know you could just drop a string in there. obviously i only rspec at about a 3rd grade level. Thanks!

@dblock dblock merged commit 413ce4a into slack-ruby:master Aug 7, 2023
9 checks passed
@dblock
Copy link
Collaborator

dblock commented Aug 7, 2023

Merged, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants