syntropy 0.29.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +2 -2
  3. data/CHANGELOG.md +22 -0
  4. data/README.md +0 -2
  5. data/bin/syntropy +8 -86
  6. data/cmd/_banner.rb +16 -0
  7. data/cmd/help.rb +12 -0
  8. data/cmd/serve.rb +95 -0
  9. data/cmd/test.rb +40 -0
  10. data/examples/{counter.rb → basic/counter.rb} +1 -1
  11. data/examples/{templates.rb → basic/templates.rb} +1 -1
  12. data/examples/mcp-oauth/.ruby-version +1 -0
  13. data/examples/mcp-oauth/Gemfile +8 -0
  14. data/examples/mcp-oauth/README.md +128 -0
  15. data/examples/mcp-oauth/app/.well-known/oauth-authorization-server.rb +18 -0
  16. data/examples/mcp-oauth/app/.well-known/oauth-protected-resource.rb +10 -0
  17. data/examples/mcp-oauth/app/_lib/auth_store.rb +23 -0
  18. data/examples/mcp-oauth/app/index.md +1 -0
  19. data/examples/mcp-oauth/app/mcp.rb +38 -0
  20. data/examples/mcp-oauth/app/oauth/authorize.rb +26 -0
  21. data/examples/mcp-oauth/app/oauth/consent.rb +86 -0
  22. data/examples/mcp-oauth/app/oauth/register.rb +15 -0
  23. data/examples/mcp-oauth/app/oauth/token.rb +79 -0
  24. data/examples/mcp-oauth/app/signin.rb +85 -0
  25. data/examples/mcp-oauth/test/helper.rb +9 -0
  26. data/examples/mcp-oauth/test/test_app.rb +27 -0
  27. data/examples/mcp-oauth/test/test_oauth.rb +628 -0
  28. data/lib/syntropy/app.rb +23 -12
  29. data/lib/syntropy/applets/builtin/default_error_handler.rb +3 -3
  30. data/lib/syntropy/applets/builtin/req.rb +1 -1
  31. data/lib/syntropy/dev_mode.rb +1 -1
  32. data/lib/syntropy/errors.rb +19 -12
  33. data/lib/syntropy/http/client.rb +43 -0
  34. data/lib/syntropy/http/client_connection.rb +36 -0
  35. data/lib/syntropy/http/io_extensions.rb +148 -0
  36. data/lib/syntropy/http/server.rb +174 -0
  37. data/lib/syntropy/http/server_connection.rb +367 -0
  38. data/lib/syntropy/http/status.rb +76 -0
  39. data/lib/syntropy/http.rb +7 -0
  40. data/lib/syntropy/json_api.rb +2 -5
  41. data/lib/syntropy/logger.rb +5 -1
  42. data/lib/syntropy/mime_types.rb +37 -0
  43. data/lib/syntropy/papercraft_extensions.rb +1 -1
  44. data/lib/syntropy/request/mock_adapter.rb +60 -0
  45. data/lib/syntropy/request/request_info.rb +255 -0
  46. data/lib/syntropy/request/response.rb +206 -0
  47. data/lib/syntropy/request/validation.rb +146 -0
  48. data/lib/syntropy/request.rb +99 -0
  49. data/lib/syntropy/routing_tree.rb +2 -1
  50. data/lib/syntropy/test.rb +65 -0
  51. data/lib/syntropy/utils.rb +1 -1
  52. data/lib/syntropy/version.rb +1 -1
  53. data/lib/syntropy.rb +4 -27
  54. data/syntropy.gemspec +2 -4
  55. data/test/app/.well-known/foo.rb +3 -0
  56. data/test/app/about/_error.rb +1 -1
  57. data/test/app/api+.rb +1 -1
  58. data/test/app_custom/_site.rb +1 -1
  59. data/test/bm_router_proc.rb +3 -3
  60. data/test/helper.rb +4 -27
  61. data/test/test_app.rb +83 -98
  62. data/test/test_caching.rb +2 -2
  63. data/test/test_errors.rb +6 -6
  64. data/test/test_http_client.rb +52 -0
  65. data/test/test_http_client_connection.rb +43 -0
  66. data/test/{test_connection.rb → test_http_server_connection.rb} +32 -32
  67. data/test/test_json_api.rb +14 -12
  68. data/test/test_mock_adapter.rb +59 -0
  69. data/test/{test_request_extensions.rb → test_request.rb} +150 -18
  70. data/test/test_response.rb +112 -0
  71. data/test/test_routing_tree.rb +15 -3
  72. data/test/test_server.rb +1 -1
  73. metadata +57 -35
  74. data/lib/syntropy/connection.rb +0 -402
  75. data/lib/syntropy/request_extensions.rb +0 -308
  76. data/lib/syntropy/server.rb +0 -173
  77. /data/examples/{bad.rb → basic/bad.rb} +0 -0
  78. /data/examples/{card.rb → basic/card.rb} +0 -0
  79. /data/examples/{counter.js → basic/counter.js} +0 -0
  80. /data/examples/{counter_api.rb → basic/counter_api.rb} +0 -0
  81. /data/examples/{favicon.ico → basic/favicon.ico} +0 -0
  82. /data/examples/{index.md → basic/index.md} +0 -0
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'syntropy'
4
+ require 'syntropy/request/mock_adapter'
5
+ require 'minitest'
6
+
7
+ module Syntropy
8
+ class TestHarness
9
+ def initialize(app)
10
+ @app = app
11
+ @app.test_mode = true
12
+ end
13
+
14
+ def request(headers, body = nil)
15
+ req = mock_req(headers, body)
16
+ @app.call(req)
17
+ req
18
+ end
19
+
20
+ private
21
+
22
+ def mock_req(headers, body = nil)
23
+ Syntropy::MockAdapter.mock(headers, body)
24
+ end
25
+ end
26
+
27
+ class Request
28
+ def response_headers
29
+ adapter.response_headers
30
+ end
31
+
32
+ def response_status
33
+ adapter.status
34
+ end
35
+
36
+ def response_body
37
+ adapter.response_body
38
+ end
39
+
40
+ def response_json
41
+ raise if response_content_type != 'application/json'
42
+ JSON.parse(response_body)
43
+ end
44
+
45
+ def response_content_type
46
+ ct = response_headers['Content-Type']
47
+ return nil if !ct
48
+
49
+ m = ct.match(/^([^;]+)/)
50
+ return nil if !m
51
+
52
+ m[1]
53
+ end
54
+
55
+ def response_cookie(name)
56
+ sc = response_headers['Set-Cookie']
57
+ return nil if !sc
58
+
59
+ m = sc.match(/#{name}=([^\s]+)$/)
60
+ return nil if !m
61
+
62
+ m[1]
63
+ end
64
+ end
65
+ end
@@ -20,7 +20,7 @@ module Syntropy
20
20
  #
21
21
  lambda { |req|
22
22
  site = sites[req.host]
23
- site ? site.call(req) : req.respond(nil, ':status' => Status::BAD_REQUEST)
23
+ site ? site.call(req) : req.respond(nil, ':status' => HTTP::BAD_REQUEST)
24
24
  }
25
25
  end
26
26
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Syntropy
4
- VERSION = '0.29.0'
4
+ VERSION = '0.31.0'
5
5
  end
data/lib/syntropy.rb CHANGED
@@ -1,18 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'qeweney'
4
3
  require 'uringmachine'
5
4
  require 'papercraft'
6
5
 
6
+ require 'syntropy/request'
7
7
  require 'syntropy/logger'
8
- require 'syntropy/connection'
9
- require 'syntropy/server'
8
+ require 'syntropy/http'
9
+ require 'syntropy/mime_types'
10
10
  require 'syntropy/app'
11
11
  require 'syntropy/connection_pool'
12
12
  require 'syntropy/errors'
13
13
  require 'syntropy/markdown'
14
14
  require 'syntropy/module'
15
- require 'syntropy/request_extensions'
16
15
  require 'syntropy/papercraft_extensions'
17
16
  require 'syntropy/routing_tree'
18
17
  require 'syntropy/json_api'
@@ -21,8 +20,6 @@ require 'syntropy/utils'
21
20
  require 'syntropy/version'
22
21
 
23
22
  module Syntropy
24
- Status = Qeweney::Status
25
-
26
23
  extend Utilities
27
24
 
28
25
  class << self
@@ -35,25 +32,6 @@ module Syntropy
35
32
  end
36
33
  end
37
34
 
38
- def colorize(color_code)
39
- "\e[#{color_code}m#{self}\e[0m"
40
- end
41
-
42
- GREEN = "\e[32m"
43
- CLEAR = "\e[0m"
44
- YELLOW = "\e[33m"
45
-
46
- BANNER =
47
- "\n"\
48
- " #{GREEN}\n"\
49
- " #{GREEN} ooo\n"\
50
- " #{GREEN}ooooo\n"\
51
- " #{GREEN} ooo vvv #{CLEAR}Syntropy - a web framework for Ruby\n"\
52
- " #{GREEN} o vvvvv #{CLEAR}--------------------------------------\n"\
53
- " #{GREEN} #{YELLOW}|#{GREEN} vvv o #{CLEAR}https://github.com/digital-fabric/syntropy\n"\
54
- " #{GREEN} :#{YELLOW}|#{GREEN}:::#{YELLOW}|#{GREEN}::#{YELLOW}|#{GREEN}:\n"\
55
- "#{YELLOW}+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\e[0m\n\n"
56
-
57
35
  class << self
58
36
  def run(env = {}, &app)
59
37
  if @in_run
@@ -66,11 +44,10 @@ module Syntropy
66
44
  begin
67
45
  @in_run = true
68
46
  machine = env[:machine] || UM.new
69
- machine.puts(env[:banner]) if env[:banner]
70
47
 
71
48
  env[:logger]&.info(message: "Running Syntropy #{Syntropy::VERSION}, UringMachine #{UM::VERSION}, Ruby #{RUBY_VERSION}")
72
49
 
73
- server = Server.new(machine, env, &app)
50
+ server = HTTP::Server.new(machine, env, &app)
74
51
 
75
52
  setup_signal_handling(machine, Fiber.current)
76
53
  server.run
data/syntropy.gemspec CHANGED
@@ -23,10 +23,8 @@ Gem::Specification.new do |s|
23
23
 
24
24
  s.add_dependency 'extralite', '~>2.14'
25
25
  s.add_dependency 'papercraft', '~>3.2.0'
26
- s.add_dependency 'qeweney', '~>0.24'
27
- s.add_dependency 'uringmachine', '~>1.0.0'
28
-
29
- s.add_dependency 'listen', '~>3.9.0'
26
+ s.add_dependency 'uringmachine', '~>1.0.2'
27
+ s.add_dependency 'escape_utils', '1.3.0'
30
28
 
31
29
  s.add_dependency 'json'
32
30
  s.add_dependency 'logger'
@@ -0,0 +1,3 @@
1
+ export ->(req) {
2
+ req.respond('foo')
3
+ }
@@ -1,4 +1,4 @@
1
- DEFAULT_STATUS = Qeweney::Status::INTERNAL_SERVER_ERROR
1
+ DEFAULT_STATUS = HTTP::INTERNAL_SERVER_ERROR
2
2
 
3
3
  export ->(req, err) {
4
4
  status = err.respond_to?(:http_status) ? err.http_status : DEFAULT_STATUS
data/test/app/api+.rb CHANGED
@@ -10,7 +10,7 @@ class API < Syntropy::JSONAPI
10
10
 
11
11
  def incr!(req)
12
12
  if req.path != '/test/api'
13
- raise Syntropy::Error.new('Teapot', Qeweney::Status::TEAPOT)
13
+ raise Syntropy::Error.new('Teapot', HTTP::TEAPOT)
14
14
  end
15
15
 
16
16
  @count += 1
@@ -1,3 +1,3 @@
1
1
  export ->(req) {
2
- req.respond(nil, ':status' => Status::TEAPOT)
2
+ req.respond(nil, ':status' => HTTP::TEAPOT)
3
3
  }
@@ -13,7 +13,7 @@ require 'roda'
13
13
  require 'benchmark/ips'
14
14
  require 'securerandom'
15
15
  require 'rack/mock_request'
16
- require 'qeweney/mock_adapter'
16
+ require 'syntropy/request/mock_adapter'
17
17
 
18
18
  class BM
19
19
  def self.name(name)
@@ -100,7 +100,7 @@ p roda_app.(req)
100
100
 
101
101
  ################################################################################
102
102
 
103
- class Qeweney::Request
103
+ class Syntropy::Request
104
104
  def response_headers
105
105
  adapter.headers
106
106
  end
@@ -157,7 +157,7 @@ proc = ->(req) { syntropy_app.(req) }
157
157
 
158
158
  module ::Kernel
159
159
  def mock_req(headers, body = nil)
160
- Qeweney::MockAdapter.mock(headers, body).tap { it.setup_mock_request }
160
+ Syntropy::MockAdapter.mock(headers, body)
161
161
  end
162
162
  end
163
163
 
data/test/helper.rb CHANGED
@@ -4,7 +4,7 @@ require 'bundler/setup'
4
4
  require_relative './coverage' if ENV['COVERAGE']
5
5
  require 'uringmachine'
6
6
  require 'syntropy'
7
- require 'qeweney/mock_adapter'
7
+ require 'syntropy/test'
8
8
  require 'minitest/autorun'
9
9
  require 'fileutils'
10
10
 
@@ -19,7 +19,7 @@ end
19
19
 
20
20
  module ::Kernel
21
21
  def mock_req(headers, body = nil)
22
- Qeweney::MockAdapter.mock(headers, body).tap { it.setup_mock_request }
22
+ Syntropy::MockAdapter.mock(headers, body)
23
23
  end
24
24
 
25
25
  def capture_exception
@@ -89,33 +89,10 @@ module Minitest::Assertions
89
89
  return unless exp_content_type
90
90
 
91
91
  if Symbol === exp_content_type
92
- exp_content_type = Qeweney::MimeTypes[exp_content_type]
92
+ exp_content_type = Syntropy::MimeTypes[exp_content_type]
93
93
  end
94
94
  actual = req.response_content_type
95
- assert_equal exp_content_type, actual
96
- end
97
- end
98
-
99
- # Extensions to be used in conjunction with `Qeweney::TestAdapter`
100
- class Qeweney::Request
101
- def response_headers
102
- adapter.response_headers
103
- end
104
-
105
- def response_status
106
- adapter.status
107
- end
108
-
109
- def response_body
110
- adapter.response_body
111
- end
112
95
 
113
- def response_json
114
- raise if response_content_type != 'application/json'
115
- JSON.parse(response_body, symbolize_names: true)
116
- end
117
-
118
- def response_content_type
119
- response_headers['Content-Type']
96
+ assert_equal exp_content_type, actual
120
97
  end
121
98
  end
data/test/test_app.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require_relative 'helper'
4
4
 
5
5
  class AppTest < Minitest::Test
6
- Status = Qeweney::Status
6
+ HTTP = Syntropy::HTTP
7
7
 
8
8
  APP_ROOT = File.join(__dir__, 'app')
9
9
 
@@ -19,156 +19,156 @@ class AppTest < Minitest::Test
19
19
  watch_files: 0.05,
20
20
  machine: @machine
21
21
  )
22
- end
23
22
 
24
- def make_request(*, **)
25
- req = mock_req(*, **)
26
- @app.call(req)
27
- req
23
+ @test_harness = Syntropy::TestHarness.new(@app)
28
24
  end
29
25
 
30
26
  def test_app_rendering
31
- req = make_request(':method' => 'GET', ':path' => '/')
32
- assert_equal Status::NOT_FOUND, req.response_status
27
+ req = @test_harness.request(':method' => 'GET', ':path' => '/')
28
+ assert_equal HTTP::NOT_FOUND, req.response_status
33
29
  assert_equal 'Not found', req.response_body
34
30
 
35
- req = make_request(':method' => 'HEAD', ':path' => '/')
36
- assert_equal Status::NOT_FOUND, req.response_status
31
+ req = @test_harness.request(':method' => 'HEAD', ':path' => '/')
32
+ assert_equal HTTP::NOT_FOUND, req.response_status
37
33
  assert_nil req.response_body
38
34
 
39
- req = make_request(':method' => 'POST', ':path' => '/')
35
+ req = @test_harness.request(':method' => 'POST', ':path' => '/')
40
36
  assert_equal 'Not found', req.response_body
41
- assert_equal Status::NOT_FOUND, req.response_status
37
+ assert_equal HTTP::NOT_FOUND, req.response_status
42
38
 
43
- req = make_request(':method' => 'GET', ':path' => '/test')
44
- assert_equal Status::OK, req.response_status
39
+ req = @test_harness.request(':method' => 'GET', ':path' => '/test')
40
+ assert_equal HTTP::OK, req.response_status
45
41
  assert_equal '<h1>Hello, world!</h1>', req.response_body
46
42
 
47
- req = make_request(':method' => 'HEAD', ':path' => '/test')
48
- assert_equal Status::OK, req.response_status
43
+ req = @test_harness.request(':method' => 'HEAD', ':path' => '/test')
44
+ assert_equal HTTP::OK, req.response_status
49
45
  assert_nil req.response_body
50
46
 
51
- req = make_request(':method' => 'POST', ':path' => '/test')
52
- assert_equal Status::METHOD_NOT_ALLOWED, req.response_status
47
+ req = @test_harness.request(':method' => 'POST', ':path' => '/test')
48
+ assert_equal HTTP::METHOD_NOT_ALLOWED, req.response_status
53
49
  assert_equal "Method not allowed", req.response_body
54
50
 
55
- req = make_request(':method' => 'GET', ':path' => '/test/assets/style.css')
51
+ req = @test_harness.request(':method' => 'GET', ':path' => '/test/assets/style.css')
56
52
  assert_equal '* { color: beige }', req.response_body
57
53
  assert_equal 'text/css', req.response_headers['Content-Type']
58
54
 
59
- req = make_request(':method' => 'GET', ':path' => '/assets/style.css')
60
- assert_equal Status::NOT_FOUND, req.response_status
55
+ req = @test_harness.request(':method' => 'GET', ':path' => '/assets/style.css')
56
+ assert_equal HTTP::NOT_FOUND, req.response_status
61
57
 
62
- req = make_request(':method' => 'GET', ':path' => '/test/api?q=get')
63
- assert_equal({ status: 'OK', response: 0 }, req.response_json)
58
+ req = @test_harness.request(':method' => 'GET', ':path' => '/test/api?q=get')
59
+ assert_equal({ 'status' => 'OK', 'response' => 0 }, req.response_json)
64
60
 
65
- req = make_request(':method' => 'POST', ':path' => '/test/api?q=get')
66
- assert_equal Status::METHOD_NOT_ALLOWED, req.response_status
67
- assert_equal({ status: 'Error', message: 'Method not allowed' }, req.response_json)
61
+ req = @test_harness.request(':method' => 'POST', ':path' => '/test/api?q=get')
62
+ assert_equal HTTP::METHOD_NOT_ALLOWED, req.response_status
63
+ assert_equal({ 'status' => 'Error', 'message' => 'Method not allowed' }, req.response_json)
68
64
 
69
- req = make_request(':method' => 'GET', ':path' => '/test/api/foo?q=get')
70
- assert_equal({ status: 'OK', response: 0 }, req.response_json)
65
+ req = @test_harness.request(':method' => 'GET', ':path' => '/test/api/foo?q=get')
66
+ assert_equal({ 'status' => 'OK', 'response' => 0 }, req.response_json)
71
67
 
72
- req = make_request(':method' => 'POST', ':path' => '/test/api?q=incr')
73
- assert_equal({ status: 'OK', response: 1 }, req.response_json)
68
+ req = @test_harness.request(':method' => 'POST', ':path' => '/test/api?q=incr')
69
+ assert_equal({ 'status' => 'OK', 'response' => 1 }, req.response_json)
74
70
 
75
- req = make_request(':method' => 'GET', ':path' => '/test/api?q=incr')
76
- assert_equal Status::METHOD_NOT_ALLOWED, req.response_status
77
- assert_equal({ status: 'Error', message: 'Method not allowed' }, req.response_json)
71
+ req = @test_harness.request(':method' => 'GET', ':path' => '/test/api?q=incr')
72
+ assert_equal HTTP::METHOD_NOT_ALLOWED, req.response_status
73
+ assert_equal({ 'status' => 'Error', 'message' => 'Method not allowed' }, req.response_json)
78
74
 
79
- req = make_request(':method' => 'POST', ':path' => '/test/api/foo?q=incr')
80
- assert_equal({ status: 'Error', message: 'Teapot' }, req.response_json)
81
- assert_equal Status::TEAPOT, req.response_status
75
+ req = @test_harness.request(':method' => 'POST', ':path' => '/test/api/foo?q=incr')
76
+ assert_equal({ 'status' => 'Error', 'message' => 'Teapot' }, req.response_json)
77
+ assert_equal HTTP::TEAPOT, req.response_status
82
78
 
83
- req = make_request(':method' => 'POST', ':path' => '/test/api/foo/bar?q=incr')
84
- assert_equal({ status: 'Error', message: 'Teapot' }, req.response_json)
85
- assert_equal Status::TEAPOT, req.response_status
79
+ req = @test_harness.request(':method' => 'POST', ':path' => '/test/api/foo/bar?q=incr')
80
+ assert_equal({ 'status' => 'Error', 'message' => 'Teapot' }, req.response_json)
81
+ assert_equal HTTP::TEAPOT, req.response_status
86
82
 
87
- req = make_request(':method' => 'GET', ':path' => '/test/bar')
83
+ req = @test_harness.request(':method' => 'GET', ':path' => '/test/bar')
88
84
  assert_equal 'foobar', req.response_body
89
- assert_equal Status::OK, req.response_status
85
+ assert_equal HTTP::OK, req.response_status
90
86
 
91
- req = make_request(':method' => 'POST', ':path' => '/test/bar')
87
+ req = @test_harness.request(':method' => 'POST', ':path' => '/test/bar')
92
88
  assert_equal 'foobar', req.response_body
93
- assert_equal Status::OK, req.response_status
89
+ assert_equal HTTP::OK, req.response_status
94
90
 
95
- req = make_request(':method' => 'GET', ':path' => '/test/baz')
91
+ req = @test_harness.request(':method' => 'GET', ':path' => '/test/baz')
96
92
  assert_equal 'foobar', req.response_body
97
- assert_equal Status::OK, req.response_status
93
+ assert_equal HTTP::OK, req.response_status
98
94
 
99
- req = make_request(':method' => 'POST', ':path' => '/test/baz')
95
+ req = @test_harness.request(':method' => 'POST', ':path' => '/test/baz')
100
96
  assert_equal 'Method not allowed', req.response_body
101
- assert_equal Status::METHOD_NOT_ALLOWED, req.response_status
97
+ assert_equal HTTP::METHOD_NOT_ALLOWED, req.response_status
102
98
 
103
- req = make_request(':method' => 'GET', ':path' => '/test/about')
99
+ req = @test_harness.request(':method' => 'GET', ':path' => '/test/about')
104
100
  assert_equal 'About', req.response_body.chomp
105
101
 
106
- req = make_request(':method' => 'GET', ':path' => '/test/about/foo')
102
+ req = @test_harness.request(':method' => 'GET', ':path' => '/test/about/foo')
107
103
  assert_equal '<!DOCTYPE html><html><head><title></title></head><body><p>Hello from Markdown</p></body></html>', req.response_body.gsub(/\n/, '')
108
104
 
109
- req = make_request(':method' => 'HEAD', ':path' => '/test/about/foo')
105
+ req = @test_harness.request(':method' => 'HEAD', ':path' => '/test/about/foo')
110
106
  assert_nil req.response_body
111
107
 
112
- req = make_request(':method' => 'GET', ':path' => '/test/about/foo/bar')
113
- assert_equal Status::NOT_FOUND, req.response_status
108
+ req = @test_harness.request(':method' => 'GET', ':path' => '/test/about/foo/bar')
109
+ assert_equal HTTP::NOT_FOUND, req.response_status
114
110
 
115
- req = make_request(':method' => 'GET', ':path' => '/test/params/abc')
111
+ req = @test_harness.request(':method' => 'GET', ':path' => '/test/params/abc')
116
112
  assert_equal '/test/params/[foo]-abc', req.response_body.chomp
117
113
 
118
- req = make_request(':method' => 'GET', ':path' => '/test/rss')
114
+ req = @test_harness.request(':method' => 'GET', ':path' => '/test/rss')
119
115
  assert_equal '<link>foo</link>', req.response_body
120
116
 
121
- req = make_request(':method' => 'GET', ':path' => '/test/bad_mod')
122
- assert_equal Status::INTERNAL_SERVER_ERROR, req.response_status
117
+ req = @test_harness.request(':method' => 'GET', ':path' => '/test/bad_mod')
118
+ assert_equal HTTP::INTERNAL_SERVER_ERROR, req.response_status
119
+
120
+ req = @test_harness.request(':method' => 'GET', ':path' => '/test/.well-known/foo')
121
+ assert_equal HTTP::OK, req.response_status
122
+ assert_equal 'foo', req.response_body
123
123
  end
124
124
 
125
125
  def test_automatic_redirect_on_trailing_slash
126
- req = make_request(':method' => 'GET', ':path' => '/test/rss/')
127
- assert_equal Status::MOVED_PERMANENTLY, req.response_status
126
+ req = @test_harness.request(':method' => 'GET', ':path' => '/test/rss/')
127
+ assert_equal HTTP::MOVED_PERMANENTLY, req.response_status
128
128
  assert_equal '/test/rss', req.response_headers['Location']
129
129
  end
130
130
 
131
131
  def test_app_file_watching
132
132
  @machine.sleep 0.3
133
133
 
134
- req = make_request(':method' => 'GET', ':path' => @tmp_path)
134
+ req = @test_harness.request(':method' => 'GET', ':path' => @tmp_path)
135
135
  assert_equal 'foo', req.response_body
136
136
 
137
137
  orig_body = IO.read(@tmp_fn)
138
138
  IO.write(@tmp_fn, orig_body.gsub('foo', 'bar'))
139
139
  @machine.sleep(0.3)
140
140
 
141
- req = make_request(':method' => 'GET', ':path' => @tmp_path)
141
+ req = @test_harness.request(':method' => 'GET', ':path' => @tmp_path)
142
142
  assert_equal 'bar', req.response_body
143
143
  ensure
144
144
  IO.write(@tmp_fn, orig_body) if orig_body
145
145
  end
146
146
 
147
147
  def test_middleware
148
- req = make_request(':method' => 'HEAD', ':path' => '/test?foo=42')
149
- assert_equal Status::OK, req.response_status
148
+ req = @test_harness.request(':method' => 'HEAD', ':path' => '/test?foo=42')
149
+ assert_equal HTTP::OK, req.response_status
150
150
  assert_nil req.response_body
151
151
  assert_equal '42', req.ctx[:foo]
152
152
 
153
- req = make_request(':method' => 'HEAD', ':path' => '/test/about/raise?foo=43')
154
- assert_equal Status::INTERNAL_SERVER_ERROR, req.response_status
153
+ req = @test_harness.request(':method' => 'HEAD', ':path' => '/test/about/raise?foo=43')
154
+ assert_equal HTTP::INTERNAL_SERVER_ERROR, req.response_status
155
155
  assert_equal '<h1>Raised error</h1>', req.response_body
156
156
  assert_equal '43', req.ctx[:foo]
157
157
  end
158
158
 
159
159
  def test_middleware_invocation_on_404
160
- req = make_request(':method' => 'HEAD', ':path' => '/azerty?foo=bar')
161
- assert_equal Status::NOT_FOUND, req.response_status
160
+ req = @test_harness.request(':method' => 'HEAD', ':path' => '/azerty?foo=bar')
161
+ assert_equal HTTP::NOT_FOUND, req.response_status
162
162
  assert_nil req.ctx[:foo]
163
163
 
164
- req = make_request(':method' => 'HEAD', ':path' => '/test/azerty?foo=bar')
165
- assert_equal Status::NOT_FOUND, req.response_status
164
+ req = @test_harness.request(':method' => 'HEAD', ':path' => '/test/azerty?foo=bar')
165
+ assert_equal HTTP::NOT_FOUND, req.response_status
166
166
  assert_equal 'bar', req.ctx[:foo]
167
167
  end
168
168
  end
169
169
 
170
170
  class CustomAppTest < Minitest::Test
171
- Status = Qeweney::Status
171
+ HTTP = Syntropy::HTTP
172
172
 
173
173
  APP_ROOT = File.join(__dir__, 'app_custom')
174
174
 
@@ -179,23 +179,18 @@ class CustomAppTest < Minitest::Test
179
179
  root_dir: APP_ROOT,
180
180
  mount_path: '/'
181
181
  )
