rail_feeds 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +3 -0
- data/.rubocop.yml +31 -0
- data/.travis.yml +26 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +6 -0
- data/Guardfile +25 -0
- data/LICENSE.md +32 -0
- data/README.md +77 -0
- data/Rakefile +3 -0
- data/doc/guides/Logging.md +13 -0
- data/doc/guides/Network Rail/CORPUS.md +34 -0
- data/doc/guides/Network Rail/SMART.md +39 -0
- data/doc/guides/Network Rail/Schedule.md +138 -0
- data/file +0 -0
- data/lib/rail_feeds/credentials.rb +45 -0
- data/lib/rail_feeds/logging.rb +51 -0
- data/lib/rail_feeds/network_rail/corpus.rb +77 -0
- data/lib/rail_feeds/network_rail/credentials.rb +22 -0
- data/lib/rail_feeds/network_rail/http_client.rb +57 -0
- data/lib/rail_feeds/network_rail/schedule/association.rb +208 -0
- data/lib/rail_feeds/network_rail/schedule/data.rb +215 -0
- data/lib/rail_feeds/network_rail/schedule/days.rb +95 -0
- data/lib/rail_feeds/network_rail/schedule/fetcher.rb +193 -0
- data/lib/rail_feeds/network_rail/schedule/header/cif.rb +102 -0
- data/lib/rail_feeds/network_rail/schedule/header/json.rb +79 -0
- data/lib/rail_feeds/network_rail/schedule/header.rb +22 -0
- data/lib/rail_feeds/network_rail/schedule/parser/cif.rb +141 -0
- data/lib/rail_feeds/network_rail/schedule/parser/json.rb +87 -0
- data/lib/rail_feeds/network_rail/schedule/parser.rb +108 -0
- data/lib/rail_feeds/network_rail/schedule/stp_indicator.rb +72 -0
- data/lib/rail_feeds/network_rail/schedule/tiploc.rb +100 -0
- data/lib/rail_feeds/network_rail/schedule/train_schedule/change_en_route.rb +158 -0
- data/lib/rail_feeds/network_rail/schedule/train_schedule/location/intermediate.rb +119 -0
- data/lib/rail_feeds/network_rail/schedule/train_schedule/location/origin.rb +91 -0
- data/lib/rail_feeds/network_rail/schedule/train_schedule/location/terminating.rb +72 -0
- data/lib/rail_feeds/network_rail/schedule/train_schedule/location.rb +76 -0
- data/lib/rail_feeds/network_rail/schedule/train_schedule.rb +392 -0
- data/lib/rail_feeds/network_rail/schedule.rb +33 -0
- data/lib/rail_feeds/network_rail/smart.rb +186 -0
- data/lib/rail_feeds/network_rail/stomp_client.rb +77 -0
- data/lib/rail_feeds/network_rail.rb +16 -0
- data/lib/rail_feeds/version.rb +14 -0
- data/lib/rail_feeds.rb +10 -0
- data/rail_feeds.gemspec +32 -0
- data/spec/fixtures/network_rail/schedule/data/full.yaml +60 -0
- data/spec/fixtures/network_rail/schedule/data/starting.yaml +131 -0
- data/spec/fixtures/network_rail/schedule/data/update-gap.yaml +10 -0
- data/spec/fixtures/network_rail/schedule/data/update-next.yaml +13 -0
- data/spec/fixtures/network_rail/schedule/data/update-old.yaml +10 -0
- data/spec/fixtures/network_rail/schedule/data/update.yaml +112 -0
- data/spec/fixtures/network_rail/schedule/parser/train_create.json +1 -0
- data/spec/fixtures/network_rail/schedule/parser/train_delete.json +1 -0
- data/spec/fixtures/network_rail/schedule/train_schedule/json-data.yaml +67 -0
- data/spec/rail_feeds/credentials_spec.rb +46 -0
- data/spec/rail_feeds/logging_spec.rb +81 -0
- data/spec/rail_feeds/network_rail/corpus_spec.rb +92 -0
- data/spec/rail_feeds/network_rail/credentials_spec.rb +22 -0
- data/spec/rail_feeds/network_rail/http_client_spec.rb +88 -0
- data/spec/rail_feeds/network_rail/schedule/association_spec.rb +205 -0
- data/spec/rail_feeds/network_rail/schedule/data_spec.rb +219 -0
- data/spec/rail_feeds/network_rail/schedule/days_shared.rb +99 -0
- data/spec/rail_feeds/network_rail/schedule/days_spec.rb +4 -0
- data/spec/rail_feeds/network_rail/schedule/fetcher_spec.rb +228 -0
- data/spec/rail_feeds/network_rail/schedule/header/cif_spec.rb +72 -0
- data/spec/rail_feeds/network_rail/schedule/header/json_spec.rb +51 -0
- data/spec/rail_feeds/network_rail/schedule/header_spec.rb +19 -0
- data/spec/rail_feeds/network_rail/schedule/parser/cif_spec.rb +197 -0
- data/spec/rail_feeds/network_rail/schedule/parser/json_spec.rb +172 -0
- data/spec/rail_feeds/network_rail/schedule/parser_spec.rb +34 -0
- data/spec/rail_feeds/network_rail/schedule/stp_indicator_shared.rb +49 -0
- data/spec/rail_feeds/network_rail/schedule/stp_indicator_spec.rb +4 -0
- data/spec/rail_feeds/network_rail/schedule/tiploc_spec.rb +77 -0
- data/spec/rail_feeds/network_rail/schedule/train_schedule/change_en_route_spec.rb +121 -0
- data/spec/rail_feeds/network_rail/schedule/train_schedule/location/intermediate_spec.rb +95 -0
- data/spec/rail_feeds/network_rail/schedule/train_schedule/location/origin_spec.rb +87 -0
- data/spec/rail_feeds/network_rail/schedule/train_schedule/location/terminating_spec.rb +81 -0
- data/spec/rail_feeds/network_rail/schedule/train_schedule/location_spec.rb +35 -0
- data/spec/rail_feeds/network_rail/schedule/train_schedule_spec.rb +284 -0
- data/spec/rail_feeds/network_rail/schedule_spec.rb +41 -0
- data/spec/rail_feeds/network_rail/smart_spec.rb +194 -0
- data/spec/rail_feeds/network_rail/stomp_client_spec.rb +151 -0
- data/spec/rail_feeds/network_rail_spec.rb +7 -0
- data/spec/rail_feeds_spec.rb +11 -0
- data/spec/spec_helper.rb +47 -0
- metadata +282 -0
@@ -0,0 +1 @@
|
|
1
|
+
{"JsonScheduleV1":{"CIF_bank_holiday_running":null,"CIF_stp_indicator":"P","CIF_train_uid":"W20309","applicable_timetable":"Y","atoc_code":"IL","new_schedule_segment":{"traction_class":"","uic_code":""},"schedule_days_runs":"1111100","schedule_end_date":"2018-12-07","schedule_segment":{"signalling_id":"2U10","CIF_train_category":"OO","CIF_headcode":"","CIF_course_indicator":1,"CIF_train_service_code":"24627006","CIF_business_sector":"??","CIF_power_type":"EMU","CIF_timing_load":"483","CIF_speed":"045","CIF_operating_characteristics":null,"CIF_train_class":"S","CIF_sleepers":null,"CIF_reservations":null,"CIF_connection_indicator":null,"CIF_catering_code":null,"CIF_service_branding":"","schedule_location":[{"location_type":"LO","record_identity":"LO","tiploc_code":"SHANKLN","tiploc_instance":null,"departure":"0718","public_departure":"0718","platform":null,"line":null,"engineering_allowance":null,"pathing_allowance":null,"performance_allowance":null},{"location_type":"LI","record_identity":"LI","tiploc_code":"LAKEIOW","tiploc_instance":null,"arrival":"0720","departure":"0721","pass":null,"public_arrival":"0720","public_departure":"0721","platform":null,"line":null,"path":null,"engineering_allowance":null,"pathing_allowance":null,"performance_allowance":null},{"location_type":"LI","record_identity":"LI","tiploc_code":"SNDOWN","tiploc_instance":null,"arrival":"0723","departure":"0724","pass":null,"public_arrival":"0723","public_departure":"0724","platform":null,"line":null,"path":null,"engineering_allowance":null,"pathing_allowance":null,"performance_allowance":null},{"location_type":"LI","record_identity":"LI","tiploc_code":"BRDING","tiploc_instance":null,"arrival":"0727H","departure":"0728","pass":null,"public_arrival":"0728","public_departure":"0728","platform":null,"line":null,"path":null,"engineering_allowance":null,"pathing_allowance":null,"performance_allowance":null},{"location_type":"LI","record_identity":"LI","tiploc_code":"SMALBRK","tiploc_instance":null,"arrival":null,"departure":null,"pass":"0732","public_arrival":null,"public_departure":null,"platform":null,"line":null,"path":null,"engineering_allowance":null,"pathing_allowance":null,"performance_allowance":null},{"location_type":"LI","record_identity":"LI","tiploc_code":"RYDS","tiploc_instance":null,"arrival":"0735","departure":"0735H","pass":null,"public_arrival":"0735","public_departure":"0735","platform":null,"line":null,"path":null,"engineering_allowance":null,"pathing_allowance":null,"performance_allowance":null},{"location_type":"LI","record_identity":"LI","tiploc_code":"RYDE","tiploc_instance":null,"arrival":"0738H","departure":"0740","pass":null,"public_arrival":"0739","public_departure":"0740","platform":null,"line":null,"path":null,"engineering_allowance":null,"pathing_allowance":null,"performance_allowance":null},{"location_type":"LT","record_identity":"LT","tiploc_code":"RYDP","tiploc_instance":null,"arrival":"0742","public_arrival":"0742","platform":null,"path":null}]},"schedule_start_date":"2018-05-21","train_status":"P","transaction_type":"Create"}}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"JsonScheduleV1":{"CIF_train_uid":"C77605","schedule_start_date":"2018-08-25","CIF_stp_indicator":"O","transaction_type":"Delete"}}
|
@@ -0,0 +1,67 @@
|
|
1
|
+
---
|
2
|
+
JsonScheduleV1:
|
3
|
+
CIF_bank_holiday_running: B
|
4
|
+
CIF_stp_indicator: P
|
5
|
+
CIF_train_uid: W20309
|
6
|
+
applicable_timetable: Y
|
7
|
+
atoc_code: IL
|
8
|
+
schedule_start_date: "2018-05-21"
|
9
|
+
train_status: S
|
10
|
+
transaction_type: Create
|
11
|
+
schedule_days_runs: "1111100"
|
12
|
+
schedule_end_date: "2018-12-07"
|
13
|
+
new_schedule_segment:
|
14
|
+
traction_class: ""
|
15
|
+
uic_code: "12345"
|
16
|
+
schedule_segment:
|
17
|
+
signalling_id: 2U10
|
18
|
+
CIF_train_category: "OO"
|
19
|
+
CIF_headcode: "1234"
|
20
|
+
CIF_course_indicator:
|
21
|
+
CIF_train_service_code: "24627006"
|
22
|
+
CIF_business_sector: ??
|
23
|
+
CIF_power_type: EMU
|
24
|
+
CIF_timing_load: "483"
|
25
|
+
CIF_speed: "045"
|
26
|
+
CIF_operating_characteristics: OPER-CHAR
|
27
|
+
CIF_train_class: S
|
28
|
+
CIF_sleepers: B
|
29
|
+
CIF_reservations: R
|
30
|
+
CIF_connection_indicator:
|
31
|
+
CIF_catering_code: C
|
32
|
+
CIF_service_branding: Br
|
33
|
+
schedule_location:
|
34
|
+
- location_type: LO
|
35
|
+
record_identity: LO
|
36
|
+
tiploc_code: SHANKLN
|
37
|
+
tiploc_instance:
|
38
|
+
departure: "0718"
|
39
|
+
public_departure: "0718"
|
40
|
+
platform: P
|
41
|
+
line: L
|
42
|
+
engineering_allowance: "1"
|
43
|
+
pathing_allowance: "1H"
|
44
|
+
performance_allowance: "2"
|
45
|
+
- location_type: LI
|
46
|
+
record_identity: LI
|
47
|
+
tiploc_code: LAKEIOW
|
48
|
+
tiploc_instance: nil
|
49
|
+
arrival: "0720"
|
50
|
+
departure: "0721"
|
51
|
+
pass: "0722"
|
52
|
+
public_arrival: "0720"
|
53
|
+
public_departure: "0721"
|
54
|
+
platform: P
|
55
|
+
line: L
|
56
|
+
path: p
|
57
|
+
engineering_allowance:
|
58
|
+
pathing_allowance:
|
59
|
+
performance_allowance:
|
60
|
+
- location_type: LT
|
61
|
+
record_identity: LT
|
62
|
+
tiploc_code: RYDP
|
63
|
+
tiploc_instance:
|
64
|
+
arrival: "0742"
|
65
|
+
public_arrival: "0742"
|
66
|
+
platform: P
|
67
|
+
path: p
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe RailFeeds::Credentials do
|
4
|
+
context 'Using system wide credentials' do
|
5
|
+
before :each do
|
6
|
+
described_class.configure(
|
7
|
+
username: 'user@example.com',
|
8
|
+
password: 'P@55word'
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'Sets class attributes' do
|
13
|
+
expect(described_class.username).to eq 'user@example.com'
|
14
|
+
expect(described_class.password).to eq 'P@55word'
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'A new instance defaults to class attributes' do
|
18
|
+
expect(subject.username).to eq 'user@example.com'
|
19
|
+
expect(subject.password).to eq 'P@55word'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
context 'Using specific credentials' do
|
25
|
+
subject do
|
26
|
+
described_class.new(
|
27
|
+
username: 'user2@example.com',
|
28
|
+
password: 'P@55word2'
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
before :each do
|
33
|
+
described_class.configure username: nil, password: nil
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'Sets instance attributes' do
|
37
|
+
expect(subject.username).to eq 'user2@example.com'
|
38
|
+
expect(subject.password).to eq 'P@55word2'
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'Leaves class attributes alone' do
|
42
|
+
expect(described_class.username).to eq ''
|
43
|
+
expect(described_class.password).to eq ''
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class DummyClassForLoggerTests
|
4
|
+
include RailFeeds::Logging
|
5
|
+
end
|
6
|
+
|
7
|
+
describe RailFeeds::Logging do
|
8
|
+
let(:global_logger) { double Logger }
|
9
|
+
|
10
|
+
it 'Default system logger' do
|
11
|
+
described_class.logger = nil
|
12
|
+
formatter = double Proc
|
13
|
+
expect(described_class).to receive(:formatter).and_return(formatter)
|
14
|
+
expect(Logger).to receive(:new).with(
|
15
|
+
STDOUT,
|
16
|
+
formatter: formatter,
|
17
|
+
level: Logger::DEBUG
|
18
|
+
).and_return(global_logger)
|
19
|
+
expect(described_class.logger).to be global_logger
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'Default system formatter' do
|
23
|
+
subject { described_class.formatter }
|
24
|
+
it 'With all items passed in' do
|
25
|
+
expect(subject.call('s', 'd', 'p', 'm')).to eq "d p s: m\n"
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'Without progname' do
|
29
|
+
expect(subject.call('S', 'D', nil, 'M')).to eq "D S: M\n"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'included in a class' do
|
34
|
+
let(:klass) { DummyClassForLoggerTests }
|
35
|
+
let(:instance) { klass.new }
|
36
|
+
before(:each) do
|
37
|
+
allow(described_class).to receive(:logger).and_return(global_logger)
|
38
|
+
end
|
39
|
+
let(:class_logger) { double Logger }
|
40
|
+
let(:instance_logger) { double Logger }
|
41
|
+
|
42
|
+
describe 'Defaults to using global logger' do
|
43
|
+
it '::logger' do
|
44
|
+
expect(klass.logger).to be global_logger
|
45
|
+
end
|
46
|
+
|
47
|
+
it '#logger' do
|
48
|
+
expect(instance.logger).to be global_logger
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe 'Setting custom logger for class' do
|
53
|
+
before(:each) { klass.logger = class_logger }
|
54
|
+
|
55
|
+
it 'Global logger is unaffected' do
|
56
|
+
expect(described_class.logger).to be global_logger
|
57
|
+
end
|
58
|
+
it 'Instance defaults to using class logger' do
|
59
|
+
expect(instance.logger).to be class_logger
|
60
|
+
end
|
61
|
+
it 'Sets class logger' do
|
62
|
+
expect(klass.logger).to be class_logger
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe 'Setting custom logger for instance' do
|
67
|
+
before(:each) { klass.logger = class_logger }
|
68
|
+
before(:each) { instance.logger = instance_logger }
|
69
|
+
|
70
|
+
it 'Global logger is unaffected' do
|
71
|
+
expect(described_class.logger).to be global_logger
|
72
|
+
end
|
73
|
+
it 'Class logger is unaffected' do
|
74
|
+
expect(klass.logger).to be class_logger
|
75
|
+
end
|
76
|
+
it 'Sets instance logger' do
|
77
|
+
expect(instance.logger).to be instance_logger
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe RailFeeds::NetworkRail::CORPUS do
|
4
|
+
let :json do
|
5
|
+
'{"TIPLOCDATA":[{"NLCDESC":"MERSEYRAIL ELECTRICS","NLC":"8","TIPLOC":"MERSELE","3A' \
|
6
|
+
'LPHA":"ABC","STANOX":"1","NLCDESC16":"MPTE HQ","UIC":"2"},{"NLCDESC":" ","NLC":" ' \
|
7
|
+
'","TIPLOC":" ","3ALPHA":" ","STANOX":" ","NLCDESC16":" ","UIC":" "}]}'
|
8
|
+
end
|
9
|
+
let(:http_client) { double RailFeeds::NetworkRail::HTTPClient }
|
10
|
+
let(:reader) { double Zlib::GzipReader }
|
11
|
+
|
12
|
+
describe '::download' do
|
13
|
+
let(:http_client) { double RailFeeds::NetworkRail::HTTPClient }
|
14
|
+
let(:temp_file) { double Tempfile }
|
15
|
+
|
16
|
+
it 'Using default credentials' do
|
17
|
+
expect(RailFeeds::NetworkRail::HTTPClient).to receive(:new)
|
18
|
+
.with(credentials: RailFeeds::NetworkRail::Credentials).and_return(http_client)
|
19
|
+
expect(http_client).to receive(:download)
|
20
|
+
.with('ntrod/SupportingFileAuthenticate?type=CORPUS', 'file')
|
21
|
+
described_class.download 'file'
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'Using passed credentials' do
|
25
|
+
credentials = double RailFeeds::NetworkRail::Credentials
|
26
|
+
expect(RailFeeds::NetworkRail::HTTPClient).to receive(:new)
|
27
|
+
.with(credentials: credentials).and_return(http_client)
|
28
|
+
expect(http_client).to receive(:download)
|
29
|
+
described_class.download 'file', credentials: credentials
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '::fetch' do
|
34
|
+
let(:http_client) { double RailFeeds::NetworkRail::HTTPClient }
|
35
|
+
let(:temp_file) { double Tempfile }
|
36
|
+
|
37
|
+
it 'Using default credentials' do
|
38
|
+
expect(RailFeeds::NetworkRail::HTTPClient).to receive(:new)
|
39
|
+
.with(credentials: RailFeeds::NetworkRail::Credentials).and_return(http_client)
|
40
|
+
expect(http_client).to receive(:fetch)
|
41
|
+
.with('ntrod/SupportingFileAuthenticate?type=CORPUS').and_return(temp_file)
|
42
|
+
expect(described_class.fetch).to eq temp_file
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'Using passed credentials' do
|
46
|
+
credentials = double RailFeeds::NetworkRail::Credentials
|
47
|
+
expect(RailFeeds::NetworkRail::HTTPClient).to receive(:new)
|
48
|
+
.with(credentials: credentials).and_return(http_client)
|
49
|
+
expect(http_client).to receive(:fetch).and_return(temp_file)
|
50
|
+
expect(described_class.fetch(credentials: credentials)).to eq temp_file
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '::load_file' do
|
55
|
+
it 'json file' do
|
56
|
+
expect(Zlib::GzipReader).to receive(:open).with('filename') { fail Zlib::GzipFile::Error }
|
57
|
+
expect(File).to receive(:read).with('filename').and_return(json)
|
58
|
+
data = described_class.load_file('filename')
|
59
|
+
expect(data[0].tiploc).to eq 'MERSELE'
|
60
|
+
expect(data[0].stanox).to eq 1
|
61
|
+
expect(data[0].crs).to eq 'ABC'
|
62
|
+
expect(data[0].uic).to eq 2
|
63
|
+
expect(data[0].nlc).to eq '8'
|
64
|
+
expect(data[0].nlc_description).to eq 'MERSEYRAIL ELECTRICS'
|
65
|
+
expect(data[0].nlc_short_description).to eq 'MPTE HQ'
|
66
|
+
expect(data[1].tiploc).to be_nil
|
67
|
+
expect(data[1].stanox).to be_nil
|
68
|
+
expect(data[1].crs).to be_nil
|
69
|
+
expect(data[1].uic).to be_nil
|
70
|
+
expect(data[1].nlc).to be_nil
|
71
|
+
expect(data[1].nlc_description).to be_nil
|
72
|
+
expect(data[1].nlc_short_description).to be_nil
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'json.gz file' do
|
76
|
+
expect(Zlib::GzipReader).to receive(:open).with('filename')
|
77
|
+
.and_yield(StringIO.new(json))
|
78
|
+
expect(described_class.load_file('filename').count).to eq 2
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
it '::fetch_data' do
|
83
|
+
expect(RailFeeds::NetworkRail::HTTPClient)
|
84
|
+
.to receive(:new).with(credentials: RailFeeds::NetworkRail::Credentials)
|
85
|
+
.and_return(http_client)
|
86
|
+
expect(http_client).to receive(:fetch_unzipped)
|
87
|
+
.with('ntrod/SupportingFileAuthenticate?type=CORPUS')
|
88
|
+
.and_yield(reader)
|
89
|
+
expect(reader).to receive(:read).and_return(json)
|
90
|
+
expect(described_class.fetch_data.count).to eq 2
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe RailFeeds::NetworkRail::Credentials do
|
4
|
+
subject { described_class.new username: 'user-i', password: 'pass-i' }
|
5
|
+
|
6
|
+
it 'Has seperate values to RailFeeds::Credentials' do
|
7
|
+
described_class.configure username: 'a', password: 'b'
|
8
|
+
expect(described_class.username).to_not eq RailFeeds::Credentials.username
|
9
|
+
expect(described_class.password).to_not eq RailFeeds::Credentials.password
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'Outputs an array' do
|
13
|
+
it '::to_a' do
|
14
|
+
described_class.configure username: 'user', password: 'pass'
|
15
|
+
expect(described_class.to_a).to eq ['user', 'pass']
|
16
|
+
end
|
17
|
+
|
18
|
+
it '#to_a' do
|
19
|
+
expect(subject.to_a).to eq ['user-i', 'pass-i']
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe RailFeeds::NetworkRail::HTTPClient do
|
4
|
+
let(:temp_file) { double Tempfile }
|
5
|
+
let(:uri) { double URI }
|
6
|
+
|
7
|
+
describe '#download' do
|
8
|
+
let(:file) { double File }
|
9
|
+
|
10
|
+
it 'Saves the file' do
|
11
|
+
expect(URI).to receive(:parse).with('https://datafeeds.networkrail.co.uk/path').and_return(uri)
|
12
|
+
expect(uri).to receive(:open).and_return(temp_file)
|
13
|
+
expect(File).to receive(:open).with('file', 'w').and_yield(file)
|
14
|
+
expect(IO).to receive(:copy_stream).with(temp_file, file)
|
15
|
+
subject.download('path', 'file')
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'Adds credentials when getting path' do
|
19
|
+
credentials = RailFeeds::NetworkRail::Credentials.new(
|
20
|
+
username: 'user',
|
21
|
+
password: 'pass'
|
22
|
+
)
|
23
|
+
expect(URI).to receive(:parse).and_return(uri)
|
24
|
+
expect(uri).to receive(:open)
|
25
|
+
.with(http_basic_authentication: ['user', 'pass'])
|
26
|
+
.and_return(temp_file)
|
27
|
+
expect(File).to receive(:open).and_yield(file)
|
28
|
+
expect(IO).to receive(:copy_stream).with(temp_file, file)
|
29
|
+
subject = described_class.new credentials: credentials
|
30
|
+
subject.download('path', 'file')
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'Handles special characters in credentials' do
|
34
|
+
credentials = RailFeeds::NetworkRail::Credentials.new(
|
35
|
+
username: 'a@example.com',
|
36
|
+
password: '!:@'
|
37
|
+
)
|
38
|
+
expect(URI).to receive(:parse).and_return(uri)
|
39
|
+
expect(uri).to receive(:open).and_return(temp_file)
|
40
|
+
expect(File).to receive(:open).and_yield(file)
|
41
|
+
expect(IO).to receive(:copy_stream).with(temp_file, file)
|
42
|
+
subject = described_class.new credentials: credentials
|
43
|
+
expect { subject.download('path', 'file') }.to_not raise_error
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#fetch' do
|
48
|
+
it 'Yields what uri.open does' do
|
49
|
+
expect(URI).to receive(:parse).with('https://datafeeds.networkrail.co.uk/path').and_return(uri)
|
50
|
+
expect(uri).to receive(:open).and_return(temp_file)
|
51
|
+
expect { |a| subject.fetch('path', &a) }.to yield_with_args(temp_file)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'Adds credentials when getting path' do
|
55
|
+
credentials = RailFeeds::NetworkRail::Credentials.new(
|
56
|
+
username: 'user',
|
57
|
+
password: 'pass'
|
58
|
+
)
|
59
|
+
expect(URI).to receive(:parse).and_return(uri)
|
60
|
+
expect(uri).to receive(:open)
|
61
|
+
.with(http_basic_authentication: ['user', 'pass'])
|
62
|
+
.and_return(temp_file)
|
63
|
+
subject = described_class.new credentials: credentials
|
64
|
+
subject.fetch('path') {}
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'Handles special characters in credentials' do
|
68
|
+
credentials = RailFeeds::NetworkRail::Credentials.new(
|
69
|
+
username: 'a@example.com',
|
70
|
+
password: '!:@'
|
71
|
+
)
|
72
|
+
expect(URI).to receive(:parse).and_return(uri)
|
73
|
+
expect(uri).to receive(:open).and_return(temp_file)
|
74
|
+
subject = described_class.new credentials: credentials
|
75
|
+
expect { subject.fetch('path') {} }.to_not raise_error
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#fetch_unzipped' do
|
80
|
+
it 'Returns what Zlib::GzipReader.open does' do
|
81
|
+
reader = double Zlib::GzipReader
|
82
|
+
expect(subject).to receive(:fetch).with('path').and_yield(temp_file)
|
83
|
+
expect(temp_file).to receive(:path).and_return('gz_file_path')
|
84
|
+
expect(Zlib::GzipReader).to receive(:open).with('gz_file_path').and_return(reader)
|
85
|
+
expect { |a| subject.fetch_unzipped('path', &a) }.to yield_with_args(reader)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe RailFeeds::NetworkRail::Schedule::Association do
|
4
|
+
let(:line) do
|
5
|
+
'AANmmmmmmaaaaaa0102030405060101010AcDTTTTTTT12T P'
|
6
|
+
end
|
7
|
+
subject { described_class.from_cif line }
|
8
|
+
|
9
|
+
describe '::from_cif' do
|
10
|
+
it 'Sets attributes' do
|
11
|
+
expect(subject.main_train_uid).to eq 'mmmmmm'
|
12
|
+
expect(subject.associated_train_uid).to eq 'aaaaaa'
|
13
|
+
expect(subject.start_date).to eq Date.new(2001, 2, 3)
|
14
|
+
expect(subject.end_date).to eq Date.new(2004, 5, 6)
|
15
|
+
expect(subject.days).to eq [false, true, false, true, false, true, false]
|
16
|
+
expect(subject.category).to eq 'Ac'
|
17
|
+
expect(subject.date_indicator).to eq 'D'
|
18
|
+
expect(subject.tiploc).to eq 'TTTTTTT'
|
19
|
+
expect(subject.main_location_suffix).to eq 1
|
20
|
+
expect(subject.associated_location_suffix).to eq 2
|
21
|
+
expect(subject.stp_indicator).to eq :permanent
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'Fails for invalid line' do
|
25
|
+
expect { described_class.from_cif('bad line') }
|
26
|
+
.to raise_error ArgumentError, "Invalid line:\nbad line"
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'Delete line' do
|
30
|
+
line = 'AADW43767W43768180529 ASHFKY T C'
|
31
|
+
subject = described_class.from_cif line
|
32
|
+
expect(subject.main_train_uid).to eq 'W43767'
|
33
|
+
expect(subject.associated_train_uid).to eq 'W43768'
|
34
|
+
expect(subject.start_date).to eq Date.new(2018, 5, 29)
|
35
|
+
expect(subject.end_date).to be_nil
|
36
|
+
expect(subject.days).to eq [false, false, false, false, false, false, false]
|
37
|
+
expect(subject.category).to be_nil
|
38
|
+
expect(subject.date_indicator).to be_nil
|
39
|
+
expect(subject.tiploc).to eq 'ASHFKY'
|
40
|
+
expect(subject.main_location_suffix).to be_nil
|
41
|
+
expect(subject.associated_location_suffix).to be_nil
|
42
|
+
expect(subject.stp_indicator).to eq :stp_cancellation
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it '::from_json' do
|
47
|
+
subject = described_class.from_json(
|
48
|
+
'{"JsonAssociationV1":{"transaction_type":"Create","main_train_uid":"C69888",' \
|
49
|
+
'"assoc_train_uid":"C69870","assoc_start_date":"2018-10-08T00:00:00Z",' \
|
50
|
+
'"assoc_end_date":"2018-12-07T00:00:00Z","assoc_days":"1111100","category":"NP",' \
|
51
|
+
'"date_indicator":"S","location":"NTNG","base_location_suffix":null,' \
|
52
|
+
'"assoc_location_suffix":null,"diagram_type":"T","CIF_stp_indicator":"P"}}'
|
53
|
+
)
|
54
|
+
|
55
|
+
expect(subject.main_train_uid).to eq 'C69888'
|
56
|
+
expect(subject.associated_train_uid).to eq 'C69870'
|
57
|
+
expect(subject.start_date).to eq Date.new(2018, 10, 8)
|
58
|
+
expect(subject.end_date).to eq Date.new(2018, 12, 7)
|
59
|
+
expect(subject.days).to eq [true, true, true, true, true, false, false]
|
60
|
+
expect(subject.category).to eq 'NP'
|
61
|
+
expect(subject.date_indicator).to eq 'S'
|
62
|
+
expect(subject.tiploc).to eq 'NTNG'
|
63
|
+
expect(subject.main_location_suffix).to be_nil
|
64
|
+
expect(subject.associated_location_suffix).to be_nil
|
65
|
+
expect(subject.stp_indicator).to eq :permanent
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'Helper methods' do
|
69
|
+
context 'Join association' do
|
70
|
+
before(:each) { subject.category = 'JJ' }
|
71
|
+
it { should be_join }
|
72
|
+
it { should_not be_divide }
|
73
|
+
it { should_not be_next }
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'Divide association' do
|
77
|
+
before(:each) { subject.category = 'VV' }
|
78
|
+
it { should_not be_join }
|
79
|
+
it { should be_divide }
|
80
|
+
it { should_not be_next }
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'Next association' do
|
84
|
+
before(:each) { subject.category = 'NP' }
|
85
|
+
it { should_not be_join }
|
86
|
+
it { should_not be_divide }
|
87
|
+
it { should be_next }
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'Happens on the same day' do
|
91
|
+
before(:each) { subject.date_indicator = 'S' }
|
92
|
+
it { should be_same_day }
|
93
|
+
it { should_not be_over_next_midnight }
|
94
|
+
it { should_not be_over_previous_midnight }
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'Happens over the following midnight' do
|
98
|
+
before(:each) { subject.date_indicator = 'N' }
|
99
|
+
it { should_not be_same_day }
|
100
|
+
it { should be_over_next_midnight }
|
101
|
+
it { should_not be_over_previous_midnight }
|
102
|
+
end
|
103
|
+
|
104
|
+
context 'Happens over the previous midnight' do
|
105
|
+
before(:each) { subject.date_indicator = 'P' }
|
106
|
+
it { should_not be_same_day }
|
107
|
+
it { should_not be_over_next_midnight }
|
108
|
+
it { should be_over_previous_midnight }
|
109
|
+
end
|
110
|
+
|
111
|
+
it '#main_train_event_id' do
|
112
|
+
subject.tiploc = 'TTTTTTT'
|
113
|
+
subject.main_location_suffix = 1
|
114
|
+
expect(subject.main_train_event_id).to eq 'TTTTTTT-1'
|
115
|
+
end
|
116
|
+
|
117
|
+
it '#associated_train_event_id' do
|
118
|
+
subject.tiploc = 'TTTTTTT'
|
119
|
+
subject.associated_location_suffix = 2
|
120
|
+
expect(subject.associated_train_event_id).to eq 'TTTTTTT-2'
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
it '#to_cif' do
|
125
|
+
expect(subject.to_cif).to eq "#{line}\n"
|
126
|
+
end
|
127
|
+
|
128
|
+
it '#to_json' do
|
129
|
+
expect(subject.to_json).to eq '{"JsonAssociationV1":{"transaction_type":"Create",' \
|
130
|
+
'"main_train_uid":"mmmmmm","assoc_train_uid":"aaaaaa",' \
|
131
|
+
'"assoc_start_date":"2001-02-03T00:00:00Z",' \
|
132
|
+
'"assoc_end_date":"2004-05-06T00:00:00Z",' \
|
133
|
+
'"assoc_days":"0101010","category":"Ac",' \
|
134
|
+
'"date_indicator":"D","location":"TTTTTTT",' \
|
135
|
+
'"base_location_suffix":1,"assoc_location_suffix":2,' \
|
136
|
+
'"diagram_type":"T","CIF_stp_indicator":"P"}}'
|
137
|
+
end
|
138
|
+
|
139
|
+
describe '#hash' do
|
140
|
+
it 'Uses tiploc, main_location_suffix and associated_location_suffix' do
|
141
|
+
subject.tiploc = 'TIPLOC'
|
142
|
+
subject.main_location_suffix = 1
|
143
|
+
subject.associated_location_suffix = 2
|
144
|
+
expect(subject.hash).to eq 'TIPLOC-1-2'
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe '#<=>' do
|
149
|
+
let(:association1) { described_class.new start_date: Date.new(2000, 1, 1) }
|
150
|
+
let(:association2) { described_class.new start_date: Date.new(2000, 1, 2) }
|
151
|
+
|
152
|
+
it 'Match' do
|
153
|
+
association2.start_date = Date.new 2000, 1, 1
|
154
|
+
expect(association1 <=> association2).to eq 0
|
155
|
+
expect(association2 <=> association1).to eq 0
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'Doesn\'t match' do
|
159
|
+
expect(association1 <=> association2).to eq(-1)
|
160
|
+
expect(association2 <=> association1).to eq 1
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'Compares to nil without error' do
|
164
|
+
expect { association1 <=> nil }.to_not raise_error
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe '#==' do
|
169
|
+
let(:other_association) { double described_class }
|
170
|
+
before :each do
|
171
|
+
subject.tiploc = 'TTTTTTT'
|
172
|
+
subject.main_location_suffix = 1
|
173
|
+
subject.associated_location_suffix = 2
|
174
|
+
allow(other_association).to receive(:main_train_event_id).and_return(nil)
|
175
|
+
allow(other_association).to receive(:associated_train_event_id).and_return(nil)
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'Matches on neither main or associated train\'s event' do
|
179
|
+
expect(subject == other_association).to eq false
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'Matches on main train\'s event only' do
|
183
|
+
allow(other_association).to receive(:main_train_event_id).and_return('TTTTTTT-1')
|
184
|
+
expect(subject == other_association).to eq false
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'Matches on associated train\'s event only' do
|
188
|
+
allow(other_association).to receive(:associated_train_event_id).and_return('TTTTTTT-2')
|
189
|
+
expect(subject == other_association).to eq false
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'Matches on both main and associated train\'s event' do
|
193
|
+
allow(other_association).to receive(:main_train_event_id).and_return('TTTTTTT-1')
|
194
|
+
allow(other_association).to receive(:associated_train_event_id).and_return('TTTTTTT-2')
|
195
|
+
expect(subject == other_association).to eq true
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'Compares to nil without error' do
|
199
|
+
expect(subject).to_not eq nil
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
it_behaves_like 'it has an STP indicator'
|
204
|
+
it_behaves_like 'it has a days array'
|
205
|
+
end
|