omgdav 0.0.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.
- data/.document +3 -0
- data/.gitignore +17 -0
- data/.manifest +53 -0
- data/.wrongdoc.yml +6 -0
- data/COPYING +661 -0
- data/ChangeLog +185 -0
- data/GIT-VERSION-FILE +1 -0
- data/GIT-VERSION-GEN +33 -0
- data/GNUmakefile +35 -0
- data/LATEST +1 -0
- data/NEWS +1 -0
- data/README +154 -0
- data/bin/omgdav-setup +4 -0
- data/bin/omgdav-sync +32 -0
- data/lib/omgdav/app.rb +70 -0
- data/lib/omgdav/copy.rb +100 -0
- data/lib/omgdav/copy_move.rb +54 -0
- data/lib/omgdav/db.rb +258 -0
- data/lib/omgdav/delete.rb +66 -0
- data/lib/omgdav/get.rb +35 -0
- data/lib/omgdav/http_get.rb +146 -0
- data/lib/omgdav/input_wrapper.rb +32 -0
- data/lib/omgdav/migrations/0001_initial.rb +45 -0
- data/lib/omgdav/migrations/0002_contenttype.rb +15 -0
- data/lib/omgdav/migrations/0003_synctmp.rb +14 -0
- data/lib/omgdav/mkcol.rb +28 -0
- data/lib/omgdav/move.rb +74 -0
- data/lib/omgdav/options.rb +21 -0
- data/lib/omgdav/propfind.rb +46 -0
- data/lib/omgdav/propfind_response.rb +150 -0
- data/lib/omgdav/proppatch.rb +116 -0
- data/lib/omgdav/put.rb +110 -0
- data/lib/omgdav/rack_util.rb +56 -0
- data/lib/omgdav/setup.rb +16 -0
- data/lib/omgdav/sync.rb +78 -0
- data/lib/omgdav/version.rb +2 -0
- data/lib/omgdav.rb +27 -0
- data/omgdav.gemspec +35 -0
- data/pkg.mk +175 -0
- data/setup.rb +1586 -0
- data/test/integration.rb +232 -0
- data/test/test_copy.rb +121 -0
- data/test/test_delete.rb +15 -0
- data/test/test_litmus.rb +61 -0
- data/test/test_move.rb +66 -0
- data/test/test_omgdav_app.rb +102 -0
- data/test/test_propfind.rb +30 -0
- data/test/test_proppatch.rb +156 -0
- data/test/test_put.rb +31 -0
- data/test/test_readonly.rb +22 -0
- data/test/test_sync.rb +49 -0
- data/test/test_urlmap.rb +59 -0
- data/test/test_worm.rb +26 -0
- metadata +342 -0
data/test/integration.rb
ADDED
@@ -0,0 +1,232 @@
|
|
1
|
+
# Copyright (C) 2012, Eric Wong <normalperson@yhbt.net>
|
2
|
+
# License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt)
|
3
|
+
$stdout.sync = $stderr.sync = true
|
4
|
+
Thread.abort_on_exception = true
|
5
|
+
if ENV["COVERAGE"]
|
6
|
+
require "coverage"
|
7
|
+
Coverage.start
|
8
|
+
at_exit do
|
9
|
+
# Dirty little text formatter. I tried simplecov but the default
|
10
|
+
# HTML+JS is unusable without a GUI (I hate GUIs :P) and it would've
|
11
|
+
# taken me longer to search the Internets to find a plain-text
|
12
|
+
# formatter I like...
|
13
|
+
res = Coverage.result
|
14
|
+
relevant = res.keys.grep(%r{/lib/omgdav/\w+\.rb})
|
15
|
+
relevant.each do |file|
|
16
|
+
cov = res[file]
|
17
|
+
puts "==> #{file} <=="
|
18
|
+
File.readlines(file).each_with_index do |line, i|
|
19
|
+
n = cov[i]
|
20
|
+
if n == 0 # BAD
|
21
|
+
print(" *** 0 #{line}")
|
22
|
+
elsif n
|
23
|
+
printf("% 7u %s", n, line)
|
24
|
+
elsif line =~ /\S/ # probably a line with just "end" in it
|
25
|
+
print(" #{line}")
|
26
|
+
else # blank line
|
27
|
+
print "\n" # don't output trailing whitespace on blank lines
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'pp'
|
35
|
+
require 'minitest/autorun'
|
36
|
+
require 'net/http'
|
37
|
+
require 'uri'
|
38
|
+
require 'tempfile'
|
39
|
+
require 'stringio'
|
40
|
+
require 'logger'
|
41
|
+
require 'mogilefs'
|
42
|
+
require 'open-uri'
|
43
|
+
require 'rack'
|
44
|
+
require 'rack/mock'
|
45
|
+
require 'omgdav/app'
|
46
|
+
require 'nokogiri'
|
47
|
+
require 'time'
|
48
|
+
require 'json'
|
49
|
+
Sequel.extension(:migration)
|
50
|
+
|
51
|
+
module TestMogileFSIntegration
|
52
|
+
def x(*cmd)
|
53
|
+
out = Tempfile.new("out")
|
54
|
+
err = Tempfile.new("err")
|
55
|
+
puts cmd.join(' ') if $VERBOSE
|
56
|
+
pid = fork do
|
57
|
+
$stderr.reopen(err.path, "a")
|
58
|
+
$stdout.reopen(out.path, "a")
|
59
|
+
out.close
|
60
|
+
err.close
|
61
|
+
ObjectSpace.each_object(Tempfile) do |tmp|
|
62
|
+
next if tmp.closed?
|
63
|
+
ObjectSpace.undefine_finalizer(tmp)
|
64
|
+
tmp.close_on_exec = true if tmp.respond_to?(:close_on_exec=)
|
65
|
+
end
|
66
|
+
exec(*cmd)
|
67
|
+
end
|
68
|
+
_, status = Process.waitpid2(pid)
|
69
|
+
out.rewind
|
70
|
+
err.rewind
|
71
|
+
[ status, out, err ]
|
72
|
+
end
|
73
|
+
|
74
|
+
def x!(*cmd)
|
75
|
+
status, out, err = x(*cmd)
|
76
|
+
assert status.success?, "#{status.inspect} / #{out.read} / #{err.read}"
|
77
|
+
[ status, out, err ]
|
78
|
+
end
|
79
|
+
|
80
|
+
def setup_mogilefs(plugins = nil, mogstored = "mogstored")
|
81
|
+
@test_host = "127.0.0.1"
|
82
|
+
setup_mogstored(mogstored)
|
83
|
+
@tracker = TCPServer.new(@test_host, 0)
|
84
|
+
@tracker_port = @tracker.addr[1]
|
85
|
+
|
86
|
+
@dbname = Tempfile.new(["mogfresh", ".sqlite3"])
|
87
|
+
@mogilefsd_conf = Tempfile.new(["mogilefsd", "conf"])
|
88
|
+
@mogilefsd_pid = Tempfile.new(["mogilefsd", "pid"])
|
89
|
+
|
90
|
+
cmd = %w(mogdbsetup --yes --type=SQLite --dbname) << @dbname.path
|
91
|
+
x!(*cmd)
|
92
|
+
|
93
|
+
@mogilefsd_conf.puts "db_dsn DBI:SQLite:#{@dbname.path}"
|
94
|
+
@mogilefsd_conf.write <<EOF
|
95
|
+
conf_port #@tracker_port
|
96
|
+
listen #@test_host
|
97
|
+
pidfile #{@mogilefsd_pid.path}
|
98
|
+
replicate_jobs 1
|
99
|
+
fsck_jobs 1
|
100
|
+
query_jobs 1
|
101
|
+
mogstored_stream_port #@mogstored_mgmt_port
|
102
|
+
node_timeout 10
|
103
|
+
EOF
|
104
|
+
@mogilefsd_conf.flush
|
105
|
+
|
106
|
+
@trackers = @hosts = [ "#@test_host:#@tracker_port" ]
|
107
|
+
@tracker.close
|
108
|
+
x!("mogilefsd", "--daemon", "--config=#{@mogilefsd_conf.path}")
|
109
|
+
wait_for_port @tracker_port
|
110
|
+
@admin = MogileFS::Admin.new(:hosts => @hosts)
|
111
|
+
500.times do
|
112
|
+
break if File.size(@mogstored_pid.path) > 0
|
113
|
+
sleep 0.05
|
114
|
+
end
|
115
|
+
|
116
|
+
args = { :ip => @test_host, :port => @mogstored_http_port }
|
117
|
+
args[:status] = "alive"
|
118
|
+
@admin.create_host("me", args)
|
119
|
+
yield_for_monitor_update { @admin.get_hosts.empty? or break }
|
120
|
+
|
121
|
+
wait_for_usage_file "dev1"
|
122
|
+
wait_for_usage_file "dev2"
|
123
|
+
@admin.create_device("me", 1)
|
124
|
+
@admin.create_device("me", 2)
|
125
|
+
@admin.create_domain("testdom")
|
126
|
+
yield_for_monitor_update { @admin.get_devices.size == 2 and break }
|
127
|
+
@mogc = MogileFS::MogileFS.new(:hosts => @hosts, :domain => "testdom")
|
128
|
+
end
|
129
|
+
|
130
|
+
def yield_for_monitor_update
|
131
|
+
50.times do
|
132
|
+
yield
|
133
|
+
sleep 0.1
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def wait_for_port(port)
|
138
|
+
tries = 500
|
139
|
+
begin
|
140
|
+
TCPSocket.new(@test_host, port).close
|
141
|
+
return
|
142
|
+
rescue
|
143
|
+
sleep 0.05
|
144
|
+
end while (tries -= 1) > 0
|
145
|
+
raise "#@test_host:#{port} never became ready"
|
146
|
+
end
|
147
|
+
|
148
|
+
def teardown
|
149
|
+
if @mogstored_pid
|
150
|
+
pid = File.read(@mogstored_pid.path).to_i
|
151
|
+
Process.kill(:TERM, pid) if pid > 0
|
152
|
+
end
|
153
|
+
if @mogilefsd_pid
|
154
|
+
s = TCPSocket.new(@test_host, @tracker_port)
|
155
|
+
s.write "!shutdown\r\n"
|
156
|
+
s.close
|
157
|
+
end
|
158
|
+
FileUtils.rmtree(@docroot)
|
159
|
+
end
|
160
|
+
|
161
|
+
def wait_for_usage_file(device)
|
162
|
+
uri = URI("http://#@test_host:#@mogstored_http_port/#{device}/usage")
|
163
|
+
res = nil
|
164
|
+
100.times do
|
165
|
+
res = Net::HTTP.get_response(uri)
|
166
|
+
if Net::HTTPOK === res
|
167
|
+
puts res.body if $DEBUG
|
168
|
+
return
|
169
|
+
end
|
170
|
+
puts res.inspect if $DEBUG
|
171
|
+
sleep 0.1
|
172
|
+
end
|
173
|
+
raise "#{uri} failed to appear: #{res.inspect}"
|
174
|
+
end
|
175
|
+
|
176
|
+
def setup_mogstored(mogstored = "mogstored")
|
177
|
+
@docroot = Dir.mktmpdir(["mogfresh", "docroot"])
|
178
|
+
Dir.mkdir("#@docroot/dev1")
|
179
|
+
Dir.mkdir("#@docroot/dev2")
|
180
|
+
@mogstored_mgmt = TCPServer.new(@test_host, 0)
|
181
|
+
@mogstored_http = TCPServer.new(@test_host, 0)
|
182
|
+
@mogstored_mgmt_port = @mogstored_mgmt.addr[1]
|
183
|
+
@mogstored_http_port = @mogstored_http.addr[1]
|
184
|
+
@mogstored_conf = Tempfile.new(["mogstored", "conf"])
|
185
|
+
@mogstored_pid = Tempfile.new(["mogstored", "pid"])
|
186
|
+
@mogstored_conf.write <<EOF
|
187
|
+
pidfile = #{@mogstored_pid.path}
|
188
|
+
maxconns = 500
|
189
|
+
httplisten = #@test_host:#@mogstored_http_port
|
190
|
+
mgmtlisten = #@test_host:#@mogstored_mgmt_port
|
191
|
+
docroot = #@docroot
|
192
|
+
EOF
|
193
|
+
@mogstored_conf.flush
|
194
|
+
@mogstored_mgmt.close
|
195
|
+
@mogstored_http.close
|
196
|
+
|
197
|
+
x!(mogstored, "--daemon", "--config=#{@mogstored_conf.path}")
|
198
|
+
wait_for_port @mogstored_mgmt_port
|
199
|
+
wait_for_port @mogstored_http_port
|
200
|
+
end
|
201
|
+
|
202
|
+
def setup
|
203
|
+
setup_mogilefs
|
204
|
+
@err = StringIO.new
|
205
|
+
logger = Logger.new(@err)
|
206
|
+
logger = Logger.new($stderr)
|
207
|
+
@opts = { "rack.logger" => logger }
|
208
|
+
@db_file = Tempfile.new(%w(omgdav .sqlite))
|
209
|
+
@db = Sequel.connect("sqlite://#{@db_file.path}")
|
210
|
+
@db.pragma_set(:synchronous, :off)
|
211
|
+
@migdir = File.dirname(File.dirname(__FILE__)) + "/lib/omgdav/migrations"
|
212
|
+
Sequel::Migrator.apply(@db, @migdir, nil)
|
213
|
+
@app = OMGDAV::App.new(@db, @mogc)
|
214
|
+
@req = Rack::MockRequest.new(@app)
|
215
|
+
end
|
216
|
+
|
217
|
+
def body_string(body)
|
218
|
+
rv = ""
|
219
|
+
body.each { |chunk| rv << chunk }
|
220
|
+
rv
|
221
|
+
end
|
222
|
+
|
223
|
+
# Rack::MockRequest doesn't always work since it converts the
|
224
|
+
# response into an array without dup-ing Strings, so response
|
225
|
+
# bodies which reuse a buffer (when they assume they're writing
|
226
|
+
# to a socket) fail
|
227
|
+
def req(method, uri, opts = {})
|
228
|
+
o = { method: method }.merge!(opts)
|
229
|
+
env = @req.class.env_for(uri, o)
|
230
|
+
@app.call(env)
|
231
|
+
end
|
232
|
+
end
|
data/test/test_copy.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# Copyright (C) 2012, Eric Wong <normalperson@yhbt.net>
|
3
|
+
# License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt)
|
4
|
+
require './test/integration'
|
5
|
+
class TestCopy < MiniTest::Unit::TestCase
|
6
|
+
include TestMogileFSIntegration
|
7
|
+
|
8
|
+
def test_copy_file
|
9
|
+
# create a file
|
10
|
+
resp = @req.put("/hello", input: StringIO.new("HELLO"))
|
11
|
+
assert_equal 201, resp.status.to_i
|
12
|
+
|
13
|
+
resp = @req.put("/goodbye", input: StringIO.new("GOODBYE"))
|
14
|
+
assert_equal 201, resp.status.to_i
|
15
|
+
|
16
|
+
opts = {
|
17
|
+
"HTTP_DESTINATION" => 'http://example.com/bar',
|
18
|
+
"HTTP_HOST" => 'example.com',
|
19
|
+
}
|
20
|
+
status, _, _ = req("COPY", "/hello", opts)
|
21
|
+
assert_equal 201, status.to_i
|
22
|
+
|
23
|
+
status, _, body = req("GET", "/bar")
|
24
|
+
assert_equal 200, status.to_i
|
25
|
+
assert_equal "HELLO", body_string(body)
|
26
|
+
|
27
|
+
opts = {
|
28
|
+
"HTTP_DESTINATION" => 'http://example.com/bar',
|
29
|
+
"HTTP_HOST" => 'example.com',
|
30
|
+
}
|
31
|
+
status, _, _ = req("COPY", "/goodbye", opts)
|
32
|
+
assert_equal 204, status.to_i
|
33
|
+
|
34
|
+
status, _, body = req("GET", "/bar")
|
35
|
+
assert_equal 200, status.to_i
|
36
|
+
assert_equal "GOODBYE", body_string(body)
|
37
|
+
|
38
|
+
opts = {
|
39
|
+
"HTTP_DESTINATION" => 'http://example.com/bar',
|
40
|
+
"HTTP_HOST" => 'example.com',
|
41
|
+
}
|
42
|
+
status, _, _ = req("COPY", "/bar", opts)
|
43
|
+
assert_equal 403, status.to_i
|
44
|
+
|
45
|
+
opts = {
|
46
|
+
"HTTP_DESTINATION" => 'http://example.com/a/b',
|
47
|
+
"HTTP_HOST" => 'example.com:80',
|
48
|
+
}
|
49
|
+
status, _, _ = req("COPY", "/bar", opts)
|
50
|
+
assert_equal 409, status.to_i
|
51
|
+
|
52
|
+
assert_equal 201, req("MKCOL", "/a")[0].to_i
|
53
|
+
|
54
|
+
opts = {
|
55
|
+
"HTTP_DESTINATION" => 'http://example.com/a/b',
|
56
|
+
"HTTP_HOST" => 'example.com:80',
|
57
|
+
}
|
58
|
+
status, _, _ = req("COPY", "/bar", opts)
|
59
|
+
assert_equal 201, status.to_i
|
60
|
+
status, _, body = req("GET", "/a/b")
|
61
|
+
assert_equal 200, status.to_i
|
62
|
+
assert_equal "GOODBYE", body_string(body)
|
63
|
+
|
64
|
+
opts = {
|
65
|
+
'HTTP_DESTINATION' => 'http://example.com/a/b',
|
66
|
+
'HTTP_HOST' => 'example.com',
|
67
|
+
'HTTP_OVERWRITE' => 'F',
|
68
|
+
}
|
69
|
+
status, _, _ = req("COPY", "/hello", opts)
|
70
|
+
assert_equal 412, status.to_i
|
71
|
+
status, _, body = req("GET", "/a/b")
|
72
|
+
assert_equal 200, status.to_i
|
73
|
+
assert_equal "GOODBYE", body_string(body)
|
74
|
+
|
75
|
+
opts = {
|
76
|
+
'HTTP_DESTINATION' => 'http://www.example.com/a/b',
|
77
|
+
'HTTP_HOST' => 'example.com',
|
78
|
+
}
|
79
|
+
assert_equal 502, req("COPY", "/hello", opts)[0].to_i
|
80
|
+
|
81
|
+
opts = {
|
82
|
+
'HTTP_DESTINATION' => 'http://example.com/a/b',
|
83
|
+
'HTTP_HOST' => 'example.com:8080',
|
84
|
+
}
|
85
|
+
assert_equal 502, req("COPY", "/hello", opts)[0].to_i
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_copy_collection_empty
|
89
|
+
assert_equal 201, req("MKCOL", "/a")[0].to_i
|
90
|
+
|
91
|
+
opts = {
|
92
|
+
'HTTP_DESTINATION' => 'http://example.com/b',
|
93
|
+
'HTTP_HOST' => 'example.com',
|
94
|
+
}
|
95
|
+
assert_equal 201, req("COPY", "/a", opts)[0].to_i
|
96
|
+
assert @db[:paths][name: "b"][:collection]
|
97
|
+
|
98
|
+
assert_equal 204, req("COPY", "/a", opts)[0].to_i
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_copy_collection_full
|
102
|
+
assert_equal 201, req("MKCOL", "/a")[0].to_i
|
103
|
+
assert_equal 201, req("MKCOL", "/a/b")[0].to_i
|
104
|
+
assert_equal 201, @req.put("/a/c", input: StringIO.new("a")).status.to_i
|
105
|
+
assert_equal 201, @req.put("/a/b/c", input: StringIO.new("C")).status.to_i
|
106
|
+
|
107
|
+
opts = {
|
108
|
+
'HTTP_DESTINATION' => 'http://example.com/b',
|
109
|
+
'HTTP_HOST' => 'example.com',
|
110
|
+
}
|
111
|
+
assert_equal 201, req("COPY", "/a", opts)[0].to_i
|
112
|
+
|
113
|
+
status, _, body = req("GET", "/b/c")
|
114
|
+
assert_equal 200, status.to_i
|
115
|
+
assert_equal "a", body_string(body)
|
116
|
+
|
117
|
+
status, _, body = req("GET", "/b/b/c")
|
118
|
+
assert_equal 200, status.to_i
|
119
|
+
assert_equal "C", body_string(body)
|
120
|
+
end
|
121
|
+
end
|
data/test/test_delete.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# Copyright (C) 2012, Eric Wong <normalperson@yhbt.net>
|
3
|
+
# License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt)
|
4
|
+
require './test/integration'
|
5
|
+
class TestDelete < MiniTest::Unit::TestCase
|
6
|
+
include TestMogileFSIntegration
|
7
|
+
|
8
|
+
def test_delete
|
9
|
+
status, _, _ = req("DELETE", "/non-existent")
|
10
|
+
assert_equal 404, status.to_i
|
11
|
+
|
12
|
+
status, _, _ = req("DELETE", "/")
|
13
|
+
assert_equal 403, status.to_i
|
14
|
+
end
|
15
|
+
end
|
data/test/test_litmus.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# Copyright (C) 2012, Eric Wong <normalperson@yhbt.net>
|
2
|
+
# License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt)
|
3
|
+
require "./test/integration"
|
4
|
+
require "unicorn"
|
5
|
+
|
6
|
+
LITMUS_DIR = ENV["LITMUS_DIR"]
|
7
|
+
class TestLitmus < MiniTest::Unit::TestCase
|
8
|
+
include TestMogileFSIntegration
|
9
|
+
|
10
|
+
def setup
|
11
|
+
super
|
12
|
+
@uni_err = Tempfile.new(%w(stderr .log))
|
13
|
+
@ru = Tempfile.new(%w(omgdav .ru))
|
14
|
+
@ru.sync = true
|
15
|
+
@ru.write <<EOF
|
16
|
+
require 'omgdav/app'
|
17
|
+
db = Sequel.connect("sqlite://#{@db_file.path}")
|
18
|
+
db.pragma_set(:synchronous, :off)
|
19
|
+
mogc = MogileFS::MogileFS.new(hosts: #{@hosts.inspect}, domain: "testdom")
|
20
|
+
run OMGDAV::App.new(db, mogc)
|
21
|
+
EOF
|
22
|
+
uni_sock = TCPServer.new(@test_host, 0)
|
23
|
+
port = uni_sock.addr[1]
|
24
|
+
@url = "http://#@test_host:#{port}/"
|
25
|
+
@uni_pid = fork do
|
26
|
+
ENV["UNICORN_FD"] = uni_sock.fileno.to_s
|
27
|
+
ENV["RACK_ENV"] = "deployment"
|
28
|
+
$stderr.reopen(@uni_err.path, "a")
|
29
|
+
uni = Unicorn::HttpServer.new(@app, listeners: ["#@test_host:#{port}"])
|
30
|
+
uni.start.join
|
31
|
+
end
|
32
|
+
uni_sock.close
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_litmus
|
36
|
+
cmd = %W(#{ENV["MAKE"] || 'make'} -C #{LITMUS_DIR} URL=#@url check)
|
37
|
+
out = Tempfile.new("err")
|
38
|
+
err = Tempfile.new("out")
|
39
|
+
pid = fork do
|
40
|
+
$stdout.reopen(out)
|
41
|
+
$stderr.reopen(err)
|
42
|
+
exec(*cmd)
|
43
|
+
end
|
44
|
+
_, status = Process.waitpid2(pid)
|
45
|
+
if $DEBUG || $VERBOSE
|
46
|
+
out.rewind
|
47
|
+
err.rewind
|
48
|
+
puts out.read
|
49
|
+
puts err.read
|
50
|
+
end
|
51
|
+
assert status.success?, status.inspect
|
52
|
+
end
|
53
|
+
|
54
|
+
def teardown
|
55
|
+
Process.kill(:QUIT, @uni_pid)
|
56
|
+
_, status = Process.waitpid2(@uni_pid)
|
57
|
+
assert status.success?, status.inspect
|
58
|
+
puts @uni_err.read if $DEBUG || $VERBOSE
|
59
|
+
super
|
60
|
+
end
|
61
|
+
end if LITMUS_DIR
|
data/test/test_move.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# Copyright (C) 2012, Eric Wong <normalperson@yhbt.net>
|
3
|
+
# License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt)
|
4
|
+
require './test/integration'
|
5
|
+
class TestMove < MiniTest::Unit::TestCase
|
6
|
+
include TestMogileFSIntegration
|
7
|
+
|
8
|
+
def test_move_file
|
9
|
+
# create a file
|
10
|
+
resp = @req.put("/hello", input: StringIO.new("HELLO"))
|
11
|
+
assert_equal 201, resp.status.to_i
|
12
|
+
resp = @req.put("/goodbye", input: StringIO.new("GOODBYE"))
|
13
|
+
assert_equal 201, resp.status.to_i
|
14
|
+
|
15
|
+
opts = {
|
16
|
+
"HTTP_DESTINATION" => 'http://example.com/bar',
|
17
|
+
"HTTP_HOST" => 'example.com',
|
18
|
+
}
|
19
|
+
status, _, _ = req("MOVE", "/hello", opts)
|
20
|
+
assert_equal 201, status.to_i
|
21
|
+
|
22
|
+
status, headers, body = req("GET", "/bar")
|
23
|
+
assert_equal 200, status.to_i
|
24
|
+
assert_equal "5", headers["Content-Length"]
|
25
|
+
assert_equal "HELLO", body_string(body)
|
26
|
+
|
27
|
+
assert_equal 404, req("GET", "/hello")[0].to_i
|
28
|
+
|
29
|
+
opts = {
|
30
|
+
"HTTP_DESTINATION" => 'http://example.com/bar',
|
31
|
+
"HTTP_HOST" => 'example.com',
|
32
|
+
}
|
33
|
+
status, _, _ = req("MOVE", "/goodbye", opts)
|
34
|
+
assert_equal 204, status.to_i
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_move_collection
|
38
|
+
assert_equal 201, req("MKCOL", "/a")[0].to_i
|
39
|
+
assert_equal 201, req("MKCOL", "/a/b")[0].to_i
|
40
|
+
(0..9).each do |i|
|
41
|
+
input = StringIO.new(i.to_s)
|
42
|
+
assert_equal 201, @req.put("/a/#{i}", input: input).status.to_i
|
43
|
+
input.rewind
|
44
|
+
assert_equal 201, @req.put("/a/b/#{i}", input: input).status.to_i
|
45
|
+
end
|
46
|
+
|
47
|
+
opts = {
|
48
|
+
"HTTP_DESTINATION" => 'http://example.com/c',
|
49
|
+
"HTTP_HOST" => 'example.com',
|
50
|
+
}
|
51
|
+
status, _, _ = req("MOVE", "/a", opts)
|
52
|
+
assert_equal 201, status.to_i
|
53
|
+
(0..9).each do |i|
|
54
|
+
assert_equal 404, @req.get("/a/#{i}").status.to_i
|
55
|
+
assert_equal 404, @req.get("/a/b/#{i}").status.to_i
|
56
|
+
|
57
|
+
status, _, body = req("GET", "/c/#{i}")
|
58
|
+
assert_equal 200, status.to_i
|
59
|
+
assert_equal i.to_s, body_string(body)
|
60
|
+
|
61
|
+
status, _, body = req("GET", "/c/b/#{i}")
|
62
|
+
assert_equal 200, status.to_i
|
63
|
+
assert_equal i.to_s, body_string(body)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# Copyright (C) 2012, Eric Wong <normalperson@yhbt.net>
|
3
|
+
# License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt)
|
4
|
+
require './test/integration'
|
5
|
+
class Test_OMGDAV_App < MiniTest::Unit::TestCase
|
6
|
+
include TestMogileFSIntegration
|
7
|
+
|
8
|
+
def test_basic
|
9
|
+
# create a file
|
10
|
+
resp = @req.put("/foo", input: StringIO.new("HELLO"))
|
11
|
+
assert_equal 201, resp.status.to_i
|
12
|
+
|
13
|
+
# make sure it got into MogileFS
|
14
|
+
info = @mogc.file_info("foo")
|
15
|
+
assert_equal 5, info["length"]
|
16
|
+
assert_equal "HELLO", @mogc.get_file_data("foo")
|
17
|
+
|
18
|
+
# ensure we can get it back
|
19
|
+
status, headers, body = req("GET", "/foo")
|
20
|
+
assert_equal 200, status.to_i
|
21
|
+
assert_equal "5", headers["Content-Length"]
|
22
|
+
assert_equal "HELLO", body_string(body)
|
23
|
+
mtime = Time.parse(headers["Last-Modified"])
|
24
|
+
assert_nil body.close
|
25
|
+
|
26
|
+
# ensure we can get a range back
|
27
|
+
status, headers, body = req("GET", "/foo", "HTTP_RANGE" => "bytes=1-")
|
28
|
+
assert_equal 206, status.to_i
|
29
|
+
assert_equal "ELLO", body_string(body)
|
30
|
+
assert_nil body.close
|
31
|
+
|
32
|
+
status, headers, body = req("PROPFIND", "/foo", "HTTP_DEPTH" => "0")
|
33
|
+
assert_equal 207, status.to_i
|
34
|
+
assert_match(%r{\Atext/xml\b}, headers["Content-Type"])
|
35
|
+
foo0 = body_string(body)
|
36
|
+
|
37
|
+
status, headers, body = req("PROPFIND", "/foo", "HTTP_DEPTH" => "1")
|
38
|
+
assert_equal 207, status.to_i
|
39
|
+
assert_match(%r{\Atext/xml\b}, headers["Content-Type"])
|
40
|
+
foo1 = body_string(body)
|
41
|
+
assert_equal foo0, foo1
|
42
|
+
|
43
|
+
# ensure we can get a PROPFIND with Depth: 0
|
44
|
+
status, headers, body = req("PROPFIND", "/", "HTTP_DEPTH" => "0")
|
45
|
+
assert_equal 207, status.to_i
|
46
|
+
xml = Nokogiri::XML(body_string(body))
|
47
|
+
responses = xml.search("//D:multistatus/D:response")
|
48
|
+
assert_equal 1, responses.size
|
49
|
+
root = responses[0]
|
50
|
+
assert_equal "/", root.search("//D:href").text
|
51
|
+
assert_equal 1, root.search(".//D:collection").size
|
52
|
+
|
53
|
+
# PROPFIND with Depth:1
|
54
|
+
status, headers, body = req("PROPFIND", "/", "HTTP_DEPTH" => "1")
|
55
|
+
assert_equal 207, status.to_i
|
56
|
+
xml = Nokogiri::XML(body = body_string(body))
|
57
|
+
responses = xml.search("//D:multistatus/D:response")
|
58
|
+
assert_equal 2, responses.size
|
59
|
+
assert_equal root.to_s, responses[0].to_s
|
60
|
+
foo = responses[1]
|
61
|
+
refute_equal foo.to_s, root.to_s
|
62
|
+
assert_equal "/foo", foo.search(".//D:href").text
|
63
|
+
clen = foo.search(".//lp1:getcontentlength", "lp1" => "DAV:")
|
64
|
+
assert_equal '5', clen.text
|
65
|
+
modified = foo.search(".//lp1:getlastmodified", "lp1" => "DAV:")
|
66
|
+
assert_equal mtime, Time.httpdate(modified.text)
|
67
|
+
created= foo.search(".//lp1:creationdate", "lp1" => "DAV:")
|
68
|
+
assert_equal mtime, Time.xmlschema(created.text)
|
69
|
+
assert_empty foo.search(".//D:collection")
|
70
|
+
|
71
|
+
# PROPFIND with Depth:infinity
|
72
|
+
status, headers, body = req("PROPFIND", "/", "HTTP_DEPTH" => "infinity")
|
73
|
+
assert_equal 400, status.to_i
|
74
|
+
|
75
|
+
# PROPFIND with unspecified Depth (assumed infinity)
|
76
|
+
status, headers, body = req("PROPFIND", "/")
|
77
|
+
assert_equal 400, status.to_i
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_deep_delete
|
81
|
+
assert_equal 201, req("MKCOL", "/a")[0].to_i
|
82
|
+
assert_equal 201, req("MKCOL", "/a/b")[0].to_i
|
83
|
+
assert_equal 201, req("MKCOL", "/a/b/c")[0].to_i
|
84
|
+
assert_equal 201, req("MKCOL", "/a/b/c/d")[0].to_i
|
85
|
+
range = (0..9)
|
86
|
+
range.each do |i|
|
87
|
+
resp = @req.put("/a/b/c/d/#{i}", input: StringIO.new("#{i}"))
|
88
|
+
assert_equal 201, resp.status.to_i
|
89
|
+
assert @mogc.exist?("a/b/c/d/#{i}")
|
90
|
+
|
91
|
+
resp = @req.put("/a/#{i}", input: StringIO.new("#{i}"))
|
92
|
+
assert_equal 201, resp.status.to_i
|
93
|
+
assert @mogc.exist?("a/#{i}")
|
94
|
+
end
|
95
|
+
assert_equal 204, req("DELETE", "/a")[0].to_i
|
96
|
+
range.each do |i|
|
97
|
+
refute @mogc.exist?("a/b/c/d/#{i}")
|
98
|
+
refute @mogc.exist?("a/#{i}")
|
99
|
+
end
|
100
|
+
assert_equal [ @app.root_node ], @db[:paths].to_a
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# Copyright (C) 2012, Eric Wong <normalperson@yhbt.net>
|
3
|
+
# License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt)
|
4
|
+
require './test/integration'
|
5
|
+
class TestPropfind < MiniTest::Unit::TestCase
|
6
|
+
include TestMogileFSIntegration
|
7
|
+
|
8
|
+
def test_invalid_input
|
9
|
+
opts = {
|
10
|
+
"HTTP_DEPTH" => "0",
|
11
|
+
input: StringIO.new("<foo>bar")
|
12
|
+
}
|
13
|
+
assert_equal 400, req("PROPFIND", "/", opts)[0].to_i
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_dir
|
17
|
+
opts = { "HTTP_DEPTH" => "0" }
|
18
|
+
assert_equal 201, req("MKCOL", "/a")[0].to_i
|
19
|
+
|
20
|
+
status, _, body = req("PROPFIND", "/a", opts)
|
21
|
+
assert_equal 207, status.to_i
|
22
|
+
a0 = body_string(body)
|
23
|
+
|
24
|
+
opts["HTTP_DEPTH"] = "1"
|
25
|
+
status, _, body = req("PROPFIND", "/a", opts)
|
26
|
+
assert_equal 207, status.to_i
|
27
|
+
a1 = body_string(body)
|
28
|
+
assert_equal a0, a1
|
29
|
+
end
|
30
|
+
end
|