182
- end
183
-
184
- def make_request(*, **)
185
- req = mock_req(*, **)
186
- @app.call(req)
187
- req
182
+ @test_harness = Syntropy::TestHarness.new(@app)
188
183
  end
189
184
 
190
185
  def test_app_with_site_rb_file
191
- req = make_request(':method' => 'GET', ':path' => '/foo/bar')
186
+ req = @test_harness.request(':method' => 'GET', ':path' => '/foo/bar')
192
187
  assert_nil req.response_body
193
- assert_equal Status::TEAPOT, req.response_status
188
+ assert_equal HTTP::TEAPOT, req.response_status
194
189
  end
195
190
  end
196
191
 
197
192
  class MultiSiteAppTest < Minitest::Test
198
- Status = Qeweney::Status
193
+ HTTP = Syntropy::HTTP
199
194
 
200
195
  APP_ROOT = File.join(__dir__, 'app_multi_site')
201
196
 
@@ -206,29 +201,24 @@ class MultiSiteAppTest < Minitest::Test
206
201
  root_dir: APP_ROOT,
207
202
  mount_path: '/'
208
203
  )
209
- end
210
-
211
- def make_request(*, **)
212
- req = mock_req(*, **)
213
- @app.call(req)
214
- req
204
+ @test_harness = Syntropy::TestHarness.new(@app)
215
205
  end
