File: test_gcalcli.py

package info (click to toggle)
gcalcli 4.5.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,376 kB
  • sloc: python: 4,135; makefile: 10; sh: 7
file content (366 lines) | stat: -rw-r--r-- 11,366 bytes parent folder | download
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
from __future__ import absolute_import

import io
import os
import re
from datetime import datetime
from json import load

from gcalcli.argparsers import (
    get_cal_query_parser,
    get_color_parser,
    get_conflicts_parser,
    get_output_parser,
    get_search_parser,
    get_start_end_parser,
    get_updates_parser,
)
from gcalcli.cli import parse_cal_names
from gcalcli.utils import parse_reminder

TEST_DATA_DIR = os.path.dirname(os.path.abspath(__file__)) + '/data'


# TODO: These are more like placeholders for proper unit tests
#       We just try the commands and make sure no errors occur.
def test_list(capsys, PatchedGCalI):
    gcal = PatchedGCalI(**vars(get_color_parser().parse_args([])))
    with open(TEST_DATA_DIR + '/cal_list.json') as cl:
        cal_count = len(load(cl)['items'])

    # test data has 6 cals
    assert cal_count == len(gcal.all_cals)
    expected_header = gcal.printer.get_colorcode(
            gcal.options['color_title']) + ' Access  Title\n'

    gcal.ListAllCalendars()
    captured = capsys.readouterr()
    assert captured.out.startswith(expected_header)

    # +3 cos one for the header, one for the '----' decorations,
    # and one for the eom
    assert len(captured.out.split('\n')) == cal_count + 3


def test_agenda(PatchedGCalI):
    assert PatchedGCalI().AgendaQuery() == 0

    opts = get_start_end_parser().parse_args(['tomorrow'])
    assert PatchedGCalI().AgendaQuery(start=opts.start, end=opts.end) == 0

    opts = get_start_end_parser().parse_args(['today', 'tomorrow'])
    assert PatchedGCalI().AgendaQuery(start=opts.start, end=opts.end) == 0


def test_updates(PatchedGCalI):
    since = datetime(2019, 7, 10)
    assert PatchedGCalI().UpdatesQuery(since) == 0

    opts = get_updates_parser().parse_args(
            ['2019-07-10', '2019-07-19', '2019-08-01'])
    assert PatchedGCalI().UpdatesQuery(
            last_updated_datetime=opts.since,
            start=opts.start,
            end=opts.end) == 0


def test_conflicts(PatchedGCalI):
    assert PatchedGCalI().ConflictsQuery() == 0

    opts = get_conflicts_parser().parse_args(
            ['search text', '2019-07-19', '2019-08-01'])
    assert PatchedGCalI().ConflictsQuery(
            'search text',
            start=opts.start,
            end=opts.end) == 0


def test_cal_query(capsys, PatchedGCalI):
    opts = vars(get_cal_query_parser().parse_args([]))
    opts.update(vars(get_output_parser().parse_args([])))
    opts.update(vars(get_color_parser().parse_args([])))
    gcal = PatchedGCalI(**opts)

    gcal.CalQuery('calw')
    captured = capsys.readouterr()
    art = gcal.printer.art
    expect_top = (
            gcal.printer.colors[gcal.options['color_border']] + art['ulc'] +
            art['hrz'] * gcal.options['cal_width'])
    assert captured.out.startswith(expect_top)

    gcal.CalQuery('calm')
    captured = capsys.readouterr()
    assert captured.out.startswith(expect_top)


def test_add_event(PatchedGCalI):
    cal_names = parse_cal_names(['jcrowgey@uw.edu'], printer=None)
    gcal = PatchedGCalI(
            cal_names=cal_names, allday=False, default_reminders=True)
    assert gcal.AddEvent(title='test event',
                         where='anywhere',
                         start='now',
                         end='tomorrow',
                         descr='testing',
                         who='anyone',
                         reminders=None,
                         color='banana')


