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
require_relative "../../test_helper"
require "pro_tacts/admin/search"
require "pro_tacts/contact"
require "pro_tacts/group"
require "pro_tacts/store"
require "pro_tacts/vcard"
class SearchTest < Minitest::Test
# A card from its extra lines, keyed as the store keys it, beside its
# id. The rows are listed most recent first, the way
# Store#search_keys_by_recency hands them over.
def row(id, name, *lines)
bytes = ["BEGIN:VCARD", "VERSION:3.0", "FN:#{name}", "UID:#{id}", *lines, "END:VCARD", ""].join("\r\n")
contact = ProTacts::Contact.new(id:, stored: ProTacts::VCard.new(bytes), birthday: nil, inherited: [])
[id, ProTacts::Admin::Search.key(contact)]
end
def search(rows, query, groups: [])
ProTacts::Admin::Search.contacts(rows.to_h, query:, groups:)
end
# Each word finds its own field: neither "ada" nor "london" is
# the whole of any one value.
def test_every_word_matches_somewhere_in_the_contact
ada = row("ada", "Ada Lovelace", "ADR:;;12 St James Sq;London;;;UK")
grace = row("grace", "Grace Hopper", "ADR:;;1 Main St;London;;;UK")
assert_equal ["ada"], search([ada, grace], "ada london")
assert_equal [], search([ada, grace], "ada arlington")
end
# A number is found by its digits whatever punctuation the card
# and the query spell it with.
def test_a_phone_compares_digits_only
ada = row("ada", "Ada Lovelace", "TEL:(555) 010-0100")
assert_equal ["ada"], search([ada], "555-010-0100")
assert_equal ["ada"], search([ada], "5550100100")
assert_equal ["ada"], search([ada], "0100")
end
# A word with letters in it is not a number, so its digits alone do
# not reach a phone.
def test_a_word_with_letters_does_not_match_a_phone
ada = row("ada", "Ada Lovelace", "TEL:555-0100")
assert_equal [], search([ada], "x555")
end
def test_addresses_and_notes_are_values_a_search_finds
ada = row("ada", "Ada Lovelace", "ADR:;;12 St James Sq;London;;;UK", "NOTE:Met at the Analytical Society")
assert_equal ["ada"], search([ada], "james sq")
assert_equal ["ada"], search([ada], "analytical")
end
def test_the_groups_a_contact_is_in_find_it
ada = row("ada", "Ada Lovelace")
math = ProTacts::Group.new(id: "math", name: "Mathematicians", label: "Mathematicians", lines: [], members: ["ada"])
assert_equal ["ada"], search([ada], "mathematicians", groups: [math])
end
# What a group lends is matched as the group's, not keyed into the
# member: a lent address and a lent note find every member, and
# nobody outside the group.
def test_what_a_group_lends_finds_its_members
ada = row("ada", "Ada Lovelace")
grace = row("grace", "Grace Hopper")
home = ProTacts::Group.new(id: "home", name: "Home", label: "Home",
lines: ["ADR:;;12 St James Sq;London;;;UK", "NOTE:Gate code 7946"], members: ["ada"])
assert_equal ["ada"], search([ada, grace], "james sq", groups: [home])
assert_equal ["ada"], search([ada, grace], "gate code", groups: [home])
end
# A group lends no name, so its label finds a member without making
# the find a name hit: Grace's own name still leads.
def test_a_group_label_is_not_a_name_hit
ada = row("ada", "Ada Lovelace")
grace = row("grace", "Grace Hopper")
graces = ProTacts::Group.new(id: "graces", name: "Graces", label: "Graces", lines: [], members: ["ada"])
assert_equal ["grace", "ada"], search([ada, grace], "grace", groups: [graces])
end
# Charles is the more recent, and only his note says "ada"; Ada's
# name does, so she leads anyway. Within a tier, recency stands:
# the two notes keep the order they arrived in.
def test_a_name_prefix_hit_ranks_above_a_hit_elsewhere
charles = row("charles", "Charles Babbage", "NOTE:Worked with Ada")
byron = row("byron", "Lord Byron", "NOTE:Father of Ada")
ada = row("ada", "Ada Lovelace")
assert_equal ["ada", "charles", "byron"], search([charles, byron, ada], "ada")
end
# A prefix of a later word of the name counts, and so does the
# nickname; a match inside a word is still a match, just not a name
# hit.
def test_a_name_hit_is_a_prefix_of_any_word_of_the_name_or_nickname
lovelace = row("ada", "Ada Lovelace")
red = row("red", "Sarah Jones", "NICKNAME:Lovey")
clove = row("clove", "Clove Smith")
assert_equal ["ada", "red", "clove"], search([clove, lovelace, red], "love")
end
end