Skip to content

Commit

Permalink
specs on types
Browse files Browse the repository at this point in the history
  • Loading branch information
michelson committed Sep 12, 2023
1 parent 4ba46eb commit 5bd1df7
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 28 deletions.
5 changes: 4 additions & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ Metrics/MethodLength:
Max: 13

Metrics/BlockLength:
Max: 80
Max: 120

Metrics/AbcSize:
Max: 20
59 changes: 32 additions & 27 deletions lib/activitypub/activity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ module ActivityPub
class Activity
attr_accessor :id, :type, :actor, :object, :target, :published, :to, :cc, :bcc, :context

# Initializes a new Activity instance.
KNOWN_TYPES = %w[Create Update Delete Follow Accept Reject Add Remove Block Like].freeze

def initialize(attributes = {})
@id = attributes[:id]
@type = attributes[:type]
Expand All @@ -20,14 +21,40 @@ def initialize(attributes = {})
@cc = attributes[:cc]
@bcc = attributes[:bcc]
@context = attributes[:context]

validate!
end

def validate!
validate_required_fields!
validate_known_types!
validate_url_format!(@id)
validate_url_format!(@actor)
validate_url_format!(@object)
validate_url_format!(@target)
validate_timestamp_format!(@published)
end

def validate_required_fields!
raise "Required field 'type' is missing" unless @type
raise "Required field 'actor' is missing" unless @actor
raise "Required field 'object' is missing" unless @object
end

def validate_known_types!
raise "'#{@type}' is not a recognized activity type" unless KNOWN_TYPES.include?(@type)
end

def validate_url_format!(url)
raise "Invalid URL format: '#{url}'" unless url.nil? || url.match?(URI::DEFAULT_PARSER.make_regexp)
end

# Validate the activity attributes.
def valid?
validate_type && validate_actor && validate_object
def validate_timestamp_format!(timestamp)
Time.iso8601(timestamp)
rescue ArgumentError
raise "Invalid timestamp format: '#{timestamp}'"
end

# Convert the Activity object into a hash representation.
def to_h
{
id: @id,
Expand All @@ -43,7 +70,6 @@ def to_h
}
end

# Generate an Activity object from a given hash.
def self.from_h(hash)
Activity.new(
id: hash["id"],
Expand All @@ -58,27 +84,6 @@ def self.from_h(hash)
context: hash["context"]
)
end

private

# Validate the type attribute. ActivityStreams specifies a set of core activity types.
# For simplicity, let's validate against a subset of them.
def validate_type
valid_types = %w[Create Update Delete Follow Like Add Remove Block Undo]
valid_types.include?(@type)
end

# Validate the actor attribute. For this example, we'll just ensure it's present.
def validate_actor
!@actor.nil? && !@actor.empty?
end

# Validate the object attribute. For this example, we'll ensure it's present.
def validate_object
!@object.nil? && !@object.empty?
end

# Additional validations and methods based on scenarios can be added here.
end
end

Expand Down
105 changes: 105 additions & 0 deletions spec/activity_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,109 @@
expect(activity.object).to eq("https://example.com/posts/1")
end
end

describe "#initialize" do
context "when required fields are missing" do
it "raises an error if type is missing" do
expect do
ActivityPub::Activity.new(actor: "http://actor.example.com", object: "http://object.example.com")
end.to raise_error("Required field 'type' is missing")
end

it "raises an error if actor is missing" do
expect do
ActivityPub::Activity.new(type: "Create", object: "http://object.example.com")
end.to raise_error("Required field 'actor' is missing")
end

it "raises an error if object is missing" do
expect do
ActivityPub::Activity.new(type: "Create", actor: "http://actor.example.com")
end.to raise_error("Required field 'object' is missing")
end
end

context "when an unknown type is provided" do
it "raises an error" do
expect do
ActivityPub::Activity.new(type: "UnknownType", actor: "http://actor.example.com", object: "http://object.example.com")
end.to raise_error("'UnknownType' is not a recognized activity type")
end
end

context "when an invalid URL format is provided" do
it "raises an error for invalid actor URL" do
expect do
ActivityPub::Activity.new(type: "Create", actor: "invalid_actor", object: "http://object.example.com")
end.to raise_error("Invalid URL format: 'invalid_actor'")
end

it "raises an error for invalid object URL" do
expect do
ActivityPub::Activity.new(type: "Create", actor: "http://actor.example.com", object: "invalid_object")
end.to raise_error("Invalid URL format: 'invalid_object'")
end
end

context "when an invalid timestamp is provided" do
it "raises an error" do
expect do
ActivityPub::Activity.new(type: "Create", actor: "http://actor.example.com", object: "http://object.example.com", published: "invalid_timestamp")
end.to raise_error("Invalid timestamp format: 'invalid_timestamp'")
end
end
end

describe "#to_h" do
let(:activity) do
ActivityPub::Activity.new(
type: "Create",
actor: "http://actor.example.com",
object: "http://object.example.com",
target: "http://target.example.com",
to: ["http://recipient1.example.com", "http://recipient2.example.com"],
cc: ["http://cc1.example.com"]
)
end

it "returns a hash representation of the activity" do
expect(activity.to_h).to include({
id: nil,
type: "Create",
actor: "http://actor.example.com",
object: "http://object.example.com",
target: "http://target.example.com",
# published: a_kind_of(String), # As this is generated, we just check the type
to: ["http://recipient1.example.com", "http://recipient2.example.com"],
cc: ["http://cc1.example.com"],
bcc: nil,
context: nil
})

expect(activity.to_h[:published]).to match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/)
end
end

describe ".from_h" do
let(:hash_representation) do
{
"type" => "Create",
"actor" => "http://actor.example.com",
"object" => "http://object.example.com",
"target" => "http://target.example.com",
"to" => ["http://recipient1.example.com", "http://recipient2.example.com"],
"cc" => ["http://cc1.example.com"]
}
end

it "returns an activity instance from a hash representation" do
activity = ActivityPub::Activity.from_h(hash_representation)
expect(activity.type).to eq("Create")
expect(activity.actor).to eq("http://actor.example.com")
expect(activity.object).to eq("http://object.example.com")
expect(activity.target).to eq("http://target.example.com")
expect(activity.to).to eq(["http://recipient1.example.com", "http://recipient2.example.com"])
expect(activity.cc).to eq(["http://cc1.example.com"])
end
end
end

0 comments on commit 5bd1df7

Please sign in to comment.