v2 / vlib / veb / tests / static_handler_test.v
236 lines · 185 sloc · 6.25 KB · 344b9afcfe67902dd9660bd3b077f18464d1d114
Raw
1// vtest vflags: -prod
2// vtest build: !windows // fasthttp.Server.run is not implemented on windows yet
3import veb
4import net.http
5import os
6import time
7
8const port = 13003
9
10const localserver = 'http://127.0.0.1:${port}'
11
12const exit_after = time.second * 10
13
14pub struct App {
15 veb.StaticHandler
16mut:
17 started chan bool
18}
19
20pub fn (mut app App) before_accept_loop() {
21 app.started <- true
22}
23
24@['/']
25pub fn (mut app App) index(mut ctx Context) veb.Result {
26 return ctx.text('Hello V!')
27}
28
29@['/redirect_root']
30pub fn (mut app App) redirect_root(mut ctx Context) veb.Result {
31 return ctx.redirect('/root.txt')
32}
33
34@[post]
35pub fn (mut app App) post_request(mut ctx Context) veb.Result {
36 return ctx.text(ctx.req.data)
37}
38
39pub struct Context {
40 veb.Context
41}
42
43fn testsuite_begin() {
44 os.chdir(os.dir(@FILE))!
45 spawn fn () {
46 time.sleep(exit_after)
47 assert true == false, 'timeout reached!'
48 exit(1)
49 }()
50
51 run_app_test()
52}
53
54fn run_app_test() {
55 mut app := &App{}
56 if _ := app.handle_static('testdata', true) {
57 assert true == false, 'should throw unknown mime type error'
58 } else {
59 assert err.msg().starts_with('unknown MIME type for file extension ".what"'), 'throws error on unknown mime type'
60 }
61
62 app.static_mime_types['.what'] = veb.mime_types['.txt']
63
64 if _ := app.handle_static('not_found', true) {
65 assert false, 'should throw directory not found error'
66 } else {
67 assert err.msg().starts_with('directory `not_found` does not exist') == true
68 }
69
70 app.handle_static('testdata', true) or { panic(err) }
71
72 // Enable markdown content negotiation for testing
73 app.enable_markdown_negotiation = true
74
75 if _ := app.mount_static_folder_at('testdata', 'static') {
76 assert true == false, 'should throw invalid mount path error'
77 } else {
78 assert err.msg() == 'invalid mount path! The path should start with `/`'
79 }
80
81 if _ := app.mount_static_folder_at('not_found', '/static') {
82 assert true == false, 'should throw mount path does not exist error'
83 } else {
84 assert err.msg().starts_with('directory `not_found` does not exist') == true
85 }
86
87 app.mount_static_folder_at('testdata', '/static') or { panic(err) }
88
89 spawn veb.run_at[App, Context](mut app, port: port, timeout_in_seconds: 2, family: .ip)
90 // app startup time
91 _ := <-app.started
92}
93
94fn test_static_root() {
95 x := http.get('${localserver}/root.txt')!
96
97 assert x.status() == .ok
98 assert x.body == 'root'
99}
100
101fn test_route_attribute_root_with_static_handler() {
102 x := http.get('${localserver}/')!
103
104 assert x.status() == .ok
105 assert x.body == 'Hello V!'
106}
107
108fn test_redirect_to_static_root() {
109 x := http.get('${localserver}/redirect_root')!
110
111 assert x.status() == .ok
112 assert x.body == 'root'
113}
114
115fn test_scans_subdirs() {
116 x := http.get('${localserver}/sub_folder/sub.txt')!
117
118 assert x.status() == .ok
119 assert x.body == 'sub'
120}
121
122fn test_index_subdirs() {
123 x := http.get('${localserver}/sub_folder/')!
124 y := http.get('${localserver}/sub.folder/sub_folder')!
125
126 assert x.status() == .ok
127 assert x.body.trim_space() == 'OK'
128
129 assert y.status() == .ok
130 assert y.body.trim_space() == 'OK'
131}
132
133fn test_custom_mime_types() {
134 x := http.get('${localserver}/unknown_mime.what')!
135
136 assert x.status() == .ok
137 assert x.header.get(.content_type)! == veb.mime_types['.txt']
138 assert x.body.trim_space() == 'unknown_mime'
139}
140
141fn test_custom_folder_mount() {
142 x := http.get('${localserver}/static/root.txt')!
143
144 assert x.status() == .ok
145 assert x.body == 'root'
146}
147
148fn test_upper_case_mime_type() {
149 x := http.get('${localserver}/upper_case.TXT')!
150
151 assert x.status() == .ok
152 assert x.body == 'body'
153}
154
155// Content negotiation tests - Priority order
156// Tests verify: path.md > path.html.md > path/index.html.md
157
158fn test_markdown_negotiation_priority_first() {
159 // When all three variants exist, path.md (priority 1) is served
160 config := http.FetchConfig{
161 url: '${localserver}/about'
162 header: http.new_header(key: .accept, value: 'text/markdown')
163 }
164 x := http.fetch(config)!
165
166 assert x.status() == .ok
167 assert x.header.get(.content_type)! == 'text/markdown'
168 assert x.body.contains('This is the about page in markdown format.')
169 assert !x.body.contains('about.html.md variant')
170 assert !x.body.contains('about/index.html.md variant')
171}
172
173fn test_markdown_negotiation_priority_second() {
174 // When only path.html.md exists (priority 2), it is served
175 config := http.FetchConfig{
176 url: '${localserver}/page'
177 header: http.new_header(key: .accept, value: 'text/markdown')
178 }
179 x := http.fetch(config)!
180
181 assert x.status() == .ok
182 assert x.header.get(.content_type)! == 'text/markdown'
183 assert x.body.contains('# Page HTML Markdown')
184}
185
186fn test_markdown_negotiation_directory_index() {
187 // For directories, index.html.md is served when Accept: text/markdown
188 config := http.FetchConfig{
189 url: '${localserver}/sub_folder/'
190 header: http.new_header(key: .accept, value: 'text/markdown')
191 }
192 x := http.fetch(config)!
193
194 assert x.status() == .ok
195 assert x.header.get(.content_type)! == 'text/markdown'
196 assert x.body.contains('# Index HTML Markdown')
197}
198
199// Direct access tests - Verifies backward compatibility
200
201fn test_markdown_direct_access() {
202 // Without Accept header
203 x_no_header := http.get('${localserver}/test.md')!
204 assert x_no_header.status() == .ok
205 assert x_no_header.header.get(.content_type)! == 'text/markdown'
206 assert x_no_header.body.contains('# Test Markdown')
207
208 // With Accept: text/markdown header - same result
209 config := http.FetchConfig{
210 url: '${localserver}/test.md'
211 header: http.new_header(key: .accept, value: 'text/markdown')
212 }
213 x_with_header := http.fetch(config)!
214 assert x_with_header.status() == .ok
215 assert x_with_header.header.get(.content_type)! == 'text/markdown'
216 assert x_with_header.body.contains('# Test Markdown')
217}
218
219fn test_markdown_variants_direct_access() {
220 // All markdown variants remain accessible via their full paths
221 x_html_md := http.get('${localserver}/about.html.md')!
222 assert x_html_md.status() == .ok
223 assert x_html_md.body.contains('about.html.md variant')
224
225 x_index := http.get('${localserver}/about/index.html.md')!
226 assert x_index.status() == .ok
227 assert x_index.body.contains('about/index.html.md variant')
228}
229
230// Negative tests - Verifies correct behavior without Accept header
231
232fn test_markdown_no_negotiation_without_header() {
233 // Without Accept: text/markdown, content is not found for directories with no index.html
234 x := http.get('${localserver}/about')!
235 assert x.status() == .not_found
236}
237