More testing
* test for invite accept form rendering * show placeholder if there is no suggested alias * accept form and consume endpoint
This commit is contained in:
parent
6945221557
commit
b21e2b2062
|
@ -4,6 +4,7 @@ package handlers
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
|
@ -220,6 +221,9 @@ func TestFallbackAuth(t *testing.T) {
|
|||
}
|
||||
|
||||
// utils
|
||||
|
||||
// TODO: we probably want to move all of these to web/testing or somesuch
|
||||
|
||||
type localizedElement struct {
|
||||
Selector, Label string
|
||||
}
|
||||
|
@ -239,3 +243,39 @@ func assertCSRFTokenPresent(t *testing.T, sel *goquery.Selection) {
|
|||
a.True(ok, "csrf input has a type")
|
||||
a.Equal("hidden", tipe, "wrong type on csrf field")
|
||||
}
|
||||
|
||||
type inputElement struct {
|
||||
Name, Value, Type, Placeholder string
|
||||
}
|
||||
|
||||
// assertInputsInForm checks a list of defined elements. It tries to find them by input[name=$name]
|
||||
// and then proceeds with asserting their value, type or placeholder (if the fields in inputElement are not "")
|
||||
func assertInputsInForm(t *testing.T, form *goquery.Selection, elems []inputElement) {
|
||||
a := assert.New(t)
|
||||
for _, e := range elems {
|
||||
|
||||
inputSelector := form.Find(fmt.Sprintf("input[name=%s]", e.Name))
|
||||
ok := a.Equal(1, inputSelector.Length(), "expected to find input with name %s", e.Name)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if e.Value != "" {
|
||||
value, has := inputSelector.Attr("value")
|
||||
a.True(has, "expected value attribute input[name=%s]", e.Name)
|
||||
a.Equal(e.Value, value, "wrong value attribute on input[name=%s]", e.Name)
|
||||
}
|
||||
|
||||
if e.Type != "" {
|
||||
tipe, has := inputSelector.Attr("type")
|
||||
a.True(has, "expected type attribute input[name=%s]", e.Name)
|
||||
a.Equal(e.Type, tipe, "wrong type attribute on input[name=%s]", e.Name)
|
||||
}
|
||||
|
||||
if e.Placeholder != "" {
|
||||
tipe, has := inputSelector.Attr("placeholder")
|
||||
a.True(has, "expected placeholder attribute input[name=%s]", e.Name)
|
||||
a.Equal(e.Placeholder, tipe, "wrong placeholder attribute on input[name=%s]", e.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1,223 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
refs "go.mindeco.de/ssb-refs"
|
||||
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/admindb"
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/web"
|
||||
weberrors "github.com/ssb-ngi-pointer/go-ssb-room/web/errors"
|
||||
"github.com/ssb-ngi-pointer/go-ssb-room/web/router"
|
||||
)
|
||||
|
||||
func TestInviteShowAcceptForm(t *testing.T) {
|
||||
ts := setup(t)
|
||||
|
||||
urlTo := web.NewURLTo(ts.Router)
|
||||
|
||||
t.Run("token doesnt exist", func(t *testing.T) {
|
||||
a, r := assert.New(t), require.New(t)
|
||||
|
||||
testToken := "nonexistant-test-token"
|
||||
acceptURL404 := urlTo(router.CompleteInviteAccept, "token", testToken)
|
||||
r.NotNil(acceptURL404)
|
||||
|
||||
// prep the mocked db for http:404
|
||||
ts.InvitesDB.GetByTokenReturns(admindb.Invite{}, admindb.ErrNotFound)
|
||||
|
||||
// request the form
|
||||
acceptForm := acceptURL404.String()
|
||||
t.Log(acceptForm)
|
||||
doc, resp := ts.Client.GetHTML(acceptForm)
|
||||
// 500 until https://github.com/ssb-ngi-pointer/go-ssb-room/issues/66 is fixed
|
||||
a.Equal(http.StatusInternalServerError, resp.Code)
|
||||
|
||||
// check database calls
|
||||
r.EqualValues(1, ts.InvitesDB.GetByTokenCallCount())
|
||||
_, tokenFromArg := ts.InvitesDB.GetByTokenArgsForCall(0)
|
||||
a.Equal(testToken, tokenFromArg)
|
||||
|
||||
// fix #66
|
||||
// assertLocalized(t, doc, []localizedElement{
|
||||
// {"#welcome", "AuthFallbackWelcome"},
|
||||
// {"title", "AuthFallbackTitle"},
|
||||
// })
|
||||
gotErr := doc.Find("#errBody").Text()
|
||||
wantErr := weberrors.ErrNotFound{What: "invite"}
|
||||
a.EqualError(wantErr, gotErr)
|
||||
})
|
||||
|
||||
wantNewMemberPlaceholder := "@ .ed25519"
|
||||
|
||||
t.Run("token DOES exist", func(t *testing.T) {
|
||||
a, r := assert.New(t), require.New(t)
|
||||
|
||||
testToken := "existing-test-token"
|
||||
validAcceptURL := urlTo(router.CompleteInviteAccept, "token", testToken)
|
||||
r.NotNil(validAcceptURL)
|
||||
|
||||
// prep the mocked db for http:200
|
||||
fakeExistingInvite := admindb.Invite{
|
||||
ID: 1234,
|
||||
AliasSuggestion: "bestie",
|
||||
}
|
||||
ts.InvitesDB.GetByTokenReturns(fakeExistingInvite, nil)
|
||||
|
||||
// request the form
|
||||
validAcceptForm := validAcceptURL.String()
|
||||
t.Log(validAcceptForm)
|
||||
doc, resp := ts.Client.GetHTML(validAcceptForm)
|
||||
a.Equal(http.StatusOK, resp.Code)
|
||||
|
||||
// check database calls
|
||||
r.EqualValues(2, ts.InvitesDB.GetByTokenCallCount())
|
||||
_, tokenFromArg := ts.InvitesDB.GetByTokenArgsForCall(1)
|
||||
a.Equal(testToken, tokenFromArg)
|
||||
|
||||
assertLocalized(t, doc, []localizedElement{
|
||||
{"#welcome", "InviteAcceptWelcome"},
|
||||
{"title", "InviteAcceptTitle"},
|
||||
})
|
||||
|
||||
form := doc.Find("form#consume")
|
||||
r.Equal(1, form.Length())
|
||||
|
||||
assertCSRFTokenPresent(t, form)
|
||||
|
||||
assertInputsInForm(t, form, []inputElement{
|
||||
{Name: "token", Type: "hidden", Value: testToken},
|
||||
{Name: "alias", Type: "text", Value: fakeExistingInvite.AliasSuggestion},
|
||||
{Name: "new_member", Type: "text", Placeholder: wantNewMemberPlaceholder},
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("token DOES exist but has no suggested alias", func(t *testing.T) {
|
||||
a, r := assert.New(t), require.New(t)
|
||||
|
||||
testToken := "existing-test-token-2"
|
||||
validAcceptURL := urlTo(router.CompleteInviteAccept, "token", testToken)
|
||||
r.NotNil(validAcceptURL)
|
||||
|
||||
inviteWithNoAlias := admindb.Invite{ID: 4321}
|
||||
ts.InvitesDB.GetByTokenReturns(inviteWithNoAlias, nil)
|
||||
|
||||
// request the form
|
||||
validAcceptForm := validAcceptURL.String()
|
||||
t.Log(validAcceptForm)
|
||||
doc, resp := ts.Client.GetHTML(validAcceptForm)
|
||||
a.Equal(http.StatusOK, resp.Code)
|
||||
|
||||
// check database calls
|
||||
r.EqualValues(3, ts.InvitesDB.GetByTokenCallCount())
|
||||
_, tokenFromArg := ts.InvitesDB.GetByTokenArgsForCall(2)
|
||||
a.Equal(testToken, tokenFromArg)
|
||||
|
||||
assertLocalized(t, doc, []localizedElement{
|
||||
{"#welcome", "InviteAcceptWelcome"},
|
||||
{"title", "InviteAcceptTitle"},
|
||||
})
|
||||
|
||||
form := doc.Find("form#consume")
|
||||
r.Equal(1, form.Length())
|
||||
|
||||
assertCSRFTokenPresent(t, form)
|
||||
|
||||
assertInputsInForm(t, form, []inputElement{
|
||||
{Name: "token", Type: "hidden", Value: testToken},
|
||||
{Name: "alias", Type: "text", Placeholder: "you@this.room"},
|
||||
{Name: "new_member", Type: "text", Placeholder: wantNewMemberPlaceholder},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestInviteConsumeInvite(t *testing.T) {
|
||||
ts := setup(t)
|
||||
a, r := assert.New(t), require.New(t)
|
||||
urlTo := web.NewURLTo(ts.Router)
|
||||
|
||||
testToken := "existing-test-token-2"
|
||||
validAcceptURL := urlTo(router.CompleteInviteAccept, "token", testToken)
|
||||
r.NotNil(validAcceptURL)
|
||||
validAcceptURL.Host = "localhost"
|
||||
validAcceptURL.Scheme = "https"
|
||||
|
||||
inviteWithNoAlias := admindb.Invite{ID: 4321}
|
||||
ts.InvitesDB.GetByTokenReturns(inviteWithNoAlias, nil)
|
||||
|
||||
// request the form (for a valid csrf token)
|
||||
validAcceptForm := validAcceptURL.String()
|
||||
t.Log(validAcceptForm)
|
||||
doc, resp := ts.Client.GetHTML(validAcceptForm)
|
||||
a.Equal(http.StatusOK, resp.Code)
|
||||
|
||||
// we need a functional jar to unpack the Set-Cookie response for the csrf token
|
||||
jar, err := cookiejar.New(nil)
|
||||
r.NoError(err)
|
||||
|
||||
// update the jar
|
||||
csrfCookie := resp.Result().Cookies()
|
||||
a.Len(csrfCookie, 1, "should have one cookie for CSRF protection validation")
|
||||
jar.SetCookies(validAcceptURL, csrfCookie)
|
||||
|
||||
// get the corresponding token from the page
|
||||
csrfTokenElem := doc.Find("input[name='gorilla.csrf.Token']")
|
||||
a.Equal(1, csrfTokenElem.Length())
|
||||
csrfName, has := csrfTokenElem.Attr("name")
|
||||
a.True(has, "should have a name attribute")
|
||||
csrfValue, has := csrfTokenElem.Attr("value")
|
||||
a.True(has, "should have value attribute")
|
||||
|
||||
// create the consume request
|
||||
testNewMember := refs.FeedRef{
|
||||
ID: bytes.Repeat([]byte{1}, 32),
|
||||
Algo: refs.RefAlgoFeedSSB1,
|
||||
}
|
||||
consumeVals := url.Values{
|
||||
"token": []string{testToken},
|
||||
"new_member": []string{testNewMember.Ref()},
|
||||
|
||||
csrfName: []string{csrfValue},
|
||||
}
|
||||
|
||||
// construct the consume endpoint url
|
||||
consumeInviteURL, err := ts.Router.Get(router.CompleteInviteConsume).URL()
|
||||
r.Nil(err)
|
||||
consumeInviteURL.Host = "localhost"
|
||||
consumeInviteURL.Scheme = "https"
|
||||
|
||||
// construct the header with Referer and Cookie
|
||||
var csrfCookieHeader = http.Header(map[string][]string{})
|
||||
csrfCookieHeader.Set("Referer", "https://localhost")
|
||||
cs := jar.Cookies(consumeInviteURL)
|
||||
r.Len(cs, 1, "expecting one cookie for csrf")
|
||||
theCookie := cs[0].String()
|
||||
a.NotEqual("", theCookie, "should have a new cookie")
|
||||
csrfCookieHeader.Set("Cookie", theCookie)
|
||||
ts.Client.SetHeaders(csrfCookieHeader)
|
||||
|
||||
// prepare the mock
|
||||
ts.InvitesDB.ConsumeReturns(inviteWithNoAlias, nil)
|
||||
|
||||
// send the POST
|
||||
resp = ts.Client.PostForm(consumeInviteURL.String(), consumeVals)
|
||||
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code for sign in")
|
||||
|
||||
// check how consume was called
|
||||
r.EqualValues(1, ts.InvitesDB.ConsumeCallCount())
|
||||
_, tokenFromArg, newMemberRef := ts.InvitesDB.ConsumeArgsForCall(0)
|
||||
a.Equal(testToken, tokenFromArg)
|
||||
a.True(newMemberRef.Equal(&testNewMember))
|
||||
|
||||
consumedDoc, err := goquery.NewDocumentFromReader(resp.Body)
|
||||
r.NoError(err)
|
||||
t.Log(consumedDoc.Find("body").Text())
|
||||
}
|
||||
|
|
|
@ -7,11 +7,7 @@
|
|||
class="text-center"
|
||||
>{{ i18n "InviteAcceptWelcome" }}</span>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<form id="confirm" action="{{urlTo "complete:invite:consume"}}" method="POST">
|
||||
<form id="consume" action="{{urlTo "complete:invite:consume"}}" method="POST">
|
||||
{{ .csrfField }}
|
||||
<input type="hidden" name="token" value={{.Token}}>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
|
@ -26,13 +22,16 @@
|
|||
<span class="ml-2 text-red-400">TODO: make this a dropdown</span>
|
||||
</div>
|
||||
|
||||
{{ if ne .Invite.AliasSuggestion "" }}
|
||||
<p>{{ i18n "InviteAcceptAliasSuggestion" }}</p>
|
||||
<input
|
||||
name="alias"
|
||||
<p>{{ i18n "InviteAcceptAliasSuggestion" }}</p>
|
||||
<input
|
||||
type="text"
|
||||
name="alias"
|
||||
{{ if ne .Invite.AliasSuggestion "" }}
|
||||
value="{{ .Invite.AliasSuggestion }}"
|
||||
></p>
|
||||
{{ end }}
|
||||
{{else}}
|
||||
placeholder="you@this.room"
|
||||
{{ end }}
|
||||
>
|
||||
|
||||
<a
|
||||
href="javascript:history.back()"
|
||||
|
|
Loading…
Reference in New Issue