| // Copyright 2016 The LUCI Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package frontend |
| |
| import ( |
| "bytes" |
| "net/http/httptest" |
| "testing" |
| |
| "github.com/julienschmidt/httprouter" |
| "go.chromium.org/luci/appengine/gaetesting" |
| "go.chromium.org/luci/auth/identity" |
| buildbucketpb "go.chromium.org/luci/buildbucket/proto" |
| "go.chromium.org/luci/config" |
| "go.chromium.org/luci/config/cfgclient" |
| "go.chromium.org/luci/config/impl/memory" |
| "go.chromium.org/luci/milo/common" |
| "go.chromium.org/luci/milo/git" |
| "go.chromium.org/luci/server/auth" |
| "go.chromium.org/luci/server/auth/authtest" |
| "go.chromium.org/luci/server/router" |
| |
| . "github.com/smartystreets/goconvey/convey" |
| ) |
| |
| func TestFuncs(t *testing.T) { |
| t.Parallel() |
| |
| Convey("Middleware Tests", t, func() { |
| Convey("Format Commit Description", func() { |
| Convey("linkify https://", func() { |
| So(formatCommitDesc("https://foo.com"), |
| ShouldEqual, |
| "<a href=\"https://foo.com\">https://foo.com</a>") |
| Convey("but not http://", func() { |
| So(formatCommitDesc("http://foo.com"), ShouldEqual, "http://foo.com") |
| }) |
| }) |
| Convey("linkify b/ and crbug/", func() { |
| So(formatCommitDesc("blah blah b/123456 blah"), ShouldEqual, "blah blah <a href=\"http://b/123456\">b/123456</a> blah") |
| So(formatCommitDesc("crbug:foo/123456"), ShouldEqual, "<a href=\"https://crbug.com/foo/123456\">crbug:foo/123456</a>") |
| }) |
| Convey("linkify Bug: lines", func() { |
| So(formatCommitDesc("\nBug: 12345\n"), ShouldEqual, "\nBug: <a href=\"https://crbug.com/12345\">12345</a>\n") |
| So( |
| formatCommitDesc(" > > BugS= 12345, butter:12345"), |
| ShouldEqual, |
| " > > BugS= <a href=\"https://crbug.com/12345\">12345</a>, "+ |
| "<a href=\"https://crbug.com/butter/12345\">butter:12345</a>") |
| }) |
| Convey("linkify rules should not collide", func() { |
| So( |
| formatCommitDesc("I \"fixed\" https://crbug.com/123456 <today>"), |
| ShouldEqual, |
| "I "fixed" <a href=\"https://crbug.com/123456\">https://crbug.com/123456</a> <today>") |
| So( |
| formatCommitDesc("Bug: 12, crbug/34, https://crbug.com/56, 78"), |
| ShouldEqual, |
| "Bug: <a href=\"https://crbug.com/12\">12</a>, <a href=\"https://crbug.com/34\">crbug/34</a>, <a href=\"https://crbug.com/56\">https://crbug.com/56</a>, <a href=\"https://crbug.com/78\">78</a>") |
| }) |
| Convey("linkify rules interact correctly with escaping", func() { |
| So( |
| formatCommitDesc("\"https://example.com\""), |
| ShouldEqual, |
| ""<a href=\"https://example.com\">https://example.com</a>"") |
| So( |
| formatCommitDesc("Bug: <not a bug number, sorry>"), |
| ShouldEqual, |
| "Bug: <not a bug number, sorry>") |
| // This is not remotely valid of a URL, but exists to test that |
| // the linking template correctly escapes the URL, both as an |
| // attribute and as a value. |
| So( |
| formatCommitDesc("https://foo&bar<baz\"aaa>bbb"), |
| ShouldEqual, |
| "<a href=\"https://foo&bar%3cbaz%22aaa%3ebbb\">https://foo&bar<baz"aaa>bbb</a>") |
| }) |
| |
| Convey("trimLongString", func() { |
| Convey("short", func() { |
| So(trimLongString(4, "😀😀😀😀"), ShouldEqual, "😀😀😀😀") |
| }) |
| Convey("long", func() { |
| So(trimLongString(4, "😀😀😀😀😀"), ShouldEqual, "😀😀😀…") |
| }) |
| }) |
| }) |
| |
| Convey("Redirect unauthorized users to login page for projects with access restrictions", func() { |
| projectACLMiddleware := buildProjectACLMiddleware(false) |
| r := httptest.NewRecorder() |
| c := gaetesting.TestingContextWithAppID("luci-milo-dev") |
| |
| // Fake user to be anonymous. |
| c = auth.WithState(c, &authtest.FakeState{Identity: identity.AnonymousIdentity}) |
| |
| // Create fake internal project named "secret". |
| c = cfgclient.Use(c, memory.New(map[config.Set]memory.Files{ |
| "projects/secret": { |
| "project.cfg": "name: \"secret\"\naccess: \"group:googlers\"", |
| }, |
| })) |
| So(common.UpdateProjects(c), ShouldBeNil) |
| |
| ctx := &router.Context{ |
| Context: c, |
| Writer: r, |
| Request: httptest.NewRequest("GET", "/p/secret", bytes.NewReader(nil)), |
| Params: httprouter.Params{{Key: "project", Value: "secret"}}, |
| } |
| projectACLMiddleware(ctx, nil) |
| project, ok := git.ProjectFromContext(ctx.Context) |
| So(ok, ShouldBeFalse) |
| So(project, ShouldEqual, "") |
| So(r.Code, ShouldEqual, 302) |
| So(r.Result().Header["Location"], ShouldResemble, []string{"http://fake.example.com/login?dest=%2Fp%2Fsecret"}) |
| }) |
| |
| Convey("Install git project to context when the user has access to the project", func() { |
| optionalProjectACLMiddleware := buildProjectACLMiddleware(true) |
| r := httptest.NewRecorder() |
| c := gaetesting.TestingContextWithAppID("luci-milo-dev") |
| |
| // Fake user to be anonymous. |
| c = auth.WithState(c, &authtest.FakeState{Identity: identity.AnonymousIdentity, IdentityGroups: []string{"all"}}) |
| |
| // Create fake public project named "public". |
| c = cfgclient.Use(c, memory.New(map[config.Set]memory.Files{ |
| "projects/public": { |
| "project.cfg": "name: \"public\"\naccess: \"group:all\"", |
| }, |
| })) |
| So(common.UpdateProjects(c), ShouldBeNil) |
| |
| ctx := &router.Context{ |
| Context: c, |
| Writer: r, |
| Request: httptest.NewRequest("GET", "/p/public", bytes.NewReader(nil)), |
| Params: httprouter.Params{{Key: "project", Value: "public"}}, |
| } |
| nextCalled := false |
| next := func(*router.Context) { |
| nextCalled = true |
| } |
| optionalProjectACLMiddleware(ctx, next) |
| project, ok := git.ProjectFromContext(ctx.Context) |
| So(project, ShouldEqual, "public") |
| So(ok, ShouldBeTrue) |
| So(nextCalled, ShouldBeTrue) |
| So(r.Code, ShouldEqual, 200) |
| }) |
| |
| Convey("Don't install git project to context when the user doesn't have access to the project", func() { |
| optionalProjectACLMiddleware := buildProjectACLMiddleware(true) |
| r := httptest.NewRecorder() |
| c := gaetesting.TestingContextWithAppID("luci-milo-dev") |
| |
| // Fake user to be anonymous. |
| c = auth.WithState(c, &authtest.FakeState{Identity: identity.AnonymousIdentity}) |
| |
| // Create fake internal project named "secret". |
| c = cfgclient.Use(c, memory.New(map[config.Set]memory.Files{ |
| "projects/secret": { |
| "project.cfg": "name: \"secret\"\naccess: \"group:googlers\"", |
| }, |
| })) |
| So(common.UpdateProjects(c), ShouldBeNil) |
| |
| ctx := &router.Context{ |
| Context: c, |
| Writer: r, |
| Request: httptest.NewRequest("GET", "/p/secret", bytes.NewReader(nil)), |
| Params: httprouter.Params{{Key: "project", Value: "secret"}}, |
| } |
| nextCalled := false |
| next := func(*router.Context) { |
| nextCalled = true |
| } |
| optionalProjectACLMiddleware(ctx, next) |
| project, ok := git.ProjectFromContext(ctx.Context) |
| So(ok, ShouldBeFalse) |
| So(project, ShouldEqual, "") |
| So(nextCalled, ShouldBeTrue) |
| So(r.Code, ShouldEqual, 200) |
| }) |
| |
| Convey("Convert LogDog URLs", func() { |
| So( |
| logdogLink(buildbucketpb.Log{Name: "foo", Url: "logdog://www.example.com:1234/foo/bar/+/baz"}, true), |
| ShouldEqual, |
| `<a href="https://www.example.com:1234/logs/foo/bar/+/baz?format=raw" aria-label="raw log foo">raw</a>`) |
| So( |
| logdogLink(buildbucketpb.Log{Name: "foo", Url: "%zzzzz"}, true), |
| ShouldEqual, |
| `<a href="#invalid-logdog-link" aria-label="raw log foo">raw</a>`) |
| So( |
| logdogLink(buildbucketpb.Log{Name: "foo", Url: "logdog://logs.chromium.org/foo/bar/+/baz"}, false), |
| ShouldEqual, |
| `<a href="https://logs.chromium.org/logs/foo/bar/+/baz" aria-label="raw log foo">foo</a>`) |
| |
| }) |
| }) |
| } |