Merge pull request #1162 from lagartoflojo/mysql-tests

Make tests pass on MySQL
This commit is contained in:
Henne Vogelsang 2016-08-23 11:41:23 +02:00 committed by GitHub
commit 73bfc4b708
16 changed files with 174 additions and 137 deletions

View file

@ -7,6 +7,8 @@ rvm:
before_install: before_install:
- "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc" - "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
- "echo `phantomjs -v`" - "echo `phantomjs -v`"
addons:
mariadb: '10.1'
notifications: notifications:
email: email:
on_success: change on_success: change
@ -17,8 +19,9 @@ notifications:
on_success: change on_success: change
on_failure: change on_failure: change
before_script: before_script:
- cp config/database.yml.example config/database.yml - cp config/database.yml.travis config/database.yml
- cp config/secrets.yml.example config/secrets.yml - cp config/secrets.yml.example config/secrets.yml
- mysql -u root -e 'create database osem_test;'
- RAILS_ENV=test bundle exec rake db:migrate --trace - RAILS_ENV=test bundle exec rake db:migrate --trace
script: script:
- 'bundle exec rubocop -Dc .rubocop.yml' - 'bundle exec rubocop -Dc .rubocop.yml'

View file

@ -227,6 +227,8 @@ group :test do
gem 'webmock' gem 'webmock'
# for mocking Stripe responses in tests # for mocking Stripe responses in tests
gem 'stripe-ruby-mock' gem 'stripe-ruby-mock'
# For validating JSON schemas
gem 'json-schema'
end end
group :development, :test do group :development, :test do

View file

@ -230,6 +230,8 @@ GEM
jquery-ui-rails (4.2.1) jquery-ui-rails (4.2.1)
railties (>= 3.2.16) railties (>= 3.2.16)
json (1.8.3) json (1.8.3)
json-schema (2.5.0)
addressable (~> 2.3)
jwt (1.0.0) jwt (1.0.0)
launchy (2.4.2) launchy (2.4.2)
addressable (~> 2.3) addressable (~> 2.3)
@ -577,6 +579,7 @@ DEPENDENCIES
jquery-datatables-rails (~> 2.2.1) jquery-datatables-rails (~> 2.2.1)
jquery-rails jquery-rails
jquery-ui-rails (~> 4.2.1) jquery-ui-rails (~> 4.2.1)
json-schema
leaflet-rails leaflet-rails
letter_opener letter_opener
letter_opener_web letter_opener_web

View file

@ -35,8 +35,8 @@
edit_admin_conference_campaign_path(@conference.short_title, campaign.id), edit_admin_conference_campaign_path(@conference.short_title, campaign.id),
class: 'btn btn-primary' class: 'btn btn-primary'
= link_to 'Delete', = link_to 'Delete',
admin_conference_campaign_path(@conference.short_title, campaign.id), admin_conference_campaign_path(@conference.short_title, campaign.id),
method: :delete, class: 'btn btn-danger', data: { confirm: "Do you really want to delete the campaign #{campaign.name}?" } method: :delete, class: 'btn btn-danger', data: { confirm: "Do you really want to delete the campaign #{campaign.name}?" }
.row .row
.col-md-12 .col-md-12
= link_to 'New Campaign', new_admin_conference_campaign_path, class: 'btn btn-success pull-right' = link_to 'New Campaign', new_admin_conference_campaign_path, class: 'btn btn-success pull-right'

View file

@ -0,0 +1,5 @@
test:
adapter: mysql2
database: osem_test
username: root
encoding: utf8

View file

