syntropy 0.33.0 → 0.34.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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/cmd/console.rb +18 -7
  4. data/cmd/serve.rb +26 -18
  5. data/cmd/test.rb +37 -24
  6. data/examples/blog/.gitignore +1 -0
  7. data/examples/blog/app/_lib/database.rb +13 -0
  8. data/examples/blog/app/_lib/{post_store.rb → posts.rb} +3 -1
  9. data/examples/blog/app/posts/[id]/edit.rb +2 -2
  10. data/examples/blog/app/posts/[id]/index.rb +4 -4
  11. data/examples/blog/app/posts/index.rb +4 -4
  12. data/examples/blog/app/posts/new.rb +1 -1
  13. data/examples/blog/config/development.rb +5 -0
  14. data/examples/blog/config/production.rb +4 -0
  15. data/examples/blog/config/test.rb +5 -0
  16. data/examples/blog/test/test_posts.rb +65 -0
  17. data/examples/mcp-oauth/app/oauth/token.rb +1 -1
  18. data/lib/syntropy/app.rb +48 -40
  19. data/lib/syntropy/applets/builtin/auto_refresh/watch.sse.rb +1 -1
  20. data/lib/syntropy/db/schema.rb +1 -1
  21. data/lib/syntropy/db/store.rb +2 -0
  22. data/lib/syntropy/errors.rb +6 -2
  23. data/lib/syntropy/http/client.rb +1 -0
  24. data/lib/syntropy/http/server_connection.rb +0 -4
  25. data/lib/syntropy/json_api.rb +27 -1
  26. data/lib/syntropy/logger.rb +81 -27
  27. data/lib/syntropy/markdown.rb +61 -32
  28. data/lib/syntropy/mime_types.rb +9 -5
  29. data/lib/syntropy/module_loader.rb +20 -9
  30. data/lib/syntropy/papercraft_extensions.rb +2 -2
  31. data/lib/syntropy/request/mock_adapter.rb +10 -8
  32. data/lib/syntropy/request/request_info.rb +91 -0
  33. data/lib/syntropy/request/response.rb +1 -12
  34. data/lib/syntropy/request/validation.rb +1 -0
  35. data/lib/syntropy/request.rb +51 -19
  36. data/lib/syntropy/routing_tree.rb +27 -28
  37. data/lib/syntropy/session.rb +198 -0
  38. data/lib/syntropy/side_run.rb +25 -2
  39. data/lib/syntropy/test.rb +105 -10
  40. data/lib/syntropy/utils.rb +53 -18
  41. data/lib/syntropy/version.rb +1 -1
  42. data/lib/syntropy.rb +44 -10
  43. data/test/bm_router_proc.rb +4 -4
  44. data/test/fixtures/app/class_instance.rb +5 -0
  45. data/test/fixtures/app/http.rb +5 -0
  46. data/test/fixtures/app/post_ct.rb +5 -0
  47. data/test/fixtures/app/singleton.rb +3 -0
  48. data/test/test_app.rb +13 -52
  49. data/test/test_caching.rb +2 -2
  50. data/test/test_db_schema.rb +1 -1
  51. data/test/test_http_server_connection.rb +3 -3
  52. data/test/test_module_loader.rb +5 -2
  53. data/test/test_response.rb +0 -19
  54. data/test/test_routing_tree.rb +69 -69
  55. data/test/test_server.rb +5 -9
  56. data/test/test_test.rb +70 -0
  57. metadata +52 -42
  58. data/examples/blog/app/_setup.rb +0 -4
  59. data/lib/syntropy/request/session.rb +0 -113
  60. /data/test/{app → fixtures/app}/.well-known/foo.rb +0 -0
  61. /data/test/{app → fixtures/app}/_hook.rb +0 -0
  62. /data/test/{app → fixtures/app}/_layout/default.rb +0 -0
  63. /data/test/{app → fixtures/app}/_lib/callable.rb +0 -0
  64. /data/test/{app → fixtures/app}/_lib/dep.rb +0 -0
  65. /data/test/{app → fixtures/app}/_lib/env.rb +0 -0
  66. /data/test/{app → fixtures/app}/_lib/klass.rb +0 -0
  67. /data/test/{app → fixtures/app}/_lib/missing-export.rb +0 -0
  68. /data/test/{app → fixtures/app}/_lib/self.rb +0 -0
  69. /data/test/{app → fixtures/app}/about/_error.rb +0 -0
  70. /data/test/{app → fixtures/app}/about/foo.md +0 -0
  71. /data/test/{app → fixtures/app}/about/index.rb +0 -0
  72. /data/test/{app → fixtures/app}/about/raise.rb +0 -0
  73. /data/test/{app → fixtures/app}/api+.rb +0 -0
  74. /data/test/{app → fixtures/app}/assets/style.css +0 -0
  75. /data/test/{app → fixtures/app}/bad_mod.rb +0 -0
  76. /data/test/{app → fixtures/app}/bar.rb +0 -0
  77. /data/test/{app → fixtures/app}/baz.rb +0 -0
  78. /data/test/{app → fixtures/app}/by_method.rb +0 -0
  79. /data/test/{app → fixtures/app}/deps.rb +0 -0
  80. /data/test/{app → fixtures/app}/index.html +0 -0
  81. /data/test/{app → fixtures/app}/mod/bar/index+.rb +0 -0
  82. /data/test/{app → fixtures/app}/mod/foo/index.rb +0 -0
  83. /data/test/{app → fixtures/app}/mod/path/a.rb +0 -0
  84. /data/test/{app → fixtures/app}/mod/path/b.rb +0 -0
  85. /data/test/{app → fixtures/app}/params/[foo].rb +0 -0
  86. /data/test/{app → fixtures/app}/rss.rb +0 -0
  87. /data/test/{app → fixtures/app}/tmp.rb +0 -0
  88. /data/test/{app_custom → fixtures/app_custom}/_site.rb +0 -0
  89. /data/test/{app_multi_site → fixtures/app_multi_site}/_site.rb +0 -0
  90. /data/test/{app_multi_site → fixtures/app_multi_site}/bar.baz/index.html +0 -0
  91. /data/test/{app_multi_site → fixtures/app_multi_site}/foo.bar/index.html +0 -0
  92. /data/test/{app_setup → fixtures/app_setup}/_setup.rb +0 -0
  93. /data/test/{app_setup → fixtures/app_setup}/index.rb +0 -0
  94. /data/test/{app_with_schema → fixtures/app_with_schema}/_schema/2026-01-02-foo.rb +0 -0
  95. /data/test/{app_with_schema → fixtures/app_with_schema}/_schema/2026-05-30-bar.rb +0 -0
  96. /data/test/{schema → fixtures/schema}/2026-01-02-foo.rb +0 -0
  97. /data/test/{schema → fixtures/schema}/2026-05-30-bar.rb +0 -0