216
206
 
217
207
  def test_route_by_host
218
- req = make_request(':method' => 'GET', ':path' => '/', 'host' => 'blah')
208
+ req = @test_harness.request(':method' => 'GET', ':path' => '/', 'host' => 'blah')
219
209
  assert_nil req.response_body
220
- assert_equal Status::BAD_REQUEST, req.response_status
210
+ assert_equal HTTP::BAD_REQUEST, req.response_status
221
211
 
222
- req = make_request(':method' => 'GET', ':path' => '/', 'host' => 'foo.bar')
212
+ req = @test_harness.request(':method' => 'GET', ':path' => '/', 'host' => 'foo.bar')
223
213
  assert_equal '<h1>foo.bar</h1>', req.response_body.chomp
224
214
 
225
- req = make_request(':method' => 'GET', ':path' => '/', 'host' => 'bar.baz')
215
+ req = @test_harness.request(':method' => 'GET', ':path' => '/', 'host' => 'bar.baz')
226
216
  assert_equal '<h1>bar.baz</h1>', req.response_body.chomp
227
217
  end
228
218
  end
229
219
 
230
220
  class AppAPITest < Minitest::Test
231
- Status = Qeweney::Status
221
+ HTTP = Syntropy::HTTP
232
222
 
233
223
  APP_ROOT = File.join(__dir__, 'app')
