syntropy 0.38.1 → 0.40.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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/README.md +2 -0
  4. data/TODO.md +55 -144
  5. data/cmd/console.rb +2 -4
  6. data/cmd/new/template/config/production.rb +2 -1
  7. data/cmd/new.rb +1 -1
  8. data/cmd/serve.rb +29 -4
  9. data/cmd/test.rb +12 -4
  10. data/examples/agent/Dockerfile +25 -0
  11. data/examples/agent/Gemfile +3 -0
  12. data/examples/agent/README.md +40 -0
  13. data/examples/agent/TODO.md +5 -0
  14. data/examples/agent/app/_layout/default.rb +15 -0
  15. data/examples/agent/app/_lib/storage.rb +13 -0
  16. data/examples/agent/app/_schema/2026-01-01-initial.rb +9 -0
  17. data/examples/agent/app/assets/agent.js +63 -0
  18. data/examples/agent/app/assets/minigfm.js +207 -0
  19. data/examples/agent/app/assets/model.js +45 -0
  20. data/examples/agent/app/assets/style.css +25 -0
  21. data/examples/agent/app/assets/tools.js +74 -0
  22. data/examples/agent/app/assets/ui.js +50 -0
  23. data/examples/agent/app/index.rb +19 -0
  24. data/examples/agent/app/test.rb +7 -0
  25. data/examples/agent/config/Caddyfile +5 -0
  26. data/examples/agent/config/development.rb +5 -0
  27. data/examples/agent/config/production.rb +5 -0
  28. data/examples/agent/config/test.rb +5 -0
  29. data/examples/agent/docker-compose.yml +71 -0
  30. data/examples/agent/response_example.json +108 -0
  31. data/examples/agent/test/test_app.rb +14 -0
  32. data/examples/blog/app/foo.rb +1 -0
  33. data/examples/blog/app/posts/[id]/edit.rb +3 -3
  34. data/examples/blog/app/posts/[id]/index.rb +8 -10
  35. data/examples/blog/app/posts/index.rb +6 -8
  36. data/examples/blog/app/posts/new.rb +3 -3
  37. data/examples/blog/config/production.rb +2 -1
  38. data/lib/syntropy/app.rb +12 -60
  39. data/lib/syntropy/applets/builtin/auto_refresh/watch.js +1 -1
  40. data/lib/syntropy/applets/builtin/auto_refresh/watch.sse.rb +7 -0
  41. data/lib/syntropy/controller_extensions.rb +1 -1
  42. data/lib/syntropy/dev_mode.rb +8 -3
  43. data/lib/syntropy/http/client.rb +17 -1
  44. data/lib/syntropy/http/client_connection.rb +16 -0
  45. data/lib/syntropy/http/io_extensions.rb +32 -4
  46. data/lib/syntropy/http/server.rb +55 -24
  47. data/lib/syntropy/http/server_connection.rb +72 -2
  48. data/lib/syntropy/logger.rb +8 -0
  49. data/lib/syntropy/markdown.rb +139 -11
  50. data/lib/syntropy/module_loader.rb +110 -68
  51. data/lib/syntropy/request/request_info.rb +8 -0
  52. data/lib/syntropy/request/response.rb +76 -6
  53. data/lib/syntropy/request/validation.rb +6 -0
  54. data/lib/syntropy/storage/connection_pool.rb +34 -0
  55. data/lib/syntropy/test.rb +14 -5
  56. data/lib/syntropy/version.rb +1 -1
  57. data/lib/syntropy.rb +9 -2
  58. data/syntropy.gemspec +4 -4
  59. data/test/fixtures/app/_layout/default.rb +1 -1
  60. data/test/fixtures/app/_layout/kuku.rb +8 -0
  61. data/test/fixtures/app/_lib/circular/a.rb +2 -0
  62. data/test/fixtures/app/_lib/circular/b.rb +2 -0
  63. data/test/fixtures/app/_lib/circular/c.rb +2 -0
  64. data/test/fixtures/app/mod/concurrent.rb +9 -0
  65. data/test/test_app.rb +1 -1
  66. data/test/test_caching.rb +3 -3
  67. data/test/test_markdown.rb +268 -0
  68. data/test/test_module_loader.rb +32 -4
  69. data/test/test_request.rb +7 -0
  70. data/test/test_response.rb +30 -0
  71. data/test/test_schema.rb +1 -0
  72. metadata +40 -11
