1+ # frozen_string_literal: true
2+
3+ require_relative "../../../spec_helper"
4+ require "fileutils"
5+
6+ describe Hooks ::Core ::Builder do
7+ let ( :temp_dir ) { "/tmp/hooks_builder_test" }
8+
9+ before do
10+ FileUtils . mkdir_p ( temp_dir )
11+ end
12+
13+ after do
14+ FileUtils . rm_rf ( temp_dir )
15+ end
16+
17+ describe "#initialize" do
18+ it "initializes with no parameters" do
19+ builder = described_class . new
20+
21+ expect ( builder . instance_variable_get ( :@log ) ) . to be_nil
22+ expect ( builder . instance_variable_get ( :@config_input ) ) . to be_nil
23+ end
24+
25+ it "initializes with config parameter" do
26+ config = { log_level : "debug" }
27+ builder = described_class . new ( config : config )
28+
29+ expect ( builder . instance_variable_get ( :@config_input ) ) . to eq ( config )
30+ end
31+
32+ it "initializes with custom logger" do
33+ logger = double ( "Logger" )
34+ builder = described_class . new ( log : logger )
35+
36+ expect ( builder . instance_variable_get ( :@log ) ) . to eq ( logger )
37+ end
38+
39+ it "initializes with both config and logger" do
40+ config = { environment : "test" }
41+ logger = double ( "Logger" )
42+ builder = described_class . new ( config : config , log : logger )
43+
44+ expect ( builder . instance_variable_get ( :@config_input ) ) . to eq ( config )
45+ expect ( builder . instance_variable_get ( :@log ) ) . to eq ( logger )
46+ end
47+ end
48+
49+ describe "#build" do
50+ context "with minimal configuration" do
51+ let ( :builder ) { described_class . new }
52+
53+ before do
54+ # Mock dependencies to prevent actual file system operations
55+ allow ( Hooks ::Core ::ConfigLoader ) . to receive ( :load ) . and_return ( {
56+ log_level : "info" ,
57+ environment : "test" ,
58+ endpoints_dir : "/nonexistent"
59+ } )
60+ allow ( Hooks ::Core ::ConfigValidator ) . to receive ( :validate_global_config ) . and_return ( {
61+ log_level : "info" ,
62+ environment : "test" ,
63+ endpoints_dir : "/nonexistent"
64+ } )
65+ allow ( Hooks ::Core ::ConfigLoader ) . to receive ( :load_endpoints ) . and_return ( [ ] )
66+ allow ( Hooks ::Core ::ConfigValidator ) . to receive ( :validate_endpoints ) . and_return ( [ ] )
67+ allow ( Hooks ::App ::API ) . to receive ( :create ) . and_return ( "mock_api" )
68+ end
69+
70+ it "builds and returns an API instance" do
71+ result = builder . build
72+
73+ expect ( result ) . to eq ( "mock_api" )
74+ end
75+
76+ it "calls ConfigLoader.load with the config input" do
77+ expect ( Hooks ::Core ::ConfigLoader ) . to receive ( :load ) . with ( config_path : nil )
78+
79+ builder . build
80+ end
81+
82+ it "validates the global configuration" do
83+ config = { log_level : "info" , environment : "test" , endpoints_dir : "/nonexistent" }
84+ expect ( Hooks ::Core ::ConfigValidator ) . to receive ( :validate_global_config ) . with ( config )
85+
86+ builder . build
87+ end
88+
89+ it "loads endpoints from the endpoints directory" do
90+ config = { log_level : "info" , environment : "test" , endpoints_dir : "/nonexistent" }
91+ expect ( Hooks ::Core ::ConfigLoader ) . to receive ( :load_endpoints ) . with ( "/nonexistent" )
92+
93+ builder . build
94+ end
95+
96+ it "validates the loaded endpoints" do
97+ expect ( Hooks ::Core ::ConfigValidator ) . to receive ( :validate_endpoints ) . with ( [ ] )
98+
99+ builder . build
100+ end
101+
102+ it "creates API with all required parameters" do
103+ expect ( Hooks ::App ::API ) . to receive ( :create ) do |args |
104+ expect ( args [ :config ] ) . to be_a ( Hash )
105+ expect ( args [ :endpoints ] ) . to eq ( [ ] )
106+ expect ( args [ :log ] ) . to respond_to ( :info )
107+ expect ( args [ :signal_handler ] ) . to be_a ( Hooks ::Core ::SignalHandler )
108+ "mock_api"
109+ end
110+
111+ builder . build
112+ end
113+ end
114+
115+ context "with custom configuration" do
116+ let ( :config ) { { log_level : "debug" , environment : "development" } }
117+ let ( :builder ) { described_class . new ( config : config ) }
118+
119+ before do
120+ allow ( Hooks ::Core ::ConfigLoader ) . to receive ( :load ) . and_return ( config )
121+ allow ( Hooks ::Core ::ConfigValidator ) . to receive ( :validate_global_config ) . and_return ( config )
122+ allow ( Hooks ::Core ::ConfigLoader ) . to receive ( :load_endpoints ) . and_return ( [ ] )
123+ allow ( Hooks ::Core ::ConfigValidator ) . to receive ( :validate_endpoints ) . and_return ( [ ] )
124+ allow ( Hooks ::App ::API ) . to receive ( :create ) . and_return ( "mock_api" )
125+ end
126+
127+ it "passes the custom config to ConfigLoader" do
128+ expect ( Hooks ::Core ::ConfigLoader ) . to receive ( :load ) . with ( config_path : config )
129+
130+ builder . build
131+ end
132+ end
133+
134+ context "with custom logger" do
135+ let ( :custom_logger ) { double ( "Logger" , info : nil ) }
136+ let ( :builder ) { described_class . new ( log : custom_logger ) }
137+
138+ before do
139+ allow ( Hooks ::Core ::ConfigLoader ) . to receive ( :load ) . and_return ( { log_level : "info" } )
140+ allow ( Hooks ::Core ::ConfigValidator ) . to receive ( :validate_global_config ) . and_return ( { log_level : "info" } )
141+ allow ( Hooks ::Core ::ConfigLoader ) . to receive ( :load_endpoints ) . and_return ( [ ] )
142+ allow ( Hooks ::Core ::ConfigValidator ) . to receive ( :validate_endpoints ) . and_return ( [ ] )
143+ allow ( Hooks ::App ::API ) . to receive ( :create ) . and_return ( "mock_api" )
144+ end
145+
146+ it "uses the custom logger instead of creating one" do
147+ expect ( Hooks ::Core ::LoggerFactory ) . not_to receive ( :create )
148+
149+ builder . build
150+ end
151+
152+ it "passes the custom logger to API.create" do
153+ expect ( Hooks ::App ::API ) . to receive ( :create ) do |args |
154+ expect ( args [ :log ] ) . to eq ( custom_logger )
155+ "mock_api"
156+ end
157+
158+ builder . build
159+ end
160+ end
161+
162+ context "with endpoints" do
163+ let ( :endpoints ) do
164+ [
165+ { path : "/webhook/test1" , handler : "Handler1" } ,
166+ { path : "/webhook/test2" , handler : "Handler2" }
167+ ]
168+ end
169+ let ( :builder ) { described_class . new }
170+
171+ before do
172+ allow ( Hooks ::Core ::ConfigLoader ) . to receive ( :load ) . and_return ( {
173+ endpoints_dir : "/test/endpoints"
174+ } )
175+ allow ( Hooks ::Core ::ConfigValidator ) . to receive ( :validate_global_config ) . and_return ( {
176+ endpoints_dir : "/test/endpoints"
177+ } )
178+ allow ( Hooks ::Core ::ConfigLoader ) . to receive ( :load_endpoints ) . and_return ( endpoints )
179+ allow ( Hooks ::Core ::ConfigValidator ) . to receive ( :validate_endpoints ) . and_return ( endpoints )
180+ allow ( Hooks ::App ::API ) . to receive ( :create ) . and_return ( "mock_api" )
181+ end
182+
183+ it "loads endpoints from the specified directory" do
184+ expect ( Hooks ::Core ::ConfigLoader ) . to receive ( :load_endpoints ) . with ( "/test/endpoints" )
185+
186+ builder . build
187+ end
188+
189+ it "validates the loaded endpoints" do
190+ expect ( Hooks ::Core ::ConfigValidator ) . to receive ( :validate_endpoints ) . with ( endpoints )
191+
192+ builder . build
193+ end
194+
195+ it "passes validated endpoints to API.create" do
196+ expect ( Hooks ::App ::API ) . to receive ( :create ) do |args |
197+ expect ( args [ :endpoints ] ) . to eq ( endpoints )
198+ "mock_api"
199+ end
200+
201+ builder . build
202+ end
203+ end
204+
205+ context "with logging" do
206+ let ( :builder ) { described_class . new }
207+ let ( :mock_logger ) { double ( "Logger" , info : nil ) }
208+
209+ before do
210+ allow ( Hooks ::Core ::ConfigLoader ) . to receive ( :load ) . and_return ( {
211+ log_level : "debug" ,
212+ environment : "test"
213+ } )
214+ allow ( Hooks ::Core ::ConfigValidator ) . to receive ( :validate_global_config ) . and_return ( {
215+ log_level : "debug" ,
216+ environment : "test"
217+ } )
218+ allow ( Hooks ::Core ::ConfigLoader ) . to receive ( :load_endpoints ) . and_return ( [ ] )
219+ allow ( Hooks ::Core ::ConfigValidator ) . to receive ( :validate_endpoints ) . and_return ( [ ] )
220+ allow ( Hooks ::Core ::LoggerFactory ) . to receive ( :create ) . and_return ( mock_logger )
221+ allow ( Hooks ::App ::API ) . to receive ( :create ) . and_return ( "mock_api" )
222+ end
223+
224+ it "creates a logger with the configured log level" do
225+ expect ( Hooks ::Core ::LoggerFactory ) . to receive ( :create ) . with (
226+ log_level : "debug" ,
227+ custom_logger : nil
228+ )
229+
230+ builder . build
231+ end
232+
233+ it "logs startup information" do
234+ expect ( mock_logger ) . to receive ( :info ) . with ( "starting hooks server v#{ Hooks ::VERSION } " )
235+ expect ( mock_logger ) . to receive ( :info ) . with ( "config: 0 endpoints loaded" )
236+ expect ( mock_logger ) . to receive ( :info ) . with ( "environment: test" )
237+ expect ( mock_logger ) . to receive ( :info ) . with ( "available endpoints: " )
238+
239+ builder . build
240+ end
241+
242+ it "logs endpoint information when endpoints are present" do
243+ endpoints = [
244+ { path : "/webhook/test1" , handler : "Handler1" } ,
245+ { path : "/webhook/test2" , handler : "Handler2" }
246+ ]
247+ allow ( Hooks ::Core ::ConfigLoader ) . to receive ( :load_endpoints ) . and_return ( endpoints )
248+ allow ( Hooks ::Core ::ConfigValidator ) . to receive ( :validate_endpoints ) . and_return ( endpoints )
249+
250+ expect ( mock_logger ) . to receive ( :info ) . with ( "config: 2 endpoints loaded" )
251+ expect ( mock_logger ) . to receive ( :info ) . with ( "available endpoints: /webhook/test1, /webhook/test2" )
252+
253+ builder . build
254+ end
255+ end
256+
257+ context "error handling" do
258+ let ( :builder ) { described_class . new }
259+
260+ it "raises ConfigurationError when global config validation fails" do
261+ allow ( Hooks ::Core ::ConfigLoader ) . to receive ( :load ) . and_return ( { } )
262+ allow ( Hooks ::Core ::ConfigValidator ) . to receive ( :validate_global_config )
263+ . and_raise ( Hooks ::Core ::ConfigValidator ::ValidationError , "Invalid config" )
264+
265+ expect {
266+ builder . build
267+ } . to raise_error ( Hooks ::Core ::ConfigurationError ,
268+ "Configuration validation failed: Invalid config" )
269+ end
270+
271+ it "raises ConfigurationError when endpoint validation fails" do
272+ allow ( Hooks ::Core ::ConfigLoader ) . to receive ( :load ) . and_return ( { endpoints_dir : "/test" } )
273+ allow ( Hooks ::Core ::ConfigValidator ) . to receive ( :validate_global_config ) . and_return ( { endpoints_dir : "/test" } )
274+ allow ( Hooks ::Core ::ConfigLoader ) . to receive ( :load_endpoints ) . and_return ( [ { } ] )
275+ allow ( Hooks ::Core ::ConfigValidator ) . to receive ( :validate_endpoints )
276+ . and_raise ( Hooks ::Core ::ConfigValidator ::ValidationError , "Invalid endpoint" )
277+
278+ expect {
279+ builder . build
280+ } . to raise_error ( Hooks ::Core ::ConfigurationError ,
281+ "Endpoint validation failed: Invalid endpoint" )
282+ end
283+ end
284+ end
285+
286+ describe "#load_and_validate_config" do
287+ let ( :builder ) { described_class . new }
288+
289+ it "is a private method" do
290+ expect ( described_class . private_instance_methods ) . to include ( :load_and_validate_config )
291+ end
292+ end
293+
294+ describe "#load_endpoints" do
295+ let ( :builder ) { described_class . new }
296+
297+ it "is a private method" do
298+ expect ( described_class . private_instance_methods ) . to include ( :load_endpoints )
299+ end
300+ end
301+
302+ describe "ConfigurationError" do
303+ it "is a StandardError" do
304+ expect ( Hooks ::Core ::ConfigurationError . new ) . to be_a ( StandardError )
305+ end
306+
307+ it "can be raised with a custom message" do
308+ expect {
309+ raise Hooks ::Core ::ConfigurationError , "Custom error"
310+ } . to raise_error ( Hooks ::Core ::ConfigurationError , "Custom error" )
311+ end
312+ end
313+ end
0 commit comments