togglv9 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,456 @@
1
+ require 'date'
2
+
3
+ describe 'Time Entries' do
4
+ before :all do
5
+ @toggl = TogglV9::API.new(Testing::API_TOKEN)
6
+ @workspaces = @toggl.workspaces
7
+ @workspace_id = @workspaces.first['id']
8
+ end
9
+
10
+ context 'CRUD time entry' do
11
+ before :each do
12
+ time_entry_info = {
13
+ 'wid' => @workspace_id,
14
+ 'start' => @toggl.iso8601(DateTime.now),
15
+ 'duration' => 77
16
+ }
17
+
18
+ @expected = time_entry_info.clone
19
+
20
+ @time_entry = @toggl.create_time_entry(@workspace_id, time_entry_info)
21
+ end
22
+
23
+ after :each do
24
+ @toggl.delete_time_entry(@workspace_id, @time_entry['id'])
25
+ rescue RuntimeError => e
26
+ if e.message != "HTTP Status: 404"
27
+ raise e
28
+ end
29
+ end
30
+
31
+ it 'creates a time entry' do
32
+ expect(@time_entry['wid']).to eq @expected['wid']
33
+ expect(@time_entry['duration']).to eq @expected['duration']
34
+ expect(Time.parse(@time_entry['start'])).to eq Time.parse(@expected['start'])
35
+ end
36
+
37
+ it 'requires a workspace, project, or task to create' do
38
+ time_entry_info = {
39
+ 'start' => @toggl.iso8601(DateTime.now),
40
+ 'duration' => 77
41
+ }
42
+
43
+ expect {
44
+ @toggl.create_time_entry(@workspace_id, time_entry_info)
45
+ }.to raise_error(ArgumentError)
46
+ end
47
+
48
+ it 'gets a time entry' do
49
+ retrieved_time_entry = @toggl.get_time_entry(@time_entry['id'])
50
+ retrieved_time_entry.delete('guid')
51
+
52
+ ['start', 'stop'].each do |key|
53
+ expect(retrieved_time_entry[key]).to eq_ts @time_entry[key]
54
+ retrieved_time_entry.delete(key)
55
+ @time_entry.delete(key)
56
+ end
57
+ expected = @time_entry.clone
58
+ expected['tags'] ||= []
59
+ expected['tag_ids'] ||= []
60
+ expected.delete('uid')
61
+ expected.delete('wid')
62
+
63
+ expect(retrieved_time_entry).to eq expected
64
+ end
65
+
66
+ it 'updates a time entry' do
67
+ time_entry_info = {
68
+ 'start' => '2010-02-13T23:31:30+00:00',
69
+ 'duration' => 42
70
+ }
71
+
72
+ expected = time_entry_info.clone
73
+
74
+ time_entry_updated = @toggl.update_time_entry(@workspace_id, @time_entry['id'], time_entry_info)
75
+ expect(time_entry_updated['duration']).to eq expected['duration']
76
+ expect(Time.parse(time_entry_updated['start'])).to eq Time.parse(expected['start'])
77
+ end
78
+
79
+ it 'deletes a time entry' do
80
+ existing_time_entry = @toggl.get_time_entry(@time_entry['id'])
81
+ expect(existing_time_entry.has_key?('server_deleted_at')).to eq true
82
+ expect(existing_time_entry['server_deleted_at']).to eq nil
83
+
84
+ deleted_time_entry = @toggl.delete_time_entry(@workspace_id, @time_entry['id'])
85
+
86
+ expect { @toggl.get_time_entry(@time_entry['id']) }.to raise_error(RuntimeError, 'HTTP Status: 404')
87
+ end
88
+ end
89
+
90
+ context '+ UTC offset' do
91
+ # ISO8601 times with positive '+' UTC offsets must be properly encoded
92
+
93
+ before :each do
94
+ time_entry_info = {
95
+ 'wid' => @workspace_id,
96
+ 'start' => '2016-01-22T12:08:14+02:00',
97
+ 'duration' => 77
98
+ }
99
+
100
+ @expected = time_entry_info.clone
101
+
102
+ @time_entry = @toggl.create_time_entry(@workspace_id, time_entry_info)
103
+ end
104
+
105
+ after :each do
106
+ @toggl.delete_time_entry(@workspace_id, @time_entry['id'])
107
+ rescue RuntimeError => e
108
+ if e.message != "HTTP Status: 404"
109
+ raise e
110
+ end
111
+ end
112
+
113
+ it 'creates a time entry' do
114
+ expect(@time_entry['wid']).to eq @expected['wid']
115
+ expect(@time_entry['duration']).to eq @expected['duration']
116
+ expect(Time.parse(@time_entry['start'])).to eq Time.parse(@expected['start'])
117
+ end
118
+
119
+ it 'requires a workspace, project, or task to create' do
120
+ time_entry_info = {
121
+ 'start' => '2016-01-22T12:08:14+02:00',
122
+ 'duration' => 77
123
+ }
124
+
125
+ expect {
126
+ @toggl.create_time_entry(@workspace_id, time_entry_info)
127
+ }.to raise_error(ArgumentError)
128
+ end
129
+
130
+ it 'gets a time entry' do
131
+ retrieved_time_entry = @toggl.get_time_entry(@time_entry['id'])
132
+ retrieved_time_entry.delete('guid')
133
+
134
+ ['start', 'stop'].each do |key|
135
+ expect(retrieved_time_entry[key]).to eq_ts @time_entry[key]
136
+ retrieved_time_entry.delete(key)
137
+ @time_entry.delete(key)
138
+ end
139
+ expected = @time_entry.clone
140
+ expected['tags'] ||= []
141
+ expected['tag_ids'] ||= []
142
+ expected.delete('uid')
143
+ expected.delete('wid')
144
+
145
+ expect(retrieved_time_entry).to eq expected
146
+ end
147
+
148
+ it 'updates a time entry' do
149
+ time_entry_info = {
150
+ 'start' => '2010-02-13T23:31:30+07:00',
151
+ 'duration' => 42
152
+ }
153
+
154
+ expected = time_entry_info.clone
155
+
156
+ time_entry_updated = @toggl.update_time_entry(@workspace_id, @time_entry['id'], time_entry_info)
157
+ expect(time_entry_updated['duration']).to eq expected['duration']
158
+ expect(Time.parse(time_entry_updated['start'])).to eq Time.parse(expected['start'])
159
+ end
160
+
161
+ it 'deletes a time entry' do
162
+ existing_time_entry = @toggl.get_time_entry(@time_entry['id'])
163
+ expect(existing_time_entry.has_key?('server_deleted_at')).to eq true
164
+
165
+ deleted_time_entry = @toggl.delete_time_entry(@workspace_id, @time_entry['id'])
166
+
167
+ expect { @toggl.get_time_entry(@time_entry['id']) }.to raise_error(RuntimeError, 'HTTP Status: 404')
168
+ end
169
+ end
170
+
171
+ context 'multiple time entries' do
172
+ before :all do
173
+ time_entry_info = {
174
+ 'wid' => @workspace_id,
175
+ 'duration' => 77
176
+ }
177
+ @now = DateTime.now
178
+
179
+ start = { 'start' => @toggl.iso8601(@now - 9) }
180
+ @time_entry_nine_days_ago = @toggl.create_time_entry(@workspace_id, time_entry_info.merge(start))
181
+ @nine_days_ago_id = @time_entry_nine_days_ago['id']
182
+
183
+ start = { 'start' => @toggl.iso8601(@now - 7) }
184
+ @time_entry_last_week = @toggl.create_time_entry(@workspace_id, time_entry_info.merge(start))
185
+ @last_week_id = @time_entry_last_week['id']
186
+
187
+ start = { 'start' => @toggl.iso8601(@now) }
188
+ @time_entry_now = @toggl.create_time_entry(@workspace_id, time_entry_info.merge(start))
189
+ @now_id = @time_entry_now['id']
190
+
191
+ start = { 'start' => @toggl.iso8601(@now + 7) }
192
+ @time_entry_next_week = @toggl.create_time_entry(@workspace_id, time_entry_info.merge(start))
193
+ @next_week_id = @time_entry_next_week['id']
194
+ end
195
+
196
+ after :all do
197
+ TogglV9SpecHelper.delete_all_time_entries(@toggl)
198
+ end
199
+
200
+ it 'gets time entries (reaching back 9 days up till now)' do
201
+ ids = @toggl.get_time_entries.map { |t| t['id']}
202
+ expect(ids.sort).to eq [ @last_week_id, @now_id ]
203
+ end
204
+
205
+ it 'gets time entries after start_date (up till now)' do
206
+ ids = @toggl.get_time_entries({:start_date => @now - 1}).map { |t| t['id']}
207
+ expect(ids.sort).to eq [ @now_id ]
208
+ end
209
+
210
+ it 'gets time entries between start_date and end_date' do
211
+ ids = @toggl.get_time_entries({:start_date => @now - 1, :end_date => @now + 1}).map { |t| t['id']}
212
+ expect(ids.sort).to eq [ @now_id ]
213
+ end
214
+
215
+ it 'gets time entries in the future' do
216
+ ids = @toggl.get_time_entries({:start_date => @now - 1, :end_date => @now + 8}).map { |t| t['id']}
217
+ expect(ids.sort).to eq [ @now_id, @next_week_id ]
218
+ end
219
+ end
220
+
221
+ context 'start and stop time entry' do
222
+ it 'starts and stops a time entry' do
223
+ time_entry_info = {
224
+ 'workspace_id' => @workspace_id,
225
+ 'description' => 'time entry description'
226
+ }
227
+
228
+ # start time entry
229
+ running_time_entry = @toggl.start_time_entry(@workspace_id, time_entry_info)
230
+
231
+ # get current time entry by '/current'
232
+ time_entry_current = @toggl.get_current_time_entry
233
+ # get current time entry by id
234
+ time_entry_by_id = @toggl.get_time_entry(running_time_entry['id'])
235
+ time_entry_by_id.delete('guid')
236
+
237
+ # compare two methods of getting current time entry
238
+ time_entry_current.delete('uid')
239
+ time_entry_current.delete('wid')
240
+ expect(time_entry_current).to eq time_entry_by_id
241
+
242
+ # compare current time entry with running time entry
243
+ expect(time_entry_by_id['start']).to eq_ts running_time_entry['start']
244
+ time_entry_by_id.delete('start')
245
+ running_time_entry.delete('start')
246
+
247
+ running_time_entry['tags'] = [] if running_time_entry['tags'].nil?
248
+ running_time_entry['tag_ids'] = [] if running_time_entry['tag_ids'].nil?
249
+ running_time_entry.delete('uid')
250
+ running_time_entry.delete('wid')
251
+ expect(time_entry_by_id).to eq running_time_entry
252
+ expect(time_entry_by_id.has_key?('stop')).to eq true
253
+ expect(time_entry_by_id['stop']).to eq nil
254
+
255
+ # stop time entry
256
+ stopped_time_entry = @toggl.stop_time_entry(@workspace_id, running_time_entry['id'])
257
+ expect(stopped_time_entry.has_key?('stop')).to eq true
258
+
259
+ @toggl.delete_time_entry(@workspace_id, stopped_time_entry['id'])
260
+ end
261
+
262
+ it 'returns nil if there is no current time entry' do
263
+ time_entry = @toggl.get_current_time_entry
264
+ expect(time_entry).to be {}
265
+ end
266
+
267
+ it 'requires a workspace, project, or task to start' do
268
+ time_entry_info = {
269
+ 'description' => 'time entry description'
270
+ }
271
+
272
+ expect {
273
+ @toggl.start_time_entry(@workspace_id, time_entry_info)
274
+ }.to raise_error(ArgumentError)
275
+ end
276
+ end
277
+
278
+ context 'time entry tags' do
279
+ before :each do
280
+ time_entry_info = {
281
+ 'wid' => @workspace_id,
282
+ 'duration' => 7777
283
+ }
284
+ @now = DateTime.now
285
+
286
+ start = { 'start' => @toggl.iso8601(@now - 7) }
287
+ @time7 = @toggl.create_time_entry(@workspace_id, time_entry_info.merge(start))
288
+
289
+ start = { 'start' => @toggl.iso8601(@now - 6) }
290
+ @time6 = @toggl.create_time_entry(@workspace_id, time_entry_info.merge(start))
291
+
292
+ start = { 'start' => @toggl.iso8601(@now - 5) }
293
+ @time5 = @toggl.create_time_entry(@workspace_id, time_entry_info.merge(start))
294
+
295
+ start = { 'start' => @toggl.iso8601(@now - 4) }
296
+ @time4 = @toggl.create_time_entry(@workspace_id, time_entry_info.merge(start))
297
+
298
+ @time_entry_ids = [ @time7['id'], @time6['id'], @time5['id'], @time4['id']]
299
+ end
300
+
301
+ after :each do
302
+ TogglV9SpecHelper.delete_all_time_entries(@toggl)
303
+ TogglV9SpecHelper.delete_all_tags(@toggl)
304
+ end
305
+
306
+ xit 'adds and removes one tag' do
307
+ # Add one tag
308
+ @toggl.update_time_entries_tags_fixed(@workspace_id, @time_entry_ids,
309
+ {'tags' =>['money'], 'tag_action' => 'add'})
310
+
311
+ time_entries = @toggl.get_time_entries
312
+ tags = time_entries.map { |t| t['tags'] }
313
+ expect(tags).to eq [
314
+ ['money'],
315
+ ['money'],
316
+ ['money'],
317
+ ['money']
318
+ ]
319
+
320
+ # Remove one tag
321
+ @toggl.update_time_entries_tags_fixed(@workspace_id, @time_entry_ids,
322
+ {'tags' =>['money'], 'tag_action' => 'remove'})
323
+
324
+ time_entries = @toggl.get_time_entries
325
+ tags = time_entries.map { |t| t['tags'] }.compact
326
+ expect(tags).to eq []
327
+ end
328
+
329
+ xit '"removes" a non-existent tag' do
330
+ # Not tags to start
331
+ time_entries = @toggl.get_time_entries
332
+ tags = time_entries.map { |t| t['tags'] }.compact
333
+ expect(tags).to eq []
334
+
335
+ # "Remove" a tag
336
+ @toggl.update_time_entries_tags_fixed(@workspace_id, @time_entry_ids,
337
+ {'tags' =>['void'], 'tag_action' => 'remove'})
338
+
339
+ # No tags to finish
340
+ time_entries = @toggl.get_time_entries
341
+ tags = time_entries.map { |t| t['tags'] }.compact
342
+ expect(tags).to eq []
343
+ end
344
+
345
+ xit 'adds and removes multiple tags' do
346
+ # Add multiple tags
347
+ @toggl.update_time_entries_tags_fixed(@workspace_id, @time_entry_ids,
348
+ {'tags' =>['billed', 'productive'], 'tag_action' => 'add'})
349
+
350
+ time_entries = @toggl.get_time_entries
351
+ tags = time_entries.map { |t| t['tags'] }
352
+ expect(tags).to eq [
353
+ ['billed', 'productive'],
354
+ ['billed', 'productive'],
355
+ ['billed', 'productive'],
356
+ ['billed', 'productive']
357
+ ]
358
+
359
+ # Remove multiple tags
360
+ @toggl.update_time_entries_tags_fixed(@workspace_id, @time_entry_ids,
361
+ {'tags' =>['billed','productive'], 'tag_action' => 'remove'})
362
+
363
+ time_entries = @toggl.get_time_entries
364
+ tags = time_entries.map { |t| t['tags'] }.compact
365
+ expect(tags).to eq []
366
+ end
367
+
368
+ xit 'manages multiple tags' do
369
+ # Add some tags
370
+ @toggl.update_time_entries_tags_fixed(@workspace_id, @time_entry_ids,
371
+ {'tags' =>['billed', 'productive'], 'tag_action' => 'add'})
372
+
373
+ # Remove some tags
374
+ @toggl.update_time_entries_tags_fixed(@workspace_id, [ @time6['id'], @time4['id'] ],
375
+ {'tags' =>['billed'], 'tag_action' => 'remove'})
376
+
377
+ # Add some tags
378
+ @toggl.update_time_entries_tags_fixed(@workspace_ids, [ @time7['id'] ],
379
+ {'tags' =>['best'], 'tag_action' => 'add'})
380
+
381
+ time7 = @toggl.get_time_entry(@time7['id'])
382
+ time6 = @toggl.get_time_entry(@time6['id'])
383
+ time5 = @toggl.get_time_entry(@time5['id'])
384
+ time4 = @toggl.get_time_entry(@time4['id'])
385
+
386
+ tags = [ time7['tags'], time6['tags'], time5['tags'], time4['tags'] ]
387
+ expect(tags).to eq [
388
+ ['best', 'billed', 'productive'],
389
+ [ 'productive'],
390
+ [ 'billed', 'productive'],
391
+ [ 'productive']
392
+ ]
393
+ end
394
+ end
395
+
396
+ context 'iso8601' do
397
+ before :all do
398
+ @ts = DateTime.new(2008,6,21, 13,30,2, "+09:00")
399
+ @expected = '2008-06-21T13:30:02+09:00'
400
+ end
401
+
402
+ it 'formats a DateTime' do
403
+ expect(@toggl.iso8601(@ts)).to eq @expected
404
+ end
405
+
406
+ it 'formats a Date' do
407
+ ts = @ts.to_date
408
+ expect(@toggl.iso8601(@ts)).to eq @expected
409
+ end
410
+
411
+ it 'formats a Time' do
412
+ ts = @ts.to_time
413
+ expect(@toggl.iso8601(@ts)).to eq @expected
414
+ end
415
+
416
+ it 'cannot format a FixNum' do
417
+ expect{ @toggl.iso8601(1234567890) }.to raise_error(ArgumentError)
418
+ end
419
+
420
+ it 'cannot format a malformed timestamp' do
421
+ expect{ @toggl.iso8601('X') }.to raise_error(ArgumentError)
422
+ end
423
+
424
+ context 'String' do
425
+ it 'converts +00:00 to Zulu' do
426
+ ts = '2015-08-21T09:21:02+00:00'
427
+ expected = '2015-08-21T09:21:02Z'
428
+
429
+ expect(@toggl.iso8601(ts)).to eq expected
430
+ end
431
+
432
+ it 'converts -00:00 to Z' do
433
+ ts = '2015-08-21T09:21:02-00:00'
434
+ expected = '2015-08-21T09:21:02Z'
435
+
436
+ expect(@toggl.iso8601(ts)).to eq expected
437
+ end
438
+
439
+ it 'maintains an offset' do
440
+ expect(@toggl.iso8601('2015-08-21T04:21:02-05:00')).to eq '2015-08-21T04:21:02-05:00'
441
+ end
442
+ end
443
+ end
444
+
445
+ RSpec::Matchers.define :eq_ts do |expected|
446
+ # Matching actual time is necessary due to differing formats.
447
+ # Example:
448
+ # 1) POST time_entries/start returns 2015-08-21T07:28:20Z
449
+ # when GET time_entries/{time_entry_id} returns 2015-08-21T07:28:20+00:00
450
+ # 2) 2015-08-21T03:20:30-05:00 and 2015-08-21T08:20:30+00:00 refer to
451
+ # the same moment in time, but one is in local time and the other in UTC
452
+ match do |actual|
453
+ DateTime.parse(actual) == DateTime.parse(expected)
454
+ end
455
+ end
456
+ end
@@ -0,0 +1,87 @@
1
+ describe 'Users' do
2
+ before :all do
3
+ @toggl = TogglV9::API.new(Testing::API_TOKEN)
4
+ @user = @toggl.me(all=true)
5
+ @workspaces = @toggl.workspaces
6
+ @workspace_id = @workspaces.first['id']
7
+ end
8
+
9
+ it 'returns /me' do
10
+ expect(@user).to_not be_nil
11
+ expect(@user['id']).to eq Testing::USER_ID
12
+ expect(@user['fullname']).to eq Testing::USERNAME
13
+ # expect(@user['image_url']).to eq 'https://assets.toggl.com/avatars/a5d106126b6bed8df283e708af0828ee.png'
14
+ # expect(@user['timezone']).to eq 'Etc/UTC'
15
+ # expect(@user['workspaces'].length).to eq 1
16
+ # expect(@user['workspaces'].first['name']).to eq "togglv9's workspace"
17
+ end
18
+
19
+ it 'returns my_clients' do
20
+ my_clients = @toggl.my_clients(@user)
21
+ expect(my_clients).to be_empty
22
+ end
23
+
24
+ it 'returns my_projects' do
25
+ my_projects = @toggl.my_projects(@user)
26
+ expect(my_projects).to be_empty
27
+ end
28
+
29
+ it 'returns my_projects and my_deleted_projects' do
30
+ # Create project
31
+ project = @toggl.create_project(@workspace_id, { 'name' => 'my project' })
32
+
33
+ my_project_ids = @toggl.my_projects.map { |p| p['id'] }
34
+ my_deleted_project_ids = @toggl.my_deleted_projects.map { |p| p['id'] }
35
+
36
+ expect(my_project_ids).to eq [ project['id'] ]
37
+ expect(my_deleted_project_ids).not_to include(project['id'])
38
+
39
+ # Delete project
40
+ @toggl.delete_project(@workspace_id, project['id'])
41
+
42
+ my_project_ids = @toggl.my_projects.map { |p| p['id'] }
43
+ # FIXME: in v9 the /me response looks not to contain projects that were already deleted
44
+ # my_deleted_project_ids = @toggl.my_deleted_projects.map { |p| p['id'] }
45
+
46
+ expect(my_project_ids).to eq []
47
+ # expect(my_deleted_project_ids).to include(project['id'])
48
+ end
49
+
50
+ it 'returns my_tags' do
51
+ my_tags = @toggl.my_tags(@user)
52
+ expect(my_tags).to be_empty
53
+ end
54
+
55
+ it 'returns my_tasks' do
56
+ my_tasks = @toggl.my_tasks(@user)
57
+ expect(my_tasks).to be_empty
58
+ end
59
+
60
+ it 'returns my_time_entries' do
61
+ my_time_entries = @toggl.my_time_entries(@user)
62
+ expect(my_time_entries).to be_empty
63
+ end
64
+
65
+ it 'returns my_workspaces' do
66
+ my_workspaces = @toggl.my_workspaces(@user)
67
+ expect(my_workspaces.length).to eq 1
68
+ end
69
+
70
+ context 'new user' do
71
+ # FIXME: user creation API is deprecated https://engineering.toggl.com/changes/2023/11/02/v9-auth-endpoints-deprecation/index.html
72
+ xit 'creates a new user' do
73
+ now = Time.now.to_i
74
+ user_info = {
75
+ 'email' => "test-#{now}+1@mailinator.com",
76
+ 'timezone' => 'Etc/UTC'
77
+ }
78
+ user_additional = {
79
+ 'password' => "password-#{now}+1",
80
+ 'tos_accepted' => 'true'
81
+ }
82
+
83
+ new_user = @toggl.create_user(user_info.merge(user_additional))
84
+ expect(new_user).to include(user_info)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,46 @@
1
+ describe 'Workspaces' do
2
+ before :all do
3
+ @toggl = TogglV9::API.new(Testing::API_TOKEN)
4
+ @user = @toggl.me(all=true)
5
+ @workspaces = @toggl.my_workspaces
6
+ @workspace_id = @workspaces.first['id']
7
+ @organization_id = @user['workspaces'].find { |w| w['id'] == @workspace_id }['organization_id']
8
+ @project = @toggl.create_project(@workspace_id, { 'name' => 'project with a task' })
9
+ end
10
+
11
+ after :all do
12
+ @toggl.delete_project(@workspace_id, @project['id'])
13
+ end
14
+
15
+ it 'shows users' do
16
+ users = @toggl.users(@organization_id, @workspace_id)
17
+ expect(users.length).to eq 2
18
+
19
+ expect(users.first['user_id']).to eq Testing::USER_ID
20
+ expect(users.first['email']).to eq Testing::EMAIL
21
+ expect(users.first['name']).to eq Testing::USERNAME
22
+
23
+ expect(users.last['user_id']).to eq Testing::OTHER_USER_ID
24
+ expect(users.last['email']).to eq Testing::OTHER_EMAIL
25
+ expect(users.last['name']).to eq Testing::OTHER_USERNAME
26
+ expect(users.last['workspace_id']).to eq @workspace_id
27
+ end
28
+
29
+ context 'tasks', :pro_account do
30
+ before :each do
31
+ @task = @toggl.create_task(@workspace_id, @project['id'], 'name' => 'workspace task')
32
+ end
33
+
34
+ after :each do
35
+ @toggl.delete_task(@workspace_id, @project['id'], @task['id'])
36
+ end
37
+
38
+ it 'shows tasks' do
39
+ tasks = @toggl.tasks(@workspace_id)
40
+ expect(tasks.length).to eq 1
41
+ expect(tasks.first['name']).to eq 'workspace task'
42
+ expect(tasks.first['pid']).to eq @project['id']
43
+ expect(tasks.first['wid']).to eq @workspace_id
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,88 @@
1
+ require 'fileutils'
2
+
3
+ describe 'TogglV9' do
4
+ it 'initializes with api_token' do
5
+ toggl = TogglV9::API.new(Testing::API_TOKEN)
6
+ me = toggl.me
7
+ expect(me).to_not be nil
8
+ expect(me['api_token']).to eq Testing::API_TOKEN
9
+ expect(me['email']).to eq Testing::EMAIL
10
+ end
11
+
12
+ it 'initializes with username and password' do
13
+ toggl = TogglV9::API.new(Testing::EMAIL, Testing::PASSWORD)
14
+ me = toggl.me
15
+ expect(me).to_not be nil
16
+ expect(me['api_token']).to eq Testing::API_TOKEN
17
+ expect(me['email']).to eq Testing::EMAIL
18
+ end
19
+
20
+ it 'does not initialize with bogus api_token' do
21
+ toggl = TogglV9::API.new('4880nqor1orr9n241sn08070q33oq49s')
22
+ expect { toggl.me }.to raise_error(RuntimeError, "HTTP Status: 403")
23
+ end
24
+
25
+ context '.toggl file' do
26
+ before :each do
27
+ @tmp_home = mktemp_dir
28
+ @original_home = Dir.home
29
+ ENV['HOME'] = @tmp_home
30
+ end
31
+
32
+ after :each do
33
+ FileUtils.rm_rf(@tmp_home)
34
+ ENV['HOME'] = @original_home
35
+ end
36
+
37
+ it 'initializes with .toggl file' do
38
+ toggl_file = File.join(@tmp_home, '.toggl')
39
+ File.open(toggl_file, 'w') { |file| file.write(Testing::API_TOKEN) }
40
+
41
+ toggl = TogglV9::API.new
42
+ me = toggl.me
43
+ expect(me).to_not be nil
44
+ expect(me['api_token']).to eq Testing::API_TOKEN
45
+ expect(me['email']).to eq Testing::EMAIL
46
+ end
47
+
48
+ it 'initializes with .toggl file ending with a newline' do
49
+ toggl_file = File.join(@tmp_home, '.toggl')
50
+ File.open(toggl_file, 'w') { |file| file.write(Testing::API_TOKEN + "\n") }
51
+
52
+ toggl = TogglV9::API.new
53
+ me = toggl.me
54
+ expect(me).to_not be nil
55
+ expect(me['api_token']).to eq Testing::API_TOKEN
56
+ expect(me['email']).to eq Testing::EMAIL
57
+ end
58
+
59
+ it 'raises error if .toggl file is missing' do
60
+ expect{ toggl = TogglV9::API.new }.to raise_error(RuntimeError)
61
+ end
62
+ end
63
+
64
+ context 'handles errors' do
65
+ before :all do
66
+ @toggl = TogglV9::API.new(Testing::API_TOKEN)
67
+ end
68
+
69
+ it 'surfaces an HTTP Status Code in case of error' do
70
+ expect(@toggl.conn).to receive(:get).once.and_return(
71
+ MockResponse.new(400, {}, 'body'))
72
+ expect { @toggl.me }.to raise_error(RuntimeError, "HTTP Status: 400")
73
+ end
74
+
75
+ it 'retries a request up to 3 times if a 429 is received' do
76
+ expect(@toggl.conn).to receive(:get).exactly(3).times.and_return(
77
+ MockResponse.new(429, {}, 'body'))
78
+ expect { @toggl.me }.to raise_error(RuntimeError, "HTTP Status: 429")
79
+ end
80
+
81
+ it 'retries a request after 429' do
82
+ expect(@toggl.conn).to receive(:get).twice.and_return(
83
+ MockResponse.new(429, {}, 'body'),
84
+ MockResponse.new(200, {}, nil))
85
+ expect(@toggl.me).to eq({}) # response is {} in this case because body is nil
86
+ end
87
+ end
88
+ end