@ -4,6 +4,7 @@ describe ProposalController do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:conference) { create(:conference, short_title: 'lama101') } let(:conference) { create(:conference, short_title: 'lama101') }
let(:event) { create(:event, program: conference.program) } let(:event) { create(:event, program: conference.program) }
let(:event_type) { create :event_type }
context 'user is not signed in' do context 'user is not signed in' do
describe 'GET #new' do describe 'GET #new' do
@ -28,7 +29,7 @@ describe ProposalController do
before { conference.program.update_attributes(cfp: create(:cfp)) } before { conference.program.update_attributes(cfp: create(:cfp)) }
it 'assigns url variables' do it 'assigns url variables' do
post :create, event: attributes_for(:event, event_type_id: 1), post :create, event: attributes_for(:event, event_type_id: event_type.id),
conference_id: conference.short_title, conference_id: conference.short_title,
user: attributes_for(:user) user: attributes_for(:user)
expect(assigns(:url)).to eq '/conference/lama101/program/proposal' expect(assigns(:url)).to eq '/conference/lama101/program/proposal'
@ -38,7 +39,7 @@ describe ProposalController do
describe 'user related actions' do describe 'user related actions' do
before do before do
@new_user = attributes_for(:user) @new_user = attributes_for(:user)
post :create, event: attributes_for(:event, event_type_id: 1), post :create, event: attributes_for(:event, event_type_id: event_type.id),
conference_id: conference.short_title, conference_id: conference.short_title,
user: @new_user user: @new_user
end end
@ -55,7 +56,7 @@ describe ProposalController do
context 'creates proposal successfully' do context 'creates proposal successfully' do
before(:each, run: true) do before(:each, run: true) do
@new_user = attributes_for(:user) @new_user = attributes_for(:user)
post :create, event: attributes_for(:event, event_type_id: 1), post :create, event: attributes_for(:event, event_type_id: event_type.id),
conference_id: conference.short_title, conference_id: conference.short_title,
user: @new_user user: @new_user
end end
@ -83,7 +84,7 @@ describe ProposalController do
it 'creates new event' do it 'creates new event' do
expect do expect do
post :create, event: attributes_for(:event, event_type_id: 1), post :create, event: attributes_for(:event, event_type_id: event_type.id),
conference_id: conference.short_title, conference_id: conference.short_title,
user: attributes_for(:user) user: attributes_for(:user)
end.to change{ Event.count }.by 1 end.to change{ Event.count }.by 1
@ -93,7 +94,7 @@ describe ProposalController do
context 'proposal save fails' do context 'proposal save fails' do
before(:each, run: true) do before(:each, run: true) do
allow_any_instance_of(Event).to receive(:save).and_return(false) allow_any_instance_of(Event).to receive(:save).and_return(false)
post :create, event: attributes_for(:event, event_type_id: 1), post :create, event: attributes_for(:event, event_type_id: event_type.id),
conference_id: conference.short_title, conference_id: conference.short_title,
user: attributes_for(:user) user: attributes_for(:user)
end end
@ -109,7 +110,7 @@ describe ProposalController do
it 'does not create new proposal' do it 'does not create new proposal' do
allow_any_instance_of(Event).to receive(:save).and_return(false) allow_any_instance_of(Event).to receive(:save).and_return(false)
expect do expect do
post :create, event: attributes_for(:event, event_type_id: 1), post :create, event: attributes_for(:event, event_type_id: event_type.id),
conference_id: conference.short_title, conference_id: conference.short_title,
user: attributes_for(:user) user: attributes_for(:user)
end.not_to change{ Event.count } end.not_to change{ Event.count }
@ -122,7 +123,7 @@ describe ProposalController do
it 'does not create new user' do it 'does not create new user' do
expect do expect do
post :create, event: attributes_for(:event, event_type_id: 1), post :create, event: attributes_for(:event, event_type_id: event_type.id),
conference_id: conference.short_title, conference_id: conference.short_title,
user: attributes_for(:user) user: attributes_for(:user)
end.not_to change { User.count } end.not_to change { User.count }
@ -130,7 +131,7 @@ describe ProposalController do
it 'does not create new event' do it 'does not create new event' do
expect do expect do
post :create, event: attributes_for(:event, event_type_id: 1), post :create, event: attributes_for(:event, event_type_id: event_type.id),
conference_id: conference.short_title, conference_id: conference.short_title,
user: attributes_for(:user) user: attributes_for(:user)
end.not_to change { Event.count } end.not_to change { Event.count }
@ -138,7 +139,7 @@ describe ProposalController do
describe 'response' do describe 'response' do
before do before do
post :create, event: attributes_for(:event, event_type_id: 1), post :create, event: attributes_for(:event, event_type_id: event_type.id),
conference_id: conference.short_title, conference_id: conference.short_title,
user: attributes_for(:user) user: attributes_for(:user)
end end
@ -213,7 +214,7 @@ describe ProposalController do
it 'assigns event and url variables' do it 'assigns event and url variables' do
expect(assigns(:event)).to eq event expect(assigns(:event)).to eq event
expect(assigns(:url)).to eq '/conference/lama101/program/proposal/1' expect(assigns(:url)).to eq "/conference/lama101/program/proposal/#{event.id}"
end end
it 'renders edit template' do it 'renders edit template' do
@ -226,14 +227,14 @@ describe ProposalController do
before { conference.program.update_attributes(cfp: create(:cfp)) } before { conference.program.update_attributes(cfp: create(:cfp)) }
it 'assigns url variables' do it 'assigns url variables' do
post :create, event: attributes_for(:event, event_type_id: 1), post :create, event: attributes_for(:event, event_type_id: event_type.id),
conference_id: conference.short_title conference_id: conference.short_title
expect(assigns(:url)).to eq '/conference/lama101/program/proposal' expect(assigns(:url)).to eq '/conference/lama101/program/proposal'
end end
context 'creates proposal successfully' do context 'creates proposal successfully' do
before(:each, run: true) do before(:each, run: true) do
post :create, event: attributes_for(:event, event_type_id: 1), post :create, event: attributes_for(:event, event_type_id: event_type.id),
conference_id: conference.short_title conference_id: conference.short_title
end end
@ -260,7 +261,7 @@ describe ProposalController do
it 'creates new event' do it 'creates new event' do
expect do expect do
post :create, event: attributes_for(:event, event_type_id: 1), post :create, event: attributes_for(:event, event_type_id: event_type.id),
conference_id: conference.short_title conference_id: conference.short_title
end.to change{ Event.count }.by 1 end.to change{ Event.count }.by 1
end end
@ -269,7 +270,7 @@ describe ProposalController do
context 'proposal save fails' do context 'proposal save fails' do
before(:each, run: true) do before(:each, run: true) do
allow_any_instance_of(Event).to receive(:save).and_return(false) allow_any_instance_of(Event).to receive(:save).and_return(false)
post :create, event: attributes_for(:event, event_type_id: 1), post :create, event: attributes_for(:event, event_type_id: event_type.id),
conference_id: conference.short_title conference_id: conference.short_title
end end
@ -284,7 +285,7 @@ describe ProposalController do
it 'does not create new proposal' do it 'does not create new proposal' do
allow_any_instance_of(Event).to receive(:save).and_return(false) allow_any_instance_of(Event).to receive(:save).and_return(false)
expect do expect do
post :create, event: attributes_for(:event, event_type_id: 1), post :create, event: attributes_for(:event, event_type_id: event_type.id),
conference_id: conference.short_title conference_id: conference.short_title
end.not_to change{ Event.count } end.not_to change{ Event.count }
end end
@ -294,15 +295,15 @@ describe ProposalController do
describe 'PATCH #update' do describe 'PATCH #update' do
it 'assigns url variable' do it 'assigns url variable' do
patch :update, event: attributes_for(:event, title: 'some title', event_type_id: 1), patch :update, event: attributes_for(:event, title: 'some title', event_type_id: event_type.id),
conference_id: conference.short_title, conference_id: conference.short_title,
id: event.id id: event.id
expect(assigns(:url)).to eq '/conference/lama101/program/proposal/1' expect(assigns(:url)).to eq "/conference/lama101/program/proposal/#{event.id}"
end end
context 'updates successfully' do context 'updates successfully' do
before do before do
patch :update, event: attributes_for(:event, title: 'some title', event_type_id: 1), patch :update, event: attributes_for(:event, title: 'some title', event_type_id: event_type.id),
conference_id: conference.short_title, conference_id: conference.short_title,
id: event.id id: event.id
end end
@ -324,7 +325,7 @@ describe ProposalController do
context 'update fails' do context 'update fails' do
before do before do
allow_any_instance_of(Event).to receive(:save).and_return(false) allow_any_instance_of(Event).to receive(:save).and_return(false)
patch :update, event: attributes_for(:event, title: 'some title', event_type_id: 1), patch :update, event: attributes_for(:event, title: 'some title', event_type_id: event_type.id),
conference_id: conference.short_title, conference_id: conference.short_title,
id: event.id id: event.id
end end
@ -348,7 +349,7 @@ describe ProposalController do
it 'assigns url variable' do it 'assigns url variable' do
patch :withdraw, conference_id: conference.short_title, id: event.id patch :withdraw, conference_id: conference.short_title, id: event.id
expect(assigns(:url)).to eq '/conference/lama101/program/proposal/1' expect(assigns(:url)).to eq "/conference/lama101/program/proposal/#{event.id}"
end end
context 'withdraws successfully' do context 'withdraws successfully' do
@ -425,7 +426,7 @@ describe ProposalController do
end end
it 'assigns url variable' do it 'assigns url variable' do
expect(assigns(:url)).to eq '/conference/lama101/program/proposal/1' expect(assigns(:url)).to eq "/conference/lama101/program/proposal/#{event.id}"
end end
it 'change state of event to confirmed' do it 'change state of event to confirmed' do
@ -438,7 +439,7 @@ describe ProposalController do
before { patch :confirm, conference_id: conference.short_title, id: event.id } before { patch :confirm, conference_id: conference.short_title, id: event.id }
it 'assigns url variable' do it 'assigns url variable' do
expect(assigns(:url)).to eq '/conference/lama101/program/proposal/1' expect(assigns(:url)).to eq "/conference/lama101/program/proposal/#{event.id}"
end end
it 'change state of event to confirmed' do it 'change state of event to confirmed' do
@ -523,7 +524,7 @@ describe ProposalController do
it 'assigns url variable' do it 'assigns url variable' do
patch :restart, conference_id: conference.short_title, id: event.id patch :restart, conference_id: conference.short_title, id: event.id
expect(assigns(:url)).to eq '/conference/lama101/program/proposal/1' expect(assigns(:url)).to eq "/conference/lama101/program/proposal/#{event.id}"
end end
context 'resubmits successfully' do context 'resubmits successfully' do