@@ -0,0 +1,268 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+ require 'securerandom'
5
+
6
+ class MarkdownParseTest < Minitest::Test
7
+ def test_markdown_parse_md
8
+ md = <<~MD
9
+ ---
10
+ foo: bar
11
+ ---
12
+ foo *bar*
13
+ MD
14
+
15
+ h = {}
16
+ atts, md2 = Syntropy::Markdown.parse_md(h, md)
17
+
18
+ assert_equal h, atts
19
+ assert_equal({ foo: 'bar' }, atts)
20
+ assert_equal "foo *bar*\n", md2
21
+ end
22
+
23
+ def test_markdown_parse_file
24
+ FileUtils.mkdir_p('/tmp/test')
25
+ name = "2008-06-14-test-#{SecureRandom.hex(8)}"
26
+ path = "/tmp/test/#{name}.md"
27
+ IO.write(
28
+ path,
29
+ <<~MD
30
+ ---
31
+ bar: baz
32
+ ---
33
+ bar *baz*
34
+ MD
35
+ )
36
+
37
+ env = { app_root: '/' }
38
+ atts, md = Syntropy::Markdown.parse_file(path, env)
39
+ assert_equal "/tmp/test/#{name}", atts[:url]
40
+ assert_equal 'baz', atts[:bar]
41
+ assert_equal "bar *baz*\n", md
42
+
43
+ env = { app_root: '/tmp' }
44
+ atts, md = Syntropy::Markdown.parse_file(path, env)
45
+ assert_equal "/test/#{name}", atts[:url]
46
+ assert_equal 'baz', atts[:bar]
47
+ assert_equal "bar *baz*\n", md
48
+ end
49
+ end
50
+
51
+ class MarkdownControllerTest < Minitest::Test
52
+ HTTP = Syntropy::HTTP
53
+
54
+ def setup
55
+ @machine = UM.new
56
+ @root = File.join(__dir__, 'fixtures/app')
57
+ @env = { app_root: @root, machine: @machine }
58
+ @env[:module_loader] = Syntropy::ModuleLoader.new(@env)
59
+
60
+ @controller = nil
61
+ @test_harness = Syntropy::TestHarness.new(->(req) { @controller.(req) })
62
+ end
63
+
64
+ def test_markdown_renderer_no_layout
65
+ md = <<~MD
66
+ foo *bar*
67
+ MD
68
+
69
+ atts = { foo: 'bar', title: 'Title' }
70
+
71
+ @controller = Syntropy::Markdown.make_controller(@env, atts, md)
72
+ req = @test_harness.request(':method' => 'GET', ':path' => '/')
73
+
74
+ assert_equal HTTP::OK, req.response_status
75
+ assert_equal(
76
+ "<!DOCTYPE html><html><head><title>Title</title></head><body><p>foo <em>bar</em></p>\n</body></html>",
77
+ req.response_body
78
+ )
79
+ end
80
+
81
+ def test_markdown_renderer_with_layout
82
+ src = <<~MD
83
+ ---
84
+ layout: kuku
85
+ ---
86
+ ## Bar
87
+ MD
88
+
89
+ atts, md = Syntropy::Markdown.parse_md({}, src)
90
+ assert_equal 'kuku', atts[:layout]
91
+
92
+ @controller = Syntropy::Markdown.make_controller(@env, atts, md)
93
+ req = @test_harness.request(':method' => 'GET', ':path' => '/')
94
+
95
+ assert_equal HTTP::OK, req.response_status
96
+ assert_equal(
97
+ "<header><h1>Kuku</h1></header><content><h2 id=\"bar\">Bar</h2>\n</content>",
98
+ req.response_body
99
+ )
100
+ end
101
+
102
+ def test_markdown_renderer_with_missing_module_loader
103
+ src = <<~MD
104
+ ---
105
+ layout: kuku
106
+ ---
107
+ ## Bar
108
+ MD
109
+
110
+ @env[:module_loader] = nil
111
+ atts, md = Syntropy::Markdown.parse_md({}, src)
112
+ assert_equal 'kuku', atts[:layout]
113
+
114
+ @controller = Syntropy::Markdown.make_controller(@env, atts, md)
115
+ assert_raises(Syntropy::Error) {
116
+ req = @test_harness.request(':method' => 'GET', ':path' => '/')
117
+ }
118
+ end
119
+
120
+ def test_markdown_renderer_with_invalid_layout
121
+ src = <<~MD
122
+ ---
123
+ layout: nunu
124
+ ---
125
+ ## Bar
126
+ MD
127
+
128
+ atts, md = Syntropy::Markdown.parse_md({}, src)
129
+ assert_equal 'nunu', atts[:layout]
130
+
131
+ @controller = Syntropy::Markdown.make_controller(@env, atts, md)
132
+ assert_raises(Syntropy::Error) {
133
+ req = @test_harness.request(':method' => 'GET', ':path' => '/')
134
+ }
135
+ end
136
+
137
+ def test_markdown_renderer_with_code_snipet
138
+ md = <<~MD
139
+ foo
140
+
141
+ ```ruby
142
+ foo { bar }
143
+ ```
144
+
145
+ bar
146
+ MD
147
+
148
+ atts = { foo: 'bar' }
149
+
150
+ @controller = Syntropy::Markdown.make_controller(@env, atts, md)
151
+ req = @test_harness.request(':method' => 'GET', ':path' => '/')
152
+
153
+ assert_equal HTTP::OK, req.response_status
154
+ assert_equal(
155
+ (
156
+ <<~HTML.chomp
157
+ <!DOCTYPE html><html><head></head><body><p>foo</p>
158
+
159
+ <div class=\"language-ruby highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">foo</span> <span class=\"p\">{</span> <span class=\"n\">bar</span> <span class=\"p\">}</span>
160
+ </code></pre></div></div>
161
+
162
+ <p>bar</p>
163
+ </body></html>
164
+ HTML
165
+ ),
166
+ req.response_body
167
+ )
168
+ end
169
+
170
+ def test_markdown_renderer_with_embedded_papercraft_simple
171
+ md = <<~MD
172
+ foo
173
+
174
+ ```ruby
175
+ # render: true
176
+ foo { bar }
177
+ ```
178
+
179
+ bar
180
+ MD
181
+
182
+ atts = { foo: 'bar' }
183
+
184
+ @controller = Syntropy::Markdown.make_controller(@env, atts, md)
185
+ req = @test_harness.request(':method' => 'GET', ':path' => '/')
186
+
187
+ assert_equal HTTP::OK, req.response_status
188
+ assert_equal(
189
+ (
190
+ <<~HTML.chomp
191
+ <!DOCTYPE html><html><head></head><body><p>foo</p>
192
+
193
+ <foo><bar></bar></foo>
194
+ <p>bar</p>
195
+ </body></html>
196
+ HTML
197
+ ),
198
+ req.response_body
199
+ )
200
+ end
201
+
202
+ def test_markdown_renderer_with_embedded_papercraft_env
203
+ md = <<~MD
204
+ ```ruby
205
+ # render: true
206
+ h3 @env[:app_root]
207
+ ```
208
+ bar
209
+ MD
210
+
211
+ atts = { foo: 'bar' }
212
+
213
+ @controller = Syntropy::Markdown.make_controller(@env, atts, md)
214
+ req = @test_harness.request(':method' => 'GET', ':path' => '/')
215
+
216
+ assert_equal HTTP::OK, req.response_status
217
+ assert_equal(
218
+ (
219
+ <<~HTML.chomp
220
+ <!DOCTYPE html><html><head></head><body><h3>#{@env[:app_root]}</h3>
221
+ <p>bar</p>
222
+ </body></html>
223
+ HTML
224
+ ),
225
+ req.response_body
226
+ )
227
+ end
228
+
229
+ def test_markdown_renderer_with_embedded_papercraft_dynamic
230
+ md = <<~MD
231
+ ```ruby
232
+ # render: true
233
+ @count ||= 0
234
+ h3 (@count += 1)
235
+ ```
236
+ bar
237
+ MD
238
+
239
+ atts = { foo: 'bar' }
240
+
241
+ @controller = Syntropy::Markdown.make_controller(@env, atts, md)
242
+ req = @test_harness.request(':method' => 'GET', ':path' => '/')
243
+ assert_equal HTTP::OK, req.response_status
244
+ assert_equal(
245
+ (
246
+ <<~HTML.chomp
247
+ <!DOCTYPE html><html><head></head><body><h3>1</h3>
248
+ <p>bar</p>
249
+ </body></html>
250
+ HTML
251
+ ),
252
+ req.response_body
253
+ )
254
+
255
+ req = @test_harness.request(':method' => 'GET', ':path' => '/')
256
+ assert_equal HTTP::OK, req.response_status
257
+ assert_equal(
258
+ (
259
+ <<~HTML.chomp
260
+ <!DOCTYPE html><html><head></head><body><h3>2</h3>
261
+ <p>bar</p>
262
+ </body></html>
263
+ HTML
264
+ ),
265
+ req.response_body
266
+ )
267
+ end
268
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative 'helper'
4
4
 
