client.getTestDayEntries   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 27
rs 9.55
c 0
b 0
f 0
eloc 17
nop 0
1
package client
2
3
import (
4
	"bytes"
5
	"encoding/xml"
6
	"github.com/popstas/go-toggl"
7
	"github.com/popstas/planfix-go/planfix"
8
	"github.com/stretchr/testify/assert"
9
	"github.com/stretchr/testify/mock"
10
	"github.com/viasite/planfix-toggl-server/app/config"
11
	"io/ioutil"
12
	"log"
13
	"net/http"
14
	"net/http/httptest"
15
	"testing"
16
	"time"
17
)
18
19
var output bytes.Buffer
20
21
type planfixRequestStruct struct {
22
	XMLName xml.Name `xml:"request"`
23
	Method  string   `xml:"method,attr"`
24
	Account string   `xml:"account"`
25
	Sid     string   `xml:"sid"`
26
}
27
28
func fixtureFromFile(fixtureName string) string {
29
	buf, _ := ioutil.ReadFile("../../tests/fixtures/" + fixtureName)
30
	return string(buf)
31
}
32
33
type MockedServer struct {
34
	*httptest.Server
35
	Requests  [][]byte
36
	Responses []string // fifo queue of answers
37
}
38
39
func NewMockedServer(responses []string) *MockedServer {
40
	s := &MockedServer{
41
		Requests:  [][]byte{},
42
		Responses: responses,
43
	}
44
45
	s.Server = httptest.NewServer(s)
46
	return s
47
}
48
49
func (s *MockedServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
50
	lastRequest, err := ioutil.ReadAll(req.Body)
51
	if err != nil {
52
		panic(err)
53
	}
54
55
	rs := planfixRequestStruct{}
56
	err = xml.Unmarshal(lastRequest, &rs)
57
	if err != nil {
58
		panic(err)
59
	}
60
	s.Requests = append(s.Requests, lastRequest)
61
	answer := s.Responses[0]
62
63
	s.Responses = s.Responses[1:]
64
	resp.Write([]byte(answer))
65
}
66
67
type MockedTogglSession struct {
68
	mock.Mock
69
	TogglSession
70
}
71
72
func (s *MockedTogglSession) GetAccount() (toggl.Account, error) {
73
	args := s.Called()
74
	return args.Get(0).(toggl.Account), args.Error(1)
75
}
76
77
func (s *MockedTogglSession) GetDetailedReport(workspace int, since, until string, page int) (toggl.DetailedReport, error) {
78
	args := s.Called(workspace, since, until, page)
79
	return args.Get(0).(toggl.DetailedReport), args.Error(1)
80
}
81
82
func (s *MockedTogglSession) AddRemoveTag(entryID int, tag string, add bool) (toggl.TimeEntry, error) {
83
	args := s.Called(entryID, tag, add)
84
	return args.Get(0).(toggl.TimeEntry), args.Error(1)
85
}
86
87
func newClient() TogglClient {
88
	cfg := config.Config{
89
		TogglSentTag: "sent",
90
		Debug: true,
91
	}
92
	api := planfix.New("", "apiKey", "account", "user", "password")
93
	api.Sid = "123"
94
95
	sess := &MockedTogglSession{}
96
	return TogglClient{
97
		Session:    sess,
98
		Config:     &cfg,
99
		PlanfixAPI: api,
100
		Logger:     log.New(&output, "", log.LstdFlags),
101
	}
102
}
103
104
func getTestEntries() []TogglPlanfixEntry {
105
	date := getTestDate()
106
	return []TogglPlanfixEntry{
107
		{
108
			toggl.DetailedTimeEntry{Duration: 1, Start: &date},
109
			PlanfixEntryData{TaskID: 1, GroupCount: 1},
110
		},
111
		{
112
			toggl.DetailedTimeEntry{Duration: 2, Start: &date},
113
			PlanfixEntryData{TaskID: 1, GroupCount: 1},
114
		},
115
		{
116
			toggl.DetailedTimeEntry{Duration: 3, Start: &date},
117
			PlanfixEntryData{TaskID: 2, GroupCount: 1},
118
		},
119
		{
120
			toggl.DetailedTimeEntry{Duration: 4, Start: &date},
121
			PlanfixEntryData{TaskID: 2, GroupCount: 1},
122
		},
123
		{
124
			toggl.DetailedTimeEntry{Duration: 5, Start: &date},
125
			PlanfixEntryData{TaskID: 2, GroupCount: 1},
126
		},
127
		{
128
			toggl.DetailedTimeEntry{Duration: 6, Start: &date},
129
			PlanfixEntryData{TaskID: 3, GroupCount: 1},
130
		},
131
	}
132
}
133
134
func getTestGroupedEntries() map[int][]TogglPlanfixEntry {
135
	date := getTestDate()
136
	return map[int][]TogglPlanfixEntry{
137
		1: {
138
			{
139
				toggl.DetailedTimeEntry{Duration: 1, Start: &date},
140
				PlanfixEntryData{TaskID: 1, GroupCount: 1},
141
			},
142
			{
143
				toggl.DetailedTimeEntry{Duration: 2, Start: &date},
144
				PlanfixEntryData{TaskID: 1, GroupCount: 1},
145
			},
146
		},
147
		2: {
148
			{
149
				toggl.DetailedTimeEntry{Duration: 3, Start: &date},
150
				PlanfixEntryData{TaskID: 2, GroupCount: 1},
151
			},
152
			{
153
				toggl.DetailedTimeEntry{Duration: 4, Start: &date},
154
				PlanfixEntryData{TaskID: 2, GroupCount: 1},
155
			},
156
			{
157
				toggl.DetailedTimeEntry{Duration: 5, Start: &date},
158
				PlanfixEntryData{TaskID: 2, GroupCount: 1},
159
			},
160
		},
161
		3: {
162
			{
163
				toggl.DetailedTimeEntry{Duration: 6, Start: &date},
164
				PlanfixEntryData{TaskID: 3, GroupCount: 1},
165
			},
166
		},
167
	}
168
}
169
170
func getTestDayEntries() map[string][]TogglPlanfixEntry {
171
	date := getTestDate()
172
	return map[string][]TogglPlanfixEntry{
173
		date.Format("02-01-2006"): {
174
			{
175
				toggl.DetailedTimeEntry{Duration: 1, Start: &date},
176
				PlanfixEntryData{TaskID: 1, GroupCount: 1},
177
			},
178
			{
179
				toggl.DetailedTimeEntry{Duration: 2, Start: &date},
180
				PlanfixEntryData{TaskID: 1, GroupCount: 1},
181
			},
182
			{
183
				toggl.DetailedTimeEntry{Duration: 3, Start: &date},
184
				PlanfixEntryData{TaskID: 2, GroupCount: 1},
185
			},
186
			{
187
				toggl.DetailedTimeEntry{Duration: 4, Start: &date},
188
				PlanfixEntryData{TaskID: 2, GroupCount: 1},
189
			},
190
			{
191
				toggl.DetailedTimeEntry{Duration: 5, Start: &date},
192
				PlanfixEntryData{TaskID: 2, GroupCount: 1},
193
			},
194
			{
195
				toggl.DetailedTimeEntry{Duration: 6, Start: &date},
196
				PlanfixEntryData{TaskID: 3, GroupCount: 1},
197
			},
198
		},
199
	}
200
}
201
202
func getTestDate() time.Time {
203
	return time.Date(2018, 3, 4, 1, 2, 3, 0, time.Local)
204
}
205
206
func getTestDetailedReport() toggl.DetailedReport {
207
	date := getTestDate()
208
	return toggl.DetailedReport{Data: []toggl.DetailedTimeEntry{
209
		// will be filtered by sent tag
210
		{
211
			ID:          1,
212
			Project:     "project1",
213
			Description: "description1",
214
			Tags:        []string{"12345", "sent"},
215
			Start:       &date,
216
		},
217
		{
218
			ID:          2,
219
			Project:     "project1",
220
			Description: "description1",
221
			Tags:        []string{"12345"},
222
			Start:       &date,
223
		},
224
		// will be filtered by taskID tag
225
		{
226
			ID:          3,
227
			Project:     "project1",
228
			Description: "description1",
229
			Tags:        []string{},
230
			Start:       &date,
231
		},
232
		{
233
			ID:          4,
234
			Project:     "project1",
235
			Description: "description1",
236
			Tags:        []string{"12345"},
237
			Start:       &date,
238
		},
239
	}}
240
}
241
242
func TestTogglClient_SumEntriesGroup(t *testing.T) {
243
	date := getTestDate()
244
	c := newClient()
245
	groupedEntries := getTestGroupedEntries()
246
	expected := []TogglPlanfixEntry{
247
		{
248
			toggl.DetailedTimeEntry{Duration: 3, Start: &date},
249
			PlanfixEntryData{TaskID: 1, GroupCount: 2},
250
		},
251
		{
252
			toggl.DetailedTimeEntry{Duration: 12, Start: &date},
253
			PlanfixEntryData{TaskID: 2, GroupCount: 3},
254
		},
255
		{
256
			toggl.DetailedTimeEntry{Duration: 6, Start: &date},
257
			PlanfixEntryData{TaskID: 3, GroupCount: 1},
258
		},
259
	}
260
261
	summed := c.SumEntriesGroup(groupedEntries)
262
	assert.Equal(t, expected, summed)
263
}
264
265
func TestTogglClient_GroupEntriesByTask(t *testing.T) {
266
	c := newClient()
267
	entries := getTestEntries()
268
	expected := getTestGroupedEntries()
269
270
	grouped := c.GroupEntriesByTask(entries)
271
	assert.Equal(t, expected, grouped)
272
}
273
274
func TestTogglClient_GroupEntriesByDay(t *testing.T) {
275
	c := newClient()
276
	entries := getTestEntries()
277
	expected := getTestDayEntries()
278
279
	grouped := c.GroupEntriesByDay(entries)
280
	assert.Equal(t, expected, grouped)
281
}
282
283
func TestTogglClient_GroupEntriesByTask_empty(t *testing.T) {
284
	c := newClient()
285
	entries := []TogglPlanfixEntry{}
286
	expected := map[int][]TogglPlanfixEntry{}
287
288
	grouped := c.GroupEntriesByTask(entries)
289
	assert.Equal(t, expected, grouped)
290
}
291
292
func TestTogglClient_sendEntries_dryRun(t *testing.T) {
293
	c := newClient()
294
	c.Config.DryRun = true
295
	date := getTestDate()
296
	entries := []TogglPlanfixEntry{
297
		{
298
			toggl.DetailedTimeEntry{
299
				Duration:    60000,
300
				Start:       &date,
301
				Project:     "project",
302
				Description: "description",
303
			},
304
			PlanfixEntryData{TaskID: 1, GroupCount: 1},
305
		},
306
		{
307
			toggl.DetailedTimeEntry{Duration: 120000},
308
			PlanfixEntryData{TaskID: 1, GroupCount: 1},
309
		},
310
	}
311
312
	c.sendEntries(1, entries)
313
	assert.Contains(t, output.String(), "[DEBUG] sending [project]") //  description (3)
314
	assert.Contains(t, output.String(), "[DEBUG] dry-run")
315
}
316
317
func TestTogglClient_GetTogglUserID(t *testing.T) {
318
	c := newClient()
319
	sess := &MockedTogglSession{}
320
	c.Session = sess
321
322
	returnedTogglUser := toggl.Account{Data: struct {
323
		APIToken        string            `json:"api_token"`
324
		Timezone        string            `json:"timezone"`
325
		ID              int               `json:"id"`
326
		Workspaces      []toggl.Workspace `json:"workspaces"`
327
		Clients         []toggl.Client    `json:"clients"`
328
		Projects        []toggl.Project   `json:"projects"`
329
		Tasks           []toggl.Task      `json:"tasks"`
330
		Tags            []toggl.Tag       `json:"tags"`
331
		TimeEntries     []toggl.TimeEntry `json:"time_entries"`
332
		BeginningOfWeek int               `json:"beginning_of_week"`
333
	}{ID: 123}}
334
	sess.On("GetAccount").Return(returnedTogglUser, nil)
335
336
	togglUser, _ := c.GetTogglUser()
337
	assert.Equal(t, 123, togglUser.Data.ID)
338
}
339
340
func TestTogglClient_GetPlanfixUser(t *testing.T) {
341
	c := newClient()
342
	ms := NewMockedServer([]string{
343
		fixtureFromFile("user.get.xml"),
344
		//fixtureFromFile("error.xml"),
345
	})
346
	c.PlanfixAPI.URL = ms.URL
347
348
	planfixUser, _ := c.GetPlanfixUser()
349
	assert.Equal(t, 9230, planfixUser.ID)
350
}
351
352
func TestTogglClient_GetEntries(t *testing.T) {
353
	c := newClient()
354
	sess := &MockedTogglSession{}
355
	c.Session = sess
356
357
	since := time.Now().AddDate(0, 0, -30).Format("2006-01-02")
358
	until := time.Now().AddDate(0, 0, 1).Format("2006-01-02")
359
	report := toggl.DetailedReport{Data: []toggl.DetailedTimeEntry{
360
		{
361
			ID:          1,
362
			Project:     "project1",
363
			Description: "description1",
364
			Tags:        []string{"12345", "sent"},
365
		},
366
	}}
367
368
	sess.On("GetDetailedReport", 234, since, until, 1).Return(report, nil)
369
370
	report, _ = c.Session.GetDetailedReport(234, since, until, 1)
371
	entries, _ := c.GetEntries(234, since, until)
372
373
	expected := []TogglPlanfixEntry{
374
		{
375
			DetailedTimeEntry: report.Data[0],
376
			Planfix:           PlanfixEntryData{GroupCount: 1, Sent: true, TaskID: 12345},
377
		},
378
	}
379
	assert.Equal(t, expected, entries)
380
}
381
382
/*func TestTogglClient_GetPendingEntries(t *testing.T) {
383
	c := newClient()
384
	sess := &MockedTogglSession{}
385
	c.Session = sess
386
387
	date := getTestDate()
388
	report := getTestDetailedReport()
389
390
	rp := toggl.DetailedReportParams{
391
		WorkspaceID: c.Config.TogglWorkspaceID,
392
		Rounding: true,
393
		Since: getTestDate(),
394
		Until: getTestDate(),
395
	}
396
	// не понимаю, как замокать передачу объекта, падает с паникой
397
	sess.On("GetDetailedReportV2", rp).Return(report, nil)
398
399
	entries, _ := c.GetPendingEntries()
400
401
	expected := []TogglPlanfixEntry{
402
		{
403
			DetailedTimeEntry: toggl.DetailedTimeEntry{
404
				ID:          2,
405
				Project:     "project1",
406
				Description: "description1",
407
				Tags:        []string{"12345"},
408
				Start:       &date,
409
			},
410
			Planfix: PlanfixEntryData{GroupCount: 1, Sent: false, TaskID: 12345},
411
		},
412
		{
413
			DetailedTimeEntry: toggl.DetailedTimeEntry{
414
				ID:          4,
415
				Project:     "project1",
416
				Description: "description1",
417
				Tags:        []string{"12345"},
418
				Start:       &date,
419
			},
420
			Planfix: PlanfixEntryData{GroupCount: 1, Sent: false, TaskID: 12345},
421
		},
422
	}
423
	assert.Equal(t, expected, entries)
424
}*/
425
426
func TestTogglClient_markAsSent(t *testing.T) {
427
	c := newClient()
428
	sess := &MockedTogglSession{}
429
	c.Session = sess
430
431
	planfixEntries := []TogglPlanfixEntry{
432
		{
433
			DetailedTimeEntry: toggl.DetailedTimeEntry{
434
				ID:          2,
435
				Project:     "project1",
436
				Description: "description1",
437
				Tags:        []string{"12345"},
438
			},
439
			Planfix: PlanfixEntryData{GroupCount: 1, Sent: false, TaskID: 12345},
440
		},
441
		{
442
			DetailedTimeEntry: toggl.DetailedTimeEntry{
443
				ID:          4,
444
				Project:     "project1",
445
				Description: "description1",
446
				Tags:        []string{"12345"},
447
			},
448
			Planfix: PlanfixEntryData{GroupCount: 1, Sent: false, TaskID: 12345},
449
		},
450
	}
451
	togglEntries := []toggl.TimeEntry{
452
		{
453
			ID:          2,
454
			Description: "description1",
455
			Tags:        []string{"12345"},
456
		},
457
		{
458
			ID:          4,
459
			Description: "description1",
460
			Tags:        []string{"12345"},
461
		},
462
	}
463
	sess.On("AddRemoveTag", 2, c.Config.TogglSentTag, true).Return(togglEntries[0], nil)
464
	sess.On("AddRemoveTag", 4, c.Config.TogglSentTag, true).Return(togglEntries[1], nil)
465
466
	err := c.markAsSent(planfixEntries)
467
	assert.NoError(t, err)
468
}
469
470
func TestTogglClient_getTaskEmail(t *testing.T) {
471
	c := newClient()
472
	c.Config.PlanfixAccount = "mycompany"
473
474
	taskEmail := c.getTaskEmail(123)
475
	assert.Equal(t, "[email protected]", taskEmail)
476
}
477
478
func TestTogglClient_getEmailBody(t *testing.T) {
479
	c := newClient()
480
	c.Config.PlanfixAccount = "mycompany"
481
	c.Config.SMTPEmailFrom = "[email protected]"
482
	c.Config.PlanfixAnaliticTypeValue = "Название аналитики"
483
	c.Config.PlanfixAuthorName = "Имя Фамилия"
484
485
	expectedBody := "Content-Type: text/plain; charset=\"utf-8\"\r\n" +
486
		"From: [email protected]\r\n" +
487
		"To: [email protected]\r\n" +
488
		"Subject: @toggl @nonotify\r\n" +
489
		"\r\n" +
490
		"Вид работы: Название аналитики\r\n" +
491
		"time: 234\r\n" +
492
		"Автор: Имя Фамилия\r\n" +
493
		"Дата: 2018-03-04\r\n"
494
495
	body := c.getEmailBody(123, "2018-03-04", 234)
496
	assert.Equal(t, expectedBody, body)
497
}
498
499
func TestTogglClient_GetAnaliticData(t *testing.T) {
500
	c := newClient()
501
	ms := NewMockedServer([]string{
502
		fixtureFromFile("analitic.getList.xml"),
503
		fixtureFromFile("analitic.getOptions.xml"),
504
		fixtureFromFile("analitic.getHandbook.xml"),
505
	})
506
	c.PlanfixAPI.URL = ms.URL
507
	c.Config.PlanfixUserID = 123
508
	c.Config.PlanfixAnaliticName = "Выработка"
509
	c.Config.PlanfixAnaliticTypeName = "Вид работы"
510
	c.Config.PlanfixAnaliticTypeValue = "Поминутная работа программиста"
511
	c.Config.PlanfixAnaliticCountName = "Кол-во"
512
	c.Config.PlanfixAnaliticCommentName = "Комментарий / ссылка"
513
	c.Config.PlanfixAnaliticDateName = "Дата"
514
	c.Config.PlanfixAnaliticUsersName = "Сотрудник"
515
516
	// нормальное поведение
517
	analiticData, err := c.GetAnaliticDataCached(
518
		c.Config.PlanfixAnaliticName,
519
		c.Config.PlanfixAnaliticTypeName,
520
		c.Config.PlanfixAnaliticTypeValue,
521
		c.Config.PlanfixAnaliticCountName,
522
		c.Config.PlanfixAnaliticCommentName,
523
		c.Config.PlanfixAnaliticDateName,
524
		c.Config.PlanfixAnaliticUsersName,
525
	)
526
	assert.NoError(t, err)
527
	assert.Equal(t, 725, analiticData.TypeValueID)
528
529
	// тест кеша
530
	analiticData, err = c.GetAnaliticDataCached("", "", "", "", "", "", "")
531
	assert.NoError(t, err)
532
	assert.Equal(t, 725, analiticData.TypeValueID)
533
534
	// неправильный вид работы
535
	c.analiticData = PlanfixAnaliticData{} // сброс кеша
536
	ms = NewMockedServer([]string{
537
		fixtureFromFile("analitic.getList.xml"),
538
		fixtureFromFile("analitic.getOptions.xml"),
539
		fixtureFromFile("analitic.getHandbook.xml"),
540
	})
541
	c.PlanfixAPI.URL = ms.URL
542
	c.Config.PlanfixAnaliticTypeValue = "Какой-то неизвестный вид работы"
543
	analiticData, err = c.GetAnaliticData(
544
		c.Config.PlanfixAnaliticName,
545
		c.Config.PlanfixAnaliticTypeName,
546
		c.Config.PlanfixAnaliticTypeValue,
547
		c.Config.PlanfixAnaliticCountName,
548
		c.Config.PlanfixAnaliticCommentName,
549
		c.Config.PlanfixAnaliticDateName,
550
		c.Config.PlanfixAnaliticUsersName,
551
	)
552
	assert.Error(t, err)
553
	assert.Equal(t, 0, analiticData.TypeValueID)
554
	assert.Equal(t, 749, analiticData.CommentID)
555
}
556
557
// TODO: проходит метод полностью, но непонятно что проверяет
558
func TestTogglClient_sendWithPlanfixAPI(t *testing.T) {
559
	c := newClient()
560
	c.Config.PlanfixAnaliticName = "Выработка"
561
	c.Config.PlanfixAnaliticTypeName = "Вид работы"
562
	c.Config.PlanfixAnaliticTypeValue = "Поминутная работа программиста"
563
	c.Config.PlanfixAnaliticCountName = "Кол-во"
564
	c.Config.PlanfixAnaliticCommentName = "Комментарий / ссылка"
565
	c.Config.PlanfixAnaliticDateName = "Дата"
566
	c.Config.PlanfixAnaliticUsersName = "Сотрудник"
567
568
	ms := NewMockedServer([]string{
569
		fixtureFromFile("analitic.getList.xml"),
570
		fixtureFromFile("analitic.getOptions.xml"),
571
		fixtureFromFile("analitic.getHandbook.xml"),
572
		fixtureFromFile("action.add.xml"),
573
	})
574
	c.PlanfixAPI.URL = ms.URL
575
	c.Config.PlanfixUserID = 123
576
	c.Config.PlanfixAnaliticName = "Выработка"
577
578
	err := c.sendWithPlanfixAPI(123, "2018-03-04", 234, "comment")
579
	assert.NoError(t, err)
580
}
581
582
// TODO: проходит метод полностью, но непонятно что проверяет
583
/*func TestTogglClient_SendToPlanfix(t *testing.T) {
584
	c := newClient()
585
	sess := &MockedTogglSession{}
586
	c.Session = sess
587
588
	c.Config.PlanfixAnaliticName = "Выработка"
589
	c.Config.PlanfixAnaliticTypeName = "Вид работы"
590
	c.Config.PlanfixAnaliticTypeValue = "Поминутная работа программиста"
591
	c.Config.PlanfixAnaliticCountName = "Кол-во"
592
	c.Config.PlanfixAnaliticCommentName = "Комментарий / ссылка"
593
	c.Config.PlanfixAnaliticDateName = "Дата"
594
	c.Config.PlanfixAnaliticUsersName = "Сотрудник"
595
596
	ms := NewMockedServer([]string{
597
		fixtureFromFile("analitic.getList.xml"),
598
		fixtureFromFile("analitic.getOptions.xml"),
599
		fixtureFromFile("analitic.getHandbook.xml"),
600
		fixtureFromFile("action.add.xml"),
601
	})
602
	c.PlanfixAPI.URL = ms.URL
603
	c.Config.PlanfixUserID = 123
604
	c.Config.PlanfixAnaliticName = "Выработка"
605
606
	since := time.Now().AddDate(0, 0, -30).Format("2006-01-02")
607
	until := time.Now().AddDate(0, 0, 1).Format("2006-01-02")
608
	report := getTestDetailedReport()
609
	// тут падает с паникой, см. тест про PendingEntries
610
	sess.On("GetDetailedReport", c.Config.TogglWorkspaceID, since, until, 1).Return(report, nil)
611
612
	sess.On("AddRemoveTag", 2, c.Config.TogglSentTag, true).Return(toggl.TimeEntry{}, nil)
613
	sess.On("AddRemoveTag", 4, c.Config.TogglSentTag, true).Return(toggl.TimeEntry{}, nil)
614
615
	pending, _ := c.GetPendingEntries()
616
	grouped := c.GroupEntriesByTask(pending)
617
	summedExpected := c.SumEntriesGroup(grouped)
618
619
	summed, err := c.SendToPlanfix()
620
	assert.NoError(t, err)
621
	assert.Equal(t, summedExpected, summed)
622
}*/
623