Passed
Pull Request — master (#165)
by Mathieu
02:03
created

server/src/Application/Accounting/Command/Invoice/GenerateInvoiceCommandHandler.spec.ts   A

Complexity

Total Complexity 2
Complexity/F 0

Size

Lines of Code 173
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 149
dl 0
loc 173
rs 10
c 0
b 0
f 0
wmc 2
mnd 2
bc 2
fnc 0
bpm 0
cpm 0
noi 0
1
import { mock, instance, when, verify, anything, deepEqual } from 'ts-mockito';
2
import { ProjectRepository } from 'src/Infrastructure/Project/Repository/ProjectRepository';
3
import { GenerateInvoiceCommandHandler } from './GenerateInvoiceCommandHandler';
4
import { GenerateInvoiceCommand } from './GenerateInvoiceCommand';
5
import { User } from 'src/Domain/HumanResource/User/User.entity';
6
import { ProjectNotFoundException } from 'src/Domain/Project/Exception/ProjectNotFoundException';
7
import { InvoiceIdGenerator } from 'src/Domain/Accounting/Generators/InvoiceIdGenerator';
8
import { Invoice, InvoiceStatus } from 'src/Domain/Accounting/Invoice.entity';
9
import { EventRepository } from 'src/Infrastructure/FairCalendar/Repository/EventRepository';
10
import { InvoiceRepository } from 'src/Infrastructure/Accounting/Repository/InvoiceRepository';
11
import { InvoiceItemRepository } from 'src/Infrastructure/Accounting/Repository/InvoiceItemRepository';
12
import { DateUtilsAdapter } from 'src/Infrastructure/Adapter/DateUtilsAdapter';
13
import { Project } from 'src/Domain/Project/Project.entity';
14
import { NoBillableEventsFoundException } from 'src/Domain/Accounting/Exception/NoBillableEventsFoundException';
15
import { InvoiceItem } from 'src/Domain/Accounting/InvoiceItem.entity';
16
17
describe('GenerateInvoiceCommandHandler', () => {
18
  let projectRepository: ProjectRepository;
19
  let eventRepository: EventRepository;
20
  let invoiceRepository: InvoiceRepository;
21
  let invoiceItemRepository: InvoiceItemRepository;
22
  let invoiceIdGenerator: InvoiceIdGenerator;
23
  let dateUtilsAdapter: DateUtilsAdapter;
24
  let handler: GenerateInvoiceCommandHandler;
25
26
  const user = mock(User);
27
  const project = mock(Project);
28
  const date = new Date('2020-11-23T17:43:14.299Z');
29
  const expiryDate = new Date('2020-11-28T17:43:14.299Z');
30
  const command = new GenerateInvoiceCommand(
31
    'a491ccc9-df7c-4fc6-8e90-db816208f689',
32
    InvoiceStatus.DRAFT,
33
    5,
34
    date,
35
    instance(user)
36
  );
37
38
  beforeEach(() => {
39
    projectRepository = mock(ProjectRepository);
40
    invoiceIdGenerator = mock(InvoiceIdGenerator);
41
    eventRepository = mock(EventRepository);
42
    invoiceRepository = mock(InvoiceRepository);
43
    invoiceItemRepository = mock(InvoiceItemRepository);
44
    dateUtilsAdapter = mock(DateUtilsAdapter);
45
46
    handler = new GenerateInvoiceCommandHandler(
47
      instance(projectRepository),
48
      instance(eventRepository),
49
      instance(invoiceRepository),
50
      instance(invoiceItemRepository),
51
      instance(dateUtilsAdapter),
52
      instance(invoiceIdGenerator),
53
    );
54
  });
55
56
  it('testProjectNotFound', async () => {
57
    when(
58
      projectRepository.findOneById('a491ccc9-df7c-4fc6-8e90-db816208f689')
59
    ).thenResolve(null);
60
61
    try {
62
      await handler.execute(command);
63
    } catch (e) {
64
      expect(e).toBeInstanceOf(ProjectNotFoundException);
65
      expect(e.message).toBe('crm.projects.errors.not_found');
66
      verify(
67
        projectRepository.findOneById('a491ccc9-df7c-4fc6-8e90-db816208f689')
68
      ).once();
69
      verify(invoiceIdGenerator.generate()).never();
70
      verify(eventRepository.findBillableEventsByMonthAndProject(anything(), anything())).never();
71
      verify(dateUtilsAdapter.addDaysToDate(anything(), anything())).never();
72
      verify(invoiceRepository.save(anything())).never();
73
      verify(invoiceItemRepository.save(anything())).never();
74
    }
75
  });
76
77
  it('testNoBillableEventsFound', async () => {
78
    when(
79
      projectRepository.findOneById('a491ccc9-df7c-4fc6-8e90-db816208f689')
80
    ).thenResolve(instance(project));
81
    when(invoiceIdGenerator.generate()).thenResolve('FS-2020-0001');
82
    when(
83
      eventRepository.findBillableEventsByMonthAndProject(date, instance(project))
84
    ).thenResolve([]);
85
86
    try {
87
      await handler.execute(command);
88
    } catch (e) {
89
      expect(e).toBeInstanceOf(NoBillableEventsFoundException);
90
      expect(e.message).toBe('accounting.invoices.errors.no_billable_events_found');
91
      verify(
92
        projectRepository.findOneById('a491ccc9-df7c-4fc6-8e90-db816208f689')
93
      ).once();
94
      verify(invoiceIdGenerator.generate()).once();
95
      verify(
96
        eventRepository.findBillableEventsByMonthAndProject(date, instance(project))
97
      ).once();
98
      verify(dateUtilsAdapter.addDaysToDate(anything(), anything())).never();
99
      verify(invoiceRepository.save(anything())).never();
100
      verify(invoiceItemRepository.save(anything())).never();
101
    }
102
  });
103
104
  it('testGenerateInvoice', async () => {
105
    const events = [
106
      {
107
        time_spent: '180',
108
        billable: false,
109
        task_name: 'Développement',
110
        first_name: 'Mathieu',
111
        last_name: 'MARCHOIS',
112
        amount: 60000
113
      },
114
      {
115
        time_spent: '420',
116
        billable: true,
117
        task_name: 'Architecture',
118
        first_name: 'Mathieu',
119
        last_name: 'MARCHOIS',
120
        amount: null
121
      },
122
      {
123
        time_spent: '4200',
124
        billable: true,
125
        task_name: 'Développement',
126
        first_name: 'Mathieu',
127
        last_name: 'MARCHOIS',
128
        amount: 60000
129
      }
130
    ];
131
132
    const invoice = new Invoice(
133
      'FS-2020-0001',
134
      InvoiceStatus.DRAFT,
135
      '2020-11-28T17:43:14.299Z',
136
      instance(user),
137
      instance(project)
138
    );
139
140
    const savedInvoice = mock(Invoice);
141
    when(savedInvoice.getId()).thenReturn('fc8a4cd9-31eb-4fca-814d-b30c05de485d');
142
143
    const invoiceItems = [
144
      new InvoiceItem(invoice, 'Développement - Mathieu MARCHOIS', 180, 60000, 100),
145
      new InvoiceItem(invoice, 'Architecture - Mathieu MARCHOIS', 420, 0, 0),
146
      new InvoiceItem(invoice, 'Développement - Mathieu MARCHOIS', 4200, 60000, 0),
147
    ];
148
149
    when(
150
      projectRepository.findOneById('a491ccc9-df7c-4fc6-8e90-db816208f689')
151
    ).thenResolve(instance(project));
152
    when(invoiceIdGenerator.generate()).thenResolve('FS-2020-0001');
153
    when(
154
      eventRepository.findBillableEventsByMonthAndProject(date, instance(project))
155
    ).thenResolve(events);
156
    when(dateUtilsAdapter.addDaysToDate(date, 5)).thenReturn(expiryDate);
157
    when(invoiceRepository.save(deepEqual(invoice))).thenResolve(instance(savedInvoice));
158
159
    expect(await handler.execute(command)).toBe('fc8a4cd9-31eb-4fca-814d-b30c05de485d');
160
161
    verify(
162
      projectRepository.findOneById('a491ccc9-df7c-4fc6-8e90-db816208f689')
163
    ).once();
164
    verify(invoiceIdGenerator.generate()).once();
165
    verify(
166
      eventRepository.findBillableEventsByMonthAndProject(date, instance(project))
167
    ).once();
168
    verify(dateUtilsAdapter.addDaysToDate(date, 5)).once();
169
    verify(invoiceRepository.save(deepEqual(invoice))).once();
170
    verify(invoiceItemRepository.save(deepEqual(invoiceItems))).once();
171
  });
172
});
173