5
- class ModuleTest < Minitest::Test
5
+ class ModuleLoaderTest < Minitest::Test
6
6
  def setup
7
7
  @machine = UM.new
8
8
  @root = File.join(__dir__, 'fixtures/app')
@@ -16,7 +16,10 @@ class ModuleTest < Minitest::Test
16
16
  assert_equal 42, mod.bar
17
17
 
18
18
  assert_raises(Syntropy::Error) { @loader.load('_lib/missing-module') }
19
- assert_raises(Syntropy::Error) { @loader.load('_lib/missing-export') }
19
+
20
+ mod = @loader.load('_lib/missing-export')
21
+ assert_kind_of Syntropy::ModuleContext, mod
22
+ assert_equal :bar, mod.call
20
23
 
21
24
  mod = @loader.load('_lib/callable')
22
25
  assert_kind_of Syntropy::ModuleContext, mod
@@ -81,7 +84,7 @@ class ModuleTest < Minitest::Test
81
84
 
82
85
  def test_list
83
86
  list = @loader.list('_layout')
84
- assert_equal ['_layout/default'], list
87
+ assert_equal ['_layout/default', '_layout/kuku'], list
85
88
 
86
89
  list = @loader.list('_lib')
87
90
  assert_equal [
@@ -104,7 +107,7 @@ class ModuleTest < Minitest::Test
104
107
  assert_equal [], list
105
108
 
106
109
  list = @loader.list('mod')
107
- assert_equal [], list
110
+ assert_equal ['mod/concurrent'], list
108
111
 
109
112
  list = @loader.list('mod/bar')
110
113
  assert_equal ['mod/bar/index+'], list
@@ -112,6 +115,31 @@ class ModuleTest < Minitest::Test
112
115
  list = @loader.list('non-existent')
113
116
  assert_equal [], list
114
117
  end
118
+
119
+ def test_circular_dependency
120
+ assert_raises(Syntropy::Error) { @loader.load('_lib/circular/a') }
121
+ assert_raises(Syntropy::Error) { @loader.load('_lib/circular/b') }
122
+ assert_raises(Syntropy::Error) { @loader.load('_lib/circular/c') }
123
+ end
124
+
125
+ def test_concurrent_import
126
+ c1 = nil
127
+ c2 = nil
128
+ bar = nil
129
+
130
+ @env[:concurrent_counter] = { count: 0 }
131
+
132
+ @machine.join(
133
+ @machine.spin { c1 = @loader.load('mod/concurrent') },
134
+ @machine.spin { c2 = @loader.load('mod/concurrent') },
135
+ @machine.spin { bar = @loader.load('mod/bar/index+') }
136
+ )
137
+
138
+ assert_equal c1, c2
139
+ assert_equal c1.id, c2.id
140
+
141
+ assert_kind_of Hash, bar.env
142
+ end
115
143
  end
116
144
 
117
145
 
data/test/test_request.rb CHANGED
@@ -113,6 +113,13 @@ class RequestInfoTest < Minitest::Test
113
113
  assert_equal URI.parse('/ho?a=b&c=d'), r.uri
114
114
  assert_equal 'https://foo.bar/ho?a=b&c=d', r.full_uri
115
115
  end
116
+
117
+ def test_rel
118
+ r = Syntropy::MockAdapter.mock(
119
+ ':path' => '/posts/42'
120
+ )
121
+ assert_equal '/posts', r.rel('..')
122
+ end
116
123
  end
117
124
 
118
125
  class ValidationTest < Minitest::Test
@@ -20,6 +20,36 @@ class RedirectTest < Minitest::Test
20
20
  [:respond, r, nil, {":status"=>301, "Location"=>"/bar"}]
21
21
  ], r.adapter.calls