View file

@ -17,8 +17,7 @@ feature Campaign do
click_button 'Create Campaign' click_button 'Create Campaign'
expect(flash). expect(flash).to eq("Campaign creation failed. Name can't be blank and Utm campaign can't be blank")
to eq("Campaign creation failed. Name can't be blank and Utm campaign can't be blank")
fill_in 'campaign_name', with: 'Test Campaign' fill_in 'campaign_name', with: 'Test Campaign'
fill_in 'campaign_utm_campaign', with: 'campaign' fill_in 'campaign_utm_campaign', with: 'campaign'
@ -30,13 +29,12 @@ feature Campaign do
click_button 'Create Campaign' click_button 'Create Campaign'
# Validations # Validations
expect(flash). expect(flash).to eq('Campaign successfully created.')
to eq('Campaign successfully created.')
expect(find('#name_1').text).to eq('Test Campaign') expect(page).to have_selector('[id^="name_"]', text: 'Test Campaign')
expect(find('#visits_1').text).to eq('0') expect(page).to have_selector('[id^="visits_"]', text: '0')
expect(find('#registrations_1').text).to eq('0') expect(page).to have_selector('[id^="registrations_"]', text: '0')
expect(find('#submissions_1').text).to eq('0') expect(page).to have_selector('[id^="submissions_"]', text: '0')
expect(Campaign.count).to eq(expected_count) expect(Campaign.count).to eq(expected_count)
@ -45,8 +43,7 @@ feature Campaign do
fill_in 'campaign_name', with: 'Test Campaign 42' fill_in 'campaign_name', with: 'Test Campaign 42'
click_button 'Update Campaign' click_button 'Update Campaign'
expect(flash). expect(flash).to eq("Campaign 'Test Campaign 42' successfully updated.")
to eq("Campaign 'Test Campaign 42' successfully updated.")
end end
end end

