diff options
Diffstat (limited to 'spec')
49 files changed, 4262 insertions, 0 deletions
| diff --git a/spec/factories.rb b/spec/factories/chouette_2_factories.rb index 44179b563..44179b563 100644 --- a/spec/factories.rb +++ b/spec/factories/chouette_2_factories.rb diff --git a/spec/factories/chouette_access_links.rb b/spec/factories/chouette_access_links.rb new file mode 100644 index 000000000..94717e95e --- /dev/null +++ b/spec/factories/chouette_access_links.rb @@ -0,0 +1,13 @@ +FactoryGirl.define do +   +  factory :access_link, :class => Chouette::AccessLink do +    sequence(:name) { |n| "Access link #{n}" } +    sequence(:objectid) { |n| "test:AccessLink:#{n}" } +    link_type "Mixed"  +    link_orientation "AccessPointToStopArea" + +    association :stop_area, :factory => :stop_area +    association :access_point, :factory => :access_point +  end + +end diff --git a/spec/factories/chouette_access_points.rb b/spec/factories/chouette_access_points.rb new file mode 100644 index 000000000..06d1da779 --- /dev/null +++ b/spec/factories/chouette_access_points.rb @@ -0,0 +1,12 @@ +FactoryGirl.define do + +  factory :access_point, :class => Chouette::AccessPoint do +    latitude {10.0 * rand} +    longitude {10.0 * rand} +    sequence(:name) { |n| "AccessPoint #{n}" } +    access_type "InOut" +    sequence(:objectid) { |n| "test:AccessPoint:#{n}" } +    association :stop_area, :factory => :stop_area +  end + +end diff --git a/spec/factories/chouette_companies.rb b/spec/factories/chouette_companies.rb new file mode 100644 index 000000000..c0e46fe42 --- /dev/null +++ b/spec/factories/chouette_companies.rb @@ -0,0 +1,9 @@ +FactoryGirl.define do + +  factory :company, :class => Chouette::Company do +    sequence(:name) { |n| "Company #{n}" } +    sequence(:objectid) { |n| "test:Company:#{n}" } +    sequence(:registration_number) { |n| "test-#{n}" } +  end + +end diff --git a/spec/factories/chouette_connection_links.rb b/spec/factories/chouette_connection_links.rb new file mode 100644 index 000000000..f70548721 --- /dev/null +++ b/spec/factories/chouette_connection_links.rb @@ -0,0 +1,13 @@ +FactoryGirl.define do + +  factory :connection_link, :class => Chouette::ConnectionLink do +    sequence(:name) { |n| "Connection link #{n}" } +    sequence(:link_type) { |n| "Mixed" } +    sequence(:objectid) { |n| "test:ConnectionLink:#{n}" } + +    association :departure, :factory => :stop_area +    association :arrival, :factory => :stop_area +  end +   +end + diff --git a/spec/factories/chouette_footnotes.rb b/spec/factories/chouette_footnotes.rb new file mode 100644 index 000000000..d30e305ee --- /dev/null +++ b/spec/factories/chouette_footnotes.rb @@ -0,0 +1,10 @@ +FactoryGirl.define do + +  factory :footnote, :class => Chouette::Footnote do +    sequence(:code) { |n| "#{n}" } +    sequence(:label) { |n| "footnote #{n}" } +    association :line, :factory => :line +  end + +end + diff --git a/spec/factories/chouette_group_of_lines.rb b/spec/factories/chouette_group_of_lines.rb new file mode 100644 index 000000000..3309674e3 --- /dev/null +++ b/spec/factories/chouette_group_of_lines.rb @@ -0,0 +1,9 @@ +FactoryGirl.define do + +  factory  :group_of_line, :class => Chouette::GroupOfLine do +    sequence(:name) { |n| "Group Of Line #{n}" } +    sequence(:objectid) { |n| "test:GroupOfLine:#{n}" } +    sequence(:registration_number) { |n| "#{n}" } +  end + +end diff --git a/spec/factories/chouette_journey_frequency.rb b/spec/factories/chouette_journey_frequency.rb new file mode 100644 index 000000000..aa28bdefe --- /dev/null +++ b/spec/factories/chouette_journey_frequency.rb @@ -0,0 +1,38 @@ +FactoryGirl.define do + +  factory :journey_frequency, class: Chouette::JourneyFrequency do +    timeband +    scheduled_headway_interval { Time.current } +    first_departure_time { timeband.start_time } +    last_departure_time { timeband.end_time } +  end + +  factory :journey_frequency_first_departure_time_invalid, class: Chouette::JourneyFrequency do +    timeband +    scheduled_headway_interval { Time.current } +    first_departure_time { timeband.start_time - 1.minute } +    last_departure_time { timeband.end_time } +  end + +  factory :journey_frequency_last_departure_time_invalid, class: Chouette::JourneyFrequency do +    timeband +    scheduled_headway_interval { Time.current } +    first_departure_time { timeband.start_time } +    last_departure_time { timeband.end_time + 1.minute } +  end + +  factory :journey_frequency_departure_time_invalid, class: Chouette::JourneyFrequency do +    timeband +    scheduled_headway_interval { Time.current } +    first_departure_time { '00:00' } +    last_departure_time { '00:00' } +  end + +  factory :journey_frequency_scheduled_headway_interval_invalid, class: Chouette::JourneyFrequency do +    timeband +    scheduled_headway_interval { '00:00' } +    first_departure_time { timeband.start_time } +    last_departure_time { timeband.end_time } +  end + +end diff --git a/spec/factories/chouette_journey_pattern.rb b/spec/factories/chouette_journey_pattern.rb new file mode 100644 index 000000000..bf55b286f --- /dev/null +++ b/spec/factories/chouette_journey_pattern.rb @@ -0,0 +1,40 @@ +FactoryGirl.define do +   +  factory :journey_pattern_common, :class => Chouette::JourneyPattern do +    sequence(:name) { |n| "jp name #{n}" } +    sequence(:published_name) { |n| "jp publishedname #{n}" } +    sequence(:comment) { |n| "jp comment #{n}" } +    sequence(:registration_number) { |n| "jp registration_number #{n}" } +    sequence(:objectid) { |n| "test:JourneyPattern:#{n}" } +     +    association :route, :factory => :route +     +    factory :journey_pattern do +      after(:create) do |j| +        j.stop_point_ids = j.route.stop_points.map(&:id) +        j.departure_stop_point_id = j.route.stop_points.first.id +        j.arrival_stop_point_id = j.route.stop_points.last.id +      end +    end +     +    factory :journey_pattern_odd do +      after(:create) do |j| +        j.stop_point_ids = j.route.stop_points.select { |sp| sp.position%2==0}.map(&:id) +        j.departure_stop_point_id = j.stop_point_ids.first +        j.arrival_stop_point_id = j.stop_point_ids.last +      end +    end +     +    factory :journey_pattern_even do +      after(:create) do |j| +        j.stop_point_ids = j.route.stop_points.select { |sp| sp.position%2==1}.map(&:id) +        j.departure_stop_point_id = j.stop_point_ids.first +        j.arrival_stop_point_id = j.stop_point_ids.last +      end +    end + +  end + +end + + diff --git a/spec/factories/chouette_lines.rb b/spec/factories/chouette_lines.rb new file mode 100644 index 000000000..9a5842458 --- /dev/null +++ b/spec/factories/chouette_lines.rb @@ -0,0 +1,44 @@ +FactoryGirl.define do + +  factory :line, :class => Chouette::Line do +    sequence(:name) { |n| "Line #{n}" } +    sequence(:objectid) { |n| "test:Line:#{n}" } +    sequence(:transport_mode_name) { |n| "Bus" } + +    association :network, :factory => :network +    association :company, :factory => :company + +    sequence(:registration_number) { |n| "test-#{n}" } +     +    factory :line_with_stop_areas do +       +      transient do +        routes_count 2 +        stop_areas_count 5 +      end +       +      after(:create) do |line, evaluator| +        create_list(:route, evaluator.routes_count, :line => line) do |route| +          create_list(:stop_area, evaluator.stop_areas_count, area_type: "Quay") do |stop_area| +            create(:stop_point, :stop_area => stop_area, :route => route) +          end            +        end +      end +       +      factory :line_with_stop_areas_having_parent do +         +        after(:create) do |line| +          line.routes.each do |route| +            route.stop_points.each do |stop_point| +              comm = create(:stop_area, :area_type => "CommercialStopPoint") +              stop_point.stop_area.update_attributes(:parent_id => comm.id) +            end +          end +        end +      end + +    end + +  end + +end diff --git a/spec/factories/chouette_networks.rb b/spec/factories/chouette_networks.rb new file mode 100644 index 000000000..8f9bfea51 --- /dev/null +++ b/spec/factories/chouette_networks.rb @@ -0,0 +1,9 @@ +FactoryGirl.define do + +  factory :network, :class => Chouette::Network do +    sequence(:name) { |n| "Network #{n}" } +    sequence(:objectid) { |n| "test:GroupOfLine:#{n}" } +    sequence(:registration_number) { |n| "test-#{n}" } +  end + +end diff --git a/spec/factories/chouette_route_sections.rb b/spec/factories/chouette_route_sections.rb new file mode 100644 index 000000000..d5e2cf69d --- /dev/null +++ b/spec/factories/chouette_route_sections.rb @@ -0,0 +1,6 @@ +FactoryGirl.define do +  factory :route_section, :class => Chouette::RouteSection do +    association :departure, :factory => :stop_area +    association :arrival, :factory => :stop_area +  end +end diff --git a/spec/factories/chouette_routes.rb b/spec/factories/chouette_routes.rb new file mode 100644 index 000000000..047c35912 --- /dev/null +++ b/spec/factories/chouette_routes.rb @@ -0,0 +1,26 @@ +FactoryGirl.define do + +  factory :route_common, :class => Chouette::Route do +    sequence(:name) { |n| "Route #{n}" } +    sequence(:published_name) { |n| "Long route #{n}" } +    sequence(:number) { |n| "#{n}" } +    sequence(:wayback_code) { |n| Chouette::Wayback.new( n % 2) } +    sequence(:direction_code) { |n| Chouette::Direction.new( n % 12) } +    sequence(:objectid) { |n| "test:Route:#{n}" } +     +    association :line, :factory => :line +     +    factory :route do + +      transient do +        stop_points_count 5 +      end +       +      after(:create) do |route, evaluator| +        create_list(:stop_point, evaluator.stop_points_count, route: route) +      end +       +    end +  end +   +end diff --git a/spec/factories/chouette_stop_areas.rb b/spec/factories/chouette_stop_areas.rb new file mode 100644 index 000000000..fab845de5 --- /dev/null +++ b/spec/factories/chouette_stop_areas.rb @@ -0,0 +1,12 @@ +FactoryGirl.define do + +  factory :stop_area, :class => Chouette::StopArea do +    sequence(:objectid) { |n| "test:StopArea:#{n}" } +    sequence(:name) { |n| "stop_area_#{n}" } +    sequence(:registration_number) { |n| "test-#{n}" } +    area_type "CommercialStopPoint" +    latitude {10.0 * rand} +    longitude {10.0 * rand} +  end + +end diff --git a/spec/factories/chouette_stop_points.rb b/spec/factories/chouette_stop_points.rb new file mode 100644 index 000000000..690d1c688 --- /dev/null +++ b/spec/factories/chouette_stop_points.rb @@ -0,0 +1,8 @@ +FactoryGirl.define do + +  factory :stop_point, :class => Chouette::StopPoint do +    sequence(:objectid) { |n| "test:StopPoint:#{n}" } +    association :stop_area, :factory => :stop_area +  end + +end diff --git a/spec/factories/chouette_time_table.rb b/spec/factories/chouette_time_table.rb new file mode 100644 index 000000000..722c977b2 --- /dev/null +++ b/spec/factories/chouette_time_table.rb @@ -0,0 +1,37 @@ +FactoryGirl.define do + +  factory :time_table_date, :class => Chouette::TimeTableDate do +  end +   +  factory :time_table_period, :class => Chouette::TimeTablePeriod do +  end +   +  factory :time_table, :class => Chouette::TimeTable do +    sequence(:comment) { |n| "Timetable #{n}" } +    sequence(:objectid) { |n| "test:Timetable:#{n}" } +    sequence(:int_day_types) { (1..7).to_a.map{ |n| 2**(n+1)}.sum } + +    transient do +      dates_count 4 +      periods_count 4 +    end +     +    after(:create) do |time_table, evaluator| +       +      0.upto(4) do |i| +        time_table.dates  << create(:time_table_date, :time_table => time_table, :date => i.days.since.to_date, :in_out => true) +      end +       +      start_date = Date.today +      end_date = start_date + 10 +       +      0.upto(4) do |i| +        time_table.periods << create(:time_table_period, :time_table => time_table, :period_start => start_date, :period_end => end_date) +        start_date = start_date + 20 +        end_date = start_date + 10 +      end +      time_table.save_shortcuts +    end +  end +   +end diff --git a/spec/factories/chouette_timeband.rb b/spec/factories/chouette_timeband.rb new file mode 100644 index 000000000..6e2825c22 --- /dev/null +++ b/spec/factories/chouette_timeband.rb @@ -0,0 +1,17 @@ +FactoryGirl.define do + +  factory :timeband, class: Chouette::Timeband do +    sequence(:name) { |n| "Name: #{n}" } +    start_time { Time.now } +    end_time { Time.now + 1.hour } +    sequence(:objectid) { |n| "test:Timeband:#{n}" } +  end + +  factory :timeband_invalid, class: Chouette::Timeband do +    sequence(:name) { |n| "Name: #{n}" } +    start_time { Time.now + 1.hour } +    end_time { Time.now } +    sequence(:objectid) { |n| "test:Timeband:#{n}" } +  end + +end diff --git a/spec/factories/chouette_vehicle_journey.rb b/spec/factories/chouette_vehicle_journey.rb new file mode 100644 index 000000000..2ad20e3a8 --- /dev/null +++ b/spec/factories/chouette_vehicle_journey.rb @@ -0,0 +1,66 @@ +FactoryGirl.define do +   +  factory :vehicle_journey_common, :class => Chouette::VehicleJourney do +    sequence(:objectid) { |n| "test:VehicleJourney:#{n}" } +     +    factory :vehicle_journey do +      association :journey_pattern, :factory => :journey_pattern +     +      after(:build) do |vehicle_journey| +        vehicle_journey.route = vehicle_journey.journey_pattern.route +      end +       +      after(:create) do |vehicle_journey| +        vehicle_journey.journey_pattern.stop_points.each_with_index do |stop_point, index| +          vehicle_journey.vehicle_journey_at_stops << create(:vehicle_journey_at_stop,  +                 :vehicle_journey => vehicle_journey,  +                 :stop_point => stop_point,  +                 :arrival_time => (-1 * index).minutes.ago,  +                 :departure_time => (-1 * index).minutes.ago) +        end +      end +       +      factory :vehicle_journey_odd do +        association :journey_pattern, :factory => :journey_pattern_odd +      end +       +      factory :vehicle_journey_even do +        association :journey_pattern, :factory => :journey_pattern_even +      end +    end +  end +end + +#      after(:build) do |vehicle_journey| +#        vehicle_journey.route_id = vehicle_journey.journey_pattern.route_id +#      end +#       +#      after(:create) do |vehicle_journey| +#        vehicle_journey.journey_pattern.stop_points.each_with_index do |stop_point, index| +#          vehicle_journey.vehicle_journey_at_stops.create(:vehicle_journey_at_stop,  +#                                                          :vehicle_journey => vehicle_journey,  +#                                                          :stop_point => stop_point,  +#                                                          :arrival_time => (-1 * index).minutes.ago,  +#                                                          :departure_time => (-1 * index).minutes.ago) +#        end +#      end +#    end +#     +#      after(:build) do |vehicle_journey| +#        vehicle_journey.route_id = vehicle_journey.journey_pattern.route_id +#      end +#       +#      after(:create) do |vehicle_journey| +#        vehicle_journey.journey_pattern.stop_points.each_with_index do |stop_point, index| +#          vehicle_journey.vehicle_journey_at_stops.create(:vehicle_journey_at_stop,  +#                                                          :vehicle_journey => vehicle_journey,  +#                                                          :stop_point => stop_point,  +#                                                          :arrival_time => (-1 * index).minutes.ago,  +#                                                          :departure_time => (-1 * index).minutes.ago) +#        end +#      end +#    end +#     +#  end +#end +# diff --git a/spec/factories/chouette_vehicle_journey_at_stop.rb b/spec/factories/chouette_vehicle_journey_at_stop.rb new file mode 100644 index 000000000..c452a1317 --- /dev/null +++ b/spec/factories/chouette_vehicle_journey_at_stop.rb @@ -0,0 +1,8 @@ +FactoryGirl.define do +   +  factory :vehicle_journey_at_stop, :class => Chouette::VehicleJourneyAtStop do +    association :vehicle_journey, :factory => :vehicle_journey +  end + +end + diff --git a/spec/models/chouette/access_link_spec.rb b/spec/models/chouette/access_link_spec.rb new file mode 100644 index 000000000..0e1e91593 --- /dev/null +++ b/spec/models/chouette/access_link_spec.rb @@ -0,0 +1,82 @@ +require 'spec_helper' + +describe Chouette::AccessLink, :type => :model do +  subject { create(:access_link) } + +  it { is_expected.to validate_uniqueness_of :objectid } + +  describe '#objectid' do +    subject { super().objectid } +    it { is_expected.to be_kind_of(Chouette::ObjectId) } +  end + +  it { is_expected.to validate_presence_of :name } +  #it { is_expected.to validate_presence_of :link_type } +  it { is_expected.to validate_presence_of :link_orientation } + +  describe "#access_link_type" do + +    def self.legacy_link_types +      %w{Underground Mixed Overground} +    end +     +    legacy_link_types.each do |link_type| +      context "when link_type is #{link_type}" do +        access_link_type = Chouette::ConnectionLinkType.new(link_type.underscore) +        it "should be #{access_link_type}" do +          subject.link_type = link_type +          expect(subject.access_link_type).to eq(access_link_type) +        end +      end +    end +  end + +  describe "#access_link_type=" do +     +    it "should change link_type with ConnectionLinkType#name" do +      subject.access_link_type = "underground" +      expect(subject.link_type).to eq("Underground") +    end + +  end + +  describe "#link_orientation_type" do + +    def self.legacy_link_orientations +      %w{AccessPointToStopArea StopAreaToAccessPoint} +    end +     +    legacy_link_orientations.each do |link_orientation| +      context "when link_orientation is #{link_orientation}" do +        link_orientation_type = Chouette::LinkOrientationType.new(link_orientation.underscore) +        it "should be #{link_orientation_type}" do +          subject.link_orientation = link_orientation +          expect(subject.link_orientation_type).to eq(link_orientation_type) +        end +      end +    end + +  end + +  describe "#link_orientation_type=" do +     +    it "should change link_orientation with LinkOrientationType#name" do +      subject.link_orientation_type = "access_point_to_stop_area" +      expect(subject.link_orientation).to eq("AccessPointToStopArea") +    end + +  end + +  describe "#link_key" do +    it "should calculate link_key for access to area" do +      subject.link_orientation_type = "access_point_to_stop_area" +      expect(subject.link_key).to eq("A_#{subject.access_point.id}-S_#{subject.stop_area.id}") +    end +    it "should calculate link_key for area to access" do +      subject.link_orientation_type = "stop_area_to_access_point" +      expect(subject.link_key).to eq("S_#{subject.stop_area.id}-A_#{subject.access_point.id}") +    end +     +  end + +end diff --git a/spec/models/chouette/access_point_spec.rb b/spec/models/chouette/access_point_spec.rb new file mode 100644 index 000000000..41daca871 --- /dev/null +++ b/spec/models/chouette/access_point_spec.rb @@ -0,0 +1,269 @@ +require 'spec_helper' + +describe Chouette::AccessPoint, :type => :model do + +  describe '#objectid' do +    subject { super().objectid } +    it { is_expected.to be_kind_of(Chouette::ObjectId) } +  end + +  it { is_expected.to validate_presence_of :name } +  it { is_expected.to validate_numericality_of :latitude } +  it { is_expected.to validate_numericality_of :longitude } +   +  describe ".latitude" do +    it "should accept -90 value" do +      subject = create :access_point +      subject.latitude = -90 +      expect(subject.valid?).to be_truthy +    end +    it "should reject < -90 value" do +      subject = create :access_point +      subject.latitude = -90.0001 +      expect(subject.valid?).to be_falsey +    end +    it "should accept 90 value" do +      subject = create :access_point +      subject.latitude = 90 +      expect(subject.valid?).to be_truthy +    end +    it "should reject > 90 value" do +      subject = create :access_point +      subject.latitude = 90.0001 +      expect(subject.valid?).to be_falsey +    end +  end + +  describe ".longitude" do +    it "should accept -180 value" do +      subject = create :access_point +      subject.longitude = -180 +      expect(subject.valid?).to be_truthy +    end +    it "should reject < -180 value" do +      subject = create :access_point +      subject.longitude = -180.0001 +      expect(subject.valid?).to be_falsey +    end +    it "should accept 180 value" do +      subject = create :access_point +      subject.longitude = 180 +      expect(subject.valid?).to be_truthy +    end +    it "should reject > 180 value" do +      subject = create :access_point +      subject.longitude = 180.0001 +      expect(subject.valid?).to be_falsey +    end +  end + +  describe ".long_lat" do +    it "should accept longitude and latitude both as nil" do +      subject = create :access_point +      subject.longitude = nil +      subject.latitude = nil +      expect(subject.valid?).to be_truthy +    end +    it "should accept longitude and latitude both numerical" do +      subject = create :access_point +      subject.longitude = 10 +      subject.latitude = 10 +      expect(subject.valid?).to be_truthy +    end +    it "should reject longitude nil with latitude numerical" do +      subject = create :access_point +      subject.longitude = nil +      subject.latitude = 10 +      expect(subject.valid?).to be_falsey +    end +    it "should reject longitude numerical with latitude nil" do +      subject = create :access_point +      subject.longitude = 10 +      subject.latitude = nil +      expect(subject.valid?).to be_falsey +    end +  end  + +  describe "#access_type" do +    def self.legacy_access_types +      %w{In Out InOut} +    end +     +    legacy_access_types.each do |access_type| +      context "when access_type is #{access_type}" do +        access_point_type = Chouette::AccessPointType.new(access_type.underscore) +        it "should be #{access_point_type}" do +          subject.access_type = access_type +          expect(subject.access_point_type).to eq(access_point_type) +        end +      end +    end +  end + +  describe "#access_point_type=" do     +    it "should change access_type with Chouette::AccessPointType#name" do +      subject.access_point_type = "in_out" +      expect(subject.access_type).to eq("InOut") +    end + +  end + +  describe "#to_lat_lng" do +     +    it "should return nil if latitude is nil" do +      subject.latitude = nil +      expect(subject.to_lat_lng).to be_nil +    end + +    it "should return nil if longitude is nil" do +      subject.longitude = nil +      expect(subject.to_lat_lng).to be_nil +    end + +  end + +  describe "#geometry" do +     +    it "should be nil when to_lat_lng is nil" do +      allow(subject).to receive_messages :to_lat_lng => nil +      expect(subject.geometry).to be_nil +    end + +  end + +  describe "#generic_access_link_matrix" do +    it "should have 2 generic_access_links in matrix" do +      stop_place = create :stop_area, :area_type => "StopPlace"  +      commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint" ,:parent => stop_place +      subject = create :access_point, :stop_area => stop_place +      expect(subject.generic_access_link_matrix.size).to eq(2) +    end +     +    it "should have new generic_access_links in matrix" do +      commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint"  +      subject = create :access_point, :stop_area => commercial_stop_point +      subject.generic_access_link_matrix.each do |link| +        expect(link.id).to be_nil +      end +    end +    it "should have only last generic_access_links as new in matrix" do +      commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint"  +      subject = create :access_point, :stop_area => commercial_stop_point +      link = create :access_link, :access_point => subject, :stop_area => commercial_stop_point +      subject.generic_access_link_matrix.each do |link| +        if link.link_key.start_with?"A_"  +          expect(link.id).not_to be_nil +        else +          expect(link.id).to be_nil +        end   +      end +    end +  end + +  describe "#detail_access_link_matrix" do +    it "should have 4 detail_access_links in matrix" do +      stop_place = create :stop_area, :area_type => "StopPlace"  +      commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint" ,:parent => stop_place +      quay1 = create :stop_area, :parent => commercial_stop_point, :area_type => "Quay" +      quay2 = create :stop_area, :parent => commercial_stop_point, :area_type => "Quay" +      subject = create :access_point, :stop_area => stop_place +      expect(subject.detail_access_link_matrix.size).to eq(4) +    end +     +    it "should have new detail_access_links in matrix" do +      commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint"  +      quay = create :stop_area, :parent => commercial_stop_point, :area_type => "Quay" +      subject = create :access_point, :stop_area => commercial_stop_point +      subject.detail_access_link_matrix.each do |link| +        expect(link.id).to be_nil +      end +    end +    it "should have only last detail_access_links as new in matrix" do +      commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint"  +      quay = create :stop_area, :parent => commercial_stop_point, :area_type => "Quay" +      subject = create :access_point, :stop_area => commercial_stop_point +      link = create :access_link, :access_point => subject, :stop_area => quay +      subject.detail_access_link_matrix.each do |link| +        if link.link_key.start_with?"A_"  +          expect(link.id).not_to be_nil +        else +          expect(link.id).to be_nil +        end   +      end +    end +  end + +  describe "#coordinates" do +    it "should convert coordinates into latitude/longitude" do +     commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint"  +     subject = create :access_point, :stop_area => commercial_stop_point, :coordinates => "45.123,120.456" +     expect(subject.longitude).to be_within(0.001).of(120.456) +     expect(subject.latitude).to be_within(0.001).of(45.123) +   end +    it "should set empty coordinates into nil latitude/longitude" do +     commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint"  +     subject = create :access_point, :stop_area => commercial_stop_point, :coordinates => "45.123,120.456" +     expect(subject.longitude).to be_within(0.001).of(120.456) +     expect(subject.latitude).to be_within(0.001).of(45.123) +     subject.coordinates = "" +     subject.save +     expect(subject.longitude).to be_nil +     expect(subject.latitude).to be_nil +   end +    it "should convert latitude/longitude into coordinates" do +     commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint"  +     subject = create :access_point, :stop_area => commercial_stop_point, :longitude => 120.456, :latitude => 45.123 +     expect(subject.coordinates).to eq("45.123,120.456") +   end +    it "should convert nil latitude/longitude into empty coordinates" do +    commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint"  +     subject = create :access_point, :stop_area => commercial_stop_point, :longitude => nil, :latitude => nil +     expect(subject.coordinates).to eq("") +   end +    it "should accept valid coordinates" do +     commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint"  +     subject = create :access_point, :stop_area => commercial_stop_point, :coordinates => "45.123,120.456" +     expect(subject.valid?).to be_truthy +     subject.coordinates = "45.123, 120.456" +     expect(subject.valid?).to be_truthy +     expect(subject.longitude).to be_within(0.001).of(120.456) +     expect(subject.latitude).to be_within(0.001).of(45.123) +     subject.coordinates = "45.123,  -120.456" +     expect(subject.valid?).to be_truthy +     subject.coordinates = "45.123 ,120.456" +     expect(subject.valid?).to be_truthy +     subject.coordinates = "45.123   ,   120.456" +     expect(subject.valid?).to be_truthy +     subject.coordinates = " 45.123,120.456" +     expect(subject.valid?).to be_truthy +     subject.coordinates = "45.123,120.456  " +     expect(subject.valid?).to be_truthy +    end +    it "should accept valid coordinates on limits" do +     commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint"  +     subject = create :access_point, :stop_area => commercial_stop_point, :coordinates => "90,180" +     expect(subject.valid?).to be_truthy +     subject.coordinates = "-90,-180" +     expect(subject.valid?).to be_truthy +     subject.coordinates = "-90.,180." +     expect(subject.valid?).to be_truthy +     subject.coordinates = "-90.0,180.00" +     expect(subject.valid?).to be_truthy +    end +    it "should reject invalid coordinates" do +     commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint"  +     subject = create :access_point, :stop_area => commercial_stop_point +     subject.coordinates = ",12" +     expect(subject.valid?).to be_falsey +     subject.coordinates = "-90" +     expect(subject.valid?).to be_falsey +     subject.coordinates = "-90.1,180." +     expect(subject.valid?).to be_falsey +     subject.coordinates = "-90.0,180.1" +     expect(subject.valid?).to be_falsey +     subject.coordinates = "-91.0,18.1" +     expect(subject.valid?).to be_falsey +    end +  end +   +end diff --git a/spec/models/chouette/active_record_spec.rb b/spec/models/chouette/active_record_spec.rb new file mode 100644 index 000000000..087e41576 --- /dev/null +++ b/spec/models/chouette/active_record_spec.rb @@ -0,0 +1,121 @@ +require 'spec_helper' + +describe Chouette::ActiveRecord, :type => :model do + +  it { expect(Chouette::ActiveRecord.ancestors).to include(ActiveRecord::Base) } + +  describe "table_name" do + +    it "should return line for Chouette::Line" do +      expect(Chouette::Line.table_name).to eq("lines") +    end +     +    it "should return ptnetwork for Chouette::Network" do +      expect(Chouette::Network.table_name).to eq("networks") +    end + +    it "should return timetable_date for Chouette::TimeTableDate" do +      expect(Chouette::TimeTableDate.table_name).to eq("time_table_dates") +    end + +    it "should return timetable_period for Chouette::TimeTablePeriod" do +      expect(Chouette::TimeTablePeriod.table_name).to eq("time_table_periods") +    end + +  end + +  describe "method_missing" do +     +    it "should support method with additionnal underscores" do +      stop_area = Chouette::StopArea.new +      expect(stop_area.area_type).to eq(stop_area.area_type) +    end + +  end + + +  describe "respond_to?" do +     +    it "should respond to method with additionnal underscores" do +      stop_area = Chouette::StopArea.new +      expect(stop_area.respond_to?(:area_type)).to be_truthy +    end + +  end + +#   describe "create_reflection" do + +#     let(:macro) { :has_many } +#     let(:name) { :lines } +#     let(:options) { {} } +#     let(:active_record) { Chouette::Network } +     +#     let(:modified_options) { {:modified => true}  } + +#     it "should invoke create_reflection_without_chouette_naming with modified options" do +#       allow(Chouette::ActiveRecord::Reflection).to receive_messages :new => double(:options_with_default => modified_options) +#       expect(Chouette::ActiveRecord).to receive(:create_reflection_without_chouette_naming).with macro, name, modified_options, active_record + +#       Chouette::ActiveRecord.create_reflection macro, name, options, active_record +#     end + +#   end + +end + +# describe Chouette::ActiveRecord::Reflection, :type => :model do + +#   let(:macro) { :has_many } +#   let(:name) { :lines } +#   let(:options) { {} } +#   let(:active_record) { Chouette::Network } + +#   subject { Chouette::ActiveRecord::Reflection.new macro, name, options, active_record } + +#   describe "collection?" do + +#     it "should be true when macro is has_many" do +#       allow(subject).to receive_messages :macro => :has_many +#       expect(subject).to be_collection +#     end + +#     it "should be false when macro is belongs_to" do +#       allow(subject).to receive_messages :macro => :belong_to +#       expect(subject).not_to be_collection +#     end + +#   end + +#   describe "class_name" do +     +#     it "should be Chouette::Line when name is line" do +#       allow(subject).to receive_messages :name => "line" +#       expect(subject.class_name).to eq("Chouette::Line") +#     end + +#     it "should be Chouette::Routes when name is routes and reflection is a collection" do +#       allow(subject).to receive_messages :name => "routes", :collection? => true +#       expect(subject.class_name).to eq("Chouette::Route") +#     end + +#   end + + +#   describe "options" do +     +#     it "should define class_name if not" do +#       allow(subject).to receive_messages :options => {}, :class_name => "class_name" +#       expect(subject.options_with_default[:class_name]).to eq("class_name") +#     end + +#     it "should not define class_name if presents" do +#       allow(subject).to receive_messages :options => {:class_name => "dummy"} +#       expect(subject.options_with_default[:class_name]).to eq("dummy") +#     end + +#   end +   + +# end + + diff --git a/spec/models/chouette/area_type_spec.rb b/spec/models/chouette/area_type_spec.rb new file mode 100644 index 000000000..14902416b --- /dev/null +++ b/spec/models/chouette/area_type_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' + +describe Chouette::AreaType, :type => :model do +   +  def mode(text_code = "test", numerical_code = nil) +    numerical_code ||= 1 if text_code == "test" +    Chouette::AreaType.new(text_code, numerical_code) +  end + +  describe "#to_i" do +     +    it "should return numerical code" do +      expect(mode("test", 1).to_i).to eq(1) +    end + +  end + +  it "should return true to #test? when text code is 'test'" do +    expect(mode("test")).to be_test +  end + +  it "should be equal when text codes are identical" do +    expect(mode("test",1)).to eq(mode("test", 2)) +  end + +  describe ".new" do + +    it "should find numerical code from text code" do +      expect(mode("boarding_position").to_i).to eq(0) +    end + +    it "should find text code from numerical code" do +      expect(mode(0)).to eq("boarding_position") +    end + +    it "should accept another mode" do +      expect(Chouette::AreaType.new(mode("test"))).to eq(mode("test")) +    end +     +  end + + +  describe ".all" do +     +    Chouette::AreaType.definitions.each do |text_code, numerical_code| +      it "should include a AreaType #{text_code}" do +        expect(Chouette::AreaType.all).to include(Chouette::AreaType.new(text_code)) +      end +    end + +  end + +end diff --git a/spec/models/chouette/company_spec.rb b/spec/models/chouette/company_spec.rb new file mode 100644 index 000000000..3da8b4311 --- /dev/null +++ b/spec/models/chouette/company_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +describe Chouette::Company, :type => :model do + +  subject { create(:company) } + +  it { is_expected.to validate_presence_of :name } + +  # it { should validate_presence_of :objectid } +  it { is_expected.to validate_uniqueness_of :objectid } + +  describe "#nullables empty" do +    it "should set null empty nullable attributes" do +      subject.organizational_unit = '' +      subject.operating_department_name = '' +      subject.code = '' +      subject.phone = '' +      subject.fax = '' +      subject.email = '' +      subject.nil_if_blank +      expect(subject.organizational_unit).to be_nil +      expect(subject.operating_department_name).to be_nil +      expect(subject.code).to be_nil +      expect(subject.phone).to be_nil +      expect(subject.fax).to be_nil +      expect(subject.email).to be_nil +    end +  end + +  describe "#nullables non empty" do +    it "should not set null non epmty nullable attributes" do +      subject.organizational_unit = 'a' +      subject.operating_department_name = 'b' +      subject.code = 'c' +      subject.phone = 'd' +      subject.fax = 'z' +      subject.email = 'r' +      subject.nil_if_blank +      expect(subject.organizational_unit).not_to be_nil +      expect(subject.operating_department_name).not_to be_nil +      expect(subject.code).not_to be_nil +      expect(subject.phone).not_to be_nil +      expect(subject.fax).not_to be_nil +      expect(subject.email).not_to be_nil +    end +  end + +end diff --git a/spec/models/chouette/connection_link_spec.rb b/spec/models/chouette/connection_link_spec.rb new file mode 100644 index 000000000..e76190bcf --- /dev/null +++ b/spec/models/chouette/connection_link_spec.rb @@ -0,0 +1,60 @@ +require 'spec_helper' + +describe Chouette::ConnectionLink, :type => :model do +  let!(:quay) { create :stop_area, :area_type => "Quay" } +  let!(:boarding_position) { create :stop_area, :area_type => "BoardingPosition" } +  let!(:commercial_stop_point) { create :stop_area, :area_type => "CommercialStopPoint" } +  let!(:stop_place) { create :stop_area, :area_type => "StopPlace" } +  let!(:itl) { create :stop_area, :area_type => "ITL" } +  subject { create(:connection_link) } + +  it { is_expected.to validate_uniqueness_of :objectid } + +  describe '#objectid' do +    subject { super().objectid } +    it { is_expected.to be_kind_of(Chouette::ObjectId) } +  end + +  it { is_expected.to validate_presence_of :name } + +  describe "#connection_link_type" do + +    def self.legacy_link_types +      %w{Underground Mixed Overground} +    end +     +    legacy_link_types.each do |link_type| +      context "when link_type is #{link_type}" do +        connection_link_type = Chouette::ConnectionLinkType.new(link_type.underscore) +        it "should be #{connection_link_type}" do +          subject.link_type = link_type +          expect(subject.connection_link_type).to eq(connection_link_type) +        end +      end +    end +    context "when link_type is nil" do +      it "should be nil" do +        subject.link_type = nil +        expect(subject.connection_link_type).to be_nil +      end +    end + +  end + +  describe "#connection_link_type=" do +     +    it "should change link_type with ConnectionLinkType#name" do +      subject.connection_link_type = "Test" +      expect(subject.link_type).to eq("Test") +    end + +  end + +  describe ".possible_areas" do + +    it "should not find areas type ITL" do +      expect(subject.possible_areas).not_to eq([itl])  +    end +  end + +end diff --git a/spec/models/chouette/direction_spec.rb b/spec/models/chouette/direction_spec.rb new file mode 100644 index 000000000..8075a509e --- /dev/null +++ b/spec/models/chouette/direction_spec.rb @@ -0,0 +1,60 @@ +require 'spec_helper' + +describe Chouette::Direction, :type => :model do + +  describe ".new" do +    context "when single argument provided is a direction" do +      let(:text) { "dummy"} +      let(:direction){ Chouette::Direction.new( text, 1)} +      it "should be equals to the provided direction" do +        expect(direction).to eq(Chouette::Direction.new( direction)) +      end +    end +  end + +  shared_examples_for "west direction" do +    it "should return true to #west? " do +      expect(direction).to be_west +    end +    context "#to_i" do +      it "should return 6" do +        expect(direction.to_i).to eq(6) +      end +    end +  end + +  context "when instanciating with existing text only ('west' for example)" do +    let(:direction){ Chouette::Direction.new "west"} +    it_should_behave_like "west direction" +  end +  context "when instanciating with existing numerical code only (6 for example)" do +    let(:direction){ Chouette::Direction.new 6} +    it_should_behave_like "west direction" +  end + +  context "when instanciating with 'dummy' and 1 as argumrent" do +    let(:text) { "dummy"} +    let(:number) { 1} +    let(:direction){ Chouette::Direction.new( text, number)} + +    it "should return true to #dummy? " do +      expect(direction.send( "#{text}?".to_sym)).to be_truthy +    end + +    it "should return false to #other-dummy? " do +      expect(direction.send( "other-#{text}?".to_sym)).to be_falsey +    end + +    context "#to_i" do +      it "should return provided number" do +        expect(direction.to_i).to eq(number) +      end +    end + +    context "#name" do +      it "should return provided text" do +        expect(direction.name).to eq(text) +      end +    end +  end +end diff --git a/spec/models/chouette/exporter_spec.rb b/spec/models/chouette/exporter_spec.rb new file mode 100644 index 000000000..8bcd14761 --- /dev/null +++ b/spec/models/chouette/exporter_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe Chouette::Exporter, :type => :model do + +  subject { Chouette::Exporter.new("test") } + +  describe "#export" do + +    let(:chouette_command) { double :run! => true } + +    before(:each) do +      allow(subject).to receive_messages :chouette_command => chouette_command +    end + +    it "should use specified file in -outputFile option" do +      expect(chouette_command).to receive(:run!).with(hash_including(:output_file => File.expand_path('file'))) +      subject.export "file" +    end +     +    it "should use specified format in -format option" do +      expect(chouette_command).to receive(:run!).with(hash_including(:format => 'DUMMY')) +      subject.export "file", :format => "dummy" +    end +     +  end + +end + diff --git a/spec/models/chouette/file_validator_spec.rb b/spec/models/chouette/file_validator_spec.rb new file mode 100644 index 000000000..d9b29fa21 --- /dev/null +++ b/spec/models/chouette/file_validator_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe Chouette::FileValidator, :type => :model do + +  subject { Chouette::FileValidator.new("public") } + +  before(:each) do +    allow(subject).to receive_messages :execute! => true +  end + + +  describe "#validate" do + +    let(:chouette_command) { double :run! => true } + +    before(:each) do +      allow(subject).to receive_messages :chouette_command => chouette_command +    end + +    it "should use specified file in -inputFile option" do +      expect(chouette_command).to receive(:run!).with(hash_including(:input_file => File.expand_path('file'))) +      subject.validate "file" +    end +         +  end + +end + diff --git a/spec/models/chouette/footnote_spec.rb b/spec/models/chouette/footnote_spec.rb new file mode 100644 index 000000000..5c09e3931 --- /dev/null +++ b/spec/models/chouette/footnote_spec.rb @@ -0,0 +1,9 @@ +require 'spec_helper' + +describe Chouette::Footnote do + +  subject { build(:footnote) } + +  it { should validate_presence_of :line } + +end diff --git a/spec/models/chouette/group_of_line_spec.rb b/spec/models/chouette/group_of_line_spec.rb new file mode 100644 index 000000000..66932cfd6 --- /dev/null +++ b/spec/models/chouette/group_of_line_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe Chouette::GroupOfLine, :type => :model do + +  subject { create(:group_of_line) } + +  it { is_expected.to validate_presence_of :name } + +  # it { should validate_presence_of :objectid } +  it { is_expected.to validate_uniqueness_of :objectid } + +  describe "#stop_areas" do +    let!(:line){create(:line, :group_of_lines => [subject])} +    let!(:route){create(:route, :line => line)} +    it "should retreive group of line's stop_areas" do +      expect(subject.stop_areas.count).to eq(route.stop_points.count) +    end +  end +   +  context "#line_tokens=" do +    let!(:line1){create(:line)} +    let!(:line2){create(:line)} + +    it "should return associated line ids" do +      subject.update_attributes :line_tokens => [line1.id, line2.id].join(',') +      expect(subject.lines).to include( line1) +      expect(subject.lines).to include( line2) +    end +  end + +end diff --git a/spec/models/chouette/journey_frequency_spec.rb b/spec/models/chouette/journey_frequency_spec.rb new file mode 100644 index 000000000..2e2088a0c --- /dev/null +++ b/spec/models/chouette/journey_frequency_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' + +describe Chouette::JourneyFrequency, type: :model do +  let!(:vehicle_journey) { create(:vehicle_journey_even)} + +  describe '#create' do +    context 'when valid' do +      it 'should be created' do +        journey_frequency = build(:journey_frequency) +        journey_frequency.vehicle_journey_id = vehicle_journey.id +        expect(journey_frequency.save!).to be +      end +    end + +    context 'when first_departure_time not valid' do +      it 'fails validation with first_departure_time before timeband start_time' do +        journey_frequency = build(:journey_frequency_first_departure_time_invalid) +        journey_frequency.vehicle_journey_id = vehicle_journey.id +        expect(journey_frequency).to be_invalid +      end +    end + +    context 'when last_departure_time not valid' do +      it 'fails validation with last_departure_time after timeband end_time' do +        journey_frequency = build(:journey_frequency_last_departure_time_invalid) +        journey_frequency.vehicle_journey_id = vehicle_journey.id +        expect(journey_frequency).to be_invalid +      end +    end + +    context 'when first and last departure_time not valid' do +      it 'fails validation with first_departure_time equal last_departure_time' do +        journey_frequency = build(:journey_frequency_departure_time_invalid) +        journey_frequency.vehicle_journey_id = vehicle_journey.id +        expect(journey_frequency).to be_invalid +      end +    end + +    context 'when scheduled_headway_interval not valid' do +      it 'fails validation with scheduled_headway_interval is not set' do +        journey_frequency = build(:journey_frequency_scheduled_headway_interval_invalid) +        journey_frequency.vehicle_journey_id = vehicle_journey.id +        expect(journey_frequency).to be_invalid +      end +    end +  end +end diff --git a/spec/models/chouette/journey_pattern_spec.rb b/spec/models/chouette/journey_pattern_spec.rb new file mode 100644 index 000000000..68c221c9a --- /dev/null +++ b/spec/models/chouette/journey_pattern_spec.rb @@ -0,0 +1,64 @@ +require 'spec_helper' + +describe Chouette::JourneyPattern, :type => :model do +  describe "#stop_point_ids" do +    context "for a journey_pattern using only route's stop on odd position" do +      let!(:journey_pattern){ create( :journey_pattern_odd)} +      let!(:vehicle_journey){ create( :vehicle_journey_odd, :journey_pattern => journey_pattern)} +       +      # workaroud +      #subject { journey_pattern} +      subject { Chouette::JourneyPattern.find(vehicle_journey.journey_pattern_id)} + +      context "when a all route's stop have been removed from journey_pattern" do +        before(:each) do +          subject.stop_point_ids = [] +        end +        it "should remove all vehicle_journey_at_stop" do +          vjas_stop_ids = Chouette::VehicleJourney.find(vehicle_journey.id).vehicle_journey_at_stops +          expect(vjas_stop_ids.count).to eq(0) +        end +        it "should keep departure and arrival shortcut up to date to nil" do +          expect(subject.arrival_stop_point_id).to be_nil +          expect(subject.departure_stop_point_id).to be_nil +        end +      end +       +      context "when a route's stop has been removed from journey_pattern" do +        let!(:last_stop_id){ subject.stop_point_ids.last} +        before(:each) do +          subject.stop_point_ids = subject.stop_point_ids - [last_stop_id] +        end +        it "should remove vehicle_journey_at_stop for last stop" do +          vjas_stop_ids = Chouette::VehicleJourney.find(vehicle_journey.id).vehicle_journey_at_stops.map(&:stop_point_id) +          expect(vjas_stop_ids.count).to eq(subject.stop_point_ids.size) +          expect(vjas_stop_ids).not_to include( last_stop_id) +        end +        it "should keep departure and arrival shortcut up to date" do +          ordered = subject.stop_points.sort { |a,b| a.position <=> b.position} + +          expect(subject.arrival_stop_point_id).to eq(ordered.last.id) +          expect(subject.departure_stop_point_id).to eq(ordered.first.id) +        end +      end +       +      context "when a route's stop has been added in journey_pattern" do +        let!(:new_stop){ subject.route.stop_points[1]} +        before(:each) do +          subject.stop_point_ids = subject.stop_point_ids + [new_stop.id] +        end +        it "should add a new vehicle_journey_at_stop for that stop" do +          vjas_stop_ids = Chouette::VehicleJourney.find(vehicle_journey.id).vehicle_journey_at_stops.map(&:stop_point_id) +          expect(vjas_stop_ids.count).to eq(subject.stop_point_ids.size) +          expect(vjas_stop_ids).to include( new_stop.id) +        end +        it "should keep departure and arrival shortcut up to date" do +          ordered = subject.stop_points.sort { |a,b| a.position <=> b.position} + +          expect(subject.arrival_stop_point_id).to eq(ordered.last.id) +          expect(subject.departure_stop_point_id).to eq(ordered.first.id) +        end +      end +    end +  end +end diff --git a/spec/models/chouette/line_spec.rb b/spec/models/chouette/line_spec.rb new file mode 100644 index 000000000..f0a2453b5 --- /dev/null +++ b/spec/models/chouette/line_spec.rb @@ -0,0 +1,120 @@ +require 'spec_helper' + +describe Chouette::Line, :type => :model do + +  subject { create(:line) } + +  it { is_expected.to validate_presence_of :network } +  it { is_expected.to validate_presence_of :company } + +  it { is_expected.to validate_presence_of :name } + +  # it { should validate_presence_of :objectid } +  it { is_expected.to validate_uniqueness_of :objectid } + +  describe '#objectid' do +    subject { super().objectid } +    it { is_expected.to be_kind_of(Chouette::ObjectId) } +  end + +  # it { should validate_numericality_of :objectversion } + +  describe ".last_stop_areas_parents" do + +    it "should return stop areas if no parents" do +      line = create(:line_with_stop_areas) +      expect(line.stop_areas_last_parents).to eq(line.stop_areas) +    end + +    it "should return stop areas parents if parents" do +      line = create(:line_with_stop_areas) +      route = create(:route, :line => line) +      parent = create(:stop_area) +      stop_areas = [ create(:stop_area),  create(:stop_area), create(:stop_area, :parent_id => parent.id) ] +      stop_areas.each do |stop_area| +        create(:stop_point, :stop_area => stop_area, :route => route) +      end + +      expect(line.stop_areas_last_parents).to match(line.stop_areas[0..(line.stop_areas.size - 2)].push(parent)) +    end + +  end + +  describe "#stop_areas" do +    let!(:route){create(:route, :line => subject)} +    it "should retreive route's stop_areas" do +      expect(subject.stop_areas.count).to eq(route.stop_points.count) +    end +  end + +  describe "#transport_mode" do + +    def self.legacy_transport_mode_names +      %w{Air Train LongDistanceTrain LocalTrain RapidTransit Metro Tramway Coach Bus Ferry Waterborne PrivateVehicle Walk Trolleybus Bicycle Shuttle Taxi VAL Other} +    end + +    legacy_transport_mode_names.each do |transport_mode_name| +      context "when transport_mode_name is #{transport_mode_name}" do +        transport_mode = Chouette::TransportMode.new(transport_mode_name.underscore) +        it "should be #{transport_mode}" do +          subject.transport_mode_name = transport_mode_name +          expect(subject.transport_mode).to eq(transport_mode) +        end +      end +    end +    context "when transport_mode_name is nil" do +      it "should be nil" do +        subject.transport_mode_name = nil +        expect(subject.transport_mode).to be_nil +      end +    end + +  end + +  describe "#transport_mode=" do + +    it "should change transport_mode_name with TransportMode#name" do +      subject.transport_mode = "Test" +      expect(subject.transport_mode_name).to eq("Test") +    end + +  end + +  describe ".transport_modes" do + +    it "should not include unknown transport_mode" do +      expect(Chouette::Line.transport_modes).not_to include(Chouette::TransportMode.new("unknown")) +    end + +    it "should not include interchange transport_mode" do +      expect(Chouette::Line.transport_modes).not_to include(Chouette::TransportMode.new("interchange")) +    end + +  end + +  context "#group_of_line_tokens=" do +    let!(:group_of_line1){create(:group_of_line)} +    let!(:group_of_line2){create(:group_of_line)} + +    it "should return associated group_of_line ids" do +      subject.update_attributes :group_of_line_tokens => [group_of_line1.id, group_of_line2.id].join(',') +      expect(subject.group_of_lines).to include( group_of_line1) +      expect(subject.group_of_lines).to include( group_of_line2) +    end +  end + +  describe "#update_attributes footnotes_attributes" do +    context "instanciate 2 footnotes without line" do +      let!( :footnote_first) {build( :footnote, :line_id => nil)} +      let!( :footnote_second) {build( :footnote, :line_id => nil)} +      it "should add 2 footnotes to the line" do +        subject.update_attributes :footnotes_attributes => +          { Time.now.to_i => footnote_first.attributes, +            (Time.now.to_i-5) => footnote_second.attributes} +        expect(Chouette::Line.find( subject.id ).footnotes.size).to eq(2) +      end +    end +  end + + +end diff --git a/spec/models/chouette/loader_spec.rb b/spec/models/chouette/loader_spec.rb new file mode 100644 index 000000000..d359c443e --- /dev/null +++ b/spec/models/chouette/loader_spec.rb @@ -0,0 +1,69 @@ +require 'spec_helper' + +describe Chouette::Loader, :type => :model do + +  subject { Chouette::Loader.new("test") } + +  before(:each) do +    allow(subject).to receive_messages :execute! => true +  end + +  describe "#load_dump" do + +  end + +  describe "#import" do + +    let(:chouette_command) { double :run! => true } + +    before(:each) do +      allow(subject).to receive_messages :chouette_command => chouette_command +    end + +    it "should use specified file in -inputFile option" do +      expect(chouette_command).to receive(:run!).with(hash_including(:input_file => File.expand_path('file'))) +      subject.import "file" +    end +     +    it "should use specified format in -format option" do +      expect(chouette_command).to receive(:run!).with(hash_including(:format => 'DUMMY')) +      subject.import "file", :format => "dummy" +    end +     +  end + +  describe "#create" do +     +    it "should quote schema name" do +      expect(subject).to receive(:execute!).with(/"test"/) +      subject.create +    end + +  end + +  describe "#drop" do + +    it "should quote schema name" do +      expect(subject).to receive(:execute!).with(/"test"/) +      subject.drop +    end +     +  end + +  describe "#backup" do + +    let(:file) { "/dev/null" } + +    it "should call pg_dump" do +      expect(subject).to receive(:execute!).with(/^pg_dump/) +      subject.backup file +    end + +    it "should dump in specified file" do +      expect(subject).to receive(:execute!).with(/-f #{file}/) +      subject.backup file +    end +  end + +end + diff --git a/spec/models/chouette/network_spec.rb b/spec/models/chouette/network_spec.rb new file mode 100644 index 000000000..c9e510e84 --- /dev/null +++ b/spec/models/chouette/network_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe Chouette::Network, :type => :model do + +  subject { create(:network) } + +  it { is_expected.to validate_presence_of :name } + +  # it { should validate_presence_of :objectid } +  it { is_expected.to validate_uniqueness_of :objectid } + +  describe "#stop_areas" do +    let!(:line){create(:line, :network => subject)} +    let!(:route){create(:route, :line => line)} +    it "should retrieve route's stop_areas" do +      expect(subject.stop_areas.count).to eq(route.stop_points.count) +    end +  end +end diff --git a/spec/models/chouette/object_id_spec.rb b/spec/models/chouette/object_id_spec.rb new file mode 100644 index 000000000..dd8b66388 --- /dev/null +++ b/spec/models/chouette/object_id_spec.rb @@ -0,0 +1,149 @@ +require 'spec_helper' + +describe Chouette::ObjectId, :type => :model do + +  def objectid(value = "abc:StopArea:abc123") +    Chouette::ObjectId.new value +  end + +  subject { objectid } + +  context "when invalid" do + +    subject { objectid("abc") } + +    it { is_expected.not_to be_valid } + +    describe '#parts' do +      subject { super().parts } +      it { is_expected.to be_nil } +    end + +    describe '#system_id' do +      subject { super().system_id } +      it { is_expected.to be_nil } +    end + +  end +   +  context "when with spaces in last part" do + +    subject { objectid("abc:Line:Aze toto") } + +    it { is_expected.not_to be_valid } + + +  end +   +  context "when with spaces in first part" do + +    subject { objectid("ae abc:Line:Aze") } + +    it { is_expected.not_to be_valid } + + +  end +   +  context "when with spaces in middle part" do + +    subject { objectid("aeabc:Li ne:Aze") } + +    it { is_expected.not_to be_valid } +     + +  end +   +  context "when invalid in first part" do + +    subject { objectid("Abc_+19:Line:Abc") } + +    it { is_expected.not_to be_valid } +  end +   +  context "when invalid in middle part" do + +    subject { objectid("Abc_19:Li56ne:Abc") } + +    it { is_expected.not_to be_valid } +  end + +  context "when invalid in last part" do + +    subject { objectid("Abc_19:Line:Ab+c") } + +    it { is_expected.not_to be_valid } +  end +  context "when valid" do + +    subject { objectid("Abc_19:Line:Abc_12-") } + +    it { is_expected.to be_valid } +  end + +  describe "#parts" do + +    it "should be the 3 parts of the ObjectId" do +      expect(objectid("abc:StopArea:abc123").parts).to eq(%w{abc StopArea abc123}) +    end + +  end + +  describe "#system_id" do +     +    it "should be the first ObjectId parts" do +      expect(objectid("first:second:third").system_id).to eq("first") +    end + +  end + +  describe "#object_type" do +     +    it "should be the second ObjectId parts" do +      expect(objectid("first:second:third").object_type).to eq("second") +    end + +  end + +  describe "#local_id" do +     +    it "should be the third ObjectId parts" do +      expect(objectid("first:second:third").local_id).to eq("third") +    end + +  end + +  it "should be valid when parts are found" do +    allow(subject).to receive_messages :parts => "dummy" +    expect(subject).to be_valid +  end + +  describe ".create" do + +    let(:given_system_id) { "systemId" } +    let(:given_object_type) { "objectType" } +    let(:given_local_id) { "localId" } + +    subject { Chouette::ObjectId.create(given_system_id, given_object_type, given_local_id) } + +    it "should return ObjectId attributes" do +      expect(subject.send(:system_id)).to eq(given_system_id) +      expect(subject.send(:object_type)).to eq(given_object_type) +      expect(subject.send(:local_id)).to eq(given_local_id) +    end + +  end + +  describe ".new" do +     +    it "should return an existing ObjectId" do +      expect(Chouette::ObjectId.new(objectid)).to eq(objectid) +    end + +    it "should create an empty ObjectId with nil" do +      expect(Chouette::ObjectId.new(nil)).to be_empty +    end + +  end + + +end diff --git a/spec/models/chouette/route_section_spec.rb b/spec/models/chouette/route_section_spec.rb new file mode 100644 index 000000000..f064d38ea --- /dev/null +++ b/spec/models/chouette/route_section_spec.rb @@ -0,0 +1,93 @@ +# require 'spec_helper' +# +# RSpec.describe Chouette::RouteSection, :type => :model do +# +#   subject { create :route_section } +# +#   it { should validate_presence_of(:departure) } +#   it { should validate_presence_of(:arrival) } +# +#   describe "#default_geometry" do +# +#     it "should return nil when departure isn't defined" do +#       subject.departure  = nil +#       expect(subject.default_geometry).to be_nil +#     end +# +#     it "should return nil when arrival isn't defined" do +#       subject.arrival  = nil +#       expect(subject.default_geometry).to be_nil +#     end +#  +#     it "should return nil when departure has no geometry" do +#       subject.departure.stub :geometry +#       expect(subject.default_geometry).to be_nil +#     end +# +#     it "should return nil when arrival has no geometry" do +#       subject.arrival.stub :geometry +#       expect(subject.default_geometry).to be_nil +#     end +# +#     it "should use departure geometry as first point" do +#       expect(subject.default_geometry.first).to eq(subject.departure.geometry) +#     end +# +#   end +# +#   describe "#process_geometry" do +# +#     let(:sample_geometry) { line_string("0 0,1 1").to_rgeo } +# +#     context "without processor" do +# +#       it "should use the input geometry" do +#         subject.input_geometry = sample_geometry +#         subject.process_geometry +#         expect(subject.processed_geometry).to eq(subject.input_geometry) +#       end +# +#       it "should use the default geometry when no input is defined" do +#         subject.input_geometry = nil +#         subject.process_geometry +#         expect(subject.processed_geometry).to eq(subject.default_geometry.to_rgeo) +#       end +# +#     end +# +#     # context "with a processor" do +#     # +#     #   it "should use the processor result" do +#     #     subject.processor = Proc.new { |s| sample_geometry } +#     #     subject.process_geometry +#     #     subject.processor = nil +#     #     expect(subject.processed_geometry).to eq(sample_geometry) +#     #   end +#     # end +#   end +# +#   describe "#distance" do +# +#     context "with simple line" do +#       let(:sample_geometry) { line_string("2.329534 48.842397,2.325725 48.855839").to_rgeo } +#       it "should return the right distance" do +#         subject.input_geometry = sample_geometry +#         subject.process_geometry +#         expect(subject.distance).to eq(sample_geometry.to_georuby.spherical_distance) +#       end +#     end +# +#     context "with complex line" do +#       let(:sample_geometry) { line_string("2.329561 48.842397, 2.329351 48.843119, 2.329152 48.843801, 2.3289820000000003 48.844426,2.3287960000000005 48.845059,2.3286540000000007 48.845575,2.3283130000000005 48.846748,2.3281220000000005 48.847404999999995,2.3279330000000003 48.848088,2.3278860000000003 48.848245999999996,2.3273240000000004 48.850142999999996,2.3273030000000006 48.850218999999996,2.3271630000000005 48.850745999999994,2.3270140000000006 48.85130999999999,2.3269350000000006 48.85142799999999,2.3268640000000005 48.85153599999999,2.3268290000000005 48.85161099999999,2.3267490000000004 48.85180999999999,2.3267700000000002 48.852053999999995,2.326759 48.852216999999996,2.326687 48.852427999999996,2.3266620000000002 48.852512,2.3264280000000004 48.853286,2.3264050000000003 48.853362,2.3263710000000004 48.853483,2.326125 48.854343,2.3259980000000002 48.854727,2.325737 48.855833999999994").to_rgeo } +#       it "should return the right distance" do +#         subject.input_geometry = sample_geometry +#         subject.process_geometry +#         expect(subject.distance).to eq(sample_geometry.to_georuby.spherical_distance) +#       end +#     end +# +#   end +# +# +# +# end diff --git a/spec/models/chouette/route_spec.rb b/spec/models/chouette/route_spec.rb new file mode 100644 index 000000000..1acc5a0f7 --- /dev/null +++ b/spec/models/chouette/route_spec.rb @@ -0,0 +1,238 @@ +require 'spec_helper' + +describe Chouette::Route, :type => :model do +  subject { create(:route) } + +  it { is_expected.to validate_uniqueness_of :objectid } + +  describe '#objectid' do +    subject { super().objectid } +    it { is_expected.to be_kind_of(Chouette::ObjectId) } +  end + +  #it { is_expected.to validate_presence_of :name } +  it { is_expected.to validate_presence_of :line } +  #it { is_expected.to validate_presence_of :wayback_code } +  #it { is_expected.to validate_presence_of :direction_code } + +  context "reordering methods" do +    let( :bad_stop_point_ids){subject.stop_points.map { |sp| sp.id + 1}} +    let( :ident){subject.stop_points.map(&:id)} +    let( :first_last_swap){ [ident.last] + ident[1..-2] + [ident.first]} + +    describe "#reorder!" do +      context "invalid stop_point_ids" do +        let( :new_stop_point_ids) { bad_stop_point_ids} +        it { expect(subject.reorder!( new_stop_point_ids)).to be_falsey} +      end + +      context "swaped last and first stop_point_ids" do +        let!( :new_stop_point_ids) { first_last_swap} +        let!( :old_stop_point_ids) { subject.stop_points.map(&:id) } +        let!( :old_stop_area_ids) { subject.stop_areas.map(&:id) } + +        it "should keep stop_point_ids order unchanged" do +          expect(subject.reorder!( new_stop_point_ids)).to be_truthy +          expect(subject.stop_points.map(&:id)).to eq( old_stop_point_ids) +        end +        it "should have changed stop_area_ids order" do +          expect(subject.reorder!( new_stop_point_ids)).to be_truthy +          subject.reload +          expect(subject.stop_areas.map(&:id)).to eq( [old_stop_area_ids.last] + old_stop_area_ids[1..-2] + [old_stop_area_ids.first]) +        end +      end +    end + +    describe "#stop_point_permutation?" do +      context "invalid stop_point_ids" do +        let( :new_stop_point_ids) { bad_stop_point_ids} +        it { is_expected.not_to be_stop_point_permutation( new_stop_point_ids)} +      end +      context "unchanged stop_point_ids" do +        let( :new_stop_point_ids) { ident} +        it { is_expected.to be_stop_point_permutation( new_stop_point_ids)} +      end +      context "swaped last and first stop_point_ids" do +        let( :new_stop_point_ids) { first_last_swap} +        it { is_expected.to be_stop_point_permutation( new_stop_point_ids)} +      end +    end +  end + +  describe "#stop_points_attributes=" do +      let( :journey_pattern) { create( :journey_pattern, :route => subject )} +      let( :vehicle_journey) { create( :vehicle_journey, :journey_pattern => journey_pattern)} +      def subject_stop_points_attributes +          {}.tap do |hash| +              subject.stop_points.each_with_index { |sp,index| hash[ index.to_s ] = sp.attributes } +          end +      end +      context "route having swapped a new stop" do +          let( :new_stop_point ){build( :stop_point, :route => subject)} +          def added_stop_hash +            subject_stop_points_attributes.tap do |h| +                h["4"] = new_stop_point.attributes.merge( "position" => "4", "_destroy" => "" ) +            end +          end +          let!( :new_route_size ){ subject.stop_points.size+1 } + +          it "should have added stop_point in route" do +              subject.update_attributes( :stop_points_attributes => added_stop_hash) +              expect(Chouette::Route.find( subject.id ).stop_points.size).to eq(new_route_size) +          end +          it "should have added stop_point in route's journey pattern" do +              subject.update_attributes( :stop_points_attributes => added_stop_hash) +              expect(Chouette::JourneyPattern.find( journey_pattern.id ).stop_points.size).to eq(new_route_size) +          end +          it "should have added stop_point in route's vehicle journey at stop" do +              subject.update_attributes( :stop_points_attributes => added_stop_hash) +              expect(Chouette::VehicleJourney.find( vehicle_journey.id ).vehicle_journey_at_stops.size).to eq(new_route_size) +          end +      end +      context "route having swapped stop" do +          def swapped_stop_hash +            subject_stop_points_attributes.tap do |h| +                h[ "1" ][ "position" ] = "3" +                h[ "3" ][ "position" ] = "1" +            end +          end +          let!( :new_stop_id_list ){ subject.stop_points.map(&:id).tap {|array| array.insert( 1, array.delete_at(3)); array.insert( 3, array.delete_at(2) )} } + +          it "should have swap stop_points from route" do +              subject.update_attributes( :stop_points_attributes => swapped_stop_hash) +              expect(Chouette::Route.find( subject.id ).stop_points.map(&:id)).to eq(new_stop_id_list) +          end +          it "should have swap stop_points from route's journey pattern" do +              subject.update_attributes( :stop_points_attributes => swapped_stop_hash) +              expect(Chouette::JourneyPattern.find( journey_pattern.id ).stop_points.map(&:id)).to eq(new_stop_id_list) +          end +          it "should have swap stop_points from route's vehicle journey at stop" do +              subject.update_attributes( :stop_points_attributes => swapped_stop_hash) +              expect(Chouette::VehicleJourney.find( vehicle_journey.id ).vehicle_journey_at_stops.map(&:stop_point_id)).to match_array(new_stop_id_list) +          end +      end +      context "route having a deleted stop" do +          def removed_stop_hash +            subject_stop_points_attributes.tap do |h| +                h[ "1" ][ "_destroy" ] = "1" +            end +          end +          let!( :new_stop_id_list ){ subject.stop_points.map(&:id).tap {|array| array.delete_at(1) } } + +          it "should ignore deleted stop_point from route" do +              subject.update_attributes( :stop_points_attributes => removed_stop_hash) +              expect(Chouette::Route.find( subject.id ).stop_points.map(&:id)).to eq(new_stop_id_list) +          end +          it "should ignore deleted stop_point from route's journey pattern" do +              subject.update_attributes( :stop_points_attributes => removed_stop_hash) +              expect(Chouette::JourneyPattern.find( journey_pattern.id ).stop_points.map(&:id)).to eq(new_stop_id_list) +          end +          it "should ignore deleted stop_point from route's vehicle journey at stop" do +              subject.update_attributes( :stop_points_attributes => removed_stop_hash) +              expect(Chouette::VehicleJourney.find( vehicle_journey.id ).vehicle_journey_at_stops.map(&:stop_point_id)).to match_array(new_stop_id_list) +          end +      end +  end + +  describe "#stop_points" do +    context "#find_by_stop_area" do +      context "when arg is first quay id" do +        let(:first_stop_point) { subject.stop_points.first} +        it "should return first quay" do +          expect(subject.stop_points.find_by_stop_area( first_stop_point.stop_area_id)).to eq( first_stop_point) +        end +      end +    end +  end +  describe "#stop_areas" do +    let(:line){ create(:line)} +    let(:route_1){ create(:route, :line => line)} +    let(:route_2){ create(:route, :line => line)} +    it "should retreive all stop_area on route" do +      route_1.stop_areas.each do |sa| +        expect(sa.stop_points.map(&:route_id).uniq).to eq([route_1.id]) +      end +    end + +    context "when route is looping: last and first stop area are the same" do +      it "should retreive same stop_area one last and first position" do +        route_loop = create(:route, :line => line) +        first_stop = Chouette::StopPoint.where( :route_id => route_loop.id, :position => 0).first +        last_stop = create(:stop_point, :route => route_loop, :position => 4, :stop_area => first_stop.stop_area) + +        expect(route_loop.stop_areas.size).to eq(6) +        expect(route_loop.stop_areas.select {|s| s.id == first_stop.stop_area.id}.size).to eq(2) +      end +    end +  end + +  describe "#direction_code" do +    def self.legacy_directions +      %w{A R ClockWise CounterClockWise North NorthWest West SouthWest +        South SouthEast East NorthEast} +    end +    legacy_directions.each do |direction| +      context "when direction is #{direction}" do +        direction_code = Chouette::Direction.new( Chouette::Route.direction_binding[ direction]) +        it "should be #{direction_code}" do +          subject.direction = direction +          expect(subject.direction_code).to eq(direction_code) +        end +      end +    end +    context "when direction is nil" do +      it "should be nil" do +        subject.direction = nil +        expect(subject.direction_code).to be_nil +      end +    end +  end +  describe "#direction_code=" do +    context "when unknown direction is provided" do +      it "should change direction to nil" do +        subject.direction_code = "dummy" +        expect(subject.direction).to be_nil +      end +    end +    context "when an existing direction (west) is provided" do +      it "should change direction Direction.west" do +        subject.direction_code = "west" +        expect(subject.direction).to eq("West") +      end +    end +  end +  describe "#wayback_code" do +    def self.legacy_waybacks +      %w{A R} +    end +    legacy_waybacks.each do |wayback| +      context "when wayback is #{wayback}" do +        wayback_code = Chouette::Wayback.new( Chouette::Route.wayback_binding[ wayback]) +        it "should be #{wayback_code}" do +          subject.wayback = wayback +          expect(subject.wayback_code).to eq(wayback_code) +        end +      end +    end +    context "when wayback is nil" do +      it "should be nil" do +        subject.wayback = nil +        expect(subject.wayback_code).to be_nil +      end +    end +  end +  describe "#wayback_code=" do +    context "when unknown wayback is provided" do +      it "should change wayback to nil" do +        subject.wayback_code = "dummy" +        expect(subject.wayback).to be_nil +      end +    end +    context "when an existing wayback (straight_forward) is provided" do +      it "should change wayback Wayback.straight_forward" do +        subject.wayback_code = "straight_forward" +        expect(subject.wayback).to eq("A") +      end +    end +  end +end diff --git a/spec/models/chouette/stop_area_spec.rb b/spec/models/chouette/stop_area_spec.rb new file mode 100644 index 000000000..b237f08ef --- /dev/null +++ b/spec/models/chouette/stop_area_spec.rb @@ -0,0 +1,443 @@ +require 'spec_helper' + +describe Chouette::StopArea, :type => :model do +  let!(:quay) { create :stop_area, :area_type => "Quay" } +  let!(:boarding_position) { create :stop_area, :area_type => "BoardingPosition" } +  let!(:commercial_stop_point) { create :stop_area, :area_type => "CommercialStopPoint" } +  let!(:stop_place) { create :stop_area, :area_type => "StopPlace" } +  let!(:itl) { create :stop_area, :area_type => "ITL" } + +  describe '#objectid' do +    subject { super().objectid } +    it { is_expected.to be_kind_of(Chouette::ObjectId) } +  end + +  it { is_expected.to validate_presence_of :name } +  it { is_expected.to validate_presence_of :area_type } +  it { is_expected.to validate_numericality_of :latitude } +  it { is_expected.to validate_numericality_of :longitude } + +   +  describe ".latitude" do +    it "should accept -90 value" do +      subject = create :stop_area, :area_type => "BoardingPosition" +      subject.latitude = -90 +      expect(subject.valid?).to be_truthy +    end +    it "should reject < -90 value" do +      subject = create :stop_area, :area_type => "BoardingPosition" +      subject.latitude = -90.0001 +      expect(subject.valid?).to be_falsey +    end +    it "should accept 90 value" do +      subject = create :stop_area, :area_type => "BoardingPosition" +      subject.latitude = 90 +      expect(subject.valid?).to be_truthy +    end +    it "should reject > 90 value" do +      subject = create :stop_area, :area_type => "BoardingPosition" +      subject.latitude = 90.0001 +      expect(subject.valid?).to be_falsey +    end +  end + +  describe ".longitude" do +    it "should accept -180 value" do +      subject = create :stop_area, :area_type => "BoardingPosition" +      subject.longitude = -180 +      expect(subject.valid?).to be_truthy +    end +    it "should reject < -180 value" do +      subject = create :stop_area, :area_type => "BoardingPosition" +      subject.longitude = -180.0001 +      expect(subject.valid?).to be_falsey +    end +    it "should accept 180 value" do +      subject = create :stop_area, :area_type => "BoardingPosition" +      subject.longitude = 180 +      expect(subject.valid?).to be_truthy +    end +    it "should reject > 180 value" do +      subject = create :stop_area, :area_type => "BoardingPosition" +      subject.longitude = 180.0001 +      expect(subject.valid?).to be_falsey +    end +  end + +  describe ".long_lat" do +    it "should accept longitude and latitude both as nil" do +      subject = create :stop_area, :area_type => "BoardingPosition" +      subject.longitude = nil +      subject.latitude = nil +      expect(subject.valid?).to be_truthy +    end +    it "should accept longitude and latitude both numerical" do +      subject = create :stop_area, :area_type => "BoardingPosition" +      subject.longitude = 10 +      subject.latitude = 10 +      expect(subject.valid?).to be_truthy +    end +    it "should reject longitude nil with latitude numerical" do +      subject = create :stop_area, :area_type => "BoardingPosition" +      subject.longitude = nil +      subject.latitude = 10 +      expect(subject.valid?).to be_falsey +    end +    it "should reject longitude numerical with latitude nil" do +      subject = create :stop_area, :area_type => "BoardingPosition" +      subject.longitude = 10 +      subject.latitude = nil +      expect(subject.valid?).to be_falsey +    end +  end  +   + +  describe ".children_in_depth" do +    it "should return all the deepest children from stop area" do +      subject = create :stop_area, :area_type => "StopPlace" +      commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint", :parent => subject  +      commercial_stop_point2 = create :stop_area, :area_type => "CommercialStopPoint", :parent => commercial_stop_point +      quay = create :stop_area, :parent => commercial_stop_point, :area_type => "Quay" +      expect(subject.children_in_depth).to match_array([commercial_stop_point, commercial_stop_point2, quay]) +    end +    it "should return only the deepest children from stop area" do +      subject = create :stop_area, :area_type => "StopPlace" +      commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint", :parent => subject  +      commercial_stop_point2 = create :stop_area, :area_type => "CommercialStopPoint", :parent => commercial_stop_point +      quay = create :stop_area, :parent => commercial_stop_point, :area_type => "Quay" +      expect(subject.children_at_base).to match_array([quay]) +    end +  end + +  describe ".stop_area_type" do +    it "should have area_type of BoardingPosition when stop_area_type is set to boarding_position" do +      subject = create :stop_area, :stop_area_type => "boarding_position" +      expect(subject.area_type).to eq("BoardingPosition") +    end +    it "should have area_type of Quay when stop_area_type is set to quay" do +      subject = create :stop_area, :stop_area_type => "quay" +      expect(subject.area_type).to eq("Quay") +    end +    it "should have area_type of CommercialStopPoint when stop_area_type is set to commercial_stop_point" do +      subject = create :stop_area, :stop_area_type => "commercial_stop_point" +      expect(subject.area_type).to eq("CommercialStopPoint") +    end +    it "should have area_type of StopPlace when stop_area_type is set to stop_place" do +      subject = create :stop_area, :stop_area_type => "stop_place" +      expect(subject.area_type).to eq("StopPlace") +    end +    it "should have area_type of ITL when stop_area_type is set to itl" do +      subject = create :stop_area, :stop_area_type => "itl" +      expect(subject.area_type).to eq("ITL") +    end +  end + +  describe ".parent" do +    it "should check if parent method exists" do +      subject = create :stop_area, :parent_id => commercial_stop_point.id +      expect(subject.parent).to eq(commercial_stop_point) +    end +  end + +  describe ".possible_children" do     +     +    it "should find no possible descendant for stop area type quay" do +      subject = create :stop_area, :area_type => "Quay" +      expect(subject.possible_children).to eq([])  +    end + +    it "should find no possible descendant for stop area type boarding position" do +      subject = create :stop_area, :area_type => "BoardingPosition" +      expect(subject.possible_children).to eq([])  +    end + +    it "should find descendant of type quay or boarding position for stop area type commercial stop point" do +      subject = create :stop_area, :area_type => "CommercialStopPoint" +      expect(subject.possible_children).to match_array([quay, boarding_position])  +    end + +    it "should find no children of type stop place or commercial stop point for stop area type stop place" do +      subject = create :stop_area, :area_type => "StopPlace" +      expect(subject.possible_children).to match_array([stop_place, commercial_stop_point])  +    end + +    it "should find no children of type ITL for stop area type ITL" do +      subject = create :stop_area, :area_type => "ITL" +      expect(subject.possible_children).to match_array([stop_place, commercial_stop_point, quay, boarding_position])  +    end + +  end + +  describe ".possible_parents" do + +    it "should find parent type commercial stop point for stop area type boarding position" do +      subject = create :stop_area, :area_type => "BoardingPosition" +      expect(subject.possible_parents).to eq([commercial_stop_point])  +    end + +    it "should find parent type commercial stop point for stop area type quay" do +      subject = create :stop_area, :area_type => "Quay" +      expect(subject.possible_parents).to eq([commercial_stop_point])  +    end     + +    it "should find parent type stop place for stop area type commercial stop point" do +      subject = create :stop_area, :area_type => "CommercialStopPoint" +      expect(subject.possible_parents).to eq([stop_place])  +    end     + +    it "should find parent type stop place for stop area type stop place" do +      subject = create :stop_area, :area_type => "StopPlace" +      expect(subject.possible_parents).to eq([stop_place])  +    end     + +  end + + +  describe ".near" do + +    let(:stop_area) { create :stop_area, :latitude => 1, :longitude => 1 } +    let(:stop_area2) { create :stop_area, :latitude => 1, :longitude => 1 } +     +    it "should find a StopArea at 300m from given origin" do +      expect(Chouette::StopArea.near(stop_area.to_lat_lng.endpoint(0, 0.250, :units => :kms))).to eq([stop_area]) +    end + +    it "should not find a StopArea at more than 300m from given origin" do +      expect(Chouette::StopArea.near(stop_area2.to_lat_lng.endpoint(0, 0.350, :units => :kms))).to be_empty +    end + +  end + +  describe "#to_lat_lng" do +     +    it "should return nil if latitude is nil" do +      subject.latitude = nil +      expect(subject.to_lat_lng).to be_nil +    end + +    it "should return nil if longitude is nil" do +      subject.longitude = nil +      expect(subject.to_lat_lng).to be_nil +    end + +  end + +  describe "#geometry" do +     +    it "should be nil when to_lat_lng is nil" do +      allow(subject).to receive_messages :to_lat_lng => nil +      expect(subject.geometry).to be_nil +    end + +  end + +  describe ".bounds" do +     +    it "should return transform coordinates in floats" do +      allow(Chouette::StopArea.connection).to receive_messages :select_rows => [["113.5292500000000000", "22.1127580000000000", "113.5819330000000000", "22.2157050000000000"]] +      expect(GeoRuby::SimpleFeatures::Envelope).to receive(:from_coordinates).with([[113.5292500000000000, 22.1127580000000000], [113.5819330000000000, 22.2157050000000000]]) +      Chouette::StopArea.bounds +    end + +  end + +  describe "#default_position" do +     +    it "should return nil when StopArea.bounds is nil" do +      allow(Chouette::StopArea).to receive_messages :bounds => nil +      expect(subject.default_position).to be_nil +    end + +    it "should return StopArea.bounds center" do +      allow(Chouette::StopArea).to receive_messages :bounds => double(:center => "center") +      expect(subject.default_position).to eq(Chouette::StopArea.bounds.center) +    end + +  end + +  describe "#children_at_base" do +    it "should have 2 children_at_base" do +      subject = create :stop_area, :area_type => "StopPlace"  +      commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint" ,:parent => subject +      quay1 = create :stop_area, :parent => commercial_stop_point, :area_type => "Quay" +      quay2 = create :stop_area, :parent => commercial_stop_point, :area_type => "Quay" +      expect(subject.children_at_base.size).to eq(2) +    end +   end  + + +  describe "#generic_access_link_matrix" do +    it "should have no access_links in matrix with no access_point" do +      subject = create :stop_area, :area_type => "StopPlace"  +      commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint" ,:parent => subject +      expect(subject.generic_access_link_matrix.size).to eq(0) +    end +    it "should have 4 generic_access_links in matrix with 2 access_points" do +      subject = create :stop_area, :area_type => "StopPlace"  +      commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint" ,:parent => subject +      access_point1 = create :access_point, :stop_area => subject +      access_point2 = create :access_point, :stop_area => subject +      expect(subject.generic_access_link_matrix.size).to eq(4) +    end +   end  +  describe "#detail_access_link_matrix" do +    it "should have no access_links in matrix with no access_point" do +      subject = create :stop_area, :area_type => "StopPlace"  +      commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint" ,:parent => subject +      quay1 = create :stop_area, :parent => commercial_stop_point, :area_type => "Quay" +      quay2 = create :stop_area, :parent => commercial_stop_point, :area_type => "Quay" +      expect(subject.detail_access_link_matrix.size).to eq(0) +    end +    it "should have 8 detail_access_links in matrix with 2 children_at_base and 2 access_points" do +      subject = create :stop_area, :area_type => "StopPlace"  +      commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint" ,:parent => subject +      quay1 = create :stop_area, :parent => commercial_stop_point, :area_type => "Quay" +      quay2 = create :stop_area, :parent => commercial_stop_point, :area_type => "Quay" +      access_point1 = create :access_point, :stop_area => subject +      access_point2 = create :access_point, :stop_area => subject +      expect(subject.detail_access_link_matrix.size).to eq(8) +    end +   end  +  describe "#parents" do +    it "should return parent hireachy list" do +      stop_place = create :stop_area, :area_type => "StopPlace"  +      commercial_stop_point = create :stop_area, :area_type => "CommercialStopPoint" ,:parent => stop_place +      subject = create :stop_area, :parent => commercial_stop_point, :area_type => "Quay" +      expect(subject.parents.size).to eq(2) +    end +    it "should return empty parent hireachy list" do +      subject = create :stop_area, :area_type => "Quay" +      expect(subject.parents.size).to eq(0) +    end +  end +   +  describe "#clean_invalid_access_links" do +    it "should remove invalid access links" do +      # subject is a CSP with a SP as parent, a quay as child +      # 2 access_points of SP have access_link, one on subject, one on subject child +      # when detaching subject from SP, both access_links must be deleted +      stop_place = create :stop_area, :area_type => "StopPlace"  +      subject = create :stop_area, :area_type => "CommercialStopPoint" ,:parent => stop_place +      access_point1 = create :access_point, :stop_area => stop_place +      access_point2 = create :access_point, :stop_area => stop_place +      quay = create :stop_area, :parent => subject, :area_type => "Quay" +      access_link1 = create :access_link, :stop_area => subject, :access_point => access_point1 +      access_link2 = create :access_link, :stop_area => quay, :access_point => access_point2 +      subject.save  +      expect(subject.access_links.size).to eq(1) +      expect(quay.access_links.size).to eq(1) +      subject.parent=nil +      subject.save  +      subject.reload +      expect(subject.access_links.size).to eq(0) +      expect(quay.access_links.size).to eq(0) +    end +    it "should not remove still valid access links" do +      # subject is a Q of CSP with a SP as parent +      # 2 access_points, one of SP, one of CSP have access_link on subject +      # when changing subject CSP to another CSP of same SP +      # one access_links must be kept +      stop_place = create :stop_area, :area_type => "StopPlace"  +      commercial_stop_point1 = create :stop_area, :area_type => "CommercialStopPoint" ,:parent => stop_place +      commercial_stop_point2 = create :stop_area, :area_type => "CommercialStopPoint" ,:parent => stop_place +      access_point1 = create :access_point, :stop_area => stop_place +      access_point2 = create :access_point, :stop_area => commercial_stop_point1 +      subject = create :stop_area, :parent => commercial_stop_point1, :area_type => "Quay" +      access_link1 = create :access_link, :stop_area => subject, :access_point => access_point1 +      access_link2 = create :access_link, :stop_area => subject, :access_point => access_point2 +      subject.save  +      expect(subject.access_links.size).to eq(2) +      subject.parent=commercial_stop_point2 +      subject.save  +      subject.reload +      expect(subject.access_links.size).to eq(1) +    end +  end +   +  describe "#coordinates" do +    it "should convert coordinates into latitude/longitude" do +     subject = create :stop_area, :area_type => "BoardingPosition", :coordinates => "45.123,120.456" +     expect(subject.longitude).to be_within(0.001).of(120.456) +     expect(subject.latitude).to be_within(0.001).of(45.123) +   end +    it "should set empty coordinates into nil latitude/longitude" do +     subject = create :stop_area, :area_type => "BoardingPosition", :coordinates => "45.123,120.456" +     expect(subject.longitude).to be_within(0.001).of(120.456) +     expect(subject.latitude).to be_within(0.001).of(45.123) +     subject.coordinates = "" +     subject.save +     expect(subject.longitude).to be_nil +     expect(subject.latitude).to be_nil +   end +    it "should convert latitude/longitude into coordinates" do +     subject = create :stop_area, :area_type => "BoardingPosition", :longitude => 120.456, :latitude => 45.123 +     expect(subject.coordinates).to eq("45.123,120.456") +   end +    it "should convert nil latitude/longitude into empty coordinates" do +     subject = create :stop_area, :area_type => "BoardingPosition", :longitude => nil, :latitude => nil +     expect(subject.coordinates).to eq("") +   end +    it "should accept valid coordinates" do +     subject = create :stop_area, :area_type => "BoardingPosition", :coordinates => "45.123,120.456" +     expect(subject.valid?).to be_truthy +     subject.coordinates = "45.123, 120.456" +     expect(subject.valid?).to be_truthy +     expect(subject.longitude).to be_within(0.001).of(120.456) +     expect(subject.latitude).to be_within(0.001).of(45.123) +     subject.coordinates = "45.123,  -120.456" +     expect(subject.valid?).to be_truthy +     subject.coordinates = "45.123 ,120.456" +     expect(subject.valid?).to be_truthy +     subject.coordinates = "45.123   ,   120.456" +     expect(subject.valid?).to be_truthy +     subject.coordinates = " 45.123,120.456" +     expect(subject.valid?).to be_truthy +     subject.coordinates = "45.123,120.456  " +     expect(subject.valid?).to be_truthy +    end +    it "should accept valid coordinates on limits" do +     subject = create :stop_area, :area_type => "BoardingPosition", :coordinates => "90,180" +     expect(subject.valid?).to be_truthy +     subject.coordinates = "-90,-180" +     expect(subject.valid?).to be_truthy +     subject.coordinates = "-90.,180." +     expect(subject.valid?).to be_truthy +     subject.coordinates = "-90.0,180.00" +     expect(subject.valid?).to be_truthy +    end +    it "should reject invalid coordinates" do +     subject = create :stop_area, :area_type => "BoardingPosition" +     subject.coordinates = ",12" +     expect(subject.valid?).to be_falsey +     subject.coordinates = "-90" +     expect(subject.valid?).to be_falsey +     subject.coordinates = "-90.1,180." +     expect(subject.valid?).to be_falsey +     subject.coordinates = "-90.0,180.1" +     expect(subject.valid?).to be_falsey +     subject.coordinates = "-91.0,18.1" +     expect(subject.valid?).to be_falsey +    end +  end + +  describe "#duplicate" do +      it "should be a copy of" do +        stop_place = create :stop_area, :area_type => "StopPlace"  +        subject = create :stop_area, :area_type => "CommercialStopPoint" ,:parent => stop_place, :coordinates => "45.123,120.456" +        access_point1 = create :access_point, :stop_area => subject +        access_point2 = create :access_point, :stop_area => subject +        quay1 = create :stop_area, :parent => subject, :area_type => "Quay" +        target=subject.duplicate +        expect(target.id).to be_nil +        expect(target.name).to eq(I18n.t("activerecord.copy", name: subject.name)) +        expect(target.objectid).to eq(subject.objectid+"_1") +        expect(target.area_type).to eq(subject.area_type) +        expect(target.parent).to be_nil +        expect(target.children.size).to eq(0) +        expect(target.access_points.size).to eq(0) +        expect(target.coordinates).to eq("45.123,120.456") +      end +  end + + +end diff --git a/spec/models/chouette/stop_point_spec.rb b/spec/models/chouette/stop_point_spec.rb new file mode 100644 index 000000000..212c32e1a --- /dev/null +++ b/spec/models/chouette/stop_point_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +describe Chouette::StopPoint, :type => :model do +  let!(:vehicle_journey) { create(:vehicle_journey)} +  subject { Chouette::Route.find( vehicle_journey.route_id).stop_points.first } + +  it { is_expected.to validate_uniqueness_of :objectid } +  it { is_expected.to validate_presence_of :stop_area } + +  describe '#objectid' do +    subject { super().objectid } +    it { is_expected.to be_kind_of(Chouette::ObjectId) } +  end + +  describe "#destroy" do +    before(:each) do +      @vehicle = create(:vehicle_journey) +      @stop_point = Chouette::Route.find( @vehicle.route_id).stop_points.first +    end +    def vjas_stop_point_ids( vehicle_id) +      Chouette::VehicleJourney.find( vehicle_id).vehicle_journey_at_stops.map(&:stop_point_id) +    end +    def jpsp_stop_point_ids( journey_id) +      Chouette::JourneyPattern.find( journey_id).stop_points.map(&:id) +    end +    it "should remove dependent vehicle_journey_at_stop" do +      expect(vjas_stop_point_ids(@vehicle.id)).to include(@stop_point.id) + +      @stop_point.destroy + +      expect(vjas_stop_point_ids(@vehicle.id)).not_to include(@stop_point.id) +    end +    it "should remove dependent journey_pattern_stop_point" do +      expect(jpsp_stop_point_ids(@vehicle.journey_pattern_id)).to include(@stop_point.id) + +      @stop_point.destroy + +      expect(jpsp_stop_point_ids(@vehicle.journey_pattern_id)).not_to include(@stop_point.id) +    end +  end +end diff --git a/spec/models/chouette/time_table_period_spec.rb b/spec/models/chouette/time_table_period_spec.rb new file mode 100644 index 000000000..07dc602cb --- /dev/null +++ b/spec/models/chouette/time_table_period_spec.rb @@ -0,0 +1,66 @@ +require 'spec_helper' + +describe Chouette::TimeTablePeriod, :type => :model do + +  let!(:time_table) { create(:time_table)} +  subject { create(:time_table_period ,:time_table => time_table, :period_start => Date.new(2014,6,30), :period_end => Date.new(2014,7,6) ) } +  let!(:p2) {create(:time_table_period ,:time_table => time_table, :period_start => Date.new(2014,7,6), :period_end => Date.new(2014,7,14) ) }  + +  it { is_expected.to validate_presence_of :period_start } +  it { is_expected.to validate_presence_of :period_end } +   +  describe "#overlap" do +    context "when periods intersect, " do +      it "should detect period overlap" do +         expect(subject.overlap?(p2)).to be_truthy +         expect(p2.overlap?(subject)).to be_truthy +      end +    end +    context "when periods don't intersect, " do +      before(:each) do +        p2.period_start = Date.new(2014,7,7) +      end +      it "should not detect period overlap" do +         expect(subject.overlap?(p2)).to be_falsey +         expect(p2.overlap?(subject)).to be_falsey +      end +    end +    context "when period 1 contains period 2, " do +      before(:each) do +        p2.period_start = Date.new(2014,7,1) +        p2.period_end = Date.new(2014,7,6) +      end +      it "should detect period overlap" do +         expect(subject.overlap?(p2)).to be_truthy +         expect(p2.overlap?(subject)).to be_truthy +      end +    end +  end +  describe "#contains" do +    context "when periods intersect, " do +      it "should not detect period inclusion" do +         expect(subject.contains?(p2)).to be_falsey +         expect(p2.contains?(subject)).to be_falsey +      end +    end +    context "when periods don't intersect, " do +      before(:each) do +        p2.period_start = Date.new(2014,7,7) +      end +      it "should not detect period inclusion" do +         expect(subject.contains?(p2)).to be_falsey +         expect(p2.contains?(subject)).to be_falsey +      end +    end +    context "when period 1 contains period 2, " do +      before(:each) do +        p2.period_start = Date.new(2014,7,1) +        p2.period_end = Date.new(2014,7,6) +      end +      it "should detect period inclusion" do +         expect(subject.contains?(p2)).to be_truthy +         expect(p2.contains?(subject)).to be_falsey +      end +    end +  end +end diff --git a/spec/models/chouette/time_table_spec.rb b/spec/models/chouette/time_table_spec.rb new file mode 100644 index 000000000..a3a361e7b --- /dev/null +++ b/spec/models/chouette/time_table_spec.rb @@ -0,0 +1,1266 @@ +require 'spec_helper' + +describe Chouette::TimeTable, :type => :model do + +  subject { create(:time_table) } + +  it { is_expected.to validate_presence_of :comment } +  it { is_expected.to validate_uniqueness_of :objectid } + +  describe "#periods_max_date" do +    context "when all period extends from 04/10/2013 to 04/15/2013," do +      before(:each) do +        p1 = Chouette::TimeTablePeriod.new( :period_start => Date.strptime("04/10/2013", '%m/%d/%Y'), :period_end => Date.strptime("04/12/2013", '%m/%d/%Y')) +        p2 = Chouette::TimeTablePeriod.new( :period_start => Date.strptime("04/13/2013", '%m/%d/%Y'), :period_end => Date.strptime("04/15/2013", '%m/%d/%Y')) +        subject.periods = [ p1, p2] +        subject.save +      end + +      it "should retreive 04/15/2013" do +        expect(subject.periods_max_date).to eq(Date.strptime("04/15/2013", '%m/%d/%Y')) +      end +      context "when 04/15/2013 is excluded, periods_max_dates selects the day before" do +        before(:each) do +          excluded_date = Date.strptime("04/15/2013", '%m/%d/%Y') +          subject.dates = [ Chouette::TimeTableDate.new( :date => excluded_date, :in_out => false)] +          subject.save +        end +        it "should retreive 04/14/2013" do +          expect(subject.periods_max_date).to eq(Date.strptime("04/14/2013", '%m/%d/%Y')) +        end +      end +      context "when day_types select only sunday and saturday," do +        before(:each) do +          # jeudi, vendredi +          subject.update_attributes( :int_day_types => (2**(1+6) + 2**(1+7))) +        end +        it "should retreive 04/14/2013" do +          expect(subject.periods_max_date).to eq(Date.strptime("04/14/2013", '%m/%d/%Y')) +        end +      end +      context "when day_types select only friday," do +        before(:each) do +          # jeudi, vendredi +          subject.update_attributes( :int_day_types => (2**(1+6))) +        end +        it "should retreive 04/12/2013" do +          expect(subject.periods_max_date).to eq(Date.strptime("04/13/2013", '%m/%d/%Y')) +        end +      end +      context "when day_types select only thursday," do +        before(:each) do +          # mardi +          subject.update_attributes( :int_day_types => (2**(1+2))) +        end +        it "should retreive 04/12/2013" do +          # 04/15/2013 is monday ! +          expect(subject.periods_max_date).to be_nil +        end +      end +    end +  end + +describe "update_attributes on periods and dates" do + +    context "update days selection" do +        it "should update start_date and end_end" do +            days_hash = {}.tap do |hash| +                [ :monday,:tuesday,:wednesday,:thursday,:friday,:saturday,:sunday ].each { |d| hash[d] = false } +            end +            subject.update_attributes( days_hash) + +            read = Chouette::TimeTable.find( subject.id ) +            expect(read.start_date).to eq(read.dates.select{|d| d.in_out}.map(&:date).compact.min) +            expect(read.end_date).to eq(read.dates.select{|d| d.in_out}.map(&:date).compact.max) + +        end +    end +    context "add a new period" do +        let!( :new_start_date ){ subject.start_date - 20.days } +        let!( :new_end_date ){ subject.end_date + 20.days } +        let!( :new_period_attributes ) { +            pa = periods_attributes +            pa[ "11111111111" ] = { "period_end" => new_end_date, "period_start" => new_start_date, "_destroy" => "", "position" => pa.size.to_s, "id" => "", "time_table_id" => subject.id.to_s} +            pa +        } +        it "should update start_date and end_end" do +            subject.update_attributes( :periods_attributes => new_period_attributes) + +            read = Chouette::TimeTable.find( subject.id ) +            expect(read.start_date).to eq(new_start_date) +            expect(read.end_date).to eq(new_end_date) +        end +    end +    context "update period end" do +        let!( :new_end_date ){ subject.end_date + 20.days } +        let!( :new_period_attributes ) { +            pa = periods_attributes +            pa[ "0" ].merge! "period_end" => new_end_date +            pa +        } +        it "should update end_date" do +            subject.update_attributes :periods_attributes => new_period_attributes + +            read = Chouette::TimeTable.find( subject.id ) +            expect(read.end_date).to eq(new_end_date) +        end +    end +    context "update period start" do +        let!( :new_start_date ){ subject.start_date - 20.days } +        let!( :new_period_attributes ) { +            pa = periods_attributes +            pa[ "0" ].merge! "period_start" => new_start_date +            pa +        } +        it "should update start_date" do +            subject.update_attributes :periods_attributes => new_period_attributes + +            read = Chouette::TimeTable.find( subject.id ) +            expect(read.start_date).to eq(new_start_date) +        end +    end +    context "remove periods and dates and add a new period" do +        let!( :new_start_date ){ subject.start_date + 1.days } +        let!( :new_end_date ){ subject.end_date - 1.days } +        let!( :new_dates_attributes ) { +            da = dates_attributes +            da.each { |k,v| v.merge! "_destroy" => true} +            da +        } +        let!( :new_period_attributes ) { +            pa = periods_attributes +            pa.each { |k,v| v.merge! "_destroy" => true} +            pa[ "11111111111" ] = { "period_end" => new_end_date, "period_start" => new_start_date, "_destroy" => "", "position" => pa.size.to_s, "id" => "", "time_table_id" => subject.id.to_s} +            pa +        } +        it "should update start_date and end_date with new period added" do +            subject.update_attributes :periods_attributes => new_period_attributes, :dates_attributes => new_dates_attributes + +            read = Chouette::TimeTable.find( subject.id ) +            expect(read.start_date).to eq(new_start_date) +            expect(read.end_date).to eq(new_end_date) +        end +    end +    def dates_attributes +        {}.tap do |hash| +            subject.dates.each_with_index do |p, index| +                hash.merge! index.to_s => p.attributes.merge( "_destroy" => "" ) +            end +        end +    end +    def periods_attributes +        {}.tap do |hash| +            subject.periods.each_with_index do |p, index| +                hash.merge! index.to_s => p.attributes.merge( "_destroy" => "" ) +            end +        end +    end +end + +  describe "#periods_min_date" do +    context "when all period extends from 04/10/2013 to 04/15/2013," do +      before(:each) do +        p1 = Chouette::TimeTablePeriod.new( :period_start => Date.strptime("04/10/2013", '%m/%d/%Y'), :period_end => Date.strptime("04/12/2013", '%m/%d/%Y')) +        p2 = Chouette::TimeTablePeriod.new( :period_start => Date.strptime("04/13/2013", '%m/%d/%Y'), :period_end => Date.strptime("04/15/2013", '%m/%d/%Y')) +        subject.periods = [ p1, p2] +        subject.save +      end + +      it "should retreive 04/10/2013" do +        expect(subject.periods_min_date).to eq(Date.strptime("04/10/2013", '%m/%d/%Y')) +      end +      context "when 04/10/2013 is excluded, periods_min_dates select the day after" do +        before(:each) do +          excluded_date = Date.strptime("04/10/2013", '%m/%d/%Y') +          subject.dates = [ Chouette::TimeTableDate.new( :date => excluded_date, :in_out => false)] +          subject.save +        end +        it "should retreive 04/11/2013" do +          expect(subject.periods_min_date).to eq(Date.strptime("04/11/2013", '%m/%d/%Y')) +        end +      end +      context "when day_types select only tuesday and friday," do +        before(:each) do +          # jeudi, vendredi +          subject.update_attributes( :int_day_types => (2**(1+4) + 2**(1+5))) +        end +        it "should retreive 04/11/2013" do +          expect(subject.periods_min_date).to eq(Date.strptime("04/11/2013", '%m/%d/%Y')) +        end +      end +      context "when day_types select only friday," do +        before(:each) do +          # jeudi, vendredi +          subject.update_attributes( :int_day_types => (2**(1+5))) +        end +        it "should retreive 04/12/2013" do +          expect(subject.periods_min_date).to eq(Date.strptime("04/12/2013", '%m/%d/%Y')) +        end +      end +      context "when day_types select only thursday," do +        before(:each) do +          # mardi +          subject.update_attributes( :int_day_types => (2**(1+2))) +        end +        it "should retreive 04/12/2013" do +          # 04/15/2013 is monday ! +          expect(subject.periods_min_date).to be_nil +        end +      end +    end +  end +  describe "#periods.build" do +    it "should add a new instance of period, and periods_max_date should not raise error" do +      period = subject.periods.build +      subject.periods_max_date +      expect(period.period_start).to be_nil +      expect(period.period_end).to be_nil +    end +  end +  describe "#periods" do +    context "when a period is added," do +      before(:each) do +        subject.periods << Chouette::TimeTablePeriod.new( :period_start => (subject.bounding_dates.min - 1), :period_end => (subject.bounding_dates.max + 1)) +        subject.save +      end +      it "should update shortcut" do +        expect(subject.start_date).to eq(subject.bounding_dates.min) +        expect(subject.end_date).to eq(subject.bounding_dates.max) +      end +    end +    context "when a period is removed," do +      before(:each) do +        subject.dates = [] +        subject.periods = [] +        subject.periods << Chouette::TimeTablePeriod.new( +                              :period_start => 4.days.since.to_date, +                              :period_end => 6.days.since.to_date) +        subject.periods << Chouette::TimeTablePeriod.new( +                              :period_start => 1.days.since.to_date, +                              :period_end => 10.days.since.to_date) +        subject.save +        subject.periods = subject.periods - [subject.periods.last] +        subject.save_shortcuts +      end +      def read_tm +        Chouette::TimeTable.find subject.id +      end +      it "should update shortcut" do +        tm = read_tm +        expect(subject.start_date).to eq(subject.bounding_dates.min) +        expect(subject.start_date).to eq(tm.bounding_dates.min) +        expect(subject.start_date).to eq(4.days.since.to_date) +        expect(subject.end_date).to eq(subject.bounding_dates.max) +        expect(subject.end_date).to eq(tm.bounding_dates.max) +        expect(subject.end_date).to eq(6.days.since.to_date) +      end +    end +    context "when a period is updated," do +      before(:each) do +        subject.dates = [] +        subject.periods = [] +        subject.periods << Chouette::TimeTablePeriod.new( +                              :period_start => 4.days.since.to_date, +                              :period_end => 6.days.since.to_date) +        subject.periods << Chouette::TimeTablePeriod.new( +                              :period_start => 1.days.since.to_date, +                              :period_end => 10.days.since.to_date) +        subject.save +        subject.periods.last.period_end = 15.days.since.to_date +        subject.save +      end +      def read_tm +        Chouette::TimeTable.find subject.id +      end +      it "should update shortcut" do +        tm = read_tm +        expect(subject.start_date).to eq(subject.bounding_dates.min) +        expect(subject.start_date).to eq(tm.bounding_dates.min) +        expect(subject.start_date).to eq(1.days.since.to_date) +        expect(subject.end_date).to eq(subject.bounding_dates.max) +        expect(subject.end_date).to eq(tm.bounding_dates.max) +        expect(subject.end_date).to eq(15.days.since.to_date) +      end +    end + +  end +  describe "#periods.valid?" do +    context "when an empty period is set," do +      it "should not save tm if period invalid" do +        subject = Chouette::TimeTable.new({"comment"=>"test", +                                           "version"=>"", +                                           "monday"=>"0", +                                           "tuesday"=>"0", +                                           "wednesday"=>"0", +                                           "thursday"=>"0", +                                           "friday"=>"0", +                                           "saturday"=>"0", +                                           "sunday"=>"0", +                                           "objectid"=>"", +                                           "periods_attributes"=>{"1397136188334"=>{"period_start"=>"", +                                           "period_end"=>"", +                                           "_destroy"=>""}}}) +        subject.save +        expect(subject.id).to be_nil +      end +    end +    context "when a valid period is set," do +      it "it should save tm if period valid" do +        subject = Chouette::TimeTable.new({"comment"=>"test", +                                           "version"=>"", +                                           "monday"=>"1", +                                           "tuesday"=>"1", +                                           "wednesday"=>"1", +                                           "thursday"=>"1", +                                           "friday"=>"1", +                                           "saturday"=>"1", +                                           "sunday"=>"1", +                                           "objectid"=>"", +                                           "periods_attributes"=>{"1397136188334"=>{"period_start"=>"2014-01-01", +                                           "period_end"=>"2015-01-01", +                                           "_destroy"=>""}}}) +        subject.save +        tm = Chouette::TimeTable.find subject.id +        expect(tm.periods.empty?).to be_falsey +        expect(tm.start_date).to eq(Date.new(2014, 01, 01)) +        expect(tm.end_date).to eq(Date.new(2015, 01, 01)) + +      end +    end +  end + +  describe "#dates" do +    context "when a date is added," do +      before(:each) do +        subject.dates << Chouette::TimeTableDate.new( :date => (subject.bounding_dates.max + 1), :in_out => true) +        subject.save +      end +      it "should update shortcut" do +        expect(subject.start_date).to eq(subject.bounding_dates.min) +        expect(subject.end_date).to eq(subject.bounding_dates.max) +      end +    end +    context "when a date is removed," do +      before(:each) do +        subject.periods = [] +        subject.dates = subject.dates - [subject.bounding_dates.max + 1] +        subject.save_shortcuts +      end +      it "should update shortcut" do +        expect(subject.start_date).to eq(subject.bounding_dates.min) +        expect(subject.end_date).to eq(subject.bounding_dates.max) +      end +    end +    context "when all the dates and periods are removed," do +      before(:each) do +        subject.periods = [] +        subject.dates = [] +        subject.save_shortcuts +      end +      it "should update shortcut" do +        expect(subject.start_date).to be_nil +        expect(subject.end_date).to be_nil +      end +    end +    context "when a date is updated," do +      before(:each) do +        subject.dates = [] + +        subject.periods = [] +        subject.periods << Chouette::TimeTablePeriod.new( +                              :period_start => 4.days.since.to_date, +                              :period_end => 6.days.since.to_date) +        subject.periods << Chouette::TimeTablePeriod.new( +                              :period_start => 1.days.since.to_date, +                              :period_end => 10.days.since.to_date) +        subject.dates << Chouette::TimeTableDate.new( :date => 10.days.since.to_date, :in_out => true) +        subject.save +        subject.dates.last.date = 15.days.since.to_date +        subject.save +      end +      def read_tm +        Chouette::TimeTable.find subject.id +      end +      it "should update shortcut" do +        tm = read_tm +        expect(subject.start_date).to eq(subject.bounding_dates.min) +        expect(subject.start_date).to eq(tm.bounding_dates.min) +        expect(subject.start_date).to eq(1.days.since.to_date) +        expect(subject.end_date).to eq(subject.bounding_dates.max) +        expect(subject.end_date).to eq(tm.bounding_dates.max) +        expect(subject.end_date).to eq(15.days.since.to_date) +      end +    end +  end +  describe "#dates.valid?" do +    it "should not save tm if date invalid" do +      subject = Chouette::TimeTable.new({"comment"=>"test", +                                         "version"=>"", +                                         "monday"=>"0", +                                         "tuesday"=>"0", +                                         "wednesday"=>"0", +                                         "thursday"=>"0", +                                         "friday"=>"0", +                                         "saturday"=>"0", +                                         "sunday"=>"0", +                                         "objectid"=>"", +                                         "dates_attributes"=>{"1397136189216"=>{"date"=>"", +                                         "_destroy"=>"", "in_out" => true}}}) +      subject.save +      expect(subject.id).to be_nil +    end +    it "it should save tm if date valid" do +      subject = Chouette::TimeTable.new({"comment"=>"test", +                                         "version"=>"", +                                         "monday"=>"1", +                                         "tuesday"=>"1", +                                         "wednesday"=>"1", +                                         "thursday"=>"1", +                                         "friday"=>"1", +                                         "saturday"=>"1", +                                         "sunday"=>"1", +                                         "objectid"=>"", +                                         "dates_attributes"=>{"1397136189216"=>{"date"=>"2015-01-01", +                                         "_destroy"=>"", "in_out" => true}}}) +      subject.save +      tm = Chouette::TimeTable.find subject.id +      expect(tm.dates.empty?).to be_falsey +      expect(tm.start_date).to eq(Date.new(2015, 01, 01)) +      expect(tm.end_date).to eq(Date.new(2015, 01, 01)) +    end +  end + +  describe "#valid_days" do +    it "should begin with position 0" do +      subject.int_day_types = 128 +      expect(subject.valid_days).to eq([6]) +    end +  end + +  describe "#intersects" do +    it "should return day if a date equal day" do +      time_table = Chouette::TimeTable.create!(:comment => "Test", :objectid => "test:Timetable:1") +      time_table.dates << Chouette::TimeTableDate.new( :date => Date.today, :in_out => true) +      expect(time_table.intersects([Date.today])).to eq([Date.today]) +    end + +    it "should return [] if a period not include days" do +      time_table = Chouette::TimeTable.create!(:comment => "Test", :objectid => "test:Timetable:1", :int_day_types => 12) +      time_table.periods << Chouette::TimeTablePeriod.new( +                              :period_start => Date.new(2013, 05, 27), +                              :period_end => Date.new(2013, 05, 30)) +      expect(time_table.intersects([ Date.new(2013, 05, 29),  Date.new(2013, 05, 30)])).to eq([]) +    end + +    it "should return days if a period include day" do +      time_table = Chouette::TimeTable.create!(:comment => "Test", :objectid => "test:Timetable:1", :int_day_types => 12) # Day type monday and tuesday +      time_table.periods << Chouette::TimeTablePeriod.new( +                              :period_start => Date.new(2013, 05, 27), +                              :period_end => Date.new(2013, 05, 30)) +      expect(time_table.intersects([ Date.new(2013, 05, 27),  Date.new(2013, 05, 28)])).to eq([ Date.new(2013, 05, 27),  Date.new(2013, 05, 28)]) +    end + + +  end + +  describe "#include_day?" do +    it "should return true if a date equal day" do +      time_table = Chouette::TimeTable.create!(:comment => "Test", :objectid => "test:Timetable:1") +      time_table.dates << Chouette::TimeTableDate.new( :date => Date.today, :in_out => true) +      expect(time_table.include_day?(Date.today)).to eq(true) +    end + +    it "should return true if a period include day" do +      time_table = Chouette::TimeTable.create!(:comment => "Test", :objectid => "test:Timetable:1", :int_day_types => 12) # Day type monday and tuesday +      time_table.periods << Chouette::TimeTablePeriod.new( +                              :period_start => Date.new(2013, 05, 27), +                              :period_end => Date.new(2013, 05, 29)) +      expect(time_table.include_day?( Date.new(2013, 05, 27))).to eq(true) +    end +  end + +  describe "#include_in_dates?" do +    it "should return true if a date equal day" do +      time_table = Chouette::TimeTable.create!(:comment => "Test", :objectid => "test:Timetable:1") +      time_table.dates << Chouette::TimeTableDate.new( :date => Date.today, :in_out => true) +      expect(time_table.include_in_dates?(Date.today)).to eq(true) +    end + +    it "should return false if a period include day  but that is exclued" do +      time_table = Chouette::TimeTable.create!(:comment => "Test", :objectid => "test:Timetable:1", :int_day_types => 12) # Day type monday and tuesday +      excluded_date = Date.new(2013, 05, 27) +      time_table.dates << Chouette::TimeTableDate.new( :date => excluded_date, :in_out => false) +      expect(time_table.include_in_dates?( excluded_date)).to be_falsey +    end +  end + +  describe "#include_in_periods?" do +    it "should return true if a period include day" do +      time_table = Chouette::TimeTable.create!(:comment => "Test", :objectid => "test:Timetable:1", :int_day_types => 4) +      time_table.periods << Chouette::TimeTablePeriod.new( +                              :period_start => Date.new(2012, 1, 1), +                              :period_end => Date.new(2012, 01, 30)) +      expect(time_table.include_in_periods?(Date.new(2012, 1, 2))).to eq(true) +    end + +    it "should return false if a period include day  but that is exclued" do +      time_table = Chouette::TimeTable.create!(:comment => "Test", :objectid => "test:Timetable:1", :int_day_types => 12) # Day type monday and tuesday +      excluded_date = Date.new(2013, 05, 27) +      time_table.dates << Chouette::TimeTableDate.new( :date => excluded_date, :in_out => false) +      time_table.periods << Chouette::TimeTablePeriod.new( +                              :period_start => Date.new(2013, 05, 27), +                              :period_end => Date.new(2013, 05, 29)) +      expect(time_table.include_in_periods?( excluded_date)).to be_falsey +    end +  end + +  describe "#include_in_overlap_dates?" do +    it "should return true if a day is included in overlap dates" do +      time_table = Chouette::TimeTable.create!(:comment => "Test", :objectid => "test:Timetable:1", :int_day_types => 4) +      time_table.periods << Chouette::TimeTablePeriod.new( +                              :period_start => Date.new(2012, 1, 1), +                              :period_end => Date.new(2012, 01, 30)) +      time_table.dates << Chouette::TimeTableDate.new( :date => Date.new(2012, 1, 2), :in_out => true) +      expect(time_table.include_in_overlap_dates?(Date.new(2012, 1, 2))).to eq(true) +    end +    it "should return false if the day is excluded" do +      time_table = Chouette::TimeTable.create!(:comment => "Test", :objectid => "test:Timetable:1", :int_day_types => 4) +      time_table.periods << Chouette::TimeTablePeriod.new( +                              :period_start => Date.new(2012, 1, 1), +                              :period_end => Date.new(2012, 01, 30)) +      time_table.dates << Chouette::TimeTableDate.new( :date => Date.new(2012, 1, 2), :in_out => false) +      expect(time_table.include_in_overlap_dates?(Date.new(2012, 1, 2))).to be_falsey +    end +  end + +  describe "#dates" do +    it "should have with position 0" do +      expect(subject.dates.first.position).to eq(0) +    end +    context "when first date has been removed" do +      before do +        subject.dates.first.destroy +      end +      it "should begin with position 0" do +        expect(subject.dates.first.position).to eq(0) +      end +    end +  end +  describe "#validity_out_between?" do +    let(:empty_tm) {build(:time_table)} +    it "should be false if empty calendar" do +      expect(empty_tm.validity_out_between?( Date.today, Date.today + 7.day)).to be_falsey +    end +    it "should be true if caldendar is out during start_date and end_date period" do +      start_date = subject.bounding_dates.max - 2.day +      end_date = subject.bounding_dates.max + 2.day +      expect(subject.validity_out_between?( start_date, end_date)).to be_truthy +    end +    it "should be false if calendar is out on start date" do +      start_date = subject.bounding_dates.max +      end_date = subject.bounding_dates.max + 2.day +      expect(subject.validity_out_between?( start_date, end_date)).to be_falsey +    end +    it "should be false if calendar is out on end date" do +      start_date = subject.bounding_dates.max - 2.day +      end_date = subject.bounding_dates.max +      expect(subject.validity_out_between?( start_date, end_date)).to be_truthy +    end +    it "should be false if calendar is out after start_date" do +      start_date = subject.bounding_dates.max + 2.day +      end_date = subject.bounding_dates.max + 4.day +      expect(subject.validity_out_between?( start_date, end_date)).to be_falsey +    end +  end +  describe "#validity_out_from_on?" do +    let(:empty_tm) {build(:time_table)} +    it "should be false if empty calendar" do +      expect(empty_tm.validity_out_from_on?( Date.today)).to be_falsey +    end +    it "should be true if caldendar ends on expected date" do +      expected_date = subject.bounding_dates.max +      expect(subject.validity_out_from_on?( expected_date)).to be_truthy +    end +    it "should be true if calendar ends before expected date" do +      expected_date = subject.bounding_dates.max + 30.day +      expect(subject.validity_out_from_on?( expected_date)).to be_truthy +    end +    it "should be false if calendars ends after expected date" do +      expected_date = subject.bounding_dates.max - 30.day +      expect(subject.validity_out_from_on?( expected_date)).to be_falsey +    end +  end +  describe "#bounding_dates" do +    context "when timetable contains only periods" do +      before do +        subject.dates = [] +        subject.save +      end +      it "should retreive periods.period_start.min and periods.period_end.max" do +        expect(subject.bounding_dates.min).to eq(subject.periods.map(&:period_start).min) +        expect(subject.bounding_dates.max).to eq(subject.periods.map(&:period_end).max) +      end +    end +    context "when timetable contains only dates" do +      before do +        subject.periods = [] +        subject.save +      end +      it "should retreive dates.min and dates.max" do +        expect(subject.bounding_dates.min).to eq(subject.dates.map(&:date).min) +        expect(subject.bounding_dates.max).to eq(subject.dates.map(&:date).max) +      end +    end +    it "should contains min date" do +      min_date = subject.bounding_dates.min +      subject.dates.each do |tm_date| +        expect(min_date <= tm_date.date).to be_truthy +      end +      subject.periods.each do |tm_period| +        expect(min_date <= tm_period.period_start).to be_truthy +      end + +    end +    it "should contains max date" do +      max_date = subject.bounding_dates.max +      subject.dates.each do |tm_date| +        expect(tm_date.date <= max_date).to be_truthy +      end +      subject.periods.each do |tm_period| +        expect(tm_period.period_end <= max_date).to be_truthy +      end + +    end +  end +  describe "#periods" do +    it "should begin with position 0" do +      expect(subject.periods.first.position).to eq(0) +    end +    context "when first period has been removed" do +      before do +        subject.periods.first.destroy +      end +      it "should begin with position 0" do +        expect(subject.periods.first.position).to eq(0) +      end +    end +    it "should have period_start before period_end" do +      period = Chouette::TimeTablePeriod.new +      period.period_start = Date.today +      period.period_end = Date.today + 10 +      expect(period.valid?).to be_truthy +    end +    it "should not have period_start after period_end" do +      period = Chouette::TimeTablePeriod.new +      period.period_start = Date.today +      period.period_end = Date.today - 10 +      expect(period.valid?).to be_falsey +    end +    it "should not have period_start equal to period_end" do +      period = Chouette::TimeTablePeriod.new +      period.period_start = Date.today +      period.period_end = Date.today +      expect(period.valid?).to be_falsey +    end +  end + +  describe "#effective_days_of_periods" do +      before do +        subject.periods.clear +        subject.periods << Chouette::TimeTablePeriod.new( +                              :period_start => Date.new(2014, 6, 30), +                              :period_end => Date.new(2014, 7, 6)) +        subject.int_day_types = 4|8|16 +      end +      it "should return monday to wednesday" do +        expect(subject.effective_days_of_periods.size).to eq(3) +        expect(subject.effective_days_of_periods[0]).to eq(Date.new(2014, 6, 30)) +        expect(subject.effective_days_of_periods[1]).to eq(Date.new(2014, 7, 1)) +        expect(subject.effective_days_of_periods[2]).to eq(Date.new(2014, 7, 2)) +      end +      it "should return thursday" do +        expect(subject.effective_days_of_periods(Chouette::TimeTable.valid_days(32)).size).to eq(1) +        expect(subject.effective_days_of_periods(Chouette::TimeTable.valid_days(32))[0]).to eq(Date.new(2014, 7, 3)) +      end + +  end + +  describe "#included_days" do +      before do +        subject.dates.clear +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,16), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,17), :in_out => false) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,18), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,19), :in_out => false) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,20), :in_out => true) +      end +      it "should return 3 dates" do +        days = subject.included_days +        expect(days.size).to eq(3) +        expect(days[0]).to eq(Date.new(2014, 7, 16)) +        expect(days[1]).to eq(Date.new(2014,7, 18)) +        expect(days[2]).to eq(Date.new(2014, 7,20)) +      end +  end + + + +  describe "#excluded_days" do +      before do +        subject.dates.clear +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,16), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,17), :in_out => false) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,18), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,19), :in_out => false) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,20), :in_out => true) +      end +      it "should return 3 dates" do +        days = subject.excluded_days +        expect(days.size).to eq(2) +        expect(days[0]).to eq(Date.new(2014, 7, 17)) +        expect(days[1]).to eq(Date.new(2014,7, 19)) +      end +  end + + + +  describe "#effective_days" do +      before do +        subject.periods.clear +        subject.periods << Chouette::TimeTablePeriod.new( +                              :period_start => Date.new(2014, 6, 30), +                              :period_end => Date.new(2014, 7, 6)) +        subject.int_day_types = 4|8|16 +        subject.dates.clear +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,1), :in_out => false) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,16), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,18), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,20), :in_out => true) +      end +      it "should return 5 dates" do +        days = subject.effective_days +        expect(days.size).to eq(5) +        expect(days[0]).to eq(Date.new(2014, 6, 30)) +        expect(days[1]).to eq(Date.new(2014, 7, 2)) +        expect(days[2]).to eq(Date.new(2014, 7, 16)) +        expect(days[3]).to eq(Date.new(2014, 7, 18)) +        expect(days[4]).to eq(Date.new(2014, 7, 20)) +      end +  end + + + +  describe "#optimize_periods" do +      before do +        subject.periods.clear +        subject.periods << Chouette::TimeTablePeriod.new( +                              :period_start => Date.new(2014, 6, 30), +                              :period_end => Date.new(2014, 7, 6)) +        subject.periods << Chouette::TimeTablePeriod.new( +                              :period_start => Date.new(2014, 7, 6), +                              :period_end => Date.new(2014, 7, 14)) +        subject.periods << Chouette::TimeTablePeriod.new( +                              :period_start => Date.new(2014, 6, 1), +                              :period_end => Date.new(2014, 6, 14)) +        subject.periods << Chouette::TimeTablePeriod.new( +                              :period_start => Date.new(2014, 6, 3), +                              :period_end => Date.new(2014, 6, 4)) +        subject.int_day_types = 4|8|16 +      end +      it "should return 2 ordered periods" do +        periods = subject.optimize_periods +        expect(periods.size).to eq(2) +        expect(periods[0].period_start).to eq(Date.new(2014, 6, 1)) +        expect(periods[0].period_end).to eq(Date.new(2014, 6, 14)) +        expect(periods[1].period_start).to eq(Date.new(2014, 6, 30)) +        expect(periods[1].period_end).to eq(Date.new(2014, 7, 14)) +      end +  end + +  describe "#add_included_day" do +      before do +        subject.dates.clear +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,16), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,18), :in_out => false) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,20), :in_out => true) +      end +      it "should do nothing" do +        subject.add_included_day(Date.new(2014,7,16)) +        days = subject.included_days +        expect(days.size).to eq(2) +        expect(days.include?(Date.new(2014,7,16))).to be_truthy +        expect(days.include?(Date.new(2014,7,18))).to be_falsey +        expect(days.include?(Date.new(2014,7,20))).to be_truthy +      end +      it "should switch in_out flag" do +        subject.add_included_day(Date.new(2014,7,18)) +        days = subject.included_days +        expect(days.size).to eq(3) +        expect(days.include?(Date.new(2014,7,16))).to be_truthy +        expect(days.include?(Date.new(2014,7,18))).to be_truthy +        expect(days.include?(Date.new(2014,7,20))).to be_truthy +      end +      it "should add date" do +        subject.add_included_day(Date.new(2014,7,21)) +        days = subject.included_days +        expect(days.size).to eq(3) +        expect(days.include?(Date.new(2014,7,16))).to be_truthy +        expect(days.include?(Date.new(2014,7,20))).to be_truthy +        expect(days.include?(Date.new(2014,7,21))).to be_truthy +      end +  end + + +  describe "#merge!" do +    context "timetables have periods with common day_types " do +      before do +        subject.periods.clear +        subject.dates.clear +        subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,1), :period_end => Date.new(2014,8,5)) +        subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,6,30), :period_end => Date.new(2014,7,6)) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,16), :in_out => true) +        subject.int_day_types = 4|16|32|128 +        another_tt = create(:time_table , :int_day_types => (4|16|64|128) ) +        another_tt.periods.clear +        another_tt.dates.clear +        another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,5), :period_end => Date.new(2014,8,12)) +        another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,7,15), :period_end => Date.new(2014,7,25)) +        subject.merge! another_tt +        subject.reload +      end +      it "should have merged periods" do +        expect(subject.periods.size).to eq(3) +        expect(subject.periods[0].period_start).to eq(Date.new(2014, 6, 30)) +        expect(subject.periods[0].period_end).to eq(Date.new(2014, 7, 6)) +        expect(subject.periods[1].period_start).to eq(Date.new(2014, 7, 15)) +        expect(subject.periods[1].period_end).to eq(Date.new(2014, 7, 25)) +        expect(subject.periods[2].period_start).to eq(Date.new(2014, 8, 1)) +        expect(subject.periods[2].period_end).to eq(Date.new(2014, 8, 12)) +      end +      it "should have common day_types" do +        expect(subject.int_day_types).to eq(4|16|128) +      end +      it "should have dates for thursdays and fridays" do +        expect(subject.dates.size).to eq(4) +        expect(subject.dates[0].date).to eq(Date.new(2014,7,3)) +        expect(subject.dates[1].date).to eq(Date.new(2014,7,18)) +        expect(subject.dates[2].date).to eq(Date.new(2014,7,25)) +        expect(subject.dates[3].date).to eq(Date.new(2014,8,8)) +      end +    end + +  end + +  describe "#intersect!" do +    context "timetables have periods with common day_types " do +      before do +        subject.periods.clear +        subject.dates.clear +        subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,1), :period_end => Date.new(2014,8,6)) +        subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,6,30), :period_end => Date.new(2014,7,20)) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,16), :in_out => true) +        subject.int_day_types = 4|16|32|128 +        another_tt = create(:time_table , :int_day_types => (4|16|64|128) ) +        another_tt.periods.clear +        another_tt.dates.clear +        another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,6), :period_end => Date.new(2014,8,12)) +        another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,7,15), :period_end => Date.new(2014,7,25)) +        subject.intersect! another_tt +        subject.reload +      end +      it "should have no period" do +        expect(subject.periods.size).to eq(0) +       end +      it "should have no day_types" do +        expect(subject.int_day_types).to eq(0) +      end +      it "should have date all common days" do +        expect(subject.dates.size).to eq(3) +        expect(subject.dates[0].date).to eq(Date.new(2014,7,16)) +        expect(subject.dates[1].date).to eq(Date.new(2014,7,19)) +        expect(subject.dates[2].date).to eq(Date.new(2014,8,6)) +      end +    end +    context "timetables have periods or dates " do +      before do +        subject.periods.clear +        subject.dates.clear +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,16), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,17), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,18), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,19), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,20), :in_out => true) +        subject.int_day_types = 0 +        another_tt = create(:time_table , :int_day_types => (4|16|64|128) ) +        another_tt.periods.clear +        another_tt.dates.clear +        another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,6), :period_end => Date.new(2014,8,12)) +        another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,7,17), :period_end => Date.new(2014,7,25)) +        subject.intersect! another_tt +        subject.reload +      end +      it "should have 0 period" do +        expect(subject.periods.size).to eq(0) +      end +      it "should have no day_types" do +        expect(subject.int_day_types).to eq(0) +      end +      it "should have date reduced for period" do +        expect(subject.dates.size).to eq(2) +        expect(subject.dates[0].date).to eq(Date.new(2014,7,18)) +        expect(subject.dates[1].date).to eq(Date.new(2014,7,19)) +      end +    end +    context "with only periods : intersect timetable have no one day period" do +      before do +        subject.periods.clear +        subject.dates.clear +        subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,1), :period_end => Date.new(2014,8,6)) +        subject.int_day_types = 4|8|16 +        another_tt = create(:time_table , :int_day_types => (4|8|16) ) +        another_tt.periods.clear +        another_tt.dates.clear +        another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,6), :period_end => Date.new(2014,8,12)) +        subject.intersect! another_tt +        subject.reload +      end +      it "should have 0 result periods" do +        expect(subject.periods.size).to eq(0) +      end +      it "should have no day_types" do +        expect(subject.int_day_types).to eq(0) +      end +      it "should have 1 date " do +        expect(subject.dates.size).to eq(1) +        expect(subject.dates[0].date).to eq(Date.new(2014,8,6)) +      end +    end + +  end + +  describe "#disjoin!" do +    context "timetables have periods with common day_types " do +      before do +        subject.periods.clear +        subject.dates.clear +        subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,1), :period_end => Date.new(2014,8,6)) +        subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,6,30), :period_end => Date.new(2014,7,20)) +        subject.int_day_types = 4|16|32|128 +        another_tt = create(:time_table , :int_day_types => (4|16|64|128) ) +        another_tt.periods.clear +        another_tt.dates.clear +        another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,6), :period_end => Date.new(2014,8,12)) +        another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,7,15), :period_end => Date.new(2014,8,2)) +        subject.disjoin! another_tt +        subject.reload +      end +      it "should have 0 periods" do +        expect(subject.periods.size).to eq(0) +       end +      it "should have 0 day_types" do +        expect(subject.int_day_types).to eq(0) +      end +      it "should have only dates " do +        expect(subject.dates.size).to eq(11) +        expect(subject.dates[0].date).to eq(Date.new(2014,6,30)) +        expect(subject.dates[1].date).to eq(Date.new(2014,7,2)) +        expect(subject.dates[2].date).to eq(Date.new(2014,7,3)) +        expect(subject.dates[3].date).to eq(Date.new(2014,7,5)) +        expect(subject.dates[4].date).to eq(Date.new(2014,7,7)) +        expect(subject.dates[5].date).to eq(Date.new(2014,7,9)) +        expect(subject.dates[6].date).to eq(Date.new(2014,7,10)) +        expect(subject.dates[7].date).to eq(Date.new(2014,7,12)) +        expect(subject.dates[8].date).to eq(Date.new(2014,7,14)) +        expect(subject.dates[9].date).to eq(Date.new(2014,7,17)) +        expect(subject.dates[10].date).to eq(Date.new(2014,8,4)) +      end +    end +    context "timetables have periods or dates " do +      before do +        subject.periods.clear +        subject.dates.clear +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,16), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,17), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,18), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,19), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,20), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,8,6), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,8,7), :in_out => true) +        subject.int_day_types = 0 +        another_tt = create(:time_table , :int_day_types => (4|16|64|128) ) +        another_tt.periods.clear +        another_tt.dates.clear +        another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,6), :period_end => Date.new(2014,8,12)) +        another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,7,17), :period_end => Date.new(2014,7,25)) +        subject.disjoin! another_tt +        subject.reload +      end +      it "should have 0 period" do +        expect(subject.periods.size).to eq(0) +      end +      it "should have no remained day_types" do +        expect(subject.int_day_types).to eq(0) +      end +      it "should have date reduced for period" do +        expect(subject.dates.size).to eq(4) +        expect(subject.dates[0].date).to eq(Date.new(2014,7,16)) +        expect(subject.dates[1].date).to eq(Date.new(2014,7,17)) +        expect(subject.dates[2].date).to eq(Date.new(2014,7,20)) +        expect(subject.dates[3].date).to eq(Date.new(2014,8,7)) +      end +    end +    context "disjoined timetable have all periods in removed ones " do +      before do +        subject.periods.clear +        subject.dates.clear +        subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,1), :period_end => Date.new(2014,8,8)) +        subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,6,30), :period_end => Date.new(2014,7,20)) +        subject.int_day_types = 4|16|32|128 +        another_tt = create(:time_table , :int_day_types => (4|16|64|128) ) +        another_tt.periods.clear +        another_tt.dates.clear +        another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,7,31), :period_end => Date.new(2014,8,12)) +        another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,6,30), :period_end => Date.new(2014,7,20)) +        subject.disjoin! another_tt +        subject.reload +      end +      it "should have 0 result periods" do +        expect(subject.periods.size).to eq(0) +      end +      it "should have no remained day_types" do +        expect(subject.int_day_types).to eq(0) +      end +      it "should have dates for period reduced" do +        expect(subject.dates.size).to eq(4) +        expect(subject.dates[0].date).to eq(Date.new(2014,7,3)) +        expect(subject.dates[1].date).to eq(Date.new(2014,7,10)) +        expect(subject.dates[2].date).to eq(Date.new(2014,7,17)) +        expect(subject.dates[3].date).to eq(Date.new(2014,8,7)) +      end +    end + +    context "timetable with dates against timetable with dates and periods" do +      before do +        subject.periods.clear +        subject.dates.clear +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,16), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,17), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,18), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,19), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,20), :in_out => true) +        subject.int_day_types = 0 +        another_tt = create(:time_table , :int_day_types => (4|16|64|128) ) +        another_tt.periods.clear +        another_tt.dates.clear +        another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,7,31), :period_end => Date.new(2014,8,12)) +        another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,17), :in_out => true) +        another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,18), :in_out => true) +        subject.disjoin! another_tt +        subject.reload +      end +      it "should have 0 result periods" do +        expect(subject.periods.size).to eq(0) +      end +      it "should have no remained day_types" do +        subject.int_day_types == 0 +      end +      it "should have 3 dates left" do +        expect(subject.dates.size).to eq(3) +        expect(subject.dates[0].date).to eq(Date.new(2014,7,16)) +        expect(subject.dates[1].date).to eq(Date.new(2014,7,19)) +        expect(subject.dates[2].date).to eq(Date.new(2014,7,20)) +      end +    end +    context "timetable with dates against timetable with dates and periods all covered" do +      before do +        subject.periods.clear +        subject.dates.clear +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,1), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,2), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,5), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,6), :in_out => true) +        subject.int_day_types = 512 +        another_tt = create(:time_table , :int_day_types => (32|64|512) ) +        another_tt.periods.clear +        another_tt.dates.clear +        another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,6,30), :period_end => Date.new(2014,7,11)) +        another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,1), :in_out => true) +        another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,2), :in_out => true) +        another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,5), :in_out => true) +        another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,7,6), :in_out => true) +        subject.disjoin! another_tt +        subject.reload +      end +      it "should have 0 result periods" do +        expect(subject.periods.size).to eq(0) +      end +      it "should have no remained day_types" do +        subject.int_day_types == 0 +      end +      it "should have 0 dates left" do +        expect(subject.dates.size).to eq(0) +      end +    end + +    context "with only periods : disjoined timetable have no empty period" do +      before do +        subject.periods.clear +        subject.dates.clear +        subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,1), :period_end => Date.new(2014,8,8)) +        subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,10), :period_end => Date.new(2014,8,31)) +        subject.int_day_types = 4|8 +        another_tt = create(:time_table , :int_day_types => (4|8) ) +        another_tt.periods.clear +        another_tt.dates.clear +        another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,4), :period_end => Date.new(2014,8,7)) +        subject.disjoin! another_tt +        subject.reload +      end +      it "should have 0 result periods" do +        expect(subject.periods.size).to eq(0) +      end +      it "should have 0 day_types" do +        expect(subject.int_day_types).to eq(0) +      end +      it "should have 6 dates " do +        expect(subject.dates.size).to eq(6) +        expect(subject.dates[0].date).to eq(Date.new(2014,8,11)) +        expect(subject.dates[1].date).to eq(Date.new(2014,8,12)) +        expect(subject.dates[2].date).to eq(Date.new(2014,8,18)) +        expect(subject.dates[3].date).to eq(Date.new(2014,8,19)) +        expect(subject.dates[4].date).to eq(Date.new(2014,8,25)) +        expect(subject.dates[5].date).to eq(Date.new(2014,8,26)) +      end +    end + +    context "with only periods : disjoined timetable have no one day period" do +      before do +        subject.periods.clear +        subject.dates.clear +        subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,1), :period_end => Date.new(2014,8,6)) +        subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,10), :period_end => Date.new(2014,8,31)) +        subject.int_day_types = 4|8|16 +        another_tt = create(:time_table , :int_day_types => (4|8) ) +        another_tt.periods.clear +        another_tt.dates.clear +        another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,4), :period_end => Date.new(2014,8,5)) +        subject.disjoin! another_tt +        subject.reload +      end +      it "should have 0 result periods" do +        expect(subject.periods.size).to eq(0) +     end +      it "should have 0 day_types" do +        expect(subject.int_day_types).to eq(0) +      end +      it "should have 10 dates " do +        expect(subject.dates.size).to eq(10) +        expect(subject.dates[0].date).to eq(Date.new(2014,8,6)) +        expect(subject.dates[1].date).to eq(Date.new(2014,8,11)) +        expect(subject.dates[2].date).to eq(Date.new(2014,8,12)) +        expect(subject.dates[3].date).to eq(Date.new(2014,8,13)) +        expect(subject.dates[4].date).to eq(Date.new(2014,8,18)) +        expect(subject.dates[5].date).to eq(Date.new(2014,8,19)) +        expect(subject.dates[6].date).to eq(Date.new(2014,8,20)) +        expect(subject.dates[7].date).to eq(Date.new(2014,8,25)) +        expect(subject.dates[8].date).to eq(Date.new(2014,8,26)) +        expect(subject.dates[9].date).to eq(Date.new(2014,8,27)) +      end +    end + +    context "with periods against dates: disjoined timetable have no unused excluded date" do +      before do +        subject.periods.clear +        subject.dates.clear +        subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,1), :period_end => Date.new(2014,8,8)) +        subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2014,8,10), :period_end => Date.new(2014,8,31)) +        subject.int_day_types = 4|8|16 +        another_tt = create(:time_table , :int_day_types => (0) ) +        another_tt.periods.clear +        another_tt.dates.clear +        another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,8,4), :in_out => true) +        another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,8,5), :in_out => true) +        another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2014,8,7), :in_out => true) +        subject.disjoin! another_tt +        subject.reload +      end +      it "should have same 0 result periods" do +        expect(subject.periods.size).to eq(0) +      end +      it "should have 0 day_types" do +        expect(subject.int_day_types).to eq(0) +      end +      it "should have only 10 dates " do +        expect(subject.dates.size).to eq(10) +        expect(subject.dates[0].date).to eq(Date.new(2014,8,6)) +        expect(subject.dates[1].date).to eq(Date.new(2014,8,11)) +        expect(subject.dates[2].date).to eq(Date.new(2014,8,12)) +        expect(subject.dates[3].date).to eq(Date.new(2014,8,13)) +        expect(subject.dates[4].date).to eq(Date.new(2014,8,18)) +        expect(subject.dates[5].date).to eq(Date.new(2014,8,19)) +        expect(subject.dates[6].date).to eq(Date.new(2014,8,20)) +        expect(subject.dates[7].date).to eq(Date.new(2014,8,25)) +        expect(subject.dates[8].date).to eq(Date.new(2014,8,26)) +        expect(subject.dates[9].date).to eq(Date.new(2014,8,27)) +      end +    end +   +   +    context "with same definition : dsjointed timetable should be empty" do +      before do +        subject.periods.clear +        subject.dates.clear +        subject.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2015,6,1), :period_end => Date.new(2015,6,30)) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2015,6,16), :in_out => true) +        subject.dates << Chouette::TimeTableDate.new( :date => Date.new(2015,6,22), :in_out => false) +        subject.int_day_types = 4|8|16|32|64|128|256 +        another_tt = create(:time_table , :int_day_types => ( 4|8|16|32|64|128|256) ) +        another_tt.periods.clear +        another_tt.dates.clear +        another_tt.periods << Chouette::TimeTablePeriod.new(:period_start => Date.new(2015,6,1), :period_end => Date.new(2015,6,30)) +        another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2015,6,16), :in_out => true) +        another_tt.dates << Chouette::TimeTableDate.new( :date => Date.new(2015,6,22), :in_out => false) +        subject.disjoin! another_tt +        subject.reload +      end +      it "should have same 0 result periods" do +        expect(subject.periods.size).to eq(0) +      end +      it "should have 0 day_types" do +        expect(subject.int_day_types).to eq(0) +      end +      it "should have 0 dates " do +        expect(subject.dates.size).to eq(0) +      end +    end +  end + +  describe "#duplicate" do +      it "should be a copy of" do +        target=subject.duplicate +        expect(target.id).to be_nil +        expect(target.comment).to eq(I18n.t("activerecord.copy", name: subject.comment)) +        expect(target.objectid).to eq(subject.objectid+"_1") +        expect(target.int_day_types).to eq(subject.int_day_types) +        expect(target.dates.size).to eq(subject.dates.size) +        target.dates.each do |d| +          expect(d.time_table_id).to be_nil +        end +        expect(target.periods.size).to eq(subject.periods.size) +        target.periods.each do |p| +          expect(p.time_table_id).to be_nil +        end +      end +  end + +  describe "#tags" do +      it "should accept tags" do +        subject.tag_list = "toto, titi" +        subject.save +        subject.reload +        expect(Chouette::TimeTable.tag_counts.size).to eq(2) +        expect(subject.tag_list.size).to eq(2) +      end +  end + +end diff --git a/spec/models/chouette/timeband_spec.rb b/spec/models/chouette/timeband_spec.rb new file mode 100644 index 000000000..1f812a6e2 --- /dev/null +++ b/spec/models/chouette/timeband_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe Chouette::Timeband, :type => :model do + +  describe '#create' do +    context 'when valid' do +      it { create(:timeband) } +    end + +    context 'when not valid' do +      it 'fails validation with end_time before start_time' do +        timeband = build(:timeband_invalid) +        expect(timeband).to be_invalid +      end +    end +  end + +end diff --git a/spec/models/chouette/transport_mode_spec.rb b/spec/models/chouette/transport_mode_spec.rb new file mode 100644 index 000000000..8f2b2eddb --- /dev/null +++ b/spec/models/chouette/transport_mode_spec.rb @@ -0,0 +1,64 @@ +require 'spec_helper' + +describe Chouette::TransportMode, :type => :model do +   +  def mode(text_code = "test", numerical_code = nil) +    numerical_code ||= 1 if text_code == "test" +    Chouette::TransportMode.new(text_code, numerical_code) +  end + +  describe "#to_i" do +     +    it "should return numerical code" do +      expect(mode("test", 1).to_i).to eq(1) +    end + +  end + +  it "should return true to #test? when text code is 'test'" do +    expect(mode("test")).to be_test +  end + +  it "should be equal when text codes are identical" do +    expect(mode("test",1)).to eq(mode("test", 2)) +  end + +  describe ".new" do + +    it "should find numerical code from text code" do +      expect(mode("unknown").to_i).to eq(0) +    end + +    it "should find text code from numerical code" do +      expect(mode(0)).to be_unknown +    end + +    it "should accept another mode" do +      expect(Chouette::TransportMode.new(mode("test"))).to eq(mode("test")) +    end +     +  end + +  describe "#public_transport?" do +     +    it "should return false for interchange" do +      expect(mode("interchange")).not_to be_public_transport +    end + +    it "should return true for other modes" do +      expect(mode("unknown")).to be_public_transport +    end + +  end + +  describe ".all" do +     +    Chouette::TransportMode.definitions.each do |text_code, numerical_code| +      it "should include a TransportMode #{text_code}" do +        expect(Chouette::TransportMode.all).to include(Chouette::TransportMode.new(text_code)) +      end +    end + +  end + +end diff --git a/spec/models/chouette/trident_active_record_spec.rb b/spec/models/chouette/trident_active_record_spec.rb new file mode 100644 index 000000000..9728a2923 --- /dev/null +++ b/spec/models/chouette/trident_active_record_spec.rb @@ -0,0 +1,115 @@ +require 'spec_helper' + +describe Chouette::TridentActiveRecord, :type => :model do + +  it { expect(Chouette::TridentActiveRecord.ancestors).to include(Chouette::ActiveRecord) } + +  subject { create(:time_table) } + +  describe "#uniq_objectid" do + +    it "should rebuild objectid" do +      tm = create(:time_table) +      tm.objectid = subject.objectid +      tm.uniq_objectid +      expect(tm.objectid).to eq(subject.objectid+"_1") +    end + +    it "should rebuild objectid" do +      tm = create(:time_table) +      tm.objectid = subject.objectid +      tm.uniq_objectid +      tm.save +      tm = create(:time_table) +      tm.objectid = subject.objectid +      tm.uniq_objectid +      expect(tm.objectid).to eq(subject.objectid+"_2") +    end + +  end + +  describe "#prepare_auto_columns" do + +    it "should left objectid" do +      tm = Chouette::TimeTable.new :comment => "merge1" , :objectid => "NINOXE:Timetable:merge1" +      tm.prepare_auto_columns +      expect(tm.objectid).to eq("NINOXE:Timetable:merge1") +    end + +    it "should add pending_id to objectid" do +      tm = Chouette::TimeTable.new :comment => "merge1" +      tm.prepare_auto_columns +      expect(tm.objectid.start_with?("NINOXE:Timetable:__pending_id__")).to be_truthy +    end + +    it "should set id to objectid" do +      tm = Chouette::TimeTable.new :comment => "merge1" +      tm.save +      expect(tm.objectid).to eq("NINOXE:Timetable:"+tm.id.to_s) +    end + +    it "should detect objectid conflicts" do +      tm = Chouette::TimeTable.new :comment => "merge1" +      tm.save +      tm.objectid = "NINOXE:Timetable:"+(tm.id+1).to_s +      tm.save +      tm = Chouette::TimeTable.new :comment => "merge1" +      tm.save +      expect(tm.objectid).to eq("NINOXE:Timetable:"+tm.id.to_s+"_1") +    end + +  end + +  describe "objectid" do + +    it "should build automatic objectid when empty" do +      g1 = Chouette::GroupOfLine.new( :name => "g1") +      g1.save +      expect(g1.objectid).to eq("NINOXE:GroupOfLine:"+g1.id.to_s) +    end + +    it "should build automatic objectid with fixed when only suffix given" do +      g1 = Chouette::GroupOfLine.new( :name => "g1") +      g1.objectid = "toto" +      g1.save +      expect(g1.objectid).to eq("NINOXE:GroupOfLine:toto") +    end +     +    it "should build automatic objectid with extension when already exists" do +      g1 = Chouette::GroupOfLine.new( :name => "g1") +      g1.save +      cnt = g1.id + 1 +      g1.objectid = "NINOXE:GroupOfLine:"+cnt.to_s +      g1.save +      g2 = Chouette::GroupOfLine.new( :name => "g2") +      g2.save +      expect(g2.objectid).to eq("NINOXE:GroupOfLine:"+g2.id.to_s+"_1") +    end +     +    it "should build automatic objectid with extension when already exists" do +      g1 = Chouette::GroupOfLine.new( :name => "g1") +      g1.save +      cnt = g1.id + 2 +      g1.objectid = "NINOXE:GroupOfLine:"+cnt.to_s +      g1.save +      g2 = Chouette::GroupOfLine.new( :name => "g2") +      g2.objectid = "NINOXE:GroupOfLine:"+cnt.to_s+"_1" +      g2.save +      g3 = Chouette::GroupOfLine.new( :name => "g3") +      g3.save +      expect(g3.objectid).to eq("NINOXE:GroupOfLine:"+g3.id.to_s+"_2") +    end +     +    it "should build automatic objectid when id cleared" do +      g1 = Chouette::GroupOfLine.new( :name => "g1") +      g1.objectid = "NINOXE:GroupOfLine:xxxx" +      g1.save +      g1.objectid = nil +      g1.save +      expect(g1.objectid).to eq("NINOXE:GroupOfLine:"+g1.id.to_s) +    end +  end + +end + + diff --git a/spec/models/chouette/vehicle_journey_at_stop_spec.rb b/spec/models/chouette/vehicle_journey_at_stop_spec.rb new file mode 100644 index 000000000..f771e8286 --- /dev/null +++ b/spec/models/chouette/vehicle_journey_at_stop_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' +require 'pp' + +describe Chouette::VehicleJourneyAtStop, :type => :model do +  let!(:vehicle_journey) { create(:vehicle_journey_odd)} +  subject { vehicle_journey.vehicle_journey_at_stops.first } + +  describe "#exceeds_gap?" do +    it "should return false if gap < 1.hour" do +      t1 = 1.minutes.ago +      t2 = 1.minutes.ago + 4.hour +      expect(subject.exceeds_gap?(t1, t2)).to be_truthy +    end +    it "should return false if gap > 2.hour" do +      t1 = 1.minutes.ago +      t2 = 1.minutes.ago + 3.minutes +      expect(subject.exceeds_gap?(t1, t2)).to be_falsey +    end +  end + +  describe "#increasing_times_validate" do +    let(:vjas1){ vehicle_journey.vehicle_journey_at_stops[0]} +    let(:vjas2){ vehicle_journey.vehicle_journey_at_stops[1]} +    context "when vjas#arrival_time exceeds gap" do +      it "should add errors on arrival_time" do +        vjas1.arrival_time = vjas2.arrival_time - 5.hour +        expect(vjas2.increasing_times_validate(vjas1)).to be_falsey +        expect(vjas2.errors).not_to be_empty +        expect(vjas2.errors[:arrival_time]).not_to be_blank +      end +    end +    context "when vjas#departure_time exceeds gap" do +      it "should add errors on departure_time" do +        vjas1.departure_time = vjas2.departure_time - 5.hour +        expect(vjas2.increasing_times_validate(vjas1)).to be_falsey +        expect(vjas2.errors).not_to be_empty +        expect(vjas2.errors[:departure_time]).not_to be_blank +      end +    end +    context "when vjas does'nt exceed gap" do +      it "should not add errors" do +        expect(vjas2.increasing_times_validate(vjas1)).to be_truthy +        expect(vjas2.errors).to be_empty +      end +    end +  end +end  diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb new file mode 100644 index 000000000..87c0f9d92 --- /dev/null +++ b/spec/models/chouette/vehicle_journey_spec.rb @@ -0,0 +1,223 @@ +require 'spec_helper' + +describe Chouette::VehicleJourney, :type => :model do +  subject { create(:vehicle_journey_odd) } + +  describe "in_relation_to_a_journey_pattern methods" do +    let!(:route) { create(:route)} +    let!(:journey_pattern) { create(:journey_pattern, :route => route)} +    let!(:journey_pattern_odd) { create(:journey_pattern_odd, :route => route)} +    let!(:journey_pattern_even) { create(:journey_pattern_even, :route => route)} + +    context "when vehicle_journey is on odd stop whereas selected journey_pattern is on all stops" do +      subject { create(:vehicle_journey, :route => route, :journey_pattern => journey_pattern_odd)} +      describe "#extra_stops_in_relation_to_a_journey_pattern" do +        it "should be empty" do +          expect(subject.extra_stops_in_relation_to_a_journey_pattern( journey_pattern)).to be_empty +        end +      end +      describe "#extra_vjas_in_relation_to_a_journey_pattern" do +        it "should be empty" do +          expect(subject.extra_vjas_in_relation_to_a_journey_pattern( journey_pattern)).to be_empty +        end +      end +      describe "#missing_stops_in_relation_to_a_journey_pattern" do +        it "should return even stops" do +          result = subject.missing_stops_in_relation_to_a_journey_pattern( journey_pattern) +          expect(result).to eq(journey_pattern_even.stop_points) +        end +      end +      describe "#update_journey_pattern" do +        it "should new_record for added vjas" do +          subject.update_journey_pattern( journey_pattern) +          subject.vehicle_journey_at_stops.select{ |vjas| vjas.new_record? }.each do |vjas| +            expect(journey_pattern_even.stop_points).to include( vjas.stop_point) +          end +        end +        it "should add vjas on each even stops" do +          subject.update_journey_pattern( journey_pattern) +          vehicle_stops = subject.vehicle_journey_at_stops.map(&:stop_point) +          journey_pattern_even.stop_points.each do |sp| +            expect(vehicle_stops).to include(sp) +          end +        end +        it "should not mark any vjas as _destroy" do +          subject.update_journey_pattern( journey_pattern) +          expect(subject.vehicle_journey_at_stops.any?{ |vjas| vjas._destroy }).to be_falsey +        end +      end +    end +    context "when vehicle_journey is on all stops whereas selected journey_pattern is on odd stops" do +      subject { create(:vehicle_journey, :route => route, :journey_pattern => journey_pattern)} +      describe "#missing_stops_in_relation_to_a_journey_pattern" do +        it "should be empty" do +          expect(subject.missing_stops_in_relation_to_a_journey_pattern( journey_pattern_odd)).to be_empty +        end +      end +      describe "#extra_stops_in_relation_to_a_journey_pattern" do +        it "should return even stops" do +          result = subject.extra_stops_in_relation_to_a_journey_pattern( journey_pattern_odd) +          expect(result).to eq(journey_pattern_even.stop_points) +        end +      end +      describe "#extra_vjas_in_relation_to_a_journey_pattern" do +        it "should return vjas on even stops" do +          result = subject.extra_vjas_in_relation_to_a_journey_pattern( journey_pattern_odd) +          expect(result.map(&:stop_point)).to eq(journey_pattern_even.stop_points) +        end +      end +      describe "#update_journey_pattern" do +        it "should add no new vjas" do +          subject.update_journey_pattern( journey_pattern_odd) +          expect(subject.vehicle_journey_at_stops.any?{ |vjas| vjas.new_record? }).to be_falsey +        end +        it "should mark vehicle_journey_at_stops as _destroy on even stops" do +          subject.update_journey_pattern( journey_pattern_odd) +          subject.vehicle_journey_at_stops.each { |vjas| +            expect(vjas._destroy).to eq(journey_pattern_even.stop_points.include?(vjas.stop_point)) +          } +        end +      end +    end + +  end +  context "when following departure times exceeds gap" do +    describe "#increasing_times" do +      before(:each) do +        subject.vehicle_journey_at_stops[0].departure_time = subject.vehicle_journey_at_stops[1].departure_time - 5.hour +        subject.vehicle_journey_at_stops[0].arrival_time = subject.vehicle_journey_at_stops[0].departure_time +        subject.vehicle_journey_at_stops[1].arrival_time = subject.vehicle_journey_at_stops[1].departure_time +      end +      it "should make instance invalid" do +        subject.increasing_times +        expect(subject.vehicle_journey_at_stops[1].errors[:departure_time]).not_to be_blank +        expect(subject).not_to be_valid +      end +    end +    describe "#update_attributes" do +      let!(:params){ {"vehicle_journey_at_stops_attributes" => { +            "0"=>{"id" => subject.vehicle_journey_at_stops[0].id ,"arrival_time" => 1.minutes.ago,"departure_time" => 1.minutes.ago}, +            "1"=>{"id" => subject.vehicle_journey_at_stops[1].id, "arrival_time" => (1.minutes.ago + 4.hour),"departure_time" => (1.minutes.ago + 4.hour)} +         }}} +      it "should return false" do +        expect(subject.update_attributes(params)).to be_falsey +      end +      it "should make instance invalid" do +        subject.update_attributes(params) +        expect(subject).not_to be_valid +      end +      it "should let first vjas without any errors" do +        subject.update_attributes(params) +        expect(subject.vehicle_journey_at_stops[0].errors).to be_empty +      end +      it "should add an error on second vjas" do +        subject.update_attributes(params) +        expect(subject.vehicle_journey_at_stops[1].errors[:departure_time]).not_to be_blank +      end +    end +  end + +  context "#time_table_tokens=" do +    let!(:tm1){create(:time_table, :comment => "TM1")} +    let!(:tm2){create(:time_table, :comment => "TM2")} + +    it "should return associated time table ids" do +      subject.update_attributes :time_table_tokens => [tm1.id, tm2.id].join(',') +      expect(subject.time_tables).to include( tm1) +      expect(subject.time_tables).to include( tm2) +    end +  end +  describe "#bounding_dates" do +    before(:each) do +      tm1 = build(:time_table, :dates => +                               [ build(:time_table_date, :date => 1.days.ago.to_date, :in_out => true), +                                 build(:time_table_date, :date => 2.days.ago.to_date, :in_out => true) ]) +      tm2 = build(:time_table, :periods => +                                [ build(:time_table_period, :period_start => 4.days.ago.to_date, :period_end => 3.days.ago.to_date)]) +      tm3 = build(:time_table) +      subject.time_tables = [ tm1, tm2, tm3] +    end +    it "should return min date from associated calendars" do +      expect(subject.bounding_dates.min).to eq(4.days.ago.to_date) +    end +    it "should return max date from associated calendars" do +      expect(subject.bounding_dates.max).to eq(1.days.ago.to_date) +    end +  end +  context "#vehicle_journey_at_stops" do +    it "should be ordered like stop_points on route" do +      route = subject.route +      vj_stop_ids = subject.vehicle_journey_at_stops.map(&:stop_point_id) +      expected_order = route.stop_points.map(&:id).select {|s_id| vj_stop_ids.include?(s_id)} + +      expect(vj_stop_ids).to eq(expected_order) +    end + +  end + +    describe "#transport_mode_name" do + +    def self.legacy_transport_modes +      %w{Air Train LongDistanceTrain LocalTrain RapidTransit Metro Tramway Coach Bus Ferry Waterborne PrivateVehicle Walk Trolleybus Bicycle Shuttle Taxi VAL Other} +    end + +    legacy_transport_modes.each do |transport_mode| +      context "when transport_mode is #{transport_mode}" do +        transport_mode_name = Chouette::TransportMode.new(transport_mode.underscore) +        it "should be #{transport_mode_name}" do +          subject.transport_mode = transport_mode +          expect(subject.transport_mode_name).to eq(transport_mode_name) +        end +      end +    end +    context "when transport_mode is nil" do +      it "should be nil" do +        subject.transport_mode = nil +        expect(subject.transport_mode_name).to be_nil +      end +    end + +  end + +  describe "#transport_mode_name=" do + +    it "should change transport_mode with TransportMode#name" do +      subject.transport_mode_name = "Test" +      expect(subject.transport_mode).to eq("Test") +    end + +  end + +  describe ".transport_mode_names" do + +    it "should not include unknown transport_mode_name" do +      expect(Chouette::VehicleJourney.transport_mode_names).not_to include(Chouette::TransportMode.new("unknown")) +    end + +    it "should not include interchange transport_mode" do +      expect(Chouette::VehicleJourney.transport_mode_names).not_to include(Chouette::TransportMode.new("interchange")) +    end + +  end + +  describe "#footnote_ids=" do +    context "when line have footnotes, " do +      let!( :route) { create( :route ) } +      let!( :line) { route.line } +      let!( :footnote_first) {create( :footnote, :code => "1", :label => "dummy 1", :line => route.line)} +      let!( :footnote_second) {create( :footnote, :code => "2", :label => "dummy 2", :line => route.line)} + + +      it "should update vehicle's footnotes" do +        expect(Chouette::VehicleJourney.find(subject.id).footnotes).to be_empty +        subject.footnote_ids = [ footnote_first.id ] +        subject.save +        expect(Chouette::VehicleJourney.find(subject.id).footnotes.count).to eq(1) +      end + +    end + +  end + +end + diff --git a/spec/presenters/chouette/geometry/general_presenter.rb b/spec/presenters/chouette/geometry/general_presenter.rb new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/spec/presenters/chouette/geometry/general_presenter.rb @@ -0,0 +1 @@ + diff --git a/spec/presenters/chouette/geometry/line_presenter_spec.rb b/spec/presenters/chouette/geometry/line_presenter_spec.rb new file mode 100644 index 000000000..c1432cf57 --- /dev/null +++ b/spec/presenters/chouette/geometry/line_presenter_spec.rb @@ -0,0 +1,13 @@ +require 'spec_helper' + +describe Chouette::Geometry::LinePresenter do +  let!(:line) { create(:line_with_stop_areas_having_parent) } +  subject { Chouette::Geometry::LinePresenter.new(line)} + +  describe "#routes_localized_commercials" do +    it "should return 3 stop_areas" do +      expect(subject.routes_localized_commercials(line.routes.first).size).to eq(10) +    end +  end +end + | 