22
22
  end
23
+
24
+ def test_redirect_relative_path
25
+ r = Syntropy::MockAdapter.mock(':path' => '/foo')
26
+ r.redirect('./bar')
27
+ assert_equal '/foo/bar', r.response_headers['Location']
28
+
29
+ r = Syntropy::MockAdapter.mock(':path' => '/foo/bar')
30
+ r.redirect('./baz')
31
+ assert_equal '/foo/bar/baz', r.response_headers['Location']
32
+
33
+ r = Syntropy::MockAdapter.mock(':path' => '/foo/bar')
34
+ r.redirect('../baz')
35
+ assert_equal '/foo/baz', r.response_headers['Location']
36
+
37
+ r = Syntropy::MockAdapter.mock(':path' => '/')
38
+ r.redirect('./baz')
39
+ assert_equal '/baz', r.response_headers['Location']
40
+
41
+ r = Syntropy::MockAdapter.mock(':path' => '/')
42
+ r.redirect('../baz')
43
+ assert_equal '/baz', r.response_headers['Location']
44
+
45
+ r = Syntropy::MockAdapter.mock(':path' => '/posts')
46
+ r.redirect('./42')
47
+ assert_equal '/posts/42', r.response_headers['Location']
48
+
49
+ r = Syntropy::MockAdapter.mock(':path' => '/posts/42')
50
+ r.redirect('..')
51
+ assert_equal '/posts', r.response_headers['Location']
52
+ end
23
53
  end
