Re-implement Facebook service to use OAuth2 and Graph API. This allows
[hermes] / package / test / unit / test_facebook.py
1 from org.maemo.hermes.engine.facebook.service import Service
2 from org.maemo.hermes.engine.facebook.api import FacebookApi
3 from org.maemo.hermes.engine.names import canonical
4 from org.maemo.hermes.engine.friend import Friend
5 import unittest
6
7
8 class FakeContact():
9     id_counter = 0
10     def __init__(self, name, addr, id=None):
11         self.name = name;
12         self.urls = addr
13         self.id = id or FakeContact.id_counter
14         FakeContact.id_counter = FakeContact.id_counter + 1;
15     def get_urls(self):
16         return self.urls
17     def get_name(self):
18         return self.name
19     def get_identifiers(self):
20         return [canonical(self.name)]
21     
22 class FakeFacebookApi():
23     friends = []
24     
25     def get_friends(self):
26         return FakeFacebookApi.friends
27     
28 class FakeOAuth():
29     def authorise(self, authorise_url, access_token_url, args = None):
30         pass
31     
32     def request(self, url, args = None):
33         if url == 'https://graph.facebook.com/me':
34             return '{"id":"1234567","name":"Maemo Hermes"}'
35         
36         elif url == 'https://graph.facebook.com/me/friends':
37             return '{"data":[{"id":"1","name":"J Smith","website":"http:\/\/www.example.org"},{"id":"2","name":"P Barnum"}]}'
38         
39         else:
40             raise Exception("Unknown URL: %s" % (url))
41         
42
43 class TestFacebookService(unittest.TestCase):
44     
45     def setUp(self):
46         self.testee = Service("facebook", FakeFacebookApi())
47         
48     
49     def test_that_get_friends_to_create_contacts_for_works(self):
50         def run_test(expected_length):
51             self._fake_server_response([{'id':'123456','name':'Facebook Junkie', 'birthday':'now'}])
52             self._run_service([])
53             friends = self.testee.get_friends_to_create_contacts_for()
54             assert len(friends) == expected_length
55             
56         # default is to NOT create contacts
57         self.testee = Service("facebook", FakeFacebookApi())
58         run_test(0)
59         
60         # passing False explicitly
61         self.testee = Service("facebook", FakeFacebookApi(), False)
62         run_test(0)
63         
64         # passing True to constructor
65         self.testee = Service("facebook", FakeFacebookApi(), True)
66         run_test(1)
67         
68     
69     def test_that_gftccf_returns_friends_with_birth_date(self):
70         self.testee = Service("facebook", FakeFacebookApi(), True)
71         bday = '1980-10-15'
72         props_with_bday = {'id':'123456','name':'Facebook Junkie', 'birthday':bday}
73         props_without = {'id':'123457','name':'Johnny Secret'}
74         self._fake_server_response([props_with_bday, props_without])
75         self._run_service([])
76         
77         assert len(self.testee.get_friends()) == 2
78         friends = self.testee.get_friends_to_create_contacts_for()
79         assert len(friends) == 1
80         assert friends[0].has_birthday_date()
81         assert friends[0].get_birthday_date() == bday 
82         
83         
84     def test_that_process_contact_returns_friend_object_for_known_contact(self):
85         known_url = 'http://www.facebook.com/profile.php?id=123456'
86         known_contact = FakeContact('Facebook Junkie', [known_url])
87         self._fake_server_response([{'id':'123456','name':'Facebook Junkie'}])
88         
89         self.testee.process_friends()
90         result = self.testee.process_contact(known_contact)
91         assert isinstance(result, Friend)
92
93
94     def test_that_process_contact_returns_None_for_unknown_contact(self):
95         known_contact = FakeContact('Facebook Junkie', [])
96         self._fake_server_response([])
97         
98         self.testee.process_friends()
99         result = self.testee.process_contact(known_contact)
100         assert result is None
101
102
103     def test_main_flow_one_match_by_url_one_by_name(self):
104         # arrange
105         self.existing_address = 'http://www.facebook.com/profile.php?id=123456'
106         self.existing_contact = FakeContact("Facebook Person", [self.existing_address])
107         existing_fake = {'id':'123456','name':'Name Doesnt Match but URL Does'}
108         
109         self.other_address = 'http://twitter.com/not/correct/site'
110         self.other_contact = FakeContact("Twitter Person", [self.other_address])
111         other_fake = {'id':'123','name':self.other_contact.get_name()}
112         
113         self.none_contact = FakeContact("No URLson", [])
114         
115         fake_data = [existing_fake, other_fake]
116         self._fake_server_response(fake_data)
117
118         # act        
119         self._run_service([self.existing_contact, self.other_contact, self.none_contact])
120         
121         # assert
122         friends = self.testee.get_friends()
123         contacts = self.testee.get_contacts_with_match()
124         assert len(friends) == 2
125         assert len(contacts) == 2
126         assert self.other_contact in contacts
127         assert self.other_contact == contacts[self.other_contact].get_contact()
128         assert self.existing_contact in contacts
129         assert self.existing_contact == contacts[self.existing_contact].get_contact()
130         assert self.none_contact not in contacts
131         
132         
133     def test_name_collision_avoided_by_previous_matching(self):
134         name = "Same Name"
135         contact_do_match = FakeContact(name, ["http://www.facebook.com/profile.php?id=123"], 1);
136         contact_no_match = FakeContact(name, [None], 2)
137         
138         data = [{'id':'123','name':name}]
139         self._fake_server_response(data)
140         
141         self._run_service([contact_no_match, contact_do_match])
142         
143         assert len(self.testee.get_unmatched_friends()) == 0
144         matchers = self.testee.get_contacts_with_match().keys()
145         assert len(matchers) == 1
146         assert matchers[0].id == 1
147
148       
149     def test_name_collision_avoided_only_one_person_matched(self):
150         name = "Same Name"
151         contact_do_match = FakeContact(name, ["Contact 1"]);
152         contact_no_match = FakeContact(name, ["Contact 2"])
153         
154         data = [{'id':'123','name':name}]
155         self._fake_server_response(data)
156         
157         self._run_service([contact_no_match, contact_do_match])
158         
159         matchers = self.testee.get_contacts_with_match().keys()
160         assert len(matchers) == 1
161         assert len(self.testee.get_unmatched_friends()) == 0
162         
163         
164     def _run_service(self, contacts):
165         for contact in contacts:
166             self.testee.pre_process_contact(contact)
167         self.testee.process_friends()
168         for contact in contacts:
169             self.testee.process_contact(contact)
170         
171     def _fake_server_response(self, data):
172         FakeFacebookApi.friends = data
173
174
175 class TestFacebookAPI(unittest.TestCase):
176
177     def setUp(self):
178         self.oauth = FakeOAuth()
179         self.api = FacebookApi(self.oauth)
180
181     def test_authenticate(self):
182         pass
183     
184     
185     def test_get_user(self):
186         assert self.api.get_user() == 'Maemo Hermes'
187        
188         
189     def test_get_friends(self):
190         friends = self.api.get_friends()
191         assert len(friends) == 2
192         assert friends[0]['name'] == 'J Smith'
193         assert friends[1]['id'] == '2'
194
195     
196 if __name__ == '__main__':
197     unittest.main()