1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
require_relative "../../test_helper"
require "rack/test"
require "pro_tacts/profile"
require "pro_tacts/web"
# GET /setup and the document it links to, exercised the way the other
# screens are: real requests through the Roda app. The profile is
# rendered from the request alone, so only naming the book needs a
# database standing behind it.
class AdminDeviceSetupTest < Minitest::Test
include Rack::Test::Methods
include ThrowawayContacts
def app
ProTacts::Web
end
def setup
header "Remote-User", "test@example.com"
header "Host", "box.example.ts.net"
end
# The account name is read from the environment, so a name set in the
# shell running the suite would otherwise decide what these assert.
def with_config(env = {})
original = ProTacts.config
ProTacts.config = ProTacts::Config.new(env)
yield
ensure
ProTacts.config = original
end
def test_screen_names_the_account_the_download_carries
with_config do
get "/setup"
assert_equal 200, last_response.status
assert_equal "text/html; charset=utf-8", last_response["Content-Type"]
assert_includes last_response.body, "box.example.ts.net"
assert_includes last_response.body, %(<dd class="type-body-sm">pro-tacts</dd>)
assert_includes last_response.body, "/setup/carddav.mobileconfig"
end
end
# The screen states what the document is about to carry, so a rename
# reaching only one of the two would put a name on the screen that the
# installed account does not have.
def test_the_configured_name_reaches_the_screen_and_the_document
with_config("PRO_TACTS_INSTANCE_NAME" => "pro-tacts (dev)") do
get "/setup"
assert_includes last_response.body, %(<dd class="type-body-sm">pro-tacts (dev)</dd>)
get "/setup/carddav.mobileconfig"
assert_includes last_response.body, "<string>pro-tacts (dev)</string>"
end
end
# The media type is what makes a device offer to install rather than
# save, so it is the assertion that matters most here.
def test_document_is_served_as_a_configuration_profile
get "/setup/carddav.mobileconfig"
assert_equal 200, last_response.status
assert_equal "application/x-apple-aspen-config", last_response["Content-Type"]
assert_includes last_response.body, "<string>com.apple.carddav.account</string>"
end
# The point of reading the hostname off the request: whatever address
# the device reached us at is the address the account is provisioned
# for.
def test_document_names_the_host_the_request_arrived_on
header "Host", "other.example.ts.net"
get "/setup/carddav.mobileconfig"
assert_includes last_response.body, "<string>other.example.ts.net</string>"
end
def test_document_names_the_requester_as_the_username
header "Remote-User", "zoe@example.com"
get "/setup/carddav.mobileconfig"
assert_match(%r{<key>CardDAVUsername</key>\s*<string>zoe@example.com</string>}, last_response.body)
end
# Fresh per download (see Profile): a reinstall is a cold account, not
# an update of the one already there.
def test_each_download_carries_its_own_identifier
get "/setup/carddav.mobileconfig"
first = last_response.body
get "/setup/carddav.mobileconfig"
refute_equal first, last_response.body
assert_includes first, ProTacts::Profile::IDENTIFIER_PREFIX
assert_includes last_response.body, ProTacts::Profile::IDENTIFIER_PREFIX
end
## Naming the book
def test_naming_the_book_names_the_requesters_own
with_contacts({}) do |store|
id = store.create_group(name: "sync:test@example.com")
post "/setup/book", name: "test"
assert_equal 200, last_response.status
assert_equal "test@example.com syncs sync:test\n", last_response.body
assert_equal "sync:test", store.group(id).name
post "/setup/book"
assert_equal "test@example.com syncs sync:test@example.com\n", last_response.body
assert_equal "test@example.com", store.book_name("test@example.com")
end
end
def test_a_taken_book_name_is_a_conflict
with_contacts({}) do |store|
store.name_book("zoe@example.com", "zoe")
post "/setup/book", name: "zoe"
assert_equal 409, last_response.status
assert_equal "sync:zoe is taken; test@example.com's book is unchanged.\n", last_response.body
assert_equal "test@example.com", store.book_name("test@example.com")
post "/setup/book", name: "*"
assert_equal 409, last_response.status
end
end
end