fixtures: seed a contact that no book holds
Pat Private is in no sync: group, so the listing leaves that card out;
its put moves the ctag to 19, and that is all the replay diff is about.
Assisted-by: Claude Opus 5 via Claude Code
diff --git a/test/fixture_data.rb b/test/fixture_data.rb
index 4881d5b..67bae0e 100644
--- a/test/fixture_data.rb
+++ b/test/fixture_data.rb
@@ -86,14 +86,23 @@ module FixtureData
cards.keys.grep(/\Abday-/)
end
- # The third is `sync:*` holding every card, so every user's book is
- # the whole seed — the replay's included, whose recorded sessions saw
- # every card (docs/plans/2026-09-12-per-user-books.md).
+ # The seed's cards that no client syncs, read off the ids for
+ # birthday_cards' reason.
+ #: () -> Array[String]
+ def self.unsynced_cards
+ cards.keys.grep(/\Aunsynced-/)
+ end
+
+ # The third is `sync:*`, holding every card but the unsynced ones, so
+ # every user's book is the rest of the seed — the replay's included,
+ # whose recorded sessions saw every card they asked for — and the
+ # admin UI still shows a contact that is in nobody's
+ # (docs/plans/2026-09-12-per-user-books.md).
#: (ProTacts::Store store) -> void
def self.seed_groups(store)
seed_group(store, name: "Booles", lines: HOUSEHOLD, members: MEMBERS)
seed_group(store, members: birthday_cards)
- seed_group(store, name: ProTacts::Store::EVERYONE, members: cards.keys)
+ seed_group(store, name: ProTacts::Store::EVERYONE, members: cards.keys - unsynced_cards)
end
# A group, seeded without a change-log entry for any member it moves
diff --git a/test/fixtures/cards/unsynced-pat.vcf b/test/fixtures/cards/unsynced-pat.vcf
new file mode 100644
index 0000000..7b9364b
--- /dev/null
+++ b/test/fixtures/cards/unsynced-pat.vcf
@@ -0,0 +1,7 @@
+BEGIN:VCARD
+VERSION:3.0
+N:Private;Pat;;;
+FN:Pat Private
+EMAIL:pat@example.com
+UID:unsynced-pat
+END:VCARD
diff --git a/test/fixtures/ios-exchange/02-propfind-addressbook-ctag/response b/test/fixtures/ios-exchange/02-propfind-addressbook-ctag/response
index b7d86f1..d298041 100644
--- a/test/fixtures/ios-exchange/02-propfind-addressbook-ctag/response
+++ b/test/fixtures/ios-exchange/02-propfind-addressbook-ctag/response
@@ -16,8 +16,8 @@ Content-Type: text/xml
<d:report><d:sync-collection/></d:report>
</d:supported-report>
</d:supported-report-set>
- <cs:getctag>18</cs:getctag>
- <d:sync-token>http://pro-tacts/sync/18/c8dae6371b33b8d6</d:sync-token>
+ <cs:getctag>19</cs:getctag>
+ <d:sync-token>http://pro-tacts/sync/19/c8dae6371b33b8d6</d:sync-token>
<d:current-user-privilege-set>
<d:privilege><d:read/></d:privilege>
<d:privilege><d:write/></d:privilege>
diff --git a/test/fixtures/ios-exchange/03-propfind-addressbook-bootstrap/response b/test/fixtures/ios-exchange/03-propfind-addressbook-bootstrap/response
index c949537..ee3f601 100644
--- a/test/fixtures/ios-exchange/03-propfind-addressbook-bootstrap/response
+++ b/test/fixtures/ios-exchange/03-propfind-addressbook-bootstrap/response
@@ -16,8 +16,8 @@ Content-Type: text/xml
<d:report><d:sync-collection/></d:report>
</d:supported-report>
</d:supported-report-set>
- <cs:getctag>18</cs:getctag>
- <d:sync-token>http://pro-tacts/sync/18/c8dae6371b33b8d6</d:sync-token>
+ <cs:getctag>19</cs:getctag>
+ <d:sync-token>http://pro-tacts/sync/19/c8dae6371b33b8d6</d:sync-token>
<d:current-user-privilege-set>
<d:privilege><d:read/></d:privilege>
<d:privilege><d:write/></d:privilege>
diff --git a/test/fixtures/macos-exchange/05-propfind-addressbook-ctag/response b/test/fixtures/macos-exchange/05-propfind-addressbook-ctag/response
index b7d86f1..d298041 100644
--- a/test/fixtures/macos-exchange/05-propfind-addressbook-ctag/response
+++ b/test/fixtures/macos-exchange/05-propfind-addressbook-ctag/response
@@ -16,8 +16,8 @@ Content-Type: text/xml
<d:report><d:sync-collection/></d:report>
</d:supported-report>
</d:supported-report-set>
- <cs:getctag>18</cs:getctag>
- <d:sync-token>http://pro-tacts/sync/18/c8dae6371b33b8d6</d:sync-token>
+ <cs:getctag>19</cs:getctag>
+ <d:sync-token>http://pro-tacts/sync/19/c8dae6371b33b8d6</d:sync-token>
<d:current-user-privilege-set>
<d:privilege><d:read/></d:privilege>
<d:privilege><d:write/></d:privilege>
diff --git a/test/fixtures/macos-exchange/09-propfind-addressbook-bootstrap/response b/test/fixtures/macos-exchange/09-propfind-addressbook-bootstrap/response
index c949537..ee3f601 100644
--- a/test/fixtures/macos-exchange/09-propfind-addressbook-bootstrap/response
+++ b/test/fixtures/macos-exchange/09-propfind-addressbook-bootstrap/response
@@ -16,8 +16,8 @@ Content-Type: text/xml
<d:report><d:sync-collection/></d:report>
</d:supported-report>
</d:supported-report-set>
- <cs:getctag>18</cs:getctag>
- <d:sync-token>http://pro-tacts/sync/18/c8dae6371b33b8d6</d:sync-token>
+ <cs:getctag>19</cs:getctag>
+ <d:sync-token>http://pro-tacts/sync/19/c8dae6371b33b8d6</d:sync-token>
<d:current-user-privilege-set>
<d:privilege><d:read/></d:privilege>
<d:privilege><d:write/></d:privilege>
diff --git a/test/pro_tacts/test_web.rb b/test/pro_tacts/test_web.rb
index dc7030d..7250a89 100644
--- a/test/pro_tacts/test_web.rb
+++ b/test/pro_tacts/test_web.rb
@@ -97,6 +97,17 @@ class WebTest < Minitest::Test
assert_includes last_response.body, "END:VCARD"
end
+ # The seed's unsynced card is in no `sync:` group, so no book holds it
+ # (FixtureData.unsynced_cards).
+ def test_a_seed_card_in_no_book_is_not_served
+ request "/dav/addressbook/", method: "PROPFIND", "HTTP_DEPTH" => "1", input: etag_only_propfind
+ assert_includes last_response.body, "household-george.vcf"
+ refute_includes last_response.body, "unsynced-"
+
+ get "/dav/addressbook/unsynced-pat.vcf"
+ assert_equal 404, last_response.status
+ end
+
def test_propfind_addressbook_includes_ctag
request "/dav/addressbook/", method: "PROPFIND"