fargo 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,104 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fargo::Protocol::PeerDownload do
4
+ let(:conn) {
5
+ helper_object(Fargo::Protocol::Peer).tap do |conn|
6
+ conn.client = Fargo::Client.new
7
+ conn.instance_variable_set '@other_nick', 'foobar'
8
+ end
9
+ }
10
+
11
+ shared_examples_for 'a downloader' do |command, data|
12
+ it "downloads a file correctly after receiving: #{command}" do
13
+ conn.stub(:send_message)
14
+
15
+ conn.begin_download!
16
+ conn.receive_data command
17
+ conn.receive_data data
18
+
19
+ file = conn.client.config.download_dir + '/foobar/file'
20
+ file.should have_contents('0123456789')
21
+ end
22
+ end
23
+
24
+ before :each do
25
+ conn.stub :set_comm_inactivity_timeout
26
+ conn.post_init
27
+ conn.download = Fargo::Download.new 'foobar', 'path/to/file', 'tth', 100, 0
28
+ end
29
+
30
+ describe 'the standard DC protocol for downloading' do
31
+ before :each do
32
+ conn.instance_variable_set '@client_extensions', []
33
+ end
34
+
35
+ it "requests a download via the $Get command" do
36
+ conn.should_receive(:send_message).with('Get', "path/to/file$1")
37
+
38
+ conn.begin_download!
39
+ end
40
+
41
+ it "requests the file to be sent after the $FileLength command" do
42
+ conn.stub(:send_message).with('Get', "path/to/file$1")
43
+ conn.should_receive(:send_message).with('Send')
44
+
45
+ conn.begin_download!
46
+ conn.receive_data '$FileLength 10|'
47
+ end
48
+
49
+ it_should_behave_like 'a downloader', '$FileLength 10|', '0123456789'
50
+ end
51
+
52
+ describe 'the ADC protocol for downloading' do
53
+ before :each do
54
+ conn.instance_variable_set '@client_extensions', ['ADCGet']
55
+ end
56
+
57
+ context "with zlib compression" do
58
+ before :each do
59
+ conn.instance_variable_get('@client_extensions') << 'ZLIG'
60
+ end
61
+
62
+ it "requests a download via the $ADCGET command with ZL1" do
63
+ conn.should_receive(:send_message).with('ADCGET',
64
+ 'file path/to/file 0 100 ZL1')
65
+
66
+ conn.begin_download!
67
+ end
68
+
69
+ it_should_behave_like 'a downloader',
70
+ '$ADCSND file path/to/file 0 10 ZL1|',
71
+ Zlib::Deflate.deflate('0123456789')
72
+
73
+ it_should_behave_like 'a downloader',
74
+ '$ADCSND file path/to/file 0 10|', '0123456789'
75
+ end
76
+
77
+ context "without zlib compression" do
78
+ it "requests a download via the $ADCGET command with ZL1" do
79
+ conn.should_receive(:send_message).with('ADCGET',
80
+ 'file path/to/file 0 100')
81
+
82
+ conn.begin_download!
83
+ end
84
+
85
+ it_should_behave_like 'a downloader',
86
+ '$ADCSND file path/to/file 0 10|', '0123456789'
87
+ end
88
+
89
+ context "with TTHF enabled" do
90
+ before :each do
91
+ conn.instance_variable_get('@client_extensions') << 'TTHF'
92
+ end
93
+
94
+ it "requests a download via $ADCGET with the tth of the file" do
95
+ conn.should_receive(:send_message).with('ADCGET', 'file TTH/tth 0 100')
96
+
97
+ conn.begin_download!
98
+ end
99
+
100
+ it_should_behave_like 'a downloader',
101
+ '$ADCSND file TTH/tth 0 10|', '0123456789'
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fargo::Protocol::Peer do
4
+
5
+ let(:client) { Fargo::Client.new }
6
+
7
+ let(:conn) {
8
+ helper_object(described_class).tap do |conn|
9
+ conn.stub(:generate_lock).and_return ['lock', 'pk']
10
+ conn.stub :set_comm_inactivity_timeout
11
+ conn.post_init
12
+ conn.client = client
13
+ end
14
+ }
15
+
16
+ include Fargo::Utils
17
+
18
+ describe "the client to client handshake" do
19
+ before :each do
20
+ client.config.nick = 'fargo'
21
+ end
22
+
23
+ it "responds with all the correct info when the remote nick is received" do
24
+ conn.should_receive(:send_data).with('$MyNick fargo|').ordered
25
+ conn.should_receive(:send_data).with(/^\$Lock \w+ Pk=\w+\|$/).ordered
26
+ conn.should_receive(:send_data).with(/^\$Supports (\w+ ?)+\|$/).ordered
27
+ conn.should_receive(:send_data).with(
28
+ /^\$Direction (Download|Upload) \d+\|$/).ordered
29
+ conn.should_receive(:send_data).with(
30
+ "$Key #{generate_key('lock')}|").ordered
31
+
32
+ conn.receive_data '$MyNick foobar|$Lock lock Pk=pk|'
33
+ end
34
+
35
+ it "responds correctly when the nick is sent first" do
36
+ conn.should_receive(:send_data).with('$MyNick fargo|').ordered
37
+ conn.should_receive(:send_data).with('$Lock lock Pk=pk|').ordered
38
+
39
+ conn.send_lock
40
+
41
+ conn.should_receive(:send_data).with(/^\$Supports (\w+ ?)+\|$/).ordered
42
+ conn.should_receive(:send_data).with(
43
+ /^\$Direction (Download|Upload) \d+\|$/).ordered
44
+ conn.should_receive(:send_data).with(
45
+ "$Key #{generate_key('lock')}|").ordered
46
+
47
+ conn.receive_data '$MyNick foobar|$Lock lock Pk=pk|$Supports a|' +
48
+ "$Direction Download 100|$Key #{generate_key('lock')}|"
49
+ end
50
+ end
51
+
52
+ it "disconnects with an appropriate message" do
53
+ client.channel.should_receive(:<<).with(
54
+ [:peer_disconnected, instance_of(Hash)])
55
+
56
+ conn.unbind
57
+ end
58
+ end
@@ -0,0 +1,128 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fargo::Protocol::PeerUpload do
4
+ let(:conn) {
5
+ helper_object(Fargo::Protocol::Peer).tap do |conn|
6
+ conn.client = Fargo::Client.new
7
+ end
8
+ }
9
+ let(:file) { Fargo.config.download_dir + '/file' }
10
+
11
+ before :each do
12
+ File.open(file, 'w'){ |f| f << '0123456789' }
13
+ conn.client.share_directory Fargo.config.download_dir
14
+ conn.stub(:set_comm_inactivity_timeout)
15
+ conn.stub(:get_outbound_data_size).and_return 0
16
+ conn.post_init
17
+ conn.instance_variable_set '@handshake_step', 5
18
+ end
19
+
20
+ describe "the standard DC protocol using $Get" do
21
+ it "sends the $FileLength command with the length of the file requested" do
22
+ conn.should_receive(:send_message).with('FileLength', 10)
23
+
24
+ conn.receive_data '$Get tmp/file$1|'
25
+ end
26
+
27
+ it "sends the $FileLength discounting the offset" do
28
+ conn.should_receive(:send_message).with('FileLength', 10)
29
+
30
+ conn.receive_data '$Get tmp/file$5|'
31
+ end
32
+
33
+ it "uploads the file after receiving $Send" do
34
+ conn.stub(:send_message)
35
+ conn.receive_data '$Get tmp/file$1|'
36
+ conn.should_receive(:send_data).with('0123456789')
37
+ conn.receive_data '$Send|'
38
+ end
39
+ end
40
+
41
+ describe "uploading via the ADC protocol" do
42
+ it "sends the $ADCSND command with ZL1 if requested" do
43
+ conn.should_receive(:send_message).with('ADCSND',
44
+ 'file tmp/file 0 10 ZL1')
45
+ conn.stub(:begin_streaming)
46
+
47
+ conn.receive_data '$ADCGET file tmp/file 0 -1 ZL1|'
48
+ end
49
+
50
+ it "sends the $ADCSND command without ZL1 when not requested" do
51
+ conn.should_receive(:send_message).with('ADCSND', 'file tmp/file 0 10')
52
+ conn.stub(:begin_streaming)
53
+
54
+ conn.receive_data '$ADCGET file tmp/file 0 -1|'
55
+ end
56
+
57
+ it "immediately begins uploading the file" do
58
+ conn.stub(:send_message)
59
+ conn.should_receive(:send_data).with '0123'
60
+
61
+ conn.receive_data '$ADCGET file tmp/file 0 4|'
62
+ end
63
+
64
+ it "compresses the sent data with zlib if requested" do
65
+ conn.stub(:send_message)
66
+ conn.should_receive(:send_data).with Zlib::Deflate.deflate('0123')
67
+
68
+ conn.receive_data '$ADCGET file tmp/file 0 4 ZL1|'
69
+ end
70
+ end
71
+
72
+ describe "huge files" do
73
+ let(:size) { 16 * 1024 * 2 + 100 }
74
+
75
+ before :each do
76
+ File.open(file + '2', 'w'){ |f| f << ('a' * size) }
77
+ conn.client.share_directory Fargo.config.download_dir
78
+ end
79
+
80
+ it "are uploaded efficiently" do
81
+ conn.stub(:send_message)
82
+ conn.should_receive(:send_data).with('a' * 16 * 1024).twice.ordered
83
+ conn.should_receive(:send_data).with('a' * 100).ordered
84
+
85
+ conn.receive_data '$ADCGET file tmp/file2 0 -1|'
86
+ end
87
+
88
+ it "compresses successfully" do
89
+ sent_data = ''
90
+ conn.stub(:send_message)
91
+ conn.stub(:send_data){ |d| sent_data << d }
92
+
93
+ conn.receive_data '$ADCGET file tmp/file2 0 -1 ZL1|'
94
+ sent_data.should == Zlib::Deflate.deflate('a' * size)
95
+ end
96
+ end
97
+
98
+ it "sends $MaxedOut when the client has no slots" do
99
+ conn.client.take_slot! while conn.client.open_upload_slots > 0
100
+
101
+ conn.should_receive(:send_message).with('MaxedOut')
102
+
103
+ conn.receive_data '$Get tmp/file$1|'
104
+ end
105
+
106
+ it "doesn't sent $MaxedOut if the requested file is the file list" do
107
+ conn.client.take_slot! while conn.client.open_upload_slots > 0
108
+
109
+ conn.stub(:send_message)
110
+ conn.should_receive(:send_message).with('MaxedOut').never
111
+ conn.stub(:begin_streaming)
112
+
113
+ conn.receive_data '$Get files.xml.bz2$1|'
114
+ end
115
+
116
+ it "closes the connection when a file list is finished uploading" do
117
+ conn.stub(:send_data)
118
+ conn.should_receive(:close_connection_after_writing)
119
+
120
+ conn.receive_data '$ADCGET file files.xml.bz2 0 -1|'
121
+ end
122
+
123
+ it "sends $Error when the requested file does not exist" do
124
+ conn.should_receive(:send_message).with('Error', 'File Not Available')
125
+
126
+ conn.receive_data '$Get tmp/nonexistent$1|'
127
+ end
128
+ end
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec::Matchers.define :match_hash do |hash|
4
+ match do |search|
5
+ search.matches? hash
6
+ end
7
+
8
+ failure_message_for_should do |search|
9
+ "#{search.to_s} should have matched the hash: #{hash.inspect}"
10
+ end
11
+ end
12
+
13
+ RSpec::Matchers.define :match_file do |file|
14
+ match do |search|
15
+ search.matches? :file => file
16
+ end
17
+
18
+ failure_message_for_should do |search|
19
+ "#{search.to_s} should have matched the file: #{file}"
20
+ end
21
+ end
22
+
23
+ describe Fargo::Search do
24
+ it "matches based on file names" do
25
+ subject.query = 'foo bar'
26
+
27
+ subject.should match_file('foobar.mkv')
28
+ subject.should match_file('foo/bar.avi')
29
+ subject.should_not match_file('foo.avi')
30
+ subject.should_not match_file('bar.jpg')
31
+ end
32
+
33
+ it "filters based off of the maximum size of the file" do
34
+ subject.query = 'foo'
35
+ subject.size = 100
36
+ subject.size_restricted = true
37
+
38
+ subject.should match_hash(:file => 'foo', :size => 50)
39
+ subject.should_not match_hash(:file => 'foo', :size => 101)
40
+ end
41
+
42
+ it "filters based off of the minimum size of the file" do
43
+ subject.query = 'foo'
44
+ subject.size = 100
45
+ subject.size_restricted = true
46
+ subject.is_minimum_size = true
47
+
48
+ subject.should_not match_hash(:file => 'foo', :size => 50)
49
+ subject.should match_hash(:file => 'foo', :size => 101)
50
+ end
51
+
52
+ it "matches based on TTH values" do
53
+ subject.query = 'TTH:foobar'
54
+ subject.filetype = Fargo::Search::TTH
55
+
56
+ subject.should match_hash(:tth => 'foobar')
57
+ end
58
+
59
+ it "doesn't match on tth if not specified" do
60
+ subject.query = 'TTH:foobar'
61
+
62
+ subject.should_not match_hash(:tth => 'foobar')
63
+ end
64
+
65
+ it "only matches anything with a filetype of ANY" do
66
+ subject.query = 'foo bar baz'
67
+ subject.filetype = Fargo::Search::ANY
68
+
69
+ subject.should match_file('foo/bar.baz')
70
+ subject.should match_file('foo/bar/baz')
71
+ subject.should match_file('foo/a/barb/bazc')
72
+ subject.should match_file('foo.bar.baz')
73
+
74
+ subject.should_not match_file('foo.bar')
75
+ subject.should_not match_file('foo.babaz')
76
+ subject.should_not match_file('bar/baz')
77
+ end
78
+
79
+ it "only matches audio with a filetype of AUDIO" do
80
+ subject.query = 'foo'
81
+ subject.filetype = Fargo::Search::AUDIO
82
+
83
+ subject.should match_file('foo.mp3')
84
+ subject.should match_file('foo.wav')
85
+ subject.should match_file('foo.flac')
86
+ subject.should match_file('foo.m4a')
87
+
88
+ subject.should_not match_file('foo.avi')
89
+ subject.should_not match_file('foo.jpg')
90
+ subject.should_not match_file('foo.mov')
91
+ end
92
+
93
+ it "only matches videos with a filetype of VIDEOS" do
94
+ subject.query = 'foo'
95
+ subject.filetype = Fargo::Search::VIDEO
96
+
97
+ subject.should match_file('foobar.mkv')
98
+ subject.should match_file('foo/bar.avi')
99
+ subject.should match_file('foobar.mov')
100
+ subject.should match_file('foobar.mpeg')
101
+ subject.should match_file('foobar.mpg')
102
+
103
+ subject.should_not match_file('foobar')
104
+ subject.should_not match_file('foobar.jpg')
105
+ subject.should_not match_file('foo.bar')
106
+ end
107
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fargo::Supports::Chat, :type => :em do
4
+ let(:client) { Fargo::Client.new }
5
+
6
+ it "keeps a log of all messages sent" do
7
+ client.channel << [:chat, 'this is a chat!']
8
+ client.messages.should == ['this is a chat!']
9
+
10
+ client.channel << [:chat, 'another chat']
11
+ client.messages.should == ['this is a chat!', 'another chat']
12
+ end
13
+
14
+ it "keeps a log of all private messages sent" do
15
+ client.channel << [:privmsg, {:from => 'foo', :msg => 'this is a chat!'}]
16
+ client.channel << [:privmsg, {:from => 'foo', :msg => 'another'}]
17
+
18
+ client.messages_with('foo').should == [
19
+ {:from => 'foo', :msg => 'this is a chat!'},
20
+ {:from => 'foo', :msg => 'another'}
21
+ ]
22
+ end
23
+
24
+ it "clears all the messages once the hub disconnects" do
25
+ client.channel << [:chat, 'this is a chat!']
26
+ client.channel << [:privmsg, {:from => 'foo', :msg => 'this is a chat!'}]
27
+
28
+ client.channel << [:hub_disconnected, 'foobar!']
29
+
30
+ client.messages.should == []
31
+ client.messages_with('foo').should == []
32
+ end
33
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fargo::Supports::LocalFileList do
4
+
5
+ before :each do
6
+ @root = Fargo.config.download_dir + '/shared'
7
+ FileUtils.mkdir_p @root
8
+ File.open(@root + '/a', 'w'){ |f| f << 'a' }
9
+ File.open(@root + '/b', 'w'){ |f| f << 'c' }
10
+ FileUtils.mkdir @root + '/c'
11
+ File.open(@root + '/c/d', 'w'){ |f| f << 'd' }
12
+
13
+ @client = Fargo::Client.new
14
+ @client.config.override_share_size = nil
15
+ end
16
+
17
+ it "maintains a list of local files, recursively searching folders" do
18
+ @client.share_directory @root
19
+
20
+ hash = @client.local_file_list
21
+ hash['shared'].should be_a(Hash)
22
+ hash['shared']['a'].should be_a(Fargo::Listing)
23
+ hash['shared']['b'].should be_a(Fargo::Listing)
24
+ hash['shared']['c'].should be_a(Hash)
25
+ hash['shared']['c']['d'].should be_a(Fargo::Listing)
26
+
27
+ hash['shared']['a'].name.should == 'shared/a'
28
+ hash['shared']['b'].name.should == 'shared/b'
29
+ hash['shared']['c']['d'].name.should == 'shared/c/d'
30
+ end
31
+
32
+ it "caches the size of each file shared" do
33
+ @client.share_directory @root
34
+
35
+ @client.share_size.should == 3 # 3 bytes, one in each file
36
+ end
37
+
38
+ it "allows overwriting the published share size just for fun" do
39
+ @client.config.override_share_size = 100
40
+
41
+ @client.share_size.should == 100
42
+ end
43
+
44
+ it "correctly creates an array of listings that it's sharing" do
45
+ @client.share_directory @root
46
+
47
+ listings = @client.local_listings
48
+
49
+ listings.size.should == 3
50
+
51
+ listings.shift.name.should == 'shared/a'
52
+ listings.shift.name.should == 'shared/b'
53
+ listings.shift.name.should == 'shared/c/d'
54
+ end
55
+
56
+ it "finds listings correctly when given their name" do
57
+ @client.share_directory @root
58
+
59
+ ret_val = @client.listing_for 'shared/a'
60
+ ret_val.should be_a(Fargo::Listing)
61
+ ret_val.name.should == 'shared/a'
62
+ end
63
+
64
+ it "also finds listings based on their TTH value" do
65
+ @client.share_directory @root
66
+ listing = @client.local_listings[0]
67
+
68
+ @client.listing_for('TTH/' + listing.tth).should == listing
69
+ end
70
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fargo::Supports::NickList, :type => :em do
4
+
5
+ before :each do
6
+ @client = Fargo::Client.new
7
+ end
8
+
9
+ it 'gets the most up to date information about the nick' do
10
+ @client.should_receive(:info).with('nick').twice
11
+ @client.nick_has_slot? 'nick' # Triggers info the first time
12
+ @client.nick_has_slot? 'nick' # Make sure we didn't cache the results
13
+ end
14
+
15
+ it "uses results from search queries for open slot information" do
16
+ @client.channel << [:search_result, {:nick => 'nick', :open_slots => 0}]
17
+ @client.nick_has_slot?('nick').should be_false
18
+
19
+ @client.channel << [:search_result, {:nick => 'nick', :open_slots => 1}]
20
+ @client.nick_has_slot?('nick').should be_true
21
+
22
+ t = Time.now + 20.minutes
23
+ Time.stub(:now).and_return t
24
+ @client.stub(:info).with('nick').and_return nil
25
+ @client.nick_has_slot?('nick').should be_false
26
+ end
27
+
28
+ it "gets the nick list from the subscribed channel" do
29
+ @client.nicks.should == []
30
+
31
+ @client.channel << [:nick_list, {:nicks => ['a', 'b']}]
32
+
33
+ @client.nicks.should == ['a', 'b']
34
+ end
35
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fargo::Utils do
4
+
5
+ let(:helper) {
6
+ o = Object.new
7
+ o.send(:class_eval) do
8
+ include Fargo::Utils
9
+ end
10
+ o
11
+ }
12
+
13
+ it "generates a correct key" do
14
+ helper.generate_key('FOO').should == "4\220/%DCN000%/"
15
+ end
16
+
17
+ it "generates a correct lock and pk" do
18
+ # there's not a whole lot of documentation about this online...
19
+ 20.times do
20
+ lock, pk = helper.generate_lock
21
+ pk.size.should >= 0
22
+ lock.size.should >= 0
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'rspec/core'
5
+ require 'fargo'
6
+
7
+ download_dir = File.expand_path '../tmp', __FILE__
8
+
9
+ Fargo.logger.level = ActiveSupport::BufferedLogger::WARN
10
+
11
+ RSpec.configure do |c|
12
+ c.color_enabled = true
13
+
14
+ c.before :each do
15
+ Fargo.configure do |config|
16
+ config.download_dir = download_dir
17
+ config.config_dir = download_dir + '/config'
18
+ config.nick = 'fargo'
19
+ config.override_share_size = nil
20
+ config.upload_slots = 4
21
+ end
22
+ end
23
+
24
+ c.around :each do |example|
25
+ FileUtils.mkdir_p download_dir
26
+ example.run
27
+ FileUtils.rm_rf download_dir
28
+ end
29
+
30
+ c.around :each, :type => :em do |example|
31
+ EventMachine.run {
32
+ example.run
33
+
34
+ EventMachine.stop_event_loop
35
+ }
36
+ end
37
+ end
38
+
39
+ Dir[File.dirname(__FILE__) + '/support/*.rb'].each { |f| load f }
40
+
41
+ def helper_object mod
42
+ o = Object.new
43
+ o.send(:class_eval) do
44
+ include mod
45
+ end
46
+ o
47
+ end
@@ -0,0 +1,17 @@
1
+ RSpec::Matchers.define :have_contents do |contents|
2
+ match do |file|
3
+ File.file?(file) && File.read(file).chomp.should == contents.chomp
4
+ end
5
+
6
+ failure_message do |file|
7
+ msg = "Expected #{file} to contain:\n\t#{contents.gsub("\n", "\n\t")}\nWhen"
8
+
9
+ if File.file?(file)
10
+ msg += " it contained:\n#{File.read(file)}".gsub("\n", "\n\t")
11
+ else
12
+ msg += ' it did not exist.'
13
+ end
14
+
15
+ msg
16
+ end
17
+ end