234
224
 
@@ -292,16 +282,10 @@ class AppAPITest < Minitest::Test
292
282
  end
293
283
 
294
284
  class AppDependenciesTest < Minitest::Test
295
- Status = Qeweney::Status
285
+ HTTP = Syntropy::HTTP
296
286
 
297
287
  APP_ROOT = File.join(__dir__, 'app')
298
288
 
299
- def make_request(*, **)
300
- req = mock_req(*, **)
301
- @app.call(req)
302
- req
303
- end
304
-
305
289
  def test_app_dependencies
306
290
  foo = { foo: 'foo' }
307
291
  bar = { bar: 'bar' }
@@ -318,9 +302,10 @@ class AppDependenciesTest < Minitest::Test
318
302
  foo: foo,
319
303
  bar: bar
320
304
  )
305
+ @test_harness = Syntropy::TestHarness.new(@app)
321
306
 
322
- req = make_request(':method' => 'GET', ':path' => '/test/bar')
307
+ req = @test_harness.request(':method' => 'GET', ':path' => '/test/bar')
323
308
  assert_equal 'foobar', req.response_body
324
- assert_equal Status::OK, req.response_status
309
+ assert_equal HTTP::OK, req.response_status
325
310
  end
326
311
  end
data/test/test_caching.rb CHANGED
@@ -4,7 +4,7 @@ require_relative 'helper'
4
4
  require 'digest/sha1'
5
5
 
6
6
  class CachingTest < Minitest::Test
7
- Status = Qeweney::Status
7
+ HTTP = Syntropy::HTTP
8
8
 
9
9
  APP_ROOT = File.join(__dir__, 'app')
10
10
 
@@ -40,7 +40,7 @@ class CachingTest < Minitest::Test
40
40
  @app = Syntropy::App.new(**@env)
41
41
 
42
42
  @c_fd, @s_fd = make_socket_pair
43
- @adapter = Syntropy::Connection.new(nil, @machine, @s_fd, @env) { @app.(it) }
43
+ @adapter = Syntropy::HTTP::ServerConnection.new(@machine, @s_fd, @env) { @app.(it) }
44
44
  end
45
45
 
46
46
  def teardown