Add plain text and coloured logging to the Client

Logs websocket events (connect, message, error and close events)
This commit is contained in:
Wilson Silva 2024-04-13 13:55:36 +01:00
parent a0cf41bfb4
commit 90ab1a6149
No known key found for this signature in database
GPG Key ID: 65135F94E23F82C8
19 changed files with 742 additions and 10 deletions

View File

@ -8,6 +8,8 @@ AllCops:
TargetRubyVersion: 3.3 TargetRubyVersion: 3.3
DisplayCopNames: true DisplayCopNames: true
NewCops: enable NewCops: enable
Exclude:
- docs/**/*
# ----------------------- Gemspec ----------------------- # ----------------------- Gemspec -----------------------

View File

@ -0,0 +1,122 @@
# 3. Logging methods vs logger class
Date: 2024-03-19
## Status
Accepted
## Context
I'm deciding between integrating logging directly into the main class or creating a dedicated logger class.
### Option 1: Logging methods
The first approach weaves logging actions into the operational code, resulting in a tight coupling of functionality and
logging. Classes should be open for extension but closed for modification, and this strategy violates that principle.
```ruby
class Client
def connect(relay)
execute_within_an_em_thread do
client = build_websocket_client(relay.url)
parent_to_child_channel.subscribe do |msg|
client.send(msg)
emit(:send, msg)
log_send(msg) # <------ new code
end
client.on :open do
child_to_parent_channel.push(type: :open, relay:)
log_connection_opened(relay) # <------ new code
end
client.on :message do |event|
child_to_parent_channel.push(type: :message, data: event.data)
log_message_received(event.data) # <------ new code
end
client.on :error do |event|
child_to_parent_channel.push(type: :error, message: event.message)
log_error(event.message) # <------ new code
end
client.on :close do |event|
child_to_parent_channel.push(type: :close, code: event.code, reason: event.reason)
log_connection_closed(event.code, event.reason) # <------ new code
end
end
# ------ new code below ------
def log_send(msg)
logger.info("Message sent: #{msg}")
end
def log_connection_opened(relay)
logger.info("Connection opened to #{relay.url}")
end
def log_message_received(data)
logger.info("Message received: #{data}")
end
def log_error(message)
logger.error("Error: #{message}")
end
def log_connection_closed(code, reason)
logger.info("Connection closed with code: #{code}, reason: #{reason}")
end
end
end
```
### Option 2: Logger class
The second strategy separates logging into its own class, promoting cleaner code and adherence to the Single
Responsibility Principle. Client already exposes events that can be tapped into, so the logger class can listen to these
events and log accordingly.
```ruby
class ClientLogger
def attach_to(client)
logger_instance = self
client.on(:connect) { |relay| logger_instance.on_connect(relay) }
client.on(:message) { |message| logger_instance.on_message(message) }
client.on(:send) { |message| logger_instance.on_send(message) }
client.on(:error) { |message| logger_instance.on_error(message) }
client.on(:close) { |code, reason| logger_instance.on_close(code, reason) }
end
def on_connect(relay); end
def on_message(message); end
def on_send(message); end
def on_error(message); end
def on_close(code, reason); end
end
client = Nostr::Client.new
logger = Nostr::ClientLogger.new
logger.attach_to(client)
```
This approach decouples logging from the main class, making it easier to maintain and extend the logging system without
affecting the core logic.
## Decision
I've chosen the dedicated logger class route. This choice is driven by a desire for extensibility in the logging system.
With a separate logger, I can easily modify logging behavior—like changing formats, adjusting verbosity levels,
switching colors, or altering output destinations (files, networks, etc.) — without needing to rewrite any of the main
operational code.
## Consequences
Adopting a dedicated logger class offers greater flexibility and simplifies maintenance, making it straightforward to
adjust how and what I log independently of the core logic. This separation of concerns means that any future changes
to logging preferences or requirements can be implemented quickly and without risk to the main class's functionality.
However, it's important to manage the integration carefully to avoid introducing complexity, such as handling
dependencies and ensuring seamless communication between the main operations and the logging system.

View File

