Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d324f65

Browse filesBrowse files
committed
Merge pull request revel#744 from anonx/tests/enhancement
Tests/enhancement to close revel#687 and revel#696
2 parents d769f09 + f2e5dc6 commit d324f65
Copy full SHA for d324f65

File tree

Expand file treeCollapse file tree

26 files changed

+6657
-54
lines changed
Filter options
Expand file treeCollapse file tree

26 files changed

+6657
-54
lines changed

‎samples/persona/tests/apptest.go

Copy file name to clipboardExpand all lines: samples/persona/tests/apptest.go
+41-16Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ package tests
22

33
import (
44
"encoding/json"
5-
"net/http"
65
"net/url"
6+
"time"
77

88
"github.com/revel/revel"
99
)
1010

11+
var personaTestUsers map[string]PersonaTestUser
12+
1113
type AppTest struct {
1214
revel.TestSuite
1315
}
@@ -17,27 +19,28 @@ type PersonaTestUser struct {
1719
Audience string `json:"audience"`
1820
Email string `json:"email"`
1921
Pass string `json:"pass"`
22+
Received time.Time
2023
}
2124

22-
func (t AppTest) TestThatLoginPageWorks() {
25+
func (t *AppTest) TestThatLoginPageWorks() {
2326
// Make sure empty assertion will cause an error.
2427
t.PostForm("/login", url.Values{
25-
"assertion": []string{""},
28+
"assertion": {""},
2629
})
2730
t.AssertStatus(400)
2831

2932
// Ensure that incorrect audience parameter will lead to an error.
3033
user := t.EmailWithAssertion("https://example.com")
3134
t.PostForm("/login", url.Values{
32-
"assertion": []string{user.Assertion},
35+
"assertion": {user.Assertion},
3336
})
3437
t.AssertEqual(user.Audience, "https://example.com")
3538
t.AssertStatus(400)
3639

3740
// Check whether authentication works.
3841
user = t.EmailWithAssertion("http://" + revel.Config.StringDefault("http.host", "localhost"))
3942
t.PostForm("/login", url.Values{
40-
"assertion": []string{user.Assertion},
43+
"assertion": {user.Assertion},
4144
})
4245
t.AssertOk()
4346
t.AssertContains("Login successful")
@@ -47,11 +50,11 @@ func (t AppTest) TestThatLoginPageWorks() {
4750
t.AssertContains(user.Email)
4851
}
4952

50-
func (t AppTest) TestThatLogoutPageWorks() {
53+
func (t *AppTest) TestThatLogoutPageWorks() {
5154
// Authenticating a user.
5255
user := t.EmailWithAssertion("http://" + revel.Config.StringDefault("http.host", "localhost"))
5356
t.PostForm("/login", url.Values{
54-
"assertion": []string{user.Assertion},
57+
"assertion": {user.Assertion},
5558
})
5659
t.AssertOk()
5760
t.AssertContains("Login successful")
@@ -69,23 +72,45 @@ func (t AppTest) TestThatLogoutPageWorks() {
6972
}
7073

7174
// EmailWithAssertion uses personatestuser.org service for getting testing parameters.
72-
// Audience is expected to begin with protocol, for example: "http://".
73-
func (t AppTest) EmailWithAssertion(audience string) *PersonaTestUser {
75+
// The test persona service expects audience to begin with protocol, for example: "http://".
76+
func (t *AppTest) EmailWithAssertion(audience string) PersonaTestUser {
77+
// The process of getting new test users takes a lot of time. To reduce the number
78+
// of http requests using the same user data till they are up-to-date.
79+
if user, ok := personaTestUsers[audience]; ok {
80+
// Make sure user data are still valid.
81+
// Data expire after 2 minutes. We are updating them after 1 just in case.
82+
if !time.Now().After(user.Received.Add(time.Minute)) {
83+
return user
84+
}
85+
}
86+
7487
// Trying to get data from testing server.
75-
uri := "/email_with_assertion/" + url.QueryEscape(audience)
76-
req, err := http.NewRequest("GET", "http://personatestuser.org"+uri, nil)
77-
t.Assert(err == nil)
78-
req.URL.Opaque = uri // Use unescaped version of URI for request.
79-
t.MakeRequest(req)
88+
u := "http://personatestuser.org"
89+
urn := "/email_with_assertion/" + url.QueryEscape(audience)
90+
91+
req := t.GetCustom(u + urn)
92+
req.URL.Opaque = urn // Use unescaped version of URN for the request.
93+
req.Send()
8094

8195
// Check whether response status is OK.
8296
revel.TRACE.Printf("PERSONA TESTING: Response of testing server is %q", t.ResponseBody)
8397
t.AssertOk()
8498

8599
// Parsing the response from server.
86100
var user PersonaTestUser
87-
err = json.Unmarshal(t.ResponseBody, &user)
101+
err := json.Unmarshal(t.ResponseBody, &user)
88102
t.Assert(err == nil)
89103

90-
return &user
104+
// Register the time when new user data are received. We are not using "Expire"
105+
// parameter from persona test server because then we'll have to synchronise our clock.
106+
user.Received = time.Now()
107+
108+
// Cache the user data.
109+
personaTestUsers[audience] = user
110+
111+
return user
112+
}
113+
114+
func init() {
115+
personaTestUsers = map[string]PersonaTestUser{}
91116
}

‎samples/upload/.gitignore

Copy file name to clipboard
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
test-results/
2+
tmp/
3+
routes/

‎samples/upload/app/controllers/app.go

Copy file name to clipboard
+29Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package controllers
2+
3+
import (
4+
"github.com/revel/revel"
5+
)
6+
7+
type App struct {
8+
*revel.Controller
9+
}
10+
11+
type FileInfo struct {
12+
ContentType string
13+
Filename string
14+
RealFormat string `json:",omitempty"`
15+
Resolution string `json:",omitempty"`
16+
Size int
17+
Status string `json:",omitempty"`
18+
}
19+
20+
func (c *App) Before() revel.Result {
21+
// Rendering useful info here.
22+
c.RenderArgs["action"] = c.Controller.Action
23+
24+
return nil
25+
}
26+
27+
func init() {
28+
revel.InterceptMethod((*App).Before, revel.BEFORE)
29+
}
+47Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package controllers
2+
3+
import (
4+
"github.com/revel/revel/samples/upload/app/routes"
5+
6+
"github.com/revel/revel"
7+
)
8+
9+
type Multiple struct {
10+
App
11+
}
12+
13+
func (c *Multiple) Upload() revel.Result {
14+
return c.Render()
15+
}
16+
17+
func (c *Multiple) HandleUpload() revel.Result {
18+
var files [][]byte
19+
c.Params.Bind(&files, "file")
20+
21+
// Make sure at least 2 but no more than 3 files are submitted.
22+
c.Validation.MinSize(files, 2).Message("You cannot submit less than 2 files")
23+
c.Validation.MaxSize(files, 3).Message("You cannot submit more than 3 files")
24+
25+
// Handle errors.
26+
if c.Validation.HasErrors() {
27+
c.Validation.Keep()
28+
c.FlashParams()
29+
return c.Redirect(routes.Multiple.Upload())
30+
}
31+
32+
// Prepare result.
33+
filesInfo := make([]FileInfo, len(files))
34+
for i, _ := range files {
35+
filesInfo[i] = FileInfo{
36+
ContentType: c.Params.Files["file[]"][i].Header.Get("Content-Type"),
37+
Filename: c.Params.Files["file[]"][i].Filename,
38+
Size: len(files[i]),
39+
}
40+
}
41+
42+
return c.RenderJson(map[string]interface{}{
43+
"Count": len(files),
44+
"Files": filesInfo,
45+
"Status": "Successfully uploaded",
46+
})
47+
}
+64Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package controllers
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"image"
7+
_ "image/jpeg"
8+
_ "image/png"
9+
10+
"github.com/revel/revel/samples/upload/app/routes"
11+
12+
"github.com/revel/revel"
13+
)
14+
15+
const (
16+
_ = iota
17+
KB int = 1 << (10 * iota)
18+
MB
19+
GB
20+
)
21+
22+
type Single struct {
23+
App
24+
}
25+
26+
func (c *Single) Upload() revel.Result {
27+
return c.Render()
28+
}
29+
30+
func (c *Single) HandleUpload(avatar []byte) revel.Result {
31+
// Validation rules.
32+
c.Validation.Required(avatar)
33+
c.Validation.MinSize(avatar, 2*KB).
34+
Message("Minimum a file size of 2KB expected")
35+
c.Validation.MaxSize(avatar, 2*MB).
36+
Message("File cannot be larger than 2MB")
37+
38+
// Check format of the file.
39+
conf, format, err := image.DecodeConfig(bytes.NewReader(avatar))
40+
c.Validation.Required(err == nil).Key("avatar").
41+
Message("Incorrect file format")
42+
c.Validation.Required(format == "jpeg" || format == "png").Key("avatar").
43+
Message("JPEG or PNG file format is expected")
44+
45+
// Check resolution.
46+
c.Validation.Required(conf.Height >= 150 && conf.Width >= 150).Key("avatar").
47+
Message("Minimum allowed resolution is 150x150px")
48+
49+
// Handle errors.
50+
if c.Validation.HasErrors() {
51+
c.Validation.Keep()
52+
c.FlashParams()
53+
return c.Redirect(routes.Single.Upload())
54+
}
55+
56+
return c.RenderJson(FileInfo{
57+
ContentType: c.Params.Files["avatar"][0].Header.Get("Content-Type"),
58+
Filename: c.Params.Files["avatar"][0].Filename,
59+
RealFormat: format,
60+
Resolution: fmt.Sprintf("%dx%d", conf.Width, conf.Height),
61+
Size: len(avatar),
62+
Status: "Successfully uploaded",
63+
})
64+
}

‎samples/upload/app/init.go

Copy file name to clipboard
+62Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package app
2+
3+
import (
4+
"errors"
5+
6+
"github.com/revel/revel"
7+
)
8+
9+
func init() {
10+
// Filters is the default set of global filters.
11+
revel.Filters = []revel.Filter{
12+
revel.PanicFilter, // Recover from panics and display an error page instead.
13+
revel.RouterFilter, // Use the routing table to select the right Action
14+
revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
15+
revel.ParamsFilter, // Parse parameters into Controller.Params.
16+
revel.SessionFilter, // Restore and write the session cookie.
17+
revel.FlashFilter, // Restore and write the flash cookie.
18+
revel.ValidationFilter, // Restore kept validation errors and save new ones from cookie.
19+
revel.I18nFilter, // Resolve the requested language
20+
HeaderFilter, // Add some security based headers
21+
revel.InterceptorFilter, // Run interceptors around the action.
22+
revel.CompressFilter, // Compress the result.
23+
revel.ActionInvoker, // Invoke the action.
24+
}
25+
26+
// register startup functions with OnAppStart
27+
// ( order dependent )
28+
// revel.OnAppStart(InitDB)
29+
// revel.OnAppStart(FillCache)
30+
31+
// Helper for calling a template with several pipeline parameters.
32+
// Example input: key1 value1 key2 value2.
33+
// Example use: {{template "button.html" dict "dot" . "class" "active"}}
34+
revel.TemplateFuncs["dict"] = func(values ...interface{}) (map[string]interface{}, error) {
35+
if len(values)%2 != 0 {
36+
return nil, errors.New("invalid dict call")
37+
}
38+
39+
dict := make(map[string]interface{}, len(values)/2)
40+
for i := 0; i < len(values); i += 2 {
41+
if key, ok := values[i].(string); ok {
42+
dict[key] = values[i+1]
43+
} else {
44+
return nil, errors.New("dict keys must be strings")
45+
}
46+
}
47+
48+
return dict, nil
49+
}
50+
}
51+
52+
// TODO turn this into revel.HeaderFilter
53+
// should probably also have a filter for CSRF
54+
// not sure if it can go in the same filter or not
55+
var HeaderFilter = func(c *revel.Controller, fc []revel.Filter) {
56+
// Add some common security headers
57+
c.Response.Out.Header().Add("X-Frame-Options", "SAMEORIGIN")
58+
c.Response.Out.Header().Add("X-XSS-Protection", "1; mode=block")
59+
c.Response.Out.Header().Add("X-Content-Type-Options", "nosniff")
60+
61+
fc[0](c, fc[1:]) // Execute the next filter stage.
62+
}

‎samples/upload/app/views/debug.html

Copy file name to clipboard
+64Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<style type="text/css">
2+
#sidebar {
3+
position: absolute;
4+
right: 0px;
5+
top:69px;
6+
max-width: 75%;
7+
z-index: 1000;
8+
background-color: #fee;
9+
border: thin solid grey;
10+
padding: 10px;
11+
}
12+
#toggleSidebar {
13+
position: absolute;
14+
right: 0px;
15+
top: 50px;
16+
background-color: #fee;
17+
}
18+
19+
</style>
20+
<div id="sidebar" style="display:none;">
21+
<h4>Available pipelines</h4>
22+
<dl>
23+
{{ range $index, $value := .}}
24+
<dt>{{$index}}</dt>
25+
<dd>{{$value}}</dd>
26+
{{end}}
27+
</dl>
28+
<h4>Flash</h4>
29+
<dl>
30+
{{ range $index, $value := .flash}}
31+
<dt>{{$index}}</dt>
32+
<dd>{{$value}}</dd>
33+
{{end}}
34+
</dl>
35+
36+
<h4>Errors</h4>
37+
<dl>
38+
{{ range $index, $value := .errors}}
39+
<dt>{{$index}}</dt>
40+
<dd>{{$value}}</dd>
41+
{{end}}
42+
</dl>
43+
</div>
44+
<a id="toggleSidebar" href="#" class="toggles"><i class="icon-chevron-left"></i></a>
45+
46+
<script>
47+
$sidebar = 0;
48+
$('#toggleSidebar').click(function() {
49+
if ($sidebar === 1) {
50+
$('#sidebar').hide();
51+
$('#toggleSidebar i').addClass('icon-chevron-left');
52+
$('#toggleSidebar i').removeClass('icon-chevron-right');
53+
$sidebar = 0;
54+
}
55+
else {
56+
$('#sidebar').show();
57+
$('#toggleSidebar i').addClass('icon-chevron-right');
58+
$('#toggleSidebar i').removeClass('icon-chevron-left');
59+
$sidebar = 1;
60+
}
61+
62+
return false;
63+
});
64+
</script>

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.