Add a layer of safety around required ActivityPub Actor fields (#4703)
* fix(ap): add safe constructors, getters, and validators to ActivityPub actors to address #4701 * chore: replace nil check with the new Validate() method * chore(test): added test to verify the values extracted from actors * fix: return the specific error type + test for it * fix(ap): add recovery for each AP inbox worker for a worst case scenario * fix(ap): add additional safe accessor methods to other AP entities other than actors * chore(tests): add tests for the new safe accessors * fix(ap): handle empty public keys in AP actors
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package apmodels
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -46,6 +47,8 @@ func makeFakeService() vocab.ActivityStreamsService {
|
||||
service.SetActivityStreamsIcon(icon)
|
||||
|
||||
publicKeyProperty := streams.NewW3IDSecurityV1PublicKeyProperty()
|
||||
publicKeyType := streams.NewW3IDSecurityV1PublicKey()
|
||||
publicKeyProperty.AppendW3IDSecurityV1PublicKey(publicKeyType)
|
||||
service.SetW3IDSecurityV1PublicKey(publicKeyProperty)
|
||||
|
||||
return service
|
||||
@@ -65,34 +68,6 @@ func TestMain(m *testing.M) {
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestMakeActorFromExternalAPEntity(t *testing.T) {
|
||||
service := makeFakeService()
|
||||
actor, err := MakeActorFromExernalAPEntity(service)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if actor.ActorIri != service.GetJSONLDId().GetIRI() {
|
||||
t.Errorf("actor.ID = %v, want %v", actor.ActorIri, service.GetJSONLDId().GetIRI())
|
||||
}
|
||||
|
||||
if actor.Name != service.GetActivityStreamsName().At(0).GetXMLSchemaString() {
|
||||
t.Errorf("actor.Name = %v, want %v", actor.Name, service.GetActivityStreamsName().At(0).GetXMLSchemaString())
|
||||
}
|
||||
|
||||
if actor.Username != service.GetActivityStreamsPreferredUsername().GetXMLSchemaString() {
|
||||
t.Errorf("actor.Username = %v, want %v", actor.Username, service.GetActivityStreamsPreferredUsername().GetXMLSchemaString())
|
||||
}
|
||||
|
||||
if actor.Inbox != service.GetActivityStreamsInbox().GetIRI() {
|
||||
t.Errorf("actor.Inbox = %v, want %v", actor.Inbox.String(), service.GetActivityStreamsInbox().GetIRI())
|
||||
}
|
||||
|
||||
if actor.Image != service.GetActivityStreamsIcon().At(0).GetActivityStreamsImage().GetActivityStreamsUrl().At(0).GetIRI() {
|
||||
t.Errorf("actor.Image = %v, want %v", actor.Image, service.GetActivityStreamsIcon().At(0).GetActivityStreamsImage().GetActivityStreamsUrl().At(0).GetIRI())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeActorPropertyWithID(t *testing.T) {
|
||||
iri, _ := url.Parse("https://fake.fediverse.server/user/mrfoo")
|
||||
actor := MakeActorPropertyWithID(iri)
|
||||
@@ -180,3 +155,463 @@ func TestMakeServiceForAccount(t *testing.T) {
|
||||
t.Errorf("actor.URL = %v, want %v", person.GetActivityStreamsUrl().At(0).GetIRI().String(), expectedIRI)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests for nil-safe accessor methods
|
||||
|
||||
func TestActorIriStringWithNilValue(t *testing.T) {
|
||||
actor := ActivityPubActor{}
|
||||
result := actor.ActorIriString()
|
||||
if result != "" {
|
||||
t.Errorf("ActorIriString() with nil ActorIri = %v, want empty string", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActorIriStringWithValue(t *testing.T) {
|
||||
iri, _ := url.Parse("https://example.com/user/test")
|
||||
actor := ActivityPubActor{ActorIri: iri}
|
||||
result := actor.ActorIriString()
|
||||
if result != "https://example.com/user/test" {
|
||||
t.Errorf("ActorIriString() = %v, want %v", result, "https://example.com/user/test")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInboxStringWithNilValue(t *testing.T) {
|
||||
actor := ActivityPubActor{}
|
||||
result := actor.InboxString()
|
||||
if result != "" {
|
||||
t.Errorf("InboxString() with nil Inbox = %v, want empty string", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInboxStringWithValue(t *testing.T) {
|
||||
inbox, _ := url.Parse("https://example.com/user/test/inbox")
|
||||
actor := ActivityPubActor{Inbox: inbox}
|
||||
result := actor.InboxString()
|
||||
if result != "https://example.com/user/test/inbox" {
|
||||
t.Errorf("InboxString() = %v, want %v", result, "https://example.com/user/test/inbox")
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageStringWithNilValue(t *testing.T) {
|
||||
actor := ActivityPubActor{}
|
||||
result := actor.ImageString()
|
||||
if result != "" {
|
||||
t.Errorf("ImageString() with nil Image = %v, want empty string", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageStringWithValue(t *testing.T) {
|
||||
image, _ := url.Parse("https://example.com/avatar.png")
|
||||
actor := ActivityPubActor{Image: image}
|
||||
result := actor.ImageString()
|
||||
if result != "https://example.com/avatar.png" {
|
||||
t.Errorf("ImageString() = %v, want %v", result, "https://example.com/avatar.png")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFollowRequestIriStringWithNilValue(t *testing.T) {
|
||||
actor := ActivityPubActor{}
|
||||
result := actor.FollowRequestIriString()
|
||||
if result != "" {
|
||||
t.Errorf("FollowRequestIriString() with nil FollowRequestIri = %v, want empty string", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFollowRequestIriStringWithValue(t *testing.T) {
|
||||
followIri, _ := url.Parse("https://example.com/follow/123")
|
||||
actor := ActivityPubActor{FollowRequestIri: followIri}
|
||||
result := actor.FollowRequestIriString()
|
||||
if result != "https://example.com/follow/123" {
|
||||
t.Errorf("FollowRequestIriString() = %v, want %v", result, "https://example.com/follow/123")
|
||||
}
|
||||
}
|
||||
|
||||
func TestActorIriHostnameWithNilValue(t *testing.T) {
|
||||
actor := ActivityPubActor{}
|
||||
result := actor.ActorIriHostname()
|
||||
if result != "" {
|
||||
t.Errorf("ActorIriHostname() with nil ActorIri = %v, want empty string", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActorIriHostnameWithValue(t *testing.T) {
|
||||
iri, _ := url.Parse("https://example.com/user/test")
|
||||
actor := ActivityPubActor{ActorIri: iri}
|
||||
result := actor.ActorIriHostname()
|
||||
if result != "example.com" {
|
||||
t.Errorf("ActorIriHostname() = %v, want %v", result, "example.com")
|
||||
}
|
||||
}
|
||||
|
||||
// Tests for Validate() and IsValid() methods
|
||||
|
||||
func TestValidateWithAllNilFields(t *testing.T) {
|
||||
actor := ActivityPubActor{}
|
||||
err := actor.Validate()
|
||||
if err == nil {
|
||||
t.Error("Validate() with all nil fields should return error")
|
||||
}
|
||||
if !errors.Is(err, ErrActorMissingRequiredField) {
|
||||
t.Errorf("Validate() error = %v, want ErrActorMissingRequiredField", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateWithNilActorIri(t *testing.T) {
|
||||
inbox, _ := url.Parse("https://example.com/inbox")
|
||||
actor := ActivityPubActor{Inbox: inbox}
|
||||
err := actor.Validate()
|
||||
if err == nil {
|
||||
t.Error("Validate() with nil ActorIri should return error")
|
||||
}
|
||||
if !errors.Is(err, ErrActorMissingRequiredField) {
|
||||
t.Errorf("Validate() error = %v, want ErrActorMissingRequiredField", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateWithNilInbox(t *testing.T) {
|
||||
iri, _ := url.Parse("https://example.com/user/test")
|
||||
actor := ActivityPubActor{ActorIri: iri}
|
||||
err := actor.Validate()
|
||||
if err == nil {
|
||||
t.Error("Validate() with nil Inbox should return error")
|
||||
}
|
||||
if !errors.Is(err, ErrActorMissingRequiredField) {
|
||||
t.Errorf("Validate() error = %v, want ErrActorMissingRequiredField", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateWithRequiredFields(t *testing.T) {
|
||||
iri, _ := url.Parse("https://example.com/user/test")
|
||||
inbox, _ := url.Parse("https://example.com/inbox")
|
||||
actor := ActivityPubActor{ActorIri: iri, Inbox: inbox}
|
||||
err := actor.Validate()
|
||||
if err != nil {
|
||||
t.Errorf("Validate() with required fields should not return error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidWithInvalidActor(t *testing.T) {
|
||||
actor := ActivityPubActor{}
|
||||
if actor.IsValid() {
|
||||
t.Error("IsValid() with invalid actor should return false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidWithValidActor(t *testing.T) {
|
||||
iri, _ := url.Parse("https://example.com/user/test")
|
||||
inbox, _ := url.Parse("https://example.com/inbox")
|
||||
actor := ActivityPubActor{ActorIri: iri, Inbox: inbox}
|
||||
if !actor.IsValid() {
|
||||
t.Error("IsValid() with valid actor should return true")
|
||||
}
|
||||
}
|
||||
|
||||
// Tests for NewActivityPubActor constructor
|
||||
|
||||
func TestNewActivityPubActorWithNilActorIri(t *testing.T) {
|
||||
inbox, _ := url.Parse("https://example.com/inbox")
|
||||
_, err := NewActivityPubActor(nil, inbox)
|
||||
if err == nil {
|
||||
t.Error("NewActivityPubActor with nil actorIri should return error")
|
||||
}
|
||||
if !errors.Is(err, ErrActorMissingRequiredField) {
|
||||
t.Errorf("NewActivityPubActor error = %v, want ErrActorMissingRequiredField", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewActivityPubActorWithNilInbox(t *testing.T) {
|
||||
iri, _ := url.Parse("https://example.com/user/test")
|
||||
_, err := NewActivityPubActor(iri, nil)
|
||||
if err == nil {
|
||||
t.Error("NewActivityPubActor with nil inbox should return error")
|
||||
}
|
||||
if !errors.Is(err, ErrActorMissingRequiredField) {
|
||||
t.Errorf("NewActivityPubActor error = %v, want ErrActorMissingRequiredField", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewActivityPubActorWithBothNil(t *testing.T) {
|
||||
_, err := NewActivityPubActor(nil, nil)
|
||||
if err == nil {
|
||||
t.Error("NewActivityPubActor with both nil should return error")
|
||||
}
|
||||
if !errors.Is(err, ErrActorMissingRequiredField) {
|
||||
t.Errorf("NewActivityPubActor error = %v, want ErrActorMissingRequiredField", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewActivityPubActorWithValidArgs(t *testing.T) {
|
||||
iri, _ := url.Parse("https://example.com/user/test")
|
||||
inbox, _ := url.Parse("https://example.com/inbox")
|
||||
actor, err := NewActivityPubActor(iri, inbox)
|
||||
if err != nil {
|
||||
t.Errorf("NewActivityPubActor with valid args should not return error, got %v", err)
|
||||
}
|
||||
if actor == nil {
|
||||
t.Error("NewActivityPubActor with valid args should return non-nil actor")
|
||||
}
|
||||
if actor.ActorIri != iri {
|
||||
t.Errorf("actor.ActorIri = %v, want %v", actor.ActorIri, iri)
|
||||
}
|
||||
if actor.Inbox != inbox {
|
||||
t.Errorf("actor.Inbox = %v, want %v", actor.Inbox, inbox)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests for NewActivityPubActorFromEntity with invalid entities
|
||||
|
||||
func makeFakeServiceWithoutUsername() vocab.ActivityStreamsService {
|
||||
iri, _ := url.Parse("https://fake.fediverse.server/user/mrfoo")
|
||||
inbox, _ := url.Parse("https://fake.fediverse.server/user/mrfoo/inbox")
|
||||
|
||||
service := streams.NewActivityStreamsService()
|
||||
|
||||
id := streams.NewJSONLDIdProperty()
|
||||
id.Set(iri)
|
||||
service.SetJSONLDId(id)
|
||||
|
||||
inboxProp := streams.NewActivityStreamsInboxProperty()
|
||||
inboxProp.SetIRI(inbox)
|
||||
service.SetActivityStreamsInbox(inboxProp)
|
||||
|
||||
publicKeyProperty := streams.NewW3IDSecurityV1PublicKeyProperty()
|
||||
service.SetW3IDSecurityV1PublicKey(publicKeyProperty)
|
||||
|
||||
return service
|
||||
}
|
||||
|
||||
func makeFakeServiceWithoutPublicKey() vocab.ActivityStreamsService {
|
||||
iri, _ := url.Parse("https://fake.fediverse.server/user/mrfoo")
|
||||
inbox, _ := url.Parse("https://fake.fediverse.server/user/mrfoo/inbox")
|
||||
username := "foodawg"
|
||||
|
||||
service := streams.NewActivityStreamsService()
|
||||
|
||||
id := streams.NewJSONLDIdProperty()
|
||||
id.Set(iri)
|
||||
service.SetJSONLDId(id)
|
||||
|
||||
preferredUsernameProperty := streams.NewActivityStreamsPreferredUsernameProperty()
|
||||
preferredUsernameProperty.SetXMLSchemaString(username)
|
||||
service.SetActivityStreamsPreferredUsername(preferredUsernameProperty)
|
||||
|
||||
inboxProp := streams.NewActivityStreamsInboxProperty()
|
||||
inboxProp.SetIRI(inbox)
|
||||
service.SetActivityStreamsInbox(inboxProp)
|
||||
|
||||
return service
|
||||
}
|
||||
|
||||
func makeFakeServiceWithEmptyPublicKey() vocab.ActivityStreamsService {
|
||||
iri, _ := url.Parse("https://fake.fediverse.server/user/mrfoo")
|
||||
inbox, _ := url.Parse("https://fake.fediverse.server/user/mrfoo/inbox")
|
||||
username := "foodawg"
|
||||
|
||||
service := streams.NewActivityStreamsService()
|
||||
|
||||
id := streams.NewJSONLDIdProperty()
|
||||
id.Set(iri)
|
||||
service.SetJSONLDId(id)
|
||||
|
||||
preferredUsernameProperty := streams.NewActivityStreamsPreferredUsernameProperty()
|
||||
preferredUsernameProperty.SetXMLSchemaString(username)
|
||||
service.SetActivityStreamsPreferredUsername(preferredUsernameProperty)
|
||||
|
||||
inboxProp := streams.NewActivityStreamsInboxProperty()
|
||||
inboxProp.SetIRI(inbox)
|
||||
service.SetActivityStreamsInbox(inboxProp)
|
||||
|
||||
// Set an empty public key property (Len() == 0)
|
||||
publicKeyProperty := streams.NewW3IDSecurityV1PublicKeyProperty()
|
||||
service.SetW3IDSecurityV1PublicKey(publicKeyProperty)
|
||||
|
||||
return service
|
||||
}
|
||||
|
||||
func makeFakeServiceWithoutId() vocab.ActivityStreamsService {
|
||||
inbox, _ := url.Parse("https://fake.fediverse.server/user/mrfoo/inbox")
|
||||
username := "foodawg"
|
||||
|
||||
service := streams.NewActivityStreamsService()
|
||||
|
||||
preferredUsernameProperty := streams.NewActivityStreamsPreferredUsernameProperty()
|
||||
preferredUsernameProperty.SetXMLSchemaString(username)
|
||||
service.SetActivityStreamsPreferredUsername(preferredUsernameProperty)
|
||||
|
||||
inboxProp := streams.NewActivityStreamsInboxProperty()
|
||||
inboxProp.SetIRI(inbox)
|
||||
service.SetActivityStreamsInbox(inboxProp)
|
||||
|
||||
publicKeyProperty := streams.NewW3IDSecurityV1PublicKeyProperty()
|
||||
service.SetW3IDSecurityV1PublicKey(publicKeyProperty)
|
||||
|
||||
return service
|
||||
}
|
||||
|
||||
func makeFakeServiceWithoutInbox() vocab.ActivityStreamsService {
|
||||
iri, _ := url.Parse("https://fake.fediverse.server/user/mrfoo")
|
||||
username := "foodawg"
|
||||
|
||||
service := streams.NewActivityStreamsService()
|
||||
|
||||
id := streams.NewJSONLDIdProperty()
|
||||
id.Set(iri)
|
||||
service.SetJSONLDId(id)
|
||||
|
||||
preferredUsernameProperty := streams.NewActivityStreamsPreferredUsernameProperty()
|
||||
preferredUsernameProperty.SetXMLSchemaString(username)
|
||||
service.SetActivityStreamsPreferredUsername(preferredUsernameProperty)
|
||||
|
||||
publicKeyProperty := streams.NewW3IDSecurityV1PublicKeyProperty()
|
||||
service.SetW3IDSecurityV1PublicKey(publicKeyProperty)
|
||||
|
||||
return service
|
||||
}
|
||||
|
||||
func TestNewActivityPubActorFromEntityWithoutUsername(t *testing.T) {
|
||||
service := makeFakeServiceWithoutUsername()
|
||||
_, err := NewActivityPubActorFromEntity(service)
|
||||
if err == nil {
|
||||
t.Error("NewActivityPubActorFromEntity without username should return error")
|
||||
}
|
||||
if !errors.Is(err, ErrActorMissingRequiredField) {
|
||||
t.Errorf("NewActivityPubActorFromEntity error = %v, want ErrActorMissingRequiredField", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewActivityPubActorFromEntityWithoutPublicKey(t *testing.T) {
|
||||
service := makeFakeServiceWithoutPublicKey()
|
||||
_, err := NewActivityPubActorFromEntity(service)
|
||||
if err == nil {
|
||||
t.Error("NewActivityPubActorFromEntity without public key should return error")
|
||||
}
|
||||
if !errors.Is(err, ErrActorMissingRequiredField) {
|
||||
t.Errorf("NewActivityPubActorFromEntity error = %v, want ErrActorMissingRequiredField", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewActivityPubActorFromEntityWithEmptyPublicKey(t *testing.T) {
|
||||
service := makeFakeServiceWithEmptyPublicKey()
|
||||
_, err := NewActivityPubActorFromEntity(service)
|
||||
if err == nil {
|
||||
t.Error("NewActivityPubActorFromEntity with empty public key should return error")
|
||||
}
|
||||
if !errors.Is(err, ErrActorMissingRequiredField) {
|
||||
t.Errorf("NewActivityPubActorFromEntity error = %v, want ErrActorMissingRequiredField", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewActivityPubActorFromEntityWithoutId(t *testing.T) {
|
||||
service := makeFakeServiceWithoutId()
|
||||
_, err := NewActivityPubActorFromEntity(service)
|
||||
if err == nil {
|
||||
t.Error("NewActivityPubActorFromEntity without ID should return error")
|
||||
}
|
||||
if !errors.Is(err, ErrActorMissingRequiredField) {
|
||||
t.Errorf("NewActivityPubActorFromEntity error = %v, want ErrActorMissingRequiredField", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewActivityPubActorFromEntityWithoutInbox(t *testing.T) {
|
||||
service := makeFakeServiceWithoutInbox()
|
||||
_, err := NewActivityPubActorFromEntity(service)
|
||||
if err == nil {
|
||||
t.Error("NewActivityPubActorFromEntity without inbox should return error")
|
||||
}
|
||||
if !errors.Is(err, ErrActorMissingRequiredField) {
|
||||
t.Errorf("NewActivityPubActorFromEntity error = %v, want ErrActorMissingRequiredField", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewActivityPubActorFromEntityWithValidEntity(t *testing.T) {
|
||||
service := makeFakeService()
|
||||
actor, err := NewActivityPubActorFromEntity(service)
|
||||
if err != nil {
|
||||
t.Errorf("NewActivityPubActorFromEntity with valid entity should not return error, got %v", err)
|
||||
}
|
||||
if actor == nil {
|
||||
t.Fatal("NewActivityPubActorFromEntity with valid entity should return non-nil actor")
|
||||
}
|
||||
|
||||
// Verify required fields are non-nil
|
||||
if actor.ActorIri == nil {
|
||||
t.Error("actor.ActorIri should not be nil")
|
||||
}
|
||||
if actor.Inbox == nil {
|
||||
t.Error("actor.Inbox should not be nil")
|
||||
}
|
||||
|
||||
// Verify extracted values match the fake service data
|
||||
expectedIri := "https://fake.fediverse.server/user/mrfoo"
|
||||
if actor.ActorIriString() != expectedIri {
|
||||
t.Errorf("actor.ActorIri = %v, want %v", actor.ActorIriString(), expectedIri)
|
||||
}
|
||||
|
||||
expectedInbox := "https://fake.fediverse.server/user/mrfoo/inbox"
|
||||
if actor.InboxString() != expectedInbox {
|
||||
t.Errorf("actor.Inbox = %v, want %v", actor.InboxString(), expectedInbox)
|
||||
}
|
||||
|
||||
expectedName := "Mr Foo"
|
||||
if actor.Name != expectedName {
|
||||
t.Errorf("actor.Name = %v, want %v", actor.Name, expectedName)
|
||||
}
|
||||
|
||||
expectedUsername := "foodawg"
|
||||
if actor.Username != expectedUsername {
|
||||
t.Errorf("actor.Username = %v, want %v", actor.Username, expectedUsername)
|
||||
}
|
||||
|
||||
expectedImage := "https://fake.fediverse.server/user/mrfoo/avatar.png"
|
||||
if actor.ImageString() != expectedImage {
|
||||
t.Errorf("actor.Image = %v, want %v", actor.ImageString(), expectedImage)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that safe accessors don't panic on zero-value struct
|
||||
|
||||
func TestSafeAccessorsOnZeroValueStruct(t *testing.T) {
|
||||
var actor ActivityPubActor
|
||||
|
||||
// These should not panic
|
||||
_ = actor.ActorIriString()
|
||||
_ = actor.InboxString()
|
||||
_ = actor.ImageString()
|
||||
_ = actor.FollowRequestIriString()
|
||||
_ = actor.ActorIriHostname()
|
||||
_ = actor.Validate()
|
||||
_ = actor.IsValid()
|
||||
}
|
||||
|
||||
// Test that safe accessors work correctly with optional nil fields on otherwise valid actor
|
||||
|
||||
func TestSafeAccessorsWithOptionalNilFields(t *testing.T) {
|
||||
iri, _ := url.Parse("https://example.com/user/test")
|
||||
inbox, _ := url.Parse("https://example.com/inbox")
|
||||
actor := ActivityPubActor{
|
||||
ActorIri: iri,
|
||||
Inbox: inbox,
|
||||
// Image and FollowRequestIri are intentionally nil
|
||||
}
|
||||
|
||||
// Required fields should return values
|
||||
if actor.ActorIriString() != "https://example.com/user/test" {
|
||||
t.Errorf("ActorIriString() = %v, want non-empty", actor.ActorIriString())
|
||||
}
|
||||
if actor.InboxString() != "https://example.com/inbox" {
|
||||
t.Errorf("InboxString() = %v, want non-empty", actor.InboxString())
|
||||
}
|
||||
|
||||
// Optional nil fields should return empty strings without panicking
|
||||
if actor.ImageString() != "" {
|
||||
t.Errorf("ImageString() = %v, want empty string", actor.ImageString())
|
||||
}
|
||||
if actor.FollowRequestIriString() != "" {
|
||||
t.Errorf("FollowRequestIriString() = %v, want empty string", actor.FollowRequestIriString())
|
||||
}
|
||||
|
||||
// Actor should still be valid (only ActorIri and Inbox are required)
|
||||
if !actor.IsValid() {
|
||||
t.Error("Actor with ActorIri and Inbox should be valid even with nil optional fields")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user