@@ -1,52 +1,87 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'securerandom'
4
+
3
5
  module Syntropy
4
6
  # Utilities for use in modules
5
7
  module Utilities
6
- # Returns a request handler that routes request according to
8
+ def tmp_path(prefix = 'syntropy')
9
+ "/tmp/#{prefix}-#{SecureRandom.hex(16)}"
10
+ end
11
+
12
+ # Returns a request handler that routes request according to the host
13
+ # header. Looks for site directories (named by host name) in the app's root
14
+ # directory. A map may be given in order to provide additional hostnames to
15
+ # site directories.
16
+ #
17
+ # @param env [Hash] app environment hash
18
+ # @param map [Hash, nil] additional hostname map
19
+ # @return [Proc] router proc
7
20
  def route_by_host(env, map = nil)
8
- root = env[:root_dir]
9
- sites = Dir[File.join(root, '*')]
10
- .reject { File.basename(it) =~ /^_/ }
11
- .select { File.directory?(it) }
12
- .each_with_object({}) { |fn, h|
13
- name = File.basename(fn)
14
- h[name] = Syntropy::App.new(**env.merge(root_dir: fn))
15
- }
16
-
17
- # copy over map refs
21
+ sites = find_hostname_sites(env)
22
+
23
+ # add map refs
18
24
  map&.each { |k, v| sites[k] = sites[v] }
