Passed
Push — master ( 7456e0...8eb6db )
by Stanislav
01:27
created

client.TestTogglClient_GetPlanfixUser   A

Complexity

Conditions 1

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 10
rs 10
c 0
b 0
f 0
eloc 7
nop 1
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
	}
91
	api := planfix.New("", "apiKey", "account", "user", "password")
92
	api.Sid = "123"
93
94
	sess := &MockedTogglSession{}
95
	return TogglClient{
96
		Session:    sess,
97
		Config:     &cfg,
98
		PlanfixAPI: api,
99
		Logger:     log.New(&output, "", log.LstdFlags),
100
	}
101
}
102
103
func getTestEntries() []TogglPlanfixEntry {
104
	return []TogglPlanfixEntry{
105
		{
106
			toggl.DetailedTimeEntry{Duration: 1},
107
			PlanfixEntryData{TaskID: 1, GroupCount: 1},
108
		},
109
		{
110
			toggl.DetailedTimeEntry{Duration: 2},
111
			PlanfixEntryData{TaskID: 1, GroupCount: 1},
112
		},
113
		{
114
			toggl.DetailedTimeEntry{Duration: 3},
115
			PlanfixEntryData{TaskID: 2, GroupCount: 1},
116
		},
117
		{
118
			toggl.DetailedTimeEntry{Duration: 4},
119
			PlanfixEntryData{TaskID: 2, GroupCount: 1},
120
		},
121
		{
122
			toggl.DetailedTimeEntry{Duration: 5},
123
			PlanfixEntryData{TaskID: 2, GroupCount: 1},
124
		},
125
		{
126
			toggl.DetailedTimeEntry{Duration: 6},
127
			PlanfixEntryData{TaskID: 3, GroupCount: 1},
128
		},
129
	}
130
}
131
132
func getTestGroupedEntries() map[int][]TogglPlanfixEntry {
133
	return map[int][]TogglPlanfixEntry{
134
		1: {
135
			{
136
				toggl.DetailedTimeEntry{Duration: 1},
137
				PlanfixEntryData{TaskID: 1, GroupCount: 1},
138
			},
139
			{
140
				toggl.DetailedTimeEntry{Duration: 2},
141
				PlanfixEntryData{TaskID: 1, GroupCount: 1},
142
			},
143
		},
144
		2: {
145
			{
146
				toggl.DetailedTimeEntry{Duration: 3},
147
				PlanfixEntryData{TaskID: 2, GroupCount: 1},
148
			},
149
			{
150
				toggl.DetailedTimeEntry{Duration: 4},
151
				PlanfixEntryData{TaskID: 2, GroupCount: 1},
152
			},
153
			{
154
				toggl.DetailedTimeEntry{Duration: 5},
155
				PlanfixEntryData{TaskID: 2, GroupCount: 1},
156
			},
157
		},
158
		3: {
159
			{
160
				toggl.DetailedTimeEntry{Duration: 6},
161
				PlanfixEntryData{TaskID: 3, GroupCount: 1},
162
			},
163
		},
164
	}
165
}
166
167
func getTestDate() time.Time {
168
	return time.Date(2018, 3, 4, 1, 2, 3, 0, time.Local)
169
}
170
171
func getTestDetailedReport() toggl.DetailedReport {
172
	date := getTestDate()
173
	return toggl.DetailedReport{Data: []toggl.DetailedTimeEntry{
174
		// will be filtered by sent tag
175
		{
176
			ID:          1,
177
			Project:     "project1",
178
			Description: "description1",
179
			Tags:        []string{"12345", "sent"},
180
			Start:       &date,
181
		},
182
		{
183
			ID:          2,
184
			Project:     "project1",
185
			Description: "description1",
186
			Tags:        []string{"12345"},
187
			Start:       &date,
188
		},
189
		// will be filtered by taskID tag
190
		{
191
			ID:          3,
192
			Project:     "project1",
193
			Description: "description1",
194
			Tags:        []string{},
195
			Start:       &date,
196
		},
197
		{
198
			ID:          4,
199
			Project:     "project1",
200
			Description: "description1",
201
			Tags:        []string{"12345"},
202
			Start:       &date,
203
		},
204
	}}
205
}
206
207
func TestTogglClient_SumEntriesGroup(t *testing.T) {
208
	c := newClient()
209
	groupedEntries := getTestGroupedEntries()
210
	expected := []TogglPlanfixEntry{
211
		{
212
			toggl.DetailedTimeEntry{Duration: 3},
213
			PlanfixEntryData{TaskID: 1, GroupCount: 2},
214
		},
215
		{
216
			toggl.DetailedTimeEntry{Duration: 12},
217
			PlanfixEntryData{TaskID: 2, GroupCount: 3},
218
		},
219
		{
220
			toggl.DetailedTimeEntry{Duration: 6},
221
			PlanfixEntryData{TaskID: 3, GroupCount: 1},
222
		},
223
	}
224
225
	summed := c.SumEntriesGroup(groupedEntries)
226
	assert.Equal(t, expected, summed)
227
}
228
229
func TestTogglClient_GroupEntriesByTask(t *testing.T) {
230
	c := newClient()
231
	entries := getTestEntries()
232
	expected := getTestGroupedEntries()
233
234
	grouped := c.GroupEntriesByTask(entries)
235
	assert.Equal(t, expected, grouped)
236
}
237
238
func TestTogglClient_GroupEntriesByTask_empty(t *testing.T) {
239
	c := newClient()
240
	entries := []TogglPlanfixEntry{}
241
	expected := map[int][]TogglPlanfixEntry{}
242
243
	grouped := c.GroupEntriesByTask(entries)
244
	assert.Equal(t, expected, grouped)
245
}
246
247
func TestTogglClient_sendEntries_dryRun(t *testing.T) {
248
	c := newClient()
249
	c.Config.DryRun = true
250
	date := getTestDate()
251
	entries := []TogglPlanfixEntry{
252
		{
253
			toggl.DetailedTimeEntry{
254
				Duration:    60000,
255
				Start:       &date,
256
				Project:     "project",
257
				Description: "description",
258
			},
259
			PlanfixEntryData{TaskID: 1, GroupCount: 1},
260
		},
261
		{
262
			toggl.DetailedTimeEntry{Duration: 120000},
263
			PlanfixEntryData{TaskID: 1, GroupCount: 1},
264
		},
265
	}
266
267
	c.sendEntries(1, entries)
268
	assert.Contains(t, output.String(), "[DEBUG] sending [project]") //  description (3)
269
	assert.Contains(t, output.String(), "[DEBUG] dry-run")
270
}
271
272
func TestTogglClient_GetTogglUserID(t *testing.T) {
273
	c := newClient()
274
	sess := &MockedTogglSession{}
275
	c.Session = sess
276
277
	returnedTogglUser := toggl.Account{Data: struct {
278
		APIToken        string            `json:"api_token"`
279
		Timezone        string            `json:"timezone"`
280
		ID              int               `json:"id"`
281
		Workspaces      []toggl.Workspace `json:"workspaces"`
282
		Clients         []toggl.Client    `json:"clients"`
283
		Projects        []toggl.Project   `json:"projects"`
284
		Tasks           []toggl.Task      `json:"tasks"`
285
		Tags            []toggl.Tag       `json:"tags"`
286
		TimeEntries     []toggl.TimeEntry `json:"time_entries"`
287
		BeginningOfWeek int               `json:"beginning_of_week"`
288
	}{ID: 123}}
289
	sess.On("GetAccount").Return(returnedTogglUser, nil)
290
291
	togglUser, _ := c.GetTogglUser()
292
	assert.Equal(t, 123, togglUser.Data.ID)
293
}
294
295
func TestTogglClient_GetPlanfixUser(t *testing.T) {
296
	c := newClient()
297
	ms := NewMockedServer([]string{
298
		fixtureFromFile("user.get.xml"),
299
		//fixtureFromFile("error.xml"),
300
	})
301
	c.PlanfixAPI.URL = ms.URL
302
303
	planfixUserID := c.GetPlanfixUserID()
304
	assert.Equal(t, 9230, planfixUserID)
305
}
306
307
func TestTogglClient_GetEntries(t *testing.T) {
308
	c := newClient()
309
	sess := &MockedTogglSession{}
310
	c.Session = sess
311
312
	since := time.Now().AddDate(0, 0, -30).Format("2006-01-02")
313
	until := time.Now().AddDate(0, 0, 1).Format("2006-01-02")
314
	report := toggl.DetailedReport{Data: []toggl.DetailedTimeEntry{
315
		{
316
			ID:          1,
317
			Project:     "project1",
318
			Description: "description1",
319
			Tags:        []string{"12345", "sent"},
320
		},
321
	}}
322
323
	sess.On("GetDetailedReport", 234, since, until, 1).Return(report, nil)
324
325
	report, _ = c.Session.GetDetailedReport(234, since, until, 1)
326
	entries, _ := c.GetEntries(234, since, until)
327
328
	expected := []TogglPlanfixEntry{
329
		{
330
			DetailedTimeEntry: report.Data[0],
331
			Planfix:           PlanfixEntryData{GroupCount: 1, Sent: true, TaskID: 12345},
332
		},
333
	}
334
	assert.Equal(t, expected, entries)
335
}
336
337
/*func TestTogglClient_GetPendingEntries(t *testing.T) {
338
	c := newClient()
339
	sess := &MockedTogglSession{}
340
	c.Session = sess
341
342
	date := getTestDate()
343
	report := getTestDetailedReport()
344
345
	rp := toggl.DetailedReportParams{
346
		WorkspaceID: c.Config.TogglWorkspaceID,
347
		Rounding: true,
348
		Since: getTestDate(),
349
		Until: getTestDate(),
350
	}
351
	// не понимаю, как замокать передачу объекта, падает с паникой
352
	sess.On("GetDetailedReportV2", rp).Return(report, nil)
353
354
	entries, _ := c.GetPendingEntries()
355
356
	expected := []TogglPlanfixEntry{
357
		{
358
			DetailedTimeEntry: toggl.DetailedTimeEntry{
359
				ID:          2,
360
				Project:     "project1",
361
				Description: "description1",
362
				Tags:        []string{"12345"},
363
				Start:       &date,
364
			},
365
			Planfix: PlanfixEntryData{GroupCount: 1, Sent: false, TaskID: 12345},
366
		},
367
		{
368
			DetailedTimeEntry: toggl.DetailedTimeEntry{
369
				ID:          4,
370
				Project:     "project1",
371
				Description: "description1",
372
				Tags:        []string{"12345"},
373
				Start:       &date,
374
			},
375
			Planfix: PlanfixEntryData{GroupCount: 1, Sent: false, TaskID: 12345},
376
		},
377
	}
378
	assert.Equal(t, expected, entries)
379
}*/
380
381
func TestTogglClient_markAsSent(t *testing.T) {
382
	c := newClient()
383
	sess := &MockedTogglSession{}
384
	c.Session = sess
385
386
	planfixEntries := []TogglPlanfixEntry{
387
		{
388
			DetailedTimeEntry: toggl.DetailedTimeEntry{
389
				ID:          2,
390
				Project:     "project1",
391
				Description: "description1",
392
				Tags:        []string{"12345"},
393
			},
394
			Planfix: PlanfixEntryData{GroupCount: 1, Sent: false, TaskID: 12345},
395
		},
396
		{
397
			DetailedTimeEntry: toggl.DetailedTimeEntry{
398
				ID:          4,
399
				Project:     "project1",
400
				Description: "description1",
401
				Tags:        []string{"12345"},
402
			},
403
			Planfix: PlanfixEntryData{GroupCount: 1, Sent: false, TaskID: 12345},
404
		},
405
	}
406
	togglEntries := []toggl.TimeEntry{
407
		{
408
			ID:          2,
409
			Description: "description1",
410
			Tags:        []string{"12345"},
411
		},
412
		{
413
			ID:          4,
414
			Description: "description1",
415
			Tags:        []string{"12345"},
416
		},
417
	}
418
	sess.On("AddRemoveTag", 2, c.Config.TogglSentTag, true).Return(togglEntries[0], nil)
419
	sess.On("AddRemoveTag", 4, c.Config.TogglSentTag, true).Return(togglEntries[1], nil)
420
421
	err := c.markAsSent(planfixEntries)
422
	assert.NoError(t, err)
423
}
424
425
func TestTogglClient_getTaskEmail(t *testing.T) {
426
	c := newClient()
427
	c.Config.PlanfixAccount = "mycompany"
428
429
	taskEmail := c.getTaskEmail(123)
430
	assert.Equal(t, "[email protected]", taskEmail)
431
}
432
433
func TestTogglClient_getEmailBody(t *testing.T) {
434
	c := newClient()
435
	c.Config.PlanfixAccount = "mycompany"
436
	c.Config.SMTPEmailFrom = "[email protected]"
437
	c.Config.PlanfixAnaliticTypeValue = "Название аналитики"
438
	c.Config.PlanfixAuthorName = "Имя Фамилия"
439
440
	expectedBody := "Content-Type: text/plain; charset=\"utf-8\"\r\n" +
441
		"From: [email protected]\r\n" +
442
		"To: [email protected]\r\n" +
443
		"Subject: @toggl @nonotify\r\n" +
444
		"\r\n" +
445
		"Вид работы: Название аналитики\r\n" +
446
		"time: 234\r\n" +
447
		"Автор: Имя Фамилия\r\n" +
448
		"Дата: 2018-03-04\r\n"
449
450
	body := c.getEmailBody(123, "2018-03-04", 234)
451
	assert.Equal(t, expectedBody, body)
452
}
453
454
func TestTogglClient_GetAnaliticData(t *testing.T) {
455
	c := newClient()
456
	ms := NewMockedServer([]string{
457
		fixtureFromFile("analitic.getList.xml"),
458
		fixtureFromFile("analitic.getOptions.xml"),
459
		fixtureFromFile("analitic.getHandbook.xml"),
460
	})
461
	c.PlanfixAPI.URL = ms.URL
462
	c.Config.PlanfixUserID = 123
463
	c.Config.PlanfixAnaliticName = "Выработка"
464
	c.Config.PlanfixAnaliticTypeName = "Вид работы"
465
	c.Config.PlanfixAnaliticTypeValue = "Поминутная работа программиста"
466
	c.Config.PlanfixAnaliticCountName = "Кол-во"
467
	c.Config.PlanfixAnaliticCommentName = "Комментарий / ссылка"
468
	c.Config.PlanfixAnaliticDateName = "Дата"
469
	c.Config.PlanfixAnaliticUsersName = "Сотрудник"
470
471
	// нормальное поведение
472
	analiticData, err := c.GetAnaliticData(
473
		c.Config.PlanfixAnaliticName,
474
		c.Config.PlanfixAnaliticTypeName,
475
		c.Config.PlanfixAnaliticTypeValue,
476
		c.Config.PlanfixAnaliticCountName,
477
		c.Config.PlanfixAnaliticCommentName,
478
		c.Config.PlanfixAnaliticDateName,
479
		c.Config.PlanfixAnaliticUsersName,
480
	)
481
	assert.NoError(t, err)
482
	assert.Equal(t, 725, analiticData.TypeValueID)
483
484
	// тест кеша
485
	analiticData, err = c.GetAnaliticDataCached("", "", "", "", "", "", "")
486
	assert.NoError(t, err)
487
	assert.Equal(t, 725, analiticData.TypeValueID)
488
489
	// неправильный вид работы
490
	c.analiticData = PlanfixAnaliticData{} // сброс кеша
491
	ms = NewMockedServer([]string{
492
		fixtureFromFile("analitic.getList.xml"),
493
		fixtureFromFile("analitic.getOptions.xml"),
494
		fixtureFromFile("analitic.getHandbook.xml"),
495
	})
496
	c.PlanfixAPI.URL = ms.URL
497
	c.Config.PlanfixAnaliticTypeValue = "Какой-то неизвестный вид работы"
498
	analiticData, err = c.GetAnaliticData(
499
		c.Config.PlanfixAnaliticName,
500
		c.Config.PlanfixAnaliticTypeName,
501
		c.Config.PlanfixAnaliticTypeValue,
502
		c.Config.PlanfixAnaliticCountName,
503
		c.Config.PlanfixAnaliticCommentName,
504
		c.Config.PlanfixAnaliticDateName,
505
		c.Config.PlanfixAnaliticUsersName,
506
	)
507
	assert.Error(t, err)
508
	assert.Equal(t, 0, analiticData.TypeValueID)
509
	assert.Equal(t, 749, analiticData.CommentID)
510
}
511
512
// TODO: проходит метод полностью, но непонятно что проверяет
513
func TestTogglClient_sendWithPlanfixAPI(t *testing.T) {
514
	c := newClient()
515
	c.Config.PlanfixAnaliticName = "Выработка"
516
	c.Config.PlanfixAnaliticTypeName = "Вид работы"
517
	c.Config.PlanfixAnaliticTypeValue = "Поминутная работа программиста"
518
	c.Config.PlanfixAnaliticCountName = "Кол-во"
519
	c.Config.PlanfixAnaliticCommentName = "Комментарий / ссылка"
520
	c.Config.PlanfixAnaliticDateName = "Дата"
521
	c.Config.PlanfixAnaliticUsersName = "Сотрудник"
522
523
	ms := NewMockedServer([]string{
524
		fixtureFromFile("analitic.getList.xml"),
525
		fixtureFromFile("analitic.getOptions.xml"),
526
		fixtureFromFile("analitic.getHandbook.xml"),
527
		fixtureFromFile("action.add.xml"),
528
	})
529
	c.PlanfixAPI.URL = ms.URL
530
	c.Config.PlanfixUserID = 123
531
	c.Config.PlanfixAnaliticName = "Выработка"
532
533
	err := c.sendWithPlanfixAPI(123, "2018-03-04", 234, "comment")
534
	assert.NoError(t, err)
535
}
536
537
// TODO: проходит метод полностью, но непонятно что проверяет
538
/*func TestTogglClient_SendToPlanfix(t *testing.T) {
539
	c := newClient()
540
	sess := &MockedTogglSession{}
541
	c.Session = sess
542
543
	c.Config.PlanfixAnaliticName = "Выработка"
544
	c.Config.PlanfixAnaliticTypeName = "Вид работы"
545
	c.Config.PlanfixAnaliticTypeValue = "Поминутная работа программиста"
546
	c.Config.PlanfixAnaliticCountName = "Кол-во"
547
	c.Config.PlanfixAnaliticCommentName = "Комментарий / ссылка"
548
	c.Config.PlanfixAnaliticDateName = "Дата"
549
	c.Config.PlanfixAnaliticUsersName = "Сотрудник"
550
551
	ms := NewMockedServer([]string{
552
		fixtureFromFile("analitic.getList.xml"),
553
		fixtureFromFile("analitic.getOptions.xml"),
554
		fixtureFromFile("analitic.getHandbook.xml"),
555
		fixtureFromFile("action.add.xml"),
556
	})
557
	c.PlanfixAPI.URL = ms.URL
558
	c.Config.PlanfixUserID = 123
559
	c.Config.PlanfixAnaliticName = "Выработка"
560
561
	since := time.Now().AddDate(0, 0, -30).Format("2006-01-02")
562
	until := time.Now().AddDate(0, 0, 1).Format("2006-01-02")
563
	report := getTestDetailedReport()
564
	// тут падает с паникой, см. тест про PendingEntries
565
	sess.On("GetDetailedReport", c.Config.TogglWorkspaceID, since, until, 1).Return(report, nil)
566
567
	sess.On("AddRemoveTag", 2, c.Config.TogglSentTag, true).Return(toggl.TimeEntry{}, nil)
568
	sess.On("AddRemoveTag", 4, c.Config.TogglSentTag, true).Return(toggl.TimeEntry{}, nil)
569
570
	pending, _ := c.GetPendingEntries()
571
	grouped := c.GroupEntriesByTask(pending)
572
	summedExpected := c.SumEntriesGroup(grouped)
573
574
	summed, err := c.SendToPlanfix()
575
	assert.NoError(t, err)
576
	assert.Equal(t, summedExpected, summed)
577
}*/
578