24
54
 
25
55
  class UpgradeTest < Minitest::Test
data/test/test_schema.rb CHANGED
@@ -77,6 +77,7 @@ class SchemaTest < Minitest::Test
77
77
 
78
78
  def test_schema_from_module_files
79
79
  module_loader = Syntropy::ModuleLoader.new({
80
+ machine: @machine,
80
81
  app_root: File.join(__dir__, 'fixtures/schema')
81
82
  })
82
83
  schema = Syntropy::Storage::Schema.new(module_loader:, schema_root: '/')
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: syntropy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.38.1
4
+ version: 0.40.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
@@ -15,42 +15,42 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '2.14'
18
+ version: 3.0.1
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: '2.14'
25
+ version: 3.0.1
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: papercraft
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: 3.2.0
32
+ version: 3.2.2
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 3.2.0
39
+ version: 3.2.2
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: uringmachine
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: 1.0.2
46
+ version: 1.0.4
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: 1.0.2
53
+ version: 1.0.4
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: json
56
56
  requirement: !ruby/object:Gem::Requirement
@@ -125,16 +125,16 @@ dependencies:
125
125
  name: rake
126
126
  requirement: !ruby/object:Gem::Requirement
127
127
  requirements:
128
- - - "~>"
128
+ - - ">="
129
129
  - !ruby/object:Gem::Version
