tynn 2.0.0.alpha → 2.0.0.beta1
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/README.md +168 -29
- data/lib/tynn.rb +0 -4
- data/lib/tynn/base.rb +368 -24
- data/lib/tynn/errors.rb +7 -0
- data/lib/tynn/json.rb +18 -5
- data/lib/tynn/render.rb +36 -25
- data/lib/tynn/request.rb +86 -15
- data/lib/tynn/response.rb +214 -12
- data/lib/tynn/secure_headers.rb +2 -6
- data/lib/tynn/session.rb +14 -5
- data/lib/tynn/ssl.rb +19 -16
- data/lib/tynn/test.rb +45 -38
- data/lib/tynn/utils.rb +15 -0
- data/lib/tynn/version.rb +1 -1
- data/test/default_headers_test.rb +1 -1
- data/test/environment_test.rb +1 -1
- data/test/helper.rb +9 -0
- data/test/json_test.rb +21 -6
- data/test/middleware_test.rb +23 -13
- data/test/plugin_test.rb +1 -1
- data/test/render_test.rb +24 -15
- data/test/request_headers_test.rb +8 -4
- data/test/request_test.rb +9 -0
- data/test/response_test.rb +217 -0
- data/test/routing_test.rb +128 -38
- data/test/secure_headers_test.rb +1 -1
- data/test/session_test.rb +6 -6
- data/test/settings_test.rb +3 -3
- data/test/ssl_test.rb +3 -3
- data/test/static_test.rb +1 -1
- metadata +14 -24
- data/lib/tynn/default_headers.rb +0 -50
- data/lib/tynn/settings.rb +0 -107
data/test/plugin_test.rb
CHANGED
data/test/render_test.rb
CHANGED
@@ -2,20 +2,17 @@
|
|
2
2
|
|
3
3
|
require_relative "helper"
|
4
4
|
require_relative "../lib/tynn/render"
|
5
|
-
require "tilt/erubis"
|
6
5
|
|
7
6
|
class RenderTest < Minitest::Test
|
8
|
-
VIEWS_PATH = File.expand_path("views", __dir__)
|
9
|
-
|
10
7
|
def setup
|
11
|
-
@app =
|
8
|
+
@app = new_app
|
12
9
|
|
13
|
-
@app.plugin(Tynn::Render,
|
10
|
+
@app.plugin(Tynn::Render, root: __dir__)
|
14
11
|
end
|
15
12
|
|
16
13
|
def test_partial
|
17
14
|
@app.define do
|
18
|
-
on
|
15
|
+
on "partial" do
|
19
16
|
res.write(partial("partial", name: "alice"))
|
20
17
|
end
|
21
18
|
end
|
@@ -23,12 +20,12 @@ class RenderTest < Minitest::Test
|
|
23
20
|
ts = Tynn::Test.new(@app)
|
24
21
|
ts.get("/partial")
|
25
22
|
|
26
|
-
assert_equal "alice", ts.res.body.strip
|
23
|
+
assert_equal "alice", ts.res.body.join.strip
|
27
24
|
end
|
28
25
|
|
29
26
|
def test_view
|
30
27
|
@app.define do
|
31
|
-
on
|
28
|
+
on "view" do
|
32
29
|
res.write(view("view", title: "welcome", name: "alice"))
|
33
30
|
end
|
34
31
|
end
|
@@ -36,12 +33,25 @@ class RenderTest < Minitest::Test
|
|
36
33
|
ts = Tynn::Test.new(@app)
|
37
34
|
ts.get("/view")
|
38
35
|
|
39
|
-
assert_equal "welcome / alice", ts.res.body.strip
|
36
|
+
assert_equal "welcome / alice", ts.res.body.join.strip
|
40
37
|
end
|
41
38
|
|
42
39
|
def test_render
|
43
40
|
@app.define do
|
44
|
-
on
|
41
|
+
on "render" do
|
42
|
+
render("view", title: "welcome", name: "alice")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
ts = Tynn::Test.new(@app)
|
47
|
+
ts.get("/render")
|
48
|
+
|
49
|
+
assert_equal "welcome / alice", ts.res.body.join.strip
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_render_content_type
|
53
|
+
@app.define do
|
54
|
+
on "render" do
|
45
55
|
render("view", title: "welcome", name: "alice")
|
46
56
|
end
|
47
57
|
end
|
@@ -50,12 +60,11 @@ class RenderTest < Minitest::Test
|
|
50
60
|
ts.get("/render")
|
51
61
|
|
52
62
|
assert_equal "text/html", ts.res.content_type
|
53
|
-
assert_equal "welcome / alice", ts.res.body.strip
|
54
63
|
end
|
55
64
|
|
56
65
|
def test_escaping
|
57
66
|
@app.define do
|
58
|
-
on
|
67
|
+
on "escape" do
|
59
68
|
res.write(partial("partial", name: "<a></a>"))
|
60
69
|
end
|
61
70
|
end
|
@@ -63,14 +72,14 @@ class RenderTest < Minitest::Test
|
|
63
72
|
ts = Tynn::Test.new(@app)
|
64
73
|
ts.get("/escape")
|
65
74
|
|
66
|
-
assert_equal "<a><
|
75
|
+
assert_equal "<a></a>", ts.res.body.join.strip
|
67
76
|
end
|
68
77
|
|
69
78
|
def test_custom_layout
|
70
79
|
@app.set(:render, layout: "custom_layout")
|
71
80
|
|
72
81
|
@app.define do
|
73
|
-
get do
|
82
|
+
on get do
|
74
83
|
render("view", title: "welcome", name: "alice")
|
75
84
|
end
|
76
85
|
end
|
@@ -78,6 +87,6 @@ class RenderTest < Minitest::Test
|
|
78
87
|
ts = Tynn::Test.new(@app)
|
79
88
|
ts.get("/")
|
80
89
|
|
81
|
-
assert_equal "custom / welcome / alice", ts.res.body.strip
|
90
|
+
assert_equal "custom / welcome / alice", ts.res.body.join.strip
|
82
91
|
end
|
83
92
|
end
|
@@ -4,14 +4,18 @@ require_relative "helper"
|
|
4
4
|
|
5
5
|
class RequestHeadersTest < Minitest::Test
|
6
6
|
def setup
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
@headers = Tynn::Request::Headers.new(
|
8
|
+
"HTTP_HOST" => "127.0.0.1",
|
9
|
+
"CONTENT_TYPE" => "text/plain"
|
10
|
+
)
|
11
11
|
end
|
12
12
|
|
13
13
|
def test_get_header
|
14
14
|
assert_equal "127.0.0.1", @headers["Host"]
|
15
|
+
assert_equal "127.0.0.1", @headers["host"]
|
16
|
+
assert_equal "text/plain", @headers["Content-Type"]
|
17
|
+
assert_equal "text/plain", @headers["content-type"]
|
18
|
+
assert_equal "text/plain", @headers["CONTENT_TYPE"]
|
15
19
|
end
|
16
20
|
|
17
21
|
def test_check_if_key_exists
|
@@ -0,0 +1,217 @@
|
|
1
|
+
require_relative "helper"
|
2
|
+
|
3
|
+
class ResponseTest < Minitest::Test
|
4
|
+
def test_new
|
5
|
+
res = Tynn::Response.new
|
6
|
+
|
7
|
+
assert_nil res.status
|
8
|
+
assert_empty res.headers
|
9
|
+
assert_empty res.body
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_new_with_headers
|
13
|
+
res = Tynn::Response.new("Content-Type" => "text/plain")
|
14
|
+
|
15
|
+
assert_equal "text/plain", res.headers["Content-Type"]
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_status
|
19
|
+
res = Tynn::Response.new
|
20
|
+
|
21
|
+
res.status = 200
|
22
|
+
|
23
|
+
assert_equal 200, res.status
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_headers
|
27
|
+
res = Tynn::Response.new
|
28
|
+
|
29
|
+
res.headers["Content-Type"] = "text/plain"
|
30
|
+
|
31
|
+
assert_equal "text/plain", res.headers["Content-Type"]
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_content_length
|
35
|
+
res = Tynn::Response.new
|
36
|
+
|
37
|
+
res.headers["Content-Length"] = "42"
|
38
|
+
|
39
|
+
assert_equal 42, res.content_length
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_content_type
|
43
|
+
res = Tynn::Response.new
|
44
|
+
|
45
|
+
res.content_type = "text/html"
|
46
|
+
|
47
|
+
assert_equal "text/html", res.headers["Content-Type"]
|
48
|
+
assert_equal "text/html", res.content_type
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_location
|
52
|
+
res = Tynn::Response.new
|
53
|
+
|
54
|
+
res.location = "/users"
|
55
|
+
|
56
|
+
assert_equal "/users", res.headers["Location"]
|
57
|
+
assert_equal "/users", res.location
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_write_body
|
61
|
+
res = Tynn::Response.new
|
62
|
+
|
63
|
+
res.write("foo")
|
64
|
+
res.write("bar")
|
65
|
+
|
66
|
+
assert_equal ["foo", "bar"], res.body
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_write_content_length
|
70
|
+
res = Tynn::Response.new
|
71
|
+
|
72
|
+
res.write("foo")
|
73
|
+
res.write("bar")
|
74
|
+
|
75
|
+
assert_equal "6", res.headers["Content-Length"]
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_finish_without_status_and_body
|
79
|
+
res = Tynn::Response.new
|
80
|
+
|
81
|
+
status, _, body = res.finish
|
82
|
+
|
83
|
+
assert_equal 404, status
|
84
|
+
assert_empty body
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_finish_without_status_but_body
|
88
|
+
res = Tynn::Response.new
|
89
|
+
|
90
|
+
res.write("hei!")
|
91
|
+
|
92
|
+
status, _, body = res.finish
|
93
|
+
|
94
|
+
assert_equal 200, status
|
95
|
+
assert_equal ["hei!"], body
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_redirect_with_path
|
99
|
+
res = Tynn::Response.new
|
100
|
+
|
101
|
+
res.redirect("/path")
|
102
|
+
|
103
|
+
assert_equal 302, res.status
|
104
|
+
assert_equal "/path", res.location
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_redirect_with_url
|
108
|
+
res = Tynn::Response.new
|
109
|
+
|
110
|
+
res.redirect("https://google.com")
|
111
|
+
|
112
|
+
assert_equal 302, res.status
|
113
|
+
assert_equal "https://google.com", res.location
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_redirect_with_status
|
117
|
+
res = Tynn::Response.new
|
118
|
+
|
119
|
+
res.redirect("/path", 303)
|
120
|
+
|
121
|
+
assert_equal 303, res.status
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_set_cookie
|
125
|
+
res = Tynn::Response.new
|
126
|
+
|
127
|
+
res.set_cookie("foo", "bar")
|
128
|
+
|
129
|
+
assert_equal "foo=bar", res.headers["Set-Cookie"]
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_set_multiple_cookies
|
133
|
+
res = Tynn::Response.new
|
134
|
+
|
135
|
+
res.set_cookie("foo1", "bar1")
|
136
|
+
res.set_cookie("foo2", "bar2")
|
137
|
+
|
138
|
+
assert_equal "foo1=bar1\nfoo2=bar2", res.headers["Set-Cookie"]
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_set_cookie_with_options
|
142
|
+
res = Tynn::Response.new
|
143
|
+
|
144
|
+
time = Time.new(2016)
|
145
|
+
|
146
|
+
res.set_cookie("foo", "bar", {
|
147
|
+
domain: "example.org",
|
148
|
+
path: "/path",
|
149
|
+
max_age: 100, # seconds
|
150
|
+
expires: time,
|
151
|
+
httponly: true,
|
152
|
+
secure: true,
|
153
|
+
same_site: :Lax
|
154
|
+
})
|
155
|
+
|
156
|
+
expected = %W(
|
157
|
+
foo=bar
|
158
|
+
domain=example.org
|
159
|
+
path=/path
|
160
|
+
max-age=100
|
161
|
+
expires=#{ time.gmtime.rfc2822 }
|
162
|
+
secure
|
163
|
+
HttpOnly
|
164
|
+
SameSite=Lax
|
165
|
+
).join("; ")
|
166
|
+
|
167
|
+
assert_equal expected, res.headers["Set-Cookie"]
|
168
|
+
end
|
169
|
+
|
170
|
+
def test_delete_cookie
|
171
|
+
res = Tynn::Response.new
|
172
|
+
|
173
|
+
res.set_cookie("foo1", "bar1")
|
174
|
+
res.set_cookie("foo2", "bar2")
|
175
|
+
|
176
|
+
res.delete_cookie("foo1")
|
177
|
+
|
178
|
+
expected = [
|
179
|
+
"foo2=bar2",
|
180
|
+
"foo1=; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000"
|
181
|
+
].join("\n")
|
182
|
+
|
183
|
+
assert_equal expected, res.headers["Set-Cookie"]
|
184
|
+
end
|
185
|
+
|
186
|
+
def test_delete_cookie_with_multiple_domains
|
187
|
+
res = Tynn::Response.new
|
188
|
+
|
189
|
+
res.set_cookie("foo1", "bar1", domain: "foo1.example.org")
|
190
|
+
res.set_cookie("foo2", "bar2", domain: "foo2.example.org")
|
191
|
+
|
192
|
+
res.delete_cookie("foo1", domain: "foo1.example.org")
|
193
|
+
|
194
|
+
expected = [
|
195
|
+
"foo2=bar2; domain=foo2.example.org",
|
196
|
+
"foo1=; domain=foo1.example.org; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000"
|
197
|
+
].join("\n")
|
198
|
+
|
199
|
+
assert_equal expected, res.headers["Set-Cookie"]
|
200
|
+
end
|
201
|
+
|
202
|
+
def test_delete_cookie_with_same_domain_and_multiple_paths
|
203
|
+
res = Tynn::Response.new
|
204
|
+
|
205
|
+
res.set_cookie("foo1", "bar1", domain: "example.org", path: "/foo1")
|
206
|
+
res.set_cookie("foo2", "bar2", domain: "example.org", path: "/foo2")
|
207
|
+
|
208
|
+
res.delete_cookie("foo1", domain: "example.org", path: "/foo1")
|
209
|
+
|
210
|
+
expected = [
|
211
|
+
"foo2=bar2; domain=example.org; path=/foo2",
|
212
|
+
"foo1=; domain=example.org; path=/foo1; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000"
|
213
|
+
].join("\n")
|
214
|
+
|
215
|
+
assert_equal expected, res.headers["Set-Cookie"]
|
216
|
+
end
|
217
|
+
end
|
data/test/routing_test.rb
CHANGED
@@ -4,17 +4,31 @@ require_relative "helper"
|
|
4
4
|
|
5
5
|
class RoutingTest < Minitest::Test
|
6
6
|
def setup
|
7
|
-
@app =
|
7
|
+
@app = new_app
|
8
8
|
end
|
9
9
|
|
10
10
|
def test_raise_if_not_handler
|
11
|
-
assert_raises { @app.call({}) }
|
11
|
+
assert_raises(Tynn::Error) { @app.call({}) }
|
12
12
|
end
|
13
13
|
|
14
14
|
def test_path_matching
|
15
15
|
@app.define do
|
16
|
-
on "
|
17
|
-
res.write("
|
16
|
+
on "path" do
|
17
|
+
res.write("/path")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
ts = Tynn::Test.new(@app)
|
22
|
+
ts.get("/path")
|
23
|
+
|
24
|
+
assert_equal 200, ts.res.status
|
25
|
+
assert_equal "/path", ts.res.body.join
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_path_dont_match
|
29
|
+
@app.define do
|
30
|
+
on "private" do
|
31
|
+
res.write("nothing to see here")
|
18
32
|
end
|
19
33
|
end
|
20
34
|
|
@@ -22,90 +36,166 @@ class RoutingTest < Minitest::Test
|
|
22
36
|
ts.get("/")
|
23
37
|
|
24
38
|
assert_equal 404, ts.res.status
|
39
|
+
assert_empty ts.res.body
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_capture
|
43
|
+
@app.define do
|
44
|
+
on :foo do |foo|
|
45
|
+
on :bar do |bar|
|
46
|
+
res.write(sprintf("%s:%s", foo, bar))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
25
50
|
|
26
|
-
ts.
|
51
|
+
ts = Tynn::Test.new(@app)
|
52
|
+
ts.get("/foo/bar")
|
27
53
|
|
28
54
|
assert_equal 200, ts.res.status
|
29
|
-
assert_equal "
|
55
|
+
assert_equal "foo:bar", ts.res.body.join
|
30
56
|
end
|
31
57
|
|
32
|
-
def
|
33
|
-
|
58
|
+
def test_capture_root
|
59
|
+
@app.define do
|
60
|
+
on :foo do |foo|
|
61
|
+
res.write(foo)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
ts = Tynn::Test.new(@app)
|
66
|
+
ts.get("/")
|
34
67
|
|
35
|
-
|
36
|
-
|
37
|
-
|
68
|
+
assert_equal 404, ts.res.status
|
69
|
+
assert_empty ts.res.body
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_path_and_verbs
|
73
|
+
methods = %w(get post patch put delete)
|
74
|
+
|
75
|
+
@app.define do
|
76
|
+
methods.each do |method|
|
77
|
+
on method do
|
78
|
+
on send(method) do
|
79
|
+
res.write(method)
|
80
|
+
end
|
81
|
+
end
|
38
82
|
end
|
83
|
+
end
|
39
84
|
|
40
|
-
|
41
|
-
|
85
|
+
ts = Tynn::Test.new(@app)
|
86
|
+
|
87
|
+
methods.each do |method|
|
88
|
+
ts.send(method, "/#{ method }")
|
42
89
|
|
43
90
|
assert_equal 200, ts.res.status
|
44
|
-
assert_equal method
|
91
|
+
assert_equal method, ts.res.body.join
|
45
92
|
end
|
46
93
|
end
|
47
94
|
|
48
|
-
def
|
95
|
+
def test_verbs_on_root
|
49
96
|
@app.define do
|
50
|
-
on
|
51
|
-
|
52
|
-
res.write(
|
97
|
+
on "foo" do
|
98
|
+
get do
|
99
|
+
res.write("nothing to see here")
|
53
100
|
end
|
54
101
|
end
|
55
102
|
end
|
56
103
|
|
57
104
|
ts = Tynn::Test.new(@app)
|
58
|
-
ts.get("/
|
105
|
+
ts.get("/foo/bar/baz")
|
59
106
|
|
60
|
-
assert_equal
|
61
|
-
|
107
|
+
assert_equal 404, ts.res.status
|
108
|
+
assert_empty ts.res.body
|
62
109
|
end
|
63
110
|
|
64
111
|
def test_composition
|
65
|
-
|
112
|
+
admin = new_app
|
113
|
+
|
114
|
+
admin.define do
|
115
|
+
on get do
|
116
|
+
res.write("/admin")
|
117
|
+
end
|
118
|
+
end
|
66
119
|
|
67
120
|
@app.define do
|
68
|
-
on
|
69
|
-
run(
|
121
|
+
on "admin" do
|
122
|
+
run(admin)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
ts = Tynn::Test.new(@app)
|
127
|
+
ts.get("/admin")
|
128
|
+
|
129
|
+
assert_equal 200, ts.res.status
|
130
|
+
assert_equal "/admin", ts.res.body.join
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_composition_with_inbox
|
134
|
+
foo = new_app
|
135
|
+
bar = new_app
|
136
|
+
baz = new_app
|
137
|
+
|
138
|
+
@app.define do
|
139
|
+
on "foo" do
|
140
|
+
run(foo, foo: "foo")
|
70
141
|
end
|
71
142
|
end
|
72
143
|
|
73
144
|
foo.define do
|
74
|
-
|
75
|
-
|
145
|
+
on "bar" do
|
146
|
+
run(bar, inbox.merge(bar: "bar"))
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
bar.define do
|
151
|
+
on "baz" do
|
152
|
+
run(baz, inbox.merge(baz: "baz"))
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
baz.define do
|
157
|
+
on get do
|
158
|
+
res.write(sprintf("%{foo}/%{bar}/%{baz}", inbox))
|
76
159
|
end
|
77
160
|
end
|
78
161
|
|
79
162
|
ts = Tynn::Test.new(@app)
|
80
|
-
ts.get("/foo")
|
163
|
+
ts.get("/foo/bar/baz")
|
81
164
|
|
82
165
|
assert_equal 200, ts.res.status
|
83
|
-
assert_equal "
|
166
|
+
assert_equal "foo/bar/baz", ts.res.body.join
|
84
167
|
end
|
85
168
|
|
86
|
-
def
|
169
|
+
def test_run_rack_app
|
87
170
|
@app.define do
|
88
|
-
|
89
|
-
|
90
|
-
|
171
|
+
run(proc { |_|
|
172
|
+
[200, { "Content-Type" => "text/plain", "Content-Length" => "4" }, ["hei!"]]
|
173
|
+
})
|
91
174
|
end
|
92
175
|
|
93
176
|
ts = Tynn::Test.new(@app)
|
94
177
|
ts.get("/")
|
95
178
|
|
96
|
-
assert_equal
|
179
|
+
assert_equal 200, ts.res.status
|
180
|
+
assert_equal "hei!", ts.res.body.join
|
181
|
+
assert_equal 4, ts.res.content_length
|
182
|
+
assert_equal "text/plain", ts.res.content_type
|
97
183
|
end
|
98
184
|
|
99
|
-
def
|
185
|
+
def test_halt
|
100
186
|
@app.define do
|
101
|
-
|
102
|
-
|
103
|
-
|
187
|
+
res.status = 401
|
188
|
+
res.write("Unauthorized")
|
189
|
+
|
190
|
+
halt(res.finish)
|
191
|
+
|
192
|
+
raise "nothing to see here"
|
104
193
|
end
|
105
194
|
|
106
195
|
ts = Tynn::Test.new(@app)
|
107
196
|
ts.get("/")
|
108
197
|
|
109
|
-
assert_equal
|
198
|
+
assert_equal 401, ts.res.status
|
199
|
+
assert_equal "Unauthorized", ts.res.body.join
|
110
200
|
end
|
111
201
|
end
|