View file

@ -36,7 +36,7 @@ describe Campaign do
campaign.conference = build(:conference) campaign.conference = build(:conference)
create(:visit, utm_source: 'google+', utm_medium: 'advertisement', create(:visit, utm_source: 'google+', utm_medium: 'advertisement',
utm_term: 'opensource', utm_content: 'content', utm_campaign: '20percent', started_at: Time.now) utm_term: 'opensource', utm_content: 'content', utm_campaign: '20percent', started_at: Time.now + 1.hour)
expect(campaign.visits_count).to eq(1) expect(campaign.visits_count).to eq(1)
end end

View file

@ -906,7 +906,7 @@ describe Conference do
let!(:organizer) { create(:user, role_ids: [organizer_role.id]) } let!(:organizer) { create(:user, role_ids: [organizer_role.id]) }
it 'self#event_distribution calculates correct values with user' do it 'self#event_distribution calculates correct values with user' do
create(:user, last_sign_in_at: Date.today - 3.months) # active create(:user, last_sign_in_at: Date.today - 3.months + 1.day) # active
create(:user, confirmed_at: nil) # unconfirmed create(:user, confirmed_at: nil) # unconfirmed
create(:user, last_sign_in_at: Date.today - 1.year - 1.day) # dead create(:user, last_sign_in_at: Date.today - 1.year - 1.day) # dead
result = {} result = {}
@ -918,7 +918,7 @@ describe Conference do
end end
it 'self#event_distribution calculates correct with only active user' do it 'self#event_distribution calculates correct with only active user' do
create(:user, last_sign_in_at: Date.today - 3.months) # active create(:user, last_sign_in_at: Date.today - 3.months + 1.day) # active
result = {} result = {}
result['Active'] = { 'color' => 'green', 'value' => 1 } result['Active'] = { 'color' => 'green', 'value' => 1 }