130
- version: 13.3.1
130
+ version: '0'
131
131
  type: :development
132
132
  prerelease: false
133
133
  version_requirements: !ruby/object:Gem::Requirement
134
134
  requirements:
135
- - - "~>"
135
+ - - ">="
136
136
  - !ruby/object:Gem::Version
137
- version: 13.3.1
137
+ version: '0'
138
138
  - !ruby/object:Gem::Dependency
139
139
  name: solargraph
140
140
  requirement: !ruby/object:Gem::Requirement
@@ -191,6 +191,28 @@ files:
191
191
  - cmd/test.rb
192
192
  - cmd/version.rb
193
193
  - docker-compose.yml
194
+ - examples/agent/Dockerfile
195
+ - examples/agent/Gemfile
196
+ - examples/agent/README.md
197
+ - examples/agent/TODO.md
198
+ - examples/agent/app/_layout/default.rb
199
+ - examples/agent/app/_lib/storage.rb
200
+ - examples/agent/app/_schema/2026-01-01-initial.rb
201
+ - examples/agent/app/assets/agent.js
202
+ - examples/agent/app/assets/minigfm.js
203
+ - examples/agent/app/assets/model.js
204
+ - examples/agent/app/assets/style.css
205
+ - examples/agent/app/assets/tools.js
206
+ - examples/agent/app/assets/ui.js
207
+ - examples/agent/app/index.rb
208
+ - examples/agent/app/test.rb
209
+ - examples/agent/config/Caddyfile
210
+ - examples/agent/config/development.rb
211
+ - examples/agent/config/production.rb
212
+ - examples/agent/config/test.rb
213
+ - examples/agent/docker-compose.yml
214
+ - examples/agent/response_example.json
215
+ - examples/agent/test/test_app.rb
194
216
  - examples/basic/bad.rb
195
217
  - examples/basic/card.rb
196
218
  - examples/basic/counter.js
@@ -205,6 +227,7 @@ files:
205
227
  - examples/blog/app/_lib/storage.rb
206
228
  - examples/blog/app/_schema/2026-01-01-initial.rb
207
229
  - examples/blog/app/assets/style.css
230
+ - examples/blog/app/foo.rb
208
231
  - examples/blog/app/index.rb
209
232
  - examples/blog/app/posts/[id]/edit.rb
210
233
  - examples/blog/app/posts/[id]/index.rb
@@ -285,7 +308,11 @@ files:
285
308
  - test/fixtures/app/.well-known/foo.rb
286
309
  - test/fixtures/app/_hook.rb
287
310
  - test/fixtures/app/_layout/default.rb
311
+ - test/fixtures/app/_layout/kuku.rb
288
312
  - test/fixtures/app/_lib/callable.rb
313
+ - test/fixtures/app/_lib/circular/a.rb
314
+ - test/fixtures/app/_lib/circular/b.rb
315
+ - test/fixtures/app/_lib/circular/c.rb
289
316
  - test/fixtures/app/_lib/dep.rb
290
317
  - test/fixtures/app/_lib/env.rb
291
318
  - test/fixtures/app/_lib/klass.rb
@@ -307,6 +334,7 @@ files:
307
334
  - test/fixtures/app/http.rb
308
335
  - test/fixtures/app/index.html
309
336
  - test/fixtures/app/mod/bar/index+.rb
337
+ - test/fixtures/app/mod/concurrent.rb
310
338
  - test/fixtures/app/mod/foo/index.rb
311
339
  - test/fixtures/app/mod/path/a.rb
312
340
  - test/fixtures/app/mod/path/b.rb
@@ -360,6 +388,7 @@ files:
360
388
  - test/test_http_server_connection.rb
361
389
  - test/test_json_api.rb
362
390
  - test/test_kv_store.rb
391
+ - test/test_markdown.rb
363
392
  - test/test_mock_adapter.rb
364
393
  - test/test_module_loader.rb
365
394
  - test/test_request.rb