def test_add_event_with_cal_prompt(PatchedGCalI, capsys, monkeypatch):
    cal_names = parse_cal_names(
        ['jcrowgey@uw.edu', 'joshuacrowgey@gmail.com'], None)
    gcal = PatchedGCalI(
            cal_names=cal_names, allday=False, default_reminders=True)
    # Fake selecting calendar 0 at the prompt
    monkeypatch.setattr('sys.stdin', io.StringIO('0\n'))
    assert gcal.AddEvent(title='test event',
                         where='',
                         start='now',
                         end='tomorrow',
                         descr='',
                         who='',
                         reminders=None,
                         color='')
    captured = capsys.readouterr()
    assert re.match(
        r'(?sm)^0 .*\n1 .*\n.*Specify calendar.*$', captured.out), \
        f'Unexpected stderr: {captured.out}'


def test_add_event_override_color(capsys, default_options,
                                  PatchedGCalIForEvents):
    default_options.update({'override_color': True})
    cal_names = parse_cal_names(['jcrowgey@uw.edu'], None)
    gcal = PatchedGCalIForEvents(cal_names=cal_names, **default_options)
    gcal.AgendaQuery()
    captured = capsys.readouterr()
    # this could be parameterized with pytest eventually
    # assert colorId 10: green
    assert '\033[0;32m' in captured.out


def test_quick_add(PatchedGCalI):
    cal_names = parse_cal_names(['jcrowgey@uw.edu'], None)
    gcal = PatchedGCalI(cal_names=cal_names)
    assert gcal.QuickAddEvent(
        event_text='quick test event',
        reminders=['5m sms'])


def test_quick_add_with_cal_prompt(PatchedGCalI, capsys, monkeypatch):
    cal_names = parse_cal_names(
        ['jcrowgey@uw.edu', 'joshuacrowgey@gmail.com'], None)
    gcal = PatchedGCalI(cal_names=cal_names)
    # Fake selecting calendar 0 at the prompt
    monkeypatch.setattr('sys.stdin', io.StringIO('0\n'))
    assert gcal.QuickAddEvent(
        event_text='quick test event',
        reminders=['5m sms'])
    captured = capsys.readouterr()
    assert re.match(
        r'(?sm)^0 .*\n1 .*\n.*Specify calendar.*$', captured.out), \
        f'Unexpected stderr: {captured.out}'


def test_text_query(PatchedGCalI):
    search_parser = get_search_parser()
    gcal = PatchedGCalI()

    # TODO: mock the api reply for the search
    # and then assert something greater than zero

    opts = search_parser.parse_args(['test', '1970-01-01', '2038-01-18'])
    assert gcal.TextQuery(opts.text, opts.start, opts.end) == 0

    opts = search_parser.parse_args(['test', '1970-01-01'])
    assert gcal.TextQuery(opts.text, opts.start, opts.end) == 0

    opts = search_parser.parse_args(['test'])
    assert gcal.TextQuery(opts.text, opts.start, opts.end) == 0


def test_declined_event_no_attendees(PatchedGCalI):
    gcal = PatchedGCalI()
    event = {
        'gcalcli_cal': {
            'id': 'user@email.com',
        },
        'attendees': []
    }
    assert not gcal._DeclinedEvent(event)


def test_declined_event_non_matching_attendees(PatchedGCalI):
    gcal = PatchedGCalI()
    event = {
        'gcalcli_cal': {
            'id': 'user@email.com',
        },
        'attendees': [{
            'email': 'user2@otheremail.com',
            'responseStatus': 'declined',
        }]
    }
    assert not gcal._DeclinedEvent(event)


def test_declined_event_matching_attendee_declined(PatchedGCalI):
    gcal = PatchedGCalI()
    event = {
        'gcalcli_cal': {
            'id': 'user@email.com',
        },
        'attendees': [
            {
                'email': 'user@email.com',
                'responseStatus': 'declined',
            },
            {
                'email': 'user2@otheremail.com',
                'responseStatus': 'accepted',
            },
        ]
    }
    assert gcal._DeclinedEvent(event)


def test_declined_event_matching_attendee_accepted(PatchedGCalI):
    gcal = PatchedGCalI()
    event = {
        'gcalcli_cal': {
            'id': 'user@email.com',
        },
        'attendees': [
            {
                'email': 'user@email.com',
                'responseStatus': 'accepted',
            },
            {
                'email': 'user2@otheremail.com',
                'responseStatus': 'declined',
            },
        ]
    }
    assert not gcal._DeclinedEvent(event)