View file

@ -29,13 +29,13 @@ describe SponsorshipLevel do
it 'is positions sponsorship_levels in order' do it 'is positions sponsorship_levels in order' do
expect(SponsorshipLevel.where(conference_id: conference.id).order(:position).map(&:id)) expect(SponsorshipLevel.where(conference_id: conference.id).order(:position).map(&:id))
.to eq [2, 1, 3] .to eq [@second_sponsorship_level.id, @first_sponsorship_level.id, @third_sponsorship_level.id]
end end
it 'maintains order after deleting one element' do it 'maintains order after deleting one element' do
@first_sponsorship_level.destroy @first_sponsorship_level.destroy
expect(SponsorshipLevel.where(conference_id: conference.id).order(:position).map(&:id)) expect(SponsorshipLevel.where(conference_id: conference.id).order(:position).map(&:id))
.to eq [2, 3] .to eq [@second_sponsorship_level.id, @third_sponsorship_level.id]
end end
end end
end end

View file

@ -348,9 +348,7 @@ describe User do
describe 'assigns admin attribute' do describe 'assigns admin attribute' do
it 'to second user when first user is deleted_user' do it 'to second user when first user is deleted_user' do
DatabaseCleaner.clean_with(:truncation) deleted_user = User.find_by(email: 'deleted@localhost.osem')
deleted_user = create(:user, email: 'deleted@localhost.osem', name: 'User deleted')
expect(deleted_user.is_admin).to be false expect(deleted_user.is_admin).to be false
user_after_deleted = create(:admin) user_after_deleted = create(:admin)
@ -360,8 +358,6 @@ describe User do
describe 'does not assign admin attribute' do describe 'does not assign admin attribute' do
it 'when first user is not deleted_user' do it 'when first user is not deleted_user' do
DatabaseCleaner.clean_with(:truncation)
first_user = create(:user) first_user = create(:user)
expect(first_user.is_admin).to be false expect(first_user.is_admin).to be false

View file