19
25
 
20
- #
21
26
  lambda { |req|
22
27
  site = sites[req.host]
23
28
  site ? site.call(req) : req.respond(nil, ':status' => HTTP::BAD_REQUEST)
24
29
  }
25
30
  end
26
31
 
32
+ # Returns a list of parsed markdown pages at the given path.
33
+ #
34
+ # @param env [Hash] app environment hash
35
+ # @param ref [String] directory path
36
+ # @return [Array<Hash>] array of page entries
27
37
  def page_list(env, ref)
28
- full_path = File.join(env[:root_dir], ref)
38
+ full_path = File.join(env[:app_root], ref)
29
39
  raise 'Not a directory' if !File.directory?(full_path)
30
40
 
31
41
  Dir[File.join(full_path, '*.md')].sort.map {
32
- atts, markdown = Syntropy.parse_markdown_file(it, env)
42
+ atts, markdown = Syntropy::Markdown.parse(it, env)
33
43
  { atts:, markdown: }
34
44
  }
35
45
  end
36
46
 
37
- def app(**env)
38
- Syntropy::App.new(**env)
47
+ # Instantiates a Syntropy app for the given environment hash.
48
+ #
49
+ # @return [Syntropy::App]
50
+ def app(**)
51
+ Syntropy::App.new(**)
39
52
  end
40
53
 
41
- BUILTIN_APPLET_ROOT_DIR = File.expand_path(File.join(__dir__, 'applets/builtin'))
54
+ BUILTIN_APPLET_app_root = File.expand_path(File.join(__dir__, 'applets/builtin'))
55
+
56
+ # Creates a builtin applet with the given environment hash. By default the
57
+ # builtin applet is mounted at /.syntropy.
58
+ #
59
+ # @param env [Hash] app environment
60
+ # @param mount_path [String] mount path for the builtin applet
61
+ # @return [Syntropy::App] applet
42
62
  def builtin_applet(env, mount_path: '/.syntropy')
43
63
  app(
44
64
  machine: env[:machine],
45
- root_dir: BUILTIN_APPLET_ROOT_DIR,
65
+ app_root: BUILTIN_APPLET_app_root,
46
66
  mount_path: mount_path,
47
67
  builtin_applet_path: nil,
48
68
  watch_files: nil
49
69
  )
50
70
  end
71
+
72
+ private
73
+
74
+ # Finds sites in the root directory for the given environment hash.
75
+ #
76
+ # @param env [Hash] app environment hash
77
+ # @return [Hash] hash mapping hostname to app
78
+ def find_hostname_sites(env)
79
+ Dir[File.join(env[:app_root], '*')]
80
+ .select { File.directory?(it) && File.basename(it) !~ /^_/ }
81
+ .each_with_object({}) { |fn, h|
82
+ name = File.basename(fn)
83
+ h[name] = Syntropy::App.new(**env.merge(app_root: fn))
84
+ }
85
+ end
51
86
  end
52
87
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Syntropy
4
- VERSION = '0.33.0'
4
+ VERSION = '0.34.0'
5
5
  end
data/lib/syntropy.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'uringmachine'
4
4
  require 'papercraft'
5
+ require 'yaml'
5
6
 
6
7
  require 'syntropy/request'
7
8
  require 'syntropy/logger'
@@ -21,20 +22,31 @@ require 'syntropy/side_run'
21
22
  require 'syntropy/utils'
22
23
  require 'syntropy/version'
23
24
 
25
+ # Syntropy is a web framework for building web apps in Ruby. Syntropy uses
26
+ # UringMachine for I/O and concurrency, and provides a comprehensive and
27
+ # flexible solution for writing web apps with minimal boilerplate.
24
28
  module Syntropy
