Skip to content

Commit a84fa5f

Browse files
CopilotGrantBirki
andcommitted
Add comprehensive unit tests for Handlers::Base, RequestValidator::Base, and Lifecycle plugins
Co-authored-by: GrantBirki <[email protected]>
1 parent d37b550 commit a84fa5f

File tree

3 files changed

+684
-0
lines changed

3 files changed

+684
-0
lines changed
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "../../../spec_helper"
4+
5+
describe Hooks::Handlers::Base do
6+
describe "#call" do
7+
let(:handler) { described_class.new }
8+
let(:payload) { { "data" => "test" } }
9+
let(:headers) { { "Content-Type" => "application/json" } }
10+
let(:config) { { "endpoint" => "/test" } }
11+
12+
it "raises NotImplementedError by default" do
13+
expect {
14+
handler.call(payload: payload, headers: headers, config: config)
15+
}.to raise_error(NotImplementedError, "Handler must implement #call method")
16+
end
17+
18+
it "can be subclassed and overridden" do
19+
test_handler_class = Class.new(described_class) do
20+
def call(payload:, headers:, config:)
21+
{
22+
received_payload: payload,
23+
received_headers: headers,
24+
received_config: config,
25+
status: "success"
26+
}
27+
end
28+
end
29+
30+
handler = test_handler_class.new
31+
result = handler.call(payload: payload, headers: headers, config: config)
32+
33+
expect(result).to eq({
34+
received_payload: payload,
35+
received_headers: headers,
36+
received_config: config,
37+
status: "success"
38+
})
39+
end
40+
41+
it "accepts different payload types" do
42+
test_handler_class = Class.new(described_class) do
43+
def call(payload:, headers:, config:)
44+
{ payload_class: payload.class.name }
45+
end
46+
end
47+
48+
handler = test_handler_class.new
49+
50+
# Test with hash
51+
result = handler.call(payload: { "test" => "data" }, headers: headers, config: config)
52+
expect(result[:payload_class]).to eq("Hash")
53+
54+
# Test with string
55+
result = handler.call(payload: "raw string", headers: headers, config: config)
56+
expect(result[:payload_class]).to eq("String")
57+
58+
# Test with nil
59+
result = handler.call(payload: nil, headers: headers, config: config)
60+
expect(result[:payload_class]).to eq("NilClass")
61+
end
62+
63+
it "accepts different header types" do
64+
test_handler_class = Class.new(described_class) do
65+
def call(payload:, headers:, config:)
66+
{ headers_received: headers }
67+
end
68+
end
69+
70+
handler = test_handler_class.new
71+
72+
# Test with hash
73+
headers_hash = { "User-Agent" => "test", "X-Custom" => "value" }
74+
result = handler.call(payload: payload, headers: headers_hash, config: config)
75+
expect(result[:headers_received]).to eq(headers_hash)
76+
77+
# Test with empty hash
78+
result = handler.call(payload: payload, headers: {}, config: config)
79+
expect(result[:headers_received]).to eq({})
80+
81+
# Test with nil
82+
result = handler.call(payload: payload, headers: nil, config: config)
83+
expect(result[:headers_received]).to be_nil
84+
end
85+
86+
it "accepts different config types" do
87+
test_handler_class = Class.new(described_class) do
88+
def call(payload:, headers:, config:)
89+
{ config_received: config }
90+
end
91+
end
92+
93+
handler = test_handler_class.new
94+
95+
# Test with complex config
96+
complex_config = {
97+
"endpoint" => "/test",
98+
"opts" => { "timeout" => 30 },
99+
"handler" => "TestHandler"
100+
}
101+
result = handler.call(payload: payload, headers: headers, config: complex_config)
102+
expect(result[:config_received]).to eq(complex_config)
103+
104+
# Test with empty config
105+
result = handler.call(payload: payload, headers: headers, config: {})
106+
expect(result[:config_received]).to eq({})
107+
end
108+
109+
it "requires all keyword arguments" do
110+
expect {
111+
handler.call(payload: payload, headers: headers)
112+
}.to raise_error(ArgumentError, /missing keyword.*config/)
113+
114+
expect {
115+
handler.call(payload: payload, config: config)
116+
}.to raise_error(ArgumentError, /missing keyword.*headers/)
117+
118+
expect {
119+
handler.call(headers: headers, config: config)
120+
}.to raise_error(ArgumentError, /missing keyword.*payload/)
121+
end
122+
end
123+
124+
describe "inheritance" do
125+
it "can be inherited" do
126+
child_class = Class.new(described_class)
127+
expect(child_class.ancestors).to include(described_class)
128+
end
129+
130+
it "maintains method signature in subclasses" do
131+
child_class = Class.new(described_class) do
132+
def call(payload:, headers:, config:)
133+
"child implementation"
134+
end
135+
end
136+
137+
handler = child_class.new
138+
result = handler.call(
139+
payload: { "test" => "data" },
140+
headers: { "Content-Type" => "application/json" },
141+
config: { "endpoint" => "/test" }
142+
)
143+
144+
expect(result).to eq("child implementation")
145+
end
146+
end
147+
148+
describe "documentation compliance" do
149+
it "has the expected public interface" do
150+
expect(described_class.instance_methods(false)).to include(:call)
151+
end
152+
153+
it "call method accepts the documented parameters" do
154+
method = described_class.instance_method(:call)
155+
expect(method.parameters).to include([:keyreq, :payload])
156+
expect(method.parameters).to include([:keyreq, :headers])
157+
expect(method.parameters).to include([:keyreq, :config])
158+
end
159+
end
160+
end

0 commit comments

Comments
 (0)