Merge lp:~mardy/account-polld/lp1493733 into lp:~ubuntu-push-hackers/account-polld/trunk

Proposed by Alberto Mardegan
Status: Merged
Approved by: Jonas G. Drange
Approved revision: 140
Merged at revision: 141
Proposed branch: lp:~mardy/account-polld/lp1493733
Merge into: lp:~ubuntu-push-hackers/account-polld/trunk
Diff against target: 1374 lines (+16/-1296)
6 files modified
cmd/account-polld/main.go (+0/-6)
debian/account-polld.conf (+1/-1)
debian/changelog (+14/-0)
debian/control (+1/-1)
plugins/facebook/facebook.go (+0/-453)
plugins/facebook/facebook_test.go (+0/-835)
To merge this branch: bzr merge lp:~mardy/account-polld/lp1493733
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Jonas G. Drange (community) Needs Information
Review via email: mp+270503@code.launchpad.net

Commit message

Remove non working Facebook plugin

Facebook removed the possibility to retrieve notifications, effective from October 6, 2015. From https://developers.facebook.com/docs/apps/changelog/#v2_4:

"The GET /v2.4/{user_id}/home, GET /v2.4/{user_id}/inbox, and GET /v2.4/{user_id}/notifications operations as well as read_stream, read_mailbox, and manage_notifications permissions are deprecated in v2.4."

and

"From October 6, 2015 onwards, in all previous API versions, these endpoints will return empty arrays"

This change contains a couple of packaging changes:
* debian/control, debian/account-polld.conf:
  Remove mentions of Facebook from the descriptions.

Description of the change

Remove non working Facebook plugin

Facebook removed the possibility to retrieve notifications, effective from October 6, 2015. From https://developers.facebook.com/docs/apps/changelog/#v2_4:

"The GET /v2.4/{user_id}/home, GET /v2.4/{user_id}/inbox, and GET /v2.4/{user_id}/notifications operations as well as read_stream, read_mailbox, and manage_notifications permissions are deprecated in v2.4."

and

"From October 6, 2015 onwards, in all previous API versions, these endpoints will return empty arrays"

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~mardy/account-polld/lp1493733 updated
139. By Alberto Mardegan

Sync changelog from archive

No-change test rebuild for g++5 ABI transition

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Jonas G. Drange (jonas-drange) wrote :

Could you make a note of why the files under debian/ changed, as I think it's required for landing. Thanks!

review: Needs Information
lp:~mardy/account-polld/lp1493733 updated
140. By Alberto Mardegan

Update changelog