25
29
  extend Utilities
26
30
 
27
31
  class << self
28
- attr_accessor :machine
32
+ attr_accessor :machine, :dev_mode
29
33
 
34
+ # Runs the given block on a separate thread. Use this method for running
35
+ # code that is not fiber-aware (i.e. does not use UringMachine).
36
+ #
37
+ # @return [any] operation return value
30
38
  def side_run(&block)
31
39
  raise 'Syntropy.machine not set' if !@machine
32
40
 
33
41
  SideRun.call(@machine, &block)
34
42
  end
35
- end
36
43
 
37
- class << self
44
+ # Runs a web app with the given environment hash. The given block is either
45
+ # an instance of Syntropy::App, or a Proc/callable that takes a request as
46
+ # argument.
47
+ #
48
+ # @param env [Hash] environment
49
+ # @return [void]
38
50
  def run(env = {}, &app)
39
51
  if @in_run
40
52
  @env = env
@@ -47,7 +59,14 @@ module Syntropy
47
59
  @in_run = true
48
60
  machine = env[:machine] || UM.new
49
61
 
50
- env[:logger]&.info(message: "Running Syntropy #{Syntropy::VERSION}, UringMachine #{UM::VERSION}, Ruby #{RUBY_VERSION}")
62
+ if (logger = env[:logger])
63
+ logger.info(
64
+ message: "Syntropy #{Syntropy::VERSION}, UringMachine #{UM::VERSION}, Ruby #{RUBY_VERSION}"
65
+ )
66
+ logger.info(
67
+ message: "Running in #{env[:mode]} mode"
68
+ )
69
+ end
51
70
 
52
71
  server = HTTP::Server.new(machine, env, &app)
53
72
 
@@ -58,23 +77,38 @@ module Syntropy
58
77
  end
59
78
  end
60
79
 
61
- def env(env = nil, &app)
62
- return @env if !env && !app
80
+ def load_config(env)
81
+ return if !env[:config_root]
82
+
83
+ loader_env = env.merge(
84
+ app_root: env[:config_root],
85
+ logger: nil
86
+ )
87
+ loader = ModuleLoader.new(loader_env)
88
+ config = loader.load(env[:mode])
63
89
 
64
- @env = env || {}
65
- @env[:app] = app if app
90
+ env[:config] = config
66
91
  end
67
92
 
68
93
  private
69
94
 
95
+ # Sets up asynchronous SIGINT handling.
96
+ #
97
+ # @param machine [UringMachine] machine instance
98
+ # @param fiber [Fiber] fiber to terminate on SIGINT
99
+ # @return [void]
70
100
  def setup_signal_handling(machine, fiber)
71
101
  queue = UM::Queue.new
72
102
  trap('SIGINT') { machine.push(queue, :SIGINT) }
73
103
  machine.spin { watch_for_int_signal(machine, queue, fiber) }
74
104
  end
75
105
 
76
- # waits for signal from queue, then terminates given fiber
77
- # to be done
106
+ # Waits for signal from queue, then terminates the given fiber.
107
+ #
108
+ # @param machine [UringMachine] machine instance
109
+ # @param queue [UringMachine::Queue] queue to wait on
110
+ # @param fiber [Fiber] fiber to terminate
111
+ # @return [void]
78
112
  def watch_for_int_signal(machine, queue, fiber)
79
113
  machine.shift(queue)
80
114
  machine.schedule(fiber, UM::Terminate.new)
@@ -138,8 +138,8 @@ def make_tmp_file_tree(dir, spec)
138
138
  dir
139
139
  end
140
140
 
