async-caldav 1.2.3 → 1.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.
- checksums.yaml +4 -4
- data/lib/async/caldav/client/addressbook.rb +21 -9
- data/lib/async/caldav/client/calendar.rb +39 -15
- data/lib/async/caldav/client.rb +423 -371
- data/lib/async/caldav/forward_auth.rb +61 -54
- data/lib/async/caldav/handlers/delete.rb +28 -30
- data/lib/async/caldav/handlers/get.rb +45 -47
- data/lib/async/caldav/handlers/head.rb +16 -18
- data/lib/async/caldav/handlers/mkcol.rb +64 -68
- data/lib/async/caldav/handlers/move.rb +99 -90
- data/lib/async/caldav/handlers/options.rb +15 -17
- data/lib/async/caldav/handlers/propfind.rb +140 -139
- data/lib/async/caldav/handlers/proppatch.rb +95 -90
- data/lib/async/caldav/handlers/put.rb +139 -119
- data/lib/async/caldav/handlers/report.rb +192 -161
- data/lib/async/caldav/server.rb +1029 -994
- data/lib/async/caldav/storage/filesystem.rb +247 -220
- data/lib/async/caldav/storage/mock.rb +254 -246
- data/lib/async/caldav/version.rb +1 -1
- metadata +19 -5
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "bundler/setup"
|
|
4
|
-
require "scampi"
|
|
5
|
-
require "async/caldav"
|
|
6
|
-
|
|
7
3
|
module Async
|
|
8
4
|
module Caldav
|
|
9
5
|
module ForwardAuth
|
|
@@ -11,16 +7,19 @@ module Async
|
|
|
11
7
|
|
|
12
8
|
def extract(env)
|
|
13
9
|
{
|
|
14
|
-
user:
|
|
15
|
-
email:
|
|
16
|
-
name:
|
|
17
|
-
groups: parse_groups(env['HTTP_REMOTE_GROUPS'])
|
|
10
|
+
user: env['HTTP_REMOTE_USER'],
|
|
11
|
+
email: env['HTTP_REMOTE_EMAIL'],
|
|
12
|
+
name: env['HTTP_REMOTE_NAME'],
|
|
13
|
+
groups: parse_groups(env['HTTP_REMOTE_GROUPS']),
|
|
18
14
|
}
|
|
19
15
|
end
|
|
20
16
|
|
|
21
17
|
def parse_groups(header)
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
if header.nil? || header.empty?
|
|
19
|
+
[]
|
|
20
|
+
else
|
|
21
|
+
header.split(',').map(&:strip).reject(&:empty?)
|
|
22
|
+
end
|
|
24
23
|
end
|
|
25
24
|
|
|
26
25
|
private_class_method :parse_groups
|
|
@@ -31,9 +30,15 @@ module Async
|
|
|
31
30
|
|
|
32
31
|
def inject(env, user: 'admin', email: nil, name: nil, groups: nil)
|
|
33
32
|
env['HTTP_REMOTE_USER'] ||= user
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
if email
|
|
34
|
+
env['HTTP_REMOTE_EMAIL'] ||= email
|
|
35
|
+
end
|
|
36
|
+
if name
|
|
37
|
+
env['HTTP_REMOTE_NAME'] ||= name
|
|
38
|
+
end
|
|
39
|
+
if groups
|
|
40
|
+
env['HTTP_REMOTE_GROUPS'] ||= groups
|
|
41
|
+
end
|
|
37
42
|
env
|
|
38
43
|
end
|
|
39
44
|
end
|
|
@@ -41,54 +46,56 @@ module Async
|
|
|
41
46
|
end
|
|
42
47
|
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
describe "Async::Caldav::ForwardAuth" do
|
|
46
|
-
it "sets user from HTTP_REMOTE_USER" do
|
|
47
|
-
result = Async::Caldav::ForwardAuth.extract({ 'HTTP_REMOTE_USER' => 'admin' })
|
|
48
|
-
result[:user].should.equal 'admin'
|
|
49
|
-
end
|
|
49
|
+
__END__
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
result = Async::Caldav::ForwardAuth.extract({})
|
|
53
|
-
result[:user].should.be.nil
|
|
54
|
-
end
|
|
51
|
+
require_relative "../caldav"
|
|
55
52
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
describe "Async::Caldav::ForwardAuth" do
|
|
54
|
+
it "sets user from HTTP_REMOTE_USER" do
|
|
55
|
+
result = Async::Caldav::ForwardAuth.extract({ 'HTTP_REMOTE_USER' => 'admin' })
|
|
56
|
+
result[:user].should.equal 'admin'
|
|
57
|
+
end
|
|
60
58
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
it "leaves user nil when header absent" do
|
|
60
|
+
result = Async::Caldav::ForwardAuth.extract({})
|
|
61
|
+
result[:user].should.be.nil
|
|
62
|
+
end
|
|
65
63
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
it "parses HTTP_REMOTE_GROUPS as comma-separated list" do
|
|
65
|
+
result = Async::Caldav::ForwardAuth.extract({ 'HTTP_REMOTE_GROUPS' => 'a,b,c' })
|
|
66
|
+
result[:groups].should.equal ['a', 'b', 'c']
|
|
67
|
+
end
|
|
70
68
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
'HTTP_REMOTE_NAME' => 'Admin'
|
|
75
|
-
})
|
|
76
|
-
result[:email].should.equal 'a@b.com'
|
|
77
|
-
result[:name].should.equal 'Admin'
|
|
78
|
-
end
|
|
69
|
+
it "trims whitespace around group names" do
|
|
70
|
+
result = Async::Caldav::ForwardAuth.extract({ 'HTTP_REMOTE_GROUPS' => ' a , b ' })
|
|
71
|
+
result[:groups].should.equal ['a', 'b']
|
|
79
72
|
end
|
|
80
73
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
env['HTTP_REMOTE_USER'].should.equal 'admin'
|
|
86
|
-
end
|
|
74
|
+
it "empty groups header produces empty list" do
|
|
75
|
+
result = Async::Caldav::ForwardAuth.extract({ 'HTTP_REMOTE_GROUPS' => '' })
|
|
76
|
+
result[:groups].should.equal []
|
|
77
|
+
end
|
|
87
78
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
79
|
+
it "email and name flow through" do
|
|
80
|
+
result = Async::Caldav::ForwardAuth.extract({
|
|
81
|
+
'HTTP_REMOTE_EMAIL' => 'a@b.com',
|
|
82
|
+
'HTTP_REMOTE_NAME' => 'Admin'
|
|
83
|
+
})
|
|
84
|
+
result[:email].should.equal 'a@b.com'
|
|
85
|
+
result[:name].should.equal 'Admin'
|
|
93
86
|
end
|
|
94
87
|
end
|
|
88
|
+
|
|
89
|
+
describe "Async::Caldav::TestStub" do
|
|
90
|
+
it "injects defaults when no header present" do
|
|
91
|
+
env = {}
|
|
92
|
+
Async::Caldav::TestStub.inject(env)
|
|
93
|
+
env['HTTP_REMOTE_USER'].should.equal 'admin'
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it "respects existing headers when present" do
|
|
97
|
+
env = { 'HTTP_REMOTE_USER' => 'existing' }
|
|
98
|
+
Async::Caldav::TestStub.inject(env)
|
|
99
|
+
env['HTTP_REMOTE_USER'].should.equal 'existing'
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "bundler/setup"
|
|
4
|
-
require "scampi"
|
|
5
|
-
require "async/caldav"
|
|
6
|
-
|
|
7
3
|
module Async
|
|
8
4
|
module Caldav
|
|
9
5
|
module Handlers
|
|
@@ -28,33 +24,35 @@ module Async
|
|
|
28
24
|
end
|
|
29
25
|
end
|
|
30
26
|
|
|
31
|
-
|
|
32
|
-
describe "Async::Caldav::Handlers::Delete" do
|
|
33
|
-
def call(**opts)
|
|
34
|
-
Async::Caldav::Handlers::Delete.call(**opts)
|
|
35
|
-
end
|
|
27
|
+
__END__
|
|
36
28
|
|
|
37
|
-
|
|
38
|
-
s = Async::Caldav::Storage::Mock.new
|
|
39
|
-
s.create_collection('/calendars/admin/cal/', type: :calendar)
|
|
40
|
-
s.put_item('/calendars/admin/cal/event.ics', 'data', 'text/calendar')
|
|
41
|
-
status, = call(path: Protocol::Caldav::Path.new('/calendars/admin/cal/event.ics', storage_class: s), storage: s)
|
|
42
|
-
status.should.equal 204
|
|
43
|
-
s.get_item('/calendars/admin/cal/event.ics').should.be.nil
|
|
44
|
-
end
|
|
29
|
+
require_relative "../../caldav"
|
|
45
30
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
status.should.equal 204
|
|
51
|
-
s.collection_exists?('/calendars/admin/cal/').should.equal false
|
|
52
|
-
end
|
|
31
|
+
describe "Async::Caldav::Handlers::Delete" do
|
|
32
|
+
def call(**opts)
|
|
33
|
+
Async::Caldav::Handlers::Delete.call(**opts)
|
|
34
|
+
end
|
|
53
35
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
36
|
+
it "deletes an item and returns 204" do
|
|
37
|
+
s = Async::Caldav::Storage::Mock.new
|
|
38
|
+
s.create_collection('/calendars/admin/cal/', type: :calendar)
|
|
39
|
+
s.put_item('/calendars/admin/cal/event.ics', 'data', 'text/calendar')
|
|
40
|
+
status, = call(path: Protocol::Caldav::Path.new('/calendars/admin/cal/event.ics', storage_class: s), storage: s)
|
|
41
|
+
status.should.equal 204
|
|
42
|
+
s.get_item('/calendars/admin/cal/event.ics').should.be.nil
|
|
59
43
|
end
|
|
60
|
-
|
|
44
|
+
|
|
45
|
+
it "deletes a collection and returns 204" do
|
|
46
|
+
s = Async::Caldav::Storage::Mock.new
|
|
47
|
+
s.create_collection('/calendars/admin/cal/', type: :calendar)
|
|
48
|
+
status, = call(path: Protocol::Caldav::Path.new('/calendars/admin/cal/', storage_class: s), storage: s)
|
|
49
|
+
status.should.equal 204
|
|
50
|
+
s.collection_exists?('/calendars/admin/cal/').should.equal false
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "returns 404 for non-existent resource" do
|
|
54
|
+
s = Async::Caldav::Storage::Mock.new
|
|
55
|
+
status, = call(path: Protocol::Caldav::Path.new('/calendars/admin/nope/', storage_class: s), storage: s)
|
|
56
|
+
status.should.equal 404
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "bundler/setup"
|
|
4
|
-
require "scampi"
|
|
5
|
-
require "async/caldav"
|
|
6
|
-
|
|
7
3
|
module Async
|
|
8
4
|
module Caldav
|
|
9
5
|
module Handlers
|
|
@@ -35,53 +31,55 @@ module Async
|
|
|
35
31
|
end
|
|
36
32
|
end
|
|
37
33
|
|
|
38
|
-
|
|
39
|
-
describe "Async::Caldav::Handlers::Get" do
|
|
40
|
-
def call(**opts)
|
|
41
|
-
Async::Caldav::Handlers::Get.call(**opts)
|
|
42
|
-
end
|
|
34
|
+
__END__
|
|
43
35
|
|
|
44
|
-
|
|
45
|
-
Protocol::Caldav::Path.new(p, storage_class: s)
|
|
46
|
-
end
|
|
36
|
+
require_relative "../../caldav"
|
|
47
37
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
status.should.equal 200
|
|
53
|
-
headers['content-type'].should.equal 'text/calendar'
|
|
54
|
-
body[0].should.equal 'BEGIN:VCALENDAR'
|
|
55
|
-
end
|
|
38
|
+
describe "Async::Caldav::Handlers::Get" do
|
|
39
|
+
def call(**opts)
|
|
40
|
+
Async::Caldav::Handlers::Get.call(**opts)
|
|
41
|
+
end
|
|
56
42
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
status, = call(path: path('/cal/ev.ics', s), storage: s, headers: { 'if-none-match' => item[:etag] })
|
|
61
|
-
status.should.equal 304
|
|
62
|
-
end
|
|
43
|
+
def path(p, s)
|
|
44
|
+
Protocol::Caldav::Path.new(p, storage_class: s)
|
|
45
|
+
end
|
|
63
46
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
body[0].should.include 'B'
|
|
73
|
-
end
|
|
47
|
+
it "returns item body with 200" do
|
|
48
|
+
s = Async::Caldav::Storage::Mock.new
|
|
49
|
+
s.put_item('/cal/ev.ics', 'BEGIN:VCALENDAR', 'text/calendar')
|
|
50
|
+
status, headers, body = call(path: path('/cal/ev.ics', s), storage: s)
|
|
51
|
+
status.should.equal 200
|
|
52
|
+
headers['content-type'].should.equal 'text/calendar'
|
|
53
|
+
body[0].should.equal 'BEGIN:VCALENDAR'
|
|
54
|
+
end
|
|
74
55
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
56
|
+
it "returns 304 on If-None-Match hit" do
|
|
57
|
+
s = Async::Caldav::Storage::Mock.new
|
|
58
|
+
item, = s.put_item('/cal/ev.ics', 'data', 'text/calendar')
|
|
59
|
+
status, = call(path: path('/cal/ev.ics', s), storage: s, headers: { 'if-none-match' => item[:etag] })
|
|
60
|
+
status.should.equal 304
|
|
61
|
+
end
|
|
80
62
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
63
|
+
it "returns collection contents" do
|
|
64
|
+
s = Async::Caldav::Storage::Mock.new
|
|
65
|
+
s.create_collection('/cal/')
|
|
66
|
+
s.put_item('/cal/a.ics', 'A', 'text/calendar')
|
|
67
|
+
s.put_item('/cal/b.ics', 'B', 'text/calendar')
|
|
68
|
+
status, _, body = call(path: path('/cal/', s), storage: s)
|
|
69
|
+
status.should.equal 200
|
|
70
|
+
body[0].should.include 'A'
|
|
71
|
+
body[0].should.include 'B'
|
|
86
72
|
end
|
|
87
|
-
|
|
73
|
+
|
|
74
|
+
it "returns 404 for deep non-existent path" do
|
|
75
|
+
s = Async::Caldav::Storage::Mock.new
|
|
76
|
+
status, = call(path: path('/calendars/admin/nope/item.ics', s), storage: s)
|
|
77
|
+
status.should.equal 404
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "returns 200 for shallow path" do
|
|
81
|
+
s = Async::Caldav::Storage::Mock.new
|
|
82
|
+
status, = call(path: path('/calendars/admin/', s), storage: s)
|
|
83
|
+
status.should.equal 200
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "bundler/setup"
|
|
4
|
-
require "scampi"
|
|
5
|
-
require "async/caldav"
|
|
6
|
-
|
|
7
3
|
module Async
|
|
8
4
|
module Caldav
|
|
9
5
|
module Handlers
|
|
@@ -19,18 +15,20 @@ module Async
|
|
|
19
15
|
end
|
|
20
16
|
end
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
18
|
+
__END__
|
|
19
|
+
|
|
20
|
+
require_relative "../../caldav"
|
|
21
|
+
|
|
22
|
+
describe "Async::Caldav::Handlers::Head" do
|
|
23
|
+
it "returns same status and headers as GET but empty body" do
|
|
24
|
+
s = Async::Caldav::Storage::Mock.new
|
|
25
|
+
s.put_item('/cal/ev.ics', 'BEGIN:VCALENDAR', 'text/calendar')
|
|
26
|
+
status, headers, body = Async::Caldav::Handlers::Head.call(
|
|
27
|
+
path: Protocol::Caldav::Path.new('/cal/ev.ics', storage_class: s),
|
|
28
|
+
storage: s
|
|
29
|
+
)
|
|
30
|
+
status.should.equal 200
|
|
31
|
+
headers['content-type'].should.equal 'text/calendar'
|
|
32
|
+
body.should.equal []
|
|
35
33
|
end
|
|
36
|
-
end
|
|
34
|
+
end
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "bundler/setup"
|
|
4
|
-
require "scampi"
|
|
5
|
-
require "async/caldav"
|
|
6
|
-
|
|
7
3
|
module Async
|
|
8
4
|
module Caldav
|
|
9
5
|
module Handlers
|
|
@@ -15,81 +11,81 @@ module Async
|
|
|
15
11
|
col_path = path.ensure_trailing_slash
|
|
16
12
|
|
|
17
13
|
if storage.collection_exists?(col_path.to_s)
|
|
18
|
-
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
unless col_path.parent_exists?
|
|
22
|
-
return [409, { 'content-type' => 'text/plain' }, ['Parent collection does not exist']]
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
displayname = Protocol::Caldav::Xml.extract_value(body, 'displayname')
|
|
26
|
-
description = Protocol::Caldav::Xml.extract_value(body, 'calendar-description')
|
|
27
|
-
color = Protocol::Caldav::Xml.extract_value(body, 'calendar-color')
|
|
28
|
-
|
|
29
|
-
type = if method == 'MKCALENDAR'
|
|
30
|
-
:calendar
|
|
31
|
-
elsif body && body.include?('addressbook')
|
|
32
|
-
:addressbook
|
|
14
|
+
[405, { 'content-type' => 'text/plain' }, ['Collection already exists']]
|
|
33
15
|
else
|
|
34
|
-
|
|
16
|
+
if col_path.parent_exists?
|
|
17
|
+
displayname = Protocol::Caldav::Xml.extract_value(body, 'displayname')
|
|
18
|
+
description = Protocol::Caldav::Xml.extract_value(body, 'calendar-description')
|
|
19
|
+
color = Protocol::Caldav::Xml.extract_value(body, 'calendar-color')
|
|
20
|
+
if method == 'MKCALENDAR'
|
|
21
|
+
type = :calendar
|
|
22
|
+
elsif body && body.include?('addressbook')
|
|
23
|
+
type = :addressbook
|
|
24
|
+
else
|
|
25
|
+
type = resource_type || :collection
|
|
26
|
+
end
|
|
27
|
+
storage.create_collection(col_path.to_s, type: type, displayname: displayname, description: description, color: color)
|
|
28
|
+
[201, {}, ['']]
|
|
29
|
+
else
|
|
30
|
+
[409, { 'content-type' => 'text/plain' }, ['Parent collection does not exist']]
|
|
31
|
+
end
|
|
35
32
|
end
|
|
36
|
-
|
|
37
|
-
storage.create_collection(col_path.to_s, type: type, displayname: displayname, description: description, color: color)
|
|
38
|
-
[201, {}, ['']]
|
|
39
33
|
end
|
|
40
34
|
end
|
|
41
35
|
end
|
|
42
36
|
end
|
|
43
37
|
end
|
|
44
38
|
|
|
45
|
-
|
|
46
|
-
describe "Async::Caldav::Handlers::Mkcol" do
|
|
47
|
-
def call(**opts)
|
|
48
|
-
Async::Caldav::Handlers::Mkcol.call(**opts)
|
|
49
|
-
end
|
|
39
|
+
__END__
|
|
50
40
|
|
|
51
|
-
|
|
52
|
-
Protocol::Caldav::Path.new(p, storage_class: s)
|
|
53
|
-
end
|
|
41
|
+
require_relative "../../caldav"
|
|
54
42
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
path: path('/calendars/admin/work', s), storage: s,
|
|
60
|
-
body: '<d:displayname>Work</d:displayname>', method: 'MKCALENDAR'
|
|
61
|
-
)
|
|
62
|
-
status.should.equal 201
|
|
63
|
-
col = s.get_collection('/calendars/admin/work/')
|
|
64
|
-
col[:type].should.equal :calendar
|
|
65
|
-
col[:displayname].should.equal 'Work'
|
|
66
|
-
end
|
|
43
|
+
describe "Async::Caldav::Handlers::Mkcol" do
|
|
44
|
+
def call(**opts)
|
|
45
|
+
Async::Caldav::Handlers::Mkcol.call(**opts)
|
|
46
|
+
end
|
|
67
47
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
status, = call(
|
|
72
|
-
path: path('/addressbooks/admin/contacts', s), storage: s,
|
|
73
|
-
body: '<resourcetype><addressbook/></resourcetype>', method: 'MKCOL'
|
|
74
|
-
)
|
|
75
|
-
status.should.equal 201
|
|
76
|
-
s.get_collection('/addressbooks/admin/contacts/')[:type].should.equal :addressbook
|
|
77
|
-
end
|
|
48
|
+
def path(p, s)
|
|
49
|
+
Protocol::Caldav::Path.new(p, storage_class: s)
|
|
50
|
+
end
|
|
78
51
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
52
|
+
it "creates a calendar and returns 201" do
|
|
53
|
+
s = Async::Caldav::Storage::Mock.new
|
|
54
|
+
s.create_collection('/calendars/admin/')
|
|
55
|
+
status, = call(
|
|
56
|
+
path: path('/calendars/admin/work', s), storage: s,
|
|
57
|
+
body: '<d:displayname>Work</d:displayname>', method: 'MKCALENDAR'
|
|
58
|
+
)
|
|
59
|
+
status.should.equal 201
|
|
60
|
+
col = s.get_collection('/calendars/admin/work/')
|
|
61
|
+
col[:type].should.equal :calendar
|
|
62
|
+
col[:displayname].should.equal 'Work'
|
|
63
|
+
end
|
|
85
64
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
65
|
+
it "creates an addressbook via MKCOL" do
|
|
66
|
+
s = Async::Caldav::Storage::Mock.new
|
|
67
|
+
s.create_collection('/addressbooks/admin/')
|
|
68
|
+
status, = call(
|
|
69
|
+
path: path('/addressbooks/admin/contacts', s), storage: s,
|
|
70
|
+
body: '<resourcetype><addressbook/></resourcetype>', method: 'MKCOL'
|
|
71
|
+
)
|
|
72
|
+
status.should.equal 201
|
|
73
|
+
s.get_collection('/addressbooks/admin/contacts/')[:type].should.equal :addressbook
|
|
94
74
|
end
|
|
95
|
-
|
|
75
|
+
|
|
76
|
+
it "returns 405 if collection exists" do
|
|
77
|
+
s = Async::Caldav::Storage::Mock.new
|
|
78
|
+
s.create_collection('/cal/')
|
|
79
|
+
status, = call(path: path('/cal/', s), storage: s, body: '', method: 'MKCALENDAR')
|
|
80
|
+
status.should.equal 405
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "returns 409 if parent does not exist" do
|
|
84
|
+
s = Async::Caldav::Storage::Mock.new
|
|
85
|
+
status, = call(
|
|
86
|
+
path: path('/calendars/admin/deep/nested/cal', s), storage: s,
|
|
87
|
+
body: '', method: 'MKCALENDAR'
|
|
88
|
+
)
|
|
89
|
+
status.should.equal 409
|
|
90
|
+
end
|
|
91
|
+
end
|