def test_declined_event_aliased_attendee(PatchedGCalI):
    """Should detect declined events if attendee has self=True (#620)."""
    gcal = PatchedGCalI()
    event = {
        'gcalcli_cal': {
            'id': 'user@email.com',
        },
        'attendees': [
            {
                'email': 'user@otherdomain.com',
                'self': True,
                'responseStatus': 'declined',
            },
        ]
    }
    assert gcal._DeclinedEvent(event), \
        "Must detect declined 'self' events regardless of email"


def test_modify_event(PatchedGCalI):
    opts = get_search_parser().parse_args(['test'])
    gcal = PatchedGCalI(**vars(opts))
    assert gcal.ModifyEvents(
            gcal._edit_event, opts.text, opts.start, opts.end) == 0


def test_import(PatchedGCalI):
    cal_names = parse_cal_names(['jcrowgey@uw.edu'], None)
    gcal = PatchedGCalI(cal_names=cal_names, default_reminders=True)
    vcal_path = TEST_DATA_DIR + '/vv.txt'
    assert gcal.ImportICS(icsFile=open(vcal_path, errors='replace'))


def test_legacy_import(PatchedGCalI):
    cal_names = parse_cal_names(['jcrowgey@uw.edu'], None)
    gcal = PatchedGCalI(
        cal_names=cal_names, default_reminders=True, use_legacy_import=True)
    vcal_path = TEST_DATA_DIR + '/vv.txt'
    assert gcal.ImportICS(icsFile=open(vcal_path, errors='replace'))


def test_parse_reminder():
    MINS_PER_DAY = 60 * 24
    MINS_PER_WEEK = MINS_PER_DAY * 7

    rem = '5m email'
    tim, method = parse_reminder(rem)
    assert method == 'email'
    assert tim == 5

    rem = '2h sms'
    tim, method = parse_reminder(rem)
    assert method == 'sms'
    assert tim == 120

    rem = '1d popup'
    tim, method = parse_reminder(rem)
    assert method == 'popup'
    assert tim == MINS_PER_DAY

    rem = '1w'
    tim, method = parse_reminder(rem)
    assert method == 'popup'
    assert tim == MINS_PER_WEEK

    rem = '10w'
    tim, method = parse_reminder(rem)
    assert method == 'popup'
    assert tim == MINS_PER_WEEK * 10

    rem = 'invalid reminder'
    assert parse_reminder(rem) is None


def test_parse_cal_names(PatchedGCalI):
    # TODO we need to mock the event list returned by the search
    # and then assert the right number of events
    # for the moment, we assert 0 (which indicates successful completion of
    # the code path, but no events printed)
    cal_names = parse_cal_names(['j*#green'], None)
    gcal = PatchedGCalI(cal_names=cal_names)
    assert gcal.AgendaQuery() == 0

    cal_names = parse_cal_names(['j*'], None)
    gcal = PatchedGCalI(cal_names=cal_names)
    assert gcal.AgendaQuery() == 0

    cal_names = parse_cal_names(['jcrowgey@uw.edu'], None)
    gcal = PatchedGCalI(cal_names=cal_names)
    assert gcal.AgendaQuery() == 0


def test_iterate_events(capsys, PatchedGCalI):
    gcal = PatchedGCalI()
    assert gcal._iterate_events(gcal.now, []) == 0

    # TODO: add some events to a list and assert their selection


def test_next_cut(PatchedGCalI):
    gcal = PatchedGCalI()
    # default width is 10
    test_cal_width = 10
    gcal.options['cal_width'] = test_cal_width
    event_title = "first looooong"
    assert gcal._next_cut(event_title) == (5, 5)

    event_title = "tooooooloooong"
    assert gcal._next_cut(event_title) == (test_cal_width, test_cal_width)

    event_title = "one two three four"
    assert gcal._next_cut(event_title) == (7, 7)

    # event_title = "& G NSW VIM Project"
    # assert gcal._next_cut(event_title) == (7, 7)

    event_title = "樹貞 fun fun fun"
    assert gcal._next_cut(event_title) == (8, 6)