@ -0,0 +1,66 @@
# 3. Default Logging Activation
Date: 2024-03-19
## Status
Accepted
## Context
Logging provides visibility into the gem's behavior and helps to diagnose issues. The decision centered on whether
to enable logging by default or require manual activation.
### Option 1: On-demand Logging
```ruby
client = Nostr::Client.new
logger = Nostr::ClientLogger.new
logger.attach_to(client)
```
#### Advantages:
- Offers users flexibility and control over logging.
- Conserves resources by logging only when needed.
#### Disadvantages:
- Potential to miss critical logs if not enabled.
- Requires users to read and understand documentation to enable logging.
- Requires users to manually activate and configure logging.
### Option 2: Automatic Logging
```ruby
class Client
def initialize(logger: ClientLogger.new)
@logger = logger
logger&.attach_to(self)
end
end
client = Nostr::Client.new
```
#### Advantages:
- Ensures comprehensive logging without user intervention.
- Simplifies debugging and monitoring.
- Balances logging detail with performance impact.
#### Disadvantages:
- Needs additional steps to disable logging.
## Decision
Logging will be enabled by default. Users can disable logging by passing a `nil` logger to the client:
```ruby
client = Nostr::Client.new(logger: nil)
```
## Consequences
Enabling logging by default favors ease of use and simplifies the developer's experience.

32
adr/0005-logger-types.md Normal file
View File

@ -0,0 +1,32 @@
# 3. Logger Types
Date: 2024-04-01
## Status
Accepted
## Context
In developing the `Nostr::Client` logging mechanism, I identified a need to cater to multiple development environments
and developer preferences. The consideration was whether to implement a singular logger type or to introduce
multiple, specialized loggers. The alternatives were:
- A single `ClientLogger` providing a one-size-fits-all solution.
- Multiple logger types:
- `Nostr::Client::Logger`: The base class for logging, providing core functionalities.
- `Nostr::Client::ColorLogger`: An extension of the base logger, introducing color-coded outputs for enhanced readability in environments that support ANSI colors.
- `Nostr::Client::PlainLogger`: A variation that produces logs without color coding, suitable for environments lacking color support or for users preferring plain text.
## Decision
I decided to implement the latter option: three specific kinds of loggers (`Nostr::Client::Logger`,
`Nostr::Client::ColorLogger`, and `Nostr::Client::PlainLogger`). This approach is intended to offer flexibility and
cater to the varied preferences and requirements of our users, recognizing the diverse environments in which our
library might be used.
## Consequences
- `Developer Choice`: Developers gain the ability to select the one that best matches their needs and environmental constraints, thereby enhancing the library's usability.
- `Code Complexity`: While introducing multiple logger types increases the library's code complexity, this is offset by the significant gain in flexibility and user satisfaction.
- `Broad Compatibility`: This decision ensures that the logging mechanism is adaptable to a wide range of operational environments, enhancing the library's overall robustness and accessibility.

View File

