Browse Source

Refactor node to become "place".

Matches the DDD domain model much better!
tags/0.3.0^2
Bèr Kessels 1 year ago
parent
commit
21cbf43f68

+ 4
- 4
Rakefile View File

@@ -12,7 +12,7 @@ end

desc 'Setup Event Stream Processors'
task setup_processors: :environment do
Hours::Projections::Nodes::Projector.new.setup
Hours::Projections::Places::Projector.new.setup
end

desc 'Run Event Stream Processors'
@@ -22,7 +22,7 @@ task run_processors: %i[environment event_sourcery] do
Hours.projections_database.disconnect

esps = [
Hours::Projections::Nodes::Projector.new
Hours::Projections::Places::Projector.new
]

# The ESPRunner will fork child processes for each of the ESPs passed to it.
@@ -63,7 +63,7 @@ namespace :db do

desc 'Re Create Projections database and tables'
task projections: %i[environment event_sourcery] do
Hours::Projections::Nodes::Projector.new.setup
Hours::Projections::Places::Projector.new.setup
end
end

@@ -73,7 +73,7 @@ namespace :db do
Dir['./test/support/workflows/*.rb'].each { |file| require file }

include DataHelpers
include Workflows::AddNode
include Workflows::AddPlace

events.each do |event|
puts event

app/aggregates/node.rb → app/aggregates/place.rb View File

@@ -5,24 +5,24 @@ require_relative './place/place_id.rb'
module Hours
module Aggregates
##
# A +node+ is one of the core elements in the OpenStreetMap data model. It
# A +place+ is one of the core elements in the OpenStreetMap data model. It
# consists of a single point in space defined by its latitude, longitude
# and node id.
class Node
# and place id.
class Place
include EventSourcery::AggregateRoot

attr_writer :place_id_builder

# These apply methods are the hook that this aggregate uses to update
# its internal state from events.
apply NodeAdded do |event|
apply PlaceAdded do |event|
@aggregate_id = event.aggregate_id
end

def add(payload)
@payload = payload
body = payload.merge(place_id: place_id_builder.id)
apply_event(NodeAdded, aggregate_id: id, body: body)
apply_event(PlaceAdded, aggregate_id: id, body: body)
end

def place_id_builder

app/aggregates/place/place.rb → app/aggregates/place/place_body.rb View File

@@ -6,7 +6,7 @@ module Hours
module Aggregates
# Model to hold a "place". Wraps a GeoJSON feature object.
# Currently only handles POINTs, so is mostly a model for a POI
class Place
class PlaceBody
CATALOG_FILE = Hours.base_path.join('config', 'poi_catalog.json')

def initialize(geojson)

app/commands/add_node_command.rb → app/commands/add_place_command.rb View File

@@ -4,7 +4,7 @@ module Hours
# A command handler and commands that can be issued against the system.
# These form an interface between the web API and the domain model in
# the aggregate.
class AddNodeCommand
class AddPlaceCommand
attr_reader :payload, :aggregate_id

def self.build(**args)
@@ -20,7 +20,7 @@ module Hours
end

def aggregate_class
Aggregates::Node
Aggregates::Place
end

def validate

app/events/node_added.rb → app/events/place_added.rb View File

@@ -2,4 +2,4 @@

require 'event_sourcery/event'

NodeAdded = Class.new(EventSourcery::Event)
PlaceAdded = Class.new(EventSourcery::Event)

+ 5
- 0
app/events/place_proposed.rb View File

@@ -0,0 +1,5 @@
# frozen_string_literal: true

require 'event_sourcery/event'

PlaceProposed = Class.new(EventSourcery::Event)

app/models/node.rb → app/models/place.rb View File

@@ -5,11 +5,11 @@ require 'opening_hours_converter'
module Hours
module Models
##
# A Node is a readonly model for querying the projection of "places".
# A Place is a readonly model for querying the projection of "places".
# * Readonly: it is not enforced on model or ORM level, but should be
# enforced in the database-server and user setup. RO only means that
# this model is only tested and optimized against reading.
class Node < Sequel::Model(::Hours.projections_database[:query_nodes])
class Place < Sequel::Model(::Hours.projections_database[:query_places])
ITERATOR = OpeningHoursConverter::Iterator.new
PARSER = OpeningHoursConverter::OpeningHoursParser.new


app/projections/nodes.rb → app/projections/places.rb View File

@@ -2,16 +2,16 @@