141
- ROOT_DIR = "/tmp/#{__FILE__.gsub('/', '-')}-#{SecureRandom.hex}"
142
- make_tmp_file_tree(ROOT_DIR, {
141
+ app_root = "/tmp/#{__FILE__.gsub('/', '-')}-#{SecureRandom.hex}"
142
+ make_tmp_file_tree(app_root, {
143
143
  'index.rb': "export ->(req) { req.redirect('/hello') }",
144
144
  'hello': {
145
145
  'index.rb': "export ->(req) { req.respond('Hello!', 'Content-Type' => 'text/html') }",
@@ -149,7 +149,7 @@ make_tmp_file_tree(ROOT_DIR, {
149
149
 
150
150
  machine = UM.new
151
151
  syntropy_app = Syntropy::App.new(
152
- root_dir: ROOT_DIR,
152
+ app_root: app_root,
153
153
  mount_path: '/',
154
154
  machine: machine
155
155
  )
@@ -185,7 +185,7 @@ BM.run do
185
185
  def setup
186
186
  machine = UM.new
187
187
  syntropy_app = Syntropy::App.new(
188
- root_dir: ROOT_DIR,
188
+ app_root: app_root,
189
189
  mount_path: '/',
190
190
  # watch_files: 0.05,
191
191
  machine: machine
@@ -0,0 +1,5 @@
1
+ class Foo
2
+ def bar; :baz; end
3
+ end
4
+
5
+ export Foo.new
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ export ->(req) {
4
+ req.respond('hi', ':status' => HTTP::TEAPOT)
5
+ }
@@ -0,0 +1,5 @@
1
+ export http_methods
2
+
3
+ def post(req)
4
+ req.respond("#{req.content_type}:#{req.read}")
5
+ end
@@ -0,0 +1,3 @@
1
+ class Bar; end
2
+
3
+ export self
data/test/test_app.rb CHANGED
@@ -5,7 +5,7 @@ require_relative 'helper'
5
5
  class AppTest < Minitest::Test
6
6
  HTTP = Syntropy::HTTP
7
7
 
8
- APP_ROOT = File.join(__dir__, 'app')
8
+ APP_ROOT = File.join(__dir__, 'fixtures/app')
9
9
 
10
10
  def setup
11
11
  @machine = UM.new
@@ -14,7 +14,7 @@ class AppTest < Minitest::Test
14
14
  @tmp_fn = File.join(APP_ROOT, 'tmp.rb')
15
15
 
16
16
  @app = Syntropy::App.new(
17
- root_dir: APP_ROOT,
17
+ app_root: APP_ROOT,
18
18
  mount_path: '/test',
19
19
  watch_files: 0.05,
20
20
  machine: @machine
@@ -133,6 +133,9 @@ class AppTest < Minitest::Test
133
133
 
134
134
  req = @test_harness.request(':method' => 'DELETE', ':path' => '/test/by_method')
135
135
  assert_equal HTTP::METHOD_NOT_ALLOWED, req.response_status
136
+
137
+ req = @test_harness.request(':method' => 'GET', ':path' => '/test/http')
138
+ assert_equal HTTP::TEAPOT, req.response_status
136
139
  end
137
140
 
138
141
  def test_automatic_redirect_on_trailing_slash
@@ -183,13 +186,13 @@ end
183
186
  class CustomAppTest < Minitest::Test
184
187
  HTTP = Syntropy::HTTP
185
188
 
186
- APP_ROOT = File.join(__dir__, 'app_custom')
189
+ APP_ROOT = File.join(__dir__, 'fixtures/app_custom')
187
190
 
188
191
  def setup
189
192
  @machine = UM.new
190
193
  @app = Syntropy::App.load(
191
194
  machine: @machine,
192
- root_dir: APP_ROOT,
195
+ app_root: APP_ROOT,
193
196
  mount_path: '/'
194
197
  )
195
198
  @test_harness = Syntropy::TestHarness.new(@app)
@@ -205,13 +208,13 @@ end
205
208
  class MultiSiteAppTest < Minitest::Test
206
209
  HTTP = Syntropy::HTTP
207
210
 
208
- APP_ROOT = File.join(__dir__, 'app_multi_site')
211
+ APP_ROOT = File.join(__dir__, 'fixtures/app_multi_site')
209
212
 
210
213
  def setup
211
214
  @machine = UM.new
212
215
  @app = Syntropy::App.load(
213
216
  machine: @machine,
214
- root_dir: APP_ROOT,
217
+ app_root: APP_ROOT,
215
218
  mount_path: '/'
216
219
  )
217
220
  @test_harness = Syntropy::TestHarness.new(@app)
@@ -233,7 +236,7 @@ end
233
236
  class AppAPITest < Minitest::Test
234
237
  HTTP = Syntropy::HTTP
235
238
 
236
- APP_ROOT = File.join(__dir__, 'app')
239
+ APP_ROOT = File.join(__dir__, 'fixtures/app')
237
240
 
238
241
  def setup
239
242
  @machine = UM.new
@@ -242,7 +245,7 @@ class AppAPITest < Minitest::Test
242
245
  @tmp_fn = File.join(APP_ROOT, 'tmp.rb')
243
246
 
244
247
  @app = Syntropy::App.new(
245
- root_dir: APP_ROOT,
248
+ app_root: APP_ROOT,
246
249
  mount_path: '/test',
247
250
  watch_files: 0.05,
248
251
  machine: @machine
@@ -297,7 +300,7 @@ end
297
300
  class AppDependenciesTest < Minitest::Test
298
301
  HTTP = Syntropy::HTTP
299
302
 
300
- APP_ROOT = File.join(__dir__, 'app')
303
+ APP_ROOT = File.join(__dir__, 'fixtures/app')
301
304
 
302
305
  def test_app_dependencies
303
306
  foo = { foo: 'foo' }
@@ -309,7 +312,7 @@ class AppDependenciesTest < Minitest::Test
309
312
  @tmp_fn = File.join(APP_ROOT, 'tmp.rb')
310
313
 
311
314
  @app = Syntropy::App.new(
312
- root_dir: APP_ROOT,
315
+ app_root: APP_ROOT,
313
316
  mount_path: '/test',
314
317
  machine: @machine,
315
318
  foo: foo,
@@ -322,45 +325,3 @@ class AppDependenciesTest < Minitest::Test
322
325
  assert_equal HTTP::OK, req.response_status
323
326
  end
324
327
  end
325
-
326
- class AppDBSetupDBTest < Minitest::Test
327
- HTTP = Syntropy::HTTP
328
-
329
- APP_ROOT = File.join(__dir__, 'app_with_schema')
330
-
331
- def test_app_setup_db
332
- machine = UM.new
333
-
334
- app = Syntropy::App.new(
335
- root_dir: APP_ROOT,
336
- mount_path: '/test',
337
- machine: machine
338
- )
339
-
340
- assert_equal false, app.respond_to?(:connection_pool)
341
- assert_equal false, app.respond_to?(:schema)
342
-
343
- fn = "/tmp/#{rand(100000)}.db"
344
-
345
- app.setup_db(
346
- db_path: fn,
347
- schema_root: '_schema'
348
- )
349
-
350
- assert_equal true, app.respond_to?(:connection_pool)
351
- assert_equal fn, app.connection_pool.with_db { it.filename }
352
-
353
- assert_equal true, app.respond_to?(:schema)
354
- app.schema.apply(app.connection_pool)
355
- assert_equal '2026-05-30-bar', app.schema.current_version(app.connection_pool)
356
-
357
- assert_equal [
358
- {
359
- id: 1,
360
- title: 'foo',
361
- body: 'baz'
362
- }
363
- ], app.connection_pool.query('select id, title, body from posts')
364
-
365
- end
366
- end
data/test/test_caching.rb CHANGED
@@ -6,7 +6,7 @@ require 'digest/sha1'
6
6
  class CachingTest < Minitest::Test
7
7
  HTTP = Syntropy::HTTP
8
8
 
9
- APP_ROOT = File.join(__dir__, 'app')
9
+ APP_ROOT = File.join(__dir__, 'fixtures/app')
10
10
 
11
11
  def make_socket_pair
12
12
  port = SecureRandom.random_number(10000..40000)
@@ -32,7 +32,7 @@ class CachingTest < Minitest::Test
32
32
 
33
33
  @env = {
34
34
  machine: @machine,
35
- root_dir: APP_ROOT,
35
+ app_root: APP_ROOT,
36
36
  mount_path: '/test',
37
37
  watch_files: 0.05
38
38
  }
@@ -77,7 +77,7 @@ class DBSchemaTest < Minitest::Test
77
77
 
78
78
  def test_schema_from_module_files
79
79
  module_loader = Syntropy::ModuleLoader.new({
80
- root_dir: File.join(__dir__, 'schema')
80
+ app_root: File.join(__dir__, 'fixtures/schema')
81
81
  })
82
82
  schema = Syntropy::DB::Schema.new(module_loader:, schema_root: '/')
83
83
 
@@ -454,7 +454,7 @@ class HTTPServerConnectionTest < Minitest::Test
454
454
  write_client_side("GET / HTTP/1.1\r\n\r\n")
455
455
  @machine.spin do
456
456
  @connection.serve_request
457
- rescue => e
457
+ rescue StandardError => e
458
458
  p e
459
459
  p e.backtrace
460
460
  end
@@ -479,7 +479,7 @@ class HTTPServerConnectionTest < Minitest::Test
479
479
 
480
480
  @hook = ->(req) do
481
481
  req.respond_with_static_file(fn, nil, nil, nil)
482
- rescue => e
482
+ rescue StandardError => e
483
483
  p e
484
484
  p e.backtrace
485
485
  end
@@ -490,7 +490,7 @@ class HTTPServerConnectionTest < Minitest::Test
490
490
  write_client_side("GET / HTTP/1.1\r\n\r\n")
491
491
  @machine.spin do
492
492
  @connection.serve_request
493
- rescue => e
493
+ rescue StandardError => e
494
494
  p e
495
495
  p e.backtrace
496
496
  end
@@ -5,8 +5,8 @@ require_relative 'helper'
5
5
  class ModuleTest < Minitest::Test
6
6
  def setup
7
7
  @machine = UM.new
8
- @root = File.join(__dir__, 'app')
9
- @env = { root_dir: @root, baz: 42, machine: @machine, app: 42 }
8
+ @root = File.join(__dir__, 'fixtures/app')
9
+ @env = { app_root: @root, baz: 42, machine: @machine, app: 42 }
10
10
  @loader = Syntropy::ModuleLoader.new(@env)
11
11
  end
12
12
 
@@ -109,5 +109,8 @@ class ModuleTest < Minitest::Test
109
109
 
110
110
  list = @loader.list('mod/bar')
111
111
  assert_equal ['mod/bar/index+'], list
112
+
113
+ list = @loader.list('non-existent')
114
+ assert_equal [], list
112
115
  end
113
116
  end
@@ -22,25 +22,6 @@ class RedirectTest < Minitest::Test
22
22
  end
23
23
  end
24
24
 
25
- class StaticFileResponeTest < Minitest::Test
26
- def setup
27
- @path = File.join(__dir__, 'helper.rb')
28
- @stat = File.stat(@path)
29
-
30
- @etag = Syntropy::StaticFileCaching.file_stat_to_etag(@stat)
31
- @last_modified = Syntropy::StaticFileCaching.file_stat_to_last_modified(@stat)
32
- @content = IO.read(@path)
33
- end
34
-
35
- def test_serve_file_non_existent
36
- r = Syntropy::MockAdapter.mock
37
- r.serve_file('foo.rb', base_path: __dir__)
38
- assert_equal [
39
- [:respond, r, nil, { ':status' => Syntropy::HTTP::NOT_FOUND }]
40
- ], r.adapter.calls
41
- end
42
- end
43
-
44
25
  class UpgradeTest < Minitest::Test
45
26
  def test_upgrade
46
27
  r = Syntropy::MockAdapter.mock