1+ require 'spec_helper'
2+ require 'open3'
3+ require 'net/http'
4+ require 'connpass_api_v2'
5+
6+ RSpec . describe 'bin/c-search' do
7+ let ( :script_path ) { File . expand_path ( '../../bin/c-search' , __dir__ ) }
8+ let ( :api_key ) { 'test_api_key_123' }
9+
10+ before do
11+ ENV [ 'CONNPASS_API_KEY' ] = api_key
12+ end
13+
14+ after do
15+ ENV . delete ( 'CONNPASS_API_KEY' )
16+ end
17+
18+ describe '使い方の表示' do
19+ context '引数なしで実行した場合' do
20+ it 'Usageメッセージを表示して終了コード1を返す' do
21+ output , error , status = Open3 . capture3 ( { "CONNPASS_API_KEY" => api_key } , "bundle" , "exec" , "ruby" , script_path )
22+
23+ expect ( status . exitstatus ) . to eq ( 1 )
24+ expect ( output ) . to include ( 'Usage: c-search [CONNPASS_URL | CONNPASS_EVENT_ID]' )
25+ expect ( output ) . to include ( '例: c-search https://coderdojoaoyama.connpass.com/' )
26+ expect ( output ) . to include ( '例: c-search https://coderdojoaoyama.connpass.com/event/356972/' )
27+ expect ( output ) . to include ( '例: c-search 356972' )
28+ end
29+ end
30+
31+ context 'CONNPASS_API_KEYが設定されていない場合' do
32+ before { ENV . delete ( 'CONNPASS_API_KEY' ) }
33+
34+ it 'エラーメッセージを表示して終了コード1を返す' do
35+ output , error , status = Open3 . capture3 ( { } , "bundle" , "exec" , "ruby" , script_path , "123456" )
36+
37+ expect ( status . exitstatus ) . to eq ( 1 )
38+ expect ( output ) . to include ( 'CONNPASS_API_KEY が設定されていません' )
39+ end
40+ end
41+ end
42+
43+ describe 'イベントIDでの検索(既存機能)' do
44+ context '数字のみを指定した場合' do
45+ it 'イベントAPIを呼び出してgroup_idを表示する' do
46+ # ConnpassApiV2 gemのモック
47+ mock_client = double ( 'ConnpassApiV2::Client' )
48+ mock_result = double ( 'result' ,
49+ results_returned : 1 ,
50+ events : [ {
51+ 'id' => 356972 ,
52+ 'title' => 'CoderDojo 青山' ,
53+ 'group' => { 'id' => 1234 , 'title' => 'CoderDojo 青山' }
54+ } ]
55+ )
56+
57+ allow ( ConnpassApiV2 ) . to receive ( :client ) . with ( api_key ) . and_return ( mock_client )
58+ allow ( mock_client ) . to receive ( :get_events ) . with ( event_id : '356972' ) . and_return ( mock_result )
59+
60+ output , error , status = Open3 . capture3 ( { "CONNPASS_API_KEY" => api_key } , "bundle" , "exec" , "ruby" , script_path , "356972" )
61+
62+ if status . exitstatus != 0
63+ puts "Error output: #{ error } "
64+ puts "Standard output: #{ output } "
65+ end
66+
67+ expect ( status . exitstatus ) . to eq ( 0 )
68+ expect ( output . strip ) . to eq ( '1234' )
69+ end
70+ end
71+
72+ context 'イベントが見つからない場合' do
73+ it 'エラーメッセージを表示して終了コード1を返す' do
74+ mock_client = double ( 'ConnpassApiV2::Client' )
75+ mock_result = double ( 'result' , results_returned : 0 , events : [ ] )
76+
77+ allow ( ConnpassApiV2 ) . to receive ( :client ) . with ( api_key ) . and_return ( mock_client )
78+ allow ( mock_client ) . to receive ( :get_events ) . with ( event_id : '999999' ) . and_return ( mock_result )
79+
80+ output , error , status = Open3 . capture3 ( { "CONNPASS_API_KEY" => api_key } , "bundle" , "exec" , "ruby" , script_path , "999999" )
81+
82+ expect ( status . exitstatus ) . to eq ( 1 )
83+ expect ( output ) . to include ( 'イベントが見つかりませんでした (event_id: 999999)' )
84+ end
85+ end
86+ end
87+
88+ describe 'イベントURLでの検索(既存機能)' do
89+ context 'HTTPSのイベントURLを指定した場合' do
90+ it 'URLからイベントIDを抽出してAPIを呼び出す' do
91+ mock_client = double ( 'ConnpassApiV2::Client' )
92+ mock_result = double ( 'result' ,
93+ results_returned : 1 ,
94+ events : [ {
95+ 'id' => 356972 ,
96+ 'group' => { 'id' => 1234 }
97+ } ]
98+ )
99+
100+ allow ( ConnpassApiV2 ) . to receive ( :client ) . with ( api_key ) . and_return ( mock_client )
101+ allow ( mock_client ) . to receive ( :get_events ) . with ( event_id : '356972' ) . and_return ( mock_result )
102+
103+ output , error , status = Open3 . capture3 ( { "CONNPASS_API_KEY" => api_key } , "bundle" , "exec" , "ruby" , script_path , "https://coderdojoaoyama.connpass.com/event/356972/" )
104+
105+ expect ( status . exitstatus ) . to eq ( 0 )
106+ expect ( output . strip ) . to eq ( '1234' )
107+ end
108+ end
109+ end
110+
111+ describe 'グループURLでの検索(新機能)' do
112+ context 'HTTPSのグループURLを指定した場合' do
113+ it 'URLからサブドメインを抽出してグループAPIを呼び出す' do
114+ # Net::HTTPのモック
115+ mock_response = double ( 'response' ,
116+ code : '200' ,
117+ body : {
118+ total_items : 1 ,
119+ groups : [ {
120+ id : 1234 ,
121+ title : 'CoderDojo 青山' ,
122+ subdomain : 'coderdojoaoyama'
123+ } ]
124+ } . to_json
125+ )
126+
127+ allow ( Net ::HTTP ) . to receive ( :start ) . and_yield ( double ( 'http' ) . tap do |http |
128+ allow ( http ) . to receive ( :request ) . and_return ( mock_response )
129+ end )
130+
131+ output , error , status = Open3 . capture3 ( { "CONNPASS_API_KEY" => api_key } , "bundle" , "exec" , "ruby" , script_path , "https://coderdojoaoyama.connpass.com/" )
132+
133+ expect ( status . exitstatus ) . to eq ( 0 )
134+ expect ( output . strip ) . to eq ( '1234' )
135+ end
136+ end
137+
138+ context 'グループが見つからない場合' do
139+ it 'エラーメッセージを表示して終了コード1を返す' do
140+ mock_response = double ( 'response' ,
141+ code : '200' ,
142+ body : { total_items : 0 , groups : [ ] } . to_json
143+ )
144+
145+ allow ( Net ::HTTP ) . to receive ( :start ) . and_yield ( double ( 'http' ) . tap do |http |
146+ allow ( http ) . to receive ( :request ) . and_return ( mock_response )
147+ end )
148+
149+ output , error , status = Open3 . capture3 ( { "CONNPASS_API_KEY" => api_key } , "bundle" , "exec" , "ruby" , script_path , "https://nonexistent.connpass.com/" )
150+
151+ expect ( status . exitstatus ) . to eq ( 1 )
152+ expect ( output ) . to include ( 'グループが見つかりませんでした (subdomain: nonexistent)' )
153+ end
154+ end
155+
156+ context 'APIが404を返す場合' do
157+ it 'エラーメッセージを表示して終了コード1を返す' do
158+ mock_response = Net ::HTTPNotFound . new ( '1.1' , '404' , 'Not Found' )
159+ allow ( mock_response ) . to receive ( :body ) . and_return ( '' )
160+
161+ allow ( Net ::HTTP ) . to receive ( :start ) . and_yield ( double ( 'http' ) . tap do |http |
162+ allow ( http ) . to receive ( :request ) . and_return ( mock_response )
163+ end )
164+
165+ output , error , status = Open3 . capture3 ( { "CONNPASS_API_KEY" => api_key } , "bundle" , "exec" , "ruby" , script_path , "https://notfound.connpass.com/" )
166+
167+ expect ( status . exitstatus ) . to eq ( 1 )
168+ expect ( output ) . to include ( 'グループが見つかりませんでした (subdomain: notfound)' )
169+ end
170+ end
171+
172+ context 'APIエラーが発生した場合' do
173+ it 'エラーメッセージを表示して終了コード1を返す' do
174+ mock_response = Net ::HTTPInternalServerError . new ( '1.1' , '500' , 'Internal Server Error' )
175+ allow ( mock_response ) . to receive ( :body ) . and_return ( 'Internal Server Error' )
176+
177+ allow ( Net ::HTTP ) . to receive ( :start ) . and_yield ( double ( 'http' ) . tap do |http |
178+ allow ( http ) . to receive ( :request ) . and_return ( mock_response )
179+ end )
180+
181+ output , error , status = Open3 . capture3 ( { "CONNPASS_API_KEY" => api_key } , "bundle" , "exec" , "ruby" , script_path , "https://error.connpass.com/" )
182+
183+ expect ( status . exitstatus ) . to eq ( 1 )
184+ expect ( output ) . to include ( 'APIエラー: 500' )
185+ end
186+ end
187+
188+ context 'タイムアウトが発生した場合' do
189+ it 'タイムアウトメッセージを表示して終了コード1を返す' do
190+ allow ( Net ::HTTP ) . to receive ( :start ) . and_raise ( Timeout ::Error . new ( 'execution expired' ) )
191+
192+ output , error , status = Open3 . capture3 ( { "CONNPASS_API_KEY" => api_key } , "bundle" , "exec" , "ruby" , script_path , "https://timeout.connpass.com/" )
193+
194+ expect ( status . exitstatus ) . to eq ( 1 )
195+ expect ( output ) . to include ( 'APIへの接続がタイムアウトしました' )
196+ end
197+ end
198+ end
199+
200+ describe 'セキュリティバリデーション' do
201+ context 'HTTPのURLを指定した場合' do
202+ it 'HTTPSを要求するエラーメッセージを表示する' do
203+ output , error , status = Open3 . capture3 ( { "CONNPASS_API_KEY" => api_key } , "bundle" , "exec" , "ruby" , script_path , "http://coderdojoaoyama.connpass.com/" )
204+
205+ expect ( status . exitstatus ) . to eq ( 1 )
206+ expect ( output ) . to include ( 'HTTPSのURLを指定してください' )
207+ end
208+ end
209+
210+ context 'Connpass以外のドメインを指定した場合' do
211+ it 'Connpassドメインを要求するエラーメッセージを表示する' do
212+ output , error , status = Open3 . capture3 ( { "CONNPASS_API_KEY" => api_key } , "bundle" , "exec" , "ruby" , script_path , "https://example.com/" )
213+
214+ expect ( status . exitstatus ) . to eq ( 1 )
215+ expect ( output ) . to include ( 'Connpass のURLを指定してください' )
216+ end
217+ end
218+
219+ context '無効なURLを指定した場合' do
220+ it '無効なURLエラーメッセージを表示する' do
221+ output , error , status = Open3 . capture3 ( { "CONNPASS_API_KEY" => api_key } , "bundle" , "exec" , "ruby" , script_path , "https://[invalid" )
222+
223+ expect ( status . exitstatus ) . to eq ( 1 )
224+ expect ( output ) . to include ( '無効なURLです' )
225+ end
226+ end
227+
228+ context '認識できないURLパターンの場合' do
229+ it '認識できないパターンエラーを表示する' do
230+ output , error , status = Open3 . capture3 ( { "CONNPASS_API_KEY" => api_key } , "bundle" , "exec" , "ruby" , script_path , "https://coderdojoaoyama.connpass.com/about/" )
231+
232+ expect ( status . exitstatus ) . to eq ( 1 )
233+ expect ( output ) . to include ( '認識できないURLパターンです' )
234+ end
235+ end
236+ end
237+ end
0 commit comments