@ -78,6 +78,7 @@ export default defineConfig(withMermaid({
text: 'Common use cases', text: 'Common use cases',
collapsed: false, collapsed: false,
items: [ items: [
{ text: 'Logging and debugging', link: '/common-use-cases/logging-and-debugging' },
{ text: 'Bech32 enc/decoding (NIP-19)', link: '/common-use-cases/bech32-encoding-and-decoding-(NIP-19)' }, { text: 'Bech32 enc/decoding (NIP-19)', link: '/common-use-cases/bech32-encoding-and-decoding-(NIP-19)' },
{ text: 'Signing/verifying messages', link: '/common-use-cases/signing-and-verifying-messages' }, { text: 'Signing/verifying messages', link: '/common-use-cases/signing-and-verifying-messages' },
{ text: 'Signing/verifying events', link: '/common-use-cases/signing-and-verifying-events' }, { text: 'Signing/verifying events', link: '/common-use-cases/signing-and-verifying-events' },

View File

@ -0,0 +1,60 @@
# Logging and debugging
The `Nostr::Client` class provides built-in logging functionality to help you debug and monitor client interactions with
relays. By default, the client uses the `ColorLogger`, which logs events in color. However, you can customize the
logging behavior or disable it entirely.
## Disabling logging
To instantiate a client without any logging, simply pass `logger: nil` when creating the client instance:
```ruby
client = Nostr::Client.new(logger: nil)
```
This will disable all logging for the client.
## Formatting the logging
The `Nostr::Client::Logger` class is the base class for logging functionality. It defines the following methods for
logging different events:
- `on_connect(relay)`: Logs when the client connects to a relay.
- `on_message(message)`: Logs a message received from the relay.
- `on_send(message)`: Logs a message sent to the relay.
- `on_error(message)`: Logs an error message.
- `on_close(code, reason)`: Logs when the connection with a relay is closed.
You can create your own logger by subclassing `Nostr::Client::Logger` and overriding these methods to customize the
logging format.
The `Nostr::Client::ColorLogger` is a built-in logger that logs events in color. It uses ANSI escape codes to add color
to the log output. Here's an example of how the ColorLogger formats the log messages:
- Connection: `"\u001b[32m\u001b[1mConnected to the relay\u001b[22m #{relay.name} (#{relay.url})\u001b[0m"`
- Message received: `"\u001b[32m\u001b[1m◄-\u001b[0m #{message}"`
- Message sent: `"\u001b[32m\u001b[1m-►\u001b[0m #{message}"`
- Error: `"\u001b[31m\u001b[1mError: \u001b[22m#{message}\u001b[0m"`
- Connection closed: `"\u001b[31m\u001b[1mConnection closed: \u001b[22m#{reason} (##{code})\u001b[0m"`
## Plain text logging
If you prefer plain text logging without colors, you can use the `Nostr::Client::PlainLogger`. This logger formats the
log messages in a simple, readable format without any ANSI escape codes.
To use the `PlainLogger`, pass it as the `logger` option when creating the client instance:
```ruby
client = Nostr::Client.new(logger: Nostr::Client::PlainLogger.new)
```
The `PlainLogger` formats the log messages as follows:
- Connection: `"Connected to the relay #{relay.name} (#{relay.url})"`
- Message received: `"◄- #{message}"`
- Message sent: `"-► #{message}"`
- Error: `"Error: #{message}"`
- Connection closed: `"Connection closed: #{reason} (##{code})"`
By using the appropriate logger or creating your own custom logger, you can effectively debug and monitor your Nostr
client's interactions with relays.

View File

@ -16,6 +16,8 @@ require_relative 'nostr/signature'
require_relative 'nostr/event' require_relative 'nostr/event'
require_relative 'nostr/events/encrypted_direct_message' require_relative 'nostr/events/encrypted_direct_message'
require_relative 'nostr/client' require_relative 'nostr/client'
require_relative 'nostr/client/logger'
require_relative 'nostr/client/color_logger'
require_relative 'nostr/user' require_relative 'nostr/user'
require_relative 'nostr/key' require_relative 'nostr/key'
require_relative 'nostr/private_key' require_relative 'nostr/private_key'

View File

@ -18,11 +18,19 @@ module Nostr
# @api public # @api public
# #
# @example Instantiating a client that logs all the events it sends and receives # @example Instantiating a client that logs all the events it sends and receives
# client = Nostr::Client.new(debug: true) # client = Nostr::Client.new
# #
def initialize # @example Instantiating a client with no logging
# client = Nostr::Client.new(logger: nil)
#
# @example Instantiating a client with your own logger
# client = Nostr::Client.new(logger: YourLogger.new)
#
def initialize(logger: ColorLogger.new)
@subscriptions = {} @subscriptions = {}
@logger = logger
logger&.attach_to(self)
initialize_channels initialize_channels
end end
@ -122,6 +130,14 @@ module Nostr
private private
# The logger that prints all the events that the client sends and receives
#
# @api private
#
# @return [ClientLogger]
#
attr_reader :logger
# The subscriptions that the client has created # The subscriptions that the client has created
# #
# @api private # @api private

View File

@ -0,0 +1,69 @@
# frozen_string_literal: true
module Nostr
class Client
# Logs connection events, messages sent and received, errors, and connection closures in color.
class ColorLogger < Logger
# Logs connection to a relay
#
# @api private
#
# @param [Nostr::Relay] relay The relay the client connected to.
#
# @return [void]
#
def on_connect(relay)
puts "\u001b[32m\u001b[1mConnected to the relay\u001b[22m #{relay.name} (#{relay.url})\u001b[0m"
end
# Logs a message received from the relay
#
# @api private
#
# @param [String] message The message received.
#
# @return [void]
#
def on_message(message)
puts "\u001b[32m\u001b[1m◄-\u001b[0m #{message}"
end
# Logs a message sent to the relay
#
# @api private
#
# @param [String] message The message sent.
#
# @return [void]
#
def on_send(message)
puts "\u001b[32m\u001b[1m-►\u001b[0m #{message}"
end
# Logs an error message
#
# @api private
#
# @param [String] message The error message.
#
# @return [void]
#
def on_error(message)
puts "\u001b[31m\u001b[1mError: \u001b[22m#{message}\u001b[0m"
end
# Logs a closure of connection with a relay
#
# @api private
#
# @param [String] code The closure code.
# @param [String] reason The reason for the closure.
#
# @return [void]
#
def on_close(code, reason)
puts "\u001b[31m\u001b[1mConnection closed: \u001b[22m#{reason} (##{code})\u001b[0m"
end
end
end
end

View File

@ -0,0 +1,85 @@
# frozen_string_literal: true
module Nostr
class Client
# Logs connection events, messages sent and received, errors, and connection closures.
class Logger
# Attaches event handlers to the specified Nostr client for logging purposes
#
# @api public
#
# @example Attaching the logger to a client
# client = Nostr::Client.new
# logger = Nostr::Client::Logger.new
# logger.attach_to(client)
#
# # Now, actions like connecting, sending messages, receiving messages,
# # errors, and closing the connection will be logged to the console.
#
# @param [Nostr::Client] client The client to attach logging functionality to.
#
# @return [void]
#
def attach_to(client)
logger_instance = self
client.on(:connect) { |relay| logger_instance.on_connect(relay) }
client.on(:message) { |message| logger_instance.on_message(message) }
client.on(:send) { |message| logger_instance.on_send(message) }
client.on(:error) { |message| logger_instance.on_error(message) }
client.on(:close) { |code, reason| logger_instance.on_close(code, reason) }
end
# Logs connection to a relay
#
# @api private
#
# @param [Nostr::Relay] relay The relay the client connected to.
#
# @return [void]
#
def on_connect(relay); end
# Logs a message received from the relay
#
# @api private
#
# @param [String] message The message received.
#
# @return [void]
#
def on_message(message); end
# Logs a message sent to the relay
#
# @api private
#
# @param [String] message The message sent.
#
# @return [void]
#
def on_send(message); end
# Logs an error message
#
# @api private
#
# @param [String] message The error message.
#
# @return [void]
#
def on_error(message); end
# Logs a closure of connection with a relay
#
# @api private
#
# @param [String] code The closure code.
# @param [String] reason The reason for the closure.
#
# @return [void]
#
def on_close(code, reason); end
end
end
end

View File

@ -0,0 +1,69 @@
# frozen_string_literal: true
module Nostr
class Client
# Logs connection events, messages sent and received, errors, and connection closures.
class PlainLogger < Logger
# Logs connection to a relay
#
# @api private
#
# @param [Nostr::Relay] relay The relay the client connected to.
#
# @return [void]
#
def on_connect(relay)
puts "Connected to the relay #{relay.name} (#{relay.url})"
end
# Logs a message received from the relay
#
# @api private
#
# @param [String] message The message received.
#
# @return [void]
#
def on_message(message)
puts "◄- #{message}"
end
# Logs a message sent to the relay
#
# @api private
#
# @param [String] message The message sent.
#
# @return [void]
#
def on_send(message)
puts "-► #{message}"
end
# Logs an error message
#
# @api private
#
# @param [String] message The error message.
#
# @return [void]
#
def on_error(message)
puts "Error: #{message}"
end
# Logs a closure of connection with a relay
#
# @api private
#
# @param [String] code The closure code.
# @param [String] reason The reason for the closure.
#
# @return [void]
#
def on_close(code, reason)
puts "Connection closed: #{reason} (##{code})"
end
end
end
end

View File

@ -10,6 +10,7 @@ module Nostr
private private
attr_reader logger: Logger
attr_reader subscriptions: Hash[String, Subscription] attr_reader subscriptions: Hash[String, Subscription]
attr_reader parent_to_child_channel: EventMachine::Channel attr_reader parent_to_child_channel: EventMachine::Channel
attr_reader child_to_parent_channel: EventMachine::Channel attr_reader child_to_parent_channel: EventMachine::Channel

View File

@ -0,0 +1,6 @@
module Nostr
class Client
class ColorLogger < Logger
end
end
end

View File

@ -0,0 +1,12 @@
module Nostr
class Client
class Logger
def attach_to: (Client client) -> void
def on_connect: (Relay relay) -> void
def on_message: (String message) -> void
def on_send: (String message) -> void
def on_error: (String message) -> void
def on_close: (String code, String reason) -> void
end
end
end

View File

@ -0,0 +1,6 @@
module Nostr
class Client
class PlainLogger < Logger
end
end
end

View File

@ -0,0 +1,62 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Nostr::Client::ColorLogger do
let(:client) { instance_spy(Nostr::Client) }
let(:relay) { Nostr::Relay.new(url: 'ws://0.0.0.0:4180/', name: 'localhost') }
let(:logger) { described_class.new }
describe '#attach_to' do
it 'attaches event handlers to the client' do
logger.attach_to(client)
aggregate_failures do
expect(client).to have_received(:on).with(:connect)
expect(client).to have_received(:on).with(:message)
expect(client).to have_received(:on).with(:send)
expect(client).to have_received(:on).with(:error)
expect(client).to have_received(:on).with(:close)
end
end
end
describe '#on_connect' do
it 'logs connection to a relay' do
expect do
logger.on_connect(relay)
end.to output("\e[32m\e[1mConnected to the relay\e[22m localhost (ws://0.0.0.0:4180/)\e[0m\n").to_stdout
end
end
describe '#on_message' do
it 'logs a message received from the relay' do
message = 'Received message'
expect { logger.on_message(message) }.to output("\e[32m\e[1m◄-\e[0m #{message}\n").to_stdout
end
end
describe '#on_send' do
it 'logs a message sent to the relay' do
message = 'Sent message'
expect { logger.on_send(message) }.to output("\e[32m\e[1m-►\e[0m #{message}\n").to_stdout
end
end
describe '#on_error' do
it 'logs an error message' do
message = 'Error message'
expect { logger.on_error(message) }.to output("\e[31m\e[1mError: \e[22m#{message}\e[0m\n").to_stdout
end
end
describe '#on_close' do
it 'logs a closure of connection with a relay' do
code = '1000'
reason = 'Connection closed'
expect do
logger.on_close(code, reason)
end.to output("\e[31m\e[1mConnection closed: \e[22m#{reason} (##{code})\e[0m\n").to_stdout
end
end
end

View File

@ -0,0 +1,58 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Nostr::Client::Logger do
let(:client) { instance_spy(Nostr::Client) }
let(:relay) { Nostr::Relay.new(url: 'ws://0.0.0.0:4180/', name: 'localhost') }
let(:logger) { described_class.new }
describe '#attach_to' do
it 'attaches event handlers to the client' do
logger.attach_to(client)
aggregate_failures do
expect(client).to have_received(:on).with(:connect)
expect(client).to have_received(:on).with(:message)
expect(client).to have_received(:on).with(:send)
expect(client).to have_received(:on).with(:error)
expect(client).to have_received(:on).with(:close)
end
end
end
describe '#on_connect' do
it 'returns nil' do
expect(logger.on_connect(relay)).to be_nil
end
end
describe '#on_message' do
it 'returns nil' do
message = 'Received message'
expect(logger.on_message(message)).to be_nil
end
end
describe '#on_send' do
it 'returns nil' do
message = 'Sent message'
expect(logger.on_send(message)).to be_nil
end
end
describe '#on_error' do
it 'returns nil' do
message = 'Error message'
expect(logger.on_error(message)).to be_nil
end
end
describe '#on_close' do
it 'returns nil' do
code = 1000
reason = 'Normal closure'
expect(logger.on_close(code, reason)).to be_nil
end
end
end

View File

@ -0,0 +1,63 @@
# frozen_string_literal: true
require 'spec_helper'
require 'nostr/client/plain_logger'
RSpec.describe Nostr::Client::PlainLogger do
let(:client) { instance_spy(Nostr::Client) }
let(:relay) { Nostr::Relay.new(url: 'ws://0.0.0.0:4180/', name: 'localhost') }
let(:logger) { described_class.new }
describe '#attach_to' do
it 'attaches event handlers to the client' do
logger.attach_to(client)
aggregate_failures do
expect(client).to have_received(:on).with(:connect)
expect(client).to have_received(:on).with(:message)
expect(client).to have_received(:on).with(:send)
expect(client).to have_received(:on).with(:error)
expect(client).to have_received(:on).with(:close)
end
end
end
describe '#on_connect' do
it 'logs connection to a relay' do
expect do
logger.on_connect(relay)
end.to output("Connected to the relay localhost (ws://0.0.0.0:4180/)\n").to_stdout
end
end
describe '#on_message' do
it 'logs a message received from the relay' do
message = 'Received message'
expect { logger.on_message(message) }.to output("◄- #{message}\n").to_stdout
end
end
describe '#on_send' do
it 'logs a message sent to the relay' do
message = 'Sent message'
expect { logger.on_send(message) }.to output("-► #{message}\n").to_stdout
end
end
describe '#on_error' do
it 'logs an error message' do
message = 'Error message'
expect { logger.on_error(message) }.to output("Error: #{message}\n").to_stdout
end
end
describe '#on_close' do
it 'logs a closure of connection with a relay' do
code = '1000'
reason = 'Connection closed'
expect do
logger.on_close(code, reason)
end.to output("Connection closed: #{reason} (##{code})\n").to_stdout
end
end
end

View File

@ -17,7 +17,7 @@ RSpec.describe Nostr::Client do
@echo_server.stop @echo_server.stop
end end
let(:client) { described_class.new } let(:client) { described_class.new(logger: nil) }
let(:relay) { Nostr::Relay.new(url: plain_text_url, name: 'localhost') } let(:relay) { Nostr::Relay.new(url: plain_text_url, name: 'localhost') }
let(:port) { 4180 } let(:port) { 4180 }
@ -27,8 +27,8 @@ RSpec.describe Nostr::Client do
after { stop } after { stop }
describe '.new' do describe '.new' do
client = described_class.new
it 'creates an instance of a client' do it 'creates an instance of a client' do
client = described_class.new(logger: nil)
expect(client).to be_an_instance_of(described_class) expect(client).to be_an_instance_of(described_class)
end end
@ -134,7 +134,7 @@ RSpec.describe Nostr::Client do
it 'sends a REQ message to the relay, asking for all events and returns a subscription with the same id' do it 'sends a REQ message to the relay, asking for all events and returns a subscription with the same id' do
id = '16605b59b539f6e86762f28fb57db2fd' id = '16605b59b539f6e86762f28fb57db2fd'
client = described_class.new client = described_class.new(logger: nil)
sent_message = nil sent_message = nil
subscription = nil subscription = nil
@ -164,7 +164,7 @@ RSpec.describe Nostr::Client do
allow(SecureRandom).to receive(:hex).and_return('16605b59b539f6e86762f28fb57db2fd') allow(SecureRandom).to receive(:hex).and_return('16605b59b539f6e86762f28fb57db2fd')
filter = Nostr::Filter.new(since: 1_230_981_305) filter = Nostr::Filter.new(since: 1_230_981_305)
client = described_class.new client = described_class.new(logger: nil)
sent_message = nil sent_message = nil
subscription = nil subscription = nil
@ -194,7 +194,7 @@ RSpec.describe Nostr::Client do
id = '16605b59b539f6e86762f28fb57db2fd' id = '16605b59b539f6e86762f28fb57db2fd'
filter = Nostr::Filter.new(since: 1_230_981_305) filter = Nostr::Filter.new(since: 1_230_981_305)
client = described_class.new client = described_class.new(logger: nil)
sent_message = nil sent_message = nil
subscription = nil subscription = nil
@ -223,7 +223,7 @@ RSpec.describe Nostr::Client do
it 'sends a REQ message to the relay, asking for all events and returns a subscription with a random id' do it 'sends a REQ message to the relay, asking for all events and returns a subscription with a random id' do
allow(SecureRandom).to receive(:hex).and_return('16605b59b539f6e86762f28fb57db2fd') allow(SecureRandom).to receive(:hex).and_return('16605b59b539f6e86762f28fb57db2fd')
client = described_class.new client = described_class.new(logger: nil)
sent_message = nil sent_message = nil
subscription = nil subscription = nil
@ -253,7 +253,7 @@ RSpec.describe Nostr::Client do
it 'sends a CLOSE message to the relay, asking it to stop a subscription' do it 'sends a CLOSE message to the relay, asking it to stop a subscription' do
subscription_id = '16605b59b539f6e86762f28fb57db2fd' subscription_id = '16605b59b539f6e86762f28fb57db2fd'
client = described_class.new client = described_class.new(logger: nil)
sent_message = nil sent_message = nil
@ -277,7 +277,7 @@ RSpec.describe Nostr::Client do
describe '#publish' do describe '#publish' do
it 'sends a message to the relay' do it 'sends a message to the relay' do
relay = Nostr::Relay.new(url: plain_text_url, name: 'localhost') relay = Nostr::Relay.new(url: plain_text_url, name: 'localhost')
client = described_class.new client = described_class.new(logger: nil)
event = Nostr::Event.new( event = Nostr::Event.new(
id: '2a3184512d34077601e992ba3c3215354b21a8c76f85c2c7f66093481854e811', id: '2a3184512d34077601e992ba3c3215354b21a8c76f85c2c7f66093481854e811',
pubkey: '2d7661527d573cc8e84f665fa971dd969ba51e2526df00c149ff8e40a58f9558', pubkey: '2d7661527d573cc8e84f665fa971dd969ba51e2526df00c149ff8e40a58f9558',