syntropy 0.10.1 → 0.12
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +1 -1
- data/Rakefile +1 -1
- data/TODO.md +180 -135
- data/bin/syntropy +8 -3
- data/cmd/setup/template/site/site/_layout/default.rb +1 -1
- data/lib/syntropy/app.rb +243 -96
- data/lib/syntropy/errors.rb +40 -12
- data/lib/syntropy/markdown.rb +4 -2
- data/lib/syntropy/module.rb +12 -13
- data/lib/syntropy/request_extensions.rb +112 -2
- data/lib/syntropy/routing_tree.rb +553 -0
- data/lib/syntropy/version.rb +1 -1
- data/lib/syntropy.rb +3 -1
- data/syntropy.gemspec +3 -2
- data/test/app/_layout/default.rb +1 -1
- data/test/app/params/[foo].rb +3 -0
- data/test/helper.rb +18 -2
- data/test/test_app.rb +17 -25
- data/test/test_errors.rb +38 -0
- data/test/test_request_extensions.rb +163 -0
- data/test/test_routing_tree.rb +427 -0
- metadata +24 -8
- data/lib/syntropy/router.rb +0 -245
- data/test/test_router.rb +0 -116
- data/test/test_validation.rb +0 -35
data/test/test_app.rb
CHANGED
@@ -13,11 +13,11 @@ class AppTest < Minitest::Test
|
|
13
13
|
@tmp_path = '/test/tmp'
|
14
14
|
@tmp_fn = File.join(APP_ROOT, 'tmp.rb')
|
15
15
|
|
16
|
-
@app = Syntropy::App.
|
17
|
-
|
18
|
-
location: APP_ROOT,
|
16
|
+
@app = Syntropy::App.new(
|
17
|
+
root_dir: APP_ROOT,
|
19
18
|
mount_path: '/test',
|
20
|
-
watch_files: 0.05
|
19
|
+
watch_files: 0.05,
|
20
|
+
machine: @machine
|
21
21
|
)
|
22
22
|
end
|
23
23
|
|
@@ -28,13 +28,17 @@ class AppTest < Minitest::Test
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def test_app_rendering
|
31
|
+
# puts "*" * 40
|
32
|
+
# pp @app.routing_tree.root
|
33
|
+
# puts
|
34
|
+
|
31
35
|
req = make_request(':method' => 'GET', ':path' => '/')
|
32
|
-
assert_equal 'Not found', req.response_body
|
33
36
|
assert_equal Status::NOT_FOUND, req.response_status
|
37
|
+
assert_equal 'Not found', req.response_body
|
34
38
|
|
35
39
|
req = make_request(':method' => 'HEAD', ':path' => '/')
|
36
|
-
assert_nil req.response_body
|
37
40
|
assert_equal Status::NOT_FOUND, req.response_status
|
41
|
+
assert_nil req.response_body
|
38
42
|
|
39
43
|
req = make_request(':method' => 'POST', ':path' => '/')
|
40
44
|
assert_equal 'Not found', req.response_body
|
@@ -52,21 +56,6 @@ class AppTest < Minitest::Test
|
|
52
56
|
assert_equal Status::METHOD_NOT_ALLOWED, req.response_status
|
53
57
|
assert_nil req.response_body
|
54
58
|
|
55
|
-
req = make_request(':method' => 'GET', ':path' => '/test/index')
|
56
|
-
assert_equal '<h1>Hello, world!</h1>', req.response_body
|
57
|
-
|
58
|
-
req = make_request(':method' => 'GET', ':path' => '/test/index.html')
|
59
|
-
assert_equal '<h1>Hello, world!</h1>', req.response_body
|
60
|
-
assert_equal 'text/html', req.response_headers['Content-Type']
|
61
|
-
|
62
|
-
req = make_request(':method' => 'HEAD', ':path' => '/test/index.html')
|
63
|
-
assert_nil req.response_body
|
64
|
-
assert_equal 'text/html', req.response_headers['Content-Type']
|
65
|
-
|
66
|
-
req = make_request(':method' => 'POST', ':path' => '/test/index.html')
|
67
|
-
assert_nil req.response_body
|
68
|
-
assert_equal Status::METHOD_NOT_ALLOWED, req.response_status
|
69
|
-
|
70
59
|
req = make_request(':method' => 'GET', ':path' => '/test/assets/style.css')
|
71
60
|
assert_equal '* { color: beige }', req.response_body
|
72
61
|
assert_equal 'text/css', req.response_headers['Content-Type']
|
@@ -126,17 +115,20 @@ class AppTest < Minitest::Test
|
|
126
115
|
|
127
116
|
req = make_request(':method' => 'GET', ':path' => '/test/about/foo/bar')
|
128
117
|
assert_equal Status::NOT_FOUND, req.response_status
|
118
|
+
|
119
|
+
req = make_request(':method' => 'GET', ':path' => '/test/params/abc')
|
120
|
+
assert_equal '/test/params/[foo]-abc', req.response_body.chomp
|
129
121
|
end
|
130
122
|
|
131
123
|
def test_app_file_watching
|
132
|
-
@machine.sleep 0.
|
124
|
+
@machine.sleep 0.3
|
133
125
|
|
134
126
|
req = make_request(':method' => 'GET', ':path' => @tmp_path)
|
135
127
|
assert_equal 'foo', req.response_body
|
136
128
|
|
137
129
|
orig_body = IO.read(@tmp_fn)
|
138
130
|
IO.write(@tmp_fn, orig_body.gsub('foo', 'bar'))
|
139
|
-
@machine.sleep(0.
|
131
|
+
@machine.sleep(0.3)
|
140
132
|
|
141
133
|
req = make_request(':method' => 'GET', ':path' => @tmp_path)
|
142
134
|
assert_equal 'bar', req.response_body
|
@@ -166,7 +158,7 @@ class CustomAppTest < Minitest::Test
|
|
166
158
|
@machine = UM.new
|
167
159
|
@app = Syntropy::App.load(
|
168
160
|
machine: @machine,
|
169
|
-
|
161
|
+
root_dir: APP_ROOT,
|
170
162
|
mount_path: '/'
|
171
163
|
)
|
172
164
|
end
|
@@ -193,7 +185,7 @@ class MultiSiteAppTest < Minitest::Test
|
|
193
185
|
@machine = UM.new
|
194
186
|
@app = Syntropy::App.load(
|
195
187
|
machine: @machine,
|
196
|
-
|
188
|
+
root_dir: APP_ROOT,
|
197
189
|
mount_path: '/'
|
198
190
|
)
|
199
191
|
end
|
data/test/test_errors.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
class ErrorsTest < Minitest::Test
|
6
|
+
ISE = Qeweney::Status::INTERNAL_SERVER_ERROR
|
7
|
+
|
8
|
+
def test_error_http_status_class_method
|
9
|
+
e = RuntimeError.new
|
10
|
+
assert_equal ISE, Syntropy::Error.http_status(e)
|
11
|
+
|
12
|
+
e = Syntropy::Error.new
|
13
|
+
assert_equal ISE, Syntropy::Error.http_status(e)
|
14
|
+
|
15
|
+
e = Syntropy::Error.new(Qeweney::Status::UNAUTHORIZED)
|
16
|
+
assert_equal Qeweney::Status::UNAUTHORIZED, Syntropy::Error.http_status(e)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_method_not_allowed_error
|
20
|
+
e = Syntropy::Error.method_not_allowed('foo')
|
21
|
+
assert_kind_of Syntropy::Error, e
|
22
|
+
assert_equal Qeweney::Status::METHOD_NOT_ALLOWED, e.http_status
|
23
|
+
assert_equal 'foo', e.message
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_not_found_error
|
27
|
+
e = Syntropy::Error.not_found('bar')
|
28
|
+
assert_kind_of Syntropy::Error, e
|
29
|
+
assert_equal Qeweney::Status::NOT_FOUND, e.http_status
|
30
|
+
assert_equal 'bar', e.message
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_validation_error
|
34
|
+
e = Syntropy::ValidationError.new('baz')
|
35
|
+
assert_equal Qeweney::Status::BAD_REQUEST, e.http_status
|
36
|
+
assert_equal 'baz', e.message
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
class MethodValidationTest < Minitest::Test
|
6
|
+
VE = Syntropy::ValidationError
|
7
|
+
|
8
|
+
def test_validate_http_method
|
9
|
+
@req = mock_req(':method' => 'GET', ':path' => '/foo')
|
10
|
+
|
11
|
+
assert_equal 'get', @req.validate_http_method('get', 'head')
|
12
|
+
assert_raises(Syntropy::Error) { @req.validate_http_method('post', 'put') }
|
13
|
+
assert_raises(Syntropy::Error) { @req.validate_http_method() }
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_respond_by_http_method
|
17
|
+
@req = mock_req(':method' => 'GET', ':path' => '/foo')
|
18
|
+
|
19
|
+
@req.respond_by_http_method(
|
20
|
+
'get' => ['GET foo', {}],
|
21
|
+
'post' => ['POST foo', {}]
|
22
|
+
)
|
23
|
+
assert_equal 'GET foo', @req.response_body
|
24
|
+
|
25
|
+
assert_raises(Syntropy::Error) {
|
26
|
+
@req.respond_by_http_method(
|
27
|
+
'post' => ['POST foo', {}]
|
28
|
+
)
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class ValidationTest < Minitest::Test
|
34
|
+
def setup
|
35
|
+
@req = mock_req(':method' => 'GET', ':path' => '/foo?q=foo&x=bar&y=123&z1=t&z2=f')
|
36
|
+
end
|
37
|
+
|
38
|
+
VE = Syntropy::ValidationError
|
39
|
+
|
40
|
+
def test_validate_param
|
41
|
+
assert_nil @req.validate_param(:azerty, nil)
|
42
|
+
assert_equal 'foo', @req.validate_param(:q)
|
43
|
+
assert_equal 'foo', @req.validate_param(:q, String)
|
44
|
+
assert_equal 'foo', @req.validate_param(:q, [String, nil])
|
45
|
+
assert_nil @req.validate_param(:r, [String, nil])
|
46
|
+
|
47
|
+
assert_equal 123, @req.validate_param(:y, Integer)
|
48
|
+
assert_equal 123, @req.validate_param(:y, Integer, 120..125)
|
49
|
+
assert_equal 123.0, @req.validate_param(:y, Float)
|
50
|
+
|
51
|
+
assert_equal true, @req.validate_param(:z1, :bool)
|
52
|
+
assert_equal false, @req.validate_param(:z2, :bool)
|
53
|
+
|
54
|
+
assert_raises(VE) { @req.validate_param(:azerty, String) }
|
55
|
+
assert_raises(VE) { @req.validate_param(:q, Integer) }
|
56
|
+
assert_raises(VE) { @req.validate_param(:q, Float) }
|
57
|
+
assert_raises(VE) { @req.validate_param(:q, nil) }
|
58
|
+
|
59
|
+
assert_raises(VE) { @req.validate_param(:y, Integer, 1..100) }
|
60
|
+
|
61
|
+
assert_raises(VE) { @req.validate_param(:y, :bool) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_validate
|
65
|
+
assert_equal 'foo', @req.validate('foo')
|
66
|
+
assert_raises(VE) { @req.validate(nil) }
|
67
|
+
|
68
|
+
assert_nil @req.validate(nil, nil)
|
69
|
+
assert_raises(VE) { @req.validate(1, nil) }
|
70
|
+
|
71
|
+
assert_equal 'foo', @req.validate('foo', String)
|
72
|
+
assert_equal 'foo', @req.validate('foo', [String, nil])
|
73
|
+
assert_nil @req.validate(nil, [String, nil])
|
74
|
+
|
75
|
+
assert_equal 123, @req.validate('123', Integer)
|
76
|
+
assert_raises(VE) { @req.validate('a123', Integer) }
|
77
|
+
assert_equal 123, @req.validate('123', Integer, 120..125)
|
78
|
+
assert_raises(VE) { @req.validate('223', Integer, 120..125) }
|
79
|
+
assert_equal 123.0, @req.validate('123.0', Float)
|
80
|
+
assert_equal 123.0, @req.validate('123', Float)
|
81
|
+
assert_raises(VE) { @req.validate('123.0', Integer) }
|
82
|
+
assert_raises(VE) { @req.validate('x123.0', Float) }
|
83
|
+
assert_equal 123.0, @req.validate('123.0', Float, 120..125)
|
84
|
+
assert_raises(VE) { @req.validate('223', Float, 120..125) }
|
85
|
+
|
86
|
+
assert_equal true, @req.validate('t', :bool)
|
87
|
+
assert_equal false, @req.validate('f', :bool)
|
88
|
+
assert_equal true, @req.validate('1', :bool)
|
89
|
+
assert_equal false, @req.validate('0', :bool)
|
90
|
+
assert_raises(VE) { @req.validate('foo', :bool) }
|
91
|
+
assert_raises(VE) { @req.validate(nil, :bool) }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class FormDataTest < Minitest::Test
|
96
|
+
def test_get_form_data_multipart
|
97
|
+
@req = mock_req({
|
98
|
+
':method' => 'POST',
|
99
|
+
':path' => '/',
|
100
|
+
'content-type' => 'multipart/form-data; boundary=87654321'
|
101
|
+
}, nil)
|
102
|
+
assert_raises(Syntropy::Error) { @req.get_form_data }
|
103
|
+
|
104
|
+
@req = mock_req({
|
105
|
+
':method' => 'POST',
|
106
|
+
':path' => '/',
|
107
|
+
'content-type' => 'multipart/form-data; boundary=87654321'
|
108
|
+
}, "foobar")
|
109
|
+
assert_raises(Syntropy::Error) { @req.get_form_data }
|
110
|
+
|
111
|
+
body = <<~EOF
|
112
|
+
--87654321
|
113
|
+
Content-Disposition: form-data; name="foo"
|
114
|
+
|
115
|
+
barbaz
|
116
|
+
--87654321
|
117
|
+
Content-Disposition: form-data; name="bar"
|
118
|
+
|
119
|
+
BARBAR
|
120
|
+
--87654321--
|
121
|
+
EOF
|
122
|
+
|
123
|
+
@req = mock_req({
|
124
|
+
':method' => 'POST',
|
125
|
+
':path' => '/',
|
126
|
+
'content-type' => 'multipart/form-data; boundary=87654321'
|
127
|
+
}, body.gsub("\n", "\r\n"))
|
128
|
+
|
129
|
+
data = @req.get_form_data
|
130
|
+
assert_equal ['bar', 'foo'], data.keys.sort
|
131
|
+
|
132
|
+
assert_equal 'barbaz', data['foo']
|
133
|
+
assert_equal 'BARBAR', data['bar']
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_get_form_data_urlencoded
|
137
|
+
@req = mock_req({
|
138
|
+
':method' => 'POST',
|
139
|
+
':path' => '/',
|
140
|
+
'content-type' => 'application/x-www-form-urlencoded'
|
141
|
+
}, nil)
|
142
|
+
assert_raises(Syntropy::Error) { @req.get_form_data }
|
143
|
+
|
144
|
+
@req = mock_req({
|
145
|
+
':method' => 'POST',
|
146
|
+
':path' => '/',
|
147
|
+
'content-type' => 'application/x-www-form-urlencoded'
|
148
|
+
}, 'abc')
|
149
|
+
data = @req.get_form_data
|
150
|
+
assert_equal ['abc'], data.keys
|
151
|
+
assert_equal true, data['abc']
|
152
|
+
|
153
|
+
@req = mock_req({
|
154
|
+
':method' => 'POST',
|
155
|
+
':path' => '/',
|
156
|
+
'content-type' => 'application/x-www-form-urlencoded'
|
157
|
+
}, 'foo=bar&bar=baz')
|
158
|
+
data = @req.get_form_data
|
159
|
+
assert_equal ['bar', 'foo'], data.keys.sort
|
160
|
+
assert_equal 'bar', data['foo']
|
161
|
+
assert_equal 'baz', data['bar']
|
162
|
+
end
|
163
|
+
end
|