@ -1,4 +1,5 @@
require 'spec_helper' require 'spec_helper'
describe ConferenceSerializer, type: :serializer do describe ConferenceSerializer, type: :serializer do
let(:conference) do let(:conference) do
create(:conference, short_title: 'goto', create(:conference, short_title: 'goto',
@ -8,84 +9,20 @@ describe ConferenceSerializer, type: :serializer do
end end
let(:serializer) { ConferenceSerializer.new(conference) } let(:serializer) { ConferenceSerializer.new(conference) }
let(:expected_hash) do
{
conference: {
short_title: 'goto',
title: conference.title,
description: 'Lorem ipsum dolor sit',
start_date: '2014-03-04',
end_date: '2014-03-10',
picture_url: nil,
difficulty_levels:
[{id: 1,
title: 'Easy',
description: 'Events are understandable for everyone without knowledge of the topic.'
},
{id: 2,
title: 'Medium',
description: 'Events require a basic understanding of the topic.'
},
{id: 3,
title: 'Hard',
description: 'Events require expert knowledge of the topic.'
}
],
event_types:
[{id: 1,
title: 'Talk',
length: 30,
description: 'Presentation in lecture format'
},
{id: 2,
title: 'Workshop',
length: 60,
description: 'Interactive hands-on practice'
}
],
rooms: [],
tracks: [],
date_range: 'March 04 - 10',
revision: 1
}
}
end
context 'conference does not have rooms and tracks' do context 'when the conference does not have rooms and tracks' do
it 'sets conference attributes with empty room and tracks' do it 'correctly serializes the conference' do
expect(serializer.to_json).to eq expected_hash.to_json expect(serializer.to_json).to match_response_schema('conference')
end end
end end
context 'conference has rooms and tracks' do context 'when the conference has rooms and tracks' do
before do let(:venue) { create(:venue, conference: conference) }
venue = create(:venue, conference: conference) let!(:room) { create(:room, venue: venue) }
_room = create(:room, venue: venue) let!(:track) { create(:track, program: conference.program) }
track = create(:track, program: conference.program)
room_hash = { it 'correctly serializes the conference' do
rooms: [{ expect(serializer.to_json).to match_response_schema('conference')
id: 1,
size: 4,
events: []
}
]
}
track_hash = {
tracks: [{
id: 1,
name: track.name,
description: track.description
}
]
}
expected_hash[:conference].merge! room_hash
expected_hash[:conference].merge! track_hash
end
it 'sets conference attributes with rooms and tracks' do
expect(serializer.to_json).to eq expected_hash.to_json
end end
end end
end end

View file

@ -55,15 +55,9 @@ RSpec.configure do |config|
# --seed 1234 # --seed 1234
config.order = 'random' config.order = 'random'
# Setting up DB cleaning to maintain empty rows
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
config.around(:each) do |example| config.around(:each) do |example|
DatabaseCleaner.cleaning do DatabaseCleaner.cleaning do
load "#{Rails.root}/db/seeds.rb" Rails.application.load_seed
example.run example.run
end end
end end
@ -89,15 +83,12 @@ RSpec.configure do |config|
c.syntax = :expect c.syntax = :expect
end end
config.use_transactional_fixtures = true
# Reuse rspec as mocking framework # Reuse rspec as mocking framework
config.mock_framework = :rspec config.mock_framework = :rspec
# Types of tests (controller, feature, model) will # Types of tests (controller, feature, model) will
# be inferred from subfolder name # be inferred from subfolder name
config.infer_spec_type_from_file_location! config.infer_spec_type_from_file_location!
end end
OmniAuth.config.test_mode = true OmniAuth.config.test_mode = true

View file

@ -0,0 +1,9 @@
# Source: https://robots.thoughtbot.com/validating-json-schemas-with-an-rspec-matcher
RSpec::Matchers.define :match_response_schema do |schema|
match do |json|
schema_directory = "#{Dir.pwd}/spec/support/schemas"
schema_path = "#{schema_directory}/#{schema}.json"
JSON::Validator.validate!(schema_path, json, strict: true)
end
end

View file

@ -10,12 +10,4 @@ RSpec.configure do |config|
config.before(:each, js: true) do config.before(:each, js: true) do
DatabaseCleaner.strategy = :truncation DatabaseCleaner.strategy = :truncation
end end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end end

View file

@ -0,0 +1,101 @@
{
"type": "object",
"required": ["conference"],
"properties": {
"conference" : {
"type" : "object",
"required" : [
"short_title",
"title",
"description",
"start_date",
"end_date",
"picture_url",
"difficulty_levels",
"event_types",
"rooms",
"tracks",
"date_range",
"revision"
],
"properties" : {
"short_title": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"start_date": {
"type": "string", "format": "date"
},
"end_date": {
"type": "string", "format": "date"
},
"picture_url": {
"anyOf": [
{ "type": "string" },
{ "type": "null" }
]
},
"difficulty_levels": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "title", "description"],
"properties": {
"id": { "type": "integer" },
"title": { "type": "string" },
"description": { "type": "string" }
}
}
},
"event_types": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "title", "description", "length"],
"properties": {
"id": { "type": "integer" },
"title": { "type": "string" },
"description": { "type": "string" },
"length": { "type": "integer" }
}
}
},
"rooms": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "size", "events"],
"properties": {
"id": { "type": "integer" },
"size": { "type": "integer" },
"events": { "type": "array" }
}
}
},
"tracks": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "name", "description"],
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"description": { "type": "string" }
}
}
},
"date_range": {
"type": "string"
},
"revision": {
"type": "integer"
}
}
}
}
}