* Remove non-working facebook integration. (LP: #1493733)
* debian/control, debian/account-polld.conf:
  Remove mentions of Facebook from the descriptions.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cmd/account-polld/main.go'
2--- cmd/account-polld/main.go 2015-03-20 17:51:02 +0000
3+++ cmd/account-polld/main.go 2015-10-12 13:54:21 +0000
4@@ -27,7 +27,6 @@
5 "launchpad.net/account-polld/accounts"
6 "launchpad.net/account-polld/gettext"
7 "launchpad.net/account-polld/plugins"
8- "launchpad.net/account-polld/plugins/facebook"
9 "launchpad.net/account-polld/plugins/gmail"
10 "launchpad.net/account-polld/plugins/twitter"
11 "launchpad.net/account-polld/pollbus"
12@@ -47,7 +46,6 @@
13
14 SERVICENAME_GMAIL = "com.ubuntu.developer.webapps.webapp-gmail_webapp-gmail"
15 SERVICENAME_TWITTER = "com.ubuntu.developer.webapps.webapp-twitter_webapp-twitter"
16- SERVICENAME_FACEBOOK = "com.ubuntu.developer.webapps.webapp-facebook_webapp-facebook"
17 )
18
19 const (
20@@ -119,10 +117,6 @@
21 case SERVICENAME_GMAIL:
22 log.Println("Creating account with id", data.AccountId, "for", data.ServiceName)
23 plugin = gmail.New(data.AccountId)
24- case SERVICENAME_FACEBOOK:
25- // This is just stubbed until the plugin exists.
26- log.Println("Creating account with id", data.AccountId, "for", data.ServiceName)
27- plugin = facebook.New(data.AccountId)
28 case SERVICENAME_TWITTER:
29 // This is just stubbed until the plugin exists.
30 log.Println("Creating account with id", data.AccountId, "for", data.ServiceName)
31
32=== modified file 'debian/account-polld.conf'
33--- debian/account-polld.conf 2014-08-06 14:29:13 +0000
34+++ debian/account-polld.conf 2015-10-12 13:54:21 +0000
35@@ -1,4 +1,4 @@
36-description "account-polld checks for notifications for twitter, facebook and gmail and passes them on to the Ubuntu Push Postal service"
37+description "account-polld checks for notifications for twitter and gmail and passes them on to the Ubuntu Push Postal service"
38
39 start on started ubuntu-push-client
40 stop on stopping ubuntu-push-client
41
42=== modified file 'debian/changelog'
43--- debian/changelog 2015-04-10 17:19:48 +0000
44+++ debian/changelog 2015-10-12 13:54:21 +0000
45@@ -1,3 +1,17 @@
46+account-polld (0.1+15.04.20151012-0ubuntu1) UNRELEASED; urgency=medium
47+
48+ * Remove non-working facebook integration. (LP: #1493733)
49+ * debian/control, debian/account-polld.conf:
50+ Remove mentions of Facebook from the descriptions.
51+
52+ -- Alberto Mardegan <alberto.mardegan@canonical.com> Mon, 12 Oct 2015 16:49:52 +0300
53+
54+account-polld (0.1+15.04.20150410-0ubuntu2~gcc5.1) wily; urgency=medium
55+
56+ * No-change test rebuild for g++5 ABI transition
57+
58+ -- Steve Langasek <steve.langasek@ubuntu.com> Wed, 15 Jul 2015 07:18:29 +0000
59+
60 account-polld (0.1+15.04.20150410-0ubuntu1) vivid; urgency=medium
61
62 [ John R. Lenton ]
63
64=== modified file 'debian/control'
65--- debian/control 2014-08-24 00:04:44 +0000
66+++ debian/control 2015-10-12 13:54:21 +0000
67@@ -29,7 +29,7 @@
68 Built-Using: ${misc:Built-Using}
69 Recommends: accountsservice,
70 Description: Poll daemon for notifications though the Ubuntu Push Client
71- This component polls facebook, twitter and gmail for updates and
72+ This component polls twitter and gmail for updates and
73 communicates with the postal service provided by the ubuntu push client
74 to expose notifications for the click webapps for the aforementioned
75 services.
76
77=== removed directory 'plugins/facebook'
78=== removed file 'plugins/facebook/facebook.go'
79--- plugins/facebook/facebook.go 2015-03-20 14:34:48 +0000
80+++ plugins/facebook/facebook.go 1970-01-01 00:00:00 +0000
81@@ -1,453 +0,0 @@
82-/*
83- Copyright 2014 Canonical Ltd.
84-
85- This program is free software: you can redistribute it and/or modify it
86- under the terms of the GNU General Public License version 3, as published
87- by the Free Software Foundation.
88-
89- This program is distributed in the hope that it will be useful, but
90- WITHOUT ANY WARRANTY; without even the implied warranties of
91- MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
92- PURPOSE. See the GNU General Public License for more details.
93-
94- You should have received a copy of the GNU General Public License along
95- with this program. If not, see <http://www.gnu.org/licenses/>.
96-*/
97-
98-package facebook
99-
100-import (
101- "encoding/json"
102- "fmt"
103- "net/http"
104- "net/url"
105- "os"
106- "strings"
107- "time"
108-
109- "log"
110-
111- "launchpad.net/account-polld/accounts"
112- "launchpad.net/account-polld/gettext"
113- "launchpad.net/account-polld/plugins"
114-)
115-
116-const (
117- facebookTime = "2006-01-02T15:04:05-0700"
118- maxIndividualNotifications = 2
119- consolidatedNotificationsIndexStart = maxIndividualNotifications
120- maxIndividualThreads = 2
121- consolidatedThreadsIndexStart = maxIndividualThreads
122- timeOffset = -5
123- pluginName = "facebook"
124-)
125-
126-var baseUrl, _ = url.Parse("https://graph.facebook.com/v2.0/")
127-
128-type timeStamp string
129-
130-func (state fbState) persist(accountId uint) (err error) {
131- err = plugins.Persist(pluginName, accountId, state)
132- if err != nil {
133- log.Print("facebook plugin", accountId, ": failed to save state: ", err)
134- return err
135- }
136- return nil
137-}
138-
139-func stateFromStorage(accountId uint) (state fbState, err error) {
140- err = plugins.FromPersist(pluginName, accountId, &state)
141- if err != nil {
142- return state, err
143- }
144- if _, err := time.Parse(facebookTime, string(state.LastUpdate)); err != nil {
145- return state, err
146- }
147- if _, err := time.Parse(facebookTime, string(state.LastInboxUpdate)); err != nil {
148- return state, err
149- }
150- return state, nil
151-}
152-
153-type fbState struct {
154- LastUpdate timeStamp `json:"last_notification_update"`
155- LastInboxUpdate timeStamp `json:"last_inbox_update"`
156-}
157-
158-type fbPlugin struct {
159- state fbState
160- accountId uint
161-}
162-
163-var doRequest = request
164-
165-func request(authData *accounts.AuthData, path string) (*http.Response, error) {
166- // Resolve path relative to Graph API base URL, and add access token
167- u, err := baseUrl.Parse(path)
168- if err != nil {
169- return nil, err
170- }
171- query := u.Query()
172- query.Add("access_token", authData.AccessToken)
173- u.RawQuery = query.Encode()
174-
175- return http.Get(u.String())
176-}
177-
178-func New(accountId uint) plugins.Plugin {
179- state, err := stateFromStorage(accountId)
180- if err != nil {
181- log.Print("facebook plugin ", accountId, ": cannot load previous state from storage: ", err)
182- } else {
183- log.Print("facebook plugin ", accountId, ": last state loaded from storage")
184- }
185- return &fbPlugin{state: state, accountId: accountId}
186-}
187-
188-func (p *fbPlugin) ApplicationId() plugins.ApplicationId {
189- return "com.ubuntu.developer.webapps.webapp-facebook_webapp-facebook"
190-}
191-
192-func (p *fbPlugin) decodeResponse(resp *http.Response, result interface{}) error {
193- defer resp.Body.Close()
194- decoder := json.NewDecoder(resp.Body)
195- if resp.StatusCode != http.StatusOK {
196- var result errorDoc
197- if err := decoder.Decode(&result); err != nil {
198- return err
199- }
200- if result.Error.Code == 190 {
201- return plugins.ErrTokenExpired
202- }
203- return &result.Error
204- }
205- // TODO: Follow the "paging.next" link if we get more than one
206- // page full of notifications.
207- return decoder.Decode(result)
208-}
209-
210-func (p *fbPlugin) filterNotifications(doc Document, lastUpdate *timeStamp) []Notification {
211- var validNotifications []Notification
212- latestUpdate := *lastUpdate
213- for i := 0; i < doc.size(); i++ {
214- n := doc.notification(i)
215- if !n.isValid(*lastUpdate) {
216- log.Println("facebook plugin: skipping:", n)
217- } else {
218- log.Println("facebook plugin: valid:", n)
219- validNotifications = append(validNotifications, n) // get the actual reference, not the copy
220- if n.updatedTime() > latestUpdate {
221- latestUpdate = n.updatedTime()
222- }
223- }
224- }
225- *lastUpdate = latestUpdate
226- p.state.persist(p.accountId)
227- return validNotifications
228-}
229-
230-func (p *fbPlugin) buildPushMessages(notifications []Notification, doc Document, max int, consolidatedIndexStart int) *plugins.PushMessageBatch {
231- pushMsg := make([]*plugins.PushMessage, len(notifications))
232- for i, n := range notifications {
233- pushMsg[i] = n.buildPushMessage()
234- }
235- return &plugins.PushMessageBatch{
236- Messages: pushMsg,
237- Limit: max,
238- OverflowHandler: doc.handleOverflow,
239- Tag: doc.getTag(),
240- }
241-}
242-
243-func (p *fbPlugin) parseResponse(resp *http.Response) (*plugins.PushMessageBatch, error) {
244- var result notificationDoc
245- if err := p.decodeResponse(resp, &result); err != nil {
246- return nil, err
247- }
248- // TODO filter out of date messages before operating?
249- validNotifications := p.filterNotifications(&result, &p.state.LastUpdate)
250- pushMsgs := p.buildPushMessages(validNotifications, &result, maxIndividualNotifications, consolidatedNotificationsIndexStart)
251- return pushMsgs, nil
252-}
253-
254-func (p *fbPlugin) parseInboxResponse(resp *http.Response) (*plugins.PushMessageBatch, error) {
255- var result inboxDoc
256- if err := p.decodeResponse(resp, &result); err != nil {
257- return nil, err
258- }
259- validThreads := p.filterNotifications(&result, &p.state.LastInboxUpdate)
260- pushMsgs := p.buildPushMessages(validThreads, &result, maxIndividualThreads, consolidatedThreadsIndexStart)
261- return pushMsgs, nil
262-}
263-
264-func (p *fbPlugin) getNotifications(authData *accounts.AuthData) (*plugins.PushMessageBatch, error) {
265- resp, err := doRequest(authData, "me/notifications")
266- if err != nil {
267- log.Println("facebook plugin: notifications poll failed: ", err)
268- return nil, err
269- }
270- notifications, err := p.parseResponse(resp)
271- if err != nil {
272- log.Println("facebook plugin: failed to parse notification response: ", err)
273- return nil, err
274- }
275- return notifications, nil
276-}
277-
278-func (p *fbPlugin) getInbox(authData *accounts.AuthData) (*plugins.PushMessageBatch, error) {
279- resp, err := doRequest(authData, "me/inbox?fields=unread,unseen,comments.limit(1)")
280- if err != nil {
281- log.Println("facebook plugin: inbox poll failed: ", err)
282- return nil, err
283- }
284- inbox, err := p.parseInboxResponse(resp)
285- if err != nil {
286- log.Println("facebook plugin: failed to parse inbox response: ", err)
287- return nil, err
288- }
289- return inbox, nil
290-}
291-
292-func (p *fbPlugin) Poll(authData *accounts.AuthData) ([]*plugins.PushMessageBatch, error) {
293- // This envvar check is to ease testing.
294- if token := os.Getenv("ACCOUNT_POLLD_TOKEN_FACEBOOK"); token != "" {
295- authData.AccessToken = token
296- }
297- notifications, notifErr := p.getNotifications(authData)
298- inbox, inboxErr := p.getInbox(authData)
299- // only return error if both requests failed
300- if notifErr != nil && inboxErr != nil {
301- return nil, fmt.Errorf("poll failed with '%s' and '%s'", notifErr, inboxErr)
302- }
303- if notifErr != nil {
304- return []*plugins.PushMessageBatch{inbox}, nil
305- }
306- if inboxErr != nil {
307- return []*plugins.PushMessageBatch{notifications}, nil
308- }
309- return []*plugins.PushMessageBatch{notifications, inbox}, nil
310-}
311-
312-func toEpoch(stamp timeStamp) int64 {
313- if t, err := time.Parse(facebookTime, string(stamp)); err == nil {
314- return t.Unix()
315- }
316- return time.Now().Unix()
317-}
318-
319-// The notifications response format is described here:
320-// https://developers.facebook.com/docs/graph-api/reference/v2.0/user/notifications/
321-type notificationDoc struct {
322- Data []notification `json:"data"`
323- Paging struct {
324- Previous string `json:"previous"`
325- Next string `json:"next"`
326- } `json:"paging"`
327-}
328-
329-type notification struct {
330- Id string `json:"id"`
331- From object `json:"from"`
332- To object `json:"to"`
333- CreatedTime timeStamp `json:"created_time"`
334- UpdatedTime timeStamp `json:"updated_time"`
335- Title string `json:"title"`
336- Link string `json:"link"`
337- Application object `json:"application"`
338- Unread int `json:"unread"`
339- Object object `json:"object"`
340-}
341-
342-func picture(msgId string, id string) string {
343- u, err := baseUrl.Parse(fmt.Sprintf("%s/picture", id))
344- if err != nil {
345- log.Println("facebook plugin: cannot get picture for", msgId)
346- return ""
347- }
348- query := u.Query()
349- query.Add("redirect", "true")
350- u.RawQuery = query.Encode()
351- return u.String()
352-}
353-
354-type object struct {
355- Id string `json:"id"`
356- Name string `json:"name"`
357-}
358-
359-// The error response format is described here:
360-// https://developers.facebook.com/docs/graph-api/using-graph-api/v2.0#errors
361-type errorDoc struct {
362- Error GraphError `json:"error"`
363-}
364-
365-type GraphError struct {
366- Message string `json:"message"`
367- Type string `json:"type"`
368- Code int `json:"code"`
369- Subcode int `json:"error_subcode"`
370-}
371-
372-func (err *GraphError) Error() string {
373- return err.Message
374-}
375-
376-// The inbox response format is described here:
377-// https://developers.facebook.com/docs/graph-api/reference/v2.0/user/inbox
378-type inboxDoc struct {
379- Data []thread `json:"data"`
380- Paging struct {
381- Previous string `json:"previous"`
382- Next string `json:"next"`
383- } `json:"paging"`
384- Summary summary `json:"summary"`
385-}
386-
387-type summary struct {
388- UnseenCount int `json:"unseen_count"`
389- UnreadCount int `json:"unread_count"`
390- UpdatedTime timeStamp `json:"updated_time"`
391-}
392-
393-type thread struct {
394- Id string `json:"id"`
395- Comments comments `json:"comments"`
396- To []object `json:"to"`
397- Unread int `json:"unread"`
398- Unseen int `json:"unseen"`
399- UpdatedTime timeStamp `json:"updated_time"`
400- Paging struct {
401- Previous string `json:"previous"`
402- Next string `json:"next"`
403- } `json:"paging"`
404-}
405-
406-type comments struct {
407- Data []message `json:"data"`
408- Paging struct {
409- Previous string `json:"previous"`
410- Next string `json:"next"`
411- } `json:"paging"`
412-}
413-
414-type message struct {
415- CreatedTime timeStamp `json:"created_time"`
416- From object `json:"from"`
417- Id string `json:"id"`
418- Message string `json:message`
419-}
420-
421-func (doc *inboxDoc) getTag() string {
422- return "inbox"
423-}
424-
425-func (doc *inboxDoc) handleOverflow(pushMsg []*plugins.PushMessage) *plugins.PushMessage {
426- usernames := []string{}
427- for _, m := range pushMsg {
428- usernames = append(usernames, m.Notification.Card.Summary)
429- if len(usernames) > 10 {
430- usernames[10] = "…"
431- break
432- }
433- }
434- // TRANSLATORS: This represents a message summary about more facebook messages
435- summary := gettext.Gettext("Multiple more messages")
436- // TRANSLATORS: This represents a message body with the comma separated facebook usernames
437- body := fmt.Sprintf(gettext.Gettext("From %s"), strings.Join(usernames, ", "))
438- action := "https://m.facebook.com/messages"
439- epoch := time.Now().Unix()
440- return plugins.NewStandardPushMessage(summary, body, action, "", epoch)
441-}
442-
443-func (doc *inboxDoc) size() int {
444- return len(doc.Data)
445-}
446-
447-func (doc *inboxDoc) notification(idx int) Notification {
448- return &doc.Data[idx]
449-}
450-
451-func (t *thread) buildPushMessage() *plugins.PushMessage {
452- link := "https://www.facebook.com/messages?action=recent-messages"
453- epoch := toEpoch(t.UpdatedTime)
454- // get the single message we fetch
455- message := t.Comments.Data[0]
456- return plugins.NewStandardPushMessage(message.From.Name, message.Message, link, picture(t.Id, message.From.Id), epoch)
457-}
458-
459-func (t *thread) isValid(tStamp timeStamp) bool {
460- limit := timeStamp(time.Now().Add(timeOffset * time.Minute).Format(facebookTime))
461- return tStamp < t.UpdatedTime && t.UpdatedTime < limit && t.Unread != 0 && t.Unseen != 0
462-}
463-
464-func (t *thread) updatedTime() timeStamp {
465- return t.UpdatedTime
466-}
467-
468-func (t *thread) String() string {
469- return fmt.Sprintf("id: %s, dated: %s, unread: %d, unseen: %d", t.Id, t.UpdatedTime, t.Unread, t.Unseen)
470-}
471-
472-func (doc *notificationDoc) getTag() string {
473- return "notification"
474-}
475-
476-func (doc *notificationDoc) handleOverflow(pushMsg []*plugins.PushMessage) *plugins.PushMessage {
477- usernames := []string{}
478- for _, m := range pushMsg {
479- usernames = append(usernames, m.Notification.Card.Summary)
480- if len(usernames) > 10 {
481- usernames[10] = "…"
482- break
483- }
484- }
485- // TRANSLATORS: This represents a notification summary about more facebook notifications
486- summary := gettext.Gettext("Multiple more notifications")
487- // TRANSLATORS: This represents a notification body with the comma separated facebook usernames
488- body := fmt.Sprintf(gettext.Gettext("From %s"), strings.Join(usernames, ", "))
489- action := "https://m.facebook.com"
490- epoch := time.Now().Unix()
491- return plugins.NewStandardPushMessage(summary, body, action, "", epoch)
492-}
493-
494-func (doc *notificationDoc) size() int {
495- return len(doc.Data)
496-}
497-
498-func (doc *notificationDoc) notification(idx int) Notification {
499- return &doc.Data[idx]
500-}
501-
502-func (n *notification) buildPushMessage() *plugins.PushMessage {
503- epoch := toEpoch(n.UpdatedTime)
504- return plugins.NewStandardPushMessage(n.From.Name, n.Title, n.Link, picture(n.Id, n.From.Id), epoch)
505-}
506-
507-func (n *notification) isValid(tStamp timeStamp) bool {
508- return n.UpdatedTime > tStamp && n.Unread >= 1
509-}
510-
511-func (n *notification) updatedTime() timeStamp {
512- return n.UpdatedTime
513-}
514-
515-func (n *notification) String() string {
516- return fmt.Sprintf("id: %s, dated: %s, unread: %d", n.Id, n.UpdatedTime, n.Unread)
517-}
518-
519-type Document interface {
520- getTag() string
521- handleOverflow([]*plugins.PushMessage) *plugins.PushMessage
522- size() int
523- notification(int) Notification
524-}
525-
526-type Notification interface {
527- buildPushMessage() *plugins.PushMessage
528- isValid(timeStamp) bool
529- updatedTime() timeStamp
530- String() string
531-}
532-
533-var _ Notification = (*thread)(nil)
534-var _ Notification = (*notification)(nil)
535
536=== removed file 'plugins/facebook/facebook_test.go'
537--- plugins/facebook/facebook_test.go 2015-03-20 14:34:48 +0000
538+++ plugins/facebook/facebook_test.go 1970-01-01 00:00:00 +0000
539@@ -1,835 +0,0 @@
540-/*
541- Copyright 2014 Canonical Ltd.
542-
543- This program is free software: you can redistribute it and/or modify it
544- under the terms of the GNU General Public License version 3, as published
545- by the Free Software Foundation.
546-
547- This program is distributed in the hope that it will be useful, but
548- WITHOUT ANY WARRANTY; without even the implied warranties of
549- MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
550- PURPOSE. See the GNU General Public License for more details.
551-
552- You should have received a copy of the GNU General Public License along
553- with this program. If not, see <http://www.gnu.org/licenses/>.
554-*/
555-package facebook
556-
557-import (
558- "bytes"
559- "encoding/json"
560- "fmt"
561- "io"
562- "io/ioutil"
563- "net/http"
564- "net/url"
565- "os"
566- "path"
567- "path/filepath"
568- "time"
569-
570- "net/http/httptest"
571- "testing"
572-
573- . "launchpad.net/gocheck"
574-
575- "launchpad.net/account-polld/accounts"
576- "launchpad.net/account-polld/plugins"
577- "launchpad.net/go-xdg/v0"
578-)
579-
580-type S struct {
581- tempDir string
582- ts *httptest.Server
583-}
584-
585-var _ = Suite(&S{})
586-
587-func Test(t *testing.T) { TestingT(t) }
588-
589-// closeWraper adds a dummy Close() method to a reader
590-type closeWrapper struct {
591- io.Reader
592-}
593-
594-func (r closeWrapper) Close() error {
595- return nil
596-}
597-
598-const (
599- errorBody = `
600-{
601- "error": {
602- "message": "Unknown path components: /xyz",
603- "type": "OAuthException",
604- "code": 2500
605- }
606-}`
607- tokenExpiredErrorBody = `
608-{
609- "error": {
610- "message": "Error validating access token: Session has expired",
611- "type": "OAuthException",
612- "code": 190 ,
613- "error_subcode": 463
614- }
615-}`
616- notificationsBody = `
617-{
618- "data": [
619- {
620- "id": "notif_id",
621- "from": {
622- "id": "sender_id",
623- "name": "Sender"
624- },
625- "to": {
626- "id": "recipient_id",
627- "name": "Recipient"
628- },
629- "created_time": "2014-07-12T09:51:57+0000",
630- "updated_time": "2014-07-12T09:51:57+0000",
631- "title": "Sender posted on your timeline: \"The message...\"",
632- "link": "http://www.facebook.com/recipient/posts/id",
633- "application": {
634- "name": "Wall",
635- "namespace": "wall",
636- "id": "2719290516"
637- },
638- "unread": 1
639- },
640- {
641- "id": "notif_1105650586_80600069",
642- "from": {
643- "id": "sender2_id",
644- "name": "Sender2"
645- },
646- "to": {
647- "id": "recipient_id",
648- "name": "Recipient"
649- },
650- "created_time": "2014-07-08T06:17:52+0000",
651- "updated_time": "2014-07-08T06:17:52+0000",
652- "title": "Sender2's birthday was on July 7.",
653- "link": "http://www.facebook.com/profile.php?id=xxx&ref=brem",
654- "application": {
655- "name": "Gifts",
656- "namespace": "superkarma",
657- "id": "329122197162272"
658- },
659- "unread": 1,
660- "object": {
661- "id": "sender2_id",
662- "name": "Sender2"
663- }
664- }
665- ],
666- "paging": {
667- "previous": "https://graph.facebook.com/v2.0/recipient/notifications?limit=5000&since=1405158717&__paging_token=enc_AewDzwIQmWOwPNO-36GaZsaJAog8l93HQ7uLEO-gp1Tb6KCiolXfzMCcGY2KjrJJsDJXdDmNJObICr5dewfMZgGs",
668- "next": "https://graph.facebook.com/v2.0/recipient/notifications?limit=5000&until=1404705077&__paging_token=enc_Aewlhut5DQyhqtLNr7pLCMlYU012t4XY7FOt7cooz4wsWIWi-Jqz0a0IDnciJoeLu2vNNQkbtOpCmEmsVsN4hkM4"
669- },
670- "summary": [
671- ]
672-}
673-`
674- largeNotificationsBody = `
675-{
676- "data": [
677- {
678- "id": "notif_id",
679- "from": {
680- "id": "sender_id",
681- "name": "Sender"
682- },
683- "to": {
684- "id": "recipient_id",
685- "name": "Recipient"
686- },
687- "created_time": "2014-07-12T09:51:57+0000",
688- "updated_time": "2014-07-12T09:51:57+0000",
689- "title": "Sender posted on your timeline: \"The message...\"",
690- "link": "http://www.facebook.com/recipient/posts/id",
691- "application": {
692- "name": "Wall",
693- "namespace": "wall",
694- "id": "2719290516"
695- },
696- "unread": 1
697- },
698- {
699- "id": "notif_1105650586_80600069",
700- "from": {
701- "id": "sender2_id",
702- "name": "Sender2"
703- },
704- "to": {
705- "id": "recipient_id",
706- "name": "Recipient"
707- },
708- "created_time": "2014-07-08T06:17:52+0000",
709- "updated_time": "2014-07-08T06:17:52+0000",
710- "title": "Sender2's birthday was on July 7.",
711- "link": "http://www.facebook.com/profile.php?id=xxx&ref=brem",
712- "application": {
713- "name": "Gifts",
714- "namespace": "superkarma",
715- "id": "329122197162272"
716- },
717- "unread": 1,
718- "object": {
719- "id": "sender2_id",
720- "name": "Sender2"
721- }
722- },
723- {
724- "id": "notif_id_3",
725- "from": {
726- "id": "sender3_id",
727- "name": "Sender3"
728- },
729- "to": {
730- "id": "recipient_id",
731- "name": "Recipient"
732- },
733- "created_time": "2014-07-12T09:51:57+0000",
734- "updated_time": "2014-07-12T09:51:57+0000",
735- "title": "Sender posted on your timeline: \"The message...\"",
736- "link": "http://www.facebook.com/recipient/posts/id",
737- "application": {
738- "name": "Wall",
739- "namespace": "wall",
740- "id": "2719290516"
741- },
742- "unread": 1
743- },
744- {
745- "id": "notif_id_4",
746- "from": {
747- "id": "sender4_id",
748- "name": "Sender2"
749- },
750- "to": {
751- "id": "recipient_id",
752- "name": "Recipient"
753- },
754- "created_time": "2014-07-08T06:17:52+0000",
755- "updated_time": "2014-07-08T06:17:52+0000",
756- "title": "Sender2's birthday was on July 7.",
757- "link": "http://www.facebook.com/profile.php?id=xxx&ref=brem",
758- "application": {
759- "name": "Gifts",
760- "namespace": "superkarma",
761- "id": "329122197162272"
762- },
763- "unread": 1
764- }
765- ],
766- "paging": {
767- "previous": "https://graph.facebook.com/v2.0/recipient/notifications?limit=5000&since=1405158717&__paging_token=enc_AewDzwIQmWOwPNO-36GaZsaJAog8l93HQ7uLEO-gp1Tb6KCiolXfzMCcGY2KjrJJsDJXdDmNJObICr5dewfMZgGs",
768- "next": "https://graph.facebook.com/v2.0/recipient/notifications?limit=5000&until=1404705077&__paging_token=enc_Aewlhut5DQyhqtLNr7pLCMlYU012t4XY7FOt7cooz4wsWIWi-Jqz0a0IDnciJoeLu2vNNQkbtOpCmEmsVsN4hkM4"
769- },
770- "summary": [
771- ]
772-}
773-`
774-
775- inboxBody = `
776-{
777- "data": [
778- {
779- "unread": 1,
780- "unseen": 1,
781- "id": "445809168892281",
782- "updated_time": "2014-08-25T18:39:32+0000",
783- "comments": {
784- "data": [
785- {
786- "id": "445809168892281_1408991972",
787- "from": {
788- "id": "346217352202239",
789- "name": "Pollod Magnifico"
790- },
791- "message": "Hola mundo!",
792- "created_time": "2014-08-25T18:39:32+0000"
793- }
794- ],
795- "paging": {
796- "previous": "https://graph.facebook.com/v2.0/445809168892281/comments?limit=1&since=1408991972&__paging_token=enc_Aew2kKJXEXzdm9k89DvLYz_y8nYxUbvElWcn6h_pKMRsoAPTPpkU7-AsGhkcYF6M1qbomOnFJf9ckL5J3hTltLFq",
797- "next": "https://graph.facebook.com/v2.0/445809168892281/comments?limit=1&until=1408991972&__paging_token=enc_Aewlixpk4h4Vq79-W1ixrTM6ONbsMUDrcj0vLABs34tbhWarfpQLf818uoASWNDEpQO4XEXh5HbgHpcCqnuNVEOR"
798- }
799- }
800- },
801- {
802- "unread": 2,
803- "unseen": 1,
804- "id": "445809168892282",
805- "updated_time": "2014-08-25T18:39:32+0000",
806- "comments": {
807- "data": [
808- {
809- "id": "445809168892282_1408991973",
810- "from": {
811- "id": "346217352202239",
812- "name": "Pollitod Magnifico"
813- },
814- "message": "Hola!",
815- "created_time": "2014-08-25T18:39:32+0000"
816- }
817- ],
818- "paging": {
819- "previous": "https://graph.facebook.com/v2.0/445809168892281/comments?limit=1&since=1408991972&__paging_token=enc_Aew2kKJXEXzdm9k89DvLYz_y8nYxUbvElWcn6h_pKMRsoAPTPpkU7-AsGhkcYF6M1qbomOnFJf9ckL5J3hTltLFq",
820- "next": "https://graph.facebook.com/v2.0/445809168892281/comments?limit=1&until=1408991972&__paging_token=enc_Aewlixpk4h4Vq79-W1ixrTM6ONbsMUDrcj0vLABs34tbhWarfpQLf818uoASWNDEpQO4XEXh5HbgHpcCqnuNVEOR"
821- }
822- }
823- },
824- {
825- "unread": 2,
826- "unseen": 1,
827- "id": "445809168892283",
828- "updated_time": "2014-08-25T18:39:32+0000",
829- "comments": {
830- "data": [
831- {
832- "id": "445809168892282_1408991973",
833- "from": {
834- "id": "346217352202240",
835- "name": "A Friend"
836- },
837- "message": "mellon",
838- "created_time": "2014-08-25T18:39:32+0000"
839- }
840- ],
841- "paging": {
842- "previous": "https://graph.facebook.com/v2.0/445809168892281/comments?limit=1&since=1408991972&__paging_token=enc_Aew2kKJXEXzdm9k89DvLYz_y8nYxUbvElWcn6h_pKMRsoAPTPpkU7-AsGhkcYF6M1qbomOnFJf9ckL5J3hTltLFq",
843- "next": "https://graph.facebook.com/v2.0/445809168892281/comments?limit=1&until=1408991972&__paging_token=enc_Aewlixpk4h4Vq79-W1ixrTM6ONbsMUDrcj0vLABs34tbhWarfpQLf818uoASWNDEpQO4XEXh5HbgHpcCqnuNVEOR"
844- }
845- }
846- }
847-
848-
849- ],
850- "paging": {
851- "previous": "https://graph.facebook.com/v2.0/270128826512416/inbox?fields=unread,unseen,comments.limit(1)&limit=25&since=1408991972&__paging_token=enc_Aey99ACSOyZqN_7I-yWLnY8K3dqu4wVsx-Th3kMHMTMQ5VPbQRPgCQiJps0II1QAXDAVzHplqPS8yNgq8Zs_G2aK",
852- "next": "https://graph.facebook.com/v2.0/270128826512416/inbox?fields=unread,unseen,comments.limit(1)&limit=25&until=1408991972&__paging_token=enc_AewjHkk10NNjRCXJCoaP5hyf22kw-htwxsDaVOiLY-IiXxB99sKNGlfFFmkcG-VeMGUETI2agZGR_1IWP5W4vyPL"
853- },
854- "summary": {
855- "unseen_count": 0,
856- "unread_count": 1,
857- "updated_time": "2014-08-25T19:05:49+0000"
858- }
859-}
860-`
861-)
862-
863-func (s *S) SetUpTest(c *C) {
864- s.tempDir = c.MkDir()
865- plugins.XdgDataFind = func(a string) (string, error) {
866- return filepath.Join(s.tempDir, a), nil
867- }
868- plugins.XdgDataEnsure = func(a string) (string, error) {
869- p := filepath.Join(s.tempDir, a)
870- base := path.Dir(p)
871- if _, err := os.Stat(base); err != nil {
872- os.MkdirAll(base, 0700)
873- }
874- return p, nil
875- }
876- s.ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
877- fmt.Fprintln(w, "Hello, client")
878- }))
879- baseUrl, _ = url.Parse(s.ts.URL)
880-}
881-
882-func (s *S) TearDownTest(c *C) {
883- plugins.XdgDataFind = xdg.Data.Find
884- plugins.XdgDataEnsure = xdg.Data.Find
885- doRequest = request
886- s.ts.Close()
887-}
888-
889-func (s *S) TestParseNotifications(c *C) {
890- resp := &http.Response{
891- StatusCode: http.StatusOK,
892- Body: closeWrapper{bytes.NewReader([]byte(notificationsBody))},
893- }
894- p := &fbPlugin{}
895- batch, err := p.parseResponse(resp)
896- c.Assert(err, IsNil)
897- c.Assert(batch, NotNil)
898- messages := batch.Messages
899- c.Assert(len(messages), Equals, 2)
900- c.Check(messages[0].Notification.Card.Summary, Equals, "Sender")
901- c.Check(messages[0].Notification.Card.Body, Equals, "Sender posted on your timeline: \"The message...\"")
902- c.Check(messages[1].Notification.Card.Summary, Equals, "Sender2")
903- c.Check(messages[1].Notification.Card.Body, Equals, "Sender2's birthday was on July 7.")
904- c.Check(p.state.LastUpdate, Equals, timeStamp("2014-07-12T09:51:57+0000"))
905-}
906-
907-func (s *S) TestParseLotsOfNotifications(c *C) {
908- resp := &http.Response{
909- StatusCode: http.StatusOK,
910- Body: closeWrapper{bytes.NewReader([]byte(largeNotificationsBody))},
911- }
912- p := &fbPlugin{}
913- batch, err := p.parseResponse(resp)
914- c.Assert(err, IsNil)
915- c.Assert(batch, NotNil)
916- messages := batch.Messages
917- c.Assert(len(messages), Equals, 4)
918- c.Check(messages[0].Notification.Card.Summary, Equals, "Sender")
919- c.Check(messages[0].Notification.Card.Body, Equals, "Sender posted on your timeline: \"The message...\"")
920- c.Check(messages[1].Notification.Card.Summary, Equals, "Sender2")
921- c.Check(messages[1].Notification.Card.Body, Equals, "Sender2's birthday was on July 7.")
922- ofMsg := batch.OverflowHandler(messages[2:])
923- c.Check(ofMsg.Notification.Card.Summary, Equals, "Multiple more notifications")
924- c.Check(ofMsg.Notification.Card.Body, Equals, "From Sender3, Sender2")
925- c.Check(p.state.LastUpdate, Equals, timeStamp("2014-07-12T09:51:57+0000"))
926-}
927-
928-func (s *S) TestIgnoreOldNotifications(c *C) {
929- resp := &http.Response{
930- StatusCode: http.StatusOK,
931- Body: closeWrapper{bytes.NewReader([]byte(notificationsBody))},
932- }
933- p := &fbPlugin{state: fbState{LastUpdate: "2014-07-08T06:17:52+0000"}}
934- batch, err := p.parseResponse(resp)
935- c.Assert(err, IsNil)
936- c.Assert(batch, NotNil)
937- messages := batch.Messages
938- c.Assert(len(messages), Equals, 1)
939- c.Check(messages[0].Notification.Card.Summary, Equals, "Sender")
940- c.Check(messages[0].Notification.Card.Body, Equals, "Sender posted on your timeline: \"The message...\"")
941- c.Check(p.state.LastUpdate, Equals, timeStamp("2014-07-12T09:51:57+0000"))
942-}
943-
944-func (s *S) TestParseResponseErrorResponse(c *C) {
945- resp := &http.Response{
946- StatusCode: http.StatusBadRequest,
947- Body: closeWrapper{bytes.NewReader([]byte(errorBody))},
948- }
949- p := &fbPlugin{}
950- notifications, err := p.parseResponse(resp)
951- c.Check(notifications, IsNil)
952- c.Assert(err, Not(IsNil))
953- graphErr := err.(*GraphError)
954- c.Check(graphErr.Message, Equals, "Unknown path components: /xyz")
955- c.Check(graphErr.Code, Equals, 2500)
956-}
957-
958-func (s *S) TestDecodeResponseErrorResponse(c *C) {
959- resp := &http.Response{
960- StatusCode: http.StatusBadRequest,
961- Body: closeWrapper{bytes.NewReader([]byte(errorBody))},
962- }
963- p := &fbPlugin{}
964- var result notificationDoc
965- err := p.decodeResponse(resp, &result)
966- c.Check(result, DeepEquals, notificationDoc{})
967- c.Assert(err, Not(IsNil))
968- graphErr := err.(*GraphError)
969- c.Check(graphErr.Message, Equals, "Unknown path components: /xyz")
970- c.Check(graphErr.Code, Equals, 2500)
971-}
972-
973-func (s *S) TestDecodeResponseErrorResponseFails(c *C) {
974- resp := &http.Response{
975- StatusCode: http.StatusBadRequest,
976- Body: closeWrapper{bytes.NewReader([]byte("hola" + errorBody))},
977- }
978- p := &fbPlugin{}
979- var result notificationDoc
980- err := p.decodeResponse(resp, &result)
981- c.Check(result, DeepEquals, notificationDoc{})
982- c.Assert(err, NotNil)
983- jsonErr := err.(*json.SyntaxError)
984- c.Check(jsonErr.Offset, Equals, int64(1))
985-}
986-
987-func (s *S) TestTokenExpiredErrorResponse(c *C) {
988- resp := &http.Response{
989- StatusCode: http.StatusBadRequest,
990- Body: closeWrapper{bytes.NewReader([]byte(tokenExpiredErrorBody))},
991- }
992- p := &fbPlugin{}
993- notifications, err := p.parseResponse(resp)
994- c.Check(notifications, IsNil)
995- c.Assert(err, Equals, plugins.ErrTokenExpired)
996-}
997-
998-func (s *S) TestParseInbox(c *C) {
999- resp := &http.Response{
1000- StatusCode: http.StatusOK,
1001- Body: closeWrapper{bytes.NewReader([]byte(inboxBody))},
1002- }
1003- p := &fbPlugin{}
1004- batch, err := p.parseInboxResponse(resp)
1005- c.Assert(err, IsNil)
1006- c.Assert(batch, NotNil)
1007- messages := batch.Messages
1008- c.Assert(len(messages), Equals, 3)
1009- c.Check(messages[0].Notification.Card.Summary, Equals, "Pollod Magnifico")
1010- c.Check(messages[0].Notification.Card.Body, Equals, "Hola mundo!")
1011- c.Check(messages[1].Notification.Card.Summary, Equals, "Pollitod Magnifico")
1012- c.Check(messages[1].Notification.Card.Body, Equals, "Hola!")
1013-
1014- ofMsg := batch.OverflowHandler(messages[batch.Limit:])
1015-
1016- c.Check(ofMsg.Notification.Card.Summary, Equals, "Multiple more messages")
1017- c.Check(ofMsg.Notification.Card.Body, Equals, "From A Friend")
1018- c.Check(p.state.LastInboxUpdate, Equals, timeStamp("2014-08-25T18:39:32+0000"))
1019-}
1020-
1021-func (s *S) TestDecodeResponse(c *C) {
1022- resp := &http.Response{
1023- StatusCode: http.StatusOK,
1024- Body: closeWrapper{bytes.NewReader([]byte(inboxBody))},
1025- }
1026- p := &fbPlugin{}
1027- var doc inboxDoc
1028- err := p.decodeResponse(resp, &doc)
1029- c.Check(err, IsNil)
1030- c.Check(len(doc.Data), Equals, 3)
1031-}
1032-
1033-func (s *S) TestDecodeResponseFails(c *C) {
1034- resp := &http.Response{
1035- StatusCode: http.StatusOK,
1036- Body: closeWrapper{bytes.NewReader([]byte("hola" + inboxBody))},
1037- }
1038- p := &fbPlugin{}
1039- var doc inboxDoc
1040- err := p.decodeResponse(resp, &doc)
1041- c.Assert(err, NotNil)
1042- jsonErr := err.(*json.SyntaxError)
1043- c.Check(jsonErr.Offset, Equals, int64(1))
1044-}
1045-
1046-func (s *S) TestFilterNotifications(c *C) {
1047- resp := &http.Response{
1048- StatusCode: http.StatusOK,
1049- Body: closeWrapper{bytes.NewReader([]byte(inboxBody))},
1050- }
1051- p := &fbPlugin{}
1052- var doc inboxDoc
1053- p.decodeResponse(resp, &doc)
1054- var state fbState
1055- notifications := p.filterNotifications(&doc, &state.LastInboxUpdate)
1056- c.Check(notifications, HasLen, 3)
1057- // check if the lastInboxUpdate is updated
1058- c.Check(state.LastInboxUpdate, Equals, timeStamp("2014-08-25T18:39:32+0000"))
1059-}
1060-
1061-func (s *S) TestBuildPushMessages(c *C) {
1062- resp := &http.Response{
1063- StatusCode: http.StatusOK,
1064- Body: closeWrapper{bytes.NewReader([]byte(inboxBody))},
1065- }
1066- var state fbState
1067- p := &fbPlugin{state: state, accountId: 32}
1068- var doc inboxDoc
1069- p.decodeResponse(resp, &doc)
1070- notifications := p.filterNotifications(&doc, &p.state.LastInboxUpdate)
1071- batch := p.buildPushMessages(notifications, &doc, doc.size(), doc.size())
1072- c.Assert(batch, NotNil)
1073- c.Check(batch.Messages, HasLen, doc.size())
1074-}
1075-
1076-func (s *S) TestBuildPushMessagesConsolidate(c *C) {
1077- resp := &http.Response{
1078- StatusCode: http.StatusOK,
1079- Body: closeWrapper{bytes.NewReader([]byte(inboxBody))},
1080- }
1081- var state fbState
1082- p := &fbPlugin{state: state, accountId: 32}
1083- var doc inboxDoc
1084- p.decodeResponse(resp, &doc)
1085- notifications := p.filterNotifications(&doc, &p.state.LastInboxUpdate)
1086- max := doc.size() - 2
1087- batch := p.buildPushMessages(notifications, &doc, max, max)
1088- c.Assert(batch, NotNil)
1089- // we should get all the messages in a batch with Limit=max
1090- c.Check(batch.Messages, HasLen, len(notifications))
1091- c.Check(batch.Limit, Equals, max)
1092-}
1093-
1094-func (s *S) TestStateFromStorageInitialState(c *C) {
1095- state, err := stateFromStorage(32)
1096- c.Check(err, NotNil)
1097- c.Check(state.LastUpdate, Equals, timeStamp(""))
1098- c.Check(state.LastInboxUpdate, Equals, timeStamp(""))
1099-}
1100-
1101-func (s *S) TestStateFromStoragePersist(c *C) {
1102- filePath := filepath.Join(s.tempDir, "facebook.test/facebook-32.json")
1103- state, err := stateFromStorage(32)
1104- c.Check(err, NotNil)
1105- state.LastInboxUpdate = timeStamp("2014-08-25T18:39:32+0000")
1106- state.LastUpdate = timeStamp("2014-08-25T18:39:33+0000")
1107- err = state.persist(32)
1108- c.Check(err, IsNil)
1109- jsonData, err := ioutil.ReadFile(filePath)
1110- c.Check(err, IsNil)
1111- var data map[string]string
1112- json.Unmarshal(jsonData, &data)
1113- c.Check(data["last_inbox_update"], Equals, "2014-08-25T18:39:32+0000")
1114- c.Check(data["last_notification_update"], Equals, "2014-08-25T18:39:33+0000")
1115-}
1116-
1117-func (s *S) TestStateFromStoragePersistFails(c *C) {
1118- state := fbState{LastInboxUpdate: "2014-08-25T18:39:32+0000", LastUpdate: "2014-08-25T18:39:33+0000"}
1119- plugins.XdgDataEnsure = plugins.XdgDataFind
1120- err := state.persist(32)
1121- c.Check(err, NotNil)
1122-}
1123-
1124-func (s *S) TestStateFromStorage(c *C) {
1125- state := fbState{LastInboxUpdate: "2014-08-25T18:39:32+0000", LastUpdate: "2014-08-25T18:39:33+0000"}
1126- err := state.persist(32)
1127- c.Check(err, IsNil)
1128- newState, err := stateFromStorage(32)
1129- c.Check(err, IsNil)
1130- c.Check(newState.LastUpdate, Equals, timeStamp("2014-08-25T18:39:33+0000"))
1131- c.Check(newState.LastInboxUpdate, Equals, timeStamp("2014-08-25T18:39:32+0000"))
1132- // bad format
1133- state = fbState{LastInboxUpdate: "yesterday", LastUpdate: "2014-08-25T18:39:33+0000"}
1134- state.persist(32)
1135- _, err = stateFromStorage(32)
1136- c.Check(err, NotNil)
1137- state = fbState{LastInboxUpdate: "2014-08-25T18:39:33+0000", LastUpdate: "today"}
1138- state.persist(32)
1139- _, err = stateFromStorage(32)
1140- c.Check(err, NotNil)
1141-}
1142-
1143-func (s *S) TestNew(c *C) {
1144- state := fbState{LastInboxUpdate: "2014-08-25T18:39:32+0000", LastUpdate: "2014-08-25T18:39:33+0000"}
1145- state.persist(32)
1146- p := New(32)
1147- fb := p.(*fbPlugin)
1148- c.Check(fb.state, DeepEquals, state)
1149- // with bad format
1150- state = fbState{LastInboxUpdate: "hola", LastUpdate: "mundo"}
1151- state.persist(32)
1152- p = New(32)
1153- fb = p.(*fbPlugin)
1154- c.Check(fb.state, DeepEquals, state)
1155-}
1156-
1157-func (s *S) TestApplicationId(c *C) {
1158- expected := plugins.ApplicationId("com.ubuntu.developer.webapps.webapp-facebook_webapp-facebook")
1159- p := New(32)
1160- c.Check(p.ApplicationId(), Equals, expected)
1161-}
1162-
1163-func (s *S) TestThreadIsValid(c *C) {
1164- t := thread{}
1165- t.Unseen = 1
1166- t.Unread = 2
1167- // 10m before Now, the thread should be valid
1168- t.UpdatedTime = timeStamp(time.Now().Add(-10 * time.Minute).Format(facebookTime))
1169- tStamp := timeStamp(time.Now().Add(-20 * time.Minute).Format(facebookTime))
1170- c.Check(t.isValid(tStamp), Equals, true)
1171- // 2m before Now, the thread should be invalid
1172- t.UpdatedTime = timeStamp(time.Now().Add(-2 * time.Minute).Format(facebookTime))
1173- c.Check(t.isValid(tStamp), Equals, false)
1174- // unseen = 0
1175- t.Unseen = 0
1176- t.UpdatedTime = timeStamp(time.Now().Add(-10 * time.Minute).Format(facebookTime))
1177- c.Check(t.isValid(tStamp), Equals, false)
1178- // unread = 0, unseen = 1
1179- t.Unread = 0
1180- t.Unseen = 1
1181- c.Check(t.isValid(tStamp), Equals, false)
1182-}
1183-
1184-func (s *S) TestGetInbox(c *C) {
1185- doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) {
1186- return &http.Response{
1187- StatusCode: http.StatusOK,
1188- Body: closeWrapper{bytes.NewReader([]byte(inboxBody))},
1189- }, nil
1190- }
1191- var state fbState
1192- authData := accounts.AuthData{}
1193- authData.AccessToken = "foo"
1194- p := &fbPlugin{state: state, accountId: 32}
1195- batch, err := p.getInbox(&authData)
1196- c.Assert(err, IsNil)
1197- c.Assert(batch, NotNil)
1198- msgs := batch.Messages
1199- c.Check(msgs, HasLen, 3)
1200-}
1201-
1202-func (s *S) TestGetInboxRequestFails(c *C) {
1203- doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) {
1204- return &http.Response{
1205- StatusCode: http.StatusBadRequest,
1206- Body: closeWrapper{bytes.NewReader([]byte(""))},
1207- }, fmt.Errorf("please, fail")
1208- }
1209- var state fbState
1210- authData := accounts.AuthData{}
1211- authData.AccessToken = "foo"
1212- p := &fbPlugin{state: state, accountId: 32}
1213- _, err := p.getInbox(&authData)
1214- c.Check(err, NotNil)
1215-}
1216-
1217-func (s *S) TestGetInboxParseResponseFails(c *C) {
1218- doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) {
1219- return &http.Response{
1220- StatusCode: http.StatusOK,
1221- Body: closeWrapper{bytes.NewReader([]byte("hola" + inboxBody))},
1222- }, nil
1223- }
1224- var state fbState
1225- authData := accounts.AuthData{}
1226- authData.AccessToken = "foo"
1227- p := &fbPlugin{state: state, accountId: 32}
1228- _, err := p.getInbox(&authData)
1229- c.Check(err, NotNil)
1230-}
1231-
1232-func (s *S) TestGetNotifications(c *C) {
1233- doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) {
1234- return &http.Response{
1235- StatusCode: http.StatusOK,
1236- Body: closeWrapper{bytes.NewReader([]byte(notificationsBody))},
1237- }, nil
1238- }
1239- var state fbState
1240- authData := accounts.AuthData{}
1241- authData.AccessToken = "foo"
1242- p := &fbPlugin{state: state, accountId: 32}
1243- batch, err := p.getNotifications(&authData)
1244- c.Assert(err, IsNil)
1245- c.Assert(batch, NotNil)
1246- msgs := batch.Messages
1247- c.Check(msgs, HasLen, 2)
1248-}
1249-
1250-func (s *S) TestGetNotificationsRequestFails(c *C) {
1251- doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) {
1252- return &http.Response{
1253- StatusCode: http.StatusBadRequest,
1254- Body: closeWrapper{bytes.NewReader([]byte(""))},
1255- }, fmt.Errorf("please, fail")
1256- }
1257- var state fbState
1258- authData := accounts.AuthData{}
1259- authData.AccessToken = "foo"
1260- p := &fbPlugin{state: state, accountId: 32}
1261- _, err := p.getNotifications(&authData)
1262- c.Check(err, NotNil)
1263-}
1264-
1265-func (s *S) TestGetNotificationsParseResponseFails(c *C) {
1266- doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) {
1267- return &http.Response{
1268- StatusCode: http.StatusOK,
1269- Body: closeWrapper{bytes.NewReader([]byte("hola" + notificationsBody))},
1270- }, nil
1271- }
1272- var state fbState
1273- authData := accounts.AuthData{}
1274- authData.AccessToken = "foo"
1275- p := &fbPlugin{state: state, accountId: 32}
1276- _, err := p.getNotifications(&authData)
1277- c.Check(err, NotNil)
1278-}
1279-
1280-func (s *S) TestPoll(c *C) {
1281- doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) {
1282- body := inboxBody
1283- if url == "me/notifications" {
1284- body = notificationsBody
1285- }
1286- return &http.Response{
1287- StatusCode: http.StatusOK,
1288- Body: closeWrapper{bytes.NewReader([]byte(body))},
1289- }, nil
1290- }
1291- var state fbState
1292- authData := accounts.AuthData{}
1293- authData.AccessToken = "foo"
1294- p := &fbPlugin{state: state, accountId: 32}
1295- batches, err := p.Poll(&authData)
1296- c.Assert(err, IsNil)
1297- c.Assert(batches, NotNil)
1298- c.Assert(batches, HasLen, 2)
1299- c.Assert(batches[0], NotNil)
1300- c.Assert(batches[1], NotNil)
1301- c.Check(batches[0].Tag, Equals, "notification")
1302- c.Check(batches[0].Messages, HasLen, 2)
1303- c.Check(batches[1].Tag, Equals, "inbox")
1304- c.Check(batches[1].Messages, HasLen, 3)
1305-}
1306-
1307-func (s *S) TestPollSingleError(c *C) {
1308- doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) {
1309- body := inboxBody
1310- if url == "me/notifications" {
1311- body = "hola" + notificationsBody
1312- }
1313- return &http.Response{
1314- StatusCode: http.StatusOK,
1315- Body: closeWrapper{bytes.NewReader([]byte(body))},
1316- }, nil
1317- }
1318- var state fbState
1319- authData := accounts.AuthData{}
1320- authData.AccessToken = "foo"
1321- p := &fbPlugin{state: state, accountId: 32}
1322- batches, err := p.Poll(&authData)
1323- c.Assert(err, IsNil)
1324- c.Assert(batches, HasLen, 1)
1325- c.Assert(batches[0], NotNil)
1326- c.Check(batches[0].Messages, HasLen, 3)
1327-}
1328-
1329-func (s *S) TestPollError(c *C) {
1330- doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) {
1331- body := "hola" + inboxBody
1332- if url == "me/notifications" {
1333- body = "hola" + notificationsBody
1334- }
1335- return &http.Response{
1336- StatusCode: http.StatusOK,
1337- Body: closeWrapper{bytes.NewReader([]byte(body))},
1338- }, nil
1339- }
1340- var state fbState
1341- authData := accounts.AuthData{}
1342- authData.AccessToken = "foo"
1343- p := &fbPlugin{state: state, accountId: 32}
1344- _, err := p.Poll(&authData)
1345- c.Check(err, NotNil)
1346-}
1347-
1348-func (s *S) TestPollUseEnvToken(c *C) {
1349- doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) {
1350- c.Check(a.AccessToken, Equals, "bar")
1351- return nil, fmt.Errorf("please, fail")
1352- }
1353- var state fbState
1354- authData := accounts.AuthData{}
1355- authData.AccessToken = "foo"
1356- os.Setenv("ACCOUNT_POLLD_TOKEN_FACEBOOK", "bar")
1357- // defer the unset
1358- defer os.Setenv("ACCOUNT_POLLD_TOKEN_FACEBOOK", "")
1359- p := &fbPlugin{state: state, accountId: 32}
1360- _, err := p.Poll(&authData)
1361- c.Check(err, NotNil)
1362-}
1363-
1364-func (s *S) TestRequest(c *C) {
1365- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1366- fmt.Fprintln(w, "Hello, client")
1367- }))
1368- defer ts.Close()
1369- baseUrl, _ = url.Parse(ts.URL)
1370- authData := accounts.AuthData{}
1371- authData.AccessToken = "foo"
1372- _, err := request(&authData, "me/notifications")
1373- c.Check(err, IsNil)
1374-}

Subscribers

People subscribed via source and target branches