Add handlers test.

Also, for all tests that use tables, log the test case being executed.
That way verbose mode will indicate which test case is causing any nil
pointer dereference or other segfault interrupts. Also, it will inform
anyone who runs in verbose mode just how many tests there really are.
このコミットが含まれているのは:
Cory Slep 2018-05-27 01:49:03 +02:00
コミット 7f2faf98e1
3個のファイルの変更537行の追加2行の削除

ファイルの表示

@ -1896,6 +1896,7 @@ func TestPostInbox_RequiresObject(t *testing.T) {
return nil
}
for _, test := range tests {
t.Logf("Running table test case %q", test.name)
resp := httptest.NewRecorder()
req := ActivityPubRequest(httptest.NewRequest("POST", testInboxURI, bytes.NewBuffer(MustSerialize(test.input()))))
handled, err := p.PostInbox(context.Background(), resp, req)
@ -1940,6 +1941,7 @@ func TestPostInbox_RequiresTarget(t *testing.T) {
return nil
}
for _, test := range tests {
t.Logf("Running table test case %q", test.name)
resp := httptest.NewRecorder()
req := ActivityPubRequest(httptest.NewRequest("POST", testInboxURI, bytes.NewBuffer(MustSerialize(test.input()))))
handled, err := p.PostInbox(context.Background(), resp, req)
@ -2008,6 +2010,7 @@ func TestPostInbox_OriginMustMatch(t *testing.T) {
},
}
for _, test := range tests {
t.Logf("Running table test case %q", test.name)
app, socialApp, fedApp, socialCb, fedCb, d, httpClient, p := NewPubberTest(t)
PreparePostInboxTest(t, app, socialApp, fedApp, socialCb, fedCb, d, httpClient, p)
resp := httptest.NewRecorder()
@ -2038,6 +2041,7 @@ func TestPostInbox_ActivityActorsMustCoverObjectActors(t *testing.T) {
},
}
for _, test := range tests {
t.Logf("Running table test case %q", test.name)
app, socialApp, fedApp, socialCb, fedCb, d, httpClient, p := NewPubberTest(t)
PreparePostInboxTest(t, app, socialApp, fedApp, socialCb, fedCb, d, httpClient, p)
resp := httptest.NewRecorder()
@ -2291,6 +2295,7 @@ func TestPostInbox_Delete_SetsTombstone(t *testing.T) {
return nil
}
for _, test := range tests {
t.Logf("Running table test case %q", test.name)
app.get = func(c context.Context, id url.URL) (PubObject, error) {
return test.input(), nil
}
@ -4241,6 +4246,7 @@ func TestPostOutbox_RequiresObject(t *testing.T) {
app, socialApp, fedApp, socialCb, fedCb, d, httpClient, p := NewPubberTest(t)
PreparePostOutboxTest(t, app, socialApp, fedApp, socialCb, fedCb, d, httpClient, p)
for _, test := range tests {
t.Logf("Running table test case %q", test.name)
resp := httptest.NewRecorder()
req := Sign(ActivityPubRequest(httptest.NewRequest("POST", testOutboxURI, bytes.NewBuffer(MustSerialize(test.input())))))
handled, err := p.PostOutbox(context.Background(), resp, req)
@ -4283,6 +4289,7 @@ func TestPostOutbox_RequiresTarget(t *testing.T) {
app, socialApp, fedApp, socialCb, fedCb, d, httpClient, p := NewPubberTest(t)
PreparePostOutboxTest(t, app, socialApp, fedApp, socialCb, fedCb, d, httpClient, p)
for _, test := range tests {
t.Logf("Running table test case %q", test.name)
resp := httptest.NewRecorder()
req := Sign(ActivityPubRequest(httptest.NewRequest("POST", testOutboxURI, bytes.NewBuffer(MustSerialize(test.input())))))
handled, err := p.PostOutbox(context.Background(), resp, req)

ファイルの表示

@ -11,8 +11,8 @@ import (
)
// ServeActivityPubObject will serve the ActivityPub object with the given IRI
// in the request. Note that requests must be signed with HTTP signatures or
// else they will be denied access. To explicitly opt out of this protection,
// in the request. Note that requests may be signed with HTTP signatures or be
// permitted without any authentication scheme. To change this default behavior,
// use ServeActivityPubObjectWithVerificationMethod instead.
func ServeActivityPubObject(c context.Context, a Application, clock Clock, w http.ResponseWriter, r *http.Request) (handled bool, err error) {
return serveActivityPubObject(c, a, clock, w, r, nil)

528
pub/handlers_test.go ノーマルファイル
ファイルの表示

@ -0,0 +1,528 @@
package pub
import (
"context"
"crypto"
"github.com/go-fed/activity/vocab"
"github.com/go-fed/httpsig"
"net/http"
"net/http/httptest"
"net/url"
"testing"
)
func TestServeActivityPubObject(t *testing.T) {
tests := []struct {
name string
app *MockApplication
clock *MockClock
input *http.Request
expectedCode int
expectedObjFn func() vocab.Serializer
expectHandled bool
}{
{
name: "unsigned request",
app: &MockApplication{
t: t,
get: func(c context.Context, id url.URL) (PubObject, error) {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
}
testNote = &vocab.Note{}
testNote.SetId(*noteIRI)
testNote.AddNameString(noteName)
testNote.AddContentString("This is a simple note")
return testNote, nil
},
owns: func(c context.Context, id url.URL) bool {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
}
return true
},
},
clock: &MockClock{now},
input: ActivityPubRequest(httptest.NewRequest("GET", noteURIString, nil)),
expectedCode: http.StatusOK,
expectedObjFn: func() vocab.Serializer {
testNote = &vocab.Note{}
testNote.SetId(*noteIRI)
testNote.AddNameString(noteName)
testNote.AddContentString("This is a simple note")
return testNote
},
expectHandled: true,
},
{
name: "http signature request",
input: Sign(ActivityPubRequest(httptest.NewRequest("GET", noteURIString, nil))),
app: &MockApplication{
t: t,
getPublicKey: func(c context.Context, publicKeyId string) (crypto.PublicKey, httpsig.Algorithm, url.URL, error) {
if publicKeyId != testPublicKeyId {
t.Fatalf("(%q) expected %s, got %s", testPublicKeyId, publicKeyId)
}
return testPrivateKey.Public(), httpsig.RSA_SHA256, *samIRI, nil
},
getAsVerifiedUser: func(c context.Context, id, user url.URL) (PubObject, error) {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
} else if u := (&user).String(); u != samIRIString {
t.Fatalf("(%q) expected %s, got %s", samIRIString, u)
}
testNote = &vocab.Note{}
testNote.SetId(*noteIRI)
testNote.AddNameString(noteName)
testNote.AddContentString("This is a simple note")
return testNote, nil
},
owns: func(c context.Context, id url.URL) bool {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
}
return true
},
},
clock: &MockClock{now},
expectedCode: http.StatusOK,
expectedObjFn: func() vocab.Serializer {
testNote = &vocab.Note{}
testNote.SetId(*noteIRI)
testNote.AddNameString(noteName)
testNote.AddContentString("This is a simple note")
return testNote
},
expectHandled: true,
},
{
name: "not owned",
input: ActivityPubRequest(httptest.NewRequest("GET", noteURIString, nil)),
app: &MockApplication{
t: t,
owns: func(c context.Context, id url.URL) bool {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
}
return false
},
},
expectedCode: http.StatusNotFound,
expectHandled: true,
},
{
name: "not activitypub get",
input: httptest.NewRequest("GET", noteURIString, nil),
expectHandled: false,
},
{
name: "bad http signature",
input: BadSignature(ActivityPubRequest(httptest.NewRequest("GET", noteURIString, nil))),
app: &MockApplication{
t: t,
getPublicKey: func(c context.Context, publicKeyId string) (crypto.PublicKey, httpsig.Algorithm, url.URL, error) {
if publicKeyId != testPublicKeyId {
t.Fatalf("(%q) expected %s, got %s", testPublicKeyId, publicKeyId)
}
return testPrivateKey.Public(), httpsig.RSA_SHA256, *samIRI, nil
},
owns: func(c context.Context, id url.URL) bool {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
}
return true
},
},
expectedCode: http.StatusForbidden,
expectHandled: true,
},
}
for _, test := range tests {
t.Logf("Running table test case %q", test.name)
resp := httptest.NewRecorder()
handled, err := ServeActivityPubObject(context.Background(), test.app, test.clock, resp, test.input)
if err != nil {
t.Fatalf("(%q) %s", test.name, err)
} else if handled != test.expectHandled {
t.Fatalf("(%q) expected %v, got %v", test.name, test.expectHandled, handled)
} else if test.expectedCode != 0 {
if resp.Code != test.expectedCode {
t.Fatalf("(%q) expected %d, got %d", test.name, test.expectedCode, resp.Code)
}
} else if test.expectedObjFn != nil {
if err := VocabEquals(resp.Body, test.expectedObjFn()); err != nil {
t.Fatalf("(%q) unexpected object: %s", test.name, err)
}
}
}
}
func TestServeActivityPubObjectWithVerificationMethod(t *testing.T) {
tests := []struct {
name string
app *MockApplication
clock *MockClock
verifier *MockSocialAPIVerifier
input *http.Request
expectedCode int
expectedObjFn func() vocab.Serializer
expectHandled bool
}{
{
name: "unsigned request",
app: &MockApplication{
t: t,
get: func(c context.Context, id url.URL) (PubObject, error) {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
}
testNote = &vocab.Note{}
testNote.SetId(*noteIRI)
testNote.AddNameString(noteName)
testNote.AddContentString("This is a simple note")
return testNote, nil
},
owns: func(c context.Context, id url.URL) bool {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
}
return true
},
},
clock: &MockClock{now},
input: ActivityPubRequest(httptest.NewRequest("GET", noteURIString, nil)),
expectedCode: http.StatusOK,
expectedObjFn: func() vocab.Serializer {
testNote = &vocab.Note{}
testNote.SetId(*noteIRI)
testNote.AddNameString(noteName)
testNote.AddContentString("This is a simple note")
return testNote
},
expectHandled: true,
},
{
name: "http signature request",
input: Sign(ActivityPubRequest(httptest.NewRequest("GET", noteURIString, nil))),
app: &MockApplication{
t: t,
getPublicKey: func(c context.Context, publicKeyId string) (crypto.PublicKey, httpsig.Algorithm, url.URL, error) {
if publicKeyId != testPublicKeyId {
t.Fatalf("(%q) expected %s, got %s", testPublicKeyId, publicKeyId)
}
return testPrivateKey.Public(), httpsig.RSA_SHA256, *samIRI, nil
},
getAsVerifiedUser: func(c context.Context, id, user url.URL) (PubObject, error) {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
} else if u := (&user).String(); u != samIRIString {
t.Fatalf("(%q) expected %s, got %s", samIRIString, u)
}
testNote = &vocab.Note{}
testNote.SetId(*noteIRI)
testNote.AddNameString(noteName)
testNote.AddContentString("This is a simple note")
return testNote, nil
},
owns: func(c context.Context, id url.URL) bool {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
}
return true
},
},
clock: &MockClock{now},
expectedCode: http.StatusOK,
expectedObjFn: func() vocab.Serializer {
testNote = &vocab.Note{}
testNote.SetId(*noteIRI)
testNote.AddNameString(noteName)
testNote.AddContentString("This is a simple note")
return testNote
},
expectHandled: true,
},
{
name: "not owned",
input: ActivityPubRequest(httptest.NewRequest("GET", noteURIString, nil)),
app: &MockApplication{
t: t,
owns: func(c context.Context, id url.URL) bool {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
}
return false
},
},
expectedCode: http.StatusNotFound,
expectHandled: true,
},
{
name: "not activitypub get",
input: httptest.NewRequest("GET", noteURIString, nil),
expectHandled: false,
},
{
name: "bad http signature",
input: BadSignature(ActivityPubRequest(httptest.NewRequest("GET", noteURIString, nil))),
app: &MockApplication{
t: t,
getPublicKey: func(c context.Context, publicKeyId string) (crypto.PublicKey, httpsig.Algorithm, url.URL, error) {
if publicKeyId != testPublicKeyId {
t.Fatalf("(%q) expected %s, got %s", testPublicKeyId, publicKeyId)
}
return testPrivateKey.Public(), httpsig.RSA_SHA256, *samIRI, nil
},
owns: func(c context.Context, id url.URL) bool {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
}
return true
},
},
expectedCode: http.StatusForbidden,
expectHandled: true,
},
{
name: "unsigned request passes verifier",
app: &MockApplication{
t: t,
getAsVerifiedUser: func(c context.Context, id, user url.URL) (PubObject, error) {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
} else if u := (&user).String(); u != samIRIString {
t.Fatalf("(%q) expected %s, got %s", samIRIString, u)
}
testNote = &vocab.Note{}
testNote.SetId(*noteIRI)
testNote.AddNameString(noteName)
testNote.AddContentString("This is a simple note")
return testNote, nil
},
owns: func(c context.Context, id url.URL) bool {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
}
return true
},
},
clock: &MockClock{now},
verifier: &MockSocialAPIVerifier{
t: t,
verify: func(r *http.Request) (*url.URL, bool, bool, error) {
return samIRI, true, true, nil
},
},
input: ActivityPubRequest(httptest.NewRequest("GET", noteURIString, nil)),
expectedCode: http.StatusOK,
expectedObjFn: func() vocab.Serializer {
testNote = &vocab.Note{}
testNote.SetId(*noteIRI)
testNote.AddNameString(noteName)
testNote.AddContentString("This is a simple note")
return testNote
},
expectHandled: true,
},
{
name: "http signature request passes verifier",
input: Sign(ActivityPubRequest(httptest.NewRequest("GET", noteURIString, nil))),
app: &MockApplication{
t: t,
getAsVerifiedUser: func(c context.Context, id, user url.URL) (PubObject, error) {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
} else if u := (&user).String(); u != samIRIString {
t.Fatalf("(%q) expected %s, got %s", samIRIString, u)
}
testNote = &vocab.Note{}
testNote.SetId(*noteIRI)
testNote.AddNameString(noteName)
testNote.AddContentString("This is a simple note")
return testNote, nil
},
owns: func(c context.Context, id url.URL) bool {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
}
return true
},
},
clock: &MockClock{now},
verifier: &MockSocialAPIVerifier{
t: t,
verify: func(r *http.Request) (*url.URL, bool, bool, error) {
return samIRI, true, true, nil
},
},
expectedCode: http.StatusOK,
expectedObjFn: func() vocab.Serializer {
testNote = &vocab.Note{}
testNote.SetId(*noteIRI)
testNote.AddNameString(noteName)
testNote.AddContentString("This is a simple note")
return testNote
},
expectHandled: true,
},
{
name: "verifier authed unauthz",
app: &MockApplication{
t: t,
owns: func(c context.Context, id url.URL) bool {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
}
return true
},
},
clock: &MockClock{now},
verifier: &MockSocialAPIVerifier{
t: t,
verify: func(r *http.Request) (*url.URL, bool, bool, error) {
return samIRI, true, false, nil
},
},
input: ActivityPubRequest(httptest.NewRequest("GET", noteURIString, nil)),
expectedCode: http.StatusForbidden,
expectHandled: true,
},
{
name: "verifier unauthed unauthz",
app: &MockApplication{
t: t,
owns: func(c context.Context, id url.URL) bool {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
}
return true
},
},
clock: &MockClock{now},
verifier: &MockSocialAPIVerifier{
t: t,
verify: func(r *http.Request) (*url.URL, bool, bool, error) {
return nil, false, false, nil
},
},
input: ActivityPubRequest(httptest.NewRequest("GET", noteURIString, nil)),
expectedCode: http.StatusBadRequest,
expectHandled: true,
},
{
name: "verifier unauthed authz unsigned fails",
app: &MockApplication{
t: t,
owns: func(c context.Context, id url.URL) bool {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
}
return true
},
},
clock: &MockClock{now},
verifier: &MockSocialAPIVerifier{
t: t,
verify: func(r *http.Request) (*url.URL, bool, bool, error) {
return nil, false, true, nil
},
},
input: ActivityPubRequest(httptest.NewRequest("GET", noteURIString, nil)),
expectedCode: http.StatusBadRequest,
expectHandled: true,
},
{
name: "verifier unauthed authz signed success",
input: Sign(ActivityPubRequest(httptest.NewRequest("GET", noteURIString, nil))),
app: &MockApplication{
t: t,
getPublicKey: func(c context.Context, publicKeyId string) (crypto.PublicKey, httpsig.Algorithm, url.URL, error) {
if publicKeyId != testPublicKeyId {
t.Fatalf("(%q) expected %s, got %s", testPublicKeyId, publicKeyId)
}
return testPrivateKey.Public(), httpsig.RSA_SHA256, *samIRI, nil
},
getAsVerifiedUser: func(c context.Context, id, user url.URL) (PubObject, error) {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
} else if u := (&user).String(); u != samIRIString {
t.Fatalf("(%q) expected %s, got %s", samIRIString, u)
}
testNote = &vocab.Note{}
testNote.SetId(*noteIRI)
testNote.AddNameString(noteName)
testNote.AddContentString("This is a simple note")
return testNote, nil
},
owns: func(c context.Context, id url.URL) bool {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
}
return true
},
},
clock: &MockClock{now},
verifier: &MockSocialAPIVerifier{
t: t,
verify: func(r *http.Request) (*url.URL, bool, bool, error) {
return nil, false, true, nil
},
},
expectedCode: http.StatusOK,
expectedObjFn: func() vocab.Serializer {
testNote = &vocab.Note{}
testNote.SetId(*noteIRI)
testNote.AddNameString(noteName)
testNote.AddContentString("This is a simple note")
return testNote
},
expectHandled: true,
},
{
name: "verifier unauthed authz unsigned fails with bad impl returning user",
app: &MockApplication{
t: t,
owns: func(c context.Context, id url.URL) bool {
if s := (&id).String(); s != noteURIString {
t.Fatalf("(%q) expected %s, got %s", noteURIString, s)
}
return true
},
},
clock: &MockClock{now},
verifier: &MockSocialAPIVerifier{
t: t,
verify: func(r *http.Request) (*url.URL, bool, bool, error) {
return samIRI, false, true, nil
},
},
input: ActivityPubRequest(httptest.NewRequest("GET", noteURIString, nil)),
expectedCode: http.StatusBadRequest,
expectHandled: true,
},
}
for _, test := range tests {
t.Logf("Running table test case %q", test.name)
resp := httptest.NewRecorder()
var handled bool
var err error
if test.verifier != nil {
handled, err = ServeActivityPubObjectWithVerificationMethod(context.Background(), test.app, test.clock, resp, test.input, test.verifier)
} else {
handled, err = ServeActivityPubObjectWithVerificationMethod(context.Background(), test.app, test.clock, resp, test.input, nil)
}
if err != nil {
t.Fatalf("(%q) %s", test.name, err)
} else if handled != test.expectHandled {
t.Fatalf("(%q) expected %v, got %v", test.name, test.expectHandled, handled)
} else if test.expectedCode != 0 {
if resp.Code != test.expectedCode {
t.Fatalf("(%q) expected %d, got %d", test.name, test.expectedCode, resp.Code)
}
} else if test.expectedObjFn != nil {
if err := VocabEquals(resp.Body, test.expectedObjFn()); err != nil {
t.Fatalf("(%q) unexpected object: %s", test.name, err)
}
}
}
}