11# frozen_string_literal: true
22
33require 'json'
4+ require 'fileutils'
5+ require 'open3'
46
57##
68# Helper methods used during :test run
@@ -27,21 +29,32 @@ module Output
2729 end
2830end
2931
32+ def test_container_exists? ( container_name )
33+ inspection , status = Open3 . capture2e ( 'docker' , 'inspect' , container_name )
34+ return false unless status . success?
35+ return false if inspection . strip . empty?
36+
37+ JSON . parse ( inspection ) . any?
38+ rescue JSON ::ParserError
39+ false
40+ end
41+
3042task default : %w[ test ]
3143
3244desc 'Build and run docker image/container, and send requests to it'
3345
3446task :test do
3547 current_dir = ENV . fetch ( 'GITHUB_WORKSPACE' , __dir__ )
48+ smoke_auto_source_enabled = ENV . fetch ( 'SMOKE_AUTO_SOURCE_ENABLED' , 'false' )
3649
3750 Output . describe 'Building and running'
3851 sh 'docker build -t gilcreator/html2rss-web -f Dockerfile .'
3952 sh [ 'docker run' ,
4053 '-d' ,
41- '-p 3000:3000 ' ,
54+ '-p 4000:4000 ' ,
4255 '--env PUMA_LOG_CONFIG=1' ,
43- '--env HEALTH_CHECK_USERNAME=username ' ,
44- ' --env HEALTH_CHECK_PASSWORD=password' ,
56+ '--env HEALTH_CHECK_TOKEN=CHANGE_ME_HEALTH_CHECK_TOKEN ' ,
57+ " --env AUTO_SOURCE_ENABLED= #{ smoke_auto_source_enabled } " ,
4558 "--mount type=bind,source=#{ current_dir } /config,target=/app/config" ,
4659 '--name html2rss-web-test' ,
4760 'gilcreator/html2rss-web' ] . join ( ' ' )
@@ -51,22 +64,17 @@ task :test do
5164 Output . describe 'Listing docker containers matching html2rss-web-test filter'
5265 sh 'docker ps -a --filter name=html2rss-web-test'
5366
54- Output . describe 'Generating feed from a html2rss-configs config'
55- sh 'curl -f "http://127.0.0.1:3000/github.com/releases.rss?username=html2rss&repository=html2rss-web" || exit 1'
56-
57- Output . describe 'Generating example feed from feeds.yml'
58- sh 'curl -f http://127.0.0.1:3000/example.rss || exit 1'
59-
60- Output . describe 'Authenticated request to GET /health_check.txt'
61- sh 'docker exec html2rss-web-test curl -f http://username:password@127.0.0.1:3000/health_check.txt || exit 1'
62-
63- # skipped as html2rss is used in development version
64- # Output.describe 'Print output of `html2rss help`'
65- # sh 'docker exec html2rss-web-test html2rss help'
67+ Output . describe 'Running RSpec smoke suite against container'
68+ smoke_env = {
69+ 'SMOKE_BASE_URL' => 'http://127.0.0.1:4000' ,
70+ 'SMOKE_HEALTH_TOKEN' => 'CHANGE_ME_HEALTH_CHECK_TOKEN' ,
71+ 'SMOKE_API_TOKEN' => 'CHANGE_ME_ADMIN_TOKEN' ,
72+ 'SMOKE_AUTO_SOURCE_ENABLED' => smoke_auto_source_enabled ,
73+ 'RUN_DOCKER_SPECS' => 'true'
74+ }
75+ sh smoke_env , 'bundle exec rspec --tag docker'
6676ensure
67- test_container_exists = JSON . parse ( `docker inspect html2rss-web-test` ) . any?
68-
69- if test_container_exists
77+ if test_container_exists? ( 'html2rss-web-test' )
7078 Output . describe 'Cleaning up test container'
7179
7280 sh 'docker logs --tail all html2rss-web-test'
@@ -76,3 +84,78 @@ ensure
7684
7785 exit 1 if $ERROR_INFO
7886end
87+
88+ namespace :openapi do
89+ desc 'Generate OpenAPI YAML from request specs'
90+ task :generate do
91+ FileUtils . mkdir_p ( 'docs/api/v1' )
92+ FileUtils . rm_f ( 'docs/api/v1/openapi.yaml' )
93+ sh ( { 'OPENAPI' => '1' } , 'bundle exec rspec spec/html2rss/web/api/v1_spec.rb --order defined' )
94+ end
95+
96+ desc 'Verify generated OpenAPI YAML is up to date'
97+ task verify : :generate do
98+ sh 'git diff --exit-code -- docs/api/v1/openapi.yaml'
99+ end
100+ end
101+
102+ namespace :yard do
103+ desc 'Fail when public methods in app/ are missing essential YARD docs'
104+ task :verify_public_docs do
105+ require 'yard'
106+
107+ files = Dir . glob ( File . join ( __dir__ , 'app/**/*.rb' ) )
108+ YARD ::Registry . clear
109+ YARD ::Registry . load ( files , true )
110+
111+ violations = [ ]
112+
113+ YARD ::Registry . all ( :method ) . each do |method_object |
114+ next unless method_object . visibility == :public
115+ next unless method_object . file &.include? ( '/app/' )
116+
117+ location = "#{ method_object . path } (#{ method_object . file } :#{ method_object . line } )"
118+ normalize_param_name = lambda do |name |
119+ name . to_s . sub ( /\A [*&]/ , '' ) . sub ( /:$/ , '' )
120+ end
121+
122+ param_tags = method_object . tags ( :param )
123+ params = method_object . parameters . map ( &:first ) . map { |name | normalize_param_name . call ( name ) }
124+ params . reject! { |name | name == 'block' }
125+
126+ param_tag_names = param_tags . map { |tag | normalize_param_name . call ( tag . name ) }
127+ missing_params = params - param_tag_names
128+ violations << "#{ location } missing @param for: #{ missing_params . join ( ', ' ) } " unless missing_params . empty?
129+
130+ param_tags . each do |tag |
131+ violations << "#{ location } @param #{ tag . name } missing type" if tag . types . nil? || tag . types . empty?
132+ end
133+
134+ return_tag = method_object . tag ( :return )
135+ if return_tag . nil?
136+ violations << "#{ location } missing @return"
137+ elsif return_tag . types . nil? || return_tag . types . empty?
138+ violations << "#{ location } @return missing type"
139+ end
140+ end
141+
142+ if violations . any?
143+ puts 'YARD public method documentation check failed:'
144+ violations . sort . each { |violation | puts " - #{ violation } " }
145+ abort "\n Found #{ violations . count } YARD documentation violation(s)."
146+ end
147+
148+ puts 'YARD public method documentation check passed.'
149+ end
150+ end
151+
152+ namespace :zeitwerk do
153+ desc 'Fail when Zeitwerk cannot eager load the app tree cleanly'
154+ task :verify do
155+ ENV [ 'RACK_ENV' ] ||= 'test'
156+ require_relative 'app'
157+
158+ Html2rss ::Web ::Boot . eager_load!
159+ puts 'Zeitwerk eager load check passed.'
160+ end
161+ end
0 commit comments