module Hours
module Projections
module Nodes
module Places
##
# Handles the Nodes Projection
# Handles the Places Projection
class Projector
include EventSourcery::Postgres::Projector

projector_name :nodes
projector_name :places

# Database tables that form the projection.
table :query_nodes do
table :query_places do
column :id, 'UUID NOT NULL', primary_key: true
column :place_id, :varchar, null: false, size: 255
column :name, :text
@@ -23,7 +23,7 @@ module Hours

# Event handlers that update the projection in response to different
# events from the store.
project NodeAdded do |event|
project PlaceAdded do |event|
body = event.body
geometry = body['geometry']
properties = body['properties']
@@ -47,7 +47,7 @@ module Hours
# TODO: implement as Address Model PORO instead.
# TODO: implement event attributes with lookup in an OpenStruct
rescue Sequel::UniqueConstraintViolation => e
raise unless e.message.include?('query_nodes_place_id_index')
raise unless e.message.include?('query_places_place_id_index')
end

def exists?(place_id)

+ 4
- 4
app/projections/query.rb View File

@@ -2,20 +2,20 @@

module Hours
module Projections
module Node
module Place
# Query handler that queries the projection table for a single node
class Query
def self.handle(id)
Hours::Models::Node[id]
Hours::Models::Place[id]
end
end
end

module Nodes
module Places
# Query handler that queries the projection table for all nodes
class Query
def self.handle
Hours::Models::Node.all
Hours::Models::Place.all
end
end
end

app/serializers/node.rb → app/serializers/place.rb View File

@@ -3,9 +3,9 @@
module Hours
module Serializers
##
# Represent a Node as JSON
class Node < JSONAPI::Serializable::Resource
type 'node'
# Represent a Place as JSON
class Place < JSONAPI::Serializable::Resource
type 'place'

attributes :name, :lat, :lon, :status, :open_this_week
id { @object.id }
@@ -23,7 +23,7 @@ module Hours

link :self do
# TODO: move into an URL-helper
"/nodes/#{@object.id}"
"/places/#{@object.id}"
end

meta do

+ 2
- 2
app/views/layout.erb View File

@@ -15,8 +15,8 @@
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="#">Openingstijden</a></li>
<li class="breadcrumb-item"><a href="#">in <%= @node.address.city %></a></li>
<li class="breadcrumb-item active" aria-current="page"><%= @node.name %></li>
<li class="breadcrumb-item"><a href="#">in <%= @place.address.city %></a></li>
<li class="breadcrumb-item active" aria-current="page"><%= @place.name %></li>
</ol>
</nav>
<%== yield %>

app/views/node.erb → app/views/place.erb View File

@@ -1,14 +1,14 @@
<div itemscope itemtype="http://schema.org/PostalAddress">
<h2 class="title" itemprop="name"><%= @node.name %></h2>
<h2 class="title" itemprop="name"><%= @place.name %></h2>
<address itemprop="address">
<span itemprop="streetAddress">
<%= @node.address.street %> <%= @node.address.housenumber %>
<%= @place.address.street %> <%= @place.address.housenumber %>
</span>,
<span itemprop="postalCode">
<%= @node.address.postcode %>
<%= @place.address.postcode %>
</span>
<span itemprop="addressLocality">
<%= @node.address.city %>
<%= @place.address.city %>
</span>
</address>
</div>

+ 2
- 3
bin/sink View File

@@ -11,7 +11,7 @@ require_relative '../config/event_sourcery.rb'
LOG_LEVEL = ENV['LOG_LEVEL'].to_i || Logger::DEBUG

# Handles a stream of nodes in STDIN emits them as events through the
# AddNodeCommand.
# AddPlaceCommand.
class EventSink
def initialize
@parser = Yajl::Parser.new(symbolize_keys: true)
@@ -23,8 +23,7 @@ class EventSink

def object_parsed(obj)
log(Logger::DEBUG, '-- parsed object')
as_params = obj.merge(node_id: SecureRandom.uuid)
command = Hours::AddNodeCommand.build(as_params)
command = Hours::AddPlaceCommand.build(obj)
Hours::CommandHandler.new.handle(command)
end


+ 12
- 12
lib/app.rb View File

@@ -70,8 +70,8 @@ module Hours
status 400
end

def nodes_url(id)
URI.join(request.base_url, "/nodes/#{id}").to_s
def places_url(id)
URI.join(request.base_url, "/places/#{id}").to_s
end

def json_params
@@ -90,26 +90,26 @@ module Hours
settings.sprockets_env.call(env)
end

