diff --git a/pub/activity.go b/pub/activity.go index c1de39f..6a1d445 100644 --- a/pub/activity.go +++ b/pub/activity.go @@ -40,6 +40,10 @@ type Activity interface { SetActivityStreamsObject(i vocab.ActivityStreamsObjectProperty) // SetActivityStreamsTo sets the "to" property. SetActivityStreamsTo(i vocab.ActivityStreamsToProperty) + // SetActivityStreamsBto sets the "bto" property. + SetActivityStreamsBto(i vocab.ActivityStreamsBtoProperty) + // SetActivityStreamsBcc sets the "bcc" property. + SetActivityStreamsBcc(i vocab.ActivityStreamsBccProperty) // SetActivityStreamsAttributedTo sets the "attributedTo" property. SetActivityStreamsAttributedTo(i vocab.ActivityStreamsAttributedToProperty) } diff --git a/pub/pub_test.go b/pub/pub_test.go index 66d6160..5621c7e 100644 --- a/pub/pub_test.go +++ b/pub/pub_test.go @@ -24,6 +24,8 @@ const ( testFederatedActorIRI2 = "https://other.example.com/addison" testFederatedActorIRI3 = "https://other.example.com/sam" testFederatedActorIRI4 = "https://other.example.com/jessie" + testFederatedInboxIRI = "https://other.example.com/dakota/inbox" + testFederatedInboxIRI2 = "https://other.example.com/addison/inbox" testNoteId1 = "https://example.com/note/1" testNoteId2 = "https://example.com/note/2" testNewActivityIRI = "https://example.com/new/1" @@ -126,6 +128,12 @@ var ( testOrderedCollectionWithBothFederatedIds vocab.ActivityStreamsOrderedCollectionPage // testPerson is a Person. testPerson vocab.ActivityStreamsPerson + // testMyPerson is my Person. + testMyPerson vocab.ActivityStreamsPerson + // testFederatedPerson1 is a federated Person. + testFederatedPerson1 vocab.ActivityStreamsPerson + // testFederatedPerson2 is a federated Person. + testFederatedPerson2 vocab.ActivityStreamsPerson // testService is a Service. testService vocab.ActivityStreamsService // testCollectionOfActors is a collection of actors. @@ -343,6 +351,39 @@ func setupData() { id.Set(mustParse(testPersonIRI)) testPerson.SetJSONLDId(id) }() + // testMyPerson + func() { + testMyPerson = streams.NewActivityStreamsPerson() + id := streams.NewJSONLDIdProperty() + id.Set(mustParse(testPersonIRI)) + testMyPerson.SetJSONLDId(id) + inbox := streams.NewActivityStreamsInboxProperty() + inbox.SetIRI(mustParse(testMyInboxIRI)) + testMyPerson.SetActivityStreamsInbox(inbox) + outbox := streams.NewActivityStreamsOutboxProperty() + outbox.SetIRI(mustParse(testMyOutboxIRI)) + testMyPerson.SetActivityStreamsOutbox(outbox) + }() + // testFederatedPerson1 + func() { + testFederatedPerson1 = streams.NewActivityStreamsPerson() + id := streams.NewJSONLDIdProperty() + id.SetIRI(mustParse(testFederatedActorIRI)) + testFederatedPerson1.SetJSONLDId(id) + inbox := streams.NewActivityStreamsInboxProperty() + inbox.SetIRI(mustParse(testFederatedInboxIRI)) + testFederatedPerson1.SetActivityStreamsInbox(inbox) + }() + // testFederatedPerson2 + func() { + testFederatedPerson2 = streams.NewActivityStreamsPerson() + id := streams.NewJSONLDIdProperty() + id.Set(mustParse(testFederatedActorIRI2)) + testFederatedPerson2.SetJSONLDId(id) + inbox := streams.NewActivityStreamsInboxProperty() + inbox.SetIRI(mustParse(testFederatedInboxIRI2)) + testFederatedPerson2.SetActivityStreamsInbox(inbox) + }() // testService func() { testService = streams.NewActivityStreamsService() diff --git a/pub/side_effect_actor_test.go b/pub/side_effect_actor_test.go index bc7de35..35c8c39 100644 --- a/pub/side_effect_actor_test.go +++ b/pub/side_effect_actor_test.go @@ -2,11 +2,14 @@ package pub import ( "context" - "github.com/go-fed/activity/streams/vocab" - "github.com/golang/mock/gomock" + "fmt" "net/http/httptest" "net/url" "testing" + + "github.com/go-fed/activity/streams" + "github.com/go-fed/activity/streams/vocab" + "github.com/golang/mock/gomock" ) // TestPassThroughMethods tests the methods that pass-through to other @@ -1068,50 +1071,583 @@ func TestAddNewIds(t *testing.T) { // TestDeliver ensures federated delivery of an activity happens correctly to // the ActivityPub specification. func TestDeliver(t *testing.T) { + baseActivityFn := func() vocab.ActivityStreamsCreate { + act := streams.NewActivityStreamsCreate() + id := streams.NewJSONLDIdProperty() + id.Set(mustParse(testNewActivityIRI)) + act.SetJSONLDId(id) + op := streams.NewActivityStreamsObjectProperty() + note := streams.NewActivityStreamsNote() + op.AppendActivityStreamsNote(note) + act.SetActivityStreamsObject(op) + return act + } + ctx := context.Background() + setupFn := func(ctl *gomock.Controller) (c *MockCommonBehavior, fp *MockFederatingProtocol, sp *MockSocialProtocol, db *MockDatabase, cl *MockClock, a DelegateActor) { + setupData() + c = NewMockCommonBehavior(ctl) + fp = NewMockFederatingProtocol(ctl) + sp = NewMockSocialProtocol(ctl) + db = NewMockDatabase(ctl) + cl = NewMockClock(ctl) + a = &sideEffectActor{ + common: c, + s2s: fp, + c2s: sp, + db: db, + clock: cl, + } + return + } t.Run("SendToRecipientsInTo", func(t *testing.T) { - t.Errorf("Not yet implemented.") + // Setup + ctl := gomock.NewController(t) + defer ctl.Finish() + c, mockFp, _, mockDb, _, a := setupFn(ctl) + mockTp := NewMockTransport(ctl) + act := baseActivityFn() + to := streams.NewActivityStreamsToProperty() + to.AppendIRI(mustParse(testFederatedActorIRI)) + to.AppendIRI(mustParse(testFederatedActorIRI2)) + act.SetActivityStreamsTo(to) + expectRecip := []*url.URL{ + mustParse(testFederatedInboxIRI), + mustParse(testFederatedInboxIRI2), + } + // Mock + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockFp.EXPECT().MaxDeliveryRecursionDepth(ctx).Return(1) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI)).Return( + mustSerializeToBytes(testFederatedPerson1), nil) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI2)).Return( + mustSerializeToBytes(testFederatedPerson2), nil) + mockDb.EXPECT().Lock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().ActorForOutbox(ctx, mustParse(testMyOutboxIRI)).Return( + mustParse(testPersonIRI), nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().Lock(ctx, mustParse(testPersonIRI)) + mockDb.EXPECT().Get(ctx, mustParse(testPersonIRI)).Return( + testMyPerson, nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testPersonIRI)) + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockTp.EXPECT().BatchDeliver(ctx, mustSerializeToBytes(act), expectRecip) + // Run & Verify + err := a.Deliver(ctx, mustParse(testMyOutboxIRI), act) + assertEqual(t, err, nil) }) t.Run("SendToRecipientsInBto", func(t *testing.T) { - t.Errorf("Not yet implemented.") + // Setup + ctl := gomock.NewController(t) + defer ctl.Finish() + c, mockFp, _, mockDb, _, a := setupFn(ctl) + mockTp := NewMockTransport(ctl) + act := baseActivityFn() + bto := streams.NewActivityStreamsBtoProperty() + bto.AppendIRI(mustParse(testFederatedActorIRI)) + bto.AppendIRI(mustParse(testFederatedActorIRI2)) + act.SetActivityStreamsBto(bto) + expectAct := baseActivityFn() // Ensure Bto is stripped + expectRecip := []*url.URL{ + mustParse(testFederatedInboxIRI), + mustParse(testFederatedInboxIRI2), + } + // Mock + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockFp.EXPECT().MaxDeliveryRecursionDepth(ctx).Return(1) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI)).Return( + mustSerializeToBytes(testFederatedPerson1), nil) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI2)).Return( + mustSerializeToBytes(testFederatedPerson2), nil) + mockDb.EXPECT().Lock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().ActorForOutbox(ctx, mustParse(testMyOutboxIRI)).Return( + mustParse(testPersonIRI), nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().Lock(ctx, mustParse(testPersonIRI)) + mockDb.EXPECT().Get(ctx, mustParse(testPersonIRI)).Return( + testMyPerson, nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testPersonIRI)) + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockTp.EXPECT().BatchDeliver(ctx, mustSerializeToBytes(expectAct), expectRecip) + // Run & Verify + err := a.Deliver(ctx, mustParse(testMyOutboxIRI), act) + assertEqual(t, err, nil) }) t.Run("SendToRecipientsInCc", func(t *testing.T) { - t.Errorf("Not yet implemented.") + // Setup + ctl := gomock.NewController(t) + defer ctl.Finish() + c, mockFp, _, mockDb, _, a := setupFn(ctl) + mockTp := NewMockTransport(ctl) + act := baseActivityFn() + cc := streams.NewActivityStreamsCcProperty() + cc.AppendIRI(mustParse(testFederatedActorIRI)) + cc.AppendIRI(mustParse(testFederatedActorIRI2)) + act.SetActivityStreamsCc(cc) + expectRecip := []*url.URL{ + mustParse(testFederatedInboxIRI), + mustParse(testFederatedInboxIRI2), + } + // Mock + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockFp.EXPECT().MaxDeliveryRecursionDepth(ctx).Return(1) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI)).Return( + mustSerializeToBytes(testFederatedPerson1), nil) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI2)).Return( + mustSerializeToBytes(testFederatedPerson2), nil) + mockDb.EXPECT().Lock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().ActorForOutbox(ctx, mustParse(testMyOutboxIRI)).Return( + mustParse(testPersonIRI), nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().Lock(ctx, mustParse(testPersonIRI)) + mockDb.EXPECT().Get(ctx, mustParse(testPersonIRI)).Return( + testMyPerson, nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testPersonIRI)) + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockTp.EXPECT().BatchDeliver(ctx, mustSerializeToBytes(act), expectRecip) + // Run & Verify + err := a.Deliver(ctx, mustParse(testMyOutboxIRI), act) + assertEqual(t, err, nil) }) t.Run("SendToRecipientsInBcc", func(t *testing.T) { - t.Errorf("Not yet implemented.") + // Setup + ctl := gomock.NewController(t) + defer ctl.Finish() + c, mockFp, _, mockDb, _, a := setupFn(ctl) + mockTp := NewMockTransport(ctl) + act := baseActivityFn() + bcc := streams.NewActivityStreamsBccProperty() + bcc.AppendIRI(mustParse(testFederatedActorIRI)) + bcc.AppendIRI(mustParse(testFederatedActorIRI2)) + act.SetActivityStreamsBcc(bcc) + expectAct := baseActivityFn() // Ensure Bcc is stripped + expectRecip := []*url.URL{ + mustParse(testFederatedInboxIRI), + mustParse(testFederatedInboxIRI2), + } + // Mock + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockFp.EXPECT().MaxDeliveryRecursionDepth(ctx).Return(1) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI)).Return( + mustSerializeToBytes(testFederatedPerson1), nil) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI2)).Return( + mustSerializeToBytes(testFederatedPerson2), nil) + mockDb.EXPECT().Lock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().ActorForOutbox(ctx, mustParse(testMyOutboxIRI)).Return( + mustParse(testPersonIRI), nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().Lock(ctx, mustParse(testPersonIRI)) + mockDb.EXPECT().Get(ctx, mustParse(testPersonIRI)).Return( + testMyPerson, nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testPersonIRI)) + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockTp.EXPECT().BatchDeliver(ctx, mustSerializeToBytes(expectAct), expectRecip) + // Run & Verify + err := a.Deliver(ctx, mustParse(testMyOutboxIRI), act) + assertEqual(t, err, nil) }) t.Run("SendToRecipientsInAudience", func(t *testing.T) { - t.Errorf("Not yet implemented.") + // Setup + ctl := gomock.NewController(t) + defer ctl.Finish() + c, mockFp, _, mockDb, _, a := setupFn(ctl) + mockTp := NewMockTransport(ctl) + act := baseActivityFn() + aud := streams.NewActivityStreamsAudienceProperty() + aud.AppendIRI(mustParse(testFederatedActorIRI)) + aud.AppendIRI(mustParse(testFederatedActorIRI2)) + act.SetActivityStreamsAudience(aud) + expectRecip := []*url.URL{ + mustParse(testFederatedInboxIRI), + mustParse(testFederatedInboxIRI2), + } + // Mock + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockFp.EXPECT().MaxDeliveryRecursionDepth(ctx).Return(1) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI)).Return( + mustSerializeToBytes(testFederatedPerson1), nil) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI2)).Return( + mustSerializeToBytes(testFederatedPerson2), nil) + mockDb.EXPECT().Lock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().ActorForOutbox(ctx, mustParse(testMyOutboxIRI)).Return( + mustParse(testPersonIRI), nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().Lock(ctx, mustParse(testPersonIRI)) + mockDb.EXPECT().Get(ctx, mustParse(testPersonIRI)).Return( + testMyPerson, nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testPersonIRI)) + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockTp.EXPECT().BatchDeliver(ctx, mustSerializeToBytes(act), expectRecip) + // Run & Verify + err := a.Deliver(ctx, mustParse(testMyOutboxIRI), act) + assertEqual(t, err, nil) }) t.Run("DoesNotSendToPublicIRI", func(t *testing.T) { - t.Errorf("Not yet implemented.") + // Setup + ctl := gomock.NewController(t) + defer ctl.Finish() + c, mockFp, _, mockDb, _, a := setupFn(ctl) + mockTp := NewMockTransport(ctl) + act := baseActivityFn() + to := streams.NewActivityStreamsToProperty() + to.AppendIRI(mustParse(testFederatedActorIRI)) + to.AppendIRI(mustParse(testFederatedActorIRI2)) + to.AppendIRI(mustParse(PublicActivityPubIRI)) + act.SetActivityStreamsTo(to) + expectRecip := []*url.URL{ + mustParse(testFederatedInboxIRI), + mustParse(testFederatedInboxIRI2), + } + // Mock + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockFp.EXPECT().MaxDeliveryRecursionDepth(ctx).Return(1) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI)).Return( + mustSerializeToBytes(testFederatedPerson1), nil) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI2)).Return( + mustSerializeToBytes(testFederatedPerson2), nil) + mockDb.EXPECT().Lock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().ActorForOutbox(ctx, mustParse(testMyOutboxIRI)).Return( + mustParse(testPersonIRI), nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().Lock(ctx, mustParse(testPersonIRI)) + mockDb.EXPECT().Get(ctx, mustParse(testPersonIRI)).Return( + testMyPerson, nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testPersonIRI)) + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockTp.EXPECT().BatchDeliver(ctx, mustSerializeToBytes(act), expectRecip) + // Run & Verify + err := a.Deliver(ctx, mustParse(testMyOutboxIRI), act) + assertEqual(t, err, nil) }) t.Run("RecursivelyResolveCollectionActors", func(t *testing.T) { - t.Errorf("Not yet implemented.") + // Setup + ctl := gomock.NewController(t) + defer ctl.Finish() + c, mockFp, _, mockDb, _, a := setupFn(ctl) + mockTp := NewMockTransport(ctl) + act := baseActivityFn() + to := streams.NewActivityStreamsToProperty() + to.AppendIRI(mustParse(testAudienceIRI)) + act.SetActivityStreamsTo(to) + expectRecip := []*url.URL{ + mustParse(testFederatedInboxIRI), + mustParse(testFederatedInboxIRI2), + } + // Mock + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockFp.EXPECT().MaxDeliveryRecursionDepth(ctx).Return(2) + mockTp.EXPECT().Dereference(ctx, mustParse(testAudienceIRI)).Return( + mustSerializeToBytes(testCollectionOfActors), nil) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI)).Return( + mustSerializeToBytes(testFederatedPerson1), nil) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI2)).Return( + mustSerializeToBytes(testFederatedPerson2), nil) + mockDb.EXPECT().Lock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().ActorForOutbox(ctx, mustParse(testMyOutboxIRI)).Return( + mustParse(testPersonIRI), nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().Lock(ctx, mustParse(testPersonIRI)) + mockDb.EXPECT().Get(ctx, mustParse(testPersonIRI)).Return( + testMyPerson, nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testPersonIRI)) + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockTp.EXPECT().BatchDeliver(ctx, mustSerializeToBytes(act), expectRecip) + // Run & Verify + err := a.Deliver(ctx, mustParse(testMyOutboxIRI), act) + assertEqual(t, err, nil) }) t.Run("RecursivelyResolveOrderedCollectionActors", func(t *testing.T) { - t.Errorf("Not yet implemented.") + // Setup + ctl := gomock.NewController(t) + defer ctl.Finish() + c, mockFp, _, mockDb, _, a := setupFn(ctl) + mockTp := NewMockTransport(ctl) + act := baseActivityFn() + to := streams.NewActivityStreamsToProperty() + to.AppendIRI(mustParse(testAudienceIRI)) + act.SetActivityStreamsTo(to) + expectRecip := []*url.URL{ + mustParse(testFederatedInboxIRI), + mustParse(testFederatedInboxIRI2), + } + // Mock + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockFp.EXPECT().MaxDeliveryRecursionDepth(ctx).Return(2) + mockTp.EXPECT().Dereference(ctx, mustParse(testAudienceIRI)).Return( + mustSerializeToBytes(testOrderedCollectionOfActors), nil) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI3)).Return( + mustSerializeToBytes(testFederatedPerson1), nil) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI4)).Return( + mustSerializeToBytes(testFederatedPerson2), nil) + mockDb.EXPECT().Lock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().ActorForOutbox(ctx, mustParse(testMyOutboxIRI)).Return( + mustParse(testPersonIRI), nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().Lock(ctx, mustParse(testPersonIRI)) + mockDb.EXPECT().Get(ctx, mustParse(testPersonIRI)).Return( + testMyPerson, nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testPersonIRI)) + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockTp.EXPECT().BatchDeliver(ctx, mustSerializeToBytes(act), expectRecip) + // Run & Verify + err := a.Deliver(ctx, mustParse(testMyOutboxIRI), act) + assertEqual(t, err, nil) }) t.Run("DoesNotRecursivelyResolveCollectionActorsIfExceedingMaxDepth", func(t *testing.T) { - t.Errorf("Not yet implemented.") - }) - t.Run("DoesNotSendIfMoreThanOneAttributedTo", func(t *testing.T) { - t.Errorf("Not yet implemented.") - }) - t.Run("DoesNotSendIfThisActorIsNotInAttributedTo", func(t *testing.T) { - t.Errorf("Not yet implemented.") + // Setup + ctl := gomock.NewController(t) + defer ctl.Finish() + c, mockFp, _, mockDb, _, a := setupFn(ctl) + mockTp := NewMockTransport(ctl) + act := baseActivityFn() + to := streams.NewActivityStreamsToProperty() + to.AppendIRI(mustParse(testAudienceIRI)) + act.SetActivityStreamsTo(to) + // Mock + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockFp.EXPECT().MaxDeliveryRecursionDepth(ctx).Return(1) + mockTp.EXPECT().Dereference(ctx, mustParse(testAudienceIRI)).Return( + mustSerializeToBytes(testCollectionOfActors), nil) + mockDb.EXPECT().Lock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().ActorForOutbox(ctx, mustParse(testMyOutboxIRI)).Return( + mustParse(testPersonIRI), nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().Lock(ctx, mustParse(testPersonIRI)) + mockDb.EXPECT().Get(ctx, mustParse(testPersonIRI)).Return( + testMyPerson, nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testPersonIRI)) + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockTp.EXPECT().BatchDeliver(ctx, mustSerializeToBytes(act), nil) + // Run & Verify + err := a.Deliver(ctx, mustParse(testMyOutboxIRI), act) + assertEqual(t, err, nil) }) t.Run("DedupesRecipients", func(t *testing.T) { - t.Errorf("Not yet implemented.") + // Setup + ctl := gomock.NewController(t) + defer ctl.Finish() + c, mockFp, _, mockDb, _, a := setupFn(ctl) + mockTp := NewMockTransport(ctl) + act := baseActivityFn() + to := streams.NewActivityStreamsToProperty() + to.AppendIRI(mustParse(testFederatedActorIRI)) + to.AppendIRI(mustParse(testFederatedActorIRI2)) + act.SetActivityStreamsTo(to) + bto := streams.NewActivityStreamsBtoProperty() + bto.AppendIRI(mustParse(testFederatedActorIRI)) + bto.AppendIRI(mustParse(testFederatedActorIRI2)) + act.SetActivityStreamsBto(bto) + cc := streams.NewActivityStreamsCcProperty() + cc.AppendIRI(mustParse(testFederatedActorIRI)) + cc.AppendIRI(mustParse(testFederatedActorIRI2)) + act.SetActivityStreamsCc(cc) + bcc := streams.NewActivityStreamsBccProperty() + bcc.AppendIRI(mustParse(testFederatedActorIRI)) + bcc.AppendIRI(mustParse(testFederatedActorIRI2)) + act.SetActivityStreamsBcc(bcc) + expectAct := baseActivityFn() // Ensure Bcc & Bto are stripped + expectAct.SetActivityStreamsTo(to) + expectAct.SetActivityStreamsCc(cc) + expectRecip := []*url.URL{ + mustParse(testFederatedInboxIRI), + mustParse(testFederatedInboxIRI2), + } + // Mock + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockFp.EXPECT().MaxDeliveryRecursionDepth(ctx).Return(1) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI)).Return( + mustSerializeToBytes(testFederatedPerson1), nil).Times(4) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI2)).Return( + mustSerializeToBytes(testFederatedPerson2), nil).Times(4) + mockDb.EXPECT().Lock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().ActorForOutbox(ctx, mustParse(testMyOutboxIRI)).Return( + mustParse(testPersonIRI), nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().Lock(ctx, mustParse(testPersonIRI)) + mockDb.EXPECT().Get(ctx, mustParse(testPersonIRI)).Return( + testMyPerson, nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testPersonIRI)) + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockTp.EXPECT().BatchDeliver(ctx, mustSerializeToBytes(expectAct), expectRecip) + // Run & Verify + err := a.Deliver(ctx, mustParse(testMyOutboxIRI), act) + assertEqual(t, err, nil) }) - t.Run("StripsBto", func(t *testing.T) { - t.Errorf("Not yet implemented.") + t.Run("StripsBtoOnObject", func(t *testing.T) { + // Setup + ctl := gomock.NewController(t) + defer ctl.Finish() + c, mockFp, _, mockDb, _, a := setupFn(ctl) + mockTp := NewMockTransport(ctl) + act := baseActivityFn() + bto := streams.NewActivityStreamsBtoProperty() + bto.AppendIRI(mustParse(testFederatedActorIRI)) + bto.AppendIRI(mustParse(testFederatedActorIRI2)) + act.SetActivityStreamsBto(bto) + act.GetActivityStreamsObject().At(0).GetActivityStreamsNote().SetActivityStreamsBto(bto) + expectAct := baseActivityFn() // Ensure Bto is stripped + expectRecip := []*url.URL{ + mustParse(testFederatedInboxIRI), + mustParse(testFederatedInboxIRI2), + } + // Mock + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockFp.EXPECT().MaxDeliveryRecursionDepth(ctx).Return(1) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI)).Return( + mustSerializeToBytes(testFederatedPerson1), nil) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI2)).Return( + mustSerializeToBytes(testFederatedPerson2), nil) + mockDb.EXPECT().Lock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().ActorForOutbox(ctx, mustParse(testMyOutboxIRI)).Return( + mustParse(testPersonIRI), nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().Lock(ctx, mustParse(testPersonIRI)) + mockDb.EXPECT().Get(ctx, mustParse(testPersonIRI)).Return( + testMyPerson, nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testPersonIRI)) + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockTp.EXPECT().BatchDeliver(ctx, mustSerializeToBytes(expectAct), expectRecip) + // Run & Verify + err := a.Deliver(ctx, mustParse(testMyOutboxIRI), act) + assertEqual(t, err, nil) }) - t.Run("StripsBcc", func(t *testing.T) { - t.Errorf("Not yet implemented.") + t.Run("StripsBccOnObject", func(t *testing.T) { + // Setup + ctl := gomock.NewController(t) + defer ctl.Finish() + c, mockFp, _, mockDb, _, a := setupFn(ctl) + mockTp := NewMockTransport(ctl) + act := baseActivityFn() + bcc := streams.NewActivityStreamsBccProperty() + bcc.AppendIRI(mustParse(testFederatedActorIRI)) + bcc.AppendIRI(mustParse(testFederatedActorIRI2)) + act.SetActivityStreamsBcc(bcc) + act.GetActivityStreamsObject().At(0).GetActivityStreamsNote().SetActivityStreamsBcc(bcc) + expectAct := baseActivityFn() // Ensure Bto is stripped + expectRecip := []*url.URL{ + mustParse(testFederatedInboxIRI), + mustParse(testFederatedInboxIRI2), + } + // Mock + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockFp.EXPECT().MaxDeliveryRecursionDepth(ctx).Return(1) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI)).Return( + mustSerializeToBytes(testFederatedPerson1), nil) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI2)).Return( + mustSerializeToBytes(testFederatedPerson2), nil) + mockDb.EXPECT().Lock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().ActorForOutbox(ctx, mustParse(testMyOutboxIRI)).Return( + mustParse(testPersonIRI), nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().Lock(ctx, mustParse(testPersonIRI)) + mockDb.EXPECT().Get(ctx, mustParse(testPersonIRI)).Return( + testMyPerson, nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testPersonIRI)) + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockTp.EXPECT().BatchDeliver(ctx, mustSerializeToBytes(expectAct), expectRecip) + // Run & Verify + err := a.Deliver(ctx, mustParse(testMyOutboxIRI), act) + assertEqual(t, err, nil) }) - t.Run("ReturnsErrorIfAnyTransportRequestsFail", func(t *testing.T) { - t.Errorf("Not yet implemented.") + t.Run("DoesNotReturnErrorIfDereferenceRecipientFails", func(t *testing.T) { + // Setup + ctl := gomock.NewController(t) + defer ctl.Finish() + c, mockFp, _, mockDb, _, a := setupFn(ctl) + mockTp := NewMockTransport(ctl) + act := baseActivityFn() + to := streams.NewActivityStreamsToProperty() + to.AppendIRI(mustParse(testFederatedActorIRI)) + to.AppendIRI(mustParse(testFederatedActorIRI2)) + act.SetActivityStreamsTo(to) + expectRecip := []*url.URL{ + mustParse(testFederatedInboxIRI2), + } + // Mock + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockFp.EXPECT().MaxDeliveryRecursionDepth(ctx).Return(1) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI)).Return( + []byte{}, fmt.Errorf("test error")) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI2)).Return( + mustSerializeToBytes(testFederatedPerson2), nil) + mockDb.EXPECT().Lock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().ActorForOutbox(ctx, mustParse(testMyOutboxIRI)).Return( + mustParse(testPersonIRI), nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().Lock(ctx, mustParse(testPersonIRI)) + mockDb.EXPECT().Get(ctx, mustParse(testPersonIRI)).Return( + testMyPerson, nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testPersonIRI)) + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockTp.EXPECT().BatchDeliver(ctx, mustSerializeToBytes(act), expectRecip) + // Run & Verify + err := a.Deliver(ctx, mustParse(testMyOutboxIRI), act) + assertEqual(t, err, nil) + }) + t.Run("ReturnsErrorIfBatchDeliverFails", func(t *testing.T) { + // Setup + ctl := gomock.NewController(t) + defer ctl.Finish() + c, mockFp, _, mockDb, _, a := setupFn(ctl) + mockTp := NewMockTransport(ctl) + act := baseActivityFn() + to := streams.NewActivityStreamsToProperty() + to.AppendIRI(mustParse(testFederatedActorIRI)) + to.AppendIRI(mustParse(testFederatedActorIRI2)) + act.SetActivityStreamsTo(to) + expectRecip := []*url.URL{ + mustParse(testFederatedInboxIRI), + mustParse(testFederatedInboxIRI2), + } + expectErr := fmt.Errorf("test error") + // Mock + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockFp.EXPECT().MaxDeliveryRecursionDepth(ctx).Return(1) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI)).Return( + mustSerializeToBytes(testFederatedPerson1), nil) + mockTp.EXPECT().Dereference(ctx, mustParse(testFederatedActorIRI2)).Return( + mustSerializeToBytes(testFederatedPerson2), nil) + mockDb.EXPECT().Lock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().ActorForOutbox(ctx, mustParse(testMyOutboxIRI)).Return( + mustParse(testPersonIRI), nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testMyOutboxIRI)) + mockDb.EXPECT().Lock(ctx, mustParse(testPersonIRI)) + mockDb.EXPECT().Get(ctx, mustParse(testPersonIRI)).Return( + testMyPerson, nil) + mockDb.EXPECT().Unlock(ctx, mustParse(testPersonIRI)) + c.EXPECT().NewTransport(ctx, mustParse(testMyOutboxIRI), goFedUserAgent()).Return( + mockTp, nil) + mockTp.EXPECT().BatchDeliver(ctx, mustSerializeToBytes(act), expectRecip).Return( + expectErr) + // Run & Verify + err := a.Deliver(ctx, mustParse(testMyOutboxIRI), act) + assertEqual(t, err, expectErr) }) } diff --git a/pub/util.go b/pub/util.go index 3f2e671..ec3e2e8 100644 --- a/pub/util.go +++ b/pub/util.go @@ -412,16 +412,17 @@ func dedupeIRIs(recipients, ignored []*url.URL) (out []*url.URL) { // Note that this requirement of the specification is under "Section 6: Client // to Server Interactions", the Social API, and not the Federative API. func stripHiddenRecipients(activity Activity) { - bto := activity.GetActivityStreamsBto() - if bto != nil { - for i := bto.Len() - 1; i >= 0; i-- { - bto.Remove(i) - } - } - bcc := activity.GetActivityStreamsBcc() - if bcc != nil { - for i := bcc.Len() - 1; i >= 0; i-- { - bcc.Remove(i) + activity.SetActivityStreamsBto(nil) + activity.SetActivityStreamsBcc(nil) + op := activity.GetActivityStreamsObject() + if op != nil { + for iter := op.Begin(); iter != op.End(); iter = iter.Next() { + if v, ok := iter.GetType().(btoer); ok { + v.SetActivityStreamsBto(nil) + } + if v, ok := iter.GetType().(bccer); ok { + v.SetActivityStreamsBcc(nil) + } } } }