get '/nodes' do
get '/places' do
render = JSONAPI::Serializable::Renderer.new
body render.render(
Hours::Projections::Nodes::Query.handle,
class: { "Hours::Models::Node": Hours::Serializers::Node }
Hours::Projections::Places::Query.handle,
class: { "Hours::Models::Place": Hours::Serializers::Place }
).to_json
status 200
end

post '/nodes/' do
command = Hours::AddNodeCommand.build(json_params)
post '/places' do
command = Hours::AddPlaceCommand.build(json_params)
Hours::CommandHandler.new.handle(command)
status 201
headers 'Location' => nodes_url(command.aggregate_id)
headers 'Location' => places_url(command.aggregate_id)
end

get %r{/nodes/(?<node_id>#{UUID_REGEX})} do
@node = Hours::Projections::Node::Query.handle(params[:node_id])
if @node
erb :node
get %r{/places/(?<place_id>#{UUID_REGEX})} do
@place = Hours::Projections::Place::Query.handle(params[:place_id])
if @place
erb :place
else
404
end

+ 4
- 4
lib/hours.rb View File

@@ -7,11 +7,11 @@ require 'event_sourcery/postgres'
require 'sequel-postgis-georuby'
require 'securerandom'

require_relative '../app/events/node_added.rb'
require_relative '../app/events/place_added.rb'
require_relative '../app/commands/command_handler.rb'
require_relative '../app/commands/add_node_command.rb'
require_relative '../app/aggregates/node.rb'
require_relative '../app/projections/nodes.rb'
require_relative '../app/commands/add_place_command.rb'
require_relative '../app/aggregates/place.rb'
require_relative '../app/projections/places.rb'
require_relative '../app/projections/query.rb'

# Monkey patch

test/aggregates/place/place_test.rb → test/aggregates/place/place_body_test.rb View File

@@ -1,11 +1,12 @@
# frozen_string_literal: true

require 'test_helper'
require_relative Hours.base_path.join('app', 'aggregates', 'place', 'place.rb')
require_relative Hours.base_path.join('app', 'aggregates', 'place',
'place_body.rb')

describe Hours::Aggregates::Place do
describe 'from geojson' do
let(:subject) { Hours::Aggregates::Place.new(geojson.to_json) }
let(:subject) { Hours::Aggregates::PlaceBody.new(geojson.to_json) }
let(:geojson) { json_fixtures('./db/full.json') }
let(:coordinates) { geojson[:geometry][:coordinates] }

@@ -32,7 +33,7 @@ describe Hours::Aggregates::Place do
end

describe 'from empty json' do
let(:subject) { Hours::Aggregates::Place.new('{}') }
let(:subject) { Hours::Aggregates::PlaceBody.new('{}') }

it 'is empty' do
assert_nil subject.name

+ 5
- 4
test/aggregates/place/place_id_test.rb View File

@@ -2,15 +2,16 @@

require 'test_helper'
require_relative Hours.base_path.join 'app/aggregates/place/place_id.rb'
require_relative Hours.base_path.join 'app/aggregates/place/place.rb'
require_relative Hours.base_path.join 'app/aggregates/place/place_body.rb'

describe Hours::Aggregates::PlaceId do
let(:body_class) { Hours::Aggregates::PlaceBody }
let(:hm_burchtst_json) { json_fixtures('db/full.json') }
let(:hm_broerst_json) { json_fixtures('db/hm_broerstraat.json') }
let(:hm_arnhem_json) { json_fixtures('db/hm_arnhem.json') }
let(:hm_burchtst) { Hours::Aggregates::Place.new(hm_burchtst_json.to_json) }
let(:hm_broerst) { Hours::Aggregates::Place.new(hm_broerst_json.to_json) }
let(:hm_arnhem) { Hours::Aggregates::Place.new(hm_arnhem_json.to_json) }
let(:hm_burchtst) { body_class.new(hm_burchtst_json.to_json) }
let(:hm_broerst) { body_class.new(hm_broerst_json.to_json) }
let(:hm_arnhem) { body_class.new(hm_arnhem_json.to_json) }

describe 'place location' do
it 'makes the ID the same if they are closer than approx 150 meter' do

test/aggregates/node_test.rb → test/aggregates/place_test.rb View File

@@ -2,12 +2,12 @@

require 'test_helper'

describe Hours::Aggregates::Node do
describe Hours::Aggregates::Place do
let(:aggregate_id) { SecureRandom.uuid }
let(:events) { [] }
let(:payload) { {} }
let(:place_id_builder) { Minitest::Mock.new }
subject { Hours::Aggregates::Node.new(aggregate_id, events) }
subject { Hours::Aggregates::Place.new(aggregate_id, events) }

before do
place_id_builder.expect(:id, 'PLACEID')

+ 0
- 30
test/commands/add_node_command_test.rb View File

@@ -1,30 +0,0 @@
# frozen_string_literal: true

require 'test_helper'

describe Hours::AddNodeCommand do
describe '.build' do
subject { Hours::AddNodeCommand }

describe 'without a node_id' do
let(:params) { { lat: 10, lon: 10 } }

it 'raises an error' do
assert_raises(Hours::ValidationError, 'node_id is blank') do
subject.build(params)
end
end
end

# TODO: validate entire geoJSON object with a specified gem
describe 'without a geometry' do
let(:params) { { node_id: SecureRandom.uuid, geometry: nil } }

it 'raises an error' do
assert_raises(Hours::ValidationError, 'geometry is blank') do
subject.build(params)
end
end
end
end
end

+ 20
- 0
test/commands/add_place_command_test.rb View File

@@ -0,0 +1,20 @@
# frozen_string_literal: true

require 'test_helper'

describe Hours::AddPlaceCommand do
describe '.build' do
subject { Hours::AddPlaceCommand }

# TODO: validate entire geoJSON object with a specified gem
describe 'without a geometry' do
let(:params) { {} }

it 'raises an error' do
assert_raises(Hours::ValidationError, 'geometry is blank') do
subject.build(params)
end
end
end
end
end

test/fixtures/json/nodes.json → test/fixtures/json/places.json View File

@@ -7,7 +7,7 @@
"meta" : {
"copyright" : "OpenStreetMap-contributors"
},
"type" : "node",
"type" : "place",
"attributes" : {
"raw_opening_hours" : "Mo-We 10:00-18:00; Th 10:00-21:00; Fr 10:00-18:00; Sa 09:30-17:30",
"open_this_week" : {

test/integration/api/add_node_test.rb → test/integration/api/add_places_test.rb View File

@@ -2,54 +2,54 @@

require 'test_helper'

describe 'add node' do
describe 'POST /nodes/' do
describe 'add place' do
describe 'POST /places' do
let(:lat) { 51.84 }
let(:lon) { 5.86 }
let(:payload) { json_fixtures('db/full.json') }

it 'returns success' do
post_json '/nodes/', payload
post_json '/places', payload

assert_status(201)
assert_match(%r{http://example.org/nodes/(.*)},
assert_match(%r{http://example.org/places/(.*)},
last_response.headers['Location'])
assert_kind_of(NodeAdded, last_event(id_from_header))
assert_kind_of(PlaceAdded, last_event(id_from_header))
assert_equal(last_event(id_from_header).body['properties'],
payload.fetch(:properties).transform_keys(&:to_s))
end

it 'adds a node to nodes projection' do
it 'adds a place to places projection' do
setup_projectors

post_json '/nodes/', payload
post_json '/places', payload

projector_process_event(id_from_header)

assert_equal(
id_from_header,
Hours::Projections::Nodes::Query.handle.first[:id]
Hours::Projections::Places::Query.handle.first[:id]
)
end

describe 'a node with the same name in a few meter radius' do
describe 'a place with the same name in a few meter radius' do
let(:payload_nearby) { json_fixtures('db/hm_broerstraat.json') }

before do
post_json '/nodes/', payload
post_json '/places', payload
@aggregate_ids = [id_from_header]
projector_process_event(@aggregate_ids.first)
end

it 'does not add a new node in the query repo' do
assert_equal(1, Hours::Projections::Nodes::Query.handle.length)
it 'does not add a new place in the query repo' do
assert_equal(1, Hours::Projections::Places::Query.handle.length)

post_json '/nodes/', payload_nearby
post_json '/places', payload_nearby
assert_status(201)
@aggregate_ids << id_from_header
refute_equal(@aggregate_ids.first, @aggregate_ids.last)
projector_process_event(@aggregate_ids.last)
assert_equal(1, Hours::Projections::Nodes::Query.handle.length)
assert_equal(1, Hours::Projections::Places::Query.handle.length)
end
end

@@ -57,7 +57,7 @@ describe 'add node' do
it 'returns bad request for missting missing geometry' do
payload_missing_geom = payload.slice(:type, :id, :properties)
assert_nil payload_missing_geom[:geometry]
post_json '/nodes/', payload_missing_geom
post_json '/places', payload_missing_geom

assert_status(400)
assert_equal last_response.body, 'Bad Request: geometry is blank'

test/integration/api/view_nodes_test.rb → test/integration/api/view_places_test.rb View File

@@ -4,21 +4,21 @@ require 'test_helper'
require 'json_expressions/minitest'
require 'timecop'

require_relative Hours.base_path.join('test/support/workflows/add_node.rb')
require_relative Hours.base_path.join('test/support/workflows/add_place.rb')

describe 'api view nodes' do
describe 'GET /nodes' do
include Workflows::AddNode
describe 'api view places' do
describe 'GET /places' do
include Workflows::AddPlace

it 'returns a list of nodes' do
it 'returns a list of places' do
Timecop.travel(die_wende) do
assert Time.now.hour < 21 # Before 21:00
assert Time.now.thursday?

get '/nodes'
get '/places'
assert_status(200)

expected = json_fixtures('json/nodes.json')
expected = json_fixtures('json/places.json')
expected[:data][0][:links][:self] = String
expected[:data][0][:id] = String

@@ -33,7 +33,7 @@ describe 'api view nodes' do
Timecop.travel(after_hours) do
assert Time.now.hour > 21 # After 21:00

get '/nodes'
get '/places'

assert_status(200)
parsed = JSON.parse(last_response.body, symbolize_names: true)

+ 1
- 1
test/integration/cli/sink_test.rb View File

@@ -15,7 +15,7 @@ describe 'sink' do

status_list.each { |status| assert status.success? }

assert_kind_of(NodeAdded, last_event)
assert_kind_of(PlaceAdded, last_event)
refute_nil(last_event.aggregate_id)
assert_equal(last_event.body['properties'],
payload.fetch(:properties).transform_keys(&:to_s))

test/integration/web/view_node_test.rb → test/integration/web/view_places_test.rb View File

@@ -5,16 +5,16 @@ require 'json_expressions/minitest'
require 'minitest/have_tag'
require 'timecop'

require_relative Hours.base_path.join('test/support/workflows/add_node.rb')
require_relative Hours.base_path.join('test/support/workflows/add_place.rb')

describe 'web view nodes' do
describe 'GET /node/:uuid' do
include Workflows::AddNode
describe 'web view places' do
describe 'GET /places/:uuid' do
include Workflows::AddPlace

it 'returns a node detail page' do
Timecop.travel(die_wende) do
header 'Accept', 'text/html'
get "/nodes/#{hm_id}"
get "/places/#{hm_id}"

assert_status(200)
assert_includes last_response.content_type, 'text/html'
@@ -42,14 +42,14 @@ describe 'web view nodes' do
end
end

describe 'GET /node/:uuid with unescaped values' do
describe 'GET /places/:uuid with unescaped values' do
let(:body) { json_fixtures('db/hacked.json') }

include Workflows::AddNode
include Workflows::AddPlace

it 'escapes ugly attributes' do
header 'Accept', 'text/html'
get "/nodes/#{hm_id}"
get "/places/#{hm_id}"
assert_status(200)
assert_have_tag last_response.body,
'nav ol.breadcrumb li.active',
@@ -60,12 +60,12 @@ describe 'web view nodes' do
end
end

describe 'GET nonexisting node' do
include Workflows::AddNode
describe 'GET nonexisting place' do
include Workflows::AddPlace

it 'gives a 404 for invalid UUID' do
header 'Accept', 'text/html'
get '/nodes/1337'
get '/places/1337'
assert_status(404)
end

@@ -73,12 +73,12 @@ describe 'web view nodes' do
header 'Accept', 'text/html'
nonexisting_uuid = SecureRandom.uuid

# When we seed with more nodes, we should determine that
# When we seed with more places, we should determine that
# our UUID does not exist in the database. This scenario is hardly
# possible, though.
refute_equal nonexisting_uuid, hm_id

get "/nodes/#{nonexisting_uuid}"
get "/places/#{nonexisting_uuid}"
assert_status(404)
end
end

test/models/node_test.rb → test/models/place_test.rb View File

@@ -1,17 +1,17 @@
# frozen_string_literal: true

require 'test_helper'
require_relative Hours.base_path.join 'app/models/node.rb'
require_relative Hours.base_path.join 'app/models/place.rb'

describe Hours::Models::Node do
describe Hours::Models::Place do
let(:pk) { SecureRandom.uuid }
subject do
Hours::Models::Node.new
Hours::Models::Place.new
end

describe 'dataset' do
it 'reads from :query_nodes' do
assert_equal :query_nodes, subject.class.table_name
it 'reads from :query_places' do
assert_equal :query_places, subject.class.table_name
end
end

@@ -35,11 +35,11 @@ describe Hours::Models::Node do
let(:point) { GeoRuby::SimpleFeatures::Point.from_x_y(lon, lat) }

it 'has a lat' do
assert_equal lat, Hours::Models::Node.new(location: point).lat
assert_equal lat, Hours::Models::Place.new(location: point).lat
end

it 'has a lon' do
assert_equal lon, Hours::Models::Node.new(location: point).lon
assert_equal lon, Hours::Models::Place.new(location: point).lon
end
end


+ 14
- 14
test/projections/query_test.rb View File

@@ -2,61 +2,61 @@

require 'test_helper'

describe Hours::Projections::Nodes::Query do
describe Hours::Projections::Places::Query do
subject do
Hours::Projections::Nodes::Query.handle
Hours::Projections::Places::Query.handle
end

let(:tracker) { Minitest::Mock.new }

let(:projector) do
Hours::Projections::Nodes::Projector.new(tracker: tracker)
Hours::Projections::Places::Projector.new(tracker: tracker)
end

let(:event) do
NodeAdded.new(
PlaceAdded.new(
aggregate_id: SecureRandom.uuid,
body: json_fixtures('db/full.json')
)
end

before do
# Ensure that we have processed events, which stores nodes in the db
# Ensure that we have processed events, which stores places in the db
projector.process(event)
end

it 'fetches a list of Nodes through Sequel model' do
it 'fetches a list of Places through Sequel model' do
assert_kind_of Array, subject
assert_kind_of Hours::Models::Node, subject.first
assert_kind_of Hours::Models::Place, subject.first
end
end

describe Hours::Projections::Node::Query do
describe Hours::Projections::Place::Query do
subject do
Hours::Projections::Node::Query.handle(id)
Hours::Projections::Place::Query.handle(id)
end

let(:tracker) { Minitest::Mock.new }

let(:projector) do
Hours::Projections::Nodes::Projector.new(tracker: tracker)
Hours::Projections::Places::Projector.new(tracker: tracker)
end

let(:id) { SecureRandom.uuid }

let(:event) do
NodeAdded.new(
PlaceAdded.new(
aggregate_id: id,
body: json_fixtures('db/full.json')
)
end

before do
# Ensure that we have processed events, which stores nodes in the db
# Ensure that we have processed events, which stores places in the db
projector.process(event)
end

it 'fetches a single of Node through Sequel model' do
assert_kind_of Hours::Models::Node, subject
it 'fetches a single of Places through Sequel model' do
assert_kind_of Hours::Models::Place, subject
end
end

test/serializers/node_test.rb → test/serializers/place_test.rb View File

@@ -1,14 +1,14 @@
# frozen_string_literal: true

require 'test_helper'
require_relative Hours.base_path.join 'app/serializers/node.rb'
require_relative Hours.base_path.join 'app/serializers/place.rb'

describe Hours::Serializers::Node do
describe Hours::Serializers::Place do
let(:address) { OpenStruct.new(city: 'Nijmegen', addr_country_code: 'nl') }
let(:model) { OpenStruct.new(id: SecureRandom.uuid, address: address) }

subject do
Hours::Serializers::Node.new(object: model).as_jsonapi
Hours::Serializers::Place.new(object: model).as_jsonapi
end

it 'has an id' do

+ 1
- 1
test/support/event_helpers.rb View File

@@ -27,7 +27,7 @@ module EventHelpers

def projectors
@projectors = [
Hours::Projections::Nodes::Projector.new
Hours::Projections::Places::Projector.new
]
end
end

test/support/workflows/add_node.rb → test/support/workflows/add_place.rb View File

@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Workflows
module AddNode
module AddPlace
def setup
super

@@ -23,11 +23,11 @@ module Workflows
end

def events
@events ||= [NodeAdded.new(aggregate_id: hm_id, body: body)]
@events ||= [PlaceAdded.new(aggregate_id: hm_id, body: body)]
end

def projector
@projector ||= Hours::Projections::Nodes::Projector.new
@projector ||= Hours::Projections::Places::Projector.new
end
end
end

Loading…
Cancel
Save